NAME
ort-nodejs —
    generate node.js module
SYNOPSIS
| ort-nodejs | [ -ev] [-Ndb] [config...] | 
DESCRIPTION
Accepts ort(5) config files, defaulting to standard input, and generates a Node module in TypeScript. Its arguments are as follows:
- -e
- Embedded output. Does not emit importstatements, nor are top-level namespaces, classes, and functions marked asexport. This is useful for embedding instead of using as a module. This flag may be deprecated in the future.
- -v
- Output Validation object and types.
- -Nbd
- Disable production of output, which may be b and/or d. The b flag suppresses output of the foundational Data structures, while d suppresses the Module and Database access methods. This flag is used when creating multiple output files.
The output requires only the "typescript", "better-sqlite3", "bcrypt", and optionally "validator" dependencies. The output is documented using the typedoc(1) language.
Module
An application context is initialised with the top-level function. It throws an exception on database error.
- ort(dbname: string, args?: ortargs): ortdb
- Creates an application-wide database instance at dbname, an existing sqlite3 database usually created with ort-sql(1). The optional args consists of configuration parameters. If provided, it must define the bcrypt_cost property with a valid number of encryption rounds. If args is not given, the number of rounds defaults to 10.
The ortdb object has the following properties and methods:
- connect(): ortctx
- Connect to the database. This should be invoked for each request. In applications not having a request, this corresponds to a single operator sequence. If roles are enabled, the connection will begin in the "default" role.
- args: ortargs
- Instance-wide configuration. If not set by ort(), is set to the default values.
- version: string
- The version of ort-nodejsused to create the module.
- vstamp: number
- The numeric version of ort-nodejsused to create the module.
Each sequence of database interaction begins with
    the
    connect()
    method from the application-wide instance. The resulting
    ortctx object has the following role methods:
- db_role(newrole: string): void
- If roles are enabled, move from the current role to
      newrole. If the role is the same as the current
      role, this does nothing. Roles may only transition to ancestor roles, not
      descendant roles or siblings, or any other non-ancestor roles. The only
      exception is when leaving "default" or entering
      "none". This does not return failure: on role violation, it
      invokes
      process.abort().
- db_role_current(): string
- If roles are enabled, get the currently-assigned role. If
      db_role() hasn't yet been called, this will be "default".
The following ortctx methods should be used for transaction management. These throw an exception on database error.
- db_trans_open_immediate(id: number): void
- Open a transaction with a unique identifier id. This is the preferred way of creating database transactions. The transaction immediately enters unshared lock mode (single writer, readers allowed).
- db_trans_open_deferred(id: number): void
- Open a transaction with a unique identifier id. The transaction locks the database on first access with shared locks (no writes allowed, reads allowed) on queries and unshared locks (single writer, reads allowed) on modification.
- db_trans_open_exclusive(id: number): void
- Open a transaction with a unique identifier id. The transaction locks exclusively, preventing all other access.
- db_trans_commit(id: number): void
- Commit a transaction opened by
      db_trans_open_immediate(),db_trans_open_deferred(), ordb_trans_open_exclusive() with identifier id.
- db_trans_rollback(id: number): void
- Roll-back a transaction opened by
      db_trans_open_immediate(),db_trans_open_deferred(), ordb_trans_open_exclusive() with identifier id.
Any insert statements are output as
    follows. Let "foo" be the name of the exemplar structure in this
    and subsequent prototypes. Throws an exception on database error.
- db_foo_insert(ARGS): bigint
- Insert a row and return its identifier or -1 on constraint violation. This
      accepts all native fields ARGS as parameters
      excluding rowid, which is automatically set by the database. If any fields are specified asnull, they may be passed asnullvalues.
Query statements count,
    iterate, list,
    search are output as follows. These throw an
    exception on database error.
- db_foo_count(): bigint
- Like
      db_foo_count_xxxx() but returning a count of all rows.
- db_foo_count_xxxx(ARGS): bigint
- Like
      db_foo_get_xxxx(), but returning a count of responses.
- db_foo_count_by_xxxx_op1_yy_zz_op2(ARGS): bigint
- Like
      db_foo_get_by_xxxx_op1_yy_zz_op2(), but returning a count of responses.
- db_foo_get_xxxx(ARGS): ortns.foo|null
- The searchstatement named "xxxx". The function accepts variables for all binary-operator fields to check (i.e., all except for those checking for null).
- db_foo_get_by_xxxx_op1_yy_zz_op2(ARGS): ortns.foo|null
- Like db_foo_get_xxxx(), but for (possibly-nested) structures. In the given example, "xxxx" is a field in the given structure with operation "op1" and "yy_zz" means a field "zz" in the nested structure "yy" with operation "op2".
- db_foo_iterate(ARGS, cb): void
- Like
      db_foo_iterate_xxxx() but iterating over all rows.
- db_foo_iterate_xxxx(ARGS, cb): void
- Like db_foo_get_xxxx(), but invoking a function callback per row. The cb callback accepts a single parameter of type ortns.foo and does not have a return value.
- void
    db_foo_iterate_by_xxxx_op1_yy_zz_op2(ARGS, cb): void
- Like db_foo_get_by_xxxx_op1_yy_zz_op2(), but invoking a function callback for each retrieved result.
- db_foo_list(): ortns.foo[]
- Like
      db_foo_list_xxxx() but allocating and filling a queue of all rows.
- db_foo_list_xxxx(ARGS): ortns.foo[]
- Like db_foo_get_xxxx(), but producing an array of responses.
- db_foo_list_by_xxxx_op1_yy_zz_op2(ARGS): ortns.foo[]
- Like db_foo_get_by_xxxx_op1_yy_zz_op2(), but producing a queue of responses.
Any update statements in the configuration
    are output as the following methods on ortctx. These
    throw an exception on database error.
- db_foo_update_xxxx(ARGS): boolean
- Run the named update function "xxxx". The update functions are
      specified with updatestatements. The parameters passed to this function are first the fields to modify, then the fields that constrain which rows are updated. Update fields are only specified for operations for binary-operator constraints, i.e., those not checking for null status. Returns true on success, false on constraint failure.
- db_foo_update_xx_mod_by_yy_op(ARGS): boolean
- Like db_foo_update_xxxx(), but using an un-named update statement modifying "xx" with modifier "mod" constrained by "yy" with operation "op". Either or both modifiers and constraints may be empty. If modifiers are empty, all fields are modified by setting. If constraints are empty, they and the preceding "by" are omitted.
Any delete statements in the configuration
    are output as the following methods on ortctx. These
    throw an exception on database error.
- db_foo_delete_xxxx(ARGS): void
- Run the named deletefunction "xxxx". The ARGS passed to this function are the fields that constrain which rows are deleted. Parameters are only specified for operations for binary-operator constraints, i.e., those not checking for null status.
- db_foo_delete_by_yy_op(ARGS): void
- Like db_foo_delete_xxxx(), but using an un-nameddeletestatement constrained by "yy" with operation "op".
Exceptions
All database functions will throw an exception if errors occur in talking to the underlying database. The exception object returned is of type SqliteError, inheriting from the generic Error. It is documented in the "better-sqlite3", but consists of two pertinent members:
- code: string
- The extended sqlite3 error code in string format. For example, the SQLITE_IOERR_FSTAT(3) error code is rendered as the string "SQLITE_IOERR_FSTAT".
- message: string
- A free-form message describing the error.
Data structures
Any enumerations or bitfields (enum,
    bitf) in the configuration are output in the
    ortns namespace and named as themselves. Each
    enumeration item is serialised as a string property.
    Bitfields have two property per item: one for the bit index, one for the
    produced mask. These are prefixed by "BITI" and "BITF",
    respectively.
namespace ortns {
	enum anenum {
		item2 = '3'
	}
	enum abitf {
		BITI_item2 = '3',
		BITF_item2 = '8',
	}
}
Each struct is output as an interface in
    the ortns namespace and named as itself followed by
    "Data", such as ortns.fooData. All items of
    the structure are mapped to properties with the following types:
| bit | bigint | 
| bits | bigint | 
| blob | Buffer | 
| date | bigint | 
| email | string | 
| enum | ortns.enum | 
| epoch | bigint | 
| int | bigint | 
| password | string | 
| real | number | 
| text | string | 
Since bitfields are combinations of bits in their respective enumerations, they are represented by bigint and not the enumeration type.
If a field is marked as null, it will also
    be given the null type, such as
    bigint|null.
namespace ortns {
        interface barData {
                anint: bigint;
                astring: string;
		anulldate: bigint|null;
        }
}
Data objects returned by query methods are output as classes in the ortns namespace and named as in the configuration. Letting "foo" be an exemplar structure name, ortns.foo, the object consists of the following.
- obj: ortns.fooData
- The read-only data itself.
- export(): any
- Create an exportable object. Export rules are governed by the role in
      which the object was created. This is usually used with
      JSON.stringify() to output JSON objects.
The exported object, when converted into a string, is readable by applications using the ort-javascript(1) tool.
Validation
If run with -v,
    ort-nodejs outputs validation functions for each
    native field type in an object ortvalid.ortValids,
    with a validator for each field. The fields (object properties) are named
    struct-field.
Validator functions are typed according to their mapped field
    types as described in Data
    structures: (value?: any) => TYPE|null, and
    accept the value (which may be undefined) of the request input. These return
    null when the input is undefined,
    undefined, null, fails any
    user-defined validation, or the following:
| bit | not in 0–63 | 
| date | not ISO-8601 format | 
| epoch | not 64-bit signed integer | 
| int | not 64-bit signed integer | 
| real | not 64-bit decimal number | 
| email | not valid e-mail | 
| bits | not contained in 64 bits | 
EXIT STATUS
The ort-nodejs utility exits 0 on
    success, and >0 if an error occurs.
EXAMPLES
The following example is a full web-server running on port 3000 using the Node framework. It uses the "express", framework for web requests, "validator" for input validation, "bcrypt" for passwords, and "better-sqlite3" for the database. It mandates the use of TypeScript instead of JavaScript. It needs only the npm(1) system installed and (depending on the operating system) a C/C++ compiler for native packages.
Begin a project (if not already begun) as follows:
% cd myproject % npm init -y % npm install typescript better-sqlite3 express bcrypt % npm install @types/express @types/bcrypt @types/better-sqlite3 % npx tsc --init
For validation:
% npm install validator @types/validator
When installing "better-sqlite3" on OpenBSD, you may need to specify an alternate Python interpreter:
% PYTHON=/usr/local/bin/python2.7 \ npm install better-sqlite3
Older versions of OpenBSD required
    overriding CXX and CC with
    ports-installed versions for both "better-sqlite3" and
    "bcrypt".
Modify package.json to mandate the use of TypeScript instead of JavaScript:
[...]
"main": "index.ts",
"scripts": {
  "tsc": "tsc"
}
[...]
Next, modify tsconfig.json to use a more up-to-date output type for JavaScript, otherwise many TypeScript security idioms will not be available.
"target": "es2015",
Now use the following toy ort(5) configuration installed as myproject.ort:
roles {
  role user;
};
struct user {
  field name text limit gt 0;
  field id int rowid;
  insert;
  search id: name id;
  roles default { all; };
};
Compile the configuration as a module. This assumes that validation is also required.
% mkdir modules % ort-nodejs -v myproject.ort > modules/ort.ts
Use the following simple application:
import express from 'express';
import { ort, ortns, ortctx, ortdb, ortvalid } from './modules/ort';
const app: express.Application = express();
const db: ortdb = ort('test.db');
app.get("/put",
  function(req: express.Request, res: express.Response) {
    const ctx: ortctx = db.connect();
    const name: string|null = ortvalid.ortValids
      ['user-name'](req.query['user-name']);
    if (name === null)
      return res.status(400).send('bad');
    const id: bigint = ctx.db_user_insert(name);
    return res.send(id.toString());
  }
);
app.get("/get",
  function(req: express.Request, res: express.Response) {
    const ctx: ortctx = db.connect();
    const id: bigint|null = ortvalid.ortValids
      ['user-id'](req.query['user-id']);
    if (id === null)
      return res.status(400).send('bad');
    const obj: ortns.user|null = ctx.db_user_get_id(id);
    if (obj === null)
      return res.status(404).send('not found');
    return res.send(JSON.stringify(obj.export()));
  }
);
app.listen(3000, function() {
  console.log('Server is running.');
});
Compile the application. This will create index.js.
% npm run tsc
Make sure that the database exists. This should only be run once.
% ort-sql db.ort | sqlite3 test.db
Lastly, run the project itself:
% node index.js Server is running.
Making an HTTP request to "localhost:3000/get?user-id=nnn" will result in a display of the created user's identifier, while "localhost:3000/put?user-name=xxx" will create one.