Tyr

The Tyr object, available on the client as window.Tyr, and usually imported on the server as Tyr, contains the namespace for Tyranid.

$: TaggedTemplateLiteral => any

This is a regular tagged template literal that also has Terms.

For example:

Tyr.$`Welcome to $$applicationName!`
$all

This is a constant used with Advanced Population that means to populate the field and grab all the properties. It can only be specified on a link.

AppError
arraySort(array: array, sortObj: object): number

This is similar to how Array.sort() works except that it takes a MongoDB-style sort definition object.

Like Array.sort(), the array is sorted in-place.

For example:

const myArray = [ { b: 1 }, { b: 'alpha' }, {}, { b: 'alpha', c: 2 } ];
Tyr.arraySort(myArray, { b: 1, c: -1 });
// myArray is now: [ {}, { b: 1 }, { b: 'alpha', c: 2 }, { b: 'alpha' } ]

See also Tyr.mongoCompare().

aux(def: CollectionDefinition)

This can be used to define a new auxiliary collection on the client. The syntax of def is the same as when specifying a new collection. You do not need to specify aux: true and collection IDs can optionally be automatically generated. For example:

const myCollection = Tyr.aux({
  name: 'myFormData',
  fields: {
    re: { is: 'regexp', label: 'Regular Expression' },
    testRe: { is: 'string', label: 'Test regular expression' }
  }
});

See auxiliary collections for more information.

byId: { collection id: Collection }

This is an hash of collections that Tyranid knows about indexed by collection id.

byName: { collection name: Collection }

This is an hash of collections that Tyranid knows about indexed by collection name.

byUid(uid: string, options: Options): Document

This will find a document by its UID.

byUids(uids: string[], options: Options): Document[]

This will find an array of documents by their UID.

The UIDs do not need to be from the same collection and this method is optimized to only query from each collection once.

The returned array is parallel to the given uids array. If the same UID shows up multiple times in the input query, then the returned array will also have multiple instances, but the instances will be shared. If a given UID does not exist in the database, then the corresponding entry will still be present, but it will contain null.

Collection
Component
collections: Collection[] & { collection name: Collection }

This is a list of all the collections that Tyranid knows about.

This also has a hash of the collections by their capitalized names to make destructuring easier, allowing you to write:

const { Depot, Train } = Tyr.collections;

instead of

const Depot = Tyr.byName.depot;
const Train = Tyr.byName.train;
components: Components[]

This is a list of all the components that are registerd with Tyranid.

config(opts: object): void

This initializes Tyranid. opts are as follows:

{
  mongoClient: myMongoClient,
  db: myMongoClient.db(optional database name here if not specified in your initial connect string),
  logLevel: 'info',
  clientLogLevel: 'error',
  consoleLogLevel: 'info',
  csrf?: { // enables CSRF support in the Tyranid client API
    cookie: string; // the name of a cookie containing the CSRF value
    header: string; // the name of the header to place this cookie value in when making requests
  },
  dbLogLevel: 'info',
  exceptions?: {
    app?: { httpCode?: number, defaultMessage?: string },    // default message and HTTP code for AppError
    secure?: { httpCode?: number, defaultMessage?: string }, // default message and HTTP code for SecureError
    user?: { httpCode?: number, defaultMessage?: string },   // default message and HTTP code for UserError
  },
  fixer?: { // if present will be used for currency conversions
    accessKey: 'fixer.io API key',
    every: 8 * 60 * 60, // how often api exchange rates will be queried in seconds, default every 8 hours
  },
  formats?: {
    [typeName: string]: string // contains date/time and other custom formats for each Type
  },
  indexes: true, // ensure indexes are created (invokes createIndexes())
  meta: { // allows you to define additional custom metadata on Tyranid objects
    collection: { // allows you to define additional custom metadata on Tyranid collections
      fieldName: { // the name of the custom metadata field name (should not conflict with built-in Tyranid metadata fields)
        client?: boolean; // indicates if this custom collection metadata should be sent down to the client
      }
    }
  },
  migration: {
    migrate: true, // indicates migrations should be run as part of configuration
    dir: __dirname + '/path/to/migrations/directory',
    list: ['migration1', 'migration2'] // list of approved migrations to run and the order in which to run them
  },
  minify: false,
  permissions: {
    find: 'view',
    insert: 'edit',
    update: 'edit',
    remove: 'delete'
  },
  secure: Secure,
  validate: validateOpts
  whiteLabel: (metadata: Collection | Field | Path) => string; // register a white label translation function
  whiteLabelClient: (metadata: Collection | Field | Path) => string; // a client-side-specific ^
}

If you specify db you must also specify mongoClient. If you leave off both, then Tyranid will function in a "database-less" mode which is not usually useful, but can be useful certain things like library code which is just working with the metadata for example.

If the validate option is not used, be sure to call Tyr.validate() once all of the collections have been imported. See Tyr.validate() for an explanation of validateOpts.

If the minify option is set to true (it also defaults to true) then the code generated by /api/tyranid will be minified.

See Log for an explanation of the log level options.

See Secure for an explanation of the secure and permissions option.

connect(opts: object): void

This method connects Tyranid to external services like express (which enables Tyranid's routing functionality) and http (which enables Tyranid's WebSocket support). opts are as follows:

{
  app: Express,
  auth: (req, res, next) => {}?): void,
  http: Node http server,
  store: Express Session Store
}

app is the instance of express to connect to returned by express().

auth is an optional callback that will be passed to all Tyranid routes via .all(app) before Tyranid routes are processed. You can do additional validation and authorization here.

http is the instance of the http server (usually returned by app.listen()). The HTTP server must be connected in order for WebSocket push capabilities to be enabled.

store is the instance of the express-session store passed into express (for example, connect-redis or connect-mongo). This must be enabled for authentication capabilities to be available inside WebSocket requests.

connect() can optionally be called multiple times. For example, you could connect to express in one call and to http in another.

See Client for more information.

createIndexes(): void

This will create any indexes defined on collections if they do not already exist.

db: native MongoDB database (Db)

This contains a reference to the underlying native MongoDB Db object.

diff: object

This contains Tyranid's diffing and patching functionality. See Diff & Patch.

documentPrototype: object

All Document instances inherit from this object. Add methods and properties to this object that you want to be available on all document instances. It's recommended that all fields added to this object start with a '$' character and are Object.defineProperty()'ed to be non-enumerable.

Event
Field
instanceId: string

If there are multiple instances of Tyranid (for example, running across multiple webservers) then they will each have a distinct instance id that is available in this property.

local

This provides access to Tyranid's continuation local storage capability on the server and also to corresponding client session data on the client.

trace|log|info|warn|error|fatal(opts): void

This are aliases to the logging methods in Log, see Log.log.

Log
migrate(): void

This executes any migrations that are defined. Use this method only when migrations are not run inside config() via migrations.migrate = true.

mixin(target: object, mixin: object): void

This mixes in a mixin into a target object. Both regular and prototype methods and properties are mixed.

mongoClient: native MongoDB client (MongoClient)

This contains a reference to the underlying native MongoDB MongoClient object.

mongoCompare(a: any, b: any): number

This is a comparator function that can be passed into methods like sort() that compares two values according to how MongoDB's sort operation works.

If a < b, returns n such that n < 0; if a > b, returns n such that n > 0; if a === b, returns 0.

See also Tyr.arraySort().

nextId(counterName: string): number

This returns the next number for the given counter name. This is an atomic operation and can be used to generate unique identifiers.

nextIds(counterName: string,
fieldName: string,
documents: Document[],
asString?: boolean): number

This returns the next set of numbers for the given counter name and assigns them to the fieldName for each document. This is an atomic operation and can be used to generate unique identifiers.

Path
parseBson(value): any

This will translate BSON-style objects into their "native" values. For example:

Tyr.parseBson(3) => 3
Tyr.parseBson({ _bsontype: 'ObjectId', id: '...' }) => ObjectID('...')
parseUid(uid): { collection: Collection, id: collection id type }

This will parse a UID into the collection and id that the uid refers to.

projectify(value: object | Path[]): projection object

If value is an object, this transforms the object into the corresponding projection that would be needed to cover that data. For example:

objTyr.projectify(obj))
{ name: 'Xi', age: 32 }{ name: 1, age: 1 }

If value is an array of paths, then this will return a minimal projection needed to access data at those paths.

Secure
SecureError
Type
U

This is a shortcut to Units.parse and provides a shortcut to create new Units instances.

Unit
Units
UnitConversionError
UnitDegree
UnitFactor
UnitSystem
UnitType
UnitDegree
UserError
validate( dirOpts | dirOpts[] ): void

If you did not specify the validate option in the Tyr.config() call, call this method once all of the collections and types have been imported and you want the entire metadata model compiled and linked (validated). dirOpts are:

{
  // dirOpts supports two styles:

  // (1) globs:
  glob: './**/model/*.js',    // All model files in a model subdirectory

  // (2) directory with regex:
  dir: './app/models',        // All files in directory.
  fileMatch?: '^tyr.*'       // Provides a regex that files in the dir need to match.
}

validate() can optionally take an array of dirOpts so that you can specify multiple directories.

If you are manually importing corrections then you do not need to pass in dirOpts.

valuesBy(filter: Field => boolean): any[]

This returns a unique list of all values that appear in any collection in the database for any field that matches the filter.

For example, the following returns a list of all of the strings that occur in the database in any string field in any collection.

Tyr.valuesFor(field => field.type.def.name === 'string')

string utilities

The following string functions can be useful when writing metadata-driven code.

capitalize(name: string): string

This capitalizes the initial character without touching the remaining characters. For example:

namecapitalize(name)
helloHello
totalValueTotalValue
userIdUserId
labelize(name: string): string

This generates a humanized version of a camel-case name. For example:

namelabelize(name)
countCount
totalValueTotal Value
userIdUser
groupIdsGroups
numberize(numbering: Numbering, num: number): string

This formats a number according to a specific numbering system.

num must be an integer that is >= 0.

For example:

Tyr.numberize('roman', 24) === 'XXIX'
Tyr.numberize('uppercase', 10)  === 'K'

Numbering

Numbering can be one of the following strings:

NumberingMeaning
lowercaseLowercase Letters: a, b, c, d, ...
uppercaseUppercase Letters: A, B, C, D, ...
integerIntegers: 0, 1, 2, 3, ...
naturalNatural numbers: 1, 2, 3, 4, ...
ordinalOrdinal numbers: 1st, 2nd, 3rd, 4th, ...
romanRoman numerals: I, II, III, IV, ...
roman-lowercaseLowercase Roman Numerals: i, ii, iii, iv, ...
ordinalize(integerValue: number): string

This generates a ordinalized form of number. For example:

integerValueordinalize(integerValue)
11st
1212th
2222nd
103103rd
pluralize(name: string): string

This generates a plural form of a singular word. For example:

namepluralize(name)
bossbosses
daydays
daisydaisies
singularize(name: string): string

This generates a singular form of a plural word. For example:

namesingularize(name)
bossesboss
daysday
daisiesdaisy
unitize(count: number, unit: string): string

This generates a properly-pluralized unit description. For example:

countunitunitize(count, unit)
0flower0 flowers
1mile1 mile
2daisy2 daisies

ObjectId-aware lodash-like utilities

addToSet(array: array, ...values: any): void

This adds all instances of values into array that are not already present.

It can handle ObjectIds from different versions of the MongoDB native driver.

clear(obj: object): void

This removes all properties from an existing object.

cloneDeep(obj: object): object

This is similar to _.clone() except that it can handle ObjectIds.

cloneDeep(obj: object): object

This is similar to _.cloneDeep() except that it can handle ObjectIds.

compactMap(arr: A[], mapFn: A => B): B[]

This applies mapFn to each element of the array and returns an array that only contains the truthy values.

indexOf(a: array, value: any): integer

This is similar to _.indexOf() except that it can handle ObjectIds from different versions of the MongoDB native driver.

isCompliant(spec: any, value: any): boolean

This function performs similarly to _.matches() except that the left object is a specification of what can appear on the right side. For example, the left-hand side can specify an array of values and as long as the right-hand side contains one of those values, it will still be considered to be compliant.

isEqual(a, b): boolean

This is similar to _.isEqual() except that it can handle ObjectIds from different versions of the MongoDB native driver.

isEqual(a, b): boolean

This is similar to _.isEqual() except that it can handle ObjectIds from different versions of the MongoDB native driver.

isEqualInBson(a, b): boolean

This is similar to Tyr.isEqual() except that it considers undefined to be equal to null. This method can be useful because the BSON specification has deprecated undefined and properties containing undefined are implicitly converted to null.

isObject(value): boolean

This is similar to _.isObject() except that it considers ObjectIds to be primitive values.

isObjectId(value): boolean

This returns true if the value is an ObjectId.

isSameId(a: string | ObjectID, b: string | ObjectID): boolean

This determines if two values reference the same ID even though either or both may be encoded as a string or as an ObjectID. For example:

Tyr.isSameId('a', new ObjectID('a')) (is true)
pullAll(a: array, ...values: any): void

This is similar to _.pullAll() except that it can handle ObjectIds from different versions of the MongoDB native driver.

stringify(value: any,
replacer?: (key: string | symbol, value: any),
space?: string | number): string

This is similar to JSON.stringify() except that it stringifies ObjectIds as strings and also stringifies regular expressions into MongoDB's format.

async & Promise utilities

eachAsync<A>(array: A[], visitor: async A => any): boolean

This is like array.forEach() except that the predicate function is asynchronous.

The elements will be visited in sequence.

everyAsync<A>(array: A[], predicate: async A => boolean): boolean

This is like array.every() except that the predicate function is asynchronous.

filterAsync<A>(array: A[], filter: async A => boolean): boolean

This is like array.filter() except that the filter function is asynchronous.

findAsync<A>(array: A[], predicate: async A => boolean): A

This is like array.find() except that the predicate function is asynchronous.

findIndexAsync<A>(array: A[], predicate: async A => boolean): integer

This is like array.findIndex() except that the predicate function is asynchronous.

mapAsync<A, B>(array: A[], mapFn: async A => B): B[]

This is like array.map() except that the map function is asynchronous.

mapAwait(value: Promise | any, fn: any => any): Promise | any

This maps a value by the given function. If the value is a promise, then the result will be a promise.

If the value is an array which contains some promises, the elements will be resolved before applying the fn.

This method permits writing mapping functions that work with values that are conditionally asynchronous.

sleep(ms: number): void

This asynchronously sleeps ms milliseconds.

sleepUntil(fn: () => boolean | Promise, maxMs = 5000, everyMs = 5): void;

This asynchronously the predicate fn for a truthy value initially and then every everyMs thereafter up until a maximum of maxMs milliseconds whereupon it will throw an Exception.

someAsync<A>(array: A[], predicate: async A => boolean): boolean

This is like array.some() except that the predicate function is asynchronous.

socket.io

If socket.io support has been enabled (see connect()) then the following properties are also available:

io: object

This contains a reference to the socket.io server. You can use it to add your own events over the socket.io connection as well.

socket: object

This contains the socket.io client socket that Tyranid is using for the user. You can use it to add your own events over the socket.io connection as well.

reconnectSocket(): void;

This method establishes a new socket.io connection. This should be called after a new login cookie has been established for the client (for example, after a user logs in) so that the socket.io socket can be synchronized with the server-side mongo session.