DATAmaestro Scripts
Related tickets :Â https://pepite.atlassian.net/browse/DATMA-2916
Â
Â
/**
* ––––––––––
* | Jira |
* ––––––––––
// Please mention on ticket DATMA-2916 when you use this script, and describe your Use Case a bit.
/**
* ––––––––––
* | Config |
* ––––––––––
*/
var baseDir = VirtualPath.parse('Valtris.be/INTENSITES_MOTEURS');
var sourceDir = baseDir;
var destinationDir = baseDir.resolve('computed', 'Données nettoyées');
var scriptName = "My script name"
var scriptHost = 'https://test.mydatamaestro.com/'
//------------------------------------------------
//CHOOSE TAG FILTER RULE (3 methods)
/**
* @type {FilterTagRule}
*/
var baseTagFilterRule = {
type: 'browse',
wildcard: '*.INTENSITE', //Check sur une string, * = N'importe quelle string
sourceDir: sourceDir // can add FullPath (true, false)
};
// OR define by list
/**
* @type {FilterTagRule}
*/
// var _baseTagFilterRule = {
// type: 'list',
// tags:[tag1Path, tag2Path]
// };
//OR BASED on a function
// var baseTagFilterRule = {
// type: 'browse',
// predicate: myFunction
// sourceDir: sourceDir
// };
//------------------------------------------------
//Do the mapping for each individual computed tag
//Modify this function to give all inputs as arguments
/**
* @type {(tag:JavaVirtualPath, index:number)=>JavaVirtualPath[]}
*/
var getInputTagsRule = function (intensiteTag) {
var parent = intensiteTag.getParent();
var name = intensiteTag.getLastToken();
var nextName = name + '_NOMINALE';
return [intensiteTag, parent.resolve(nextName)];
};
/**
* @type {(tag:JavaVirtualPath, index:number)=>TagCreateConfig}
*/
var outputTagGetter = function (intensiteTag) {
var name = intensiteTag.getLastToken();
var nextName = name + '_NETTOYEE';
// var meta= tagConnection.getTagMetadata(intensiteTag)
// return createConfigFromTagPath(destinationDir.resolve(nextName),meta); //copy source tag metadata
return createConfigFromTagPath(destinationDir.resolve(nextName), {}); //open {} to add parameter like: {isNumeric:false, unit: "ms", title:"", classifier}
//Can open {} do ctrl space and scroll down to all the different metaData properties
};
/**
*@type {ComputeTagFn}
*/
var customPartagedFunction = function (values, time, lastValue, lastTime) {
//Values are in the order defined in getInputTagsRule --> Access with values[0], values[1]...
return null;
};
/**
* @type {boolean}
*/
var isOptimistic = true;
//TAG VALIDY MAP
/**
* @type {Optional<Optional<number>[]>}
*/
var validityTagMap = null;
//var validityTagMap = [1000,1000] //in MS tag Validy Map is defined identical for all same tag
/**
* –––––––––––
* | Process |
* –––––––––––
*/
putTagCreationConfigContext({
isMetadataOwner: true, //False don't update metadata if exist
scriptFolder: baseDir,
scriptHost: scriptHost,
scriptName: scriptName
});
function main() {
var baseTags = getTags(baseTagFilterRule);
var outputTags = [];
var outputTagConfigs = [];
var inputTags = map(baseTags, function (baseTag, index) {
var outputTagConfig = outputTagGetter(baseTag, index);
outputTags.push(outputTagConfig.tagPath);
outputTagConfigs.push(outputTagConfig);
return getInputTagsRule(baseTag, index);
});
ensureTagConfigExist(outputTagConfigs);
useComputedMultipleTag(inputTags, outputTags, customPartagedFunction, isOptimistic, validityTagMap);
}
//#region Imports
/**
* ----------
* | Import |
* ----------
*/
//#region ../../TagScriptLibrary/CommonLibrary/URL.js
// An Helper object to keep information with the URL and the fetch request parameters
/**
* @template T
* @typedef {{
* getValue():T,
* getValueAsString():string,
* resolved():T,
* then<U>(callback:(next:T)=>U):U
* }} PepitePromise
*/
/**
* @typedef {{ok:boolean, status:number,statusText:string}} PepiteResponseCommon
* @typedef { PepiteResponseCommon & {
* ok:false,
* json():null,
* text():null
* }} PepiteResponseFailed
* @typedef {{
* ok:true,
* status:200,
* statusText:"ok"
* text():PepitePromise<string>,
* json():PepitePromise<unknown>
* }} PepiteResponseSuccess
* @typedef {PepiteResponseFailed | PepiteResponseSuccess} PepiteResponse
*/
/**
* @typedef {"http"|"https"} Protocol
* @typedef {{[key:string]:string}} QueryParams
* @typedef { Stringifyable &
* {
* resolvePath(path:string | string[]):FetchURL,
* clone():FetchURL,
* addParameter(key:string, value:string):FetchURL,
* addParameters(obj:any):FetchURL,
* completeFetchRequestParams(partialFetchRequest:Partial<RequestInit>):FetchURL,
* changeFetchRequestParams(fetchRequestParams:RequestInit):FetchURL,
* useFetch():PepitePromise<PepiteResponse>,
* addHeader(key:string, value:any):FetchURL,
* addHeaders(map:{[key:string]:any}):FetchURL,
* changeBody(body:any):FetchURL
* }} URLFN
* @typedef {{protocol:Protocol, host:string, port:number, path:PathContainer, queryParams:QueryParams, fetchRequestParams:RequestInit} & URLFN} FetchURL
*/
/**
* @typedef {{tokens:string[], isAbsolute:boolean, clone():PathContainer, resolve(token:string|string[]):PathContainer, toAbsolute():PathContainer, toRelative():PathContainer} & Stringifyable} PathContainer
*/
/**
* @param {string[]} tokens
* @param {boolean} isAbolute
* @returns {PathContainer}
*/
function PathContainer(tokens, isAbolute) {
function cloneTokens() {
var nxt = [];
tokens.forEach(function (it) {
nxt.push(it);
});
return nxt;
}
function toString() {
var str = isAbolute ? '/' : '';
if (tokens.length > 0) {
var endWithSlash = tokens[tokens.length - 1].endsWith('/');
str += tokens
.map(function (it) {
var rts = it;
if (rts.endsWith('/')) {
rts = rts.substring(0, rts.length - 1);
}
if (rts.startsWith('/')) {
rts = rts.substring(1, rts.length);
}
return rts;
})
.reduce(function (previous, token) {
return previous + '/' + token;
});
if (endWithSlash) {
str += '/';
}
}
return str;
}
function clone() {
return PathContainer(cloneTokens(), isAbolute);
}
function toAbsolute() {
return PathContainer(cloneTokens(), true);
}
function toRelative() {
return PathContainer(cloneTokens(), false);
}
/**
* @param {string| string[]} token
*/
function resolve(token) {
var nxt = cloneTokens();
if (isTypedArray(token)) {
token.forEach(function (it) {
nxt.push(it);
});
} else {
nxt.push(token);
}
return PathContainer(nxt, isAbolute);
}
return {
tokens: tokens,
isAbsolute: isAbolute,
clone: clone,
resolve: resolve,
toAbsolute: toAbsolute,
toRelative: toRelative,
toString: toString
};
}
/**
* @param {Protocol} protocol
* @param {string} host
* @param {number} port
* @param {PathContainer} path
* @param {QueryParams} queryParams
* @param {RequestInit} fetchRequestParams
* @returns {FetchURL}
*/
function FetchURL(protocol, host, port, path, queryParams, fetchRequestParams) {
function toString() {
var p = '';
if (port != 80 && port != 0) {
p = ':' + port;
}
var str = protocol + '://' + host + p + path.toAbsolute().toString();
var withQ = false;
for (var k in queryParams) {
if (queryParams[k] !== undefined) {
if (!withQ) {
withQ = true;
str += '?';
} else {
str += '&';
}
var param = queryParams[k];
if (isTypedArray(param)) {
for (var i = 0; i < param.length; i++) {
if (i != 0) {
str += '&';
}
str += k + '=' + encodeURIComponent(param[i]);
}
} else {
str += k + '=' + encodeURIComponent(param);
}
}
}
return str;
}
/**
* @returns {QueryParams}
*/
function cloneQueryParams() {
/**
* @type {QueryParams}
*/
var queryP = {};
for (var key in queryParams) {
queryP[key] = queryParams[key];
}
return queryP;
}
/**
* @param {string | string[]} _path
* @returns {FetchURL}
*/
function resolvePath(_path) {
var nextPath = path.resolve(_path);
return FetchURL(protocol, host, port, nextPath, cloneQueryParams(), fetchRequestParams);
}
function clone() {
return FetchURL(protocol, host, port, path, cloneQueryParams(), fetchRequestParams);
}
function addParameter(key, value) {
var clonedQueryParams = cloneQueryParams();
clonedQueryParams[key] = value;
return FetchURL(protocol, host, port, path, clonedQueryParams, fetchRequestParams);
}
function addParameters(obj) {
var clonedQueryParams = cloneQueryParams();
clonedQueryParams = assign(clonedQueryParams, obj);
return FetchURL(protocol, host, port, path, clonedQueryParams, fetchRequestParams);
}
function changeFetchRequestParams(_fetchRequestParams) {
return FetchURL(protocol, host, port, path, cloneQueryParams(), _fetchRequestParams);
}
/**
* @returns {PepitePromise<PepiteResponse>}
*/
function useFetch() {
//@ts-ignore The Pepite java interface is not the same as the web fetch API. some overlaps allow the use of the fetch API
//but the promise is not the same
return fetch(toString(), fetchRequestParams);
}
/**
* @param {Partial<RequestInit>} partialFetchRequest
* @returns {FetchURL}
*/
function completeFetchRequestParams(partialFetchRequest) {
var nextRequestParams = assign({}, fetchRequestParams);
nextRequestParams = assign(nextRequestParams, partialFetchRequest);
return changeFetchRequestParams(nextRequestParams);
}
/**
* @param {string} key
* @param {any} value
* @returns {FetchURL}
*/
function addHeader(key, value) {
var headers = assign({}, fetchRequestParams.headers || {});
headers[key] = value;
return completeFetchRequestParams({ headers: headers });
}
/**
* @param {{[key:string]:any}} headers
* @returns {FetchURL}
*/
function addHeaders(headers) {
var url = clone();
for (var key in headers) {
var value = headers[key];
url = url.addHeader(key, value);
}
return url;
}
/**
* @param {any} body
* @returns {FetchURL}
*/
function changeBody(body) {
return completeFetchRequestParams({ body: body });
}
return {
protocol: protocol,
host: host,
path: path,
port: port,
queryParams: queryParams,
fetchRequestParams: fetchRequestParams,
toString: toString,
resolvePath: resolvePath,
clone: clone,
addParameter: addParameter,
addParameters: addParameters,
changeFetchRequestParams: changeFetchRequestParams,
useFetch: useFetch,
addHeader: addHeader,
addHeaders: addHeaders,
changeBody: changeBody,
completeFetchRequestParams: completeFetchRequestParams
};
}
/**
* @param {string} url
* @returns {FetchURL}
*/
function parseUrl(url) {
var protocol = url.split('://')[0];
if (protocol.startsWith('http')) {
protocol = protocol;
} else {
protocol = 'http';
}
var l = url.replace(protocol + '://', '');
var hostSplit = l.split(':');
var host = '';
var port = 0;
if (hostSplit.length <= 1) {
hostSplit = l.split('.');
if (hostSplit.length <= 3) {
hostSplit = l.split('/');
host = hostSplit[0];
} else {
for (var i = 0; i < 3; i++) {
host += hostSplit[i];
}
}
l = l.replace(host, '');
} else {
host = hostSplit[0];
var portSplit = hostSplit[1].split('/');
port = parseInt(portSplit[0]);
l = l.replace(host + ':' + port, '');
}
var lSplit = l.split('?');
var path = parsePath(lSplit[0]);
var queryParamsNotParsed = lSplit[1];
var queryParams = {};
if (queryParamsNotParsed != null) {
var queryPairs = queryParamsNotParsed.split('&');
for (var i = 0; i < queryPairs.length; i++) {
var keyValue = queryPairs[i].split('=');
queryParams[keyValue[0]] = decodeURIComponent(keyValue[1]);
}
}
/**
* @type {RequestInit}
*/
var fetchRequestParams = {
method: 'GET'
};
//@ts-ignore
return FetchURL(protocol, host, port, path, queryParams, fetchRequestParams);
}
/**
* @param {string} path
* @returns {PathContainer}
*/
function parsePath(path) {
var isAbsolute = path.startsWith('/');
var index = isAbsolute ? 1 : 0;
var tokens = [];
var currentToken = '';
var isEscaping = false;
while (index < path.length) {
var c = path.charAt(index);
if (isEscaping) {
currentToken += c;
isEscaping = false;
} else {
if (c == '/') {
tokens.push(currentToken);
currentToken = '';
isEscaping = false;
} else if (c == '\\') {
isEscaping = true;
} else {
currentToken += c;
}
}
index++;
}
if (currentToken != '') {
tokens.push(currentToken);
}
return PathContainer(tokens, isAbsolute);
}
//#endregion
//#region ../../TagScriptLibrary/CommonLibrary/WildCardPredicate.js
/**
* @typedef {(name:string | {toString():string})=>boolean} WildcardPredicate
*/
/**
* Create a filter function based on a wildcard string
* ex: WildcardPredicate('test*')('test123') // true
* ex: WildcardPredicate('*test')('123test') // true
* ex: WildcardPredicate('test*')('123test') // false
* ex: WildcardPredicate('test*123')('test_random_123') // true
* ex: WildcardPredicate('test*123')('test123') // true
* @param {string} starName
* @returns {WildcardPredicate}
*/
function WildcardPredicate(starName) {
var flterValues = starName.split(/\*/gm);
var hasStars = starName.indexOf('*') != -1;
var startWihStars = starName.startsWith('*');
var endWihStars = starName.endsWith('*');
/**
*
* @param {string | {toString():string}} _name
* @returns {boolean}
*/
function filter(_name) {
var name = typeof _name == 'string' ? _name : _name.toString();
if (!hasStars) {
return name == starName;
}
for (var i = 0; i < flterValues.length; i++) {
var fltr = flterValues[i];
if (fltr != '' && fltr.length > 0) {
if (i == 0 && !startWihStars) {
if (name.startsWith(fltr)) {
name = name.substr(fltr.length);
} else {
return false;
}
} else if (i == flterValues.length - 1 && !endWihStars) {
if (!name.endsWith(fltr)) {
return false;
}
} else {
var startOf = name.indexOf(fltr);
if (startOf == -1) {
return false;
}
name = name.substr(startOf + fltr.length);
}
}
}
return true;
}
return filter;
}
//#endregion
//#region ../../TagScriptLibrary/CommonLibrary/JavaArrayHelper.js
//Common function used with array to be agnostic of the type (either a Java List or a JSArray)
/**
* The Java List implementation does not possess the method filter, the wrapper will serve this purpose
* /!\ will recreate a JS array (not a stream)
* @template T
* @param {T[] | List<T>} src
* @param {(input:T, i:number)=>boolean} filterCallBack
* @returns {T[]}
*/
function filter(src, filterCallBack) {
if (isList(src)) {
var nxt = [];
var max = getLength(src);
for (var i = 0; i < max; i++) {
if (filterCallBack(src[i], i)) {
nxt.push(src[i]);
}
}
return nxt;
} else {
return src.filter(filterCallBack);
}
}
/**
* The Java List implementation does not possess the method map, the wrapper will serve this purpose
* /!\ will recreate a JS array (not a stream)
* @template T, O
* @param {T[] | List<T>} src
* @param {(input:T, i:number)=>O} mapper
* @returns {O[]}
*/
function map(src, mapper) {
if (isList(src)) {
var nxt = [];
var max = getLength(src);
for (var i = 0; i < max; i++) {
nxt.push(mapper(src[i], i));
}
return nxt;
} else {
return src.map(mapper);
}
}
/**
* @template T
* @param {T[] | List<T>} src
*/
function getLength(src) {
if ('size' in src) {
return src.size();
} else {
return src.length;
}
}
/**
* The Java List implementation does not possess the method forEach, the wrapper will serve this purpose
* @template T
* @param {T[] | List<T>} src
* @param {(input:T, i:number)=>void} onEach
*/
function forEach(src, onEach) {
if (isList(src)) {
var max = getLength(src);
for (var i = 0; i < max; i++) {
onEach(src[i], i);
}
} else {
src.forEach(onEach);
}
}
/**
* find the element by predicate
* @template T
* @template {T} S
* @param {T[] | List<T>} src
* @param {T extends S ? ((input:T)=>boolean): ((input:T)=>input is S)} predicate
* @returns {S | undefined}
*/
function find(src, predicate) {
if (isList(src)) {
var max = getLength(src);
for (var i = 0; i < max; i++) {
if (predicate(src[i])) {
return src[i];
}
}
return undefined;
} else {
//@ts-ignore
return src.find(predicate);
}
}
/**
* The Java List implementation does not have all the method like the JSArray object,
* Transform the List into a java array
* @template T
* @param {T[] | List<T>} src
* @returns {T[]}
*/
function asJSArray(src) {
//first check if it is not already a JSArray object
if (isList(src)) {
return map(src, function (it) {
return it;
});
} else {
return src;
}
}
/**
* Check if it is a JSArray or a JavaList
* @template T
* @param {T[] | List<T>} src
* @returns {src is List<T>}
*/
function isList(src) {
try {
if (typeof src == 'string') {
return false;
}
//@ts-ignore
return !(src.length != undefined && src.push != null && src.forEach != null);
} finally {
return true;
}
}
//#endregion
//#region ../../TagScriptLibrary/CommonLibrary/ObjectAssign.js
//Clone or assign element to object === to {...obj, ...element} for clone
/**
* assign the value of obj in target (mutate target)
* @template {any} T
* @template {any} O
* @param {T} target
* @param {O} obj
* @returns {T & O}
*/
function assign(target, obj) {
for (var key in obj) {
var next = obj[key];
if (next !== undefined) {
//@ts-ignore
target[key] = obj[key];
}
}
//@ts-ignore
return target;
}
/**
* Similar to var out = { ...tag, ...obj}
* @template {any} T
* @template {Partial<T>} O
* @param {O} target
* @param {Partial<O> & Omit<T, keyof O>} obj
* @returns {T}
*/
function cloneAssign(target, obj) {
var newObj = cloneObj(target);
//@ts-ignore
return assign(newObj, obj);
}
/**
* Similar to var out = { ...obj }
* @template {any} T
* @param {T} obj
* @returns {T}
*/
function cloneObj(obj) {
return assign({}, obj);
}
//#endregion
//#region ../../TagScriptLibrary/CommonLibrary/isArray.js
/**
* Check if the object is a JSArray of T or the object T
* @template T
* @param {T | T[]} obj
* @returns {obj is T[]}
*/
function isTypedArray(obj) {
if (Array.isArray(obj)) {
return true;
} else {
if (typeof obj != 'object') {
return false;
} else {
try {
return typeof (/**@type {any} */ (obj).length) == 'number';
} catch (e) {
return false;
}
}
}
}
//#endregion
//#region ../../TagScriptLibrary/CommonLibrary/ComputeMultipleTag.js
//Create a computed tag function to apply on multiple tags
/**
* @typedef {( values:TagValue[], time:number, lastValue:Optional<TagValue[]>, lastTime:Optional<number>)=>Optional<TagValue>} ComputeTagFn
*/
/**
* @param {JavaVirtualPath[][]} inputsTags The tags to be used as inputs
* @param {JavaVirtualPath[]} outputTags The corresponding output tag
* @param {ComputeTagFn} computeTagFn The function to be applied on each inputTags (called for each inputTags[i] and outputTags[i] pair)
* @param {boolean} [isOptimistic] compute the data until the min date of the input tags included if true, excluded if false, default to true
* @param {Optional<Optional<number>[]>} [validityTagMap] The validity of each tags (inputs)
*/
function useComputedMultipleTag(inputsTags, outputTags, computeTagFn, isOptimistic, validityTagMap) {
for (var i = 0; i < inputsTags.length; i++) {
useAComputedTag(inputsTags[i], outputTags[i], computeTagFn, isOptimistic == null ? true : isOptimistic, validityTagMap);
}
}
/**
* @param {JavaVirtualPath[]} inputsTag
* @param {JavaVirtualPath} outputTag
* @param {ComputeTagFn} computeTagFn
* @param {boolean} isOptimistic
* @param {Optional<Optional<number>[]> | undefined} validityTagMap
*/
function useAComputedTag(inputsTag, outputTag, computeTagFn, isOptimistic, validityTagMap) {
var lastDate = tagConnection.getLastDate(outputTag);
var lastDateNum = lastDate == null ? null : lastDate.getTime();
/**
* @type {SingleRollingInput<TagValue[]>}
*/
var rollInput = SingleRollingInput();
/**
* @type {{lastTime:Optional<number>}}
*/
var lastTimeHolder = {
lastTime: null
};
/**
* @type {JavaDate | null}
*/
var minEndTime = null;
for (var i = 0; i < inputsTag.length; i++) {
var inputTagLastDate = tagConnection.getLastDate(inputsTag[i]);
if (inputTagLastDate == null) {
//no data for input tag, skip
return;
} else {
var inputTagLastTime = inputTagLastDate.getTime();
var validity = validityTagMap ? validityTagMap[i] : null;
if (validity != null) {
inputTagLastTime += validity;
}
if (minEndTime == null || inputTagLastTime < minEndTime.getTime()) {
minEndTime = JDate.fromTime(inputTagLastTime);
}
}
}
if (isOptimistic) {
minEndTime = minEndTime != null ? JDate.fromTime(minEndTime.getTime() + 1) : null;
}
/**
* @param {HistoryRecord} record
*/
function processor(record) {
var recordValues = [];
for (var i = 0; i < record.colCount(); i++) {
recordValues.push(record.get(i));
}
var time = record.getTime();
var lastTime = lastTimeHolder.lastTime;
var lastRecordsValues = rollInput.getInput();
var lastValues = lastRecordsValues.previous;
var output = computeTagFn(recordValues, time, lastValues, lastTime);
if (lastDateNum == null || time > lastDateNum) {
tagConnection.putValue(outputTag, JDate.fromTime(time), output);
}
lastTimeHolder.lastTime = time;
rollInput.roll(recordValues);
return true;
}
tagConnection.retrieveValues(lastDate, minEndTime, true, inputsTag).apply(processor);
}
//#endregion
//#region ../../TagScriptLibrary/CommonLibrary/RollInput.js
// An input that can be rolled to keep track of previous values
/**
* @template Values
* @typedef {{current:Optional<Values>, previous:Optional<Values>, next:Optional<Values>}} FNInput
*/
/**
* @template Values
* @typedef {{current:Optional<Values>, previous:Optional<Values>}} SingleFnInput
*/
/**
* @template Values
* @typedef {{size:number, current:Optional<Values>, [back:number]:Optional<Values>}} CountFnInput
*/
/**
* @template V
* @typedef {{roll(value:Optional<V>), getInput():FNInput<V> }} RollingInput
*/
/**
* @template V
* @typedef {{roll(value:Optional<V>), getInput():SingleFnInput<V> }} SingleRollingInput
*/
/**
* @template V
* @typedef {{roll(value:Optional<V>), getInput():CountFnInput<V> }} CountRollingInput
*/
/**
* @template V
* @returns {RollingInput<V>}
*/
function RollingInput() {
/**
* @type {FNInput<V>}
*/
var roller = {
previous: null,
current: null,
next: null
};
/**
* @param {V} value
*/
function roll(value) {
roller.previous = roller.current;
roller.current = roller.next;
roller.next = value;
}
/**
* @returns {FNInput<V>}
*/
function getInput() {
return assign({}, roller);
}
return {
roll: roll,
getInput: getInput
};
}
/**
* @template V
* @returns {SingleRollingInput<V>}
*/
function SingleRollingInput() {
/**
* @type {SingleFnInput<V>}
*/
var roller = {
previous: null,
current: null
};
/**
* @param {V} value
*/
function roll(value) {
roller.previous = roller.current;
roller.current = value;
}
/**
* @returns {SingleFnInput<V>}
*/
function getInput() {
return assign({}, roller);
}
return {
roll: roll,
getInput: getInput
};
}
/**
* @template V
* @param {number} size
* @returns {CountRollingInput<V>}
*/
function CountRollingInput(size) {
/**
* @type {CountFnInput<V>}
*/
var roller = {
size: size,
current: null
};
/**
* @param {V} value
*/
function roll(value) {
if (size > 0) {
for (var i = -size; i < -1; i++) {
roller[i] = roller[i + 1];
}
roller[-1] = roller.current;
}
roller.current = value;
}
/**
* @returns {CountFnInput<V>}
*/
function getInput() {
return assign({}, roller);
}
return {
roll: roll,
getInput: getInput
};
}
//#endregion
//#region ../../TagScriptLibrary/CommonLibrary/TypeHelp.js
/**
* @template T
* @typedef {T | null} Optional<T>
*/
/**
* @typedef {()=>void} Toggler
* @typedef {{toString():string}} Stringifyable
*/
/**
* @template T
* @typedef {{[key:string]:T}} JSMap
*/
/**
* @template { string} K
* @template V
* @typedef {{[P in K]?:Optional<V>}} PartialEnumTypeToMap
*/
/**
* @template { string} K
* @template V
* @typedef {{[P in K]:V}} EnumTypeToMap
*/
/**
* @template {string} U
* @template {string} F
* @typedef {U extends `${infer A}${F}${infer B}` ? U : F} StringMustContains
*/
/**
* @template T
* @typedef {T extends null | undefined ? null | undefined : T} IsNotNullType
*/
/**
* @typedef {"Equals" | "Less" | "Greater" } Order
*/
//#endregion
//#region ../../TagScriptLibrary/CommonLibrary/MetaInfo.js
/**
* @typedef {{name:string, isNumeric:boolean, title?:Optional<string>, classifiers?:Optional<Map<string, any> | JSMap<any>>, description?:Optional<string>, unit?:Optional<string|Unit>}} MetaInfo
*/
//#endregion
//#region ../../TagScriptLibrary/CommonLibrary/isUnit.js
/**
* @param {string | Unit} unit
* @returns {unit is Unit}
*/
function isUnit(unit) {
return isJavaObject(unit);
}
//#endregion
//#region ../../TagScriptLibrary/CommonLibrary/Context.js
// Context of the script, to store data to share between function without using global variable (safer as global variable could not be initialized).
/**
* @type {{[contextName:string]:any}}
*/
var SCRIPT_CONTEXT;
/**
* return the context value or create if don't exist.
* /!\ As the CONTEXT itself is not created until the code is read (ie, if not in a function that run after the code is completely read)
* The @param contextName can be null if store in a variable.
* @template T
* @param {string} contextName
* @param {T} [initialValue]
* @returns {(initialValue extends undefined ? Partial<T> : T)}
*/
function getContext(contextName, initialValue) {
if (!isInContext(contextName)) {
SCRIPT_CONTEXT[contextName] = initialValue;
}
return SCRIPT_CONTEXT[contextName];
}
/**
* return the context value or create if don't exist.
* /!\ As the CONTEXT itself is not created until the code is read (ie, if not in a function that run after the code is completely read)
* The @param contextName can be null if store in a variable.
* @template T
* @param {string} contextName
* @param {()=>T} initialValue
* @returns {T}
*/
function getContextOrCreate(contextName, initialValue) {
if (!isInContext(contextName)) {
SCRIPT_CONTEXT[contextName] = initialValue();
}
return SCRIPT_CONTEXT[contextName];
}
function initialiseContext() {
if (SCRIPT_CONTEXT == null) {
SCRIPT_CONTEXT = {};
}
}
/**
* @param {string} contextName
*/
function isInContext(contextName) {
initialiseContext();
return contextName in SCRIPT_CONTEXT;
}
/**
* @template T
* @param {string} contextName
* @param {T} value
* @returns {T}
*/
function putInContext(contextName, value) {
initialiseContext();
SCRIPT_CONTEXT[contextName] = value;
return SCRIPT_CONTEXT[contextName];
}
/**
* @template {{[key:string]:any}} T
* @template {keyof T} U
* @param {string} contextName
* @param {U} key
* @param {T[U]} value
* @returns {T}
*/
function putContextValue(contextName, key, value) {
var context = /** @type {T} */ (getContext(contextName, {}));
context[key] = value;
return context;
}
/**
* @template {{[key:string]:any}} T
* @param {string} contextName
* @param {Partial<T>} value
* @returns {T}
*/
function updateContext(contextName, value) {
var context = /** @type {T} */ (getContext(contextName, {}));
return assign(context, value);
}
//#endregion
//#region ../../TagScriptLibrary/CommonLibrary/tagCreationConfig.js
// Help with the creation of tags and their metadata