{"id":23672746,"url":"https://github.com/dlwicksell/nodem","last_synced_at":"2025-04-05T23:11:08.550Z","repository":{"id":6415157,"uuid":"7653517","full_name":"dlwicksell/nodem","owner":"dlwicksell","description":"A YottaDB and GT.M database driver and language binding for Node.js","archived":false,"fork":false,"pushed_at":"2024-10-26T21:01:55.000Z","size":1665,"stargazers_count":66,"open_issues_count":1,"forks_count":14,"subscribers_count":26,"default_branch":"master","last_synced_at":"2025-03-29T18:07:54.231Z","etag":null,"topics":["c-plus-plus","gtm","javascript","m","mumps","yottadb"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dlwicksell.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2013-01-16T20:44:15.000Z","updated_at":"2024-10-26T21:01:59.000Z","dependencies_parsed_at":"2024-06-18T21:25:24.279Z","dependency_job_id":null,"html_url":"https://github.com/dlwicksell/nodem","commit_stats":{"total_commits":63,"total_committers":2,"mean_commits":31.5,"dds":"0.015873015873015928","last_synced_commit":"3ed225da22b7a073853fc68bad87d11fac9bf917"},"previous_names":[],"tags_count":58,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dlwicksell%2Fnodem","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dlwicksell%2Fnodem/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dlwicksell%2Fnodem/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dlwicksell%2Fnodem/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dlwicksell","download_url":"https://codeload.github.com/dlwicksell/nodem/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247411235,"owners_count":20934653,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["c-plus-plus","gtm","javascript","m","mumps","yottadb"],"created_at":"2024-12-29T11:18:33.761Z","updated_at":"2025-04-05T23:11:08.517Z","avatar_url":"https://github.com/dlwicksell.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Node Version][version-image]][npm-url]\n[![NPM License][license-image]][npm-url]\n[![NPM Downloads][downloads-image]][npm-url]\n\n# NodeM #\n\n## A YottaDB and GT.M database driver and language binding for Node.js ##\n\nVersion 0.20.9 - 2024 Oct 26\n\n## Copyright and License ##\n\nAddon module written and maintained by David Wicksell \u003cdlw@linux.com\u003e  \nCopyright © 2012-2024 Fourth Watch Software LC\n\nThis program is free software: you can redistribute it and/or modify it under\nthe terms of the GNU Affero General Public License (AGPL) as published by the\nFree Software Foundation, either version 3 of the License, or (at your option)\nany later version.\n\nThis program is distributed in the hope that it will be useful, but WITHOUT ANY\nWARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A\nPARTICULAR PURPOSE. See the GNU Affero General Public License for more details.\n\nYou should have received a copy of the GNU Affero General Public License along\nwith this program. If not, see \u003chttps://www.gnu.org/licenses/\u003e.\n\nFull license text: [AGPL-3.0][license-text]\n\nContact me if you are interested in using Nodem with a different license.\n\n## Summary and Info ##\n\nNodem is an open source addon module that integrates [Node.js][] with the\n[YottaDB][] and [GT.M][] implementations of M, providing in-process access to\ntheir database systems and some language features, as well as networked access\nto their core database functionality. Nodem provides access to the basic global\ndatabase handling operations, as well as providing the ability to invoke M\nlanguage functions and procedures. It also supports full local symbol table\nmanagement and manipulation of the M environment.\n\nAll of Nodem's APIs support synchronous operation and accept arguments passed\nvia a single JavaScript object (except for help, which takes no arguments or an\nargument string, and version, which takes no arguments), containing specific\nper-API properties, usually `global` or `local`, and `subscripts` or\n`arguments`. The APIs that currently support both synchronous and asynchronous\noperation, as well as accepting arguments passed by-position (except `version`,\nwhich takes no arguments, and `merge`, which requires passing arguments via a\nJavaScript object), are: `version`, `data`, `get`, `set`, `kill`, `merge`,\n`order`, `previous`, `nextNode`, `previousNode`, `increment`, `lock`, `unlock`,\n`function`, and `procedure`. In order to use the asynchronous versions of those\nAPIs, you must pass a JavaScript function, taking two arguments - conventionally\n`error` and `result` - as the last argument to the API. When passing arguments\nto those APIs by-position, the first argument would be the global (prefaced by\n`^`), local, or intrinsic special variable string (prefaced by `$` and only\nsupported in the `get` and `set` APIs), and the next set of arguments would be\neach subscript (or function/procedure argument), separated as a different\nargument. In order to specify an intrinsic special variable (in the `get` or\n`set` API) when passing arguments inside of a JavaScript object, use the `local`\nproperty, and preface the name with a `$`. For the `set` API, the last\nnon-function argument would be treated as the data to set into the node.\nAsynchronous support for the rest of the API (`open`, `close`, `configure`,\n`globalDirectory`, and `localDirectory`) is coming soon.\n\nNodem uses the YottaDB and GT.M C Call-in interface. YottaDB has released a\nfaster, low-level database access API, with version r1.20, called the SimpleAPI.\nNodem uses YottaDB's SimpleAPI for the `data`, `get`, `set`, `kill`, `order`,\n`previous`, `nextNode`, `previousNode`, `increment`, `lock`, and `unlock` APIs,\nwhen it is available, and falls back to the Call-in interface when it is not.\n\nYottaDB, LLC. has created extensive documentation for [Nodem][].\n\n**NOTE:** The Nodem Developer API and User Guide [Wiki][] is in development.\n\n## Example Usage ##\n\nUsing Nodem with YottaDB, in the Node.js REPL:\n\n```javascript\n\u003e const ydb = require('nodem').Ydb(); // Create YottaDB connection instance - use Gtm() when connecting to GT.M\nundefined\n\u003e ydb.open(); // Open connection to YottaDB\n{ ok: true, pid: 12345, tid: 12345 }\n\u003e ydb.version();\n'Node.js Adaptor for YottaDB: Version: 0.20.9 (ABI=131) [FWS]; YottaDB Version: 2.00'\n\u003e ydb.get({global: 'v4wTest', subscripts: [0, 2, 0]}); // write ^v4wTest(0,2,0)\n{\n  ok: true,\n  global: 'v4wTest',\n  subscripts: [ 0, 2, 0 ],\n  data: '2 bags of wheat',\n  defined: true\n}\n\u003e ydb.get('^v4wTest', 0, 2, 0); // write ^v4wTest(0,2,0)\n'2 bags of wheat'\n\u003e ydb.get({global: 'v4wTest', subscripts: [0, 2, 0]}, (error, result) =\u003e {if (!error) {console.log('result:', result);}});\nundefined\n\u003e result: {\n  ok: true,\n  global: 'v4wTest',\n  subscripts: [ 0, 2, 0 ],\n  data: '2 bags of wheat',\n  defined: true\n}\n\n\u003e ydb.get('^v4wTest', 0, 2, 0, (error, result) =\u003e {if (!error) {console.log('result:', result);}});\nundefined\n\u003e result: 2 bags of wheat\n\n\u003e ydb.set('^v4wTest', 0, 2, 0, '3 bags of wheat'); // set ^v4wTest(0,2,0)=\"3 bags of wheat\"\ntrue\n\u003e ydb.get({global: 'v4wTest', subscripts: [0, 2, 0]});\n{\n  ok: true,\n  global: 'v4wTest',\n  subscripts: [ 0, 2, 0 ],\n  data: '3 bags of wheat',\n  defined: true\n}\n\u003e ydb.get({global: 'v4wTest', subscripts: ['']});\n{\n  ok: false,\n  errorCode: 150373498,\n  errorMessage:\n   '(SimpleAPI),%YDB-E-NULSUBSC, DB access failed because Null subscripts are not allowed for current region,%YDB-I-GVIS, Global variable: ^v4wTest(\"\")'\n}\n\u003e ydb.close(); // Close connection to YottaDB, releasing resources and restoring terminal settings\nundefined\n```\n\n## Installation ##\n\nNodem should run on every version of Node.js starting with version 0.12.0,\nthrough the current release (v23.1.0 at this time), as well as every version of\nIO.js. However, in the future, both Node.js and the V8 JavaScript engine at its\ncore, could change their APIs in a non-backwards compatible way, which might\nbreak Nodem for that version.\n\nIn order to use the Nodem addon, you will need to have YottaDB (or GT.M)\ninstalled and [configured][get-started] correctly, including setting up your\nenvironment with the required YottaDB (or GT.M) environment variables, or\nsetting the appropriate options in the `open` API. Make sure you have either\n`$ydb_dist` (only applicable for YottaDB) or `$gtm_dist` set to the root of the\nYottaDB (or GT.M) instance before you compile Nodem, whether manually, or via\n`npm`. You will also need to have Node.js installed and working.\n\n**ATTENTION:** These instructions assume that the `nodem` repository has been\ninstalled in your home directory. The paths will likely be different if you have\ninstalled this with `npm`.\n\n**NOTE:** If you have installed Nodem using `npm`, it will attempt to build\n`nodem.node` during installation. If there is a file in the `nodem` directory\ncalled `builderror.log`, and if that file contains no build errors for\n`nodem.node`, it built without issue. It also attempts to pre-compile the\n`v4wNode.m` integration routine, and there might be warnings from that, which\nwon't affect the building of `nodem.node` itself. If you downloaded Nodem any\nother way, including cloning it from its github repository, then you'll have to\nbuild it from source. Remember to make sure that either `$ydb_dist` or\n`$gtm_dist` is set to the root of the YottaDB (or GT.M) instance before building\nNodem. In order to build it, while in the root of the Nodem repository, run the\n`npm run install` command, e.g.\n\n```bash\n$ cd ~/nodem\n$ npm run install\n```\nor\n```bash\n$ node-gyp rebuild 2\u003e builderror.log\n```\n\nIn addition you will need to set a few environment variables, or set the\nappropriate configuration options in the call to the `open` API, in order for\nYottaDB (or GT.M) to find the Call-in table and the `v4wNode.m` routine that it\nmaps to. The Nodem package supplies a sample environment file, called `environ`.\nIt has a commented out command to set `$LD_LIBRARY_PATH` to `$ydb_dist` or\n`$gtm_dist`, which you will need to uncomment if you need it. It is located in\n`~/nodem/resources` and can be sourced into your working environment, either\ndirectly, or from your own environment scripts or profile/login script, e.g.\n\n```bash\n$ cd ~/nodem/resources\n$ source environ\n```\nor\n```bash\n$ echo \"source ~/nodem/resources/environ\" \u003e\u003e ~/.profile\n```\n\nIf you don't source the `environ` file, then you will need to put a copy of\n`v4wNode.m` into a directory that is specified in your `$ydb_routines` (only\napplicable for YottaDB) or `$gtmroutines` routines path, or in the\n`routinesPath` property in your call to the `open` API, so that YottaDB (or\nGT.M) can find it. It is located in the `~/nodem/src` directory. Again, if you\ndon't source the `environ` file, then you will also need to define the `$ydb_ci`\n(only applicable for YottaDB) or `$GTMCI` environment variable, or set the\n`callinTable` property in your call to the `open` API, and point it at the file\n`nodem.ci`, located in the `~/nodem/resources` directory, e.g.\n\n```bash\n$ export ydb_ci=~/nodem/resources/nodem.ci\n$ cp ~/nodem/src/v4wNode.m ~/p\n```\nor\n```javascript\n\u003e const callinTable = process.env.HOME + '/nodem/resources/nodem.ci';\n\u003e const routinesPath = process.env.HOME + '/nodem/src .'; // Make sure to include your routine directories\n\u003e ydb.open({callinTable: callinTable, routinesPath: routinesPath});\n```\n\n**NOTE:** As of Nodem version 0.20.7, if `$ydb_ci` and `$GTMCI` are undefined,\nNodem will use the path to the `nodem.ci` file within the repository, without\nhaving to set the `callinTable` property, by default, simplifying configuration.\n\n**NOTE:** As of Nodem version 0.20.7, if `$ydb_routines` and `$gtmroutines` are\nundefined, Nodem will use the path to the `v4wNode.m` file within the\nrepository, without having to set the `routinesPath` property, by default,\nsimplifying configuration when you don't need to call other M code with the\n`function` or `procedure` APIs.\n\nYou can clone the repository with this command..\n\n```bash\n$ git clone https://github.com/dlwicksell/nodem.git\n```\n\nYou can also install it via `npm` with this command..\n\n```bash\n$ npm install nodem\n```\n\nIf you are having an issue installing `nodem`, you can try this..\n\n```bash\n$ npm install nodem --ignore-scripts\n```\n\nYou can update to the latest version with this command..\n\n```bash\n$ npm update nodem\n```\nor\n```bash\n$ npm install nodem@latest\n```\n\n## Important Notes ##\n\nThe `open` call does not require any arguments, and will connect with the\ndatabase specified in the environment variable `$ydb_gbldir` (only applicable\nfor YottaDB) or `$gtmgbldir`. If you have more than one database and would like\nto connect to a different one than what is defined in your environment, you can\npass an object, with a property called either `globalDirectory` or `namespace`,\ndefined as the path to your global directory file for that database, e.g.\n\n```javascript\n\u003e ydb.open({globalDirectory: process.env.HOME + '/data/globals/db_utf8.gld'});\n```\n\nNodem supports setting up a custom routines path, for resolving calls to other M\nfunctions and procedures, via the `routinesPath` property. Make sure that one of\nthe directories in the `routinesPath` contains the `v4wNode.m` routine, located\nin the Nodem src directory, or its compiled object, `v4wNode.o`, otherwise Nodem\nwill not be fully functional. This could be used to provide some security, by\ngiving access only to certain routines, within a Nodem process, within an\nenvironment that contains routines with unfettered access to the system in its\ndefault environment configuration, e.g.\n\n```javascript\n\u003e const HOME = process.env.HOME;\n\u003e ydb.open({routinesPath: `${HOME}/code/local/r138(${HOME}/code/local)`});\n```\n\nNodem supports setting the Call-in path directly in the `open` call, via the\n`callinTable` property. This can be handy if you are running Nodem in an\nenvironment that has other software that uses the YottaDB (or GT.M) Call-in\ninterface, and you don't want to worry about namespace issues. Nor would you\nneed to set the `$ydb_ci`/`$GTMCI` environment variable, in order for Nodem to\nbe fully functional, e.g.\n\n```javascript\n\u003e ydb.open({callinTable: process.env.HOME + '/nodem/resources/nodem.ci'});\n```\n\n### GT.CM Networking Support ###\n\nYou can configure Nodem to function as a [GT.CM][] client, allowing Nodem to\nconnect with a remote database. In the `open` method, you can set an\n`ipAddress`, and/or a `tcpPort` property, and Nodem will set up the environment\nto connect with a YottaDB (or GT.M) database on a remote server that already has\na GT.CM server running on that address and port. If only `ipAddress` or\n`tcpPort` is defined, the other one will be set with a default value; 127.0.0.1\nfor `ipAddress`, or 6789 for `tcpPort`. Nodem will then set the `$ydb_cm_NODEM`\n(if you are using YottaDB) or `$GTCM_NODEM` (if you are using GT.M) environment\nvariable, for that Nodem process only, with the address and port you set in the\n`open` call, e.g.\n\n```javascript\n\u003e ydb.open({ipAddress: '127.0.0.1', tcpPort: 6789});\n```\n\nIf you are using IPv6, you need to surround your IP address with square\nbrackets, e.g.\n\n```javascript\n\u003e ydb.open({ipAddress: '[::1]', tcpPort: 6789});\n```\n\nYou will also need to create, or modify, a global directory file that maps one\nor more database segments to a data file on the remote server you want to\nconnect with, noting that the prefix to the `-file=` argument in the example\nbelow must be NODEM, in order to match the `$ydb_cm_NODEM`/`$GTCM_NODEM`\nenvironment variable name that Nodem sets up for you, e.g.\n\n```bash\n$ $ydb_dist/mumps -run GDE\nGDE\u003e change -segment DEFAULT -file=NODEM:/home/user/data/globals/gtcm-server.dat\n```\n\nThen on the server you are connecting to, make sure you have the data file set\nup at the same path that you set the `-file=` option to in the global directory\nof your GT.CM client configuration, and have started the GT.CM server on the\nsame IP address and port that you configured in the `open` call in Nodem, e.g.\n\n```bash\n$ $ydb_dist/gtcm_gnp_server -log=gtcm.log -service=6789\n```\n\n**NOTE:** GT.CM only allows remote connections for the database access APIs, not\nthe `function` nor `procedure` APIs. So while using Nodem in a remote GT.CM\nconfiguration, any calls to the `function` or `procedure` APIs will result in\nlocal calls, not remote [RPC] calls. Data nodes accessed by GT.CM cannot\nparticipate in transactions.\n\n### Character Encodings ###\n\nNodem supports two different character encodings, UTF-8 and M. It defaults to\nUTF-8 mode. M mode is similar to ASCII, except that it utilizes all 8 bits in a\nbyte, and it collates slightly differently. Instead of collation based only on\nthe character codes themselves, it sorts numbers before everything else (except\nfor the empty string). The character encoding you set in Nodem is decoupled from\nthe underlying character encoding you have set up for the YottaDB (or GT.M)\nenvironment it is running in. So it is possible to work with UTF-8 encoded data\nin the database, while in Nodem, even if you haven't set up YottaDB (or GT.M) to\nwork with UTF-8 directly. You can set it to UTF-8 mode directly by passing\n`utf-8` or `utf8`, case insensitively, to the `charset` property. If you'd\nrather work with an older byte-encoding scheme, that stores all characters in a\nsingle byte, you can set charset to either `m`, `ascii`, or `binary`, case\ninsensitively. One thing to keep in mind when you do so, is that Node.js\ninternally stores data in UTF-16, but interprets data in UTF-8 in most cases.\nYou can control this through the process stream encoding methods inside of your\nNode.js code. Call those methods to change the encoding to `binary` or `ascii`,\nand it will interpret your data as a byte encoding, using the character glyphs\nin your current locale, e.g.\n\n```javascript\n\u003e process.stdin.setEncoding('binary');\n\u003e process.stdout.setDefaultEncoding('binary');\n\u003e ydb.open({charset: 'm'}); // For all threads\n```\nor\n```javascript\n\u003e process.stdin.setEncoding('binary');\n\u003e process.stdout.setDefaultEncoding('binary');\n\u003e ydb.configure({charset: 'm'}); // For the current thread\n```\n\n### Data Modes ###\n\nThere are currently two different data modes that Nodem supports. The mode can\nbe set to `canonical` or `string`. The default is `canonical`, and interprets\ndata using the M canonical representation. I.e. Numbers will be represented\nnumerically, rather than as strings, and numbers collate before strings (except\nfor the empty string). The other mode, `string`, interprets all data as strings,\nthough they still collate the same as canonical mode, e.g.\n\n```javascript\n\u003e ydb.open({mode: 'canonical'}); // For all threads\n\u003e ydb.get('v4wTest', 'numOfBeds');\n25\n```\nor\n```javascript\n\u003e ydb.configure({mode: 'string'}); // For the current thread\n\u003e ydb.get('v4wTest', 'numOfBeds');\n'25'\n```\n\n### Debug Tracing Mode ###\n\nNodem also has a debug tracing mode, in case something doesn't seem to be\nworking right, or you want to see what happens to data as it moves through the\nNodem APIs. It has four levels of debugging, defaulting to `off`. The other\ndebug levels are `low`, `medium`, and `high`. You can also use the numbers 0-3.\nThe higher the debug level, the more verbose the debug output will be, e.g.\n\n```javascript\n\u003e ydb.open({debug: 'low'}); // For all threads\n```\nor\n```javascript\n\u003e ydb.open({debug: 2}); // For all threads\n```\nor\n```javascript\n\u003e ydb.configure({debug: 'high'}); // For the current thread\n```\n\n### Signal Handling ###\n\nNodem handles several common signals that are typically used to stop processes,\nby closing the database connection, resetting the controlling terminal\nconfiguration, and stopping the Node.js process. These signals include `SIGINT`,\n`SIGTERM`, and `SIGQUIT`. The handling of the `SIGQUIT` signal will also\ngenerate a core dump of the process. All three signal handlers are on by\ndefault. However, you can turn the signal handling on or off directly, via\npassing true or false to a `signalHandler` object (with properties for each of\nthe signals) for each individual signal, or all of them at once, e.g.\n\n```javascript\n\u003e ydb.open({signalHandler: {sigint: true, sigterm: false, sigquit: false}});\n```\nor\n```javascript\n\u003e ydb.open({signalHandler: false});\n```\n\n### Function and Procedure Auto-relink ###\n\nNodem supports a feature called auto-relink, which will automatically relink a\nroutine object containing any function or procedure called by the `function` or\n`procedure` API. By default auto-relink is off. You can enable it in one of four\nways. First, you can pass it as a property of the JavaScript object argument\nwhich is passed to the `function` or `procedure` API directly, with a value of\ntrue. This will turn on auto-relink just for that call. You can also disable it,\nby setting `autoRelink` to false if it was already enabled by one of the global\nsettings, e.g.\n\n```javascript\n\u003e ydb.function({function: 'version^v4wTest', autoRelink: true});\n```\n\nSecond, you can enable it globally, for every thread, and for every call to the\n`function` (or `procedure`) API, by setting the same property in a JavaScript\nobject passed to the `open` API, e.g.\n\n```javascript\n\u003e ydb.open({autoRelink: true});\n```\n\nThird, you can enable it globally, per-thread, for every call to the `function`\n(or `procedure`) API, by setting the same property in a JavaScript object passed\nto the `configure` API, e.g.\n\n```javascript\n\u003e ydb.configure({autoRelink: true});\n```\n\nFourth, you can also enable it globally, for every thread, by setting the\nenvironment variable NODEM_AUTO_RELINK to 1, or any other non-zero number, e.g.\n\n```bash\n$ export NODEM_AUTO_RELINK=1\n$ node function.js\n```\nor\n```bash\n$ NODEM_AUTO_RELINK=1 node function.js\n```\n\n### Asynchronous APIs ###\n\nNodem's asynchronous APIs, do their work in a separate thread pool,\npre-allocated by Node.js via libuv. By default, four threads are created, and\nwill take turns executing each asynchronous call, including asynchronous calls\nfrom other APIs. Nodem supports setting a different value for the pre-allocated\nthread pool for asynchronous calls, in its `open` API, up to a max of 1024, in\nthe latest versions of Node.js, e.g.\n\n```javascript\n\u003e ydb.open({threadpoolSize: 1024});\n```\n\nHowever, if your Node.js process executes any call asynchronously, from any API\nor module, before you open the database connection with Nodem, then the\nthreadpoolSize property is ignored. So make sure you open the database\nconnection first in any process, if you want to control how large the\npre-allocated thread pool is.\n\n**NOTE:** The Node.js core worker_thread API, which also allocates threads from\nthe same worker thread pool in libuv, allows complete control of creating and\ndestroying threads, and does not utilize the threadpoolSize (which just sets the\nlibuv environment variable `UV_THREADPOOL_SIZE`) set in the Nodem `open` API.\n\n### Terminal Handling ###\n\nYottaDB (and GT.M) changes some settings of its controlling terminal device, and\nNodem resets them when it closes the database connection. By default, Nodem will\nrestore the terminal device to the state it was in when the `open` call was\ninvoked. Normally this is the desired option, however, if you wish to reset the\nterminal to typically sane settings, the `close` call allows this by setting the\n`resetTerminal` property to true, e.g.\n\n```javascript\n\u003e ydb.close({resetTerminal: true});\n```\n\n### Worker Threads ###\n\nNodem supports the Worker Threads [API][worker-threads], for both synchronous\nand asynchronous calls. Since YottaDB and GT.M are single-threaded, opening and\nclosing a connection to their database and runtime, should only be done once per\nprocess lifetime. Nodem's `open` and `close` APIs will only work when called\nfrom the main thread of the process. In order to work with the worker threads\nAPI, you should call the Nodem `open` API in the main thread before creating any\nworker threads, and you should call the Nodem `close` API in the main thread,\nafter all the worker threads have exited. You will still need to make sure that\nyou require Nodem in each worker thread, as well as the main thread, in order to\nhave access to the Nodem API in each thread.\n\n### Configure API ###\n\nNodem has a `configure` API, which will allow worker threads to change some\nper-thread database configuration options. It can be called from the worker\nthreads, or the main thread, and will allow you to change per-thread\nconfiguration options as often as you like. There are four configuration options\nthat are set per-thread. They can be set in the `open` API, by the main thread,\nbefore any other Nodem calls are made, or they can be set in the `configure`\nAPI, anytime you like, in the main thread, or in the worker threads. Those\nconfiguration options are: `charset`, `mode`, `autoRelink`, and `debug`.\n\n### Transaction API ###\n\nNodem has a `transaction` API, which provides support for full ACID\ntransactions. It is only supported when running Nodem with YottaDB at this time.\nIt requires, as its first argument, a JavaScript function, which takes no\narguments, and which can contain other Nodem calls, nested `transaction` calls,\nor any JavaScript code. The JavaScript function will be run within a transaction\nby YottaDB. It will also be run synchronously, and every Nodem API that is\ncalled within the transaction must also be run synchronously. By default,\ntransactions are run in serial mode (providing full ACID semantics), and no\nlocal variables are reset during transaction restarts. You can pass an optional\nsecond argument; a JavaScript object, with one or two properties. The properties\nare `variables`, an array of local variables that are reset to their values\nbefore the transaction started, whenever a transaction is restarted, and `type`,\nwhich if set to `Batch` (`batch` or `BATCH` will also work) will run the\ntransaction in batch mode, (which does not provide durability, but does provide\nthe rest of the ACID semantics). If `variables` has `'*'` as its only array\nitem, then every local variable in the symbol table will be reset during a\ntransaction restart.\n\nIn order to restart a transaction, pass the string 'Restart' ('restart',\n'RESTART', or the `tpRestart` property will also work), as the argument to the\nreturn statement. In order to rollback a transaction, pass the string 'Rollback'\n('rollback', 'ROLLBACK', or the `tpRollback` property will also work), as the\nargument to the return statement. Any other argument to the return statement\nwill commit the transaction, including functions without a return statement.\nWhen you call a Nodem API within a transaction, make sure to check for returned\nerrors, and return with 'Rollback' in that case. If any Nodem API within a\ntransaction returns with an error code of `YDB_TP_RESTART` (a YottaDB restart\ncode), or with an error code of `YDB_TP_ROLLBACK` (a YottaDB rollback code) make\nsure to return with the appropriate transaction message, 'Restart' or 'Rollback'\nrespectively, or simply return with the `YDB_TP_*` error code directly. In order\nto make it simpler to test for restart and rollback error codes from the YottaDB\ntransaction engine, when more sophisticated logic is desired, Nodem stores the\nrestart code in the `tpRestart` property, and the rollback code in the\n`tpRollback` property, for convenience.\n\nIf you throw a JavaScript error inside of the transaction function, it will be\nwritten to standard error, and will cause a rollback operation to occur. If you\nhandle it with a try-catch block, then what happens will depend upon how you\nhandle it, and whether you throw another error, or return with a specific\ntransaction processing message or not. Transaction code should be as short and\nsimple as possbile, and should avoid calling any code with side effects, e.g.\n\n```javascript\n\u003e ydb.transaction(() =\u003e {\n    let flag = ydb.get({global: 'v4wTest', subscripts: ['flag']});\n\n    if (flag.errorCode === ydb.tpRestart) return 'Restart';\n    if (!flag.ok) return 'Rollback';\n\n    let data = ydb.get({global: 'v4wTest', subscripts: ['data']});\n\n    if (data.errorCode === ydb.tpRestart) return 'Restart';\n    if (!data.ok) return 'Rollback';\n\n    if (data.data \u003c 0) return 'Rollback';\n\n    if (data.data \u003c 10) {\n        let increment = ydb.increment({global: 'v4wTest'});\n\n        if (increment.errorCode === ydb.tpRestart) return 'Restart';\n        if (!increment.ok) return 'Rollback';\n    }\n\n    let type = ydb.get({global: 'v4wTest', subscripts: ['type']});\n\n    if (type.errorCode === ydb.tpRestart) return 'Restart';\n    if (!type.ok) return 'Rollback';\n\n    let test = ydb.set({global: 'v4wTest', data: type + ':' + data});\n\n    if (test.errorCode === ydb.tpRestart) return 'Restart';\n    if (!test.ok) return 'Rollback';\n\n    return 'Commit';\n});\n```\n\nEven though the `transaction` API runs synchronously, it is fully compatible\nwith the Worker Threads API. By creating a new worker thread and running the\n`transaction` API, and any other APIs it calls in it, you can emulate an\nasynchronous pattern, as the running transaction will not block the main thread,\nor any of the other worker threads. For an example of this pattern, see the\nsupplied `transaction.js` program in the `examples` directory.\n\n### Procedure API ###\n\nNodem has a `procedure` or `routine` API, which is similar to the `function`\nAPI, except that it is used to call M procedures or routines, which do not\nreturn any values. If the `procedure` API is called via a JavaScript object,\nthen the object must contain the required `procedure`/`routine` property, set to\nthe name of the procedure/routine. It may also contain an optional property,\ncalled `arguments`, which is an array of arguments to pass to the\nprocedure/routine. It can also be called by-position, just like the `function`\nAPI. It also supports the `autoRelink` option, just as described in the\n`function` API, e.g.\n\n```javascript\n\u003e ydb.procedure({procedure: 'set^v4wTest', arguments: ['test', 5]});\n```\nor\n```javascript\n\u003e ydb.procedure('set^v4wTest', 'test', 5);\n```\n\n### Lock API ###\n\nThe `lock` API takes an optional `timeout` argument. If you do not set a\ntimeout, it will wait to acquire the lock indefinitely. If you wish to come back\nfrom the call right away, if the lock is not available, simply pass a timeout\nargument of 0, e.g.\n\n```javascript\n\u003e ydb.lock({global: 'v4wTest', timeout: 5});\n```\nor\n```javascript\n\u003e ydb.lock({global: 'v4wTest', timeout: 0});\n```\n\n### Kill API ###\n\nThe `kill` API takes an optional `nodeOnly` argument. It can be set to true or\nfalse, defaulting to false. If set to true, then it will only remove the node\nthat is passed to it; if set to false, then it will remove the node passed to\nit, and all of its children, or the full sub-tree, e.g.\n\n```javascript\n\u003e ydb.kill({global: 'v4wTest', nodeOnly: true});\n```\nor\n```javascript\n\u003e ydb.kill({local: 'v4wTest', nodeOnly: true});\n```\n\nThe `nodeOnly` option is available when calling the `kill` API by passing\narguments in a single JavaScript object, like above, but not when passing\narguments by-position.\n\n### Additional Features ###\n\nNodem provides a built-in API usage help menu. By calling the `help` method\nwithout an argument, Nodem will display a list of APIs and a short description\nof what they do. Calling the help method with an argument string of one of those\nAPIs will display more in-depth usage information for that method.\n\nNodem supports full M local symbol table manipulation with the current APIs. In\norder to use it, instead of defining a `global` property in your argument\nobject, you define a `local` property. For APIs that support passing arguments\nby-position, you signify that you want them to work on a global, by using a `^`\nas the first character of the first argument, otherwise they are working on a\nlocal variable. For the get and set APIs, if the first character of the first\nargument is a `$`, then you are working with an intrinsic special variable\n(ISV). There is also a `localDirectory` API, that works the same way as the\n`globalDirectory` API, except that it lists the local symbols in the symbol\ntable, rather than the globals in the database. One caveat is that you cannot\nmanipulate any local variable that begins with `v4w`, as Nodem internally uses\nthat namespace to implement the `v4wNode.m` integration routine. You can also\ncall the `kill` API with no arguments, and it will clear the local symbol table.\nThis functionality will allow you to call legacy M functions and procedures,\nwithout having to write M routine wrappers. Here is an example of using the\nlocal symbol table functionality to call a legacy API directly from Nodem. In\nthis example, the local variable, 'U', needs to be set before this API is\ncalled, as it expects it to be defined already. You can also see how the local\nsymbol table changes, after setting the required local variable, making the\ncall, and then clearing the symbol table, e.g.\n\n```javascript\n\u003e ydb.localDirectory();\n[]\n\u003e ydb.set({local: 'U', data: '^'});\n{ ok: true, local: 'U', data: '^' }\n\u003e ydb.localDirectory();\n[ 'U' ]\n\u003e ydb.procedure({procedure: 'AGET^ORWORR', arguments: [, 9, '2^0', 13, 0]});\n{\n  ok: true,\n  procedure: 'AGET^ORWORR',\n  arguments: [ \u003c1 empty item\u003e, 9, '2^0', 13, 0 ]\n}\n\u003e ydb.localDirectory();\n[ 'DILOCKTM', 'DISYS', 'DT', 'DTIME', 'DUZ', 'IO', 'U', 'XPARSYS' ]\n\u003e ydb.kill();\ntrue\n\u003e ydb.localDirectory();\n[]\n```\n\nNodem supports calling functions and procedures with arguments passed\nby-reference, or by-variable, in addition to the standard passing by-value.\nThis will allow someone who needs to interface Nodem with legacy M APIs that\nrequire using local variables in this manner, the ability to do so directly in\nNodem, rather than having to write an M wrapper around the API, and calling that\nfrom Nodem. In order to use this functionality, you need to pass your arguments\nvia a specially formatted object, in order to instruct Nodem that you wish to\npass arguments differently than normal. This is necessary because if you tried\nto pass an argument by-reference or by-variable directly, Node.js will try to\ndereference it as a local JavaScript variable, and you would never be able to\nrefer to the right symbol in the back-end M environment. The structure of the\nspecially formatted object is simple. It contains a `type` property, which can\nbe one of three values: `reference`, `variable`, or `value`; and it also\ncontains a `value` property which contains the name you want to use when the\ntype is `reference` or `variable`, and the actual data you want to pass if type\nis `value`. The `value` type is there for consistency, but you would normally\njust pass arguments by value directly, without resorting to this specially\nformatted argument object. Here is an example of how you could use this\nfunctionality, while calling a legacy M API, many of which require passing\narguments in this fashion, e.g.\n\n```javascript\n\u003e ydb.set({local: 'U', data: '^'});\n{ ok: true, local: 'U', data: '^' }\n\u003e const arg = {type: 'reference', value: 'LIST'};\nundefined\n\u003e ydb.procedure({procedure: 'LISTALL^ORWPT', arguments: [arg, 'A', 1]});\n{\n  ok: true,\n  procedure: 'LISTALL^ORWPT',\n  arguments: [ { type: 'reference', value: 'LIST' }, 'A', 1 ]\n}\n\u003e ydb.localDirectory();\n[ 'LIST', 'U' ]\n\u003e ydb.data({local: 'LIST'});\n{ ok: true, local: 'LIST', defined: 10 }\n\u003e ydb.nextNode({local: 'LIST'});\n{\n  ok: true,\n  local: 'LIST',\n  subscripts: [ 1 ],\n  data: '1^ZZ PATIENT,TEST ONE^^^^ZZ PATIENT,TEST ONE',\n  defined: true\n}\n\u003e ydb.nextNode({local: 'LIST', subscripts: [1]});\n{\n  ok: true,\n  local: 'LIST',\n  subscripts: [ 2 ],\n  data: '3^ZZ PATIENT,TEST THREE^^^^ZZ PATIENT,TEST THREE',\n  defined: true\n}\n\u003e ydb.nextNode({local: 'LIST', subscripts: [2]});\n{\n  ok: true,\n  local: 'LIST',\n  subscripts: [ 3 ],\n  data: '2^ZZ PATIENT,TEST TWO^^^^ZZ PATIENT,TEST TWO',\n  defined: true\n}\n\u003e ydb.nextNode({local: 'LIST', subscripts: [3]});\n{ ok: true, local: 'LIST', defined: false }\n```\n\n## Interface ##\n\nAPI                      | Description\n-------------------------|------------------------------------------------------------------------------------------------------\n*open*                   | Open the database connection\n*configure*              | Configure per-thread parameters of the database connection\n*close*                  | Close the database connection\n*help*                   | Display a help menu of method usage\n*version* or *about*     | Display version information; if database connection open, display its version\n*data*                   | Determine whether a global or local node has data and/or children\n*get*                    | Retrieve the value of a global, local, or intrinsic special variable node\n*set*                    | Set a global, local, or intrinsic special variable node, to a new value\n*kill*                   | Delete a global or local node, and optionally, all of its children; or delete all local variables\n*merge*                  | Merge a global or local tree/sub-tree, or data node, to a global or local tree/sub-tree, or data node\n*order* or *next*        | Retrieve the next global or local node, at the current subscript level\n*previous*               | Same as order, only in reverse\n*nextNode*               | Retrieve the next global or local node, regardless of subscript level\n*previousNode*           | Same as nextNode, only in reverse\n*increment*              | Atomically increment the value stored in a global or local node\n*lock*                   | Lock a global or global node, or local or local node, incrementally\n*unlock*                 | Unlock a global or global node, or local or local node, incrementally; or release all locks\n*transaction*            | Call a JavaScript function within a YottaDB transaction - synchronous only\n*function*               | Call an extrinsic function\n*procedure* or *routine* | Call a procedure/routine\n*globalDirectory*        | List the names of the globals in the database\n*localDirectory*         | List the names of the variables in the local symbol table\n*retrieve*               | Not yet implemented\n*update*                 | Not yet implemented\n\n## Disclaimer ##\n\nFourth Watch Software endeavors not to make any breaking changes to APIs, but as\nNodem is still in development, its interface may change in future versions.\n\n## Contact Info ##\n\nIf you have any questions or feature requests, email me at \u003cdlw@linux.com\u003e  \nTo report any issues, visit \u003chttps://github.com/dlwicksell/nodem/issues\u003e\n\n## See Also ##\n\n* The [Node.js][] server-side JavaScript runtime.\n* The [YottaDB][] implementation of M.\n* The [GT.M][] implementation of M.\n\n[version-image]: https://img.shields.io/node/v/nodem.svg\n[license-image]: https://img.shields.io/npm/l/nodem.svg?colorB=blue\n[downloads-image]: https://img.shields.io/npm/dm/nodem.svg?colorB=orange\n[npm-url]: https://npmjs.org/package/nodem\n[license-text]: https://github.com/dlwicksell/nodem/blob/HEAD/COPYING\n[get-started]: https://yottadb.com/product/get-started\n[worker-threads]: https://nodejs.org/api/worker_threads.html\n\n[Node.js]: https://nodejs.org\n[YottaDB]: https://yottadb.com\n[GT.M]: https://sourceforge.net/projects/fis-gtm\n[Nodem]: https://docs.yottadb.com/MultiLangProgGuide/jsprogram.html\n[Wiki]: https://github.com/dlwicksell/nodem/wiki\n[GT.CM]: https://docs.yottadb.com/AdminOpsGuide/gtcm.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdlwicksell%2Fnodem","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdlwicksell%2Fnodem","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdlwicksell%2Fnodem/lists"}