{"id":41692591,"url":"https://github.com/wshager/frink","last_synced_at":"2026-01-24T20:23:41.957Z","repository":{"id":18453141,"uuid":"84342491","full_name":"wshager/frink","owner":"wshager","description":"Standards-based hierarchical data processing library","archived":false,"fork":false,"pushed_at":"2023-01-03T15:16:39.000Z","size":2562,"stargazers_count":3,"open_issues_count":9,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-20T00:09:11.405Z","etag":null,"topics":["immutable","json","l3n","raddle","xml","xpath","xquery"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/wshager.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-08T16:32:47.000Z","updated_at":"2021-06-16T21:57:27.000Z","dependencies_parsed_at":"2023-01-13T19:50:23.549Z","dependency_job_id":null,"html_url":"https://github.com/wshager/frink","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/wshager/frink","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wshager%2Ffrink","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wshager%2Ffrink/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wshager%2Ffrink/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wshager%2Ffrink/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/wshager","download_url":"https://codeload.github.com/wshager/frink/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/wshager%2Ffrink/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28736509,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-24T19:23:36.361Z","status":"ssl_error","status_checked_at":"2026-01-24T19:23:28.966Z","response_time":89,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["immutable","json","l3n","raddle","xml","xpath","xquery"],"created_at":"2026-01-24T20:23:41.273Z","updated_at":"2026-01-24T20:23:41.951Z","avatar_url":"https://github.com/wshager.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Frink: everything but the kitchen sink\n\nParse XML into a DOM level 4 compatible, persistent (AKA immutable) virtual tree.\n\nTraverse trees with speed-optimized functions reminiscent of XPath.\n\nParse JSON into similar, traversable trees, or mix with XML.\n\nGenerate L3, a lightweight, flat representation of XML or JSON trees (or mixed) for fast storage and retrieval.\n\nConstruct trees with a coherent set of functions, to create persistent HTML, XML or JSON documents.\n\nRender persistent trees or L3 as HTML in the browser. Uses a super-simple, lightning-fast tree diffing algorithm.\n\nValidate both XML and JSON using a custom extension of JSON Schema v0.4.\n\nTo be used together with [Raddle](http://raddle.org)'s standards-based document processing language.\n\n---\n\n## Why Frink?\n\n\u003e It is not the spoon that bends\n\nThe browser's DOM API is ... inconvenient. The stuff you see in a web application is not always represented in the source code. Instead, they are in some part of the browser you need to access or update. So, like others have done before, we'll just connect to those parts and modify our document as though they're already part of it. I didn't come up with this idea: it's called virtual DOM, and there are many libraries out there that work like this.\n\nHowever, what is called virtual DOM is actually not HTML anymore, as it must obey stricter rules than is just required for the representation you download from the server. It's actually a mix of XML and javascript data (AKA JSON). This library tries to formalize that notion, in a data model called [L3N](http://l3n.org). It also provides tried and tested tools for accessing and updating this data model, as they have been developed in the W3C XML working group. These functional tools have been used for years on semi-structured data with great success. As XML has lost its appeal as data modelling language, I try to salvage the good parts with a modern javascript API, for use in the browser and in nodejs.\n\nFrink integrates with legacy XML projects that don't rely on DTD validation.\n\n## API (WIP)\n\n### Sequences\n\nSequences come in two flavors. The default (ArraySeq) is based on a javascript array, the other is an Observable sequence. The two are interopable, to the extend that the array-based Sequence implements a limited number of methods from RxJS and converts to an Observable when needed. To guarantee interoperability, you should use the functions provided by Frink instead of RxJS.\n\n#### seq(...) =\u003e \u003ccode\u003eArraySeq|Observable\u003c/code\u003e\n\nCreates a sequence. Any sequences or iterables (except strings) in arguments are flattened. In case any argument is an Observable or a Promise, the sequence is converted to an Observable.\n\n#### zeroOrOne(seqOrValue) =\u003e \u003ccode\u003eArraySeq|Observable\u003c/code\u003e\n\nTests a sequence for cardinality. If it contains zero or one item, the sequence is returned. Else an error is thrown instead.\n\n| Param  | Type                | Description  |\n| ------ | ------------------- | ------------ |\n| seqOrValue | \u003ccode\u003eArraySeq|Observable|\\*\u003c/code\u003e | The sequence or value to test |\n\n\n#### exactlyOne(seqOrValue) =\u003e \u003ccode\u003eArraySeq|Observable\u003c/code\u003e\n\nTests a sequence for cardinality. If it contains exactly one item, the sequence is returned. Else an error is thrown instead.\n\n| Param  | Type                | Description  |\n| ------ | ------------------- | ------------ |\n| seqOrValue | \u003ccode\u003eArraySeq|Observable|\\*\u003c/code\u003e | The sequence or value to test |\n\n\n#### oneOrMore(seqOrValue) =\u003e \u003ccode\u003eArraySeq|Observable\u003c/code\u003e\n\nTests a sequence for cardinality. If it contains one or more items, the sequence is returned. Else an error is thrown instead.\n\n| Param  | Type                | Description  |\n| ------ | ------------------- | ------------ |\n| seqOrValue | \u003ccode\u003eArraySeq|Observable|\\*\u003c/code\u003e | The sequence or value to test |\n\n\n#### empty(seqOrValue) =\u003e \u003ccode\u003eBoolean|Observable\u003cBoolean\u003e\u003c/code\u003e\n\nTests is a sequence is entry. Returns a boolean (for `ArraySeq` or `null`) or an `Observable` holding a boolean value if the param is an Observable.\n\n| Param  | Type                | Description  |\n| ------ | ------------------- | ------------ |\n| seqOrValue | \u003ccode\u003eArraySeq|Observable|\\*\u003c/code\u003e | The sequence or value to test |\n\n\n#### exists(seqOrValue) =\u003e \u003ccode\u003eBoolean|Observable\u003cBoolean\u003e\u003c/code\u003e\n\nThe opposite of `empty`. Returns a boolean for `ArraySeq` or any other value that isn't a sequence. Returns an `Observable` holding a boolean value if the param is an Observable.\n\n| Param  | Type                | Description  |\n| ------ | ------------------- | ------------ |\n| seqOrValue | \u003ccode\u003eArraySeq|Observable|\\*\u003c/code\u003e | The sequence or value to test |\n\n\n#### forEach(seqOrValue, fn)\n\nApply function to value or each value in sequence.\n\n| Param  | Type                | Description  |\n| ------ | ------------------- | ------------ |\n| seqOrValue | \u003ccode\u003eArraySeq|Observable|\\*\u003c/code\u003e | The sequence or value to test |\n| fn | \u003ccode\u003efunction\u003c/code\u003e | Transformation |\n\n\n#### filter(seqOrValue, fn)\n\nFilter value or values in sequence based on provided function.\n\n| Param  | Type                | Description  |\n| ------ | ------------------- | ------------ |\n| seqOrValue | \u003ccode\u003eArraySeq|Observable|\\*\u003c/code\u003e | The sequence or value to test |\n| fn | \u003ccode\u003efunction\u003c/code\u003e | Filter function |\n\n\n#### foldLeft(seqOrValue[, seed], fn)\n\nReduce sequence or value to a new sequence.\n\n| Param  | Type                | Description  |\n| ------ | ------------------- | ------------ |\n| seqOrValue | \u003ccode\u003eArraySeq|Observable|\\*\u003c/code\u003e | The sequence or value to test |\n| fn | \u003ccode\u003efunction\u003c/code\u003e | Transformation |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwshager%2Ffrink","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwshager%2Ffrink","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwshager%2Ffrink/lists"}