### MongoDB Query Editor Example
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/mongodb-query.html
Provides an example of how to write a query function in the editor. The function receives an mquery instance and the input data. It should return the query object. Examples include returning all documents or using specific query methods.
```javascript
// query; mquery instance
// data; data from the incomming object
return query.find(); // return all documents
```
--------------------------------
### Tangular Template Examples (HTML)
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/tangular.html
These examples demonstrate how to use Tangular templates with the Total.js Flow Tangular component. The first example shows how to access incoming data properties, while the second shows how to access controller-specific information like the URL, which is available when the message originates from a Route component.
```html
App name: {{ value.app_name }}
```
```html
URL: {{ $.refs.controller.url }}
```
--------------------------------
### Run Unit Tests for Total.js Flow Components
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/README.md
This snippet outlines the steps to run unit tests for the Total.js Flow components. It requires navigating to the project directory, installing the 'total4' package once, and then executing the 'tester.js' script using Node.js.
```bash
cd flowstreamcomponents
npm install total4
node tester.js
```
--------------------------------
### Cheerio HTML Parsing Example
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/cheerio.html
An example demonstrating how to use the Cheerio component to parse an HTML string. It extracts text from list items within a `
` tag and returns them as an array.
```javascript
// instance : component instance
// $: cheerio instance
// html: html string
var txt = [];
$('ul > li').each(function (i, elem) {
txt.push($(this).text());
});
return txt;
```
--------------------------------
### Server Extension Component Initialization and Configuration
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/serverextension.html
This JavaScript code defines a Total.js Flow component that acts as a server-side extension. It allows for the execution of custom JavaScript code during the component's initialization and when its configuration is updated. The code handles the creation of installation and uninstallation functions from provided configuration strings, executing them at the appropriate times.
```javascript
exports.id = 'serverextension';
exports.name = 'Server extension';
exports.group = 'Common';
exports.version = '1';
exports.icon = 'ti ti-check-square';
exports.author = 'Total.js';
exports.config = {
name: 'My extension',
install: '',
uninstall: '',
icon: exports.icon
};
exports.make = function(instance, config) {
var install = null;
var uninstall = null;
instance.configure = function() {
if (uninstall) {
uninstall.call(instance, instance, require);
uninstall = null;
setTimeout(instance.configure, 1000);
return;
}
try {
install = config.install ? new Function('instance', 'require', config.install) : null;
uninstall = config.uninstall ? new Function('instance', 'require', config.uninstall) : null;
} catch (e) {
install = null;
uninstall = null;
instance.throw('Code: ' + e.message);
}
install && install.call(instance, instance, require);
};
instance.close = function() {
uninstall && uninstall.call(instance, instance, require);
uninstall = null;
install = null;
};
instance.configure();
};
```
--------------------------------
### SQLite3 Component Initialization and Message Handling
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/sqlite3.html
Initializes the SQLite3 component for Total.js. It establishes a connection to the SQLite database using 'better-sqlite3' and defines a message handler to process incoming requests for database operations. The handler supports 'run', 'get', 'all', and 'exec' functions, taking SQL statements and optional data as input.
```javascript
exports.id = 'sqlite3';
exports.name = 'SQLite3';
exports.group = 'Databases';
exports.icon = 'ti ti-database';
exports.author = 'Total.js';
exports.version = '2';
exports.config = {};
exports.inputs = [{ id: 'input', name: 'Input' }];
exports.outputs = [{ id: 'output', name: 'Output' }, { id: 'error', name: 'Error' }];
exports.npm = ['better-sqlite3'];
exports.DBS = {};
exports.make = function(instance, config) {
const Database = require('better-sqlite3');
var db, file;
var fns = ['run', 'get', 'all', 'exec'];
instance.message = function($) {
if (!db) {
$.send('error', { error: 'db not ready' });
return instance.throw('SQLite3 not ready');
}
var data = $.data;
if (!data.prepare || !data.fn) {
instance.throw('No \`prepare\` or \`op\` provided');
return $.send('error', { error: 'invalid input data' });;
}
if (fns.indexOf(data.fn) < 0) {
instance.throw('Unsupported function: ' + data.fn);
return $.send('error', { error: 'unsupported function' });
}
if (data.fn === 'exec') {
let result = db.exec(data.prepare);
return $.send('output2', result);
}
let stmt = db.prepare(data.prepare);
let result = data.data ? stmt[data.fn](data.data) : stmt[data.fn]();
$.send('output', result);
};
instance.configure = function() {
let newfile = PATH.databases(instance.replace(config.filename) || `${instance.id}.sqlite`);
if (file === newfile) return;
file = newfile;
let tmp = exports.DBS[file];
if (tmp) {
db = tmp.db;
tmp.count++;
return;
}
db = new Database(file, { verbose: console.log });
exports.DBS[file] = { db, count: 1 };
};
instance.close = function() {
if (file && db) {
exports.DBS[file].count--;
if (exports.DBS[file].count === 0) {
db && db.close();
db = null;
delete exports.DBS[file];
}
}
};
instance.configure();
};
```
--------------------------------
### JavaScript Flow Action Component
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/taction.html
Defines a Total.js Flow component for custom actions. It includes configuration options, input/output definitions, and an `install` function that sets up the `Options` class for managing action data, request context, and response handling. The `make` function initializes the action with provided configuration.
```javascript
exports.id = 'taction';
exports.name = 'Action';
exports.icon = 'ti ti-totaljs';
exports.author = 'Total.js';
exports.version = '1';
exports.group = 'Total.js';
exports.config = {
partial: false,
name: 'Unknown',
input: '',
query: '',
color: '#68B25B',
params: '',
user: 0,
permissions: '',
code: '// model\n// $.query\n// $.params\n// $.headers\n// $.user\n\n$.success();',
strerr: false,
path: 'response',
autoresponse: false
};
exports.inputs = [{ id: 'input', name: 'Payload' }];
exports.outputs = [{ id: 'output', name: 'Response' }, { id: 'error', name: 'Error' }];
exports.install = function(com) {
MAIN.flowtaction = {};
const ARGS = /\{{1,2}[a-z0-9\_.-\s]+\}{1,2}/gi;
const Options = function($, config) {
var t = this;
var data = $.data;
if (!data || typeof(data) !== 'object') data = {};
t.id = config.name;
t.error = new ErrorBuilder();
t.controller = $.refs.controller;
t.payload = data.payload || data.model || data.body;
if (typeof(t.payload) !== 'object' || !t.payload) t.payload = {};
t.message = $;
t.config = config;
t.user = data.user;
t.query = data.query || {};
t.params = data.params || {};
t.url = data.url;
t.language = data.language;
t.headers = data.headers || {};
t.files = data.files || [];
t.cookies = data.cookies || {};
t.ip = data.ip;
t.ua = data.ua;
t.request = data;
};
Options.prototype = {
get value() { return this.payload; },
get model() { return this.payload; },
set value(value) { this.payload = value; },
set model(value) { this.payload = value; }
};
Options.prototype.audit = function(message, type) {
F.audit(this, message ? this.variables(message) : '', type);
};
Options.prototype.success = function(value) {
var self = this;
self.request[self.config.path] = { success: true, value: value };
if (self.config.autoresponse) {
var refs = self.message.refs;
if (refs) {
refs.controller.json(self.request[self.config.path]);
refs.controller = null;
}
}
self.message.send('output', self.request);
};
Options.prototype.cancel = function() {
var self = this;
self.message.destroy();
};
Options.prototype.successful = function(callback) {
var self = this;
return function(err, a, b, c) {
if (err) self.invalid(err);
else callback.call(self, a, b, c);
};
};
Options.prototype.send = Options.prototype.callback = function(value) {
var self = this;
if (arguments.length == 0) {
return function(err, response) {
err && self.error.push(err);
self.callback(response);
};
}
let msg = self.message;
let refs = msg.refs;
let cfg = self.config;
if (self.error.items.length) {
self.request[cfg.path] = cfg.strerr ? self.error.toString() : self.error.output();
if (cfg.autoresponse) {
if (refs.controller) {
refs.controller.invalid(self.error);
refs.controller = null;
}
}
msg.send('error', self.request);
} else {
self.request[cfg.path] = value;
if (cfg.autoresponse) {
if (refs.controller) {
refs.controller.json(value);
refs.controller = null;
}
}
msg.send('output', self.request);
}
};
Options.prototype.done = function(arg) {
var self = this;
return function(err, response) {
if (err) {
self.error.push(err);
self.callback(null);
} else self.callback({ success: true, value: arg === true ? response : arg });
};
};
Options.prototype.invalid = function(error, path, index) {
var self = this;
self.error.push(error, path, index);
self.callback(null);
};
Options.prototype.cookie = function(name, value, expire, options) {
var self = this;
if (value === undefined) return self.cookies[name];
if (value === null) expire = '-1 day';
if (self.controller) {
self.controller.cookie(name, value, expire, options);
return true;
}
return false;
};
Options.prototype.variables = function(str, data) {
if (str.indexOf('{') === -1) return str;
var $ = this;
return str.replace(ARGS, function(text) {
var l = text[1] === '{' ? 2 : 1;
var key = text.substring(l, text.length - l).trim();
var val = null;
var five = key.substring(0, 5);
if (five === 'user.') {
if ($.user) {
key = key.substring(5);
val = key.indexOf('.') === -1 ? $.user[key] : U.get($.user, key);
}
} else if (five === 'data.') {
if (data) {
key = key.substring(5);
val = key.indexOf('.') === -1 ? data[key] : U.get(data, key);
}
} else {
var six = key.substring(0, 6);
if (six === 'model.' || six === 'value.') {
if ($.model) {
key = key.substring(6);
val = key.indexOf('.') === -1 ? $.model[key] : U.get($.model, key);
}
} else if (six === 'query.') val = $.query[key.substring(6)];
else if (key.substring(0, 7) === 'params.') val = $.params[key.substring(7)];
}
return val == null ? text : val;
});
};
MAIN.flowtaction.Options = Options;
MAIN.flowtaction.AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
};
exports.uninstall = function() {
delete MAIN.flowtaction;
};
exports.make = function(instance, config) {
var cfg = {};
var fn;
instance.message = function($) {
var opt = new MAIN.flowtaction.Options($, cfg);
if (fn) {
// check user
if (cfg.user || cfg.permissions) {
if ((cfg.user === 2 && opt.user) || (cfg.user === 1 && !opt.user)) {
opt.invalid(
```
--------------------------------
### Initialize and Test FlowStream Component
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/tests/readme.md
Initializes a FlowStream testing instance and describes how to test a component. It shows how to use the 'test' function to initialize a component by its filename and then use the returned 'test' instance to perform assertions like 'test.ok()'. It also demonstrates how to manually end the test using the 'done()' method.
```javascript
require('./tester')(function (describe, done) {
// this.timeout = 10000; // 10 seconds timeout
describe('counter', function (test) {
// Test was successful
test.ok();
// End tester and show results
done();
});
});
```
--------------------------------
### Expression Type Example
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/switch.html
Shows how to use JavaScript expressions to return a value that will be used in a specified operation. This example demonstrates subtracting an offset from the data value.
```javascript
data.value - variables.offset
```
--------------------------------
### Configure MQTT Publish Event Handler (JavaScript)
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/mqtt-publish.html
Handles the 'configure_mqttpublish' event, typically used to fetch available MQTT brokers and populate a dropdown or list for the user to select a broker.
```javascript
ON('configure_mqttpublish', function(data) {
data.call(function(response) {
SET('%brokers', response);
}, true);
});
```
--------------------------------
### Example 2: Direct Data Transformation
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/transformer.html
This example shows a direct transformation of the incoming `data`. It assumes `data` is a string or has a method like `toUpperCase()` and returns the transformed value.
```javascript
data = data.toUpperCase();
```
--------------------------------
### Executing the Exec Component with Options
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/exec.html
Demonstrates how to execute the Exec component using `flowinstance.exec()`. It shows the structure of the options object, including `id`, `data`, and a `callback` function to process potential errors and received messages. Optional parameters like `vars`, `repo`, `uid`, and `ref` are also mentioned.
```javascript
var opt = {};
opt.id = 'ID_OF_EXEC_INSTANCE';
opt.data = { custom: 'data' };
opt.callback = function(err, msg) {
// msg.uid;
// msg.ref;
// msg.repo {Object}
// msg.data {Object}
// msg.cloned {Number} how many times was the message cloned?
// msg.duration {Number} in milliseconds
};
// optional:
// opt.vars = {}; --> custom variables
// opt.repo = {}; --> custom repository data and this data will be returned in the callback
// opt.uid; --> for storing some unique ID
// opt.ref; --> for storing some reference ID
flowinstance.exec(opt);
```
--------------------------------
### Expression Operator Example
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/switch.html
Illustrates using JavaScript expressions directly as operators in conditions, where the expression must evaluate to true or false. It includes type checking for the data value.
```javascript
if (typeof data.value !== 'number') return false; else return data.value > parseInt(variables.threshold);
```
--------------------------------
### Initialize and Configure Code Actions (JavaScript)
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/taction.html
This snippet initializes the code actions module, sets up key bindings for saving, and defines a function to render schema information for dynamic parameters, URL query arguments, and payload. It's designed to be used within the Total.js Flow framework.
```javascript
TOUCH(function(exports, reinit) {
if (!common.codeactions) {
common.codeactions = {};
W.codeactionssubmit = function(el) {
if (BLOCKED('codeaction', 1000)) return;
var winid = ATTRD(el);
var id = winid.substring(4);
var code = common.codeactions[winid];
var instance = flowinstances.instances[id];
var config = instance.config;
config.code = code;
instance.reconfigure(config);
};
W.codeactionsinit = function(com) {
var save = () => W.codeactionssubmit(com.element);
var map = { 'Cmd-S': save, 'Ctrl-S': save };
com.editor.addKeyMap(map);
};
}
exports.renderschema = function() {
var builder = [];
var config = exports.config;
var write = function(name, label) {
if (config[name]) {
builder.push('{0}
'.format(label));
var val = config[name].replace(/\,/g, '\n');
builder.push('{0}
'.format(val));
}
};
write('params', 'Dynamic parameters');
write('query', 'URL query arguments');
write('input', 'Payload');
return builder.join('');
};
exports.configure = function() {
var key = 'code' + exports.id;
var win = common.windows.findItem('id', key);
if (win) {
var path = '\*codeactions.' + key;
var prev = GET(path);
if (prev !== exports.config.code)
SET(path, exports.config.code);
win.element.find('.CLASS-code-info').html(exports.renderschema());
}
};
exports.sourcecode = function() {
var config = exports.config;
var path = '\*codeactions.code' + exports.id;
var winid = 'code' + exports.id;
if (common.windows.findItem('id', winid)) {
SETTER('windows/focus', winid);
return;
}
SET(path, config.code);
PUSH('common.windows', {
id: winid,
cachekey: 'codeaction',
cache: 'readme',
html: ''.format(path, exports.renderschema()),
title: 'Action script: ' + config.name,
actions: {
move: true,
autosave: true,
close: true,
maximize: true,
minimize: false
},
offset: {
x: ((WW / 2) - 275) >> 0,
y: ((WH / 2) - 250) >> 0,
width: 750,
height: 500,
minwidth: 200,
minheight: 300,
maxwidth: 1200,
maxheight: 1200
},
make: function(el) {
el.closest('.ui-windows-item').css('z-index', 50);
el.find('.CLASS-code').rclass('invisible', 500);
}
});
};
});
```
--------------------------------
### Expression Example for Conditions
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/switch.html
Demonstrates how to use JavaScript expressions within the 'Expression' field to manipulate data for condition checks. It shows accessing variables like 'data', 'repo', and 'variables'.
```javascript
data.value - variables.offset
```
--------------------------------
### Cheerio Component Make Function
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/cheerio.html
The 'make' function initializes the Cheerio component, setting up the logic for processing HTML content based on the provided configuration. It handles loading HTML, executing user-defined functions, and sending output.
```javascript
exports.make = function(instance, config) {
const cheerio = require('cheerio');
var get, fn;
instance.message = function($) {
var data = $.data;
var input = CLONE(data);
var html;
if (config.path) {
html = get(data);
} else {
html = data;
}
if (typeof(html) !== 'string') {
$.send('output', { error: 'invalid data, expected "data{0}{1}" to be a string'.format(config.path ? '.' : '', config.path) });
return;
}
const $$ = cheerio.load(html);
var result = fn($.instance, $$, html);
$.data = { result, input };
$.send('output');
};
instance.configure = function() {
if (config.path) get = new Function('data', 'return data?.{0};'.format(config.path.replace(/\./g, '?.')));
if (config.fn) {
try {
fn = new Function('instance', '$', 'html', config.fn);
instance.status('');
} catch(e){
instance.throw(e.stack);
instance.status('Invalid code');
}
} else instance.status('Not configured');
};
instance.configure();
};
```
--------------------------------
### Example 1: Uppercase Name Transformation
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/transformer.html
This JavaScript code snippet demonstrates how to transform incoming data by converting the `name` property to uppercase. It accesses the `data` object and modifies its `name` property.
```javascript
// "data" is a reference to message data
data.name = data.name.toUpperCase();
```
--------------------------------
### Initialize NoSQL Component
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/nosql.html
Defines the metadata and core logic for the NoSQL component in Total.js Flow. It includes properties for ID, name, group, author, version, configuration, inputs, outputs, and the main 'make' function.
```javascript
exports.id = 'nosql';
exports.name = 'NoSQL';
exports.group = 'Databases';
exports.icon = 'ti ti-database';
exports.author = 'Total.js';
exports.version = '1';
exports.config = {};
exports.inputs = [{ id: 'input', name: 'Input' }];
exports.outputs = [{ id: 'output', name: 'Output' }];
```
--------------------------------
### Example 3: Restructuring Data
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/transformer.html
This snippet illustrates how to restructure the incoming data. It stores the original `data` in a temporary variable `tmp`, then creates a new object for `data` with a `name` property derived from `tmp.Name`.
```javascript
var tmp = data;
data = {};
data.name = tmp.Name;
```
--------------------------------
### Simulate Component Input and Catch Output
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/tests/readme.md
Explains how to simulate sending data to a component using `test.input`. It shows how to provide an array of data and a callback function to process the received message, including assertions on the message data.
```javascript
describe('increment', function(test) {
const data = [1, 2];
test.input(data, msg => {
// Test is flagged as Successful when message data is equal to 3
test.ok(msg.data === 3);
// Same result
test.fail(msg.data !== 3);
});
});
```
--------------------------------
### Get Available MQTT Brokers
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/mqtt-subscribe.html
Provides a function to retrieve a list of available MQTT brokers configured within the Total.js Flow environment. This is used to populate dropdowns or lists for selecting a broker.
```javascript
exports.call = function(data, answer) {
var arr = [];
var instances = this.instances();
instances = instances.filter(i => i.module.flags && i.module.flags.includes('mqttbroker'));
for (let com of instances) arr.push({ id: com.id, name: `${com.state.name} (${com.state.status})` });
answer(arr);
};
```
--------------------------------
### PostgreSQL Component Configuration and Message Handling (JavaScript)
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/postgresql.html
This snippet defines the PostgreSQL component for Total.js. It includes configuration for database connections using connection strings, manages connection pools, and handles incoming messages to execute SQL queries against the database. It also defines the expected input data format and the structure of the output response.
```javascript
exports.id = 'postgresql';
exports.name = 'PostgreSQL';
exports.group = 'Databases';
exports.icon = 'ti ti-database';
exports.author = 'Total.js';
exports.version = '2';
exports.config = {};
exports.inputs = [{ id: 'input', name: 'Input' }];
exports.outputs = [{ id: 'output', name: 'Output' }];
exports.npm = ['pg'];
exports.POOLS = {};
exports.make = function(instance, config) {
const { Pool } = require('pg');
var PG;
instance.message = function($) {
var data = $.data;
if (!PG || !PG.pool) return $.send('output', { error: 'Postgre connection not configured' });
PG.pool.query(data.query, (err, response) => {
if (err) return $.send('output', { error: err.message });
var { command, rows, rowCount } = response;
$.send('output', { command, rows, rowCount });
});
};
instance.configure = function() {
// same connection, ignore
if (!config.connection || (PG && PG.string === config.connection)) return;
instance.close(); // already existing connection, use it
if (exports.POOLS[config.connection]) {
PG = exports.POOLS[config.connection];
PG.count++;
return;
}
var pool = new Pool({ connectionString: config.connection });
PG = exports.POOLS[config.connection] = { pool, count: 1, string: config.connection };
pool.on('error', (err, client) => {
console.log('[PG] Unexpected error on idle client', err);
if (err) instance.throw(err.message);
});
pool.query('SELECT NOW() AS message;', (err, response) => {
if (err) instance.throw(err.message);
});
};
instance.close = function() {
if (PG) {
PG.count--;
if (PG.count === 0) { // last component using this connection so destroy it
PG.pool.end();
delete exports.POOLS[config.connection];
}
PG = null;
}
};
instance.configure();
};
```
--------------------------------
### MongoDB Query Select/Exclusion Rules
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/mongodb-query.html
Explains the rules for using the `.select()` method in mquery. It highlights that inclusion and exclusion cannot be used together in a single `.select()` call. Provides valid examples for both exclusion and inclusion.
```javascript
// Valid exclusion:
// .select('-lastname') or .select('-email -lastname') etc.
// Valid inclusion:
// .select('name email') or .select('email lastname') etc.
```
--------------------------------
### Map Array Component Logic (JavaScript)
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/components/map-array.html
Implements the core logic for the 'maparray' component, handling array iteration, property mapping, and output generation. It includes utility functions for getting and setting nested object properties.
```javascript
exports.id = 'maparray';
exports.name = 'Map array';
exports.icon = 'ti ti-table';
exports.author = 'Total.js';
exports.group = 'Common';
exports.version = '1';
exports.config = { rules: [] };
exports.inputs = [{ id: 'input', name: 'Input' }];
exports.outputs = [{ id: 'output', name: 'Output' }];
exports.make = function(instance, config) {
var Rules = [];
instance.message = function($) {
var arr, newarr = [];
if (config.path) {
try {
arr = get(config.path);
} catch(e) {}
} else {
arr = $.data;
}
if (!config.map) return instance.throw('No configuration');
if (!Array.isArray(arr)) return instance.throw('Input data is not an array');
var len = arr.length;
for (let i = 0; i < len; i++) {
try {
var item = arr[i];
if (typeof(item) !== 'object' || Array.isArray(item)) continue;
var val = getNewObj(arr[i], config.map);
newarr[i] = val;
} catch(e) {
instance.throw(e.stack);
}
}
$.data = newarr;
$.send('output', );
};
instance.configure = function() {};
instance.configure();
// from obj to result -> obj.name to result.name
const getNewObj = (obj, map) => {
var result = config.array ? [] : {};
var len = map.length;
for (let i = 0; i < len; i++) {
var paths = map[i]; // { source: 'path.to.value', target: 'new.path.to.value' }
var val = get(obj, paths.source);
if (val) {
if (config.array) {
result.push(val);
continue;
}
if (!paths.target) // no path, ignore it
continue;
if (paths.target === '...') // keep the same path
paths.target = paths.source;
if (config.array) result.push(val);
else set(result, paths.target, val);
} else {
if (config.array) result.push(null);
}
}
return result;
};
const get = (obj, path) => path.split(".").reduce((r, k) => r?.[k], obj);
const set = (obj, path, value) => {
var ok = true;
var props = path.split('.').trim();
var plen = props.length;
if (!plen) return false;
var path = props.map(p => isNaN(p) ? p : `[${p}]`).join('.').replace(/\.\[/g, '[');
var paths = [];
props.reduce((prev, curr) => {
curr = isNaN(curr) ? curr : `[${curr}]`;
let p = prev + (prev && curr[0] !== '[' ? '.' : '') + curr;
paths.push(p);
return p;
}, '');
if (path[0] !== '[') path = '.' + path;
var fn = new Function('o', 'v', 'o' + path + ' = v;');
if (plen === 1) {
var isobject = isNaN(props[0]);
if ((isobject && typeof(obj) === 'object') || (!isobject && obj instanceof Array)) obj[props[0]] = value;
else ok = false;
} else {
paths.forEach((p, index) => {
var islast = plen === index + 1;
if (!islast) {
var t = get(obj, p);
var type = isNaN(paths[index + 1]) ? '{}' : '[]';
if (t == null)
new Function('target', 'set', 'target' + (p[0] === '[' ? '' : '.') + p + ' = ' + type + ';')(obj, set);
} else {
fn(obj, value);
}
});
}
return ok;
}
};
```
--------------------------------
### Handle Component Errors with test.onerror()
Source: https://github.com/totaljs/flowstreamcomponents/blob/main/tests/readme.md
This snippet demonstrates how to capture and handle errors thrown by a single component in Total.js Flowstream. It shows the component implementation throwing an error and the test setup to define an error handler using `test.onerror()`.
```javascript
// Component
exports.make = function(instance, config) {
instance.message = function($) {
instance.throw('a', 'b', 'c', 'd');
});
}
// Test
describe('test', function (test) {
test.onerror = function (a, b, c, d) {
console.log(a, b, c, d);
};
});
```