From 155744d9015b3636398bf0ce56242c5d26e9bbc3 Mon Sep 17 00:00:00 2001 From: Dmitrii Nikeshkin Date: Thu, 3 Apr 2025 11:38:43 +0200 Subject: [PATCH 01/15] feat: MLE app template --- generators/index.js | 214 ++++ generators/index.ts | 1 + generators/run-generator.js | 17 + package-lock.json | 1122 ++++++++++++++------ package.json | 16 +- src/index.ts | 22 +- templates/app/.env.example | 2 +- templates/app/.env.example.basic | 2 + templates/mle-app/.gitignore.template | 22 + templates/mle-app/deploy.js | 30 + templates/mle-app/package.json | 14 + templates/mle-app/src/database/cleanup.sql | 4 + templates/mle-app/src/database/db.js | 29 + templates/mle-app/src/database/initdb.sql | 32 + templates/mle-app/src/index.ts | 272 +++++ templates/mle-app/test-sql/call-specs.sql | 137 +++ templates/mle-app/test-sql/module.sql | 274 +++++ templates/mle-app/tsconfig.json | 12 + 18 files changed, 1889 insertions(+), 333 deletions(-) create mode 100644 generators/index.js create mode 100644 generators/run-generator.js create mode 100644 templates/mle-app/.gitignore.template create mode 100644 templates/mle-app/deploy.js create mode 100644 templates/mle-app/package.json create mode 100644 templates/mle-app/src/database/cleanup.sql create mode 100644 templates/mle-app/src/database/db.js create mode 100644 templates/mle-app/src/database/initdb.sql create mode 100644 templates/mle-app/src/index.ts create mode 100644 templates/mle-app/test-sql/call-specs.sql create mode 100644 templates/mle-app/test-sql/module.sql create mode 100644 templates/mle-app/tsconfig.json diff --git a/generators/index.js b/generators/index.js new file mode 100644 index 0000000..8653a2b --- /dev/null +++ b/generators/index.js @@ -0,0 +1,214 @@ +"use strict"; +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var __assign = (this && this.__assign) || function () { + __assign = Object.assign || function(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) + t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); +}; +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +var __generator = (this && this.__generator) || function (thisArg, body) { + var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); + return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; + function verb(n) { return function (v) { return step([n, v]); }; } + function step(op) { + if (f) throw new TypeError("Generator is already executing."); + while (g && (g = 0, op[0] && (_ = 0)), _) try { + if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; + if (y = 0, t) op = [op[0] & 2, t.value]; + switch (op[0]) { + case 0: case 1: t = op; break; + case 4: _.label++; return { value: op[1], done: false }; + case 5: _.label++; y = op[1]; op = [0]; continue; + case 7: op = _.ops.pop(); _.trys.pop(); continue; + default: + if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } + if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } + if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } + if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } + if (t[2]) _.ops.pop(); + _.trys.pop(); continue; + } + op = body.call(thisArg, _); + } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } + if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; + } +}; +Object.defineProperty(exports, "__esModule", { value: true }); +/* +** +** Copyright (c) 2024, Oracle and/or its affiliates. +** All rights reserved +** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +*/ +var extract_zip_1 = require("extract-zip"); +var node_fs_1 = require("node:fs"); +var node_path_1 = require("node:path"); +var node_url_1 = require("node:url"); +var yeoman_generator_1 = require("yeoman-generator"); +var __filename = (0, node_url_1.fileURLToPath)(import.meta.url); +var __dirname = node_path_1.default.dirname(__filename); +var retrieveConnectionStringDetailsFromORAFile = function (oraFilePath) { + var data = node_fs_1.default.readFileSync(oraFilePath, 'utf8'); + var protocol = data.slice((data.indexOf('protocol=') + 9), (data.indexOf(')(port'))); + var hostname = data.slice((data.indexOf('host=') + 5), (data.indexOf('))('))); + var port = data.slice((data.indexOf('port=') + 5), (data.indexOf(')(host'))); + var serviceName = data.slice((data.indexOf('service_name=') + 13), (data.indexOf('))(security'))); + return { protocol: protocol, hostname: hostname, port: port, serviceName: serviceName }; +}; +var generateConnectionString = function (protocol, hostname, port, serviceName) { return "".concat(protocol, "://").concat(hostname, ":").concat(port, "/").concat(serviceName); }; +var default_1 = /** @class */ (function (_super) { + __extends(default_1, _super); + function default_1(args, opts) { + var _this = _super.call(this, args, opts, { + customInstallTask: true + }) || this; + _this.sourceRoot(node_path_1.default.join(__dirname, '../templates')); + _this.options = __assign(__assign({}, opts), { apiConfiguration: opts.templateChoice.includes('todo') ? 'tasks' : 'connection' }); + return _this; + // this.env.options.nodePackageManager = 'npm'; + // this.env.options.cwd = path.join( process.cwd(), this.options.appName ); + } + default_1.prototype.install = function () { + this.spawnCommandSync('npm', ['install'], { + cwd: node_path_1.default.join(process.cwd(), this.options.appName) + }); + var lRevParseResult; + try { + lRevParseResult = this.spawnCommandSync('git', ['rev-parse', '--git-dir'], { + cwd: node_path_1.default.join(process.cwd(), this.options.appName), + stdio: 'pipe', + }); + } + catch (pError) { + lRevParseResult = pError; + } + if (lRevParseResult.failed) { + this.spawnCommandSync('git', ['init'], { + cwd: node_path_1.default.join(process.cwd(), this.options.appName) + }); + } + else { + this.log("Directory is already inside of the git repository \"".concat(lRevParseResult.stdout, "\". Skipping \"git init\"")); + } + }; + default_1.prototype.path = function () { + this.destinationRoot(node_path_1.default.join(process.cwd(), this.options.appName)); + }; + default_1.prototype.welcome = function () { + this.log('Generating database app...'); + }; + // copy the files in the templates/app folder to the root of appname + default_1.prototype.writing = function () { + return __awaiter(this, void 0, void 0, function () { + var walletPath, _a, protocol, hostname, port, serviceName, readme_data; + return __generator(this, function (_b) { + switch (_b.label) { + case 0: + if (!(!('connectionString' in this.options) && ('walletPath' in this.options))) return [3 /*break*/, 4]; + walletPath = node_path_1.default.join(process.cwd(), this.options.appName, 'server', 'utils', 'db', 'wallet'); + if (!this.options.walletPath.endsWith('.zip')) return [3 /*break*/, 2]; + return [4 /*yield*/, (0, extract_zip_1.default)(this.options.walletPath, { + dir: walletPath + })]; + case 1: + _b.sent(); + return [3 /*break*/, 3]; + case 2: + node_fs_1.default.cpSync(this.options.walletPath, walletPath, { recursive: true }); + _b.label = 3; + case 3: + _a = retrieveConnectionStringDetailsFromORAFile(node_path_1.default.join(walletPath, 'tnsnames.ora')), protocol = _a.protocol, hostname = _a.hostname, port = _a.port, serviceName = _a.serviceName; + this.options.connectionString = generateConnectionString(protocol, hostname, port, serviceName); + _b.label = 4; + case 4: + // Copy files that are common to all of the templates. + this.fs.copyTpl(this.templatePath(this.options.templateChoice), this.destinationPath(), { + appName: this.options.appName + }); + this.fs.copy(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/.github")), this.destinationPath('.github')); + // This copy of `eslintrc.cjs` should be removed once all templates support eslint v9 + this.fs.copy(this.templatePath("".concat(this.options.templateChoice, "/.eslintrc.cjs")), this.destinationPath('.eslintrc.cjs'), { + ignoreNoMatch: true + }); + this.fs.copy(this.templatePath("".concat(this.options.templateChoice, "/eslint.config.mjs")), this.destinationPath('eslint.config.mjs'), { + ignoreNoMatch: true + }); + this.fs.copy(this.templatePath("".concat(this.options.templateChoice, "/.gitignore.template")), this.destinationPath('.gitignore')); + /** + * The ORDS Concert App template provides: + * A markdown lint configuration file (.markdownlint.json) + * A .env.example file + * Additionally, the sample app expects that the user configures their development + * environment on their own to provide a better understanding of ords and how the + * app is structured. + * The rest of the files, like utils/* and db/* are also not needed since the sample + * app contains their own mechanisms to talk with the db. + */ + if (this.options.templateChoice.includes('ords-remix-jwt-sample')) { + this.fs.copy(this.templatePath("".concat(this.options.templateChoice, "/.markdownlint.jsonc")), this.destinationPath('.markdownlint.jsonc')); + this.fs.copy(this.templatePath("".concat(this.options.templateChoice, "/.env.example")), this.destinationPath('.env.example')); + } + else { + this.fs.copyTpl(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/").concat(node_path_1.default.basename(this.options.templateChoice) == 'node-jet' ? 'index-proxied' : 'index', ".cjs")), this.destinationPath('server/index.cjs'), this.options); + this.fs.copy(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/routes/").concat(this.options.apiConfiguration, ".cjs")), this.destinationPath("server/routes/".concat(this.options.apiConfiguration, ".cjs"))); + this.fs.copy(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/utils/db/**/*")), this.destinationPath('server/utils/db/')); + this.fs.copy(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/utils/rest-services/").concat(this.options.apiConfiguration, ".cjs")), this.destinationPath("server/utils/rest-services/".concat(this.options.apiConfiguration, ".cjs"))); + this.fs.copyTpl(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/.env.example")), this.destinationPath('.env.example'), { + appName: '', + connectionPassword: '', + connectionString: '', + connectionUsername: '', + walletPassword: '', + walletPath: '', + }); + this.fs.copyTpl(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/.env.example.").concat(('walletPath' in this.options) ? 'cloud-wallet' : 'basic')), this.destinationPath('.env'), this.options); + this.fs.copy(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/CONTRIBUTING.md")), this.destinationPath('CONTRIBUTING.md')); + readme_data = this.fs.read(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/README.md"))); + if (this.fs.exists((this.destinationPath('README.md')))) { + this.fs.append(this.destinationPath('README.md'), readme_data); + } + else { + this.fs.copy(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/README.md")), this.destinationPath('README.md')); + } + } + return [2 /*return*/]; + } + }); + }); + }; + default_1.prototype.end = function () { + this.log('Application generated successfully. Run the following command: \n\ncd ' + node_path_1.default.join(process.cwd(), this.options.appName) + '\n'); + if (!this.options.templateChoice.includes('ords-remix-jwt-sample')) { } + this.log('Please check out the README file to learn how to configurate the ORDS Concert App'); + }; + return default_1; +}(yeoman_generator_1.default)); +exports.default = default_1; diff --git a/generators/index.ts b/generators/index.ts index ab6ee1a..2290d8b 100644 --- a/generators/index.ts +++ b/generators/index.ts @@ -170,6 +170,7 @@ export default class extends Generator { connectionUsername: '', walletPassword: '', walletPath: '', + sqlclPath: '', } ); this.fs.copyTpl( diff --git a/generators/run-generator.js b/generators/run-generator.js new file mode 100644 index 0000000..073ef45 --- /dev/null +++ b/generators/run-generator.js @@ -0,0 +1,17 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.generateDatabaseApp = generateDatabaseApp; +/* +** +** Copyright (c) 2024, Oracle and/or its affiliates. +** All rights reserved +** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ +*/ +var yeoman_environment_1 = require("yeoman-environment"); +// import DatabaseAppGenerator from './generators/app/index.js'; +var index_js_1 = require("./index.js"); +var generatorEnvironment = yeoman_environment_1.default.createEnv(); +generatorEnvironment.registerStub(index_js_1.default, 'database-app'); +function generateDatabaseApp(opts) { + generatorEnvironment.run('database-app', opts); +} diff --git a/package-lock.json b/package-lock.json index e004b4c..d04c8ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "@oclif/plugin-plugins": "^5.3.7", "extract-zip": "^2.0.1", "inquirer": "^9.3.6", + "mle-js": "^23.7.0", "untildify": "^5.0.0", "yeoman-environment": "^3.19.3", "yeoman-generator": "^5.10.0" @@ -45,7 +46,7 @@ "@types/yeoman-generator": "^5.2.14", "chai": "^4", "dotenv-cli": "^8.0.0", - "eslint": "^8", + "eslint": "^9", "eslint-config-oclif": "^5", "eslint-config-oclif-typescript": "^3", "eslint-config-prettier": "^9.1.0", @@ -1736,17 +1737,73 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.19.2", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "dev": true, + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/config-helpers/-/config-helpers-0.2.0.tgz", + "integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.3.1", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, - "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -1754,53 +1811,51 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/eslintrc/node_modules/ajv": { "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/@eslint/eslintrc/node_modules/argparse": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" + "dev": true }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "engines": { + "node": ">=18" + } + }, "node_modules/@eslint/eslintrc/node_modules/js-yaml": { "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1810,17 +1865,15 @@ }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1829,13 +1882,34 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "version": "9.23.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/js/-/js-9.23.0.tgz", + "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", "dev": true, - "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.7", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", + "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", + "dev": true, + "dependencies": { + "@eslint/core": "^0.12.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@gar/promisify": { @@ -1845,13 +1919,44 @@ "inBundle": true, "license": "MIT" }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "engines": { + "node": ">=18.18" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, - "license": "Apache-2.0", + "peer": true, "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", @@ -1863,10 +1968,10 @@ }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1874,10 +1979,10 @@ }, "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "ISC", + "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1901,11 +2006,20 @@ }, "node_modules/@humanwhocodes/object-schema": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", "dev": true, - "license": "BSD-3-Clause" + "peer": true + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "engines": { + "node": ">=18.18" + } }, "node_modules/@inquirer/checkbox": { "version": "1.5.2", @@ -5484,10 +5598,9 @@ }, "node_modules/@types/semver": { "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@types/semver/-/semver-7.5.8.tgz", "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/@types/text-table": { "version": "0.2.5", @@ -5660,71 +5773,6 @@ "rxjs": "^6.4.0" } }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/@typescript-eslint/scope-manager": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", @@ -5743,34 +5791,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, "node_modules/@typescript-eslint/types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", @@ -5830,32 +5850,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, "node_modules/@typescript-eslint/visitor-keys": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", @@ -5876,10 +5870,10 @@ }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true, - "license": "ISC" + "peer": true }, "node_modules/@vitest/expect": { "version": "3.0.4", @@ -6086,10 +6080,9 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -7950,10 +7943,10 @@ }, "node_modules/doctrine": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "license": "Apache-2.0", + "peer": true, "dependencies": { "esutils": "^2.0.2" }, @@ -8340,121 +8333,526 @@ "@esbuild/win32-x64": "0.24.2" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.23.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/eslint/-/eslint-9.23.0.tgz", + "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.2", + "@eslint/config-helpers": "^0.2.0", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.23.0", + "@eslint/plugin-kit": "^0.2.7", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-oclif": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/eslint-config-oclif/-/eslint-config-oclif-5.2.2.tgz", + "integrity": "sha512-NNTyyolSmKJicgxtoWZ/hoy2Rw56WIoWCFxgnBkXqDgi9qPKMwZs2Nx2b6SHLJvCiWWhZhWr5V46CFPo3PSPag==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-config-xo-space": "^0.35.0", + "eslint-plugin-mocha": "^10.5.0", + "eslint-plugin-n": "^15.1.0", + "eslint-plugin-unicorn": "^48.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eslint-config-oclif-typescript": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/eslint-config-oclif-typescript/-/eslint-config-oclif-typescript-3.1.14.tgz", + "integrity": "sha512-YeBq5OiDRZFvfZ+wO0meF38fV06+zmEg15mnOLwkiAuUhjg2lH+UxvYA7uX2zUwR4p1WMUbfX+7CMfUwQ4TQ1A==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "eslint-config-xo-space": "^0.35.0", + "eslint-import-resolver-typescript": "^3.7.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-mocha": "^10.5.0", + "eslint-plugin-n": "^15", + "eslint-plugin-perfectionist": "^2.11.0" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "dev": true, + "peer": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "dev": true, + "peer": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "peer": true + }, + "node_modules/eslint-config-oclif-typescript/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "dev": true, + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "peer": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "peer": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "peer": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "peer": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "dev": true, + "peer": true, + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" + "peer": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "inBundle": true, - "license": "MIT", + "node_modules/eslint-config-oclif-typescript/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "peer": true + }, + "node_modules/eslint-config-oclif-typescript/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "peer": true, + "dependencies": { + "p-locate": "^5.0.0" + }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "node_modules/eslint-config-oclif-typescript/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" + "brace-expansion": "^1.1.7" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "*" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "peer": true, + "dependencies": { + "yocto-queue": "^0.1.0" }, - "funding": { - "url": "https://opencollective.com/eslint" + "engines": { + "node": ">=10" } }, - "node_modules/eslint-config-oclif": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/eslint-config-oclif/-/eslint-config-oclif-5.2.2.tgz", - "integrity": "sha512-NNTyyolSmKJicgxtoWZ/hoy2Rw56WIoWCFxgnBkXqDgi9qPKMwZs2Nx2b6SHLJvCiWWhZhWr5V46CFPo3PSPag==", + "node_modules/eslint-config-oclif-typescript/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "license": "MIT", + "peer": true, "dependencies": { - "eslint-config-xo-space": "^0.35.0", - "eslint-plugin-mocha": "^10.5.0", - "eslint-plugin-n": "^15.1.0", - "eslint-plugin-unicorn": "^48.0.1" + "p-limit": "^3.0.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=10" } }, - "node_modules/eslint-config-oclif-typescript": { - "version": "3.1.14", - "resolved": "https://registry.npmjs.org/eslint-config-oclif-typescript/-/eslint-config-oclif-typescript-3.1.14.tgz", - "integrity": "sha512-YeBq5OiDRZFvfZ+wO0meF38fV06+zmEg15mnOLwkiAuUhjg2lH+UxvYA7uX2zUwR4p1WMUbfX+7CMfUwQ4TQ1A==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "node_modules/eslint-config-oclif-typescript/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, "dependencies": { - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", - "eslint-config-xo-space": "^0.35.0", - "eslint-import-resolver-typescript": "^3.7.0", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-mocha": "^10.5.0", - "eslint-plugin-n": "^15", - "eslint-plugin-perfectionist": "^2.11.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=8" + } + }, + "node_modules/eslint-config-oclif-typescript/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" } }, "node_modules/eslint-config-prettier": { @@ -8832,6 +9230,83 @@ } } }, + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/scope-manager": { + "version": "7.18.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", + "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/types": { + "version": "7.18.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/types/-/types-7.18.0.tgz", + "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", + "dev": true, + "engines": { + "node": "^18.18.0 || >=20.0.0" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/typescript-estree": { + "version": "7.18.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", + "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/visitor-keys": "7.18.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/utils": { + "version": "7.18.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/utils/-/utils-7.18.0.tgz", + "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "7.18.0", + "@typescript-eslint/types": "7.18.0", + "@typescript-eslint/typescript-estree": "7.18.0" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + }, + "peerDependencies": { + "eslint": "^8.56.0" + } + }, + "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/visitor-keys": { + "version": "7.18.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", + "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "7.18.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || >=20.0.0" + } + }, "node_modules/eslint-plugin-unicorn": { "version": "48.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-48.0.1.tgz", @@ -8866,20 +9341,16 @@ } }, "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "version": "8.3.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/eslint-utils": { @@ -8941,13 +9412,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/eslint/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -8976,6 +9440,15 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/eslint/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -8993,19 +9466,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -9111,21 +9571,26 @@ } }, "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "version": "10.3.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.14.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.2.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/esprima": { @@ -9157,10 +9622,9 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -9564,16 +10028,15 @@ } }, "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "version": "8.0.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, - "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/filelist": { @@ -9715,26 +10178,23 @@ } }, "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "version": "4.0.1", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, - "license": "MIT", "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" + "keyv": "^4.5.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16" } }, "node_modules/flatted": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", - "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", - "dev": true, - "license": "ISC" + "version": "3.3.3", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true }, "node_modules/for-each": { "version": "0.3.4", @@ -11127,10 +11587,10 @@ }, "node_modules/is-path-inside": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, - "license": "MIT", + "peer": true, "engines": { "node": ">=8" } @@ -12294,6 +12754,11 @@ "node": ">=10" } }, + "node_modules/mle-js": { + "version": "23.7.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/mle-js/-/mle-js-23.7.0.tgz", + "integrity": "sha512-tsn+judDyHMWNapMDhgorAvouKCyA0V50U3oxrafWaEDjeB2XsWhZLxpw2L4NGWbBWuzPT7t4MUG0EBQYe57ow==" + }, "node_modules/mocha": { "version": "10.8.2", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", @@ -19468,11 +19933,10 @@ } }, "node_modules/tr46": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", - "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "version": "5.1.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/tr46/-/tr46-5.1.0.tgz", + "integrity": "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==", "inBundle": true, - "license": "MIT", "dependencies": { "punycode": "^2.3.1" }, @@ -20460,12 +20924,12 @@ } }, "node_modules/whatwg-url": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", - "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", + "version": "14.2.0", + "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "inBundle": true, "dependencies": { - "tr46": "^5.0.0", + "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" }, "engines": { diff --git a/package.json b/package.json index 83b7f8f..25b91e5 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "@oclif/plugin-plugins": "^5.3.7", "extract-zip": "^2.0.1", "inquirer": "^9.3.6", + "mle-js": "^23.7.0", "untildify": "^5.0.0", "yeoman-environment": "^3.19.3", "yeoman-generator": "^5.10.0" @@ -96,7 +97,7 @@ "@types/yeoman-generator": "^5.2.14", "chai": "^4", "dotenv-cli": "^8.0.0", - "eslint": "^8", + "eslint": "^9", "eslint-config-oclif": "^5", "eslint-config-oclif-typescript": "^3", "eslint-config-prettier": "^9.1.0", @@ -113,5 +114,16 @@ }, "overrides": { "whatwg-url": "^14.1.0" - } + }, + "bundleDependencies": [ + "@inquirer/prompts", + "@oclif/core", + "@oclif/plugin-help", + "@oclif/plugin-plugins", + "extract-zip", + "inquirer", + "untildify", + "yeoman-environment", + "yeoman-generator" + ] } diff --git a/src/index.ts b/src/index.ts index 5472ff9..5ae933a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -207,7 +207,7 @@ export default class Generate extends Command { 'template': Flags.string({ char: 't', description: 'Template to use', - options: ['node-vanilla', 'node-react', 'node-vue', 'node-react-todo', 'node-jet', 'node-angular', 'ords-remix-jwt-sample'], + options: ['node-vanilla', 'node-react', 'node-vue', 'node-react-todo', 'node-jet', 'node-angular', 'ords-remix-jwt-sample', 'mle-app'], multiple: false }), @@ -313,6 +313,7 @@ export default class Generate extends Command { const databaseSID = flags['db-sid'] ?? ''; const databaseServiceName = flags['db-service-name'] ?? ''; const databaseUsername = flags['db-username'] ?? ''; + const sqlclPath = flags['sql-cl'] ?? ''; // TODO: Validate and use wallet path const walletPathDirectory = flags['wallet-path'] ? flags['wallet-path'] : ''; @@ -373,6 +374,11 @@ export default class Generate extends Command { value: 'ords-remix-jwt-sample', description: 'This creates a fullstack Concert Application made with Remix that leverages the Oracle REST Data Services functionalities. You will need to configurate the application yourself following the getting started guide.', }, + { + name: 'mle-app', + value: 'mle-app', + description: 'This creates an empty project with MLE and Oracle database connection starter code.' + }, ], default: 'node-vanilla' }, @@ -533,6 +539,20 @@ export default class Generate extends Command { } ); } + if(templateChoice == 'mle-app'){ + // Ask the user for the path to SQLcl + Object.assign( configObject, { + sqlclPath: sqlclPath === '' ? await input( + { + message: 'Please provide full path to your SQLcl installation: ', + validate ( input ) { + return input.trim().length === 0 ? 'This field cannot be empty!' : true; + } + }, + ) : sqlclPath + }); + } + generateDatabaseApp( configObject ); // TODO: This is the object that holds the application name, template choice, connection details depending on the chosen connection type. // console.log( JSON.stringify( configObject, null, ' ' ) ); diff --git a/templates/app/.env.example b/templates/app/.env.example index 34ca47e..a36ae2a 100644 --- a/templates/app/.env.example +++ b/templates/app/.env.example @@ -11,4 +11,4 @@ CONNECT_STRING=<%= connectionString %> # Optional HTTP Proxy Settings # HTTPS_PROXY= -# HTTPS_PROXY_PORT= +# HTTPS_PROXY_PORT= \ No newline at end of file diff --git a/templates/app/.env.example.basic b/templates/app/.env.example.basic index e98f11c..45f5255 100644 --- a/templates/app/.env.example.basic +++ b/templates/app/.env.example.basic @@ -8,3 +8,5 @@ CONNECT_STRING=<%= connectionString %> # Optional HTTP Proxy Settings # HTTPS_PROXY= # HTTPS_PROXY_PORT= + +SQL_CL_PATH=<%= sqlclPath %> diff --git a/templates/mle-app/.gitignore.template b/templates/mle-app/.gitignore.template new file mode 100644 index 0000000..c481026 --- /dev/null +++ b/templates/mle-app/.gitignore.template @@ -0,0 +1,22 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.DS_Store + +/.env +/.env.* +!/.env.example +!/.env.*.example +/server/utils/db/wallet diff --git a/templates/mle-app/deploy.js b/templates/mle-app/deploy.js new file mode 100644 index 0000000..6dbf136 --- /dev/null +++ b/templates/mle-app/deploy.js @@ -0,0 +1,30 @@ +const esbuild = require('esbuild'); +const { execSync } = require("child_process"); +const dotenv = require("dotenv"); + +dotenv.config(); +const { DB_USER, DB_PASSWORD, CONNECT_STRING, SQL_CL_PATH } = process.env; + +const bundlePath = 'dist/index.js'; +esbuild.build({ + entryPoints: ['src/index.ts'], + bundle: true, + minify: false, + platform: 'neutral', + format: 'esm', + outfile: bundlePath, +}).then(() => { + const sqlclCommand = `"${SQL_CL_PATH}/bin/sql" ${DB_USER}/${DB_PASSWORD}@${CONNECT_STRING} < { + console.log(e); + process.exit(1) +}); \ No newline at end of file diff --git a/templates/mle-app/package.json b/templates/mle-app/package.json new file mode 100644 index 0000000..4e74205 --- /dev/null +++ b/templates/mle-app/package.json @@ -0,0 +1,14 @@ +{ + "name": "<%= appName %>", + "version": "1.0.0", + "devDependencies": { + "mle-js": "^23.7.0", + "typescript": "^5.7.3", + "esbuild": "0.25.1" + }, + "scripts": { + "build": "node deploy.js", + "initdb": "node src/database/db.js initdb.sql", + "cleandb": "node src/database/db.js cleanup.sql" + } +} \ No newline at end of file diff --git a/templates/mle-app/src/database/cleanup.sql b/templates/mle-app/src/database/cleanup.sql new file mode 100644 index 0000000..8d62801 --- /dev/null +++ b/templates/mle-app/src/database/cleanup.sql @@ -0,0 +1,4 @@ +drop mle module mleapp; +drop table todo_list; +drop table categories; +drop table users; \ No newline at end of file diff --git a/templates/mle-app/src/database/db.js b/templates/mle-app/src/database/db.js new file mode 100644 index 0000000..dea6954 --- /dev/null +++ b/templates/mle-app/src/database/db.js @@ -0,0 +1,29 @@ +const { execSync } = require("child_process"); +const fs = require("fs"); +const path = require("path"); +const dotenv = require("dotenv"); +var args = process.argv.slice(2); + +dotenv.config(); + +const { DB_USER, DB_PASSWORD, CONNECT_STRING, SQL_CL_PATH } = process.env; +const dbFolder = path.resolve("src", "database"); + + +console.log("Executing database script..."); + +const filePath = path.join(dbFolder, args[0]); +console.log(`Executing: ${filePath}...`); + +const sqlclCommand = `"${SQL_CL_PATH}/bin/sql" -S ${DB_USER}/${DB_PASSWORD}@${CONNECT_STRING} @${filePath} < + +enum priorities { + LOW = "low", + MEDIUM = "medium", + HIGH = "high", +} + +export function newUser(name: string): number { + const result = session.execute( + "insert into users (name) values (:name) returning id into :id", + { + name: { + dir: oracledb.BIND_IN, + val: name, + type: oracledb.STRING, + }, + id: { + type: oracledb.NUMBER, + dir: oracledb.BIND_OUT, + }, + }, + ); + + const id = result.outBinds.id[0]; + + return id; +} + +export function getUser(id: number): any { + const result = session.execute( + "select id, name from users where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + + return result.rows?.[0] || null; +} + +export function updateUser(id: number, newName: string): boolean { + const result = session.execute( + "update users set name = :name where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + name: { + dir: oracledb.BIND_IN, + val: newName, + type: oracledb.STRING, + }, + }, + ); + return true; +} + +export function deleteUser(id: number): boolean { + const result = session.execute( + "delete from users where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + }, + ); + + return true; +} + + +export function newCategory(name: string, priority: priorities): number { + const result = session.execute( + "insert into categories (name, prio) values (:name, :prio) returning id into :id", + { + name: { + dir: oracledb.BIND_IN, + val: name, + type: oracledb.STRING, + }, + prio: { + dir: oracledb.BIND_IN, + val: priority, + type: oracledb.STRING, + }, + id: { + type: oracledb.NUMBER, + dir: oracledb.BIND_OUT, + }, + }, + ); + + const id = result.outBinds.id[0]; + + return id; +} + +export function getCategory(id: number): any { + const result = session.execute( + "select id, name, prio from categories where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + + return result.rows?.[0] || null; +} + +export function updateCategory(id: number, newName: string, newPriority: priorities): boolean { + const result = session.execute( + "update categories set name = :name, prio = :prio where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + name: { + dir: oracledb.BIND_IN, + val: newName, + type: oracledb.STRING, + }, + prio: { + dir: oracledb.BIND_IN, + val: newPriority, + type: oracledb.STRING, + }, + }, + ); + + return true; +} + +export function deleteCategory(id: number): boolean { + const result = session.execute( + "delete from categories where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + }, + ); + + return true; +} + +export function newTodoItem(userId: number, categoryId: number, name: string, completed: boolean = false): number { + const result = session.execute( + `insert into todo_list (u_id, c_id, name, completed) + values (:u_id, :c_id, :name, :completed) + returning id into :id`, + { + u_id: { + dir: oracledb.BIND_IN, + val: userId, + type: oracledb.NUMBER, + }, + c_id: { + dir: oracledb.BIND_IN, + val: categoryId, + type: oracledb.NUMBER, + }, + name: { + dir: oracledb.BIND_IN, + val: name, + type: oracledb.STRING, + }, + completed: { + dir: oracledb.BIND_IN, + val: completed ? 1 : 0, + type: oracledb.NUMBER, + }, + id: { + type: oracledb.NUMBER, + dir: oracledb.BIND_OUT, + }, + } + ); + + const id = result.outBinds.id[0]; + return id; +} + + +export function getTodoItem(id: number): any { + const result = session.execute( + `select id, u_id, c_id, name, completed from todo_list where id = :id`, + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + + return result.rows?.[0] || null; +} + +export function updateTodoItem(id: number, newName: string, newCompleted: boolean): boolean { + const result = session.execute( + `update todo_list set name = :name, completed = :completed where id = :id`, + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + name: { + dir: oracledb.BIND_IN, + val: newName, + type: oracledb.STRING, + }, + completed: { + dir: oracledb.BIND_IN, + val: newCompleted ? 1 : 0, + type: oracledb.NUMBER, + }, + } + ); + + return true; +} + +export function deleteTodoItem(id: number): boolean { + const result = session.execute( + "delete from todo_list where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + } + ); + + return true; +} + + +export function getTodosByUser(userId: number): any[] { + const result = session.execute( + "select id, c_id, name, completed from todo_list where u_id = :u_id", + { + u_id: { + dir: oracledb.BIND_IN, + val: userId, + type: oracledb.NUMBER, + }, + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + + return result.rows || []; +} \ No newline at end of file diff --git a/templates/mle-app/test-sql/call-specs.sql b/templates/mle-app/test-sql/call-specs.sql new file mode 100644 index 0000000..08154bb --- /dev/null +++ b/templates/mle-app/test-sql/call-specs.sql @@ -0,0 +1,137 @@ + +CREATE OR REPLACE PACKAGE user_package AS + PROCEDURE newUserFunc(name IN VARCHAR2); + FUNCTION getUser(id IN NUMBER) RETURN VARCHAR2; + PROCEDURE updateUser(id IN NUMBER, name IN VARCHAR2); + PROCEDURE deleteUser(id IN NUMBER); +END user_package; +/ + +CREATE OR REPLACE PACKAGE BODY user_package AS + PROCEDURE + newUserFunc(name IN VARCHAR2) + AS MLE MODULE MLEAPP + SIGNATURE 'newUser(string)'; + + FUNCTION + getUser(id IN NUMBER) RETURN VARCHAR2 + AS MLE MODULE MLEAPP + SIGNATURE 'getUser(number)'; + + PROCEDURE + updateUser(id IN NUMBER, name IN VARCHAR2) + AS MLE MODULE MLEAPP + SIGNATURE 'updateUser(number, string)'; + + PROCEDURE + deleteUser(id IN NUMBER) + AS MLE MODULE MLEAPP + SIGNATURE 'deleteUser(number)'; +END user_package; +/ + + +EXECUTE USER_PACKAGE.NEWUSERFUNC('BLABLA'); + +SELECT USER_PACKAGE.GETUSER(5); + +EXECUTE USER_PACKAGE.UPDATEUSER(5,'BLABLA'); + +SELECT USER_PACKAGE.GETUSER(5); + +EXECUTE USER_PACKAGE.DELETEUSER(5); + +SELECT USER_PACKAGE.GETUSER(5); + +-- select * from users; + +-- TRUNCATE table users; + +CREATE OR REPLACE PACKAGE category_package AS + PROCEDURE newCategory(name IN VARCHAR2, priority IN VARCHAR2); + FUNCTION getCategory(id IN NUMBER) RETURN VARCHAR2; + PROCEDURE updateCategory(id IN NUMBER, newName IN VARCHAR2, newPrio IN VARCHAR2); + PROCEDURE deleteCategory(id IN NUMBER); +END category_package; +/ + +CREATE OR REPLACE PACKAGE BODY category_package AS + PROCEDURE + newCategory(name IN VARCHAR2, priority IN VARCHAR2) + AS MLE MODULE MLEAPP + SIGNATURE 'newCategory(string,string)'; + + FUNCTION + getCategory(id IN NUMBER) RETURN VARCHAR2 + AS MLE MODULE MLEAPP + SIGNATURE 'getCategory(number)'; + + PROCEDURE + updateCategory(id IN NUMBER, newName IN VARCHAR2, newPrio IN VARCHAR2) + AS MLE MODULE MLEAPP + SIGNATURE 'updateCategory(number, string, string)'; + + PROCEDURE + deleteCategory(id IN NUMBER) + AS MLE MODULE MLEAPP + SIGNATURE 'deleteCategory(number)'; +END category_package; +/ + +EXECUTE CATEGORY_PACKAGE.NEWCATEGORY('BLABLA','high'); + +SELECT CATEGORY_PACKAGE.GETCATEGORY(2); + +EXECUTE CATEGORY_PACKAGE.UPDATECATEGORY(2,'NEW','low'); + +EXECUTE CATEGORY_PACKAGE.DELETECATEGORY(2); + +SELECT CATEGORY_PACKAGE.GETCATEGORY(2); + + +CREATE OR REPLACE PACKAGE todo_package AS + PROCEDURE newTodoItem(userId in NUMBER, categoryId in NUMBER, name IN VARCHAR2, completed IN BOOLEAN); + FUNCTION getTodoItem(id IN NUMBER) RETURN VARCHAR2; + PROCEDURE updateTodoItem(id IN NUMBER, newName IN VARCHAR2, newCompleted IN BOOLEAN); + PROCEDURE deleteTodoItem(id IN NUMBER); +END todo_package; +/ + +CREATE OR REPLACE PACKAGE BODY todo_package AS + PROCEDURE + newTodoItem(userId in NUMBER, categoryId in NUMBER, name IN VARCHAR2, completed IN BOOLEAN) + AS MLE MODULE MLEAPP + SIGNATURE 'newTodoItem(number,number,string,boolean)'; + + FUNCTION + getTodoItem(id IN NUMBER) RETURN VARCHAR2 + AS MLE MODULE MLEAPP + SIGNATURE 'getTodoItem(number)'; + + PROCEDURE + updateTodoItem(id IN NUMBER, newName IN VARCHAR2, newCompleted IN BOOLEAN) + AS MLE MODULE MLEAPP + SIGNATURE 'updateTodoItem(number, string, boolean)'; + + PROCEDURE + deleteTodoItem(id IN NUMBER) + AS MLE MODULE MLEAPP + SIGNATURE 'deleteTodoItem(number)'; +END todo_package; +/ + + +select * from users; +select * from CATEGORIES; + +EXECUTE TODO_PACKAGE.NEWTODOITEM(6,3,'GROCERIES',TRUE); + +select * from TODO_LIST; + +select TODO_PACKAGE.GETTODOITEM(1); + +EXECUTE TODO_PACKAGE.UPDATETODOITEM(1,'ERRANDS',FALSE); + +select TODO_PACKAGE.GETTODOITEM(1); + +EXECUTE TODO_PACKAGE.DELETETODOITEM(1); \ No newline at end of file diff --git a/templates/mle-app/test-sql/module.sql b/templates/mle-app/test-sql/module.sql new file mode 100644 index 0000000..ca76024 --- /dev/null +++ b/templates/mle-app/test-sql/module.sql @@ -0,0 +1,274 @@ +SET ECHO ON +SET FEEDBACK 1 +SET NUMWIDTH 10 +SET LINESIZE 200 +SET TRIMSPOOL ON +SET TAB OFF +SET PAGESIZE 100 + +SET SERVEROUTPUT ON; + +CREATE OR REPLACE MLE MODULE MLEAPP +LANGUAGE JAVASCRIPT AS +// src/index.ts +function newUser(name) { + const result = session.execute( + "insert into users (name) values (:name) returning id into :id", + { + name: { + dir: oracledb.BIND_IN, + val: name, + type: oracledb.STRING + }, + id: { + type: oracledb.NUMBER, + dir: oracledb.BIND_OUT + } + } + ); + const id = result.outBinds.id[0]; + return id; +} +function getUser(id) { + const result = session.execute( + "select name from users where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER + } + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + let user = result.rows[0]; + if(user) { + return result.rows[0].NAME; + } else { + return ""; + } +} +function updateUser(id, newName) { + const result = session.execute( + "update users set name = :name where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER + }, + name: { + dir: oracledb.BIND_IN, + val: newName, + type: oracledb.STRING + } + } + ); +} +function deleteUser(id) { + const result = session.execute( + "delete from users where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER + } + } + ); +} + +function newCategory(name, priority) { + const result = session.execute( + "insert into categories (name, prio) values (:name, :prio) returning id into :id", + { + name: { + dir: oracledb.BIND_IN, + val: name, + type: oracledb.STRING + }, + prio: { + dir: oracledb.BIND_IN, + val: priority, + type: oracledb.STRING + }, + id: { + type: oracledb.NUMBER, + dir: oracledb.BIND_OUT + } + } + ); +} +function getCategory(id) { + const result = session.execute( + "select id, name, prio from categories where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER + } + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + let category = result.rows[0]; + if(category) { + return JSON.stringify(category); + } else { + return ""; + } +} +function updateCategory(id, newName, newPriority) { + const result = session.execute( + "update categories set name = :name, prio = :prio where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER + }, + name: { + dir: oracledb.BIND_IN, + val: newName, + type: oracledb.STRING + }, + prio: { + dir: oracledb.BIND_IN, + val: newPriority, + type: oracledb.STRING + } + } + ); +} +function deleteCategory(id) { + const result = session.execute( + "delete from categories where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER + } + } + ); +} +function newTodoItem(userId, categoryId, name, completed = false) { + const result = session.execute( + `insert into todo_list (u_id, c_id, name, completed) + values (:u_id, :c_id, :name, :completed) + returning id into :id`, + { + u_id: { + dir: oracledb.BIND_IN, + val: userId, + type: oracledb.NUMBER + }, + c_id: { + dir: oracledb.BIND_IN, + val: categoryId, + type: oracledb.NUMBER + }, + name: { + dir: oracledb.BIND_IN, + val: name, + type: oracledb.STRING + }, + completed: { + dir: oracledb.BIND_IN, + val: completed ? 1 : 0, + type: oracledb.NUMBER + }, + id: { + type: oracledb.NUMBER, + dir: oracledb.BIND_OUT + } + } + ); + const id = result.outBinds.id[0]; + return id; +} +function getTodoItem(id) { + const result = session.execute( + `select id, u_id, c_id, name from todo_list where id = :id`, + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER + } + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + let todoitem = result.rows[0]; + if(todoitem) { + return JSON.stringify(todoitem); + } else { + return ""; + } +} +function updateTodoItem(id, newName, newCompleted) { + const result = session.execute( + `update todo_list set name = :name, completed = :completed where id = :id`, + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER + }, + name: { + dir: oracledb.BIND_IN, + val: newName, + type: oracledb.STRING + }, + completed: { + dir: oracledb.BIND_IN, + val: newCompleted ? 1 : 0, + type: oracledb.NUMBER + } + } + ); + return true; +} +function deleteTodoItem(id) { + const result = session.execute( + "delete from todo_list where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER + } + } + ); + return true; +} +function getTodosByUser(userId) { + const result = session.execute( + "select id, c_id, name, completed from todo_list where u_id = :u_id", + { + u_id: { + dir: oracledb.BIND_IN, + val: userId, + type: oracledb.NUMBER + } + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + return result.rows || []; +} +export { + deleteCategory, + deleteTodoItem, + deleteUser, + getCategory, + getTodoItem, + getTodosByUser, + getUser, + newCategory, + newTodoItem, + newUser, + updateCategory, + updateTodoItem, + updateUser +}; +/ \ No newline at end of file diff --git a/templates/mle-app/tsconfig.json b/templates/mle-app/tsconfig.json new file mode 100644 index 0000000..9c05e54 --- /dev/null +++ b/templates/mle-app/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "ESNext", + "target": "ES2020", + "outDir": "dist", + "skipLibCheck": true, + "strict": true, + "sourceMap": true + }, + "include": ["src"], + "exclude": ["node_modules"] + } \ No newline at end of file From 6c36f712494b0ec6cf0ff8a248271c09f3950e50 Mon Sep 17 00:00:00 2001 From: Dmitrii Nikeshkin Date: Thu, 3 Apr 2025 12:09:33 +0200 Subject: [PATCH 02/15] feat: added README --- templates/mle-app/README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 templates/mle-app/README.md diff --git a/templates/mle-app/README.md b/templates/mle-app/README.md new file mode 100644 index 0000000..7132092 --- /dev/null +++ b/templates/mle-app/README.md @@ -0,0 +1,34 @@ +## Overview + +Run in dev mode to generate MLE application: + +```sh +npm run dev +``` + +Answer all questions: + +```sh +? What would you like your application's name to be? +? Which template would you like to use for your project? mle-app +? Which database connection type would you like to choose? Basic Connection (Protocol, Hostname, Port, Service Name / SID) +? What is your database protocol? tcp +? What is your database hostname? +? What is your database port? +? Which service type would you like to use? Service name +? Please enter your database service name: +? What's your database username? +? What's your database password? +? Please provide full path to your SQLcl installation: +``` + +The project will be created in generated/ folder. + +Run in the project folder: + + - `npm install`: To install all required dependencies. + - `npm run cleandb`: To clean up database. Removes all created tables and modules. See src/database/creanup.sql. + - `npm run initdb`: Creates all necessary test tables and indexes. + - `npm run build`: Compiles and bundles typescript code (index.ts). Bundled code is located in dist/index.js and deployed as MLE module to the database using SQLcl (see deploy.js). Result MLE module name is **mleapp**. + +There are manual test scripts can be used to test created MLE module. They are located in test-sql folder. \ No newline at end of file From cccbb2e1ba4015ee10c8a9f3ffda25584dd069e8 Mon Sep 17 00:00:00 2001 From: Dmitrii Nikeshkin Date: Tue, 8 Apr 2025 14:08:30 +0200 Subject: [PATCH 03/15] feat: added more information in README --- templates/mle-app/README.md | 62 ++++++++++++++++++++++++++++++------- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/templates/mle-app/README.md b/templates/mle-app/README.md index 7132092..b5142b0 100644 --- a/templates/mle-app/README.md +++ b/templates/mle-app/README.md @@ -1,6 +1,53 @@ -## Overview +# MLE Template Application +## Description -Run in dev mode to generate MLE application: +The purpose of the project is to demonstrate how backend applications can be developed using [MLE](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/introduction-to-mle.html). Example application allows to perform CRUD operations on TODO list. TODO list item can belong to different category and user. Categories and Users can be created separately. + +## Content +- SQL scripts that create and cleanup database tables to store TODO list, Users, Categories (see src/database/initdb.sql and src/database/cleanup.sql) +- Demo Typescript code (see src/index.ts). The code uses MLE SQL api to execute INSERT, UPDATE, DELETE, SELECT statements to interract with datatables. +- SQL scripts to test functionality (see test-sql folder). + + +## Requirements + +- [SQLcl](https://www.oracle.com/database/sqldeveloper/technologies/sqlcl). Example application uses SQLcl to deploy generated JS code as MLE module. Path to SQLcl installation must be provided during the application creation process. + +## Getting Started + +### Setup your environment + +#### Install SQLcl + +#### Install project dependencies + +``` +npm install +``` + +#### Create required database objects + +``` +npm run initdb +``` + +Creates all necessary test tables and indexes. + +### Build, bundle and deploy source code (index.ts) + +``` +npm run build +``` + +Compiles and bundles typescript code (index.ts). Bundled code is located in dist/index.js and deployed as MLE module to the database using SQLcl (see deploy.js). Result MLE module name is **mleapp**. + +### Run your code + +Deployed module code functions can be executed via MLE call specification procedures or functions. Example of call specifications organized in packages is located in test-sql/call-specs.sql. + +## MLE Application creation process + +MLE Application can be created using dev. mode: ```sh npm run dev @@ -22,13 +69,4 @@ Answer all questions: ? Please provide full path to your SQLcl installation: ``` -The project will be created in generated/ folder. - -Run in the project folder: - - - `npm install`: To install all required dependencies. - - `npm run cleandb`: To clean up database. Removes all created tables and modules. See src/database/creanup.sql. - - `npm run initdb`: Creates all necessary test tables and indexes. - - `npm run build`: Compiles and bundles typescript code (index.ts). Bundled code is located in dist/index.js and deployed as MLE module to the database using SQLcl (see deploy.js). Result MLE module name is **mleapp**. - -There are manual test scripts can be used to test created MLE module. They are located in test-sql folder. \ No newline at end of file +The project will be created in generated/ folder. \ No newline at end of file From 39f83356c99f9e6ffea4d55910abbcbf0e33259c Mon Sep 17 00:00:00 2001 From: Dmitrii Nikeshkin Date: Tue, 8 Apr 2025 14:22:50 +0200 Subject: [PATCH 04/15] feat: removed generated files --- generators/index.js | 214 ------------------------------------ generators/run-generator.js | 17 --- 2 files changed, 231 deletions(-) delete mode 100644 generators/index.js delete mode 100644 generators/run-generator.js diff --git a/generators/index.js b/generators/index.js deleted file mode 100644 index 8653a2b..0000000 --- a/generators/index.js +++ /dev/null @@ -1,214 +0,0 @@ -"use strict"; -var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; -})(); -var __assign = (this && this.__assign) || function () { - __assign = Object.assign || function(t) { - for (var s, i = 1, n = arguments.length; i < n; i++) { - s = arguments[i]; - for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) - t[p] = s[p]; - } - return t; - }; - return __assign.apply(this, arguments); -}; -var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -}; -var __generator = (this && this.__generator) || function (thisArg, body) { - var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype); - return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; - function verb(n) { return function (v) { return step([n, v]); }; } - function step(op) { - if (f) throw new TypeError("Generator is already executing."); - while (g && (g = 0, op[0] && (_ = 0)), _) try { - if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; - if (y = 0, t) op = [op[0] & 2, t.value]; - switch (op[0]) { - case 0: case 1: t = op; break; - case 4: _.label++; return { value: op[1], done: false }; - case 5: _.label++; y = op[1]; op = [0]; continue; - case 7: op = _.ops.pop(); _.trys.pop(); continue; - default: - if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } - if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } - if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } - if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } - if (t[2]) _.ops.pop(); - _.trys.pop(); continue; - } - op = body.call(thisArg, _); - } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } - if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; - } -}; -Object.defineProperty(exports, "__esModule", { value: true }); -/* -** -** Copyright (c) 2024, Oracle and/or its affiliates. -** All rights reserved -** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -*/ -var extract_zip_1 = require("extract-zip"); -var node_fs_1 = require("node:fs"); -var node_path_1 = require("node:path"); -var node_url_1 = require("node:url"); -var yeoman_generator_1 = require("yeoman-generator"); -var __filename = (0, node_url_1.fileURLToPath)(import.meta.url); -var __dirname = node_path_1.default.dirname(__filename); -var retrieveConnectionStringDetailsFromORAFile = function (oraFilePath) { - var data = node_fs_1.default.readFileSync(oraFilePath, 'utf8'); - var protocol = data.slice((data.indexOf('protocol=') + 9), (data.indexOf(')(port'))); - var hostname = data.slice((data.indexOf('host=') + 5), (data.indexOf('))('))); - var port = data.slice((data.indexOf('port=') + 5), (data.indexOf(')(host'))); - var serviceName = data.slice((data.indexOf('service_name=') + 13), (data.indexOf('))(security'))); - return { protocol: protocol, hostname: hostname, port: port, serviceName: serviceName }; -}; -var generateConnectionString = function (protocol, hostname, port, serviceName) { return "".concat(protocol, "://").concat(hostname, ":").concat(port, "/").concat(serviceName); }; -var default_1 = /** @class */ (function (_super) { - __extends(default_1, _super); - function default_1(args, opts) { - var _this = _super.call(this, args, opts, { - customInstallTask: true - }) || this; - _this.sourceRoot(node_path_1.default.join(__dirname, '../templates')); - _this.options = __assign(__assign({}, opts), { apiConfiguration: opts.templateChoice.includes('todo') ? 'tasks' : 'connection' }); - return _this; - // this.env.options.nodePackageManager = 'npm'; - // this.env.options.cwd = path.join( process.cwd(), this.options.appName ); - } - default_1.prototype.install = function () { - this.spawnCommandSync('npm', ['install'], { - cwd: node_path_1.default.join(process.cwd(), this.options.appName) - }); - var lRevParseResult; - try { - lRevParseResult = this.spawnCommandSync('git', ['rev-parse', '--git-dir'], { - cwd: node_path_1.default.join(process.cwd(), this.options.appName), - stdio: 'pipe', - }); - } - catch (pError) { - lRevParseResult = pError; - } - if (lRevParseResult.failed) { - this.spawnCommandSync('git', ['init'], { - cwd: node_path_1.default.join(process.cwd(), this.options.appName) - }); - } - else { - this.log("Directory is already inside of the git repository \"".concat(lRevParseResult.stdout, "\". Skipping \"git init\"")); - } - }; - default_1.prototype.path = function () { - this.destinationRoot(node_path_1.default.join(process.cwd(), this.options.appName)); - }; - default_1.prototype.welcome = function () { - this.log('Generating database app...'); - }; - // copy the files in the templates/app folder to the root of appname - default_1.prototype.writing = function () { - return __awaiter(this, void 0, void 0, function () { - var walletPath, _a, protocol, hostname, port, serviceName, readme_data; - return __generator(this, function (_b) { - switch (_b.label) { - case 0: - if (!(!('connectionString' in this.options) && ('walletPath' in this.options))) return [3 /*break*/, 4]; - walletPath = node_path_1.default.join(process.cwd(), this.options.appName, 'server', 'utils', 'db', 'wallet'); - if (!this.options.walletPath.endsWith('.zip')) return [3 /*break*/, 2]; - return [4 /*yield*/, (0, extract_zip_1.default)(this.options.walletPath, { - dir: walletPath - })]; - case 1: - _b.sent(); - return [3 /*break*/, 3]; - case 2: - node_fs_1.default.cpSync(this.options.walletPath, walletPath, { recursive: true }); - _b.label = 3; - case 3: - _a = retrieveConnectionStringDetailsFromORAFile(node_path_1.default.join(walletPath, 'tnsnames.ora')), protocol = _a.protocol, hostname = _a.hostname, port = _a.port, serviceName = _a.serviceName; - this.options.connectionString = generateConnectionString(protocol, hostname, port, serviceName); - _b.label = 4; - case 4: - // Copy files that are common to all of the templates. - this.fs.copyTpl(this.templatePath(this.options.templateChoice), this.destinationPath(), { - appName: this.options.appName - }); - this.fs.copy(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/.github")), this.destinationPath('.github')); - // This copy of `eslintrc.cjs` should be removed once all templates support eslint v9 - this.fs.copy(this.templatePath("".concat(this.options.templateChoice, "/.eslintrc.cjs")), this.destinationPath('.eslintrc.cjs'), { - ignoreNoMatch: true - }); - this.fs.copy(this.templatePath("".concat(this.options.templateChoice, "/eslint.config.mjs")), this.destinationPath('eslint.config.mjs'), { - ignoreNoMatch: true - }); - this.fs.copy(this.templatePath("".concat(this.options.templateChoice, "/.gitignore.template")), this.destinationPath('.gitignore')); - /** - * The ORDS Concert App template provides: - * A markdown lint configuration file (.markdownlint.json) - * A .env.example file - * Additionally, the sample app expects that the user configures their development - * environment on their own to provide a better understanding of ords and how the - * app is structured. - * The rest of the files, like utils/* and db/* are also not needed since the sample - * app contains their own mechanisms to talk with the db. - */ - if (this.options.templateChoice.includes('ords-remix-jwt-sample')) { - this.fs.copy(this.templatePath("".concat(this.options.templateChoice, "/.markdownlint.jsonc")), this.destinationPath('.markdownlint.jsonc')); - this.fs.copy(this.templatePath("".concat(this.options.templateChoice, "/.env.example")), this.destinationPath('.env.example')); - } - else { - this.fs.copyTpl(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/").concat(node_path_1.default.basename(this.options.templateChoice) == 'node-jet' ? 'index-proxied' : 'index', ".cjs")), this.destinationPath('server/index.cjs'), this.options); - this.fs.copy(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/routes/").concat(this.options.apiConfiguration, ".cjs")), this.destinationPath("server/routes/".concat(this.options.apiConfiguration, ".cjs"))); - this.fs.copy(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/utils/db/**/*")), this.destinationPath('server/utils/db/')); - this.fs.copy(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/utils/rest-services/").concat(this.options.apiConfiguration, ".cjs")), this.destinationPath("server/utils/rest-services/".concat(this.options.apiConfiguration, ".cjs"))); - this.fs.copyTpl(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/.env.example")), this.destinationPath('.env.example'), { - appName: '', - connectionPassword: '', - connectionString: '', - connectionUsername: '', - walletPassword: '', - walletPath: '', - }); - this.fs.copyTpl(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/.env.example.").concat(('walletPath' in this.options) ? 'cloud-wallet' : 'basic')), this.destinationPath('.env'), this.options); - this.fs.copy(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/CONTRIBUTING.md")), this.destinationPath('CONTRIBUTING.md')); - readme_data = this.fs.read(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/README.md"))); - if (this.fs.exists((this.destinationPath('README.md')))) { - this.fs.append(this.destinationPath('README.md'), readme_data); - } - else { - this.fs.copy(this.templatePath("".concat(node_path_1.default.dirname(this.options.templateChoice), "/app/README.md")), this.destinationPath('README.md')); - } - } - return [2 /*return*/]; - } - }); - }); - }; - default_1.prototype.end = function () { - this.log('Application generated successfully. Run the following command: \n\ncd ' + node_path_1.default.join(process.cwd(), this.options.appName) + '\n'); - if (!this.options.templateChoice.includes('ords-remix-jwt-sample')) { } - this.log('Please check out the README file to learn how to configurate the ORDS Concert App'); - }; - return default_1; -}(yeoman_generator_1.default)); -exports.default = default_1; diff --git a/generators/run-generator.js b/generators/run-generator.js deleted file mode 100644 index 073ef45..0000000 --- a/generators/run-generator.js +++ /dev/null @@ -1,17 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.generateDatabaseApp = generateDatabaseApp; -/* -** -** Copyright (c) 2024, Oracle and/or its affiliates. -** All rights reserved -** Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/ -*/ -var yeoman_environment_1 = require("yeoman-environment"); -// import DatabaseAppGenerator from './generators/app/index.js'; -var index_js_1 = require("./index.js"); -var generatorEnvironment = yeoman_environment_1.default.createEnv(); -generatorEnvironment.registerStub(index_js_1.default, 'database-app'); -function generateDatabaseApp(opts) { - generatorEnvironment.run('database-app', opts); -} From 3ee44f910161da341cf3c44a4b4c3be5b8e59c55 Mon Sep 17 00:00:00 2001 From: Dmitrii Nikeshkin Date: Tue, 8 Apr 2025 14:26:58 +0200 Subject: [PATCH 05/15] feat: revert package.json and package-lock.json changes --- package-lock.json | 1128 +++++++++++++-------------------------------- package.json | 18 +- 2 files changed, 335 insertions(+), 811 deletions(-) diff --git a/package-lock.json b/package-lock.json index d04c8ae..9a8dc18 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,7 +26,6 @@ "@oclif/plugin-plugins": "^5.3.7", "extract-zip": "^2.0.1", "inquirer": "^9.3.6", - "mle-js": "^23.7.0", "untildify": "^5.0.0", "yeoman-environment": "^3.19.3", "yeoman-generator": "^5.10.0" @@ -46,7 +45,7 @@ "@types/yeoman-generator": "^5.2.14", "chai": "^4", "dotenv-cli": "^8.0.0", - "eslint": "^9", + "eslint": "^8", "eslint-config-oclif": "^5", "eslint-config-oclif-typescript": "^3", "eslint-config-prettier": "^9.1.0", @@ -1737,73 +1736,17 @@ "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", - "dev": true, - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.2.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/config-helpers/-/config-helpers-0.2.0.tgz", - "integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", + "espree": "^9.6.0", + "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -1811,51 +1754,53 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/@eslint/eslintrc/node_modules/ajv": { "version": "6.12.6", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/ajv/-/ajv-6.12.6.tgz", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/@eslint/eslintrc/node_modules/argparse": { "version": "2.0.1", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/argparse/-/argparse-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/brace-expansion/-/brace-expansion-1.1.11.tgz", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "engines": { - "node": ">=18" - } - }, "node_modules/@eslint/eslintrc/node_modules/js-yaml": { "version": "4.1.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/js-yaml/-/js-yaml-4.1.0.tgz", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1865,15 +1810,17 @@ }, "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { "version": "0.4.1", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/minimatch/-/minimatch-3.1.2.tgz", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -1882,34 +1829,13 @@ } }, "node_modules/@eslint/js": { - "version": "9.23.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/js/-/js-9.23.0.tgz", - "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.2.7", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", - "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, - "dependencies": { - "@eslint/core": "^0.12.0", - "levn": "^0.4.1" - }, + "license": "MIT", "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@gar/promisify": { @@ -1919,44 +1845,13 @@ "inBundle": true, "license": "MIT" }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "engines": { - "node": ">=18.18" - } - }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, - "peer": true, + "license": "Apache-2.0", "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", @@ -1968,10 +1863,10 @@ }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { "version": "1.1.11", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/brace-expansion/-/brace-expansion-1.1.11.tgz", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1979,10 +1874,10 @@ }, "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { "version": "3.1.2", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/minimatch/-/minimatch-3.1.2.tgz", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, - "peer": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -2006,20 +1901,11 @@ }, "node_modules/@humanwhocodes/object-schema": { "version": "2.0.3", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", "dev": true, - "peer": true - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", - "dev": true, - "engines": { - "node": ">=18.18" - } + "license": "BSD-3-Clause" }, "node_modules/@inquirer/checkbox": { "version": "1.5.2", @@ -5598,9 +5484,10 @@ }, "node_modules/@types/semver": { "version": "7.5.8", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@types/semver/-/semver-7.5.8.tgz", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/text-table": { "version": "0.2.5", @@ -5773,6 +5660,71 @@ "rxjs": "^6.4.0" } }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", + "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", @@ -5791,6 +5743,34 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", + "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@typescript-eslint/types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", @@ -5850,6 +5830,32 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@typescript-eslint/utils": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", + "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/types": "6.21.0", + "@typescript-eslint/typescript-estree": "6.21.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, "node_modules/@typescript-eslint/visitor-keys": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", @@ -5870,10 +5876,10 @@ }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true, - "peer": true + "license": "ISC" }, "node_modules/@vitest/expect": { "version": "3.0.4", @@ -6080,9 +6086,10 @@ }, "node_modules/acorn-jsx": { "version": "5.3.2", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -7943,10 +7950,10 @@ }, "node_modules/doctrine": { "version": "3.0.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/doctrine/-/doctrine-3.0.0.tgz", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "peer": true, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -8329,530 +8336,125 @@ "@esbuild/openbsd-x64": "0.24.2", "@esbuild/sunos-x64": "0.24.2", "@esbuild/win32-arm64": "0.24.2", - "@esbuild/win32-ia32": "0.24.2", - "@esbuild/win32-x64": "0.24.2" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "inBundle": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.23.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/eslint/-/eslint-9.23.0.tgz", - "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.2", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.23.0", - "@eslint/plugin-kit": "^0.2.7", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-config-oclif": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/eslint-config-oclif/-/eslint-config-oclif-5.2.2.tgz", - "integrity": "sha512-NNTyyolSmKJicgxtoWZ/hoy2Rw56WIoWCFxgnBkXqDgi9qPKMwZs2Nx2b6SHLJvCiWWhZhWr5V46CFPo3PSPag==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-config-xo-space": "^0.35.0", - "eslint-plugin-mocha": "^10.5.0", - "eslint-plugin-n": "^15.1.0", - "eslint-plugin-unicorn": "^48.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/eslint-config-oclif-typescript": { - "version": "3.1.14", - "resolved": "https://registry.npmjs.org/eslint-config-oclif-typescript/-/eslint-config-oclif-typescript-3.1.14.tgz", - "integrity": "sha512-YeBq5OiDRZFvfZ+wO0meF38fV06+zmEg15mnOLwkiAuUhjg2lH+UxvYA7uX2zUwR4p1WMUbfX+7CMfUwQ4TQ1A==", - "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", - "eslint-config-xo-space": "^0.35.0", - "eslint-import-resolver-typescript": "^3.7.0", - "eslint-plugin-import": "^2.31.0", - "eslint-plugin-mocha": "^10.5.0", - "eslint-plugin-n": "^15", - "eslint-plugin-perfectionist": "^2.11.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "peer": true, - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "dev": true, - "peer": true, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", - "dev": true, - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", - "dev": true, - "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "peer": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "peer": true - }, - "node_modules/eslint-config-oclif-typescript/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "peer": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "peer": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "dev": true, - "peer": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "peer": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/espree": { - "version": "9.6.1", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "peer": true, - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "peer": true, - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "peer": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "peer": true, - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" + "@esbuild/win32-ia32": "0.24.2", + "@esbuild/win32-x64": "0.24.2" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, - "peer": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "peer": true - }, - "node_modules/eslint-config-oclif-typescript/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "peer": true, - "dependencies": { - "p-locate": "^5.0.0" - }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "inBundle": true, + "license": "MIT", "engines": { "node": ">=10" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "peer": true, - "dependencies": { - "brace-expansion": "^1.1.7" }, - "engines": { - "node": "*" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" }, "engines": { - "node": ">=10" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/eslint-config-oclif": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/eslint-config-oclif/-/eslint-config-oclif-5.2.2.tgz", + "integrity": "sha512-NNTyyolSmKJicgxtoWZ/hoy2Rw56WIoWCFxgnBkXqDgi9qPKMwZs2Nx2b6SHLJvCiWWhZhWr5V46CFPo3PSPag==", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "eslint-config-xo-space": "^0.35.0", + "eslint-plugin-mocha": "^10.5.0", + "eslint-plugin-n": "^15.1.0", + "eslint-plugin-unicorn": "^48.0.1" }, "engines": { - "node": ">=10" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "peer": true, - "engines": { - "node": ">=8" + "node": ">=18.0.0" } }, - "node_modules/eslint-config-oclif-typescript/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/eslint-config-oclif-typescript": { + "version": "3.1.14", + "resolved": "https://registry.npmjs.org/eslint-config-oclif-typescript/-/eslint-config-oclif-typescript-3.1.14.tgz", + "integrity": "sha512-YeBq5OiDRZFvfZ+wO0meF38fV06+zmEg15mnOLwkiAuUhjg2lH+UxvYA7uX2zUwR4p1WMUbfX+7CMfUwQ4TQ1A==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, - "peer": true, + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@typescript-eslint/eslint-plugin": "^6.21.0", + "@typescript-eslint/parser": "^6.21.0", + "eslint-config-xo-space": "^0.35.0", + "eslint-import-resolver-typescript": "^3.7.0", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-mocha": "^10.5.0", + "eslint-plugin-n": "^15", + "eslint-plugin-perfectionist": "^2.11.0" }, "engines": { - "node": ">=8" - } - }, - "node_modules/eslint-config-oclif-typescript/node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "peer": true, - "engines": { - "node": ">=10" + "node": ">=18.0.0" } }, "node_modules/eslint-config-prettier": { @@ -9230,83 +8832,6 @@ } } }, - "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/scope-manager": { - "version": "7.18.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/scope-manager/-/scope-manager-7.18.0.tgz", - "integrity": "sha512-jjhdIE/FPF2B7Z1uzc6i3oWKbGcHb87Qw7AWj6jmEqNOfDFbJWtjt/XfwCpvNkpGWlcJaog5vTR+VV8+w9JflA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - } - }, - "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/types": { - "version": "7.18.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/types/-/types-7.18.0.tgz", - "integrity": "sha512-iZqi+Ds1y4EDYUtlOOC+aUmxnE9xS/yCigkjA7XpTKV6nCBd3Hp/PRGGmdwnfkV2ThMyYldP1wRpm/id99spTQ==", - "dev": true, - "engines": { - "node": "^18.18.0 || >=20.0.0" - } - }, - "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/typescript-estree": { - "version": "7.18.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/typescript-estree/-/typescript-estree-7.18.0.tgz", - "integrity": "sha512-aP1v/BSPnnyhMHts8cf1qQ6Q1IFwwRvAQGRvBFkWlo3/lH29OXA3Pts+c10nxRxIBrDnoMqzhgdwVe5f2D6OzA==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/visitor-keys": "7.18.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^1.3.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/utils": { - "version": "7.18.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/utils/-/utils-7.18.0.tgz", - "integrity": "sha512-kK0/rNa2j74XuHVcoCZxdFBMF+aq/vH83CXAOHieC+2Gis4mF8jJXT5eAfyD3K0sAxtPuwxaIOIOvhwzVDt/kw==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.18.0", - "@typescript-eslint/types": "7.18.0", - "@typescript-eslint/typescript-estree": "7.18.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "peerDependencies": { - "eslint": "^8.56.0" - } - }, - "node_modules/eslint-plugin-perfectionist/node_modules/@typescript-eslint/visitor-keys": { - "version": "7.18.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/@typescript-eslint/visitor-keys/-/visitor-keys-7.18.0.tgz", - "integrity": "sha512-cDF0/Gf81QpY3xYyJKDV14Zwdmid5+uuENhjH2EqFaF0ni+yAyq/LzMaIJdhNJXZI7uLzwIlA+V7oWoyn6Curg==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.18.0", - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - } - }, "node_modules/eslint-plugin-unicorn": { "version": "48.0.1", "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-48.0.1.tgz", @@ -9341,16 +8866,20 @@ } }, "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-utils": { @@ -9412,6 +8941,13 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -9440,15 +8976,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, "node_modules/eslint/node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -9466,6 +8993,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/eslint/node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -9571,26 +9111,21 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", - "dev": true, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, "node_modules/esprima": { @@ -9622,9 +9157,10 @@ }, "node_modules/esrecurse": { "version": "4.3.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/esrecurse/-/esrecurse-4.3.0.tgz", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -10028,15 +9564,16 @@ } }, "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, + "license": "MIT", "dependencies": { - "flat-cache": "^4.0.0" + "flat-cache": "^3.0.4" }, "engines": { - "node": ">=16.0.0" + "node": "^10.12.0 || >=12.0.0" } }, "node_modules/filelist": { @@ -10178,23 +9715,26 @@ } }, "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.4" + "keyv": "^4.5.3", + "rimraf": "^3.0.2" }, "engines": { - "node": ">=16" + "node": "^10.12.0 || >=12.0.0" } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true, + "license": "ISC" }, "node_modules/for-each": { "version": "0.3.4", @@ -11587,10 +11127,10 @@ }, "node_modules/is-path-inside": { "version": "3.0.3", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/is-path-inside/-/is-path-inside-3.0.3.tgz", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, - "peer": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -12754,11 +12294,6 @@ "node": ">=10" } }, - "node_modules/mle-js": { - "version": "23.7.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/mle-js/-/mle-js-23.7.0.tgz", - "integrity": "sha512-tsn+judDyHMWNapMDhgorAvouKCyA0V50U3oxrafWaEDjeB2XsWhZLxpw2L4NGWbBWuzPT7t4MUG0EBQYe57ow==" - }, "node_modules/mocha": { "version": "10.8.2", "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", @@ -19933,10 +19468,11 @@ } }, "node_modules/tr46": { - "version": "5.1.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/tr46/-/tr46-5.1.0.tgz", - "integrity": "sha512-IUWnUK7ADYR5Sl1fZlO1INDUhVhatWl7BtJWsIhwJ0UAK7ilzzIa8uIqOO/aYVWHZPJkKbEL+362wrzoeRF7bw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", "inBundle": true, + "license": "MIT", "dependencies": { "punycode": "^2.3.1" }, @@ -20924,12 +20460,12 @@ } }, "node_modules/whatwg-url": { - "version": "14.2.0", - "resolved": "https://artifacthub-phx.oci.oraclecorp.com/api/npm/npmjs-remote/whatwg-url/-/whatwg-url-14.2.0.tgz", - "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", "inBundle": true, "dependencies": { - "tr46": "^5.1.0", + "tr46": "^5.0.0", "webidl-conversions": "^7.0.0" }, "engines": { @@ -22805,4 +22341,4 @@ } } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index 25b91e5..d5fed82 100644 --- a/package.json +++ b/package.json @@ -69,7 +69,6 @@ "@oclif/plugin-plugins": "^5.3.7", "extract-zip": "^2.0.1", "inquirer": "^9.3.6", - "mle-js": "^23.7.0", "untildify": "^5.0.0", "yeoman-environment": "^3.19.3", "yeoman-generator": "^5.10.0" @@ -97,7 +96,7 @@ "@types/yeoman-generator": "^5.2.14", "chai": "^4", "dotenv-cli": "^8.0.0", - "eslint": "^9", + "eslint": "^8", "eslint-config-oclif": "^5", "eslint-config-oclif-typescript": "^3", "eslint-config-prettier": "^9.1.0", @@ -114,16 +113,5 @@ }, "overrides": { "whatwg-url": "^14.1.0" - }, - "bundleDependencies": [ - "@inquirer/prompts", - "@oclif/core", - "@oclif/plugin-help", - "@oclif/plugin-plugins", - "extract-zip", - "inquirer", - "untildify", - "yeoman-environment", - "yeoman-generator" - ] -} + } +} \ No newline at end of file From f91838361d83df960da36ea7474e312109b859b2 Mon Sep 17 00:00:00 2001 From: Dmitrii Nikeshkin Date: Fri, 11 Apr 2025 16:01:52 +0200 Subject: [PATCH 06/15] feat: addressing review comments --- .github/dependabot.yml | 5 + README.md | 1 + generators/index.ts | 9 +- src/index.ts | 5 +- templates/app/.env.example.basic | 4 +- templates/mle-app/.env.example | 12 + templates/mle-app/README.md | 199 ++++++++++++--- templates/mle-app/deploy.js | 43 ++-- templates/mle-app/package.json | 7 +- templates/mle-app/src/database/cleanup.sql | 4 +- templates/mle-app/src/database/db.js | 29 +-- templates/mle-app/src/database/initdb.sql | 4 +- templates/mle-app/test-sql/module.sql | 274 --------------------- templates/mle-app/tsconfig.json | 6 +- 14 files changed, 233 insertions(+), 369 deletions(-) create mode 100644 templates/mle-app/.env.example delete mode 100644 templates/mle-app/test-sql/module.sql diff --git a/.github/dependabot.yml b/.github/dependabot.yml index eb621ee..1c14805 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -188,3 +188,8 @@ updates: - "templates-ords-remix" commit-message: prefix: fix(deps) + + - package-ecosystem: "npm" + directory: "/templates/mle-app/" + schedule: + interval: "monthly" \ No newline at end of file diff --git a/README.md b/README.md index 48215e3..92b4718 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ The package offers the following templates, some of them connect to the database - `node-angular`: A starter template that uses Node.js and [Angular](https://angular.dev/). It is built by [Angular CLI](https://github.com/angular/angular-cli). (New in `v1.2.0`) - `node-react-todo`: A simple task manager template that uses Node.js and [React](https://react.dev/). It demonstrates the use of the database for Create, Read, Update and Delete (CRUD) operations. It is built by [vite](https://vitejs.dev/). - `ords-remix-jwt-sample`: A full stack Concert Application made with [Remix](https://remix.run/) that showcases the [Oracle REST Data Services](https://www.oracle.com/database/technologies/appdev/rest.html) functionalities. Some extra configuration is required, learn more about it in the `ords-remix-jwt-sample` [Getting Started Guide](/templates/ords-remix-jwt-sample/README.md#getting-started). +- `mle-app`: A starter template application that demonstrates how backend applications can be developed using [Oracle Database Multilingual Engine (MLE)](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/introduction-to-mle.html). Requires SQLcl to be installed, for more information please read [README](/templates/mle-app/README.md) Each of the templates include documentation for you to get started with them, as well as NPM scripts for you to use right after generating the application. diff --git a/generators/index.ts b/generators/index.ts index 2290d8b..5d185e5 100644 --- a/generators/index.ts +++ b/generators/index.ts @@ -142,6 +142,12 @@ export default class extends Generator { this.templatePath( `${this.options.templateChoice}/.env.example` ), this.destinationPath( '.env.example' ), ); + } else if (this.options.templateChoice.includes('mle-app' )) { + this.fs.copyTpl( + this.templatePath( `${this.options.templateChoice}/.env.example` ), + this.destinationPath( '.env' ), + this.options + ); } else { this.fs.copyTpl( this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/${ path.basename( this.options.templateChoice ) == 'node-jet' ? 'index-proxied' : 'index' }.cjs` ), @@ -169,8 +175,7 @@ export default class extends Generator { connectionString: '', connectionUsername: '', walletPassword: '', - walletPath: '', - sqlclPath: '', + walletPath: '' } ); this.fs.copyTpl( diff --git a/src/index.ts b/src/index.ts index 5ae933a..e03e971 100644 --- a/src/index.ts +++ b/src/index.ts @@ -391,7 +391,8 @@ export default class Generate extends Command { }; // Ask the user for the database connection type (Either basic connection or a connection using a cloud wallet). - const databaseConnectionType = connectionType === '' && templateChoice !== 'ords-remix-jwt-sample' ? await select( + const databaseConnectionType = templateChoice === 'mle-app' ? 'basic' + : (connectionType === '' && templateChoice !== 'ords-remix-jwt-sample' ? await select( { message: 'Which database connection type would you like to choose?', choices: [ @@ -406,7 +407,7 @@ export default class Generate extends Command { ], default: 'walletPath' } - ) : connectionType; + ) : connectionType); // If the user has chosen the basic connection type, then we ask for the protocol, hostname, port and service name / SID. if ( databaseConnectionType === 'basic' ) { diff --git a/templates/app/.env.example.basic b/templates/app/.env.example.basic index 45f5255..f6cf73d 100644 --- a/templates/app/.env.example.basic +++ b/templates/app/.env.example.basic @@ -7,6 +7,4 @@ CONNECT_STRING=<%= connectionString %> # Optional HTTP Proxy Settings # HTTPS_PROXY= -# HTTPS_PROXY_PORT= - -SQL_CL_PATH=<%= sqlclPath %> +# HTTPS_PROXY_PORT= \ No newline at end of file diff --git a/templates/mle-app/.env.example b/templates/mle-app/.env.example new file mode 100644 index 0000000..45f5255 --- /dev/null +++ b/templates/mle-app/.env.example @@ -0,0 +1,12 @@ +# Database User +DB_USER=<%= connectionUsername %> +# Database User +DB_PASSWORD=<%= connectionPassword %> +# Connection string to your ADB instance +CONNECT_STRING=<%= connectionString %> + +# Optional HTTP Proxy Settings +# HTTPS_PROXY= +# HTTPS_PROXY_PORT= + +SQL_CL_PATH=<%= sqlclPath %> diff --git a/templates/mle-app/README.md b/templates/mle-app/README.md index b5142b0..e5380b8 100644 --- a/templates/mle-app/README.md +++ b/templates/mle-app/README.md @@ -1,72 +1,197 @@ # MLE Template Application + ## Description -The purpose of the project is to demonstrate how backend applications can be developed using [MLE](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/introduction-to-mle.html). Example application allows to perform CRUD operations on TODO list. TODO list item can belong to different category and user. Categories and Users can be created separately. +This project demonstrates how backend applications can be developed using [Oracle Database Multilingual Engine (MLE)](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/introduction-to-mle.html). + +It implements a simple **TODO list** with full **CRUD** functionality. Each TODO item can be associated with a **User** and a **Category**. Users and Categories can be created independently. + +--- + +## Project Structure + +- `src/index.ts` + Main TypeScript file. It uses MLE SQL API to perform database operations (`INSERT`, `SELECT`, `UPDATE`, `DELETE`). + +- `src/database/initdb.sql` + SQL script to initialize the required database tables (TODOs, Users, Categories). + +- `src/database/cleanup.sql` + SQL script to drop all created database tables. -## Content -- SQL scripts that create and cleanup database tables to store TODO list, Users, Categories (see src/database/initdb.sql and src/database/cleanup.sql) -- Demo Typescript code (see src/index.ts). The code uses MLE SQL api to execute INSERT, UPDATE, DELETE, SELECT statements to interract with datatables. -- SQL scripts to test functionality (see test-sql folder). +- `test-sql/call-specs.sql` + SQL file that defines PL/SQL **call specifications** to wrap MLE module functions, allowing them to be invoked from SQL. It uses `MLEAPP` as default MLE module name. The purpose of the script is to demonstrate how MLE module functions can be executed. +--- ## Requirements -- [SQLcl](https://www.oracle.com/database/sqldeveloper/technologies/sqlcl). Example application uses SQLcl to deploy generated JS code as MLE module. Path to SQLcl installation must be provided during the application creation process. +- [Oracle Database 23c](https://www.oracle.com/database/) or later (with MLE support). +- [SQLcl](https://www.oracle.com/database/sqldeveloper/technologies/sqlcl): used to deploy the bundled JS code as an MLE module. +- [Node.js](https://nodejs.org/) (v16+ recommended). + +--- ## Getting Started -### Setup your environment +### 1. Install SQLcl -#### Install SQLcl +Download and install SQLcl from Oracle: -#### Install project dependencies +🔗 https://www.oracle.com/database/sqldeveloper/technologies/sqlcl -``` +Make sure it is unzipped and accessible via its full path (e.g. `/Users/yourname/sqlcl`). + +--- + +### 2. Install Project Dependencies + +```bash npm install ``` -#### Create required database objects +Installs all necessary Node.js dependencies including [esbuild](https://esbuild.github.io/). -``` +### 3. Initialize the Database + +```bash npm run initdb ``` -Creates all necessary test tables and indexes. +This creates the required tables and indexes: +- `todo_list` +- `users` +- `categories` -### Build, bundle and deploy source code (index.ts) +Make sure your environment variables or `.env` file provides connection details: +- `DB_USER` +- `DB_PASSWORD` +- `CONNECT_STRING` +- `SQL_CL_PATH` -``` +### 4. Build the Source Code + +```bash npm run build ``` -Compiles and bundles typescript code (index.ts). Bundled code is located in dist/index.js and deployed as MLE module to the database using SQLcl (see deploy.js). Result MLE module name is **mleapp**. +This compiles and bundles src/index.ts using esbuild. The output file is written to: + +``` +dist/index.js +``` + +The bundled file is compatible with Oracle's MLE module format. + +### 5. Deploy the MLE Module + +```bash +npm run deploy +``` +This uses SQLcl to upload and register the bundled JS code as an MLE module in the database. +By default, the module is named `mleapp`. To specify a custom module name: + +```bash +npm run deploy -- +``` + +Example: +```bash +npm run deploy -- my-custom-module +``` + +### 6. Test MLE Module Functions + +Once your MLE module is deployed, you can test its functionality using **call specifications** defined in the `test-sql/call-specs.sql` file. + +Call specifications allow you to invoke MLE module functions as PL/SQL procedures or functions. + +#### Example: Testing User Package + +The following SQL code demonstrates how to invoke the MLE module function `newUser` and other functions via the `user_package` package. + +```sql +-- Create the user_package package (wraps MLE functions) +CREATE OR REPLACE PACKAGE user_package AS + PROCEDURE newUserFunc(name IN VARCHAR2); + FUNCTION getUser(id IN NUMBER) RETURN VARCHAR2; + PROCEDURE updateUser(id IN NUMBER, name IN VARCHAR2); + PROCEDURE deleteUser(id IN NUMBER); +END user_package; +/ +CREATE OR REPLACE PACKAGE BODY user_package AS + PROCEDURE newUserFunc(name IN VARCHAR2) + AS MLE MODULE MLEAPP SIGNATURE 'newUser(string)'; + + FUNCTION getUser(id IN NUMBER) RETURN VARCHAR2 + AS MLE MODULE MLEAPP SIGNATURE 'getUser(number)'; + + PROCEDURE updateUser(id IN NUMBER, name IN VARCHAR2) + AS MLE MODULE MLEAPP SIGNATURE 'updateUser(number, string)'; + + PROCEDURE deleteUser(id IN NUMBER) + AS MLE MODULE MLEAPP SIGNATURE 'deleteUser(number)'; +END user_package; +/ + +-- Call MLE functions via the user_package +EXECUTE USER_PACKAGE.NEWUSERFUNC('BLABLA'); + +SELECT USER_PACKAGE.GETUSER(5); + +EXECUTE USER_PACKAGE.UPDATEUSER(5,'BLABLA'); -### Run your code +SELECT USER_PACKAGE.GETUSER(5); -Deployed module code functions can be executed via MLE call specification procedures or functions. Example of call specifications organized in packages is located in test-sql/call-specs.sql. +EXECUTE USER_PACKAGE.DELETEUSER(5); -## MLE Application creation process +SELECT USER_PACKAGE.GETUSER(5); +``` + +You can use tools like **SQLcl** or **SQL Developer** to execute these SQL commands. + +#### **Test via Oracle APEX (Application Express)**: +Oracle APEX allows you to create web applications that interact with MLE functions. You can create an APEX page that allows users to test CRUD operations, such as adding or displaying TODO items. + +**Link**: +[Oracle APEX](https://apex.oracle.com) -MLE Application can be created using dev. mode: +#### **Test via Oracle REST Data Services (ORDS)**: +If you’ve exposed your MLE functions as RESTful services using ORDS, you can test them via HTTP requests. For instance, you might have a REST endpoint for creating a TODO item. -```sh -npm run dev +**Example**: + +```bash +curl -X POST "https://yourserver.com/ords/mle/todo/create" -H "Content-Type: application/json" -d '{"todo_text": "Buy groceries", "user_id": "user123", "category": "personal"}' ``` -Answer all questions: - -```sh -? What would you like your application's name to be? -? Which template would you like to use for your project? mle-app -? Which database connection type would you like to choose? Basic Connection (Protocol, Hostname, Port, Service Name / SID) -? What is your database protocol? tcp -? What is your database hostname? -? What is your database port? -? Which service type would you like to use? Service name -? Please enter your database service name: -? What's your database username? -? What's your database password? -? Please provide full path to your SQLcl installation: +**Links**: +- [Less-well-known features of Multilingual Engine: Document API](https://blogs.oracle.com/developers/post/lesswellknown-features-of-multilingual-engine-document-api) +- [ORDS Documentation](https://www.oracle.com/database/technologies/appdev/rest.html) +--- + +### 7. Clean Up the Database + +```bash +npm run cleandb ``` -The project will be created in generated/ folder. \ No newline at end of file +This will drop all objects (tables, types, etc.) created by the `initdb` script. + +--- + +## Links for Further Reading + +- [Oracle MLE Documentation](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/introduction-to-mle.html) +- [SQLcl Documentation](https://www.oracle.com/database/sqldeveloper/technologies/sqlcl.html) +- [PL/SQL User Guide](https://docs.oracle.com/en/database/oracle/oracle-database/) +- [Oracle APEX Documentation](https://docs.oracle.com/en/database/oracle/application-express/) +- [ORDS Documentation](https://docs.oracle.com/en/database/oracle/application-express/) +- [SQL Developer](https://www.oracle.com/database/sqldeveloper/) +- [SQL Developer for VSCode](https://www.oracle.com/database/sqldeveloper/vscode/) +--- + + +## Feedback + +Feel free to open issues or suggestions if you want to improve this template. Contributions are welcome! \ No newline at end of file diff --git a/templates/mle-app/deploy.js b/templates/mle-app/deploy.js index 6dbf136..7a76551 100644 --- a/templates/mle-app/deploy.js +++ b/templates/mle-app/deploy.js @@ -1,30 +1,19 @@ -const esbuild = require('esbuild'); const { execSync } = require("child_process"); -const dotenv = require("dotenv"); - -dotenv.config(); -const { DB_USER, DB_PASSWORD, CONNECT_STRING, SQL_CL_PATH } = process.env; +const path = require("path"); +const fs = require("fs"); +const os = require("os"); const bundlePath = 'dist/index.js'; -esbuild.build({ - entryPoints: ['src/index.ts'], - bundle: true, - minify: false, - platform: 'neutral', - format: 'esm', - outfile: bundlePath, -}).then(() => { - const sqlclCommand = `"${SQL_CL_PATH}/bin/sql" ${DB_USER}/${DB_PASSWORD}@${CONNECT_STRING} < { - console.log(e); - process.exit(1) -}); \ No newline at end of file +let moduleName = process.argv[2]; + +if (!moduleName) { + moduleName = "mleapp"; +} + +const tempSqlPath = path.join(os.tmpdir(), `create_module_${Date.now()}.sql`); +fs.writeFileSync(tempSqlPath, ` +mle create-module -filename ${bundlePath} -module-name ${moduleName}; +EXIT; +`); + +execSync(`node src/database/db.js ${tempSqlPath}`, { stdio: 'inherit' }); \ No newline at end of file diff --git a/templates/mle-app/package.json b/templates/mle-app/package.json index 4e74205..577314b 100644 --- a/templates/mle-app/package.json +++ b/templates/mle-app/package.json @@ -7,8 +7,9 @@ "esbuild": "0.25.1" }, "scripts": { - "build": "node deploy.js", - "initdb": "node src/database/db.js initdb.sql", - "cleandb": "node src/database/db.js cleanup.sql" + "build": "esbuild src/index.ts --bundle --minify=false --platform=neutral --format=esm --outfile=dist/index.js", + "deploy": "node deploy.js", + "initdb": "node src/database/db.js src/database/initdb.sql", + "cleandb": "node src/database/db.js src/database/cleanup.sql" } } \ No newline at end of file diff --git a/templates/mle-app/src/database/cleanup.sql b/templates/mle-app/src/database/cleanup.sql index 8d62801..7a737e8 100644 --- a/templates/mle-app/src/database/cleanup.sql +++ b/templates/mle-app/src/database/cleanup.sql @@ -1,4 +1,4 @@ -drop mle module mleapp; drop table todo_list; drop table categories; -drop table users; \ No newline at end of file +drop table users; +EXIT; \ No newline at end of file diff --git a/templates/mle-app/src/database/db.js b/templates/mle-app/src/database/db.js index dea6954..5e28d8a 100644 --- a/templates/mle-app/src/database/db.js +++ b/templates/mle-app/src/database/db.js @@ -1,29 +1,26 @@ const { execSync } = require("child_process"); -const fs = require("fs"); const path = require("path"); const dotenv = require("dotenv"); var args = process.argv.slice(2); +const os = require("os"); dotenv.config(); const { DB_USER, DB_PASSWORD, CONNECT_STRING, SQL_CL_PATH } = process.env; -const dbFolder = path.resolve("src", "database"); +const sqlclPath = path.normalize(SQL_CL_PATH); +const sqlExecutable = path.join(sqlclPath, 'bin', os.platform() === 'win32' ? 'sql.exe' : 'sql'); -console.log("Executing database script..."); - -const filePath = path.join(dbFolder, args[0]); +const filePath = args[0]; console.log(`Executing: ${filePath}...`); -const sqlclCommand = `"${SQL_CL_PATH}/bin/sql" -S ${DB_USER}/${DB_PASSWORD}@${CONNECT_STRING} @${filePath} < Date: Tue, 15 Apr 2025 14:27:43 +0200 Subject: [PATCH 07/15] feat: enable the use of wallets and Autonomous DB --- src/index.ts | 5 ++--- templates/mle-app/.env.example | 13 ++++++++++--- templates/mle-app/src/database/db.js | 27 +++++++++++++++++++++++---- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/src/index.ts b/src/index.ts index e03e971..5ae933a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -391,8 +391,7 @@ export default class Generate extends Command { }; // Ask the user for the database connection type (Either basic connection or a connection using a cloud wallet). - const databaseConnectionType = templateChoice === 'mle-app' ? 'basic' - : (connectionType === '' && templateChoice !== 'ords-remix-jwt-sample' ? await select( + const databaseConnectionType = connectionType === '' && templateChoice !== 'ords-remix-jwt-sample' ? await select( { message: 'Which database connection type would you like to choose?', choices: [ @@ -407,7 +406,7 @@ export default class Generate extends Command { ], default: 'walletPath' } - ) : connectionType); + ) : connectionType; // If the user has chosen the basic connection type, then we ask for the protocol, hostname, port and service name / SID. if ( databaseConnectionType === 'basic' ) { diff --git a/templates/mle-app/.env.example b/templates/mle-app/.env.example index 45f5255..b0f1753 100644 --- a/templates/mle-app/.env.example +++ b/templates/mle-app/.env.example @@ -1,12 +1,19 @@ +# Path to database wallet +WALLET_PATH=<%= walletPath %> + + # Database User DB_USER=<%= connectionUsername %> -# Database User +# Database User Password DB_PASSWORD=<%= connectionPassword %> -# Connection string to your ADB instance +# Connection string to your Autonomous Database/ +# Oracle Database Free instance CONNECT_STRING=<%= connectionString %> # Optional HTTP Proxy Settings # HTTPS_PROXY= # HTTPS_PROXY_PORT= -SQL_CL_PATH=<%= sqlclPath %> +# Path to your local SQL Developer Command Line +# installation +SQL_CL_PATH=<%= sqlclPath %> \ No newline at end of file diff --git a/templates/mle-app/src/database/db.js b/templates/mle-app/src/database/db.js index 5e28d8a..ba5bed4 100644 --- a/templates/mle-app/src/database/db.js +++ b/templates/mle-app/src/database/db.js @@ -3,24 +3,43 @@ const path = require("path"); const dotenv = require("dotenv"); var args = process.argv.slice(2); const os = require("os"); +const fs = require("fs"); dotenv.config(); -const { DB_USER, DB_PASSWORD, CONNECT_STRING, SQL_CL_PATH } = process.env; +// using dotenv, read the environment variables defined in the generated .env file +const { WALLET_PATH, DB_USER, DB_PASSWORD, CONNECT_STRING, SQL_CL_PATH } = process.env; +// sqlclPath is a mandatory field, therefore must exist const sqlclPath = path.normalize(SQL_CL_PATH); const sqlExecutable = path.join(sqlclPath, 'bin', os.platform() === 'win32' ? 'sql.exe' : 'sql'); +// connecting to an Autonomous Database using the EZConnect string requires TNS_ADMIN +// the @create-database-app initialisation routine will unzip a wallet to +// ./server/utils/db/wallet. Optionally use the -tnsadmin flag to SQLcl to point it +// to the unzipped wallet directory. +if (WALLET_PATH.length > 0) { + process.env.TNS_ADMIN = path.join(process.cwd(), path.normalize('./server/utils/db/wallet')) +} + +// make sure the file supposed to be executed does exist const filePath = args[0]; +if (! fs.existsSync(filePath)) { + throw new Error(`file ${filePath} does not exist`); +} + console.log(`Executing: ${filePath}...`); +const sqlclCommand = `"${sqlExecutable}" -S /nolog <<'EOF' +connect ${DB_USER}/${DB_PASSWORD}@${CONNECT_STRING} +start ${filePath} +EOF`; -const sqlclCommand = `"${sqlExecutable}" -S ${DB_USER}/${DB_PASSWORD}@${CONNECT_STRING} @${filePath}`; try { execSync(sqlclCommand, { stdio: 'inherit', shell: true }); - console.log("Database script was executed successfully."); + console.log(`Database script ${filePath} was successfully executed`); } catch (error) { - console.error("Deployment failed:", error.message); + console.error("Deployment failed, check output"); } \ No newline at end of file From 80ef9d2787a702221bd867a899ddf1b8f88fc97a Mon Sep 17 00:00:00 2001 From: martin bach Date: Tue, 15 Apr 2025 16:15:57 +0200 Subject: [PATCH 08/15] doc: more detailed readme for mle-app --- templates/mle-app/README.md | 184 ++++++++++++++++++++++-------------- 1 file changed, 114 insertions(+), 70 deletions(-) diff --git a/templates/mle-app/README.md b/templates/mle-app/README.md index e5380b8..eae8e37 100644 --- a/templates/mle-app/README.md +++ b/templates/mle-app/README.md @@ -1,93 +1,134 @@ -# MLE Template Application + + +# In-Database JavaScript Template Application + +A small, database-centric application template for Typescript and JavaScript developers. ## Description -This project demonstrates how backend applications can be developed using [Oracle Database Multilingual Engine (MLE)](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/introduction-to-mle.html). +This project demonstrates how to develop data-centric applications within a database using [Multilingual Engine (MLE)](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/introduction-to-mle.html)/JavaScript. The code example requires you to use an Oracle Database 23ai instance. If you don't have one already, you may want to try [Oracle Database Free](https://www.oracle.com/database/free/). The [get started](https://www.oracle.com/database/free/get-started/) page lists multiple alternatives, including container images. Alternatively, you can use an [Always Free Autonomous Database](https://docs.oracle.com/en/cloud/paas/autonomous-database/serverless/adbsb/autonomous-always-free.html). + +The application template implements a simple **TODO list** with full **CRUD** (create, update, read, delete) functionality. Different users can maintain their respective TODO lists. Each list can have tasks pertaining to a category. It is possible to also assign a priority to a task. This application implements the necessary code to maintain TODO lists, there is no graphical user frontend associated with the application. + +`src/index.ts` is the main component in the template. It defines the entire application logic in Typescript. This allows developers to benefit from a wide range of tools in the Typescript eco-system, most notably: -It implements a simple **TODO list** with full **CRUD** functionality. Each TODO item can be associated with a **User** and a **Category**. Users and Categories can be created independently. +- type checking +- syntax highlighting +- tab completion based on the [MLE type declarations](https://oracle-samples.github.io/mle-modules/) +- linting +- formatting +- many others... ---- +The Oracle Database engine does not support Typescript input directly, therefore the file needs to undergo transpilation to JavaScript before it can be deployed. This is taken care off in the `package.json`'s _build_ step. Here is a list of further actions available in `package.json`: -## Project Structure +- build: transpiles the Typescript file to JavaScript based on an opinionated `tsconfig.json` +- deploy: deploys the transpiled file into the database +- initdb: creates the necessary tables in the database +- cleandb: tears the application down (use with care!) -- `src/index.ts` - Main TypeScript file. It uses MLE SQL API to perform database operations (`INSERT`, `SELECT`, `UPDATE`, `DELETE`). +You typically start by deploying the schema objects before transpiling and deploying the JavaScript code. -- `src/database/initdb.sql` - SQL script to initialize the required database tables (TODOs, Users, Categories). +### Project Structure -- `src/database/cleanup.sql` - SQL script to drop all created database tables. +The project is structured as follows: -- `test-sql/call-specs.sql` - SQL file that defines PL/SQL **call specifications** to wrap MLE module functions, allowing them to be invoked from SQL. It uses `MLEAPP` as default MLE module name. The purpose of the script is to demonstrate how MLE module functions can be executed. +| Source File | Used For | +| -- | -- | +| `src/index.ts` | This file is the main TypeScript file containing the application logic. It uses MLE SQL API to perform database operations (`INSERT`, `SELECT`, `UPDATE`, `DELETE`). | +| `src/database/initdb.sql` | A short SQL script to initialize the required database tables (TODOs, Users, Categories). | +| `src/database/cleanup.sql` | A SQL script to drop all created database tables. Use with care! | +| `test-sql/call-specs.sql` | The final SQL file defines PL/SQL [call specifications](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/call-specifications-functions.html) to wrap MLE module functions, allowing them to be invoked from SQL. It uses `MLEAPP` as default MLE module name. The purpose of the script is to demonstrate how MLE module functions can be executed. | ---- +### Requirements -## Requirements +You need the following components to use this application template. -- [Oracle Database 23c](https://www.oracle.com/database/) or later (with MLE support). -- [SQLcl](https://www.oracle.com/database/sqldeveloper/technologies/sqlcl): used to deploy the bundled JS code as an MLE module. -- [Node.js](https://nodejs.org/) (v16+ recommended). +- A running [Oracle Database 23ai](https://www.oracle.com/database/) instance. +- [SQL Developer Command Line](https://www.oracle.com/database/sqldeveloper/technologies/sqlcl), aka _sqlcl_ is used to deploy the bundled JavaScript code as an MLE module in the database. +- [Node.js](https://nodejs.org/) to create this application template. ---- +An Always Free Autonomous Database offers the quickest way to get started. If you would like to use {Docker,Podman} Compose to get up-and-running quickly, you can refer to [Server-Side JavaScript driven by node-express](https://blogs.oracle.com/developers/post/serverside-javascript-driven-by-nodeexpress) for reference. ## Getting Started -### 1. Install SQLcl +If you haven't already, please download and install SQLcl from Oracle: -Download and install SQLcl from Oracle: +🔗 -🔗 https://www.oracle.com/database/sqldeveloper/technologies/sqlcl +Make sure it is unzipped and accessible via its full path (e.g. `/Users/yourname/sqlcl`). Take a note of the top-level directory where you unzipped the file, you will need this when creating the application. -Make sure it is unzipped and accessible via its full path (e.g. `/Users/yourname/sqlcl`). +### 1. Create the application + +Use the `@create-database-app` command as described in the [top-level readme](https://github.com/oracle/create-database-app/blob/main/README.md) to set up your application. You will be prompted to make a number of choices. Make sure you select `mle-app` as shown here: + +``` +? What would you like your application's name to be? demo +? Which template would you like to use for your project? + node-angular + node-react-todo + ords-remix-jwt-sample +❯ mle-app + node-vanilla + node-react + node-vue +(Use arrow keys to reveal more choices) +This creates an empty project with MLE and Oracle database connection starter code. +``` ---- +Depending on your database configuration you can choose between a TCP connection to a database, or use a Wallet to connect to an Autonomous Database. ### 2. Install Project Dependencies +Install the necessary modules. + ```bash npm install ``` -Installs all necessary Node.js dependencies including [esbuild](https://esbuild.github.io/). +The `npm` command triggers the installation of all necessary Node.js dependencies, including [esbuild](https://esbuild.github.io/). ### 3. Initialize the Database +The application stores TODO items in various tables. These must be deployed first, before switching the focus to the application code. + ```bash npm run initdb ``` This creates the required tables and indexes: + - `todo_list` - `users` - `categories` Make sure your environment variables or `.env` file provides connection details: + - `DB_USER` - `DB_PASSWORD` - `CONNECT_STRING` - `SQL_CL_PATH` +- `WALLET_PATH` (optional) + +You should have been prompted for those values upon project initialisation. ### 4. Build the Source Code +Oracle Database 23ai requires you to create JavaScript modules. The Typescript code must first be transpiled. + ```bash npm run build ``` -This compiles and bundles src/index.ts using esbuild. The output file is written to: - -``` -dist/index.js -``` - -The bundled file is compatible with Oracle's MLE module format. +This compiles and bundles `src/index.ts` using esbuild. The output file is written to `dist/index.js`. The resulting file is created using ECMAScript syntax, therefore complying with the requirements set out by MLE. ### 5. Deploy the MLE Module +Finally you can deploy the transpiled JavaScript code using the `deploy` script: + ```bash npm run deploy ``` + This uses SQLcl to upload and register the bundled JS code as an MLE module in the database. By default, the module is named `mleapp`. To specify a custom module name: @@ -96,30 +137,24 @@ npm run deploy -- ``` Example: + ```bash npm run deploy -- my-custom-module ``` -### 6. Test MLE Module Functions +## Application Testing -Once your MLE module is deployed, you can test its functionality using **call specifications** defined in the `test-sql/call-specs.sql` file. +Once your MLE module is deployed, you can test its functionality using [call specifications](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/call-specifications-functions.html) defined in `test-sql/call-specs.sql`. -Call specifications allow you to invoke MLE module functions as PL/SQL procedures or functions. +A call specification allows you to invoke JavaScript code from SQL and PL/SQL. -#### Example: Testing User Package +### Testing in SQL and PL/SQL -The following SQL code demonstrates how to invoke the MLE module function `newUser` and other functions via the `user_package` package. +The following SQL code demonstrates how to invoke the MLE module function `newUser` and other functions via the `user_package` package. Make sure to update the call specification in case you changed the name of the MLE module. ```sql --- Create the user_package package (wraps MLE functions) -CREATE OR REPLACE PACKAGE user_package AS - PROCEDURE newUserFunc(name IN VARCHAR2); - FUNCTION getUser(id IN NUMBER) RETURN VARCHAR2; - PROCEDURE updateUser(id IN NUMBER, name IN VARCHAR2); - PROCEDURE deleteUser(id IN NUMBER); -END user_package; -/ -CREATE OR REPLACE PACKAGE BODY user_package AS +-- Create the user_package package +CREATE OR REPLACE PACKAGE AS PROCEDURE newUserFunc(name IN VARCHAR2) AS MLE MODULE MLEAPP SIGNATURE 'newUser(string)'; @@ -134,12 +169,12 @@ CREATE OR REPLACE PACKAGE BODY user_package AS END user_package; / --- Call MLE functions via the user_package -EXECUTE USER_PACKAGE.NEWUSERFUNC('BLABLA'); +-- Call MLE functions via the user_package in SQL*Plus or SQLcl +EXECUTE USER_PACKAGE.NEWUSERFUNC('EMILY'); SELECT USER_PACKAGE.GETUSER(5); -EXECUTE USER_PACKAGE.UPDATEUSER(5,'BLABLA'); +EXECUTE USER_PACKAGE.UPDATEUSER(5,'EMILY'); SELECT USER_PACKAGE.GETUSER(5); @@ -148,29 +183,40 @@ EXECUTE USER_PACKAGE.DELETEUSER(5); SELECT USER_PACKAGE.GETUSER(5); ``` -You can use tools like **SQLcl** or **SQL Developer** to execute these SQL commands. +Use any of your favourite tools to connect to the database schema and run these commands. + +### Testing MLE Code in Oracle APEX -#### **Test via Oracle APEX (Application Express)**: -Oracle APEX allows you to create web applications that interact with MLE functions. You can create an APEX page that allows users to test CRUD operations, such as adding or displaying TODO items. +Oracle [APEX](https://apex.oracle.com) was one of the first development environments supporting MLE/JavaScript. You can create an APEX page that allows users to test CRUD operations, such as adding or displaying TODO items. -**Link**: -[Oracle APEX](https://apex.oracle.com) +You can integrate JavaScript in Page Designer to create: -#### **Test via Oracle REST Data Services (ORDS)**: -If you’ve exposed your MLE functions as RESTful services using ORDS, you can test them via HTTP requests. For instance, you might have a REST endpoint for creating a TODO item. +- validations +- processes +- computations -**Example**: +Furthermore, you can use MLE/JavaScript for + +- SQL queries +- SQL Workshop + +You may want to add a page process using the JavaScript code to perform the CRUD operations, rather than the default submit actions. + +### Testing REST endpoints using Oracle REST Data Services (ORDS) + +If you’ve exposed your MLE functions as RESTful services using [ORDS](https://www.oracle.com/ords), you can test them via HTTP requests. For instance, you might have a REST endpoint for creating a TODO item. + +Assuming you REST-enabled your schema, and created an ORDS handler for MLE/JavaScript [as described in this blog post](https://blogs.oracle.com/developers/post/advanced-ords-rest-handlers-written-in-javascript), you can invoke the REST-endpoint like this: ```bash -curl -X POST "https://yourserver.com/ords/mle/todo/create" -H "Content-Type: application/json" -d '{"todo_text": "Buy groceries", "user_id": "user123", "category": "personal"}' +curl -X POST "https://yourserver.com/ords/mle/todo/create" \ + -H "Content-Type: application/json" \ + -d '{"todo_text": "Buy groceries", "user_id": "user123", "category": "personal"}' ``` -**Links**: -- [Less-well-known features of Multilingual Engine: Document API](https://blogs.oracle.com/developers/post/lesswellknown-features-of-multilingual-engine-document-api) -- [ORDS Documentation](https://www.oracle.com/database/technologies/appdev/rest.html) ---- +## Clean Up -### 7. Clean Up the Database +If you concluded your testing, and wish to remove the database objects, you can invoke the `cleanup` task via NPM, like so: ```bash npm run cleandb @@ -178,20 +224,18 @@ npm run cleandb This will drop all objects (tables, types, etc.) created by the `initdb` script. ---- - ## Links for Further Reading -- [Oracle MLE Documentation](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/introduction-to-mle.html) -- [SQLcl Documentation](https://www.oracle.com/database/sqldeveloper/technologies/sqlcl.html) +Here are some further resources relevant to Multilingual Engine/JavaScript: + +- [Oracle JavaScript Developer's Guide](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/introduction-to-mle.html) +- [SQL Developer Command Line (sqlcl)](https://www.oracle.com/database/sqldeveloper/technologies/sqlcl.html) - [PL/SQL User Guide](https://docs.oracle.com/en/database/oracle/oracle-database/) - [Oracle APEX Documentation](https://docs.oracle.com/en/database/oracle/application-express/) - [ORDS Documentation](https://docs.oracle.com/en/database/oracle/application-express/) - [SQL Developer](https://www.oracle.com/database/sqldeveloper/) - [SQL Developer for VSCode](https://www.oracle.com/database/sqldeveloper/vscode/) ---- - ## Feedback -Feel free to open issues or suggestions if you want to improve this template. Contributions are welcome! \ No newline at end of file +Feel free to open issues or suggestions if you want to improve this template. Contributions are welcome! From f45cf8fb8706078f8b7c9edc9664eabbd90ea5f6 Mon Sep 17 00:00:00 2001 From: Dmitrii Nikeshkin Date: Fri, 18 Apr 2025 11:16:13 +0200 Subject: [PATCH 09/15] feat: improvements, addressing feedback, added automated test --- .github/dependabot.yml | 2 +- README.md | 2 +- generators/index.ts | 20 +- src/index.ts | 8 +- templates/mle-app/deploy.js | 19 - templates/mle-app/package.json | 15 - templates/mle-app/src/index.ts | 272 ------------- templates/mle-app/test-sql/call-specs.sql | 137 ------- .../{mle-app => mle-js-basic}/.env.example | 6 +- templates/mle-js-basic/.env.example.wallet | 18 + .../.gitignore.template | 0 templates/{mle-app => mle-js-basic}/README.md | 66 ++- templates/mle-js-basic/deploy.mjs | 40 ++ templates/mle-js-basic/package.json | 18 + templates/mle-js-basic/src/index.ts | 383 ++++++++++++++++++ templates/mle-js-basic/test/users.test.js | 164 ++++++++ .../{mle-app => mle-js-basic}/tsconfig.json | 3 +- .../utils}/database/cleanup.sql | 0 .../utils}/database/initdb.sql | 0 .../db.js => mle-js-basic/utils/db.mjs} | 36 +- 20 files changed, 710 insertions(+), 499 deletions(-) delete mode 100644 templates/mle-app/deploy.js delete mode 100644 templates/mle-app/package.json delete mode 100644 templates/mle-app/src/index.ts delete mode 100644 templates/mle-app/test-sql/call-specs.sql rename templates/{mle-app => mle-js-basic}/.env.example (86%) create mode 100644 templates/mle-js-basic/.env.example.wallet rename templates/{mle-app => mle-js-basic}/.gitignore.template (100%) rename templates/{mle-app => mle-js-basic}/README.md (73%) create mode 100644 templates/mle-js-basic/deploy.mjs create mode 100644 templates/mle-js-basic/package.json create mode 100644 templates/mle-js-basic/src/index.ts create mode 100644 templates/mle-js-basic/test/users.test.js rename templates/{mle-app => mle-js-basic}/tsconfig.json (84%) rename templates/{mle-app/src => mle-js-basic/utils}/database/cleanup.sql (100%) rename templates/{mle-app/src => mle-js-basic/utils}/database/initdb.sql (100%) rename templates/{mle-app/src/database/db.js => mle-js-basic/utils/db.mjs} (51%) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 1c14805..a72276d 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -190,6 +190,6 @@ updates: prefix: fix(deps) - package-ecosystem: "npm" - directory: "/templates/mle-app/" + directory: "/templates/mle-js-basic/" schedule: interval: "monthly" \ No newline at end of file diff --git a/README.md b/README.md index 92b4718..8cbac4b 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ The package offers the following templates, some of them connect to the database - `node-angular`: A starter template that uses Node.js and [Angular](https://angular.dev/). It is built by [Angular CLI](https://github.com/angular/angular-cli). (New in `v1.2.0`) - `node-react-todo`: A simple task manager template that uses Node.js and [React](https://react.dev/). It demonstrates the use of the database for Create, Read, Update and Delete (CRUD) operations. It is built by [vite](https://vitejs.dev/). - `ords-remix-jwt-sample`: A full stack Concert Application made with [Remix](https://remix.run/) that showcases the [Oracle REST Data Services](https://www.oracle.com/database/technologies/appdev/rest.html) functionalities. Some extra configuration is required, learn more about it in the `ords-remix-jwt-sample` [Getting Started Guide](/templates/ords-remix-jwt-sample/README.md#getting-started). -- `mle-app`: A starter template application that demonstrates how backend applications can be developed using [Oracle Database Multilingual Engine (MLE)](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/introduction-to-mle.html). Requires SQLcl to be installed, for more information please read [README](/templates/mle-app/README.md) +- `mle-js-basic`: A starter template application that demonstrates how backend applications can be developed using [Oracle Database Multilingual Engine (MLE)](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/introduction-to-mle.html). Requires SQLcl to be installed, for more information please read [README](/templates/mle-js-basic/README.md) Each of the templates include documentation for you to get started with them, as well as NPM scripts for you to use right after generating the application. diff --git a/generators/index.ts b/generators/index.ts index 5d185e5..dfa3a2b 100644 --- a/generators/index.ts +++ b/generators/index.ts @@ -142,12 +142,20 @@ export default class extends Generator { this.templatePath( `${this.options.templateChoice}/.env.example` ), this.destinationPath( '.env.example' ), ); - } else if (this.options.templateChoice.includes('mle-app' )) { - this.fs.copyTpl( - this.templatePath( `${this.options.templateChoice}/.env.example` ), - this.destinationPath( '.env' ), - this.options - ); + } else if (this.options.templateChoice.includes('mle-js-basic' )) { + if( 'walletPath' in this.options ) { + this.fs.copyTpl( + this.templatePath( `${this.options.templateChoice}/.env.example.wallet` ), + this.destinationPath( '.env' ), + this.options + ); + } else { + this.fs.copyTpl( + this.templatePath( `${this.options.templateChoice}/.env.example` ), + this.destinationPath( '.env' ), + this.options + ); + } } else { this.fs.copyTpl( this.templatePath( `${ path.dirname( this.options.templateChoice ) }/app/${ path.basename( this.options.templateChoice ) == 'node-jet' ? 'index-proxied' : 'index' }.cjs` ), diff --git a/src/index.ts b/src/index.ts index 5ae933a..a88b683 100644 --- a/src/index.ts +++ b/src/index.ts @@ -207,7 +207,7 @@ export default class Generate extends Command { 'template': Flags.string({ char: 't', description: 'Template to use', - options: ['node-vanilla', 'node-react', 'node-vue', 'node-react-todo', 'node-jet', 'node-angular', 'ords-remix-jwt-sample', 'mle-app'], + options: ['node-vanilla', 'node-react', 'node-vue', 'node-react-todo', 'node-jet', 'node-angular', 'ords-remix-jwt-sample', 'mle-js-basic'], multiple: false }), @@ -375,8 +375,8 @@ export default class Generate extends Command { description: 'This creates a fullstack Concert Application made with Remix that leverages the Oracle REST Data Services functionalities. You will need to configurate the application yourself following the getting started guide.', }, { - name: 'mle-app', - value: 'mle-app', + name: 'mle-js-basic', + value: 'mle-js-basic', description: 'This creates an empty project with MLE and Oracle database connection starter code.' }, ], @@ -539,7 +539,7 @@ export default class Generate extends Command { } ); } - if(templateChoice == 'mle-app'){ + if(templateChoice == 'mle-js-basic'){ // Ask the user for the path to SQLcl Object.assign( configObject, { sqlclPath: sqlclPath === '' ? await input( diff --git a/templates/mle-app/deploy.js b/templates/mle-app/deploy.js deleted file mode 100644 index 7a76551..0000000 --- a/templates/mle-app/deploy.js +++ /dev/null @@ -1,19 +0,0 @@ -const { execSync } = require("child_process"); -const path = require("path"); -const fs = require("fs"); -const os = require("os"); - -const bundlePath = 'dist/index.js'; -let moduleName = process.argv[2]; - -if (!moduleName) { - moduleName = "mleapp"; -} - -const tempSqlPath = path.join(os.tmpdir(), `create_module_${Date.now()}.sql`); -fs.writeFileSync(tempSqlPath, ` -mle create-module -filename ${bundlePath} -module-name ${moduleName}; -EXIT; -`); - -execSync(`node src/database/db.js ${tempSqlPath}`, { stdio: 'inherit' }); \ No newline at end of file diff --git a/templates/mle-app/package.json b/templates/mle-app/package.json deleted file mode 100644 index 577314b..0000000 --- a/templates/mle-app/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "<%= appName %>", - "version": "1.0.0", - "devDependencies": { - "mle-js": "^23.7.0", - "typescript": "^5.7.3", - "esbuild": "0.25.1" - }, - "scripts": { - "build": "esbuild src/index.ts --bundle --minify=false --platform=neutral --format=esm --outfile=dist/index.js", - "deploy": "node deploy.js", - "initdb": "node src/database/db.js src/database/initdb.sql", - "cleandb": "node src/database/db.js src/database/cleanup.sql" - } -} \ No newline at end of file diff --git a/templates/mle-app/src/index.ts b/templates/mle-app/src/index.ts deleted file mode 100644 index eaa53e6..0000000 --- a/templates/mle-app/src/index.ts +++ /dev/null @@ -1,272 +0,0 @@ -/// - -enum priorities { - LOW = "low", - MEDIUM = "medium", - HIGH = "high", -} - -export function newUser(name: string): number { - const result = session.execute( - "insert into users (name) values (:name) returning id into :id", - { - name: { - dir: oracledb.BIND_IN, - val: name, - type: oracledb.STRING, - }, - id: { - type: oracledb.NUMBER, - dir: oracledb.BIND_OUT, - }, - }, - ); - - const id = result.outBinds.id[0]; - - return id; -} - -export function getUser(id: number): any { - const result = session.execute( - "select id, name from users where id = :id", - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - }, - { outFormat: oracledb.OUT_FORMAT_OBJECT } - ); - - return result.rows?.[0] || null; -} - -export function updateUser(id: number, newName: string): boolean { - const result = session.execute( - "update users set name = :name where id = :id", - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - name: { - dir: oracledb.BIND_IN, - val: newName, - type: oracledb.STRING, - }, - }, - ); - return true; -} - -export function deleteUser(id: number): boolean { - const result = session.execute( - "delete from users where id = :id", - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - }, - ); - - return true; -} - - -export function newCategory(name: string, priority: priorities): number { - const result = session.execute( - "insert into categories (name, prio) values (:name, :prio) returning id into :id", - { - name: { - dir: oracledb.BIND_IN, - val: name, - type: oracledb.STRING, - }, - prio: { - dir: oracledb.BIND_IN, - val: priority, - type: oracledb.STRING, - }, - id: { - type: oracledb.NUMBER, - dir: oracledb.BIND_OUT, - }, - }, - ); - - const id = result.outBinds.id[0]; - - return id; -} - -export function getCategory(id: number): any { - const result = session.execute( - "select id, name, prio from categories where id = :id", - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - }, - { outFormat: oracledb.OUT_FORMAT_OBJECT } - ); - - return result.rows?.[0] || null; -} - -export function updateCategory(id: number, newName: string, newPriority: priorities): boolean { - const result = session.execute( - "update categories set name = :name, prio = :prio where id = :id", - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - name: { - dir: oracledb.BIND_IN, - val: newName, - type: oracledb.STRING, - }, - prio: { - dir: oracledb.BIND_IN, - val: newPriority, - type: oracledb.STRING, - }, - }, - ); - - return true; -} - -export function deleteCategory(id: number): boolean { - const result = session.execute( - "delete from categories where id = :id", - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - }, - ); - - return true; -} - -export function newTodoItem(userId: number, categoryId: number, name: string, completed: boolean = false): number { - const result = session.execute( - `insert into todo_list (u_id, c_id, name, completed) - values (:u_id, :c_id, :name, :completed) - returning id into :id`, - { - u_id: { - dir: oracledb.BIND_IN, - val: userId, - type: oracledb.NUMBER, - }, - c_id: { - dir: oracledb.BIND_IN, - val: categoryId, - type: oracledb.NUMBER, - }, - name: { - dir: oracledb.BIND_IN, - val: name, - type: oracledb.STRING, - }, - completed: { - dir: oracledb.BIND_IN, - val: completed ? 1 : 0, - type: oracledb.NUMBER, - }, - id: { - type: oracledb.NUMBER, - dir: oracledb.BIND_OUT, - }, - } - ); - - const id = result.outBinds.id[0]; - return id; -} - - -export function getTodoItem(id: number): any { - const result = session.execute( - `select id, u_id, c_id, name, completed from todo_list where id = :id`, - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - }, - { outFormat: oracledb.OUT_FORMAT_OBJECT } - ); - - return result.rows?.[0] || null; -} - -export function updateTodoItem(id: number, newName: string, newCompleted: boolean): boolean { - const result = session.execute( - `update todo_list set name = :name, completed = :completed where id = :id`, - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - name: { - dir: oracledb.BIND_IN, - val: newName, - type: oracledb.STRING, - }, - completed: { - dir: oracledb.BIND_IN, - val: newCompleted ? 1 : 0, - type: oracledb.NUMBER, - }, - } - ); - - return true; -} - -export function deleteTodoItem(id: number): boolean { - const result = session.execute( - "delete from todo_list where id = :id", - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - } - ); - - return true; -} - - -export function getTodosByUser(userId: number): any[] { - const result = session.execute( - "select id, c_id, name, completed from todo_list where u_id = :u_id", - { - u_id: { - dir: oracledb.BIND_IN, - val: userId, - type: oracledb.NUMBER, - }, - }, - { outFormat: oracledb.OUT_FORMAT_OBJECT } - ); - - return result.rows || []; -} \ No newline at end of file diff --git a/templates/mle-app/test-sql/call-specs.sql b/templates/mle-app/test-sql/call-specs.sql deleted file mode 100644 index 08154bb..0000000 --- a/templates/mle-app/test-sql/call-specs.sql +++ /dev/null @@ -1,137 +0,0 @@ - -CREATE OR REPLACE PACKAGE user_package AS - PROCEDURE newUserFunc(name IN VARCHAR2); - FUNCTION getUser(id IN NUMBER) RETURN VARCHAR2; - PROCEDURE updateUser(id IN NUMBER, name IN VARCHAR2); - PROCEDURE deleteUser(id IN NUMBER); -END user_package; -/ - -CREATE OR REPLACE PACKAGE BODY user_package AS - PROCEDURE - newUserFunc(name IN VARCHAR2) - AS MLE MODULE MLEAPP - SIGNATURE 'newUser(string)'; - - FUNCTION - getUser(id IN NUMBER) RETURN VARCHAR2 - AS MLE MODULE MLEAPP - SIGNATURE 'getUser(number)'; - - PROCEDURE - updateUser(id IN NUMBER, name IN VARCHAR2) - AS MLE MODULE MLEAPP - SIGNATURE 'updateUser(number, string)'; - - PROCEDURE - deleteUser(id IN NUMBER) - AS MLE MODULE MLEAPP - SIGNATURE 'deleteUser(number)'; -END user_package; -/ - - -EXECUTE USER_PACKAGE.NEWUSERFUNC('BLABLA'); - -SELECT USER_PACKAGE.GETUSER(5); - -EXECUTE USER_PACKAGE.UPDATEUSER(5,'BLABLA'); - -SELECT USER_PACKAGE.GETUSER(5); - -EXECUTE USER_PACKAGE.DELETEUSER(5); - -SELECT USER_PACKAGE.GETUSER(5); - --- select * from users; - --- TRUNCATE table users; - -CREATE OR REPLACE PACKAGE category_package AS - PROCEDURE newCategory(name IN VARCHAR2, priority IN VARCHAR2); - FUNCTION getCategory(id IN NUMBER) RETURN VARCHAR2; - PROCEDURE updateCategory(id IN NUMBER, newName IN VARCHAR2, newPrio IN VARCHAR2); - PROCEDURE deleteCategory(id IN NUMBER); -END category_package; -/ - -CREATE OR REPLACE PACKAGE BODY category_package AS - PROCEDURE - newCategory(name IN VARCHAR2, priority IN VARCHAR2) - AS MLE MODULE MLEAPP - SIGNATURE 'newCategory(string,string)'; - - FUNCTION - getCategory(id IN NUMBER) RETURN VARCHAR2 - AS MLE MODULE MLEAPP - SIGNATURE 'getCategory(number)'; - - PROCEDURE - updateCategory(id IN NUMBER, newName IN VARCHAR2, newPrio IN VARCHAR2) - AS MLE MODULE MLEAPP - SIGNATURE 'updateCategory(number, string, string)'; - - PROCEDURE - deleteCategory(id IN NUMBER) - AS MLE MODULE MLEAPP - SIGNATURE 'deleteCategory(number)'; -END category_package; -/ - -EXECUTE CATEGORY_PACKAGE.NEWCATEGORY('BLABLA','high'); - -SELECT CATEGORY_PACKAGE.GETCATEGORY(2); - -EXECUTE CATEGORY_PACKAGE.UPDATECATEGORY(2,'NEW','low'); - -EXECUTE CATEGORY_PACKAGE.DELETECATEGORY(2); - -SELECT CATEGORY_PACKAGE.GETCATEGORY(2); - - -CREATE OR REPLACE PACKAGE todo_package AS - PROCEDURE newTodoItem(userId in NUMBER, categoryId in NUMBER, name IN VARCHAR2, completed IN BOOLEAN); - FUNCTION getTodoItem(id IN NUMBER) RETURN VARCHAR2; - PROCEDURE updateTodoItem(id IN NUMBER, newName IN VARCHAR2, newCompleted IN BOOLEAN); - PROCEDURE deleteTodoItem(id IN NUMBER); -END todo_package; -/ - -CREATE OR REPLACE PACKAGE BODY todo_package AS - PROCEDURE - newTodoItem(userId in NUMBER, categoryId in NUMBER, name IN VARCHAR2, completed IN BOOLEAN) - AS MLE MODULE MLEAPP - SIGNATURE 'newTodoItem(number,number,string,boolean)'; - - FUNCTION - getTodoItem(id IN NUMBER) RETURN VARCHAR2 - AS MLE MODULE MLEAPP - SIGNATURE 'getTodoItem(number)'; - - PROCEDURE - updateTodoItem(id IN NUMBER, newName IN VARCHAR2, newCompleted IN BOOLEAN) - AS MLE MODULE MLEAPP - SIGNATURE 'updateTodoItem(number, string, boolean)'; - - PROCEDURE - deleteTodoItem(id IN NUMBER) - AS MLE MODULE MLEAPP - SIGNATURE 'deleteTodoItem(number)'; -END todo_package; -/ - - -select * from users; -select * from CATEGORIES; - -EXECUTE TODO_PACKAGE.NEWTODOITEM(6,3,'GROCERIES',TRUE); - -select * from TODO_LIST; - -select TODO_PACKAGE.GETTODOITEM(1); - -EXECUTE TODO_PACKAGE.UPDATETODOITEM(1,'ERRANDS',FALSE); - -select TODO_PACKAGE.GETTODOITEM(1); - -EXECUTE TODO_PACKAGE.DELETETODOITEM(1); \ No newline at end of file diff --git a/templates/mle-app/.env.example b/templates/mle-js-basic/.env.example similarity index 86% rename from templates/mle-app/.env.example rename to templates/mle-js-basic/.env.example index b0f1753..ef25b39 100644 --- a/templates/mle-app/.env.example +++ b/templates/mle-js-basic/.env.example @@ -1,7 +1,3 @@ -# Path to database wallet -WALLET_PATH=<%= walletPath %> - - # Database User DB_USER=<%= connectionUsername %> # Database User Password @@ -9,7 +5,7 @@ DB_PASSWORD=<%= connectionPassword %> # Connection string to your Autonomous Database/ # Oracle Database Free instance CONNECT_STRING=<%= connectionString %> - +MLE_MODULE= # Optional HTTP Proxy Settings # HTTPS_PROXY= # HTTPS_PROXY_PORT= diff --git a/templates/mle-js-basic/.env.example.wallet b/templates/mle-js-basic/.env.example.wallet new file mode 100644 index 0000000..f8a80d6 --- /dev/null +++ b/templates/mle-js-basic/.env.example.wallet @@ -0,0 +1,18 @@ +# Path to database wallet +WALLET_PATH=<%= walletPath %> + +# Database User +DB_USER=<%= connectionUsername %> +# Database User Password +DB_PASSWORD=<%= connectionPassword %> +# Connection string to your Autonomous Database/ +# Oracle Database Free instance +CONNECT_STRING=<%= connectionString %> +MLE_MODULE= +# Optional HTTP Proxy Settings +# HTTPS_PROXY= +# HTTPS_PROXY_PORT= + +# Path to your local SQL Developer Command Line +# installation +SQL_CL_PATH=<%= sqlclPath %> \ No newline at end of file diff --git a/templates/mle-app/.gitignore.template b/templates/mle-js-basic/.gitignore.template similarity index 100% rename from templates/mle-app/.gitignore.template rename to templates/mle-js-basic/.gitignore.template diff --git a/templates/mle-app/README.md b/templates/mle-js-basic/README.md similarity index 73% rename from templates/mle-app/README.md rename to templates/mle-js-basic/README.md index eae8e37..42354cc 100644 --- a/templates/mle-app/README.md +++ b/templates/mle-js-basic/README.md @@ -35,9 +35,11 @@ The project is structured as follows: | Source File | Used For | | -- | -- | | `src/index.ts` | This file is the main TypeScript file containing the application logic. It uses MLE SQL API to perform database operations (`INSERT`, `SELECT`, `UPDATE`, `DELETE`). | -| `src/database/initdb.sql` | A short SQL script to initialize the required database tables (TODOs, Users, Categories). | -| `src/database/cleanup.sql` | A SQL script to drop all created database tables. Use with care! | -| `test-sql/call-specs.sql` | The final SQL file defines PL/SQL [call specifications](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/call-specifications-functions.html) to wrap MLE module functions, allowing them to be invoked from SQL. It uses `MLEAPP` as default MLE module name. The purpose of the script is to demonstrate how MLE module functions can be executed. | +| `utils/database/initdb.sql` | A short SQL script to initialize the required database tables (TODOs, Users, Categories). | +| `utils/database/cleanup.sql` | A SQL script to drop all created database tables. Use with care! | +| `utils/db.mjs` | `db.mjs` is a wrapper around SQLcl. It is used to execute SQL scripts in the database using creadentials specified in `.env` file. See `package.json` for usage. | +| `deploy.mjs` | The deployment logic is implemented in `deploy.mjs`. If modifications are needed, users can either update the script directly or configure a different deployment script by updating the deploy entry under the scripts section in `package.json`. | +| `test/users.test.js` | A test that showcases the execution of MLE call specifications for the USERS table. | ### Requirements @@ -49,6 +51,8 @@ You need the following components to use this application template. An Always Free Autonomous Database offers the quickest way to get started. If you would like to use {Docker,Podman} Compose to get up-and-running quickly, you can refer to [Server-Side JavaScript driven by node-express](https://blogs.oracle.com/developers/post/serverside-javascript-driven-by-nodeexpress) for reference. +To execute JavaScript using Oracle Multilingual Engine (MLE), a database user typically needs the CREATE SESSION privilege to connect, and may require EXECUTE ON JAVASCRIPT depending on the database version (this requirement is being deprecated). If the JavaScript code imports MLE modules, the user must have EXECUTE privileges on those specific modules. Additional privileges like CREATE PROCEDURE or data access privileges (SELECT, INSERT, etc.) may be needed based on what the JS code does. For more information please read [System and Object Privileges Required for Working with JavaScript in MLE](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/system-and-object-privileges-required-working-javascript-mle.html). + ## Getting Started If you haven't already, please download and install SQLcl from Oracle: @@ -59,7 +63,7 @@ Make sure it is unzipped and accessible via its full path (e.g. `/Users/yourname ### 1. Create the application -Use the `@create-database-app` command as described in the [top-level readme](https://github.com/oracle/create-database-app/blob/main/README.md) to set up your application. You will be prompted to make a number of choices. Make sure you select `mle-app` as shown here: +Use the `@create-database-app` command as described in the [top-level readme](https://github.com/oracle/create-database-app/blob/main/README.md) to set up your application. You will be prompted to make a number of choices. Make sure you select `mle-js-basic` as shown here: ``` ? What would you like your application's name to be? demo @@ -67,7 +71,7 @@ Use the `@create-database-app` command as described in the [top-level readme](ht node-angular node-react-todo ords-remix-jwt-sample -❯ mle-app +❯ mle-js-basic node-vanilla node-react node-vue @@ -108,6 +112,7 @@ Make sure your environment variables or `.env` file provides connection details: - `CONNECT_STRING` - `SQL_CL_PATH` - `WALLET_PATH` (optional) +- `MLE_MODULE` (the value is set during deployment of module. See `deploy.mjs`) You should have been prompted for those values upon project initialisation. @@ -123,7 +128,7 @@ This compiles and bundles `src/index.ts` using esbuild. The output file is writt ### 5. Deploy the MLE Module -Finally you can deploy the transpiled JavaScript code using the `deploy` script: +Finally, you can deploy the transpiled JavaScript code using the `deploy` script: ```bash npm run deploy @@ -139,14 +144,20 @@ npm run deploy -- Example: ```bash -npm run deploy -- my-custom-module +npm run deploy -- myCustomModule ``` -## Application Testing +If you see `ORA-04102` error: -Once your MLE module is deployed, you can test its functionality using [call specifications](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/call-specifications-functions.html) defined in `test-sql/call-specs.sql`. +``` +MLE Module mleapp not created: java.sql.SQLException: ORA-04102: An MLE module named MLEAPP already exists. +``` + +The module can be removed by running the `DROP MLE MODULE` command. This can be done using SQL Developer or any other tool that executes SQL commands. + +## Application Testing -A call specification allows you to invoke JavaScript code from SQL and PL/SQL. +Once your MLE module is deployed, you can test its functionality using [call specifications](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/call-specifications-functions.html). A call specification allows you to invoke JavaScript code from SQL and PL/SQL. ### Testing in SQL and PL/SQL @@ -154,20 +165,30 @@ The following SQL code demonstrates how to invoke the MLE module function `newUs ```sql -- Create the user_package package -CREATE OR REPLACE PACKAGE AS - PROCEDURE newUserFunc(name IN VARCHAR2) - AS MLE MODULE MLEAPP SIGNATURE 'newUser(string)'; +CREATE OR REPLACE PACKAGE user_package AS + FUNCTION newUserFunc(name IN VARCHAR2) RETURN NUMBER; + FUNCTION getUser(id IN NUMBER) RETURN VARCHAR2; + PROCEDURE updateUser(id IN NUMBER, name IN VARCHAR2); + PROCEDURE deleteUser(id IN NUMBER); +END user_package; +CREATE OR REPLACE PACKAGE BODY user_package AS + FUNCTION newUserFunc(name IN VARCHAR2) RETURN NUMBER + AS MLE MODULE &mleModuleName + SIGNATURE 'newUser(string)'; + FUNCTION getUser(id IN NUMBER) RETURN VARCHAR2 - AS MLE MODULE MLEAPP SIGNATURE 'getUser(number)'; - + AS MLE MODULE &mleModuleName + SIGNATURE 'getUser(number)'; + PROCEDURE updateUser(id IN NUMBER, name IN VARCHAR2) - AS MLE MODULE MLEAPP SIGNATURE 'updateUser(number, string)'; - + AS MLE MODULE &mleModuleName + SIGNATURE 'updateUser(number, string)'; + PROCEDURE deleteUser(id IN NUMBER) - AS MLE MODULE MLEAPP SIGNATURE 'deleteUser(number)'; + AS MLE MODULE &mleModuleName + SIGNATURE 'deleteUser(number)'; END user_package; -/ -- Call MLE functions via the user_package in SQL*Plus or SQLcl EXECUTE USER_PACKAGE.NEWUSERFUNC('EMILY'); @@ -185,9 +206,14 @@ SELECT USER_PACKAGE.GETUSER(5); Use any of your favourite tools to connect to the database schema and run these commands. +Alternatively, you can run the `test/users.test.js` test, which shows how to use call specifications with the USERS table. To run the test, execute: +```bash +npm run test +``` + ### Testing MLE Code in Oracle APEX -Oracle [APEX](https://apex.oracle.com) was one of the first development environments supporting MLE/JavaScript. You can create an APEX page that allows users to test CRUD operations, such as adding or displaying TODO items. +Oracle [APEX](https://apex.oracle.com) is one of the first development environments supporting MLE/JavaScript. You can create an APEX page that allows users to test CRUD operations, such as adding or displaying TODO items. You can integrate JavaScript in Page Designer to create: diff --git a/templates/mle-js-basic/deploy.mjs b/templates/mle-js-basic/deploy.mjs new file mode 100644 index 0000000..e068dda --- /dev/null +++ b/templates/mle-js-basic/deploy.mjs @@ -0,0 +1,40 @@ +import { execSync } from "child_process"; +import path from "path"; +import fs from "fs"; +import os from "os"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const bundlePath = 'dist/index.js'; +let moduleName = process.argv[2] || "mleapp"; + +const tempSqlPath = path.join(os.tmpdir(), `create_module_${Date.now()}.sql`); +fs.writeFileSync(tempSqlPath, ` +mle create-module -filename ${bundlePath} -module-name ${moduleName}; +EXIT; +`); + +execSync(`node utils/db.mjs ${tempSqlPath}`, { stdio: 'inherit' }); + +const envFilePath = path.resolve(__dirname, '.env'); +function updateEnvVariable(key, value) { + // Read the existing .env file + const envData = fs.readFileSync(envFilePath, 'utf-8'); + + // Check if the variable already exists + const regex = new RegExp(`^${key}=.*`, 'm'); + if (regex.test(envData)) { + // Update existing variable + const updatedData = envData.replace(regex, `${key}=${value}`); + fs.writeFileSync(envFilePath, updatedData, 'utf-8'); + console.log(`Updated ${key} in .env file`); + } else { + // Add new variable if not present + fs.appendFileSync(envFilePath, `\n${key}=${value}`, 'utf-8'); + console.log(`Added new variable ${key} to .env file`); + } +} + +updateEnvVariable('MLE_MODULE', moduleName); \ No newline at end of file diff --git a/templates/mle-js-basic/package.json b/templates/mle-js-basic/package.json new file mode 100644 index 0000000..41c9677 --- /dev/null +++ b/templates/mle-js-basic/package.json @@ -0,0 +1,18 @@ +{ + "name": "mleapp", + "version": "1.0.0", + "devDependencies": { + "mle-js": "^23.7.0", + "typescript": "^5.7.3", + "esbuild": "0.25.1", + "oracledb": "^6.7.1", + "vitest": "^1.0.0" + }, + "scripts": { + "build": "esbuild src/index.ts --bundle --minify=false --platform=neutral --format=esm --outfile=dist/index.js", + "deploy": "node deploy.mjs", + "initdb": "node utils/db.mjs utils/database/initdb.sql", + "cleandb": "node utils/db.mjs utils/database/cleanup.sql", + "test": "vitest" + } +} \ No newline at end of file diff --git a/templates/mle-js-basic/src/index.ts b/templates/mle-js-basic/src/index.ts new file mode 100644 index 0000000..05d8c62 --- /dev/null +++ b/templates/mle-js-basic/src/index.ts @@ -0,0 +1,383 @@ +/// + +enum priorities { + LOW = "low", + MEDIUM = "medium", + HIGH = "high", +} + +/** + * Creates a new user in the database with the given name. + * + * Executes an SQL `insert` statement to add a new user to the `users` table. + * The newly created user ID is returned. + * + * @param {string} name - The name of the new user to be added. + * @returns {number} The ID of the newly created user. + */ +export function newUser(name: string): number { + const result = session.execute( + "insert into users (name) values (:name) returning id into :id", + { + name: { + dir: oracledb.BIND_IN, + val: name, + type: oracledb.STRING, + }, + id: { + type: oracledb.NUMBER, + dir: oracledb.BIND_OUT, + }, + }, + ); + + const id = result.outBinds.id[0]; + + return id; +} + +/** + * Retrieves a user from the database based on the provided user ID. + * + * @param {number} id - The ID of the user to retrieve. + * @returns {any} An object containing the user's ID and name if found, or null if no user is found. + */ +export function getUser(id: number): string | null { + const result = session.execute( + "select id, name from users where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + + // Return the result as a JSON string so Oracle can handle it + return result.rows?.[0] ? result.rows[0].NAME : null; +} + +/** + * Updates the name of the user with the given ID in the database. + * + * @param {number} id - The ID of the user to be updated. + * @param {string} newName - The new name to be set for the user. + * + * @returns {boolean} Returns true if the update was successful. + */ +export function updateUser(id: number, newName: string): boolean { + const result = session.execute( + "update users set name = :name where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + name: { + dir: oracledb.BIND_IN, + val: newName, + type: oracledb.STRING, + }, + }, + ); + return true; +} + +/** + * Deletes a user from the database based on the provided user ID. + * + * Executes an SQL `delete` statement to remove the user from the `users` table. + * + * @param {number} id - The ID of the user to delete. + * @returns {boolean} Returns true if the deletion was executed successfully. + */ +export function deleteUser(id: number): boolean { + const result = session.execute( + "delete from users where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + }, + ); + + return true; +} + +/** + * Creates a new category in the database with the given name and priority. + * + * Executes an SQL `insert` statement to add a new category to the `categories` table. + * The newly created category ID is returned. + * + * @param {string} name - The name of the new category to be added. + * @param {priorities} priority - The priority level of the new category. It can be one of `priorities.LOW`, `priorities.MEDIUM`, or `priorities.HIGH`. + * @returns {number} The ID of the newly created category. + */ +export function newCategory(name: string, priority: priorities): number { + const result = session.execute( + "insert into categories (name, prio) values (:name, :prio) returning id into :id", + { + name: { + dir: oracledb.BIND_IN, + val: name, + type: oracledb.STRING, + }, + prio: { + dir: oracledb.BIND_IN, + val: priority, + type: oracledb.STRING, + }, + id: { + type: oracledb.NUMBER, + dir: oracledb.BIND_OUT, + }, + }, + ); + + const id = result.outBinds.id[0]; + + return id; +} + +/** + * Retrieves a category from the database based on the provided category ID. + * + * Executes an SQL `select` statement to fetch the category details from the `categories` table. + * + * @param {number} id - The ID of the category to retrieve. + * @returns {any} An object containing the category's ID, name, and priority if found, or null if no category is found. + */ +export function getCategory(id: number): any { + const result = session.execute( + "select id, name, prio from categories where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + + return result.rows?.[0] || null; +} + +/** + * Updates the name and priority of the category with the given ID in the database. + * + * Executes an SQL `update` statement to modify the category's details in the `categories` table. + * + * @param {number} id - The ID of the category to be updated. + * @param {string} newName - The new name to be set for the category. + * @param {priorities} newPriority - The new priority level to be set for the category. It can be one of `priorities.LOW`, `priorities.MEDIUM`, or `priorities.HIGH`. + * @returns {boolean} Returns true if the update was successful. + */ +export function updateCategory(id: number, newName: string, newPriority: priorities): boolean { + const result = session.execute( + "update categories set name = :name, prio = :prio where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + name: { + dir: oracledb.BIND_IN, + val: newName, + type: oracledb.STRING, + }, + prio: { + dir: oracledb.BIND_IN, + val: newPriority, + type: oracledb.STRING, + }, + }, + ); + + return true; +} + +/** + * Deletes a category from the database based on the provided category ID. + * + * Executes an SQL `delete` statement to remove the category from the `categories` table. + * + * @param {number} id - The ID of the category to delete. + * @returns {boolean} Returns true if the deletion was executed successfully. + */ +export function deleteCategory(id: number): boolean { + const result = session.execute( + "delete from categories where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + }, + ); + + return true; +} + +/** + * Creates a new to-do item in the database with the given user ID, category ID, name, and completion status. + * + * Executes an SQL `insert` statement to add a new to-do item to the `todo_list` table. + * The newly created to-do item ID is returned. + * + * @param {number} userId - The ID of the user associated with the to-do item. + * @param {number} categoryId - The ID of the category associated with the to-do item. + * @param {string} name - The name of the to-do item. + * @param {boolean} [completed=false] - The completion status of the to-do item. Defaults to `false`. + * @returns {number} The ID of the newly created to-do item. + */ +export function newTodoItem(userId: number, categoryId: number, name: string, completed: boolean = false): number { + const result = session.execute( + `insert into todo_list (u_id, c_id, name, completed) + values (:u_id, :c_id, :name, :completed) + returning id into :id`, + { + u_id: { + dir: oracledb.BIND_IN, + val: userId, + type: oracledb.NUMBER, + }, + c_id: { + dir: oracledb.BIND_IN, + val: categoryId, + type: oracledb.NUMBER, + }, + name: { + dir: oracledb.BIND_IN, + val: name, + type: oracledb.STRING, + }, + completed: { + dir: oracledb.BIND_IN, + val: completed ? 1 : 0, + type: oracledb.NUMBER, + }, + id: { + type: oracledb.NUMBER, + dir: oracledb.BIND_OUT, + }, + } + ); + + const id = result.outBinds.id[0]; + return id; +} + +/** + * Retrieves a to-do item from the database based on the provided to-do item ID. + * + * Executes an SQL `select` statement to fetch the to-do item details from the `todo_list` table. + * + * @param {number} id - The ID of the to-do item to retrieve. + * @returns {any} An object containing the to-do item's ID, user ID (u_id), category ID (c_id), name, and completion status (completed) if found, or null if no to-do item is found. + */ +export function getTodoItem(id: number): any { + const result = session.execute( + `select id, u_id, c_id, name, completed from todo_list where id = :id`, + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + + return result.rows?.[0] || null; +} + +/** + * Updates the name and completion status of the to-do item with the given ID in the database. + * + * Executes an SQL `update` statement to modify the to-do item's details in the `todo_list` table. + * + * @param {number} id - The ID of the to-do item to be updated. + * @param {string} newName - The new name to be set for the to-do item. + * @param {boolean} newCompleted - The new completion status to be set for the to-do item. + * @returns {boolean} Returns true if the update was successful. + */ +export function updateTodoItem(id: number, newName: string, newCompleted: boolean): boolean { + const result = session.execute( + `update todo_list set name = :name, completed = :completed where id = :id`, + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + name: { + dir: oracledb.BIND_IN, + val: newName, + type: oracledb.STRING, + }, + completed: { + dir: oracledb.BIND_IN, + val: newCompleted ? 1 : 0, + type: oracledb.NUMBER, + }, + } + ); + + return true; +} + +/** + * Deletes a to-do item from the database based on the provided to-do item ID. + * + * Executes an SQL `delete` statement to remove the to-do item from the `todo_list` table. + * + * @param {number} id - The ID of the to-do item to delete. + * @returns {boolean} Returns true if the deletion was executed successfully. + */ +export function deleteTodoItem(id: number): boolean { + const result = session.execute( + "delete from todo_list where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + } + ); + + return true; +} + +/** + * Retrieves all to-do items for the given user ID from the database. + * + * Executes an SQL `select` statement to fetch all to-do items associated with the specified user from the `todo_list` table. + * + * @param {number} userId - The ID of the user whose to-do items are to be retrieved. + * @returns {any[]} An array of objects, each containing the to-do item's ID, category ID (c_id), name, and completion status (completed). + */ +export function getTodosByUser(userId: number): any[] { + const result = session.execute( + "select id, c_id, name, completed from todo_list where u_id = :u_id", + { + u_id: { + dir: oracledb.BIND_IN, + val: userId, + type: oracledb.NUMBER, + }, + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + + return result.rows || []; +} \ No newline at end of file diff --git a/templates/mle-js-basic/test/users.test.js b/templates/mle-js-basic/test/users.test.js new file mode 100644 index 0000000..c3506fa --- /dev/null +++ b/templates/mle-js-basic/test/users.test.js @@ -0,0 +1,164 @@ +import dotenv from 'dotenv'; +import oracledb from 'oracledb'; +import { beforeAll, afterAll, describe, it, expect } from 'vitest'; + +dotenv.config(); + +const dbConfig = { + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + connectString: process.env.CONNECT_STRING, +}; + +let mleModuleName = process.env.MLE_MODULE; +let createdUserId = null; + +oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT; +oracledb.autoCommit = true; + +async function executeSQLScript(script) { + let connection; + try { + connection = await oracledb.getConnection(dbConfig); + await connection.execute(script); + console.log('Script executed successfully'); + } catch (err) { + console.error('Error executing script:', err); + throw err; + } finally { + if (connection) { + try { + await connection.close(); + } catch (err) { + console.error('Error closing connection:', err); + } + } + } +} + +async function prepareDatabase() { + const createPackageSQL = ` + CREATE OR REPLACE PACKAGE user_package AS + FUNCTION newUserFunc(name IN VARCHAR2) RETURN NUMBER; + FUNCTION getUser(id IN NUMBER) RETURN VARCHAR2; + PROCEDURE updateUser(id IN NUMBER, name IN VARCHAR2); + PROCEDURE deleteUser(id IN NUMBER); + END user_package; + `; + + const createPackageBodySQL = ` + CREATE OR REPLACE PACKAGE BODY user_package AS + FUNCTION newUserFunc(name IN VARCHAR2) RETURN NUMBER + AS MLE MODULE ${mleModuleName} + SIGNATURE 'newUser(string)'; + + FUNCTION getUser(id IN NUMBER) RETURN VARCHAR2 + AS MLE MODULE ${mleModuleName} + SIGNATURE 'getUser(number)'; + + PROCEDURE updateUser(id IN NUMBER, name IN VARCHAR2) + AS MLE MODULE ${mleModuleName} + SIGNATURE 'updateUser(number, string)'; + + PROCEDURE deleteUser(id IN NUMBER) + AS MLE MODULE ${mleModuleName} + SIGNATURE 'deleteUser(number)'; + END user_package; + `; + + try { + console.log('Creating package...'); + await executeSQLScript(createPackageSQL); + + console.log('Creating package body...'); + await executeSQLScript(createPackageBodySQL); + + console.log('Successfully created package and package body'); + } catch (err) { + console.error('Error during package creation:', err); + } +} + +async function cleanupDatabase() { + const dropPackageSQL = `DROP PACKAGE user_package`; + + try { + console.log('Dropping package...'); + await executeSQLScript(dropPackageSQL); + console.log('Successfully dropped package'); + } catch (err) { + console.error('Error during package drop:', err); + } +} + +async function executeSQLScriptWithOutput(plsql, binds = {}, options = {}) { + let connection; + try { + connection = await oracledb.getConnection(dbConfig); + const result = await connection.execute(plsql, binds, options); + return result; + } catch (err) { + console.error('Error executing PL/SQL:', err); + throw err; + } finally { + if (connection) { + try { + await connection.close(); + } catch (err) { + console.error('Error closing connection:', err); + } + } + } +} + +beforeAll(async () => { + await prepareDatabase(); +}); + +afterAll(async () => { + await cleanupDatabase(); +}); + +describe('user_package', () => { + it('should create a new user and return its ID', async () => { + const plsql = `BEGIN :id := USER_PACKAGE.NEWUSERFUNC(:name); END;`; + const binds = { + name: 'John Doe', + id: { dir: oracledb.BIND_OUT, type: oracledb.NUMBER }, + }; + + const result = await executeSQLScriptWithOutput(plsql, binds); + createdUserId = result.outBinds.id; + + console.log('New user created with ID:', createdUserId); + expect(createdUserId).toBeTypeOf('number'); + }); + + it('should get a user by ID', async () => { + const plsql = `BEGIN :user := USER_PACKAGE.GETUSER(:id); END;`; + const binds = { + id: createdUserId, + user: { dir: oracledb.BIND_OUT, type: oracledb.STRING }, + }; + + const result = await executeSQLScriptWithOutput(plsql, binds); + console.log('User:', result.outBinds.user); + expect(result.outBinds.user).toBe('John Doe'); + }); + + it('should update a user by ID', async () => { + const plsql = `BEGIN USER_PACKAGE.UPDATEUSER(:id, :name); END;`; + const binds = { id: createdUserId, name: 'Jane Doe' }; + + await executeSQLScriptWithOutput(plsql, binds); + console.log('User updated'); + }); + + it('should delete a user by ID', async () => { + const plsql = `BEGIN USER_PACKAGE.DELETEUSER(:id); END;`; + const binds = { id: createdUserId }; + + await executeSQLScriptWithOutput(plsql, binds); + console.log('User deleted'); + }); +}); diff --git a/templates/mle-app/tsconfig.json b/templates/mle-js-basic/tsconfig.json similarity index 84% rename from templates/mle-app/tsconfig.json rename to templates/mle-js-basic/tsconfig.json index 4abc65a..814018c 100644 --- a/templates/mle-app/tsconfig.json +++ b/templates/mle-js-basic/tsconfig.json @@ -6,8 +6,7 @@ "lib": ["es6", "es2017", "es2021"], "types": [], "noImplicitAny": true, - "strict": true, - "sourceMap": true + "strict": true }, "include": ["src"], "exclude": ["node_modules"] diff --git a/templates/mle-app/src/database/cleanup.sql b/templates/mle-js-basic/utils/database/cleanup.sql similarity index 100% rename from templates/mle-app/src/database/cleanup.sql rename to templates/mle-js-basic/utils/database/cleanup.sql diff --git a/templates/mle-app/src/database/initdb.sql b/templates/mle-js-basic/utils/database/initdb.sql similarity index 100% rename from templates/mle-app/src/database/initdb.sql rename to templates/mle-js-basic/utils/database/initdb.sql diff --git a/templates/mle-app/src/database/db.js b/templates/mle-js-basic/utils/db.mjs similarity index 51% rename from templates/mle-app/src/database/db.js rename to templates/mle-js-basic/utils/db.mjs index ba5bed4..78e24f7 100644 --- a/templates/mle-app/src/database/db.js +++ b/templates/mle-js-basic/utils/db.mjs @@ -1,30 +1,32 @@ -const { execSync } = require("child_process"); -const path = require("path"); -const dotenv = require("dotenv"); -var args = process.argv.slice(2); -const os = require("os"); -const fs = require("fs"); +import { execSync } from "child_process"; +import path from "path"; +import dotenv from "dotenv"; +import os from "os"; +import fs from "fs"; +import { fileURLToPath } from "url"; +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +// Parse command-line arguments +const args = process.argv.slice(2); + +// Load environment variables dotenv.config(); -// using dotenv, read the environment variables defined in the generated .env file const { WALLET_PATH, DB_USER, DB_PASSWORD, CONNECT_STRING, SQL_CL_PATH } = process.env; -// sqlclPath is a mandatory field, therefore must exist const sqlclPath = path.normalize(SQL_CL_PATH); const sqlExecutable = path.join(sqlclPath, 'bin', os.platform() === 'win32' ? 'sql.exe' : 'sql'); -// connecting to an Autonomous Database using the EZConnect string requires TNS_ADMIN -// the @create-database-app initialisation routine will unzip a wallet to -// ./server/utils/db/wallet. Optionally use the -tnsadmin flag to SQLcl to point it -// to the unzipped wallet directory. -if (WALLET_PATH.length > 0) { - process.env.TNS_ADMIN = path.join(process.cwd(), path.normalize('./server/utils/db/wallet')) +// Set TNS_ADMIN if WALLET_PATH is defined +if (WALLET_PATH && WALLET_PATH.length > 0) { + process.env.TNS_ADMIN = path.join(process.cwd(), path.normalize('./server/utils/db/wallet')); } -// make sure the file supposed to be executed does exist +// Validate file path const filePath = args[0]; -if (! fs.existsSync(filePath)) { +if (!fs.existsSync(filePath)) { throw new Error(`file ${filePath} does not exist`); } @@ -34,7 +36,7 @@ connect ${DB_USER}/${DB_PASSWORD}@${CONNECT_STRING} start ${filePath} EOF`; -try { +try { execSync(sqlclCommand, { stdio: 'inherit', shell: true From c77888b393b4e19d57aa85291702eb5514ae1e2b Mon Sep 17 00:00:00 2001 From: Dmitrii Nikeshkin Date: Wed, 23 Apr 2025 10:41:24 +0200 Subject: [PATCH 10/15] feat: removed mle types directive and added to tsconfig --- templates/mle-js-basic/src/index.ts | 2 -- templates/mle-js-basic/tsconfig.json | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/templates/mle-js-basic/src/index.ts b/templates/mle-js-basic/src/index.ts index 05d8c62..29c163f 100644 --- a/templates/mle-js-basic/src/index.ts +++ b/templates/mle-js-basic/src/index.ts @@ -1,5 +1,3 @@ -/// - enum priorities { LOW = "low", MEDIUM = "medium", diff --git a/templates/mle-js-basic/tsconfig.json b/templates/mle-js-basic/tsconfig.json index 814018c..7caedf7 100644 --- a/templates/mle-js-basic/tsconfig.json +++ b/templates/mle-js-basic/tsconfig.json @@ -4,7 +4,7 @@ "target": "ES2021", "outDir": "dist", "lib": ["es6", "es2017", "es2021"], - "types": [], + "types": ["mle-js"], "noImplicitAny": true, "strict": true }, From e67ed300dc23a4658b8caa5b7de10d16412e04e5 Mon Sep 17 00:00:00 2001 From: Dmitrii Nikeshkin Date: Tue, 29 Apr 2025 11:31:48 +0200 Subject: [PATCH 11/15] feat: addressing PR comments --- .github/dependabot.yml | 2 +- README.md | 2 +- generators/index.ts | 2 +- src/index.ts | 8 ++++---- templates/{mle-js-basic => mle-ts-sample}/.env.example | 2 +- .../{mle-js-basic => mle-ts-sample}/.env.example.wallet | 2 +- .../{mle-js-basic => mle-ts-sample}/.gitignore.template | 0 templates/{mle-js-basic => mle-ts-sample}/README.md | 4 ++-- templates/{mle-js-basic => mle-ts-sample}/deploy.mjs | 0 templates/{mle-js-basic => mle-ts-sample}/package.json | 0 templates/{mle-js-basic => mle-ts-sample}/src/index.ts | 0 .../{mle-js-basic => mle-ts-sample}/test/users.test.js | 0 templates/{mle-js-basic => mle-ts-sample}/tsconfig.json | 0 .../utils/database/cleanup.sql | 0 .../utils/database/initdb.sql | 0 templates/{mle-js-basic => mle-ts-sample}/utils/db.mjs | 0 16 files changed, 11 insertions(+), 11 deletions(-) rename templates/{mle-js-basic => mle-ts-sample}/.env.example (92%) rename templates/{mle-js-basic => mle-ts-sample}/.env.example.wallet (93%) rename templates/{mle-js-basic => mle-ts-sample}/.gitignore.template (100%) rename templates/{mle-js-basic => mle-ts-sample}/README.md (99%) rename templates/{mle-js-basic => mle-ts-sample}/deploy.mjs (100%) rename templates/{mle-js-basic => mle-ts-sample}/package.json (100%) rename templates/{mle-js-basic => mle-ts-sample}/src/index.ts (100%) rename templates/{mle-js-basic => mle-ts-sample}/test/users.test.js (100%) rename templates/{mle-js-basic => mle-ts-sample}/tsconfig.json (100%) rename templates/{mle-js-basic => mle-ts-sample}/utils/database/cleanup.sql (100%) rename templates/{mle-js-basic => mle-ts-sample}/utils/database/initdb.sql (100%) rename templates/{mle-js-basic => mle-ts-sample}/utils/db.mjs (100%) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index a72276d..b3460bc 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -190,6 +190,6 @@ updates: prefix: fix(deps) - package-ecosystem: "npm" - directory: "/templates/mle-js-basic/" + directory: "/templates/mle-ts-sample/" schedule: interval: "monthly" \ No newline at end of file diff --git a/README.md b/README.md index 8cbac4b..a58af91 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ The package offers the following templates, some of them connect to the database - `node-angular`: A starter template that uses Node.js and [Angular](https://angular.dev/). It is built by [Angular CLI](https://github.com/angular/angular-cli). (New in `v1.2.0`) - `node-react-todo`: A simple task manager template that uses Node.js and [React](https://react.dev/). It demonstrates the use of the database for Create, Read, Update and Delete (CRUD) operations. It is built by [vite](https://vitejs.dev/). - `ords-remix-jwt-sample`: A full stack Concert Application made with [Remix](https://remix.run/) that showcases the [Oracle REST Data Services](https://www.oracle.com/database/technologies/appdev/rest.html) functionalities. Some extra configuration is required, learn more about it in the `ords-remix-jwt-sample` [Getting Started Guide](/templates/ords-remix-jwt-sample/README.md#getting-started). -- `mle-js-basic`: A starter template application that demonstrates how backend applications can be developed using [Oracle Database Multilingual Engine (MLE)](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/introduction-to-mle.html). Requires SQLcl to be installed, for more information please read [README](/templates/mle-js-basic/README.md) +- `mle-ts-sample`: A starter template application that demonstrates how backend applications can be developed using [Oracle Database Multilingual Engine (MLE)](https://docs.oracle.com/en/database/oracle/oracle-database/23/mlejs/introduction-to-mle.html). Requires SQLcl to be installed, for more information please read [README](/templates/mle-ts-sample/README.md) Each of the templates include documentation for you to get started with them, as well as NPM scripts for you to use right after generating the application. diff --git a/generators/index.ts b/generators/index.ts index dfa3a2b..9577252 100644 --- a/generators/index.ts +++ b/generators/index.ts @@ -142,7 +142,7 @@ export default class extends Generator { this.templatePath( `${this.options.templateChoice}/.env.example` ), this.destinationPath( '.env.example' ), ); - } else if (this.options.templateChoice.includes('mle-js-basic' )) { + } else if (this.options.templateChoice.includes('mle-ts-sample')) { if( 'walletPath' in this.options ) { this.fs.copyTpl( this.templatePath( `${this.options.templateChoice}/.env.example.wallet` ), diff --git a/src/index.ts b/src/index.ts index a88b683..aef0af2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -207,7 +207,7 @@ export default class Generate extends Command { 'template': Flags.string({ char: 't', description: 'Template to use', - options: ['node-vanilla', 'node-react', 'node-vue', 'node-react-todo', 'node-jet', 'node-angular', 'ords-remix-jwt-sample', 'mle-js-basic'], + options: ['node-vanilla', 'node-react', 'node-vue', 'node-react-todo', 'node-jet', 'node-angular', 'ords-remix-jwt-sample', 'mle-ts-sample'], multiple: false }), @@ -375,8 +375,8 @@ export default class Generate extends Command { description: 'This creates a fullstack Concert Application made with Remix that leverages the Oracle REST Data Services functionalities. You will need to configurate the application yourself following the getting started guide.', }, { - name: 'mle-js-basic', - value: 'mle-js-basic', + name: 'mle-ts-sample', + value: 'mle-ts-sample', description: 'This creates an empty project with MLE and Oracle database connection starter code.' }, ], @@ -539,7 +539,7 @@ export default class Generate extends Command { } ); } - if(templateChoice == 'mle-js-basic'){ + if(templateChoice == 'mle-ts-sample'){ // Ask the user for the path to SQLcl Object.assign( configObject, { sqlclPath: sqlclPath === '' ? await input( diff --git a/templates/mle-js-basic/.env.example b/templates/mle-ts-sample/.env.example similarity index 92% rename from templates/mle-js-basic/.env.example rename to templates/mle-ts-sample/.env.example index ef25b39..d8beb2d 100644 --- a/templates/mle-js-basic/.env.example +++ b/templates/mle-ts-sample/.env.example @@ -3,7 +3,7 @@ DB_USER=<%= connectionUsername %> # Database User Password DB_PASSWORD=<%= connectionPassword %> # Connection string to your Autonomous Database/ -# Oracle Database Free instance +# Oracle Database instance CONNECT_STRING=<%= connectionString %> MLE_MODULE= # Optional HTTP Proxy Settings diff --git a/templates/mle-js-basic/.env.example.wallet b/templates/mle-ts-sample/.env.example.wallet similarity index 93% rename from templates/mle-js-basic/.env.example.wallet rename to templates/mle-ts-sample/.env.example.wallet index f8a80d6..694dc40 100644 --- a/templates/mle-js-basic/.env.example.wallet +++ b/templates/mle-ts-sample/.env.example.wallet @@ -6,7 +6,7 @@ DB_USER=<%= connectionUsername %> # Database User Password DB_PASSWORD=<%= connectionPassword %> # Connection string to your Autonomous Database/ -# Oracle Database Free instance +# Oracle Database instance CONNECT_STRING=<%= connectionString %> MLE_MODULE= # Optional HTTP Proxy Settings diff --git a/templates/mle-js-basic/.gitignore.template b/templates/mle-ts-sample/.gitignore.template similarity index 100% rename from templates/mle-js-basic/.gitignore.template rename to templates/mle-ts-sample/.gitignore.template diff --git a/templates/mle-js-basic/README.md b/templates/mle-ts-sample/README.md similarity index 99% rename from templates/mle-js-basic/README.md rename to templates/mle-ts-sample/README.md index 42354cc..7423e45 100644 --- a/templates/mle-js-basic/README.md +++ b/templates/mle-ts-sample/README.md @@ -63,7 +63,7 @@ Make sure it is unzipped and accessible via its full path (e.g. `/Users/yourname ### 1. Create the application -Use the `@create-database-app` command as described in the [top-level readme](https://github.com/oracle/create-database-app/blob/main/README.md) to set up your application. You will be prompted to make a number of choices. Make sure you select `mle-js-basic` as shown here: +Use the `@create-database-app` command as described in the [top-level readme](https://github.com/oracle/create-database-app/blob/main/README.md) to set up your application. You will be prompted to make a number of choices. Make sure you select `mle-ts-sample` as shown here: ``` ? What would you like your application's name to be? demo @@ -71,7 +71,7 @@ Use the `@create-database-app` command as described in the [top-level readme](ht node-angular node-react-todo ords-remix-jwt-sample -❯ mle-js-basic +❯ mle-ts-sample node-vanilla node-react node-vue diff --git a/templates/mle-js-basic/deploy.mjs b/templates/mle-ts-sample/deploy.mjs similarity index 100% rename from templates/mle-js-basic/deploy.mjs rename to templates/mle-ts-sample/deploy.mjs diff --git a/templates/mle-js-basic/package.json b/templates/mle-ts-sample/package.json similarity index 100% rename from templates/mle-js-basic/package.json rename to templates/mle-ts-sample/package.json diff --git a/templates/mle-js-basic/src/index.ts b/templates/mle-ts-sample/src/index.ts similarity index 100% rename from templates/mle-js-basic/src/index.ts rename to templates/mle-ts-sample/src/index.ts diff --git a/templates/mle-js-basic/test/users.test.js b/templates/mle-ts-sample/test/users.test.js similarity index 100% rename from templates/mle-js-basic/test/users.test.js rename to templates/mle-ts-sample/test/users.test.js diff --git a/templates/mle-js-basic/tsconfig.json b/templates/mle-ts-sample/tsconfig.json similarity index 100% rename from templates/mle-js-basic/tsconfig.json rename to templates/mle-ts-sample/tsconfig.json diff --git a/templates/mle-js-basic/utils/database/cleanup.sql b/templates/mle-ts-sample/utils/database/cleanup.sql similarity index 100% rename from templates/mle-js-basic/utils/database/cleanup.sql rename to templates/mle-ts-sample/utils/database/cleanup.sql diff --git a/templates/mle-js-basic/utils/database/initdb.sql b/templates/mle-ts-sample/utils/database/initdb.sql similarity index 100% rename from templates/mle-js-basic/utils/database/initdb.sql rename to templates/mle-ts-sample/utils/database/initdb.sql diff --git a/templates/mle-js-basic/utils/db.mjs b/templates/mle-ts-sample/utils/db.mjs similarity index 100% rename from templates/mle-js-basic/utils/db.mjs rename to templates/mle-ts-sample/utils/db.mjs From 649c625e727605fd35399b435789e1d88df9d77e Mon Sep 17 00:00:00 2001 From: Dmitrii Nikeshkin Date: Wed, 30 Apr 2025 10:17:10 +0200 Subject: [PATCH 12/15] feat: review comments, dependabot label, vitest version --- .github/dependabot.yml | 5 ++++- templates/mle-ts-sample/package.json | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index b3460bc..53be510 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -192,4 +192,7 @@ updates: - package-ecosystem: "npm" directory: "/templates/mle-ts-sample/" schedule: - interval: "monthly" \ No newline at end of file + interval: "monthly" + labels: + - "dependencies" + - "templates-mle" \ No newline at end of file diff --git a/templates/mle-ts-sample/package.json b/templates/mle-ts-sample/package.json index 41c9677..814ecbc 100644 --- a/templates/mle-ts-sample/package.json +++ b/templates/mle-ts-sample/package.json @@ -6,7 +6,7 @@ "typescript": "^5.7.3", "esbuild": "0.25.1", "oracledb": "^6.7.1", - "vitest": "^1.0.0" + "vitest": "^3.1.2" }, "scripts": { "build": "esbuild src/index.ts --bundle --minify=false --platform=neutral --format=esm --outfile=dist/index.js", From 5d8b2b20ad276f3eba09e455d224052414310bc6 Mon Sep 17 00:00:00 2001 From: Dmitrii Nikeshkin Date: Thu, 1 May 2025 16:52:35 +0200 Subject: [PATCH 13/15] feat: added link to SQLcl installation to the prompt --- src/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/index.ts b/src/index.ts index aef0af2..b7688b7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -544,7 +544,7 @@ export default class Generate extends Command { Object.assign( configObject, { sqlclPath: sqlclPath === '' ? await input( { - message: 'Please provide full path to your SQLcl installation: ', + message: 'Please provide full path to your SQLcl installation (SQLcl can be downloaded and installed from https://www.oracle.com/database/sqldeveloper/technologies/sqlcl): ', validate ( input ) { return input.trim().length === 0 ? 'This field cannot be empty!' : true; } From c3cd4fd014e00e7462de66a435a8367bd1777bf7 Mon Sep 17 00:00:00 2001 From: Dmitrii Nikeshkin Date: Tue, 6 May 2025 11:49:40 +0200 Subject: [PATCH 14/15] feat: review comments (indent 3 spaces, test adjustments) --- templates/mle-ts-sample/.env.example | 1 + templates/mle-ts-sample/.env.example.wallet | 1 + templates/mle-ts-sample/README.md | 24 +- templates/mle-ts-sample/deploy.mjs | 16 +- templates/mle-ts-sample/package.json | 32 +- templates/mle-ts-sample/src/index.ts | 460 ++++++++++---------- templates/mle-ts-sample/test/users.test.js | 259 +++++------ templates/mle-ts-sample/tsconfig.json | 16 +- 8 files changed, 406 insertions(+), 403 deletions(-) diff --git a/templates/mle-ts-sample/.env.example b/templates/mle-ts-sample/.env.example index d8beb2d..0bb6999 100644 --- a/templates/mle-ts-sample/.env.example +++ b/templates/mle-ts-sample/.env.example @@ -5,6 +5,7 @@ DB_PASSWORD=<%= connectionPassword %> # Connection string to your Autonomous Database/ # Oracle Database instance CONNECT_STRING=<%= connectionString %> +# Oracle MLE Module name MLE_MODULE= # Optional HTTP Proxy Settings # HTTPS_PROXY= diff --git a/templates/mle-ts-sample/.env.example.wallet b/templates/mle-ts-sample/.env.example.wallet index 694dc40..4dc35f5 100644 --- a/templates/mle-ts-sample/.env.example.wallet +++ b/templates/mle-ts-sample/.env.example.wallet @@ -8,6 +8,7 @@ DB_PASSWORD=<%= connectionPassword %> # Connection string to your Autonomous Database/ # Oracle Database instance CONNECT_STRING=<%= connectionString %> +# Oracle MLE Module name MLE_MODULE= # Optional HTTP Proxy Settings # HTTPS_PROXY= diff --git a/templates/mle-ts-sample/README.md b/templates/mle-ts-sample/README.md index 7423e45..4f21f38 100644 --- a/templates/mle-ts-sample/README.md +++ b/templates/mle-ts-sample/README.md @@ -68,13 +68,13 @@ Use the `@create-database-app` command as described in the [top-level readme](ht ``` ? What would you like your application's name to be? demo ? Which template would you like to use for your project? - node-angular - node-react-todo - ords-remix-jwt-sample +node-angular +node-react-todo +ords-remix-jwt-sample ❯ mle-ts-sample - node-vanilla - node-react - node-vue +node-vanilla +node-react +node-vue (Use arrow keys to reveal more choices) This creates an empty project with MLE and Oracle database connection starter code. ``` @@ -166,10 +166,10 @@ The following SQL code demonstrates how to invoke the MLE module function `newUs ```sql -- Create the user_package package CREATE OR REPLACE PACKAGE user_package AS - FUNCTION newUserFunc(name IN VARCHAR2) RETURN NUMBER; - FUNCTION getUser(id IN NUMBER) RETURN VARCHAR2; - PROCEDURE updateUser(id IN NUMBER, name IN VARCHAR2); - PROCEDURE deleteUser(id IN NUMBER); + FUNCTION newUserFunc(name IN VARCHAR2) RETURN NUMBER; + FUNCTION getUser(id IN NUMBER) RETURN VARCHAR2; + PROCEDURE updateUser(id IN NUMBER, name IN VARCHAR2); + PROCEDURE deleteUser(id IN NUMBER); END user_package; CREATE OR REPLACE PACKAGE BODY user_package AS @@ -236,8 +236,8 @@ Assuming you REST-enabled your schema, and created an ORDS handler for MLE/JavaS ```bash curl -X POST "https://yourserver.com/ords/mle/todo/create" \ - -H "Content-Type: application/json" \ - -d '{"todo_text": "Buy groceries", "user_id": "user123", "category": "personal"}' +-H "Content-Type: application/json" \ +-d '{"todo_text": "Buy groceries", "user_id": "user123", "category": "personal"}' ``` ## Clean Up diff --git a/templates/mle-ts-sample/deploy.mjs b/templates/mle-ts-sample/deploy.mjs index e068dda..44eae7a 100644 --- a/templates/mle-ts-sample/deploy.mjs +++ b/templates/mle-ts-sample/deploy.mjs @@ -22,18 +22,18 @@ const envFilePath = path.resolve(__dirname, '.env'); function updateEnvVariable(key, value) { // Read the existing .env file const envData = fs.readFileSync(envFilePath, 'utf-8'); - + // Check if the variable already exists const regex = new RegExp(`^${key}=.*`, 'm'); if (regex.test(envData)) { - // Update existing variable - const updatedData = envData.replace(regex, `${key}=${value}`); - fs.writeFileSync(envFilePath, updatedData, 'utf-8'); - console.log(`Updated ${key} in .env file`); + // Update existing variable + const updatedData = envData.replace(regex, `${key}=${value}`); + fs.writeFileSync(envFilePath, updatedData, 'utf-8'); + console.log(`Updated ${key} in .env file`); } else { - // Add new variable if not present - fs.appendFileSync(envFilePath, `\n${key}=${value}`, 'utf-8'); - console.log(`Added new variable ${key} to .env file`); + // Add new variable if not present + fs.appendFileSync(envFilePath, `\n${key}=${value}`, 'utf-8'); + console.log(`Added new variable ${key} to .env file`); } } diff --git a/templates/mle-ts-sample/package.json b/templates/mle-ts-sample/package.json index 814ecbc..ebb3f2f 100644 --- a/templates/mle-ts-sample/package.json +++ b/templates/mle-ts-sample/package.json @@ -1,18 +1,18 @@ { - "name": "mleapp", - "version": "1.0.0", - "devDependencies": { - "mle-js": "^23.7.0", - "typescript": "^5.7.3", - "esbuild": "0.25.1", - "oracledb": "^6.7.1", - "vitest": "^3.1.2" - }, - "scripts": { - "build": "esbuild src/index.ts --bundle --minify=false --platform=neutral --format=esm --outfile=dist/index.js", - "deploy": "node deploy.mjs", - "initdb": "node utils/db.mjs utils/database/initdb.sql", - "cleandb": "node utils/db.mjs utils/database/cleanup.sql", - "test": "vitest" - } + "name": "mleapp", + "version": "1.0.0", + "devDependencies": { + "mle-js": "^23.7.0", + "typescript": "^5.7.3", + "esbuild": "0.25.1", + "oracledb": "^6.7.1", + "vitest": "^3.1.2" + }, + "scripts": { + "build": "esbuild src/index.ts --bundle --minify=false --platform=neutral --format=esm --outfile=dist/index.js", + "deploy": "node deploy.mjs", + "initdb": "node utils/db.mjs utils/database/initdb.sql", + "cleandb": "node utils/db.mjs utils/database/cleanup.sql", + "test": "vitest" + } } \ No newline at end of file diff --git a/templates/mle-ts-sample/src/index.ts b/templates/mle-ts-sample/src/index.ts index 29c163f..0a9042d 100644 --- a/templates/mle-ts-sample/src/index.ts +++ b/templates/mle-ts-sample/src/index.ts @@ -1,7 +1,7 @@ enum priorities { - LOW = "low", - MEDIUM = "medium", - HIGH = "high", + LOW = "low", + MEDIUM = "medium", + HIGH = "high", } /** @@ -14,24 +14,22 @@ enum priorities { * @returns {number} The ID of the newly created user. */ export function newUser(name: string): number { - const result = session.execute( - "insert into users (name) values (:name) returning id into :id", - { - name: { - dir: oracledb.BIND_IN, - val: name, - type: oracledb.STRING, - }, - id: { - type: oracledb.NUMBER, - dir: oracledb.BIND_OUT, - }, - }, - ); - - const id = result.outBinds.id[0]; - - return id; + const result = session.execute( + "insert into users (name) values (:name) returning id into :id", + { + name: { + dir: oracledb.BIND_IN, + val: name, + type: oracledb.STRING, + }, + id: { + type: oracledb.NUMBER, + dir: oracledb.BIND_OUT, + }, + }, + ); + const id = result.outBinds.id[0]; + return id; } /** @@ -41,20 +39,20 @@ export function newUser(name: string): number { * @returns {any} An object containing the user's ID and name if found, or null if no user is found. */ export function getUser(id: number): string | null { - const result = session.execute( - "select id, name from users where id = :id", - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - }, - { outFormat: oracledb.OUT_FORMAT_OBJECT } - ); + const result = session.execute( + "select id, name from users where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); - // Return the result as a JSON string so Oracle can handle it - return result.rows?.[0] ? result.rows[0].NAME : null; + // Return the result as a JSON string so Oracle can handle it + return result.rows?.[0] ? result.rows[0].NAME : null; } /** @@ -63,25 +61,25 @@ export function getUser(id: number): string | null { * @param {number} id - The ID of the user to be updated. * @param {string} newName - The new name to be set for the user. * - * @returns {boolean} Returns true if the update was successful. + * @returns {number} Returns number of rows affected. */ -export function updateUser(id: number, newName: string): boolean { - const result = session.execute( - "update users set name = :name where id = :id", - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - name: { - dir: oracledb.BIND_IN, - val: newName, - type: oracledb.STRING, - }, - }, - ); - return true; +export function updateUser(id: number, newName: string) { + const result = session.execute( + "update users set name = :name where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + name: { + dir: oracledb.BIND_IN, + val: newName, + type: oracledb.STRING, + }, + }, + ); + return result.rowsAffected; } /** @@ -90,21 +88,20 @@ export function updateUser(id: number, newName: string): boolean { * Executes an SQL `delete` statement to remove the user from the `users` table. * * @param {number} id - The ID of the user to delete. - * @returns {boolean} Returns true if the deletion was executed successfully. + * @returns {number} Returns number of rows affected. */ -export function deleteUser(id: number): boolean { - const result = session.execute( - "delete from users where id = :id", - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - }, - ); - - return true; +export function deleteUser(id: number) { + const result = session.execute( + "delete from users where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + }, + ); + return result.rowsAffected; } /** @@ -118,29 +115,29 @@ export function deleteUser(id: number): boolean { * @returns {number} The ID of the newly created category. */ export function newCategory(name: string, priority: priorities): number { - const result = session.execute( - "insert into categories (name, prio) values (:name, :prio) returning id into :id", - { - name: { - dir: oracledb.BIND_IN, - val: name, - type: oracledb.STRING, - }, - prio: { - dir: oracledb.BIND_IN, - val: priority, - type: oracledb.STRING, - }, - id: { - type: oracledb.NUMBER, - dir: oracledb.BIND_OUT, - }, - }, - ); + const result = session.execute( + "insert into categories (name, prio) values (:name, :prio) returning id into :id", + { + name: { + dir: oracledb.BIND_IN, + val: name, + type: oracledb.STRING, + }, + prio: { + dir: oracledb.BIND_IN, + val: priority, + type: oracledb.STRING, + }, + id: { + type: oracledb.NUMBER, + dir: oracledb.BIND_OUT, + }, + }, + ); - const id = result.outBinds.id[0]; + const id = result.outBinds.id[0]; - return id; + return id; } /** @@ -151,20 +148,20 @@ export function newCategory(name: string, priority: priorities): number { * @param {number} id - The ID of the category to retrieve. * @returns {any} An object containing the category's ID, name, and priority if found, or null if no category is found. */ -export function getCategory(id: number): any { - const result = session.execute( - "select id, name, prio from categories where id = :id", - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - }, - { outFormat: oracledb.OUT_FORMAT_OBJECT } - ); +export function getCategory(id: number) { + const result = session.execute( + "select id, name, prio from categories where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); - return result.rows?.[0] || null; + return result.rows?.[0] || null; } /** @@ -175,31 +172,31 @@ export function getCategory(id: number): any { * @param {number} id - The ID of the category to be updated. * @param {string} newName - The new name to be set for the category. * @param {priorities} newPriority - The new priority level to be set for the category. It can be one of `priorities.LOW`, `priorities.MEDIUM`, or `priorities.HIGH`. - * @returns {boolean} Returns true if the update was successful. + * @returns {number} Returns number of rows affected. */ -export function updateCategory(id: number, newName: string, newPriority: priorities): boolean { - const result = session.execute( - "update categories set name = :name, prio = :prio where id = :id", - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - name: { - dir: oracledb.BIND_IN, - val: newName, - type: oracledb.STRING, - }, - prio: { - dir: oracledb.BIND_IN, - val: newPriority, - type: oracledb.STRING, - }, - }, - ); +export function updateCategory(id: number, newName: string, newPriority: priorities) { + const result = session.execute( + "update categories set name = :name, prio = :prio where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + name: { + dir: oracledb.BIND_IN, + val: newName, + type: oracledb.STRING, + }, + prio: { + dir: oracledb.BIND_IN, + val: newPriority, + type: oracledb.STRING, + }, + }, + ); - return true; + return result.rowsAffected; } /** @@ -208,21 +205,21 @@ export function updateCategory(id: number, newName: string, newPriority: priorit * Executes an SQL `delete` statement to remove the category from the `categories` table. * * @param {number} id - The ID of the category to delete. - * @returns {boolean} Returns true if the deletion was executed successfully. + * @returns {number} Returns number of rows affected. */ -export function deleteCategory(id: number): boolean { - const result = session.execute( - "delete from categories where id = :id", - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - }, - ); +export function deleteCategory(id: number) { + const result = session.execute( + "delete from categories where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + }, + ); - return true; + return result.rowsAffected; } /** @@ -238,40 +235,40 @@ export function deleteCategory(id: number): boolean { * @returns {number} The ID of the newly created to-do item. */ export function newTodoItem(userId: number, categoryId: number, name: string, completed: boolean = false): number { - const result = session.execute( - `insert into todo_list (u_id, c_id, name, completed) - values (:u_id, :c_id, :name, :completed) - returning id into :id`, - { - u_id: { - dir: oracledb.BIND_IN, - val: userId, - type: oracledb.NUMBER, - }, - c_id: { - dir: oracledb.BIND_IN, - val: categoryId, - type: oracledb.NUMBER, - }, - name: { - dir: oracledb.BIND_IN, - val: name, - type: oracledb.STRING, - }, - completed: { - dir: oracledb.BIND_IN, - val: completed ? 1 : 0, - type: oracledb.NUMBER, - }, - id: { - type: oracledb.NUMBER, - dir: oracledb.BIND_OUT, - }, - } - ); + const result = session.execute( + `insert into todo_list (u_id, c_id, name, completed) + values (:u_id, :c_id, :name, :completed) + returning id into :id`, + { + u_id: { + dir: oracledb.BIND_IN, + val: userId, + type: oracledb.NUMBER, + }, + c_id: { + dir: oracledb.BIND_IN, + val: categoryId, + type: oracledb.NUMBER, + }, + name: { + dir: oracledb.BIND_IN, + val: name, + type: oracledb.STRING, + }, + completed: { + dir: oracledb.BIND_IN, + val: completed ? 1 : 0, + type: oracledb.NUMBER, + }, + id: { + type: oracledb.NUMBER, + dir: oracledb.BIND_OUT, + }, + } + ); - const id = result.outBinds.id[0]; - return id; + const id = result.outBinds.id[0]; + return id; } /** @@ -282,20 +279,20 @@ export function newTodoItem(userId: number, categoryId: number, name: string, co * @param {number} id - The ID of the to-do item to retrieve. * @returns {any} An object containing the to-do item's ID, user ID (u_id), category ID (c_id), name, and completion status (completed) if found, or null if no to-do item is found. */ -export function getTodoItem(id: number): any { - const result = session.execute( - `select id, u_id, c_id, name, completed from todo_list where id = :id`, - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - }, - { outFormat: oracledb.OUT_FORMAT_OBJECT } - ); +export function getTodoItem(id: number) { + const result = session.execute( + `select id, u_id, c_id, name, completed from todo_list where id = :id`, + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); - return result.rows?.[0] || null; + return result.rows?.[0] || null; } /** @@ -306,31 +303,31 @@ export function getTodoItem(id: number): any { * @param {number} id - The ID of the to-do item to be updated. * @param {string} newName - The new name to be set for the to-do item. * @param {boolean} newCompleted - The new completion status to be set for the to-do item. - * @returns {boolean} Returns true if the update was successful. + * @returns {number} Returns number of rows affected. */ -export function updateTodoItem(id: number, newName: string, newCompleted: boolean): boolean { - const result = session.execute( - `update todo_list set name = :name, completed = :completed where id = :id`, - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - name: { - dir: oracledb.BIND_IN, - val: newName, - type: oracledb.STRING, - }, - completed: { - dir: oracledb.BIND_IN, - val: newCompleted ? 1 : 0, - type: oracledb.NUMBER, - }, - } - ); +export function updateTodoItem(id: number, newName: string, newCompleted: boolean) { + const result = session.execute( + `update todo_list set name = :name, completed = :completed where id = :id`, + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + name: { + dir: oracledb.BIND_IN, + val: newName, + type: oracledb.STRING, + }, + completed: { + dir: oracledb.BIND_IN, + val: newCompleted ? 1 : 0, + type: oracledb.NUMBER, + }, + } + ); - return true; + return result.rowsAffected; } /** @@ -339,21 +336,21 @@ export function updateTodoItem(id: number, newName: string, newCompleted: boolea * Executes an SQL `delete` statement to remove the to-do item from the `todo_list` table. * * @param {number} id - The ID of the to-do item to delete. - * @returns {boolean} Returns true if the deletion was executed successfully. + * @returns {number} Returns number of rows affected. */ -export function deleteTodoItem(id: number): boolean { - const result = session.execute( - "delete from todo_list where id = :id", - { - id: { - dir: oracledb.BIND_IN, - val: id, - type: oracledb.NUMBER, - }, - } - ); +export function deleteTodoItem(id: number) { + const result = session.execute( + "delete from todo_list where id = :id", + { + id: { + dir: oracledb.BIND_IN, + val: id, + type: oracledb.NUMBER, + }, + } + ); - return true; + return result.rowsAffected; } /** @@ -364,18 +361,17 @@ export function deleteTodoItem(id: number): boolean { * @param {number} userId - The ID of the user whose to-do items are to be retrieved. * @returns {any[]} An array of objects, each containing the to-do item's ID, category ID (c_id), name, and completion status (completed). */ -export function getTodosByUser(userId: number): any[] { - const result = session.execute( - "select id, c_id, name, completed from todo_list where u_id = :u_id", - { - u_id: { - dir: oracledb.BIND_IN, - val: userId, - type: oracledb.NUMBER, - }, - }, - { outFormat: oracledb.OUT_FORMAT_OBJECT } - ); - - return result.rows || []; +export function getTodosByUser(userId: number) { + const result = session.execute( + "select id, c_id, name, completed from todo_list where u_id = :u_id", + { + u_id: { + dir: oracledb.BIND_IN, + val: userId, + type: oracledb.NUMBER, + }, + }, + { outFormat: oracledb.OUT_FORMAT_OBJECT } + ); + return result.rows || []; } \ No newline at end of file diff --git a/templates/mle-ts-sample/test/users.test.js b/templates/mle-ts-sample/test/users.test.js index c3506fa..f2647bd 100644 --- a/templates/mle-ts-sample/test/users.test.js +++ b/templates/mle-ts-sample/test/users.test.js @@ -5,9 +5,9 @@ import { beforeAll, afterAll, describe, it, expect } from 'vitest'; dotenv.config(); const dbConfig = { - user: process.env.DB_USER, - password: process.env.DB_PASSWORD, - connectString: process.env.CONNECT_STRING, + user: process.env.DB_USER, + password: process.env.DB_PASSWORD, + connectString: process.env.CONNECT_STRING, }; let mleModuleName = process.env.MLE_MODULE; @@ -17,148 +17,153 @@ oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT; oracledb.autoCommit = true; async function executeSQLScript(script) { - let connection; - try { - connection = await oracledb.getConnection(dbConfig); - await connection.execute(script); - console.log('Script executed successfully'); - } catch (err) { - console.error('Error executing script:', err); - throw err; - } finally { - if (connection) { - try { - await connection.close(); - } catch (err) { - console.error('Error closing connection:', err); - } + let connection; + try { + connection = await oracledb.getConnection(dbConfig); + await connection.execute(script); + console.log('Script executed successfully'); + } catch (err) { + console.error('Error executing script:', err); + throw err; + } finally { + if (connection) { + try { + await connection.close(); + } catch (err) { + console.error('Error closing connection:', err); + } + } } - } } async function prepareDatabase() { - const createPackageSQL = ` - CREATE OR REPLACE PACKAGE user_package AS - FUNCTION newUserFunc(name IN VARCHAR2) RETURN NUMBER; - FUNCTION getUser(id IN NUMBER) RETURN VARCHAR2; - PROCEDURE updateUser(id IN NUMBER, name IN VARCHAR2); - PROCEDURE deleteUser(id IN NUMBER); - END user_package; - `; - - const createPackageBodySQL = ` - CREATE OR REPLACE PACKAGE BODY user_package AS - FUNCTION newUserFunc(name IN VARCHAR2) RETURN NUMBER - AS MLE MODULE ${mleModuleName} - SIGNATURE 'newUser(string)'; - - FUNCTION getUser(id IN NUMBER) RETURN VARCHAR2 - AS MLE MODULE ${mleModuleName} - SIGNATURE 'getUser(number)'; - - PROCEDURE updateUser(id IN NUMBER, name IN VARCHAR2) - AS MLE MODULE ${mleModuleName} - SIGNATURE 'updateUser(number, string)'; - - PROCEDURE deleteUser(id IN NUMBER) - AS MLE MODULE ${mleModuleName} - SIGNATURE 'deleteUser(number)'; - END user_package; - `; - - try { - console.log('Creating package...'); - await executeSQLScript(createPackageSQL); - - console.log('Creating package body...'); - await executeSQLScript(createPackageBodySQL); - - console.log('Successfully created package and package body'); - } catch (err) { - console.error('Error during package creation:', err); - } +const createPackageSQL = ` +CREATE OR REPLACE PACKAGE user_package AS + FUNCTION newUserFunc(name IN VARCHAR2) RETURN NUMBER; + FUNCTION getUser(id IN NUMBER) RETURN VARCHAR2; + FUNCTION updateUser(id IN NUMBER, name IN VARCHAR2) RETURN NUMBER; + FUNCTION deleteUser(id IN NUMBER) RETURN NUMBER; +END user_package; +`; + +const createPackageBodySQL = ` +CREATE OR REPLACE PACKAGE BODY user_package AS + FUNCTION newUserFunc(name IN VARCHAR2) RETURN NUMBER + AS MLE MODULE ${mleModuleName} + SIGNATURE 'newUser(string)'; + + FUNCTION getUser(id IN NUMBER) RETURN VARCHAR2 + AS MLE MODULE ${mleModuleName} + SIGNATURE 'getUser(number)'; + + FUNCTION updateUser(id IN NUMBER, name IN VARCHAR2) RETURN NUMBER + AS MLE MODULE ${mleModuleName} + SIGNATURE 'updateUser(number, string)'; + + FUNCTION deleteUser(id IN NUMBER) RETURN NUMBER + AS MLE MODULE ${mleModuleName} + SIGNATURE 'deleteUser(number)'; +END user_package; +`; + + try { + console.log('Creating package...'); + await executeSQLScript(createPackageSQL); + + console.log('Creating package body...'); + await executeSQLScript(createPackageBodySQL); + + console.log('Successfully created package and package body'); + } catch (err) { + console.error('Error during package creation:', err); + } } async function cleanupDatabase() { - const dropPackageSQL = `DROP PACKAGE user_package`; - - try { - console.log('Dropping package...'); - await executeSQLScript(dropPackageSQL); - console.log('Successfully dropped package'); - } catch (err) { - console.error('Error during package drop:', err); - } + const dropPackageSQL = `DROP PACKAGE user_package`; + + try { + console.log('Dropping package...'); + await executeSQLScript(dropPackageSQL); + console.log('Successfully dropped package'); + } catch (err) { + console.error('Error during package drop:', err); + } } async function executeSQLScriptWithOutput(plsql, binds = {}, options = {}) { - let connection; - try { - connection = await oracledb.getConnection(dbConfig); - const result = await connection.execute(plsql, binds, options); - return result; - } catch (err) { - console.error('Error executing PL/SQL:', err); - throw err; - } finally { - if (connection) { - try { - await connection.close(); - } catch (err) { - console.error('Error closing connection:', err); - } + let connection; + try { + connection = await oracledb.getConnection(dbConfig); + const result = await connection.execute(plsql, binds, options); + return result; + } catch (err) { + console.error('Error executing PL/SQL:', err); + throw err; + } finally { + if (connection) { + try { + await connection.close(); + } catch (err) { + console.error('Error closing connection:', err); + } + } } - } } beforeAll(async () => { - await prepareDatabase(); + await prepareDatabase(); }); afterAll(async () => { - await cleanupDatabase(); + await cleanupDatabase(); }); describe('user_package', () => { - it('should create a new user and return its ID', async () => { - const plsql = `BEGIN :id := USER_PACKAGE.NEWUSERFUNC(:name); END;`; - const binds = { - name: 'John Doe', - id: { dir: oracledb.BIND_OUT, type: oracledb.NUMBER }, - }; - - const result = await executeSQLScriptWithOutput(plsql, binds); - createdUserId = result.outBinds.id; - - console.log('New user created with ID:', createdUserId); - expect(createdUserId).toBeTypeOf('number'); - }); - - it('should get a user by ID', async () => { - const plsql = `BEGIN :user := USER_PACKAGE.GETUSER(:id); END;`; - const binds = { - id: createdUserId, - user: { dir: oracledb.BIND_OUT, type: oracledb.STRING }, - }; - - const result = await executeSQLScriptWithOutput(plsql, binds); - console.log('User:', result.outBinds.user); - expect(result.outBinds.user).toBe('John Doe'); - }); - - it('should update a user by ID', async () => { - const plsql = `BEGIN USER_PACKAGE.UPDATEUSER(:id, :name); END;`; - const binds = { id: createdUserId, name: 'Jane Doe' }; - - await executeSQLScriptWithOutput(plsql, binds); - console.log('User updated'); - }); - - it('should delete a user by ID', async () => { - const plsql = `BEGIN USER_PACKAGE.DELETEUSER(:id); END;`; - const binds = { id: createdUserId }; - - await executeSQLScriptWithOutput(plsql, binds); - console.log('User deleted'); - }); + it('should create a new user and return its ID', async () => { + const plsql = `BEGIN :id := USER_PACKAGE.NEWUSERFUNC(:name); END;`; + const binds = { + name: 'John Doe', + id: { dir: oracledb.BIND_OUT, type: oracledb.NUMBER }, + }; + + const result = await executeSQLScriptWithOutput(plsql, binds); + createdUserId = result.outBinds.id; + + console.log('New user created with ID:', createdUserId); + expect(createdUserId).toBeTypeOf('number'); + }); + + it('should get a user by ID', async () => { + const plsql = `BEGIN :user := USER_PACKAGE.GETUSER(:id); END;`; + const binds = { + id: createdUserId, + user: { dir: oracledb.BIND_OUT, type: oracledb.STRING }, + }; + + const result = await executeSQLScriptWithOutput(plsql, binds); + console.log('User:', result.outBinds.user); + expect(result.outBinds.user).toBe('John Doe'); + }); + + it('should update a user by ID', async () => { + const plsql = `BEGIN :affected := USER_PACKAGE.UPDATEUSER(:id, :name); END;`; + const binds = { id: createdUserId, + name: 'Jane Doe', + affected: { dir: oracledb.BIND_OUT, type: oracledb.NUMBER }, }; + + const result = await executeSQLScriptWithOutput(plsql, binds); + console.log('User updated'); + expect(result.outBinds.affected).toBe(1); + }); + + it('should delete a user by ID', async () => { + const plsql = `BEGIN :affected := USER_PACKAGE.DELETEUSER(:id); END;`; + const binds = { id: createdUserId, + affected: { dir: oracledb.BIND_OUT, type: oracledb.NUMBER }, }; + + const result = await executeSQLScriptWithOutput(plsql, binds); + console.log('User deleted'); + expect(result.outBinds.affected).toBe(1); + }); }); diff --git a/templates/mle-ts-sample/tsconfig.json b/templates/mle-ts-sample/tsconfig.json index 7caedf7..3961bb4 100644 --- a/templates/mle-ts-sample/tsconfig.json +++ b/templates/mle-ts-sample/tsconfig.json @@ -1,13 +1,13 @@ { "compilerOptions": { - "module": "ESNext", - "target": "ES2021", - "outDir": "dist", - "lib": ["es6", "es2017", "es2021"], - "types": ["mle-js"], - "noImplicitAny": true, - "strict": true + "module": "ESNext", + "target": "ES2021", + "outDir": "dist", + "lib": ["es6", "es2017", "es2021"], + "types": ["mle-js"], + "noImplicitAny": true, + "strict": true }, "include": ["src"], "exclude": ["node_modules"] - } \ No newline at end of file +} \ No newline at end of file From fee1202208234c118f2a2614d24bbcb677fb19ca Mon Sep 17 00:00:00 2001 From: Dmitrii Nikeshkin Date: Mon, 12 May 2025 20:34:49 +0200 Subject: [PATCH 15/15] feat: addressing PR comments --- src/index.ts | 3 ++- templates/mle-ts-sample/README.md | 7 ++++++- templates/mle-ts-sample/utils/database/cleanup.sql | 7 ++++--- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/src/index.ts b/src/index.ts index b7688b7..786b7ed 100644 --- a/src/index.ts +++ b/src/index.ts @@ -313,7 +313,7 @@ export default class Generate extends Command { const databaseSID = flags['db-sid'] ?? ''; const databaseServiceName = flags['db-service-name'] ?? ''; const databaseUsername = flags['db-username'] ?? ''; - const sqlclPath = flags['sql-cl'] ?? ''; + const sqlclPath = flags['sqlcl'] ?? ''; // TODO: Validate and use wallet path const walletPathDirectory = flags['wallet-path'] ? flags['wallet-path'] : ''; @@ -380,6 +380,7 @@ export default class Generate extends Command { description: 'This creates an empty project with MLE and Oracle database connection starter code.' }, ], + pageSize: 10, default: 'node-vanilla' }, ) : template; diff --git a/templates/mle-ts-sample/README.md b/templates/mle-ts-sample/README.md index 4f21f38..33b7965 100644 --- a/templates/mle-ts-sample/README.md +++ b/templates/mle-ts-sample/README.md @@ -191,7 +191,12 @@ CREATE OR REPLACE PACKAGE BODY user_package AS END user_package; -- Call MLE functions via the user_package in SQL*Plus or SQLcl -EXECUTE USER_PACKAGE.NEWUSERFUNC('EMILY'); +DECLARE + userId NUMBER; +BEGIN + userId := USER_PACKAGE.NEWUSERFUNC('EMILY'); + DBMS_OUTPUT.PUT_LINE(userId); +END; SELECT USER_PACKAGE.GETUSER(5); diff --git a/templates/mle-ts-sample/utils/database/cleanup.sql b/templates/mle-ts-sample/utils/database/cleanup.sql index 7a737e8..1975c2f 100644 --- a/templates/mle-ts-sample/utils/database/cleanup.sql +++ b/templates/mle-ts-sample/utils/database/cleanup.sql @@ -1,4 +1,5 @@ -drop table todo_list; -drop table categories; -drop table users; +drop table todo_list CASCADE CONSTRAINTS PURGE; +drop table categories CASCADE CONSTRAINTS PURGE; +drop table users CASCADE CONSTRAINTS PURGE; + EXIT; \ No newline at end of file