{"id":13472394,"url":"https://github.com/hasura/kriti-lang","last_synced_at":"2025-04-23T16:32:57.255Z","repository":{"id":37827820,"uuid":"390166700","full_name":"hasura/kriti-lang","owner":"hasura","description":"A minimal JSON templating language","archived":false,"fork":false,"pushed_at":"2023-06-11T01:57:32.000Z","size":432,"stargazers_count":56,"open_issues_count":6,"forks_count":9,"subscribers_count":13,"default_branch":"main","last_synced_at":"2024-10-30T04:14:00.302Z","etag":null,"topics":["haskell","hasura","json","template-lan"],"latest_commit_sha":null,"homepage":"","language":"Haskell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hasura.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-28T00:43:04.000Z","updated_at":"2024-08-01T08:47:26.000Z","dependencies_parsed_at":"2024-10-30T02:52:09.294Z","dependency_job_id":null,"html_url":"https://github.com/hasura/kriti-lang","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hasura%2Fkriti-lang","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hasura%2Fkriti-lang/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hasura%2Fkriti-lang/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hasura%2Fkriti-lang/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hasura","download_url":"https://codeload.github.com/hasura/kriti-lang/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223929530,"owners_count":17226909,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["haskell","hasura","json","template-lan"],"created_at":"2024-07-31T16:00:54.341Z","updated_at":"2024-11-10T08:24:16.620Z","avatar_url":"https://github.com/hasura.png","language":"Haskell","funding_links":[],"categories":["Haskell"],"sub_categories":[],"readme":"# Kriti Lang\n\n[![kriti-lang::CI](https://github.com/hasura/kriti-lang/actions/workflows/haskell.yml/badge.svg)](https://github.com/hasura/kriti-lang/actions/workflows/haskell.yml)\n\nA minimal json templating language inspired by Go's template language.\n\nKriti templates are a superset of JSON with path lookups, if/then/else expressions, loops, and some basic predicate and conditional operators.\n\nKriti expressions are wrapped in double curly brackets such as `\"http://wwww.google.com/{{$body.path}}\"`. The `Kriti` evaluator takes `Kriti` template and a set of source json expressions paired with binders then uses them to construct a new json expression.\n\n### Kriti Expressions\n\n#### Path Accessors\n\nValues can be looked up in bound json expressions using the standard path lookup syntax:\n```\n{{ $body.foo.bar[0]['my key'] }}\n```\n\n* `.` is used to look up object fields\n* `[x]` is used to lookup array indices\n* `['a b c']` is used to lookup string literal object fields\n\nIf a variable is unbound, the kriti template fail and throw an exception. To prevent such failures, we provide an \"optional\" lookup operator:\n\n```\n{{ $body?.foo }}\n```\nThis 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`:\n```\n{{ $body?.foo.bar.baz }}\n```\n\n* `foo?` is used to optionally look up a variable.\n* `?.` is used to optionally look up object fields\n* `?[x]` is used to optionally lookup array indices\n* `?['a b c']` is used to optionally lookup string literal object fields\n\n#### Defaulting Operator\n\nThe 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:\n\n```\n$foo?.bar ?? true\n```\n\n#### Loops\n\nThe `range` identifier is used to declare for loops:\n```\n{{ range i, x := $.event.author.articles }}\n  {{ x.title }}\n{{ end }}\n```\n`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.\n\n#### If Statements\nKriti supports if statements and `\u003e` `\u003c` `==` `||` and `\u0026\u0026` operators.\n```\n{{ if x.published \u0026\u0026 (x.post_id \u003e 100) }}\n    {\n      \"id\": {{x.id}},\n      \"title\": {{x.title}}\n    }\n{{ else }}\n    null\n{{ end }}\n```\n\nUse `elif` for multiple conditionals.\n```\n{{ if x.published \u0026\u0026 (x.post_id \u003e 100) }}\n    {\n      \"id\": {{x.id}},\n      \"title\": {{x.title}}\n    }\n{{ elif x.published \u0026\u0026 (x.post_id \u003c= 100) }}\n    {\n      \"id\": {{x.id}},\n      \"title\": {{x.title}},\n      \"content\": {{x.content}}\n    }\n{{ else }}\n    null\n{{ end }}\n```\n\n#### String Interpolation\nBound variables, booleans, integers, object/array lookups, and functions can be interpolated:\n```\n\"http://www.{{$.domain}}.com/{{$.path}}\"\n```\n\n```\n\"http://www.{{$.domain}}.com/{{ $?[1000] }}\"\n```\n\n### Library\nThe library exposes two function `runKriti` and `runKritiWith`, the type definitions of the function are:\n``` haskell\nrunKriti :: Text -\u003e [(Text, Value)] -\u003e Either KritiErr Value\n```\nThe first argument of the function is the template JSON, for example, we can use `myTemplate` as the first argument:\n``` haskell\nmyTemplate :: Text\nmyTemplate =\n    \"{\\\n    \\   'name': {{$.name.english}},\\\n    \\   'id': {{$.id}},\\\n    \\   'hp': {{$.base.HP}}\\\n    \\}\"\n```\n\n``` haskell\nrunKritiWith :: T.Text -\u003e [(T.Text, J.Value)] -\u003e Map.HashMap T.Text (J.Value -\u003e Either CustomFunctionError J.Value) -\u003e Either KritiError J.Value\n```\n`runKritiWith` has an additional argument, which takes a hashmap from name of the custon function to it's haskell definition\n\n#### Library Usage Sample Program\nTo run the example, first clone this repository using the following command:\n``` sh\ngit clone git@github.com:hasura/kriti-lang.git\n```\nNow, run the following command:\n``` sh\ncd kriti-lang\ncabal new-run example\n```\n\nThe second argument is a `list` of `tuple` of `(Text, Value)`. The first element of the tuple is the binding to be used for the JSON object, i.e. for the above template we are using `x` as the JSON binding, so, `x` will bind to the JSON object. The second element of the tuple is of type `Data.Aeson.Value` (can be obtained by `Data.Aeson.decode` method).\n\nThe function `runKriti` will return `Either KritiErr Value`. If the parser is successful, then it will return `Right Value`, else it will return `Left KritiErr` which can be used for debugging.\n\n#### Basic Functions Collection\n\nThe `Kriti.CustomFunctions` module defines functions that can be enabled by including in the `Map` given to `runKritiWith`.\n\nThere is also a collection of all these functions defined as `basicFuncMap` that can act as a Kriti stdlib or prelude.\n\nFor reference, these functions are listed here:\n\n| Function Name | Description\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp;\u0026nbsp; | Example Template | Output |\n| --- | --- | --- | --- |\n| empty | Returns `true` if an `object`, `array`, or `string` is empty, if a number is `0`, and true for `null`. Raises an error for `boolean`s. | `{\"object\": {{ empty({\"a\": 1}) }}, \"string\": {{ empty(\"\") }}, \"array\": {{ empty([1]) }} }` | `{\"array\":false,\"object\":false,\"string\":true}` |\n| size | Returns the length of an `array` or `string`, the number of keys of an `object`, the value of a `number`, `1` for `true` and `0` for `false`, and `0` for `null`. | `{\"object\": {{ size({\"a\": 1}) }}, \"string\": {{ size(\"asdf\") }}, \"array\": {{ size([1]) }} }` | `{\"array\":1,\"object\":1,\"string\":4}`|\n| inverse | Reverses an `array` or `string`, leaves an `object` or `null` as-is, takes the reciprical of a number, and negates a `bool`. | `{\"string\": {{ inverse(\"asdf\") }}, \"array\": {{ inverse([1,2,3]) }}, \"number\": {{ inverse(4) }} }` | `{\"array\":[3,2,1],\"number\":0.25,\"string\":\"fdsa\"}` |\n| head | Takes the first element or character of an `array` or `string`. Throws an error if they are empty, and throws an error for all other types. | `{\"string\": {{ head(\"asdf\") }}, \"array\": {{ head([1,2,3]) }} }` | `{\"array\":1,\"string\":\"a\"}` |\n| tail | Drops the first element of an `array` or `string`. Throws an error for all other types. | `{\"string\": {{ tail(\"asdf\") }}, \"array\": {{ tail([1,2,3]) }} }` | `{\"array\":[2,3],\"string\":\"sdf\"}` |\n| toCaseFold | Converts a `string` to a normalized casing (useful for case-insensitive string comparison). Throws an error for non-strings. | `{\"string\": {{toCaseFold(\"AbCd\")}} }` | `{\"string\":\"abcd\"}` |\n| toLower | Converts a `string` to lower-case. Throws an error for non-strings. | `{\"string\": {{toLower(\"AbCd\")}} }` | `{\"string\":\"abcd\"}` |\n| toUpper | Converts a `string` to upper-case. Throws an error for non-strings. | `{\"string\": {{toUpper(\"AbCd\")}} }` | `{\"string\":\"ABCD\"}` |\n| toTitle | Converts a `string` to title-case. Throws an error for non-strings. | `{\"string\": {{toTitle(\"AbCd\")}} }` | `{\"string\":\"Abcd\"}` |\n| fromPairs | Convert an `array` like `[ [a,b], [c,d] ... ]` to an `object` like `{ a:b, c:d ... }` | `{\"array\": {{ fromPairs([[\"a\",1],[\"b\",2]]) }} }` | `{\"array\":{\"a\":1,\"b\":2}}` |\n| toPairs | Convert an `object` like `{ a:b, c:d ... }` to an `array` like `[ [a,b], [c,d] ... ]`. | `{\"object\": {{ toPairs({\"a\": 1, \"b\": 2}) }} }` | `{\"object\":[[\"a\",1],[\"b\",2]]}` |\n| removeNulls | Removes `null` items from an array. | `{\"array\": {{ removeNulls([1,null,3,null,5]) }} }` | `{\"array\":[1,3,5]}` |\n| concat | Concatenates a `string`, `array`, or `object` - for objects keys from right-most objects are preferred in a collision. | `{\"arrays\": {{ concat([[1,2],[3,4]]) }}, \"strings\": {{ concat([\"abc\", \"def\", \"g\"]) }}, \"objects\": {{ concat([{\"a\":1, \"b\":2},{\"b\":3, \"c\":4} ] ) }} }` | `{\"arrays\":[1,2,3,4],\"objects\":{\"a\":1,\"b\":3,\"c\":4},\"strings\":\"abcdefg\"}` |\n\n### CLI Tool\nThe executable is a CLI tool which applies a transformation to a single json file:\n``` bash\n➜ cabal run kriti -- --json test/data/eval/success/source.json --template test/data/eval/success/examples/example1.kriti\n{\"guid\":\"43a922da-9665-4099-8dfc-f9af369695a4\"}\n```\nThe binder for the source file can be changed wit the `--bind` flag.\n\n## Transformation Examples\n\nJSON Input:\n```\n{\n  \"event\": {\n    \"name\": \"Freddie Jones\",\n    \"age\": 27,\n    \"author\": {\n      \"articles\": [\n        { \"id\": 0, \"title\": \"The Elements\", \"length\": 150, \"published\": true},\n        { \"id\": 1, \"title\": \"ARRL Handbook\", \"length\": 1000, \"published\": true},\n        { \"id\": 2, \"title\": \"The Mars Trilogy\", \"length\": 500, \"published\": false}\n      ]\n    }\n  }\n}\n```\n\nTemplate Example:\n```\n{\n  \"author\": {\n    \"name\": {{$.event.name}},\n    \"age\": {{$.event.age}},\n    \"articles\": [\n{{ range _, x := $.event.author.articles }}\n      {\n        \"id\": {{x.id}},\n        \"title\": {{x.title}}\n      }\n{{ end }}\n    ]\n  }\n}\n```\nJSON Output:\n```\n{\n  \"author\": {\n    \"name\": \"Freddie Jones\",\n    \"age\": 27,\n    \"articles\": [\n      {\"id\": 0, \"title\": \"The Elements\"},\n      {\"id\": 1, \"title\": \"ARRL Handbook\"},\n      {\"id\": 2, \"title\": \"The Mars Trilogy\"}\n    ]\n  }\n}\n```\n\nTemplate Example 2:\n```\n{\n  \"author\": {\n    \"name\": {{$.event.name}},\n    \"age\": {{$.event.age}},\n    \"articles\": [\n{{ range _, x := $.event.author.articles }}\n  {{ if x.published }}\n      {\n        \"id\": {{x.id}},\n        \"title\": {{x.title}}\n      }\n  {{ else }}\n      null\n  {{ end }}\n{{ end }}\n    ]\n  }\n}\n```\n\nJSON Output 2:\n```\n{\n  \"author\": {\n    \"name\": \"Freddie Jones\",\n    \"age\": 27,\n    \"articles\": [\n      {\"id\": 0, \"title\": \"The Elements\"},\n      {\"id\": 1, \"title\": \"ARRL Handbook\"},\n      null\n    ]\n  }\n}\n```\n\n## Contributing\n\nThank you for considering to contribute to `kriti-lang`!\n\n- We use `ormolu` for formatting. The minimum version of ormolu required is `0.3.0.0`.\n- Use `GHC` version `8.10.4` or above.\n- Use `cabal` version `3.2.0.0` or above.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhasura%2Fkriti-lang","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhasura%2Fkriti-lang","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhasura%2Fkriti-lang/lists"}