@rejot-dev/thalo 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +396 -0
- package/dist/ast/ast-types.d.ts +469 -0
- package/dist/ast/ast-types.d.ts.map +1 -0
- package/dist/ast/ast-types.js +11 -0
- package/dist/ast/ast-types.js.map +1 -0
- package/dist/ast/builder.js +158 -0
- package/dist/ast/builder.js.map +1 -0
- package/dist/ast/extract.js +748 -0
- package/dist/ast/extract.js.map +1 -0
- package/dist/ast/node-at-position.d.ts +147 -0
- package/dist/ast/node-at-position.d.ts.map +1 -0
- package/dist/ast/node-at-position.js +382 -0
- package/dist/ast/node-at-position.js.map +1 -0
- package/dist/ast/visitor.js +232 -0
- package/dist/ast/visitor.js.map +1 -0
- package/dist/checker/check.d.ts +53 -0
- package/dist/checker/check.d.ts.map +1 -0
- package/dist/checker/check.js +105 -0
- package/dist/checker/check.js.map +1 -0
- package/dist/checker/rules/actualize-missing-updated.js +34 -0
- package/dist/checker/rules/actualize-missing-updated.js.map +1 -0
- package/dist/checker/rules/actualize-unresolved-target.js +42 -0
- package/dist/checker/rules/actualize-unresolved-target.js.map +1 -0
- package/dist/checker/rules/alter-before-define.js +53 -0
- package/dist/checker/rules/alter-before-define.js.map +1 -0
- package/dist/checker/rules/alter-undefined-entity.js +32 -0
- package/dist/checker/rules/alter-undefined-entity.js.map +1 -0
- package/dist/checker/rules/create-requires-section.js +34 -0
- package/dist/checker/rules/create-requires-section.js.map +1 -0
- package/dist/checker/rules/define-entity-requires-section.js +31 -0
- package/dist/checker/rules/define-entity-requires-section.js.map +1 -0
- package/dist/checker/rules/duplicate-entity-definition.js +37 -0
- package/dist/checker/rules/duplicate-entity-definition.js.map +1 -0
- package/dist/checker/rules/duplicate-field-in-schema.js +38 -0
- package/dist/checker/rules/duplicate-field-in-schema.js.map +1 -0
- package/dist/checker/rules/duplicate-link-id.js +52 -0
- package/dist/checker/rules/duplicate-link-id.js.map +1 -0
- package/dist/checker/rules/duplicate-metadata-key.js +21 -0
- package/dist/checker/rules/duplicate-metadata-key.js.map +1 -0
- package/dist/checker/rules/duplicate-section-heading.js +41 -0
- package/dist/checker/rules/duplicate-section-heading.js.map +1 -0
- package/dist/checker/rules/duplicate-section-in-schema.js +38 -0
- package/dist/checker/rules/duplicate-section-in-schema.js.map +1 -0
- package/dist/checker/rules/duplicate-timestamp.js +104 -0
- package/dist/checker/rules/duplicate-timestamp.js.map +1 -0
- package/dist/checker/rules/empty-required-value.js +45 -0
- package/dist/checker/rules/empty-required-value.js.map +1 -0
- package/dist/checker/rules/empty-section.js +21 -0
- package/dist/checker/rules/empty-section.js.map +1 -0
- package/dist/checker/rules/invalid-date-range-value.js +56 -0
- package/dist/checker/rules/invalid-date-range-value.js.map +1 -0
- package/dist/checker/rules/invalid-default-value.js +86 -0
- package/dist/checker/rules/invalid-default-value.js.map +1 -0
- package/dist/checker/rules/invalid-field-type.js +45 -0
- package/dist/checker/rules/invalid-field-type.js.map +1 -0
- package/dist/checker/rules/missing-required-field.js +48 -0
- package/dist/checker/rules/missing-required-field.js.map +1 -0
- package/dist/checker/rules/missing-required-section.js +51 -0
- package/dist/checker/rules/missing-required-section.js.map +1 -0
- package/dist/checker/rules/missing-title.js +56 -0
- package/dist/checker/rules/missing-title.js.map +1 -0
- package/dist/checker/rules/remove-undefined-field.js +42 -0
- package/dist/checker/rules/remove-undefined-field.js.map +1 -0
- package/dist/checker/rules/remove-undefined-section.js +42 -0
- package/dist/checker/rules/remove-undefined-section.js.map +1 -0
- package/dist/checker/rules/rules.d.ts +71 -0
- package/dist/checker/rules/rules.d.ts.map +1 -0
- package/dist/checker/rules/rules.js +102 -0
- package/dist/checker/rules/rules.js.map +1 -0
- package/dist/checker/rules/synthesis-empty-query.js +35 -0
- package/dist/checker/rules/synthesis-empty-query.js.map +1 -0
- package/dist/checker/rules/synthesis-missing-prompt.js +42 -0
- package/dist/checker/rules/synthesis-missing-prompt.js.map +1 -0
- package/dist/checker/rules/synthesis-missing-sources.js +32 -0
- package/dist/checker/rules/synthesis-missing-sources.js.map +1 -0
- package/dist/checker/rules/synthesis-unknown-query-entity.js +39 -0
- package/dist/checker/rules/synthesis-unknown-query-entity.js.map +1 -0
- package/dist/checker/rules/timestamp-out-of-order.js +55 -0
- package/dist/checker/rules/timestamp-out-of-order.js.map +1 -0
- package/dist/checker/rules/unknown-entity.js +32 -0
- package/dist/checker/rules/unknown-entity.js.map +1 -0
- package/dist/checker/rules/unknown-field.js +40 -0
- package/dist/checker/rules/unknown-field.js.map +1 -0
- package/dist/checker/rules/unknown-section.js +47 -0
- package/dist/checker/rules/unknown-section.js.map +1 -0
- package/dist/checker/rules/unresolved-link.js +34 -0
- package/dist/checker/rules/unresolved-link.js.map +1 -0
- package/dist/checker/rules/update-without-create.js +65 -0
- package/dist/checker/rules/update-without-create.js.map +1 -0
- package/dist/checker/visitor.d.ts +69 -0
- package/dist/checker/visitor.d.ts.map +1 -0
- package/dist/checker/visitor.js +67 -0
- package/dist/checker/visitor.js.map +1 -0
- package/dist/checker/workspace-index.d.ts +50 -0
- package/dist/checker/workspace-index.d.ts.map +1 -0
- package/dist/checker/workspace-index.js +108 -0
- package/dist/checker/workspace-index.js.map +1 -0
- package/dist/commands/actualize.d.ts +113 -0
- package/dist/commands/actualize.d.ts.map +1 -0
- package/dist/commands/actualize.js +111 -0
- package/dist/commands/actualize.js.map +1 -0
- package/dist/commands/check.d.ts +65 -0
- package/dist/commands/check.d.ts.map +1 -0
- package/dist/commands/check.js +61 -0
- package/dist/commands/check.js.map +1 -0
- package/dist/commands/format.d.ts +90 -0
- package/dist/commands/format.d.ts.map +1 -0
- package/dist/commands/format.js +80 -0
- package/dist/commands/format.js.map +1 -0
- package/dist/commands/query.d.ts +152 -0
- package/dist/commands/query.d.ts.map +1 -0
- package/dist/commands/query.js +151 -0
- package/dist/commands/query.js.map +1 -0
- package/dist/constants.d.ts +31 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +51 -0
- package/dist/constants.js.map +1 -0
- package/dist/files.d.ts +58 -0
- package/dist/files.d.ts.map +1 -0
- package/dist/files.js +103 -0
- package/dist/files.js.map +1 -0
- package/dist/formatters.d.ts +39 -0
- package/dist/formatters.d.ts.map +1 -0
- package/dist/formatters.js +200 -0
- package/dist/formatters.js.map +1 -0
- package/dist/fragment.d.ts +22 -0
- package/dist/fragment.d.ts.map +1 -0
- package/dist/git/git.js +240 -0
- package/dist/git/git.js.map +1 -0
- package/dist/merge/conflict-detector.d.ts +89 -0
- package/dist/merge/conflict-detector.d.ts.map +1 -0
- package/dist/merge/conflict-detector.js +352 -0
- package/dist/merge/conflict-detector.js.map +1 -0
- package/dist/merge/conflict-formatter.js +143 -0
- package/dist/merge/conflict-formatter.js.map +1 -0
- package/dist/merge/driver.d.ts +54 -0
- package/dist/merge/driver.d.ts.map +1 -0
- package/dist/merge/driver.js +112 -0
- package/dist/merge/driver.js.map +1 -0
- package/dist/merge/entry-matcher.d.ts +50 -0
- package/dist/merge/entry-matcher.d.ts.map +1 -0
- package/dist/merge/entry-matcher.js +141 -0
- package/dist/merge/entry-matcher.js.map +1 -0
- package/dist/merge/entry-merger.js +194 -0
- package/dist/merge/entry-merger.js.map +1 -0
- package/dist/merge/merge-result-builder.d.ts +62 -0
- package/dist/merge/merge-result-builder.d.ts.map +1 -0
- package/dist/merge/merge-result-builder.js +89 -0
- package/dist/merge/merge-result-builder.js.map +1 -0
- package/dist/mod.d.ts +31 -0
- package/dist/mod.js +23 -0
- package/dist/model/document.d.ts +134 -0
- package/dist/model/document.d.ts.map +1 -0
- package/dist/model/document.js +275 -0
- package/dist/model/document.js.map +1 -0
- package/dist/model/line-index.d.ts +85 -0
- package/dist/model/line-index.d.ts.map +1 -0
- package/dist/model/line-index.js +159 -0
- package/dist/model/line-index.js.map +1 -0
- package/dist/model/workspace.d.ts +296 -0
- package/dist/model/workspace.d.ts.map +1 -0
- package/dist/model/workspace.js +562 -0
- package/dist/model/workspace.js.map +1 -0
- package/dist/parser.js +27 -0
- package/dist/parser.js.map +1 -0
- package/dist/parser.native.d.ts +51 -0
- package/dist/parser.native.d.ts.map +1 -0
- package/dist/parser.native.js +62 -0
- package/dist/parser.native.js.map +1 -0
- package/dist/parser.shared.d.ts +99 -0
- package/dist/parser.shared.d.ts.map +1 -0
- package/dist/parser.shared.js +124 -0
- package/dist/parser.shared.js.map +1 -0
- package/dist/parser.web.d.ts +67 -0
- package/dist/parser.web.d.ts.map +1 -0
- package/dist/parser.web.js +49 -0
- package/dist/parser.web.js.map +1 -0
- package/dist/schema/registry.d.ts +108 -0
- package/dist/schema/registry.d.ts.map +1 -0
- package/dist/schema/registry.js +281 -0
- package/dist/schema/registry.js.map +1 -0
- package/dist/semantic/analyzer.d.ts +107 -0
- package/dist/semantic/analyzer.d.ts.map +1 -0
- package/dist/semantic/analyzer.js +261 -0
- package/dist/semantic/analyzer.js.map +1 -0
- package/dist/services/change-tracker/change-tracker.d.ts +111 -0
- package/dist/services/change-tracker/change-tracker.d.ts.map +1 -0
- package/dist/services/change-tracker/change-tracker.js +62 -0
- package/dist/services/change-tracker/change-tracker.js.map +1 -0
- package/dist/services/change-tracker/create-tracker.d.ts +42 -0
- package/dist/services/change-tracker/create-tracker.d.ts.map +1 -0
- package/dist/services/change-tracker/create-tracker.js +53 -0
- package/dist/services/change-tracker/create-tracker.js.map +1 -0
- package/dist/services/change-tracker/git-tracker.d.ts +59 -0
- package/dist/services/change-tracker/git-tracker.d.ts.map +1 -0
- package/dist/services/change-tracker/git-tracker.js +218 -0
- package/dist/services/change-tracker/git-tracker.js.map +1 -0
- package/dist/services/change-tracker/timestamp-tracker.d.ts +22 -0
- package/dist/services/change-tracker/timestamp-tracker.d.ts.map +1 -0
- package/dist/services/change-tracker/timestamp-tracker.js +74 -0
- package/dist/services/change-tracker/timestamp-tracker.js.map +1 -0
- package/dist/services/definition.d.ts +37 -0
- package/dist/services/definition.d.ts.map +1 -0
- package/dist/services/definition.js +43 -0
- package/dist/services/definition.js.map +1 -0
- package/dist/services/entity-navigation.d.ts +200 -0
- package/dist/services/entity-navigation.d.ts.map +1 -0
- package/dist/services/entity-navigation.js +211 -0
- package/dist/services/entity-navigation.js.map +1 -0
- package/dist/services/hover.d.ts +81 -0
- package/dist/services/hover.d.ts.map +1 -0
- package/dist/services/hover.js +669 -0
- package/dist/services/hover.js.map +1 -0
- package/dist/services/query.d.ts +116 -0
- package/dist/services/query.d.ts.map +1 -0
- package/dist/services/query.js +225 -0
- package/dist/services/query.js.map +1 -0
- package/dist/services/references.d.ts +52 -0
- package/dist/services/references.d.ts.map +1 -0
- package/dist/services/references.js +66 -0
- package/dist/services/references.js.map +1 -0
- package/dist/services/semantic-tokens.d.ts +54 -0
- package/dist/services/semantic-tokens.d.ts.map +1 -0
- package/dist/services/semantic-tokens.js +213 -0
- package/dist/services/semantic-tokens.js.map +1 -0
- package/dist/services/synthesis.d.ts +90 -0
- package/dist/services/synthesis.d.ts.map +1 -0
- package/dist/services/synthesis.js +113 -0
- package/dist/services/synthesis.js.map +1 -0
- package/dist/source-map.d.ts +42 -0
- package/dist/source-map.d.ts.map +1 -0
- package/dist/source-map.js +170 -0
- package/dist/source-map.js.map +1 -0
- package/package.json +128 -0
- package/tree-sitter-thalo.wasm +0 -0
- package/web-tree-sitter.wasm +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 - present ReJot Nederland B.V., and individual contributors.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
# @rejot-dev/thalo
|
|
2
|
+
|
|
3
|
+
Core library for **thalo** (Thought And Lore Language). Provides parsing, semantic analysis,
|
|
4
|
+
validation, and workspace management for `.thalo` files.
|
|
5
|
+
|
|
6
|
+
## Features
|
|
7
|
+
|
|
8
|
+
- **Parser**: Parse `.thalo` files and extract thalo blocks from Markdown
|
|
9
|
+
- **Incremental Parsing**: Efficient incremental updates via Tree-sitter's edit API
|
|
10
|
+
- **Fragment Parsing**: Parse individual expressions (queries, values, type expressions)
|
|
11
|
+
- **AST**: Strongly-typed abstract syntax tree with inline syntax error nodes
|
|
12
|
+
- **Semantic Analysis**: Build semantic models with link indexes and schema resolution
|
|
13
|
+
- **Schema**: Entity schema registry with `define-entity` / `alter-entity` support
|
|
14
|
+
- **Checker**: Visitor-based validation rules with incremental checking support
|
|
15
|
+
- **Services**: Definition lookup, references, hover, query execution, and semantic tokens
|
|
16
|
+
- **Source Mapping**: Position translation for embedded blocks in Markdown files
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pnpm add @rejot-dev/thalo
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### Working with a Workspace
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { Workspace } from "@rejot-dev/thalo";
|
|
30
|
+
|
|
31
|
+
const workspace = new Workspace();
|
|
32
|
+
|
|
33
|
+
// Add documents (schemas first, then instances)
|
|
34
|
+
workspace.addDocument(schemaSource, { filename: "entities.thalo" });
|
|
35
|
+
workspace.addDocument(instanceSource, { filename: "entries.thalo" });
|
|
36
|
+
|
|
37
|
+
// Access semantic models
|
|
38
|
+
const model = workspace.getModel("entries.thalo");
|
|
39
|
+
console.log(model.ast.entries); // Array of parsed entries
|
|
40
|
+
|
|
41
|
+
// Query links across workspace
|
|
42
|
+
const linkDef = workspace.getLinkDefinition("my-link-id");
|
|
43
|
+
const refs = workspace.getLinkReferences("my-link-id");
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Incremental Document Updates
|
|
47
|
+
|
|
48
|
+
The workspace supports efficient incremental updates:
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { Workspace } from "@rejot-dev/thalo";
|
|
52
|
+
|
|
53
|
+
const workspace = new Workspace();
|
|
54
|
+
workspace.addDocument(source, { filename: "test.thalo" });
|
|
55
|
+
|
|
56
|
+
// Apply an incremental edit (more efficient than re-adding)
|
|
57
|
+
const invalidation = workspace.applyEdit(
|
|
58
|
+
"test.thalo",
|
|
59
|
+
startLine,
|
|
60
|
+
startColumn,
|
|
61
|
+
endLine,
|
|
62
|
+
endColumn,
|
|
63
|
+
newText,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Check what was invalidated
|
|
67
|
+
if (invalidation.schemasChanged) {
|
|
68
|
+
// Entity schemas changed - may affect other files
|
|
69
|
+
}
|
|
70
|
+
if (invalidation.linksChanged) {
|
|
71
|
+
// Link definitions/references changed
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Get files affected by changes in a specific file
|
|
75
|
+
const affected = workspace.getAffectedFiles("test.thalo");
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Validating with the Checker
|
|
79
|
+
|
|
80
|
+
The checker reports both syntax errors (from AST) and semantic errors (from rules):
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
import { Workspace, check } from "@rejot-dev/thalo";
|
|
84
|
+
|
|
85
|
+
const workspace = new Workspace();
|
|
86
|
+
workspace.addDocument(source, { filename: "test.thalo" });
|
|
87
|
+
|
|
88
|
+
const diagnostics = check(workspace);
|
|
89
|
+
for (const d of diagnostics) {
|
|
90
|
+
// Syntax errors have codes like "syntax-missing_timezone"
|
|
91
|
+
// Semantic errors have codes like "unknown-entity", "unresolved-link"
|
|
92
|
+
console.log(`${d.severity}: ${d.message} (${d.code})`);
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Configuring Rule Severity
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { check } from "@rejot-dev/thalo";
|
|
100
|
+
|
|
101
|
+
const diagnostics = check(workspace, {
|
|
102
|
+
rules: {
|
|
103
|
+
"unknown-field": "off", // Disable this rule
|
|
104
|
+
"unresolved-link": "error", // Upgrade to error
|
|
105
|
+
},
|
|
106
|
+
});
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### Parsing Fragments
|
|
110
|
+
|
|
111
|
+
Parse individual expressions without a full document context:
|
|
112
|
+
|
|
113
|
+
```typescript
|
|
114
|
+
import { parseFragment, parseQuery } from "@rejot-dev/thalo";
|
|
115
|
+
import { createParser } from "@rejot-dev/thalo/native";
|
|
116
|
+
|
|
117
|
+
// Create a parser (native for Node.js, or web parser for browser)
|
|
118
|
+
const parser = createParser();
|
|
119
|
+
|
|
120
|
+
// Parse a query expression
|
|
121
|
+
const queryResult = parseQuery(parser, "lore where subject = ^self and #career");
|
|
122
|
+
if (queryResult.valid) {
|
|
123
|
+
console.log(queryResult.node.type); // "query"
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Parse a type expression
|
|
127
|
+
const typeResult = parseFragment(parser, "type_expression", 'string | "literal"');
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Executing Queries
|
|
131
|
+
|
|
132
|
+
Query entries in a workspace based on synthesis source definitions:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { Workspace, executeQuery } from "@rejot-dev/thalo";
|
|
136
|
+
|
|
137
|
+
const workspace = new Workspace();
|
|
138
|
+
// ... add documents ...
|
|
139
|
+
|
|
140
|
+
const results = executeQuery(workspace, {
|
|
141
|
+
entity: "lore",
|
|
142
|
+
conditions: [
|
|
143
|
+
{ kind: "field", field: "subject", value: "^self" },
|
|
144
|
+
{ kind: "tag", tag: "career" },
|
|
145
|
+
],
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Source Mapping for Embedded Blocks
|
|
150
|
+
|
|
151
|
+
Handle positions in markdown files with embedded thalo blocks:
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
import { parseDocument, findBlockAtPosition, toFileLocation } from "@rejot-dev/thalo";
|
|
155
|
+
|
|
156
|
+
const parsed = parseDocument(markdownSource, { filename: "notes.md" });
|
|
157
|
+
|
|
158
|
+
// Find which block contains a file position
|
|
159
|
+
const match = findBlockAtPosition(parsed.blocks, { line: 10, column: 5 });
|
|
160
|
+
if (match) {
|
|
161
|
+
// Convert block-relative location to file-absolute
|
|
162
|
+
const fileLocation = toFileLocation(match.block.sourceMap, entryLocation);
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Module Exports
|
|
167
|
+
|
|
168
|
+
| Export | Description |
|
|
169
|
+
| --------------------------- | ---------------------------------------------------------- |
|
|
170
|
+
| `@rejot-dev/thalo` | Main entry (parser, workspace, checker, services, types) |
|
|
171
|
+
| `@rejot-dev/thalo/native` | Native parser factory (`createParser`) |
|
|
172
|
+
| `@rejot-dev/thalo/web` | WASM parser factory (`createParser`) for browsers |
|
|
173
|
+
| `@rejot-dev/thalo/ast` | AST types, builder, visitor, extraction |
|
|
174
|
+
| `@rejot-dev/thalo/model` | Workspace, Document, LineIndex, model types |
|
|
175
|
+
| `@rejot-dev/thalo/semantic` | SemanticModel, analyzer, link index types |
|
|
176
|
+
| `@rejot-dev/thalo/schema` | SchemaRegistry, EntitySchema types |
|
|
177
|
+
| `@rejot-dev/thalo/checker` | Validation rules, visitor pattern, check functions |
|
|
178
|
+
| `@rejot-dev/thalo/services` | Definition, references, hover, query execution, entity nav |
|
|
179
|
+
|
|
180
|
+
## Parser API
|
|
181
|
+
|
|
182
|
+
Both `/native` and `/web` exports provide a `createParser()` function that returns a `ThaloParser`
|
|
183
|
+
instance with a consistent API:
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// Native (Node.js) - synchronous
|
|
187
|
+
import { createParser } from "@rejot-dev/thalo/native";
|
|
188
|
+
const parser = createParser();
|
|
189
|
+
|
|
190
|
+
// Web (WASM) - asynchronous, requires WASM binaries
|
|
191
|
+
import { createParser } from "@rejot-dev/thalo/web";
|
|
192
|
+
|
|
193
|
+
const [treeSitterWasm, languageWasm] = await Promise.all([
|
|
194
|
+
fetch("/wasm/web-tree-sitter.wasm").then((r) => r.arrayBuffer()),
|
|
195
|
+
fetch("/wasm/tree-sitter-thalo.wasm").then((r) => r.arrayBuffer()),
|
|
196
|
+
]);
|
|
197
|
+
|
|
198
|
+
const parser = await createParser({
|
|
199
|
+
treeSitterWasm: new Uint8Array(treeSitterWasm),
|
|
200
|
+
languageWasm: new Uint8Array(languageWasm),
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Both return the same ThaloParser interface
|
|
204
|
+
const tree = parser.parse(source);
|
|
205
|
+
const doc = parser.parseDocument(source, { fileType: "thalo" });
|
|
206
|
+
const tree2 = parser.parseIncremental(newSource, tree);
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
### Vite Usage
|
|
210
|
+
|
|
211
|
+
For Vite projects, you can import the WASM files directly using Vite's `?url` suffix:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
import { createParser } from "@rejot-dev/thalo/web";
|
|
215
|
+
import treeSitterWasmUrl from "web-tree-sitter/web-tree-sitter.wasm?url";
|
|
216
|
+
import languageWasmUrl from "@rejot-dev/tree-sitter-thalo/tree-sitter-thalo.wasm?url";
|
|
217
|
+
|
|
218
|
+
const [treeSitterWasm, languageWasm] = await Promise.all([
|
|
219
|
+
fetch(treeSitterWasmUrl).then((r) => r.arrayBuffer()),
|
|
220
|
+
fetch(languageWasmUrl).then((r) => r.arrayBuffer()),
|
|
221
|
+
]);
|
|
222
|
+
|
|
223
|
+
const parser = await createParser({
|
|
224
|
+
treeSitterWasm: new Uint8Array(treeSitterWasm),
|
|
225
|
+
languageWasm: new Uint8Array(languageWasm),
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Both WASM files are re-exported from this package for convenience:
|
|
230
|
+
|
|
231
|
+
- `@rejot-dev/thalo/web-tree-sitter.wasm` — The web-tree-sitter runtime
|
|
232
|
+
- `@rejot-dev/thalo/tree-sitter-thalo.wasm` — The thalo language grammar
|
|
233
|
+
|
|
234
|
+
### Cloudflare Workers / WebAssembly.Module Support
|
|
235
|
+
|
|
236
|
+
In environments like Cloudflare Workers, WASM imports are provided as pre-compiled
|
|
237
|
+
`WebAssembly.Module` instances rather than raw bytes. The web parser supports this directly:
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import { createParser } from "@rejot-dev/thalo/web";
|
|
241
|
+
import treeSitterWasm from "./tree-sitter.wasm"; // WebAssembly.Module
|
|
242
|
+
import languageWasm from "./tree-sitter-thalo.wasm"; // WebAssembly.Module
|
|
243
|
+
|
|
244
|
+
const parser = await createParser({ treeSitterWasm, languageWasm });
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
The main entry (`@rejot-dev/thalo`) provides convenience functions that use a singleton native
|
|
248
|
+
parser internally for backwards compatibility.
|
|
249
|
+
|
|
250
|
+
## Validation Rules
|
|
251
|
+
|
|
252
|
+
The checker validates documents using rules organized into categories:
|
|
253
|
+
|
|
254
|
+
- **Instance**: Validates instance entries — entity types, required fields, field types, sections
|
|
255
|
+
- **Link**: Validates link references and definitions — unresolved links, duplicate IDs
|
|
256
|
+
- **Schema**: Validates entity schema definitions — duplicates, alter/define ordering, defaults
|
|
257
|
+
- **Metadata**: Validates metadata values — empty values, date range formats
|
|
258
|
+
- **Content**: Validates entry content structure — duplicate section headings
|
|
259
|
+
|
|
260
|
+
Rules use a visitor pattern for efficient single-pass execution. Each rule declares a scope (entry,
|
|
261
|
+
document, or workspace) to enable incremental checking when documents change.
|
|
262
|
+
|
|
263
|
+
To see all available rules, use the CLI:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
thalo rules list
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
## Architecture
|
|
270
|
+
|
|
271
|
+
The library follows a clear pipeline from source to services:
|
|
272
|
+
|
|
273
|
+
```
|
|
274
|
+
Source → Tree-sitter → CST → AST Builder → AST → Semantic Analyzer → SemanticModel
|
|
275
|
+
↓ ↓
|
|
276
|
+
Formatter Checker
|
|
277
|
+
(CST only) (syntax + semantic)
|
|
278
|
+
↓
|
|
279
|
+
Services
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Incremental Processing
|
|
283
|
+
|
|
284
|
+
The workspace supports incremental updates at multiple levels:
|
|
285
|
+
|
|
286
|
+
1. **Document Level**: The `Document` class wraps source text with a `LineIndex` for efficient
|
|
287
|
+
position lookups and tracks Tree-sitter trees per block
|
|
288
|
+
2. **Semantic Level**: `updateSemanticModel` incrementally updates link indexes and schema entries
|
|
289
|
+
3. **Workspace Level**: Dependency tracking (`linkDependencies`, `entityDependencies`) enables
|
|
290
|
+
targeted invalidation
|
|
291
|
+
4. **Rule Level**: Rules declare their scope and dependencies, enabling incremental checking
|
|
292
|
+
|
|
293
|
+
### Error Separation
|
|
294
|
+
|
|
295
|
+
| Error Type | Created In | Examples |
|
|
296
|
+
| ------------ | ------------- | --------------------------------------------- |
|
|
297
|
+
| **Syntax** | AST Builder | Missing timezone, missing entity name |
|
|
298
|
+
| **Semantic** | Checker Rules | Unresolved link, unknown field, type mismatch |
|
|
299
|
+
|
|
300
|
+
Syntax errors are inline `SyntaxErrorNode` instances in the AST. Semantic errors are diagnostics
|
|
301
|
+
from checker rules.
|
|
302
|
+
|
|
303
|
+
### Directory Structure
|
|
304
|
+
|
|
305
|
+
```
|
|
306
|
+
src/
|
|
307
|
+
├── parser.ts # Default parser using native singleton (backwards compat)
|
|
308
|
+
├── parser.native.ts # Native parser factory (createParser)
|
|
309
|
+
├── parser.web.ts # WASM parser factory (createParser)
|
|
310
|
+
├── parser.shared.ts # Shared parser logic (ThaloParser interface, createThaloParser)
|
|
311
|
+
├── fragment.ts # Parse individual expressions (query, value, type)
|
|
312
|
+
├── source-map.ts # Position mapping for embedded blocks
|
|
313
|
+
├── constants.ts # Language constants (directives, types)
|
|
314
|
+
├── ast/
|
|
315
|
+
│ ├── types.ts # AST node types (Entry, Timestamp, SyntaxErrorNode, etc.)
|
|
316
|
+
│ ├── extract.ts # Extract AST from tree-sitter CST
|
|
317
|
+
│ ├── builder.ts # Build decomposed types (timestamps with parts)
|
|
318
|
+
│ ├── visitor.ts # AST traversal utilities (walkAst, collectSyntaxErrors)
|
|
319
|
+
│ └── node-at-position.ts # Find semantic context at cursor position
|
|
320
|
+
├── semantic/
|
|
321
|
+
│ ├── types.ts # SemanticModel, LinkIndex, LinkDefinition, LinkReference
|
|
322
|
+
│ └── analyzer.ts # Build SemanticModel from AST (indexes links, collects schemas)
|
|
323
|
+
├── model/
|
|
324
|
+
│ ├── types.ts # Model types (ModelSchemaEntry for SchemaRegistry)
|
|
325
|
+
│ ├── document.ts # Document class with LineIndex and incremental edit support
|
|
326
|
+
│ ├── line-index.ts # Fast offset↔position conversion
|
|
327
|
+
│ └── workspace.ts # Multi-document workspace with dependency tracking
|
|
328
|
+
├── schema/
|
|
329
|
+
│ ├── types.ts # Schema types (EntitySchema, FieldSchema, TypeExpr)
|
|
330
|
+
│ └── registry.ts # Schema registry with define/alter resolution
|
|
331
|
+
├── checker/
|
|
332
|
+
│ ├── types.ts # Rule, diagnostic, and dependency types
|
|
333
|
+
│ ├── check.ts # Main check functions (check, checkDocument, checkIncremental)
|
|
334
|
+
│ ├── visitor.ts # RuleVisitor interface and visitor execution
|
|
335
|
+
│ ├── workspace-index.ts # Pre-computed indices for efficient rule execution
|
|
336
|
+
│ └── rules/ # Individual validation rules (27 rules)
|
|
337
|
+
└── services/
|
|
338
|
+
├── definition.ts # Go-to-definition lookup
|
|
339
|
+
├── references.ts # Find-all-references
|
|
340
|
+
├── hover.ts # Hover information (entries, types, directives)
|
|
341
|
+
├── query.ts # Query execution engine
|
|
342
|
+
├── entity-navigation.ts # Entity/tag/field/section navigation
|
|
343
|
+
└── semantic-tokens.ts # LSP semantic tokens
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## Development
|
|
347
|
+
|
|
348
|
+
```bash
|
|
349
|
+
# Build
|
|
350
|
+
pnpm build
|
|
351
|
+
|
|
352
|
+
# Type check
|
|
353
|
+
pnpm types:check
|
|
354
|
+
|
|
355
|
+
# Run tests
|
|
356
|
+
pnpm test
|
|
357
|
+
|
|
358
|
+
# Watch mode
|
|
359
|
+
pnpm test:watch
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
## Future Work
|
|
363
|
+
|
|
364
|
+
### Grammar-Based Type System
|
|
365
|
+
|
|
366
|
+
The Tree-Sitter grammar parses all values into typed AST nodes:
|
|
367
|
+
|
|
368
|
+
- ✅ Type validation using AST structure (no regex parsing)
|
|
369
|
+
- ✅ Query parsing at the grammar level
|
|
370
|
+
- ✅ Proper array element validation
|
|
371
|
+
- ✅ Typed default values (`quoted_value`, `link`, `datetime_value`)
|
|
372
|
+
- ✅ Query execution engine for synthesis entries
|
|
373
|
+
- ✅ Decomposed timestamps with date/time/timezone parts
|
|
374
|
+
- ✅ Inline syntax error nodes for recoverable parse errors
|
|
375
|
+
- ✅ Incremental parsing and semantic analysis
|
|
376
|
+
- ✅ Visitor-based rule system with dependency declarations
|
|
377
|
+
|
|
378
|
+
**Remaining work:**
|
|
379
|
+
|
|
380
|
+
1. **Entity-only queries**: The grammar requires `where` for query expressions. Supporting
|
|
381
|
+
entity-only queries (e.g., `sources: lore`) would require grammar changes to avoid ambiguity.
|
|
382
|
+
|
|
383
|
+
### Semantic Analysis
|
|
384
|
+
|
|
385
|
+
1. **Cross-file link resolution**: Currently, link references are validated within the workspace.
|
|
386
|
+
Future work could include import/export semantics for external references.
|
|
387
|
+
|
|
388
|
+
### Validation Rules
|
|
389
|
+
|
|
390
|
+
1. **Cyclic reference detection**: Links can form cycles; detection would prevent infinite loops.
|
|
391
|
+
|
|
392
|
+
2. **Schema evolution validation**: `alter-entity` changes could be validated for backwards
|
|
393
|
+
compatibility.
|
|
394
|
+
|
|
395
|
+
3. **Content structure validation**: Validate that content sections match the schema's section
|
|
396
|
+
definitions.
|