kriti-lang
v1.0.0
Published
A TypeScript implementation of the Kriti templating language
Maintainers
Readme
Kriti Lang TypeScript
A TypeScript implementation of the Kriti templating language - a minimal JSON templating language inspired by Go's template language.
Kriti templates are a superset of JSON with path lookups, if/then/else expressions, loops, and some basic predicate and conditional operators.
Note: This is a TypeScript port of the original Kriti Lang implementation by Hasura, which is written in Haskell.
Installation
npm install kriti-langQuick Start
import { evaluate } from 'kriti-lang';
const template = `{
"author": {
"name": {{$.event.name}},
"age": {{$.event.age}},
"articles": [
{{ range _, x := $.event.author.articles }}
{
"id": {{x.id}},
"title": {{x.title}}
}
{{ end }}
]
}
}`;
const data = {
"event": {
"name": "Freddie Jones",
"age": 27,
"author": {
"articles": [
{ "id": 0, "title": "The Elements", "length": 150, "published": true},
{ "id": 1, "title": "ARRL Handbook", "length": 1000, "published": true},
{ "id": 2, "title": "The Mars Trilogy", "length": 500, "published": false}
]
}
}
};
const result = evaluate(template, { $: data });
console.log(result);
// Output: Transformed JSON with author info and filtered articles
// {
// "author": {
// "name": "Freddie Jones",
// "age": 27,
// "articles": [
// {"id": 0, "title": "The Elements"},
// {"id": 1, "title": "ARRL Handbook"},
// {"id": 2, "title": "The Mars Trilogy"}
// ]
// }
// }Kriti Expressions
Path Accessors
Values can be looked up in bound json expressions using the standard path lookup syntax:
{{ $body.foo.bar[0]['my key'] }}.is used to look up object fields[x]is used to lookup array indices['a b c']is used to lookup string literal object fields
If a variable is unbound, the kriti template fail and throw an exception. To prevent such failures, we provide an "optional" lookup operator:
{{ $body?.foo }}This example will return a null if foo is not bound in $body. Optional lookups will immediately shortcircuit with null so that the following will not attempt any lookups past the unbound foo:
{{ $body?.foo.bar.baz }}foo?is used to optionally look up a variable.?.is used to optionally look up object fields?[x]is used to optionally lookup array indices?['a b c']is used to optionally lookup string literal object fields
Defaulting Operator
The defaulting operator ?? can be used to replace a null value with any other value. The expression null ?? true will evaluate to true. This is especially useful when used with path lookups:
$foo?.bar ?? trueLoops
The range identifier is used to declare for loops:
{{ range i, x := $.event.author.articles }}
{{ x.title }}
{{ end }}i and x above are binders for the index and value of the array element from $.event.author.articles. The index can be omitted by using an underscore in its place.
If Statements
Kriti supports if statements and > < == || and && operators.
{{ if x.published && (x.post_id > 100) }}
{
"id": {{x.id}},
"title": {{x.title}}
}
{{ else }}
null
{{ end }}Use elif for multiple conditionals.
{{ if x.published && (x.post_id > 100) }}
{
"id": {{x.id}},
"title": {{x.title}}
}
{{ elif x.published && (x.post_id <= 100) }}
{
"id": {{x.id}},
"title": {{x.title}},
"content": {{x.content}}
}
{{ else }}
null
{{ end }}String Interpolation
Bound variables, booleans, integers, object/array lookups, and functions can be interpolated:
"http://www.{{$.domain}}.com/{{$.path}}""http://www.{{$.domain}}.com/{{ $?[1000] }}"API Reference
Main Functions
evaluate(template, variables?, customFunctions?)
Evaluates a Kriti template with the provided data.
function evaluate(
input: string,
variables?: RuntimeObject,
customFunctions?: Map<string, CustomFunction>
): anyParameters:
input- The Kriti template stringvariables- Data object (typically passed as{ $: yourData })customFunctions- Map of custom function implementations
Low-Level API
For advanced use cases, you can use the individual components:
import { tokenize, parseTokens, evaluateAST } from 'kriti-lang';
// Tokenization
const tokens = tokenize(template);
// Parsing
const ast = parseTokens(tokens);
// Evaluation
const result = evaluateAST(ast, variables, customFunctions);Custom Functions
Extend Kriti with your own functions:
import { evaluate } from 'kriti-lang';
const customFunctions = new Map();
customFunctions.set('upperCase', (args) => {
return args[0]?.toString().toUpperCase();
});
const template = '{{ upperCase($.name) }}';
const result = evaluate(template, { $: { name: "alice" } }, customFunctions);
// Output: "ALICE"Transformation Examples
JSON Input:
{
"event": {
"name": "Freddie Jones",
"age": 27,
"author": {
"articles": [
{ "id": 0, "title": "The Elements", "length": 150, "published": true},
{ "id": 1, "title": "ARRL Handbook", "length": 1000, "published": true},
{ "id": 2, "title": "The Mars Trilogy", "length": 500, "published": false}
]
}
}
}Template Example:
{
"author": {
"name": {{$.event.name}},
"age": {{$.event.age}},
"articles": [
{{ range _, x := $.event.author.articles }}
{
"id": {{x.id}},
"title": {{x.title}}
}
{{ end }}
]
}
}JSON Output:
{
"author": {
"name": "Freddie Jones",
"age": 27,
"articles": [
{"id": 0, "title": "The Elements"},
{"id": 1, "title": "ARRL Handbook"},
{"id": 2, "title": "The Mars Trilogy"}
]
}
}Template Example 2:
{
"author": {
"name": {{$.event.name}},
"age": {{$.event.age}},
"articles": [
{{ range _, x := $.event.author.articles }}
{{ if x.published }}
{
"id": {{x.id}},
"title": {{x.title}}
}
{{ else }}
null
{{ end }}
{{ end }}
]
}
}JSON Output 2:
{
"author": {
"name": "Freddie Jones",
"age": 27,
"articles": [
{"id": 0, "title": "The Elements"},
{"id": 1, "title": "ARRL Handbook"},
null
]
}
}Original Implementation
This TypeScript implementation is based on the original Kriti Lang by Hasura, written in Haskell. For more information about the language specification and the original implementation, visit the original repository.
