packages/lexicon-intellisense/.DS_Store
packages/lexicon-intellisense/.DS_Store
This is a binary file and will not be displayed.
+16
packages/lexicon-intellisense/.vscode/launch.json
+16
packages/lexicon-intellisense/.vscode/launch.json
···
1
+
{
2
+
"version": "0.2.0",
3
+
"configurations": [
4
+
{
5
+
"name": "Run Extension",
6
+
"type": "extensionHost",
7
+
"request": "launch",
8
+
"args": [
9
+
"--extensionDevelopmentPath=${workspaceFolder}"
10
+
],
11
+
"outFiles": [
12
+
"${workspaceFolder}/out/**/*.js"
13
+
],
14
+
}
15
+
]
16
+
}
+30
packages/lexicon-intellisense/.vscode/tasks.json
+30
packages/lexicon-intellisense/.vscode/tasks.json
···
1
+
{
2
+
"version": "2.0.0",
3
+
"tasks": [
4
+
{
5
+
"type": "npm",
6
+
"script": "compile",
7
+
"group": "build",
8
+
"presentation": {
9
+
"panel": "dedicated",
10
+
"reveal": "never"
11
+
},
12
+
"problemMatcher": [
13
+
"$tsc"
14
+
]
15
+
},
16
+
{
17
+
"type": "npm",
18
+
"script": "watch",
19
+
"group": "build",
20
+
"presentation": {
21
+
"panel": "dedicated",
22
+
"reveal": "never"
23
+
},
24
+
"problemMatcher": [
25
+
"$tsc-watch"
26
+
],
27
+
"isBackground": true
28
+
}
29
+
]
30
+
}
+16
packages/lexicon-intellisense/.vscodeignore
+16
packages/lexicon-intellisense/.vscodeignore
···
1
+
.vscode/**
2
+
.vscode-test/**
3
+
src/**
4
+
.gitignore
5
+
**/tsconfig.json
6
+
**/*.map
7
+
**/*.ts
8
+
**/*.vsix
9
+
node_modules/**/test/**
10
+
node_modules/**/tests/**
11
+
node_modules/**/*.md
12
+
node_modules/**/*.txt
13
+
node_modules/**/LICENSE*
14
+
node_modules/**/README*
15
+
node_modules/**/.npmignore
16
+
node_modules/**/.*
+20
packages/lexicon-intellisense/LICENSE
+20
packages/lexicon-intellisense/LICENSE
···
1
+
MIT License
2
+
3
+
Copyright (c) 2025 Slices Network
4
+
5
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+
this software and associated documentation files (the "Software"), to deal in
7
+
the Software without restriction, including without limitation the rights to
8
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+
the Software, and to permit persons to whom the Software is furnished to do so,
10
+
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, FITNESS
17
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+99
packages/lexicon-intellisense/README.md
+99
packages/lexicon-intellisense/README.md
···
1
+
# Lexicon IntelliSense
2
+
3
+
VS Code extension providing IntelliSense support for AT Protocol lexicon JSON files using the `@slices/lexicon` validation library.
4
+
5
+
## Features
6
+
7
+
- **Real-time validation**: Validates lexicon files as you type using the WASM-based `@slices/lexicon` validator
8
+
- **JSON Schema support**: Provides autocomplete and validation using comprehensive JSON schemas
9
+
- **Cross-lexicon validation**: Validates references between lexicon files in your workspace
10
+
- **Error diagnostics**: Shows validation errors directly in the editor with precise location information
11
+
- **Workspace validation**: Command to validate all lexicon files in your workspace
12
+
13
+
## Installation
14
+
15
+
1. Install dependencies:
16
+
```bash
17
+
npm install
18
+
```
19
+
20
+
2. Compile the extension:
21
+
```bash
22
+
npm run compile
23
+
```
24
+
25
+
3. Install in VS Code:
26
+
- Open VS Code
27
+
- Press F5 to launch a new Extension Development Host window
28
+
- Open a workspace containing lexicon files
29
+
30
+
## Configuration
31
+
32
+
The extension can be configured in VS Code settings:
33
+
34
+
- `lexiconIntelliSense.enableValidation`: Enable/disable lexicon validation (default: true)
35
+
- `lexiconIntelliSense.lexiconDirectory`: Directory containing lexicon files relative to workspace root (default: "lexicons")
36
+
37
+
## Usage
38
+
39
+
### Automatic Validation
40
+
41
+
The extension automatically validates:
42
+
- JSON files in directories containing "lexicons" in the path
43
+
- JSON files that contain lexicon-like structure (have `id` and `defs` fields)
44
+
45
+
### Manual Validation
46
+
47
+
Use the command palette (Ctrl+Shift+P / Cmd+Shift+P):
48
+
- `Lexicon: Validate Current File` - Validates the currently open lexicon file
49
+
- `Lexicon: Validate Workspace` - Validates all lexicon files in the workspace
50
+
51
+
### Features in Action
52
+
53
+
1. **Structure validation**: Ensures lexicon files have required `id` and `defs` fields
54
+
2. **Type checking**: Validates that definition types are correct
55
+
3. **Reference validation**: Checks that references to other lexicons can be resolved
56
+
4. **Format validation**: Validates string formats like datetime, uri, nsid, etc.
57
+
58
+
## File Structure
59
+
60
+
```
61
+
packages/lexicon-intellisense/
62
+
├── package.json # Extension manifest
63
+
├── src/
64
+
│ ├── extension.ts # Main extension entry point
65
+
│ ├── language-server.ts # Language server implementation
66
+
│ └── lexicon-validator.ts # WASM validator wrapper
67
+
├── schemas/
68
+
│ └── lexicon-schema.json # JSON schema for lexicon files
69
+
├── wasm/ # WASM files from lexicon-rs
70
+
└── language-configuration.json
71
+
```
72
+
73
+
## Development
74
+
75
+
### Building
76
+
77
+
```bash
78
+
npm run compile
79
+
```
80
+
81
+
### Testing
82
+
83
+
1. Open the project in VS Code
84
+
2. Press F5 to launch the Extension Development Host
85
+
3. Open a workspace with lexicon files
86
+
4. Test validation and IntelliSense features
87
+
88
+
### WASM Integration
89
+
90
+
The extension uses WASM files compiled from the Rust-based `lexicon-rs` package for validation. These files are copied during build and provide the same validation logic used by the CLI tools.
91
+
92
+
## Commands
93
+
94
+
- `lexiconIntelliSense.validateFile`: Validate the current lexicon file
95
+
- `lexiconIntelliSense.validateWorkspace`: Validate all lexicon files in workspace
96
+
97
+
## License
98
+
99
+
MIT
packages/lexicon-intellisense/images/logo.png
packages/lexicon-intellisense/images/logo.png
This is a binary file and will not be displayed.
+24
packages/lexicon-intellisense/images/logo.svg
+24
packages/lexicon-intellisense/images/logo.svg
···
1
+
<svg viewBox="0 0 60 60" xmlns="http://www.w3.org/2000/svg">
2
+
<defs>
3
+
<linearGradient id="board1" x1="0%" y1="0%" x2="100%" y2="100%">
4
+
<stop offset="0%" style="stop-color:#FF6347;stop-opacity:1" />
5
+
<stop offset="100%" style="stop-color:#FF4500;stop-opacity:1" />
6
+
</linearGradient>
7
+
<linearGradient id="board2" x1="0%" y1="0%" x2="100%" y2="100%">
8
+
<stop offset="0%" style="stop-color:#00CED1;stop-opacity:1" />
9
+
<stop offset="100%" style="stop-color:#4682B4;stop-opacity:1" />
10
+
</linearGradient>
11
+
</defs>
12
+
13
+
<!-- Surfboard/skateboard deck shapes stacked -->
14
+
<g transform="translate(30, 30)">
15
+
<!-- Top board slice -->
16
+
<ellipse cx="0" cy="-8" rx="15" ry="6" fill="url(#board1)"/>
17
+
18
+
<!-- Middle board slice -->
19
+
<ellipse cx="0" cy="0" rx="18" ry="6" fill="url(#board2)"/>
20
+
21
+
<!-- Bottom board slice -->
22
+
<ellipse cx="0" cy="8" rx="12" ry="6" fill="#32CD32"/>
23
+
</g>
24
+
</svg>
+160
packages/lexicon-intellisense/package-lock.json
+160
packages/lexicon-intellisense/package-lock.json
···
1
+
{
2
+
"name": "@slices/lexicon-intellisense",
3
+
"version": "0.1.0",
4
+
"lockfileVersion": 3,
5
+
"requires": true,
6
+
"packages": {
7
+
"": {
8
+
"name": "@slices/lexicon-intellisense",
9
+
"version": "0.1.0",
10
+
"license": "MIT",
11
+
"dependencies": {
12
+
"vscode-languageclient": "^9.0.0",
13
+
"vscode-languageserver": "^9.0.0",
14
+
"vscode-languageserver-textdocument": "^1.0.11"
15
+
},
16
+
"devDependencies": {
17
+
"@types/node": "^20.0.0",
18
+
"@types/vscode": "^1.80.0",
19
+
"typescript": "^5.0.0"
20
+
},
21
+
"engines": {
22
+
"vscode": "^1.80.0"
23
+
}
24
+
},
25
+
"node_modules/@types/node": {
26
+
"version": "20.19.17",
27
+
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.17.tgz",
28
+
"integrity": "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ==",
29
+
"dev": true,
30
+
"license": "MIT",
31
+
"dependencies": {
32
+
"undici-types": "~6.21.0"
33
+
}
34
+
},
35
+
"node_modules/@types/vscode": {
36
+
"version": "1.104.0",
37
+
"resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.104.0.tgz",
38
+
"integrity": "sha512-0KwoU2rZ2ecsTGFxo4K1+f+AErRsYW0fsp6A0zufzGuhyczc2IoKqYqcwXidKXmy2u8YB2GsYsOtiI9Izx3Tig==",
39
+
"dev": true,
40
+
"license": "MIT"
41
+
},
42
+
"node_modules/balanced-match": {
43
+
"version": "1.0.2",
44
+
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
45
+
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
46
+
"license": "MIT"
47
+
},
48
+
"node_modules/brace-expansion": {
49
+
"version": "2.0.2",
50
+
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
51
+
"integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
52
+
"license": "MIT",
53
+
"dependencies": {
54
+
"balanced-match": "^1.0.0"
55
+
}
56
+
},
57
+
"node_modules/minimatch": {
58
+
"version": "5.1.6",
59
+
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz",
60
+
"integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==",
61
+
"license": "ISC",
62
+
"dependencies": {
63
+
"brace-expansion": "^2.0.1"
64
+
},
65
+
"engines": {
66
+
"node": ">=10"
67
+
}
68
+
},
69
+
"node_modules/semver": {
70
+
"version": "7.7.2",
71
+
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
72
+
"integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
73
+
"license": "ISC",
74
+
"bin": {
75
+
"semver": "bin/semver.js"
76
+
},
77
+
"engines": {
78
+
"node": ">=10"
79
+
}
80
+
},
81
+
"node_modules/typescript": {
82
+
"version": "5.9.2",
83
+
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
84
+
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
85
+
"dev": true,
86
+
"license": "Apache-2.0",
87
+
"bin": {
88
+
"tsc": "bin/tsc",
89
+
"tsserver": "bin/tsserver"
90
+
},
91
+
"engines": {
92
+
"node": ">=14.17"
93
+
}
94
+
},
95
+
"node_modules/undici-types": {
96
+
"version": "6.21.0",
97
+
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
98
+
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
99
+
"dev": true,
100
+
"license": "MIT"
101
+
},
102
+
"node_modules/vscode-jsonrpc": {
103
+
"version": "8.2.0",
104
+
"resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz",
105
+
"integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==",
106
+
"license": "MIT",
107
+
"engines": {
108
+
"node": ">=14.0.0"
109
+
}
110
+
},
111
+
"node_modules/vscode-languageclient": {
112
+
"version": "9.0.1",
113
+
"resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz",
114
+
"integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==",
115
+
"license": "MIT",
116
+
"dependencies": {
117
+
"minimatch": "^5.1.0",
118
+
"semver": "^7.3.7",
119
+
"vscode-languageserver-protocol": "3.17.5"
120
+
},
121
+
"engines": {
122
+
"vscode": "^1.82.0"
123
+
}
124
+
},
125
+
"node_modules/vscode-languageserver": {
126
+
"version": "9.0.1",
127
+
"resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz",
128
+
"integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==",
129
+
"license": "MIT",
130
+
"dependencies": {
131
+
"vscode-languageserver-protocol": "3.17.5"
132
+
},
133
+
"bin": {
134
+
"installServerIntoExtension": "bin/installServerIntoExtension"
135
+
}
136
+
},
137
+
"node_modules/vscode-languageserver-protocol": {
138
+
"version": "3.17.5",
139
+
"resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz",
140
+
"integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==",
141
+
"license": "MIT",
142
+
"dependencies": {
143
+
"vscode-jsonrpc": "8.2.0",
144
+
"vscode-languageserver-types": "3.17.5"
145
+
}
146
+
},
147
+
"node_modules/vscode-languageserver-textdocument": {
148
+
"version": "1.0.12",
149
+
"resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz",
150
+
"integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==",
151
+
"license": "MIT"
152
+
},
153
+
"node_modules/vscode-languageserver-types": {
154
+
"version": "3.17.5",
155
+
"resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz",
156
+
"integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==",
157
+
"license": "MIT"
158
+
}
159
+
}
160
+
}
+78
packages/lexicon-intellisense/package.json
+78
packages/lexicon-intellisense/package.json
···
1
+
{
2
+
"name": "lexicon-intellisense",
3
+
"version": "0.1.0",
4
+
"description": "VS Code IntelliSense support for AT Protocol lexicon JSON files",
5
+
"main": "./out/extension.js",
6
+
"license": "MIT",
7
+
"icon": "images/logo.png",
8
+
"publisher": "SlicesNetwork",
9
+
"keywords": [
10
+
"lexicon",
11
+
"atproto",
12
+
"at-protocol",
13
+
"json",
14
+
"validation"
15
+
],
16
+
"engines": {
17
+
"vscode": "^1.80.0"
18
+
},
19
+
"categories": [
20
+
"Programming Languages",
21
+
"Linters"
22
+
],
23
+
"activationEvents": [
24
+
"onLanguage:json"
25
+
],
26
+
"contributes": {
27
+
"commands": [
28
+
{
29
+
"command": "lexiconIntelliSense.validateFile",
30
+
"title": "Validate Current File",
31
+
"category": "Lexicon"
32
+
},
33
+
{
34
+
"command": "lexiconIntelliSense.validateWorkspace",
35
+
"title": "Validate Workspace",
36
+
"category": "Lexicon"
37
+
}
38
+
],
39
+
"jsonValidation": [
40
+
{
41
+
"fileMatch": "**/lexicons/**/*.json",
42
+
"url": "./schemas/lexicon-schema.json"
43
+
}
44
+
],
45
+
"configuration": {
46
+
"title": "Lexicon IntelliSense",
47
+
"properties": {
48
+
"lexiconIntelliSense.enableValidation": {
49
+
"type": "boolean",
50
+
"default": true,
51
+
"description": "Enable lexicon validation using @slices/lexicon"
52
+
},
53
+
"lexiconIntelliSense.lexiconDirectory": {
54
+
"type": "string",
55
+
"default": "lexicons",
56
+
"description": "Directory containing lexicon files (relative to workspace root)"
57
+
}
58
+
}
59
+
}
60
+
},
61
+
"scripts": {
62
+
"vscode:prepublish": "npm run build",
63
+
"compile": "tsc -p ./",
64
+
"watch": "tsc -watch -p ./",
65
+
"build": "npm run copy:wasm && npm run compile",
66
+
"copy:wasm": "mkdir -p wasm && cp ../lexicon-rs/pkg/* wasm/"
67
+
},
68
+
"devDependencies": {
69
+
"@types/vscode": "^1.80.0",
70
+
"@types/node": "^20.0.0",
71
+
"typescript": "^5.0.0"
72
+
},
73
+
"dependencies": {
74
+
"vscode-languageclient": "^9.0.0",
75
+
"vscode-languageserver": "^9.0.0",
76
+
"vscode-languageserver-textdocument": "^1.0.11"
77
+
}
78
+
}
+220
packages/lexicon-intellisense/schemas/lexicon-schema.json
+220
packages/lexicon-intellisense/schemas/lexicon-schema.json
···
1
+
{
2
+
"$schema": "http://json-schema.org/draft-07/schema#",
3
+
"title": "AT Protocol Lexicon",
4
+
"type": "object",
5
+
"required": ["id", "defs"],
6
+
"properties": {
7
+
"lexicon": {
8
+
"type": "integer",
9
+
"description": "Lexicon version",
10
+
"default": 1
11
+
},
12
+
"id": {
13
+
"type": "string",
14
+
"description": "Unique identifier for the lexicon",
15
+
"pattern": "^[a-zA-Z0-9-]+\\.[a-zA-Z0-9.-]+$"
16
+
},
17
+
"description": {
18
+
"type": "string",
19
+
"description": "Description of the lexicon"
20
+
},
21
+
"defs": {
22
+
"type": "object",
23
+
"description": "Lexicon definitions",
24
+
"patternProperties": {
25
+
"^[a-zA-Z_][a-zA-Z0-9_]*$": {
26
+
"$ref": "#/$defs/definition"
27
+
}
28
+
},
29
+
"additionalProperties": false
30
+
}
31
+
},
32
+
"additionalProperties": false,
33
+
"$defs": {
34
+
"definition": {
35
+
"type": "object",
36
+
"required": ["type"],
37
+
"properties": {
38
+
"type": {
39
+
"type": "string",
40
+
"enum": ["record", "object", "string", "integer", "boolean", "array", "ref", "union", "blob", "bytes", "cid-link", "unknown"]
41
+
},
42
+
"description": {
43
+
"type": "string"
44
+
}
45
+
},
46
+
"allOf": [
47
+
{
48
+
"if": {
49
+
"properties": { "type": { "const": "record" } }
50
+
},
51
+
"then": {
52
+
"properties": {
53
+
"key": {
54
+
"type": "string",
55
+
"enum": ["literal:self", "tid", "any"]
56
+
},
57
+
"record": {
58
+
"$ref": "#/$defs/objectDef"
59
+
}
60
+
},
61
+
"required": ["record"]
62
+
}
63
+
},
64
+
{
65
+
"if": {
66
+
"properties": { "type": { "const": "object" } }
67
+
},
68
+
"then": {
69
+
"$ref": "#/$defs/objectDef"
70
+
}
71
+
},
72
+
{
73
+
"if": {
74
+
"properties": { "type": { "const": "string" } }
75
+
},
76
+
"then": {
77
+
"properties": {
78
+
"format": {
79
+
"type": "string",
80
+
"enum": ["datetime", "uri", "at-uri", "did", "handle", "at-identifier", "nsid", "cid", "language", "tid", "record-key"]
81
+
},
82
+
"maxLength": { "type": "integer", "minimum": 0 },
83
+
"minLength": { "type": "integer", "minimum": 0 },
84
+
"maxGraphemes": { "type": "integer", "minimum": 0 },
85
+
"minGraphemes": { "type": "integer", "minimum": 0 },
86
+
"enum": {
87
+
"type": "array",
88
+
"items": { "type": "string" }
89
+
},
90
+
"const": { "type": "string" },
91
+
"default": { "type": "string" }
92
+
}
93
+
}
94
+
},
95
+
{
96
+
"if": {
97
+
"properties": { "type": { "const": "integer" } }
98
+
},
99
+
"then": {
100
+
"properties": {
101
+
"minimum": { "type": "integer" },
102
+
"maximum": { "type": "integer" },
103
+
"enum": {
104
+
"type": "array",
105
+
"items": { "type": "integer" }
106
+
},
107
+
"const": { "type": "integer" },
108
+
"default": { "type": "integer" }
109
+
}
110
+
}
111
+
},
112
+
{
113
+
"if": {
114
+
"properties": { "type": { "const": "boolean" } }
115
+
},
116
+
"then": {
117
+
"properties": {
118
+
"const": { "type": "boolean" },
119
+
"default": { "type": "boolean" }
120
+
}
121
+
}
122
+
},
123
+
{
124
+
"if": {
125
+
"properties": { "type": { "const": "array" } }
126
+
},
127
+
"then": {
128
+
"properties": {
129
+
"items": {
130
+
"$ref": "#/$defs/definition"
131
+
},
132
+
"maxLength": { "type": "integer", "minimum": 0 },
133
+
"minLength": { "type": "integer", "minimum": 0 }
134
+
},
135
+
"required": ["items"]
136
+
}
137
+
},
138
+
{
139
+
"if": {
140
+
"properties": { "type": { "const": "ref" } }
141
+
},
142
+
"then": {
143
+
"properties": {
144
+
"ref": {
145
+
"type": "string",
146
+
"description": "Reference to another definition"
147
+
}
148
+
},
149
+
"required": ["ref"]
150
+
}
151
+
},
152
+
{
153
+
"if": {
154
+
"properties": { "type": { "const": "union" } }
155
+
},
156
+
"then": {
157
+
"properties": {
158
+
"refs": {
159
+
"type": "array",
160
+
"items": { "type": "string" },
161
+
"minItems": 1
162
+
},
163
+
"closed": { "type": "boolean" }
164
+
},
165
+
"required": ["refs"]
166
+
}
167
+
},
168
+
{
169
+
"if": {
170
+
"properties": { "type": { "const": "blob" } }
171
+
},
172
+
"then": {
173
+
"properties": {
174
+
"accept": {
175
+
"type": "array",
176
+
"items": { "type": "string" }
177
+
},
178
+
"maxSize": { "type": "integer", "minimum": 0 }
179
+
}
180
+
}
181
+
},
182
+
{
183
+
"if": {
184
+
"properties": { "type": { "const": "bytes" } }
185
+
},
186
+
"then": {
187
+
"properties": {
188
+
"maxLength": { "type": "integer", "minimum": 0 },
189
+
"minLength": { "type": "integer", "minimum": 0 }
190
+
}
191
+
}
192
+
}
193
+
]
194
+
},
195
+
"objectDef": {
196
+
"type": "object",
197
+
"properties": {
198
+
"type": { "const": "object" },
199
+
"description": { "type": "string" },
200
+
"properties": {
201
+
"type": "object",
202
+
"patternProperties": {
203
+
"^[a-zA-Z_][a-zA-Z0-9_]*$": {
204
+
"$ref": "#/$defs/definition"
205
+
}
206
+
},
207
+
"additionalProperties": false
208
+
},
209
+
"required": {
210
+
"type": "array",
211
+
"items": { "type": "string" }
212
+
},
213
+
"nullable": {
214
+
"type": "array",
215
+
"items": { "type": "string" }
216
+
}
217
+
}
218
+
}
219
+
}
220
+
}
+93
packages/lexicon-intellisense/src/extension.ts
+93
packages/lexicon-intellisense/src/extension.ts
···
1
+
import * as vscode from 'vscode';
2
+
import * as path from 'path';
3
+
import { LexiconLanguageServer } from './language-server';
4
+
5
+
let languageServer: LexiconLanguageServer | undefined;
6
+
7
+
export function activate(context: vscode.ExtensionContext) {
8
+
// Create the language server
9
+
languageServer = new LexiconLanguageServer(context);
10
+
11
+
// Start the language server
12
+
languageServer.start();
13
+
14
+
// Register commands
15
+
const validateCommand = vscode.commands.registerCommand('lexiconIntelliSense.validateFile', async () => {
16
+
const activeEditor = vscode.window.activeTextEditor;
17
+
if (!activeEditor) {
18
+
vscode.window.showErrorMessage('No active editor found');
19
+
return;
20
+
}
21
+
22
+
const document = activeEditor.document;
23
+
if (!isLexiconFile(document)) {
24
+
vscode.window.showErrorMessage('Current file is not a lexicon JSON file');
25
+
return;
26
+
}
27
+
28
+
try {
29
+
await languageServer?.validateDocument(document);
30
+
vscode.window.showInformationMessage('Lexicon validation completed');
31
+
} catch (error) {
32
+
vscode.window.showErrorMessage(`Validation failed: ${error instanceof Error ? error.message : String(error)}`);
33
+
}
34
+
});
35
+
36
+
const validateWorkspaceCommand = vscode.commands.registerCommand('lexiconIntelliSense.validateWorkspace', async () => {
37
+
if (!languageServer) {
38
+
vscode.window.showErrorMessage('Language server not initialized');
39
+
return;
40
+
}
41
+
42
+
try {
43
+
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
44
+
if (!workspaceFolder) {
45
+
vscode.window.showErrorMessage('No workspace folder found');
46
+
return;
47
+
}
48
+
49
+
await languageServer.validateWorkspace(workspaceFolder.uri);
50
+
vscode.window.showInformationMessage('Workspace lexicon validation completed');
51
+
} catch (error) {
52
+
vscode.window.showErrorMessage(`Workspace validation failed: ${error instanceof Error ? error.message : String(error)}`);
53
+
}
54
+
});
55
+
56
+
context.subscriptions.push(validateCommand, validateWorkspaceCommand);
57
+
58
+
// Watch for configuration changes
59
+
const configWatcher = vscode.workspace.onDidChangeConfiguration((event) => {
60
+
if (event.affectsConfiguration('lexiconIntelliSense')) {
61
+
languageServer?.updateConfiguration();
62
+
}
63
+
});
64
+
65
+
context.subscriptions.push(configWatcher);
66
+
}
67
+
68
+
export function deactivate() {
69
+
if (languageServer) {
70
+
languageServer.stop();
71
+
languageServer = undefined;
72
+
}
73
+
}
74
+
75
+
function isLexiconFile(document: vscode.TextDocument): boolean {
76
+
// Check if file is in lexicons directory or has lexicon-like content
77
+
const filePath = document.uri.fsPath;
78
+
const isInLexiconsDir = filePath.includes('/lexicons/') || filePath.includes('\\lexicons\\');
79
+
80
+
if (isInLexiconsDir && document.languageId === 'json') {
81
+
return true;
82
+
}
83
+
84
+
// Check if JSON content looks like a lexicon
85
+
try {
86
+
const content = JSON.parse(document.getText());
87
+
return content && typeof content === 'object' &&
88
+
typeof content.id === 'string' &&
89
+
content.defs;
90
+
} catch {
91
+
return false;
92
+
}
93
+
}
+434
packages/lexicon-intellisense/src/language-server.ts
+434
packages/lexicon-intellisense/src/language-server.ts
···
1
+
import * as vscode from 'vscode';
2
+
import * as path from 'path';
3
+
import * as fs from 'fs';
4
+
import {
5
+
LanguageClient,
6
+
LanguageClientOptions,
7
+
ServerOptions,
8
+
TransportKind
9
+
} from 'vscode-languageclient/node';
10
+
import { LexiconValidator, LexiconDoc, ValidationError } from './lexicon-validator';
11
+
12
+
export class LexiconLanguageServer {
13
+
private client: LanguageClient | undefined;
14
+
private context: vscode.ExtensionContext;
15
+
private diagnosticCollection: vscode.DiagnosticCollection;
16
+
17
+
constructor(context: vscode.ExtensionContext) {
18
+
this.context = context;
19
+
this.diagnosticCollection = vscode.languages.createDiagnosticCollection('lexicon');
20
+
context.subscriptions.push(this.diagnosticCollection);
21
+
}
22
+
23
+
async start() {
24
+
// Set up document change listeners for real-time validation
25
+
const documentSelector = [
26
+
{ scheme: 'file', language: 'json', pattern: '**/lexicons/**/*.json' }
27
+
];
28
+
29
+
// Listen for document changes
30
+
const changeListener = vscode.workspace.onDidChangeTextDocument(async (event) => {
31
+
if (this.isLexiconDocument(event.document)) {
32
+
await this.validateDocument(event.document);
33
+
}
34
+
});
35
+
36
+
// Listen for document opens
37
+
const openListener = vscode.workspace.onDidOpenTextDocument(async (document) => {
38
+
if (this.isLexiconDocument(document)) {
39
+
await this.validateDocument(document);
40
+
}
41
+
});
42
+
43
+
// Listen for document saves
44
+
const saveListener = vscode.workspace.onDidSaveTextDocument(async (document) => {
45
+
if (this.isLexiconDocument(document)) {
46
+
await this.validateDocument(document);
47
+
}
48
+
});
49
+
50
+
this.context.subscriptions.push(changeListener, openListener, saveListener);
51
+
52
+
// Validate all open lexicon documents
53
+
for (const document of vscode.workspace.textDocuments) {
54
+
if (this.isLexiconDocument(document)) {
55
+
await this.validateDocument(document);
56
+
}
57
+
}
58
+
}
59
+
60
+
stop() {
61
+
if (this.client) {
62
+
this.client.stop();
63
+
this.client = undefined;
64
+
}
65
+
}
66
+
67
+
updateConfiguration() {
68
+
// Revalidate all open documents when configuration changes
69
+
for (const document of vscode.workspace.textDocuments) {
70
+
if (this.isLexiconDocument(document)) {
71
+
this.validateDocument(document);
72
+
}
73
+
}
74
+
}
75
+
76
+
async validateDocument(document: vscode.TextDocument) {
77
+
console.log('validateDocument called for:', document.uri.fsPath);
78
+
79
+
const config = vscode.workspace.getConfiguration('lexiconIntelliSense');
80
+
if (!config.get<boolean>('enableValidation', true)) {
81
+
console.log('Validation disabled in config');
82
+
return;
83
+
}
84
+
85
+
const diagnostics: vscode.Diagnostic[] = [];
86
+
console.log('Starting validation...');
87
+
88
+
try {
89
+
const content = document.getText();
90
+
const lexicon = JSON.parse(content);
91
+
92
+
// Basic structure validation
93
+
if (!lexicon || typeof lexicon !== 'object') {
94
+
diagnostics.push(this.createDiagnostic(
95
+
new vscode.Range(0, 0, 0, 0),
96
+
'Lexicon must be an object',
97
+
vscode.DiagnosticSeverity.Error
98
+
));
99
+
} else {
100
+
// Validate required fields
101
+
if (!lexicon.id || typeof lexicon.id !== 'string') {
102
+
const range = this.findFieldRange(document, 'id') || this.findLineWithText(document, '"id"') || new vscode.Range(0, 0, 0, 0);
103
+
diagnostics.push(this.createDiagnostic(
104
+
range,
105
+
'Lexicon must have a valid "id" field',
106
+
vscode.DiagnosticSeverity.Error
107
+
));
108
+
}
109
+
110
+
if (!lexicon.defs || typeof lexicon.defs !== 'object') {
111
+
const range = this.findFieldRange(document, 'defs') || this.findLineWithText(document, '"defs"') || new vscode.Range(0, 0, 0, 0);
112
+
diagnostics.push(this.createDiagnostic(
113
+
range,
114
+
'Lexicon must have a "defs" object',
115
+
vscode.DiagnosticSeverity.Error
116
+
));
117
+
}
118
+
119
+
// Use WASM validator for deep validation
120
+
if (lexicon.id && lexicon.defs) {
121
+
console.log('Running WASM validation for lexicon:', lexicon.id);
122
+
try {
123
+
const validator = await LexiconValidator.create([lexicon as LexiconDoc]);
124
+
try {
125
+
validator.validateLexiconSetCompleteness();
126
+
console.log('WASM validation passed');
127
+
} catch (validationError) {
128
+
console.log('WASM validation failed:', validationError);
129
+
const errorMessage = validationError instanceof Error ? validationError.message : String(validationError);
130
+
const range = this.parseErrorLocationFromMessage(document, errorMessage) || this.findFieldRange(document, 'defs') || new vscode.Range(0, 0, 0, 0);
131
+
diagnostics.push(this.createDiagnostic(
132
+
range,
133
+
`Lexicon validation error: ${errorMessage}`,
134
+
vscode.DiagnosticSeverity.Error
135
+
));
136
+
} finally {
137
+
validator.free();
138
+
}
139
+
} catch (error) {
140
+
console.log('WASM validator creation failed:', error);
141
+
const errorMessage = error instanceof Error ? error.message : String(error);
142
+
console.log('Parsing error location for:', errorMessage);
143
+
const range = this.parseErrorLocationFromMessage(document, errorMessage) || this.findFieldRange(document, 'id') || new vscode.Range(0, 0, 0, 0);
144
+
console.log('Error range found:', range);
145
+
diagnostics.push(this.createDiagnostic(
146
+
range,
147
+
`Lexicon validation failed: ${errorMessage}`,
148
+
vscode.DiagnosticSeverity.Error
149
+
));
150
+
}
151
+
} else {
152
+
console.log('Skipping WASM validation - missing id or defs');
153
+
}
154
+
}
155
+
} catch (parseError) {
156
+
diagnostics.push(this.createDiagnostic(
157
+
new vscode.Range(0, 0, 0, 0),
158
+
`Invalid JSON: ${parseError instanceof Error ? parseError.message : String(parseError)}`,
159
+
vscode.DiagnosticSeverity.Error
160
+
));
161
+
}
162
+
163
+
console.log('Setting diagnostics:', diagnostics.length, 'errors found');
164
+
this.diagnosticCollection.set(document.uri, diagnostics);
165
+
}
166
+
167
+
async validateWorkspace(workspaceUri: vscode.Uri) {
168
+
const config = vscode.workspace.getConfiguration('lexiconIntelliSense');
169
+
const lexiconDir = config.get<string>('lexiconDirectory', 'lexicons');
170
+
171
+
const lexiconPath = path.join(workspaceUri.fsPath, lexiconDir);
172
+
if (!fs.existsSync(lexiconPath)) {
173
+
vscode.window.showWarningMessage(`Lexicon directory not found: ${lexiconPath}`);
174
+
return;
175
+
}
176
+
177
+
const lexiconFiles = await this.findLexiconFiles(lexiconPath);
178
+
const lexicons: LexiconDoc[] = [];
179
+
180
+
// Load all lexicons
181
+
for (const filePath of lexiconFiles) {
182
+
try {
183
+
const content = fs.readFileSync(filePath, 'utf8');
184
+
const lexicon = JSON.parse(content);
185
+
if (lexicon.id && lexicon.defs) {
186
+
lexicons.push(lexicon as LexiconDoc);
187
+
}
188
+
} catch (error) {
189
+
console.warn(`Failed to load lexicon ${filePath}:`, error);
190
+
}
191
+
}
192
+
193
+
// Validate the complete set
194
+
if (lexicons.length > 0) {
195
+
try {
196
+
const validator = await LexiconValidator.create(lexicons);
197
+
try {
198
+
validator.validateLexiconSetCompleteness();
199
+
vscode.window.showInformationMessage(`Successfully validated ${lexicons.length} lexicon files`);
200
+
} catch (validationError) {
201
+
vscode.window.showErrorMessage(`Set validation failed: ${validationError instanceof Error ? validationError.message : String(validationError)}`);
202
+
} finally {
203
+
validator.free();
204
+
}
205
+
} catch (error) {
206
+
vscode.window.showErrorMessage(`WASM validation failed: ${error instanceof Error ? error.message : String(error)}`);
207
+
}
208
+
}
209
+
}
210
+
211
+
private async findLexiconFiles(directory: string): Promise<string[]> {
212
+
const files: string[] = [];
213
+
214
+
const readDir = async (dir: string) => {
215
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
216
+
for (const entry of entries) {
217
+
const fullPath = path.join(dir, entry.name);
218
+
if (entry.isDirectory()) {
219
+
await readDir(fullPath);
220
+
} else if (entry.name.endsWith('.json')) {
221
+
files.push(fullPath);
222
+
}
223
+
}
224
+
};
225
+
226
+
await readDir(directory);
227
+
return files;
228
+
}
229
+
230
+
private isLexiconDocument(document: vscode.TextDocument): boolean {
231
+
const filePath = document.uri.fsPath;
232
+
const isInLexiconsDir = filePath.includes('/lexicons/') || filePath.includes('\\lexicons\\');
233
+
234
+
if (isInLexiconsDir && document.languageId === 'json') {
235
+
return true;
236
+
}
237
+
238
+
// Check if JSON content looks like a lexicon
239
+
try {
240
+
const content = JSON.parse(document.getText());
241
+
return content && typeof content === 'object' &&
242
+
typeof content.id === 'string' &&
243
+
content.defs;
244
+
} catch {
245
+
return false;
246
+
}
247
+
}
248
+
249
+
private findFieldRange(document: vscode.TextDocument, fieldName: string): vscode.Range | null {
250
+
const text = document.getText();
251
+
const regex = new RegExp(`"${fieldName}"\\s*:`, 'g');
252
+
let match;
253
+
const matches = [];
254
+
255
+
// Find all matches
256
+
while ((match = regex.exec(text)) !== null) {
257
+
matches.push(match);
258
+
}
259
+
260
+
if (matches.length === 0) return null;
261
+
262
+
// If only one match, use it
263
+
if (matches.length === 1) {
264
+
const startPos = document.positionAt(matches[0].index);
265
+
const endPos = document.positionAt(matches[0].index + matches[0][0].length);
266
+
return new vscode.Range(startPos, endPos);
267
+
}
268
+
269
+
// If multiple matches, try to find the one in properties context
270
+
for (const match of matches) {
271
+
const beforeMatch = text.substring(0, match.index);
272
+
// Look for "properties" keyword before this match
273
+
if (beforeMatch.includes('"properties"')) {
274
+
const startPos = document.positionAt(match.index);
275
+
const endPos = document.positionAt(match.index + match[0].length);
276
+
return new vscode.Range(startPos, endPos);
277
+
}
278
+
}
279
+
280
+
// Fallback to first match
281
+
const startPos = document.positionAt(matches[0].index);
282
+
const endPos = document.positionAt(matches[0].index + matches[0][0].length);
283
+
return new vscode.Range(startPos, endPos);
284
+
}
285
+
286
+
private findLineWithText(document: vscode.TextDocument, searchText: string): vscode.Range | null {
287
+
const text = document.getText();
288
+
const lines = text.split('\n');
289
+
290
+
for (let i = 0; i < lines.length; i++) {
291
+
if (lines[i].includes(searchText)) {
292
+
const start = new vscode.Position(i, 0);
293
+
const end = new vscode.Position(i, lines[i].length);
294
+
return new vscode.Range(start, end);
295
+
}
296
+
}
297
+
298
+
return null;
299
+
}
300
+
301
+
private parseErrorLocationFromMessage(document: vscode.TextDocument, errorMessage: string): vscode.Range | null {
302
+
// Try to extract field names from error messages
303
+
// Common patterns: 'Property "main.title"', "'fieldName'", "at path 'defs.main.type'"
304
+
305
+
// Look for Property paths like "main.title" and map them to actual JSON structure
306
+
const propertyMatch = errorMessage.match(/Property '([^']+)'/);
307
+
if (propertyMatch) {
308
+
console.log('Found Property match:', propertyMatch[1]);
309
+
const propertyPath = propertyMatch[1];
310
+
311
+
// For "main.title" -> just look for "title" in the properties section
312
+
if (propertyPath.includes('.')) {
313
+
const parts = propertyPath.split('.');
314
+
const propertyName = parts[parts.length - 1]; // Get the last part (e.g., "title")
315
+
console.log('Looking for property name:', propertyName);
316
+
317
+
const range = this.findPropertyInContext(document, propertyName);
318
+
if (range) return range;
319
+
}
320
+
321
+
// Fallback to original mapping
322
+
const mappedPath = this.mapPropertyPathToJsonPath(propertyPath);
323
+
for (const fieldName of mappedPath) {
324
+
const range = this.findFieldRange(document, fieldName) || this.findLineWithText(document, `"${fieldName}"`);
325
+
if (range) return range;
326
+
}
327
+
}
328
+
329
+
// Look for string property definition errors like "String property definition 'main.releaseDate'"
330
+
const stringPropertyMatch = errorMessage.match(/String property definition '([^']+)'/);
331
+
if (stringPropertyMatch) {
332
+
console.log('Found String property definition match:', stringPropertyMatch[1]);
333
+
const propertyPath = stringPropertyMatch[1];
334
+
335
+
if (propertyPath.includes('.')) {
336
+
const parts = propertyPath.split('.');
337
+
const propertyName = parts[parts.length - 1]; // Get the last part (e.g., "releaseDate")
338
+
console.log('Looking for string property name:', propertyName);
339
+
340
+
const range = this.findPropertyInContext(document, propertyName);
341
+
if (range) return range;
342
+
}
343
+
}
344
+
345
+
// Look for quoted field names
346
+
const quotedFieldMatch = errorMessage.match(/'([^']+)'/);
347
+
if (quotedFieldMatch) {
348
+
const fieldPath = quotedFieldMatch[1];
349
+
350
+
// Try to find this field in the document
351
+
const range = this.findFieldRange(document, fieldPath) ||
352
+
this.findLineWithText(document, `"${fieldPath}"`);
353
+
if (range) return range;
354
+
}
355
+
356
+
// Look for "type" errors - often in definitions
357
+
if (errorMessage.toLowerCase().includes('type')) {
358
+
const typeRange = this.findLineWithText(document, '"type"');
359
+
if (typeRange) return typeRange;
360
+
}
361
+
362
+
// Look for "defs" errors
363
+
if (errorMessage.toLowerCase().includes('defs') || errorMessage.toLowerCase().includes('definition')) {
364
+
const defsRange = this.findFieldRange(document, 'defs');
365
+
if (defsRange) return defsRange;
366
+
}
367
+
368
+
return null;
369
+
}
370
+
371
+
private mapPropertyPathToJsonPath(propertyPath: string): string[] {
372
+
// Map paths like "main.{property}" to actual JSON structure "defs.main.record.properties.{property}"
373
+
// This helps find the actual field in the JSON structure
374
+
375
+
const parts = propertyPath.split('.');
376
+
377
+
if (parts.length === 2) {
378
+
// "main.title" -> this is actually "defs.main.record.properties.title" in the JSON
379
+
const [defName, propertyName] = parts;
380
+
381
+
// Try to find the specific property first, then fallback to broader locations
382
+
return [
383
+
propertyName, // Look for "title" directly
384
+
'properties', // Look for "properties" section
385
+
'record', // Look for "record" section
386
+
defName, // Look for "main"
387
+
'defs' // Look for "defs"
388
+
];
389
+
} else if (parts.length === 1) {
390
+
// "main" -> look for "main", then "defs"
391
+
return [parts[0], 'defs'];
392
+
}
393
+
394
+
// For longer paths, try each part in reverse order (most specific first)
395
+
return [...parts.reverse(), 'properties', 'record', 'defs'];
396
+
}
397
+
398
+
private findPropertyInContext(document: vscode.TextDocument, propertyName: string): vscode.Range | null {
399
+
console.log('Looking for property:', propertyName);
400
+
const text = document.getText();
401
+
402
+
// Find "properties" section first
403
+
const propertiesMatch = text.match(/"properties"\s*:\s*{/);
404
+
if (!propertiesMatch) {
405
+
console.log('No properties section found');
406
+
return null;
407
+
}
408
+
409
+
const propertiesStart = propertiesMatch.index! + propertiesMatch[0].length;
410
+
console.log('Properties section starts at:', propertiesStart);
411
+
412
+
// Find the property within the properties section
413
+
const propertiesSection = text.substring(propertiesStart);
414
+
const propertyRegex = new RegExp(`"${propertyName}"\\s*:`);
415
+
const propertyMatch = propertyRegex.exec(propertiesSection);
416
+
417
+
if (propertyMatch) {
418
+
const absoluteIndex = propertiesStart + propertyMatch.index;
419
+
const startPos = document.positionAt(absoluteIndex);
420
+
const endPos = document.positionAt(absoluteIndex + propertyMatch[0].length);
421
+
console.log('Found property at line:', startPos.line + 1);
422
+
return new vscode.Range(startPos, endPos);
423
+
}
424
+
425
+
console.log('Property not found in properties section');
426
+
return null;
427
+
}
428
+
429
+
private createDiagnostic(range: vscode.Range, message: string, severity: vscode.DiagnosticSeverity): vscode.Diagnostic {
430
+
const diagnostic = new vscode.Diagnostic(range, message, severity);
431
+
diagnostic.source = 'lexicon-intellisense';
432
+
return diagnostic;
433
+
}
434
+
}
+113
packages/lexicon-intellisense/src/lexicon-validator.ts
+113
packages/lexicon-intellisense/src/lexicon-validator.ts
···
1
+
/**
2
+
* Lexicon validator wrapper for Node.js environment
3
+
* Uses the WASM module from lexicon-rs
4
+
*/
5
+
6
+
import * as fs from 'fs';
7
+
import * as path from 'path';
8
+
9
+
// Import WASM module - these files are copied from lexicon-rs/pkg
10
+
const wasmModule = require('../wasm/slices_lexicon.js');
11
+
12
+
let wasmInitialized = false;
13
+
14
+
async function ensureWasmInit() {
15
+
if (!wasmInitialized) {
16
+
const wasmPath = path.join(__dirname, '../wasm/slices_lexicon_bg.wasm');
17
+
const wasmBytes = fs.readFileSync(wasmPath);
18
+
await wasmModule.default(wasmBytes);
19
+
wasmInitialized = true;
20
+
}
21
+
}
22
+
23
+
/**
24
+
* Lexicon document interface
25
+
*/
26
+
export interface LexiconDoc {
27
+
id: string;
28
+
defs: Record<string, unknown>;
29
+
lexicon?: number;
30
+
}
31
+
32
+
/**
33
+
* Lexicon validation error
34
+
*/
35
+
export class ValidationError extends Error {
36
+
constructor(message: string) {
37
+
super(message);
38
+
this.name = "ValidationError";
39
+
}
40
+
}
41
+
42
+
/**
43
+
* AT Protocol lexicon validator for Node.js
44
+
*/
45
+
export class LexiconValidator {
46
+
private wasmValidator: any;
47
+
48
+
constructor(lexicons: LexiconDoc[]) {
49
+
const lexiconsJson = JSON.stringify(lexicons);
50
+
this.wasmValidator = new wasmModule.WasmLexiconValidator(lexiconsJson);
51
+
}
52
+
53
+
/**
54
+
* Create a new validator with lexicon documents
55
+
* Ensures WASM is initialized first
56
+
*/
57
+
static async create(lexicons: LexiconDoc[]): Promise<LexiconValidator> {
58
+
await ensureWasmInit();
59
+
return new LexiconValidator(lexicons);
60
+
}
61
+
62
+
/**
63
+
* Validate a record against its collection's lexicon
64
+
*/
65
+
validateRecord(collection: string, record: Record<string, unknown>): void {
66
+
try {
67
+
const recordJson = JSON.stringify(record);
68
+
this.wasmValidator.validate_record(collection, recordJson);
69
+
} catch (error) {
70
+
throw new ValidationError(error instanceof Error ? error.message : String(error));
71
+
}
72
+
}
73
+
74
+
/**
75
+
* Validate that all cross-lexicon references can be resolved
76
+
*/
77
+
validateLexiconSetCompleteness(): void {
78
+
try {
79
+
this.wasmValidator.validate_lexicon_set_completeness();
80
+
} catch (error) {
81
+
throw new ValidationError(error instanceof Error ? error.message : String(error));
82
+
}
83
+
}
84
+
85
+
/**
86
+
* Clean up WASM resources
87
+
*/
88
+
free(): void {
89
+
if (this.wasmValidator) {
90
+
this.wasmValidator.free();
91
+
}
92
+
}
93
+
}
94
+
95
+
/**
96
+
* Validate a string against a specific format
97
+
*/
98
+
export async function validateStringFormat(value: string, format: string): Promise<void> {
99
+
await ensureWasmInit();
100
+
try {
101
+
wasmModule.validate_string_format(value, format);
102
+
} catch (error) {
103
+
throw new ValidationError(error instanceof Error ? error.message : String(error));
104
+
}
105
+
}
106
+
107
+
/**
108
+
* Check if a string is a valid NSID (Name Spaced Identifier)
109
+
*/
110
+
export async function isValidNsid(nsid: string): Promise<boolean> {
111
+
await ensureWasmInit();
112
+
return wasmModule.is_valid_nsid(nsid);
113
+
}
+23
packages/lexicon-intellisense/tsconfig.json
+23
packages/lexicon-intellisense/tsconfig.json
···
1
+
{
2
+
"compilerOptions": {
3
+
"target": "ES2020",
4
+
"lib": ["ES2020"],
5
+
"module": "commonjs",
6
+
"outDir": "./out",
7
+
"rootDir": "./src",
8
+
"strict": true,
9
+
"esModuleInterop": true,
10
+
"skipLibCheck": true,
11
+
"forceConsistentCasingInFileNames": true,
12
+
"declaration": true,
13
+
"sourceMap": true
14
+
},
15
+
"include": [
16
+
"src/**/*"
17
+
],
18
+
"exclude": [
19
+
"node_modules",
20
+
"out",
21
+
"**/*.test.ts"
22
+
]
23
+
}
+20
packages/lexicon-intellisense/wasm/LICENSE
+20
packages/lexicon-intellisense/wasm/LICENSE
···
1
+
MIT License
2
+
3
+
Copyright (c) 2025 Slices Network
4
+
5
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+
this software and associated documentation files (the "Software"), to deal in
7
+
the Software without restriction, including without limitation the rights to
8
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+
the Software, and to permit persons to whom the Software is furnished to do so,
10
+
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, FITNESS
17
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+100
packages/lexicon-intellisense/wasm/README.md
+100
packages/lexicon-intellisense/wasm/README.md
···
1
+
# slices-lexicon
2
+
3
+
AT Protocol lexicon validation library for Rust, compiled to WebAssembly.
4
+
5
+
## Features
6
+
7
+
- 🚀 Fast lexicon validation written in Rust
8
+
- 🌐 WebAssembly support for browser and Node.js environments
9
+
- 📦 Zero JavaScript dependencies
10
+
- 🔒 Type-safe validation with comprehensive error messages
11
+
- ✅ Full AT Protocol lexicon specification support
12
+
13
+
## Installation
14
+
15
+
```toml
16
+
[dependencies]
17
+
slices-lexicon = "0.1"
18
+
```
19
+
20
+
## Usage
21
+
22
+
### In Rust
23
+
24
+
```rust
25
+
use slices_lexicon::{LexiconValidator, ValidationError};
26
+
use serde_json::json;
27
+
28
+
let lexicons = vec![
29
+
json!({
30
+
"id": "com.example.post",
31
+
"defs": {
32
+
"main": {
33
+
"type": "record",
34
+
"record": {
35
+
"type": "object",
36
+
"properties": {
37
+
"text": { "type": "string" }
38
+
}
39
+
}
40
+
}
41
+
}
42
+
})
43
+
];
44
+
45
+
let validator = LexiconValidator::new(lexicons)?;
46
+
let record = json!({ "text": "Hello, world!" });
47
+
48
+
validator.validate_record("com.example.post", &record)?;
49
+
```
50
+
51
+
### As WebAssembly
52
+
53
+
Build with wasm-pack:
54
+
55
+
```bash
56
+
wasm-pack build --target web
57
+
```
58
+
59
+
Then use in JavaScript/TypeScript:
60
+
61
+
```javascript
62
+
import init, { WasmLexiconValidator } from './pkg/slices_lexicon.js';
63
+
64
+
await init();
65
+
66
+
const validator = new WasmLexiconValidator(JSON.stringify(lexicons));
67
+
validator.validate_record("com.example.post", JSON.stringify(record));
68
+
```
69
+
70
+
## Supported Types
71
+
72
+
- Objects with required/optional fields
73
+
- Arrays with min/max length constraints
74
+
- Strings with format validation (datetime, URI, DID, handle, etc.)
75
+
- Integers with range constraints
76
+
- Booleans with const values
77
+
- Bytes (base64 encoded)
78
+
- Unions (open and closed)
79
+
- References (local and cross-lexicon)
80
+
- CID links
81
+
- Tokens (unit types for type discrimination)
82
+
- Unknown types
83
+
84
+
## String Formats
85
+
86
+
- `datetime` - RFC3339/ISO8601 datetime
87
+
- `uri` - Generic URI
88
+
- `at-uri` - AT Protocol URI
89
+
- `did` - Decentralized Identifier
90
+
- `handle` - AT Protocol handle
91
+
- `at-identifier` - DID or handle
92
+
- `nsid` - Name Spaced Identifier
93
+
- `cid` - Content Identifier
94
+
- `language` - BCP47 language tag
95
+
- `tid` - Timestamp-based Identifier
96
+
- `record-key` - Record key format
97
+
98
+
## License
99
+
100
+
MIT
+62
packages/lexicon-intellisense/wasm/lexicon_validator.d.ts
+62
packages/lexicon-intellisense/wasm/lexicon_validator.d.ts
···
1
+
/* tslint:disable */
2
+
/* eslint-disable */
3
+
export function main(): void;
4
+
export function validate_string_format(value: string, format: string): void;
5
+
export function is_valid_nsid(nsid: string): boolean;
6
+
export class WasmLexiconValidator {
7
+
free(): void;
8
+
/**
9
+
* Create a new validator with the given lexicon documents
10
+
* Takes a JSON string containing an array of lexicon documents
11
+
*/
12
+
constructor(lexicons_json: string);
13
+
/**
14
+
* Validate a record against its collection's lexicon
15
+
* Takes collection name and record as JSON strings
16
+
*/
17
+
validate_record(collection: string, record_json: string): void;
18
+
/**
19
+
* Validate that all cross-lexicon references can be resolved
20
+
*/
21
+
validate_lexicon_set_completeness(): void;
22
+
}
23
+
24
+
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
25
+
26
+
export interface InitOutput {
27
+
readonly memory: WebAssembly.Memory;
28
+
readonly main: () => void;
29
+
readonly __wbg_wasmlexiconvalidator_free: (a: number, b: number) => void;
30
+
readonly wasmlexiconvalidator_new: (a: number, b: number) => [number, number, number];
31
+
readonly wasmlexiconvalidator_validate_record: (a: number, b: number, c: number, d: number, e: number) => [number, number];
32
+
readonly wasmlexiconvalidator_validate_lexicon_set_completeness: (a: number) => [number, number];
33
+
readonly validate_string_format: (a: number, b: number, c: number, d: number) => [number, number];
34
+
readonly is_valid_nsid: (a: number, b: number) => number;
35
+
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
36
+
readonly __wbindgen_malloc: (a: number, b: number) => number;
37
+
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
38
+
readonly __wbindgen_export_3: WebAssembly.Table;
39
+
readonly __externref_table_dealloc: (a: number) => void;
40
+
readonly __wbindgen_start: () => void;
41
+
}
42
+
43
+
export type SyncInitInput = BufferSource | WebAssembly.Module;
44
+
/**
45
+
* Instantiates the given `module`, which can either be bytes or
46
+
* a precompiled `WebAssembly.Module`.
47
+
*
48
+
* @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
49
+
*
50
+
* @returns {InitOutput}
51
+
*/
52
+
export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
53
+
54
+
/**
55
+
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
56
+
* for everything else, calls `WebAssembly.instantiate` directly.
57
+
*
58
+
* @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
59
+
*
60
+
* @returns {Promise<InitOutput>}
61
+
*/
62
+
export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;
+344
packages/lexicon-intellisense/wasm/lexicon_validator.js
+344
packages/lexicon-intellisense/wasm/lexicon_validator.js
···
1
+
let wasm;
2
+
3
+
let cachedUint8ArrayMemory0 = null;
4
+
5
+
function getUint8ArrayMemory0() {
6
+
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
7
+
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
8
+
}
9
+
return cachedUint8ArrayMemory0;
10
+
}
11
+
12
+
let cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
13
+
14
+
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
15
+
16
+
const MAX_SAFARI_DECODE_BYTES = 2146435072;
17
+
let numBytesDecoded = 0;
18
+
function decodeText(ptr, len) {
19
+
numBytesDecoded += len;
20
+
if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
21
+
cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
22
+
cachedTextDecoder.decode();
23
+
numBytesDecoded = len;
24
+
}
25
+
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
26
+
}
27
+
28
+
function getStringFromWasm0(ptr, len) {
29
+
ptr = ptr >>> 0;
30
+
return decodeText(ptr, len);
31
+
}
32
+
33
+
let WASM_VECTOR_LEN = 0;
34
+
35
+
const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } );
36
+
37
+
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
38
+
? function (arg, view) {
39
+
return cachedTextEncoder.encodeInto(arg, view);
40
+
}
41
+
: function (arg, view) {
42
+
const buf = cachedTextEncoder.encode(arg);
43
+
view.set(buf);
44
+
return {
45
+
read: arg.length,
46
+
written: buf.length
47
+
};
48
+
});
49
+
50
+
function passStringToWasm0(arg, malloc, realloc) {
51
+
52
+
if (realloc === undefined) {
53
+
const buf = cachedTextEncoder.encode(arg);
54
+
const ptr = malloc(buf.length, 1) >>> 0;
55
+
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
56
+
WASM_VECTOR_LEN = buf.length;
57
+
return ptr;
58
+
}
59
+
60
+
let len = arg.length;
61
+
let ptr = malloc(len, 1) >>> 0;
62
+
63
+
const mem = getUint8ArrayMemory0();
64
+
65
+
let offset = 0;
66
+
67
+
for (; offset < len; offset++) {
68
+
const code = arg.charCodeAt(offset);
69
+
if (code > 0x7F) break;
70
+
mem[ptr + offset] = code;
71
+
}
72
+
73
+
if (offset !== len) {
74
+
if (offset !== 0) {
75
+
arg = arg.slice(offset);
76
+
}
77
+
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
78
+
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
79
+
const ret = encodeString(arg, view);
80
+
81
+
offset += ret.written;
82
+
ptr = realloc(ptr, len, offset, 1) >>> 0;
83
+
}
84
+
85
+
WASM_VECTOR_LEN = offset;
86
+
return ptr;
87
+
}
88
+
89
+
let cachedDataViewMemory0 = null;
90
+
91
+
function getDataViewMemory0() {
92
+
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
93
+
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
94
+
}
95
+
return cachedDataViewMemory0;
96
+
}
97
+
98
+
export function main() {
99
+
wasm.main();
100
+
}
101
+
102
+
function takeFromExternrefTable0(idx) {
103
+
const value = wasm.__wbindgen_export_3.get(idx);
104
+
wasm.__externref_table_dealloc(idx);
105
+
return value;
106
+
}
107
+
/**
108
+
* @param {string} value
109
+
* @param {string} format
110
+
*/
111
+
export function validate_string_format(value, format) {
112
+
const ptr0 = passStringToWasm0(value, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
113
+
const len0 = WASM_VECTOR_LEN;
114
+
const ptr1 = passStringToWasm0(format, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
115
+
const len1 = WASM_VECTOR_LEN;
116
+
const ret = wasm.validate_string_format(ptr0, len0, ptr1, len1);
117
+
if (ret[1]) {
118
+
throw takeFromExternrefTable0(ret[0]);
119
+
}
120
+
}
121
+
122
+
/**
123
+
* @param {string} nsid
124
+
* @returns {boolean}
125
+
*/
126
+
export function is_valid_nsid(nsid) {
127
+
const ptr0 = passStringToWasm0(nsid, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
128
+
const len0 = WASM_VECTOR_LEN;
129
+
const ret = wasm.is_valid_nsid(ptr0, len0);
130
+
return ret !== 0;
131
+
}
132
+
133
+
const WasmLexiconValidatorFinalization = (typeof FinalizationRegistry === 'undefined')
134
+
? { register: () => {}, unregister: () => {} }
135
+
: new FinalizationRegistry(ptr => wasm.__wbg_wasmlexiconvalidator_free(ptr >>> 0, 1));
136
+
137
+
export class WasmLexiconValidator {
138
+
139
+
__destroy_into_raw() {
140
+
const ptr = this.__wbg_ptr;
141
+
this.__wbg_ptr = 0;
142
+
WasmLexiconValidatorFinalization.unregister(this);
143
+
return ptr;
144
+
}
145
+
146
+
free() {
147
+
const ptr = this.__destroy_into_raw();
148
+
wasm.__wbg_wasmlexiconvalidator_free(ptr, 0);
149
+
}
150
+
/**
151
+
* Create a new validator with the given lexicon documents
152
+
* Takes a JSON string containing an array of lexicon documents
153
+
* @param {string} lexicons_json
154
+
*/
155
+
constructor(lexicons_json) {
156
+
const ptr0 = passStringToWasm0(lexicons_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
157
+
const len0 = WASM_VECTOR_LEN;
158
+
const ret = wasm.wasmlexiconvalidator_new(ptr0, len0);
159
+
if (ret[2]) {
160
+
throw takeFromExternrefTable0(ret[1]);
161
+
}
162
+
this.__wbg_ptr = ret[0] >>> 0;
163
+
WasmLexiconValidatorFinalization.register(this, this.__wbg_ptr, this);
164
+
return this;
165
+
}
166
+
/**
167
+
* Validate a record against its collection's lexicon
168
+
* Takes collection name and record as JSON strings
169
+
* @param {string} collection
170
+
* @param {string} record_json
171
+
*/
172
+
validate_record(collection, record_json) {
173
+
const ptr0 = passStringToWasm0(collection, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
174
+
const len0 = WASM_VECTOR_LEN;
175
+
const ptr1 = passStringToWasm0(record_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
176
+
const len1 = WASM_VECTOR_LEN;
177
+
const ret = wasm.wasmlexiconvalidator_validate_record(this.__wbg_ptr, ptr0, len0, ptr1, len1);
178
+
if (ret[1]) {
179
+
throw takeFromExternrefTable0(ret[0]);
180
+
}
181
+
}
182
+
/**
183
+
* Validate that all cross-lexicon references can be resolved
184
+
*/
185
+
validate_lexicon_set_completeness() {
186
+
const ret = wasm.wasmlexiconvalidator_validate_lexicon_set_completeness(this.__wbg_ptr);
187
+
if (ret[1]) {
188
+
throw takeFromExternrefTable0(ret[0]);
189
+
}
190
+
}
191
+
}
192
+
193
+
const EXPECTED_RESPONSE_TYPES = new Set(['basic', 'cors', 'default']);
194
+
195
+
async function __wbg_load(module, imports) {
196
+
if (typeof Response === 'function' && module instanceof Response) {
197
+
if (typeof WebAssembly.instantiateStreaming === 'function') {
198
+
try {
199
+
return await WebAssembly.instantiateStreaming(module, imports);
200
+
201
+
} catch (e) {
202
+
const validResponse = module.ok && EXPECTED_RESPONSE_TYPES.has(module.type);
203
+
204
+
if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') {
205
+
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
206
+
207
+
} else {
208
+
throw e;
209
+
}
210
+
}
211
+
}
212
+
213
+
const bytes = await module.arrayBuffer();
214
+
return await WebAssembly.instantiate(bytes, imports);
215
+
216
+
} else {
217
+
const instance = await WebAssembly.instantiate(module, imports);
218
+
219
+
if (instance instanceof WebAssembly.Instance) {
220
+
return { instance, module };
221
+
222
+
} else {
223
+
return instance;
224
+
}
225
+
}
226
+
}
227
+
228
+
function __wbg_get_imports() {
229
+
const imports = {};
230
+
imports.wbg = {};
231
+
imports.wbg.__wbg_error_7534b8e9a36f1ab4 = function(arg0, arg1) {
232
+
let deferred0_0;
233
+
let deferred0_1;
234
+
try {
235
+
deferred0_0 = arg0;
236
+
deferred0_1 = arg1;
237
+
console.error(getStringFromWasm0(arg0, arg1));
238
+
} finally {
239
+
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
240
+
}
241
+
};
242
+
imports.wbg.__wbg_new_8a6f238a6ece86ea = function() {
243
+
const ret = new Error();
244
+
return ret;
245
+
};
246
+
imports.wbg.__wbg_stack_0ed75d68575b0f3c = function(arg0, arg1) {
247
+
const ret = arg1.stack;
248
+
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
249
+
const len1 = WASM_VECTOR_LEN;
250
+
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
251
+
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
252
+
};
253
+
imports.wbg.__wbg_wbindgenthrow_4c11a24fca429ccf = function(arg0, arg1) {
254
+
throw new Error(getStringFromWasm0(arg0, arg1));
255
+
};
256
+
imports.wbg.__wbindgen_cast_2241b6af4c4b2941 = function(arg0, arg1) {
257
+
// Cast intrinsic for `Ref(String) -> Externref`.
258
+
const ret = getStringFromWasm0(arg0, arg1);
259
+
return ret;
260
+
};
261
+
imports.wbg.__wbindgen_init_externref_table = function() {
262
+
const table = wasm.__wbindgen_export_3;
263
+
const offset = table.grow(4);
264
+
table.set(0, undefined);
265
+
table.set(offset + 0, undefined);
266
+
table.set(offset + 1, null);
267
+
table.set(offset + 2, true);
268
+
table.set(offset + 3, false);
269
+
;
270
+
};
271
+
272
+
return imports;
273
+
}
274
+
275
+
function __wbg_init_memory(imports, memory) {
276
+
277
+
}
278
+
279
+
function __wbg_finalize_init(instance, module) {
280
+
wasm = instance.exports;
281
+
__wbg_init.__wbindgen_wasm_module = module;
282
+
cachedDataViewMemory0 = null;
283
+
cachedUint8ArrayMemory0 = null;
284
+
285
+
286
+
wasm.__wbindgen_start();
287
+
return wasm;
288
+
}
289
+
290
+
function initSync(module) {
291
+
if (wasm !== undefined) return wasm;
292
+
293
+
294
+
if (typeof module !== 'undefined') {
295
+
if (Object.getPrototypeOf(module) === Object.prototype) {
296
+
({module} = module)
297
+
} else {
298
+
console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
299
+
}
300
+
}
301
+
302
+
const imports = __wbg_get_imports();
303
+
304
+
__wbg_init_memory(imports);
305
+
306
+
if (!(module instanceof WebAssembly.Module)) {
307
+
module = new WebAssembly.Module(module);
308
+
}
309
+
310
+
const instance = new WebAssembly.Instance(module, imports);
311
+
312
+
return __wbg_finalize_init(instance, module);
313
+
}
314
+
315
+
async function __wbg_init(module_or_path) {
316
+
if (wasm !== undefined) return wasm;
317
+
318
+
319
+
if (typeof module_or_path !== 'undefined') {
320
+
if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
321
+
({module_or_path} = module_or_path)
322
+
} else {
323
+
console.warn('using deprecated parameters for the initialization function; pass a single object instead')
324
+
}
325
+
}
326
+
327
+
if (typeof module_or_path === 'undefined') {
328
+
module_or_path = new URL('lexicon_validator_bg.wasm', import.meta.url);
329
+
}
330
+
const imports = __wbg_get_imports();
331
+
332
+
if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
333
+
module_or_path = fetch(module_or_path);
334
+
}
335
+
336
+
__wbg_init_memory(imports);
337
+
338
+
const { instance, module } = await __wbg_load(await module_or_path, imports);
339
+
340
+
return __wbg_finalize_init(instance, module);
341
+
}
342
+
343
+
export { initSync };
344
+
export default __wbg_init;
packages/lexicon-intellisense/wasm/lexicon_validator_bg.wasm
packages/lexicon-intellisense/wasm/lexicon_validator_bg.wasm
This is a binary file and will not be displayed.
+16
packages/lexicon-intellisense/wasm/lexicon_validator_bg.wasm.d.ts
+16
packages/lexicon-intellisense/wasm/lexicon_validator_bg.wasm.d.ts
···
1
+
/* tslint:disable */
2
+
/* eslint-disable */
3
+
export const memory: WebAssembly.Memory;
4
+
export const main: () => void;
5
+
export const __wbg_wasmlexiconvalidator_free: (a: number, b: number) => void;
6
+
export const wasmlexiconvalidator_new: (a: number, b: number) => [number, number, number];
7
+
export const wasmlexiconvalidator_validate_record: (a: number, b: number, c: number, d: number, e: number) => [number, number];
8
+
export const wasmlexiconvalidator_validate_lexicon_set_completeness: (a: number) => [number, number];
9
+
export const validate_string_format: (a: number, b: number, c: number, d: number) => [number, number];
10
+
export const is_valid_nsid: (a: number, b: number) => number;
11
+
export const __wbindgen_free: (a: number, b: number, c: number) => void;
12
+
export const __wbindgen_malloc: (a: number, b: number) => number;
13
+
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
14
+
export const __wbindgen_export_3: WebAssembly.Table;
15
+
export const __externref_table_dealloc: (a: number) => void;
16
+
export const __wbindgen_start: () => void;
+24
packages/lexicon-intellisense/wasm/package.json
+24
packages/lexicon-intellisense/wasm/package.json
···
1
+
{
2
+
"name": "slices-lexicon",
3
+
"type": "module",
4
+
"description": "AT Protocol lexicon validation library for Slices",
5
+
"version": "0.1.4",
6
+
"license": "MIT",
7
+
"files": [
8
+
"slices_lexicon_bg.wasm",
9
+
"slices_lexicon.js",
10
+
"slices_lexicon.d.ts"
11
+
],
12
+
"main": "slices_lexicon.js",
13
+
"types": "slices_lexicon.d.ts",
14
+
"sideEffects": [
15
+
"./snippets/*"
16
+
],
17
+
"keywords": [
18
+
"atproto",
19
+
"lexicon",
20
+
"validation",
21
+
"wasm",
22
+
"bluesky"
23
+
]
24
+
}
+62
packages/lexicon-intellisense/wasm/slices_lexicon.d.ts
+62
packages/lexicon-intellisense/wasm/slices_lexicon.d.ts
···
1
+
/* tslint:disable */
2
+
/* eslint-disable */
3
+
export function main(): void;
4
+
export function validate_string_format(value: string, format: string): void;
5
+
export function is_valid_nsid(nsid: string): boolean;
6
+
export class WasmLexiconValidator {
7
+
free(): void;
8
+
/**
9
+
* Create a new validator with the given lexicon documents
10
+
* Takes a JSON string containing an array of lexicon documents
11
+
*/
12
+
constructor(lexicons_json: string);
13
+
/**
14
+
* Validate a record against its collection's lexicon
15
+
* Takes collection name and record as JSON strings
16
+
*/
17
+
validate_record(collection: string, record_json: string): void;
18
+
/**
19
+
* Validate that all cross-lexicon references can be resolved
20
+
*/
21
+
validate_lexicon_set_completeness(): void;
22
+
}
23
+
24
+
export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
25
+
26
+
export interface InitOutput {
27
+
readonly memory: WebAssembly.Memory;
28
+
readonly __wbg_wasmlexiconvalidator_free: (a: number, b: number) => void;
29
+
readonly wasmlexiconvalidator_new: (a: number, b: number) => [number, number, number];
30
+
readonly wasmlexiconvalidator_validate_record: (a: number, b: number, c: number, d: number, e: number) => [number, number];
31
+
readonly wasmlexiconvalidator_validate_lexicon_set_completeness: (a: number) => [number, number];
32
+
readonly validate_string_format: (a: number, b: number, c: number, d: number) => [number, number];
33
+
readonly is_valid_nsid: (a: number, b: number) => number;
34
+
readonly main: () => void;
35
+
readonly __wbindgen_free: (a: number, b: number, c: number) => void;
36
+
readonly __wbindgen_malloc: (a: number, b: number) => number;
37
+
readonly __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
38
+
readonly __wbindgen_export_3: WebAssembly.Table;
39
+
readonly __externref_table_dealloc: (a: number) => void;
40
+
readonly __wbindgen_start: () => void;
41
+
}
42
+
43
+
export type SyncInitInput = BufferSource | WebAssembly.Module;
44
+
/**
45
+
* Instantiates the given `module`, which can either be bytes or
46
+
* a precompiled `WebAssembly.Module`.
47
+
*
48
+
* @param {{ module: SyncInitInput }} module - Passing `SyncInitInput` directly is deprecated.
49
+
*
50
+
* @returns {InitOutput}
51
+
*/
52
+
export function initSync(module: { module: SyncInitInput } | SyncInitInput): InitOutput;
53
+
54
+
/**
55
+
* If `module_or_path` is {RequestInfo} or {URL}, makes a request and
56
+
* for everything else, calls `WebAssembly.instantiate` directly.
57
+
*
58
+
* @param {{ module_or_path: InitInput | Promise<InitInput> }} module_or_path - Passing `InitInput` directly is deprecated.
59
+
*
60
+
* @returns {Promise<InitOutput>}
61
+
*/
62
+
export default function __wbg_init (module_or_path?: { module_or_path: InitInput | Promise<InitInput> } | InitInput | Promise<InitInput>): Promise<InitOutput>;
+344
packages/lexicon-intellisense/wasm/slices_lexicon.js
+344
packages/lexicon-intellisense/wasm/slices_lexicon.js
···
1
+
let wasm;
2
+
3
+
let cachedUint8ArrayMemory0 = null;
4
+
5
+
function getUint8ArrayMemory0() {
6
+
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
7
+
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
8
+
}
9
+
return cachedUint8ArrayMemory0;
10
+
}
11
+
12
+
let cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
13
+
14
+
if (typeof TextDecoder !== 'undefined') { cachedTextDecoder.decode(); };
15
+
16
+
const MAX_SAFARI_DECODE_BYTES = 2146435072;
17
+
let numBytesDecoded = 0;
18
+
function decodeText(ptr, len) {
19
+
numBytesDecoded += len;
20
+
if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
21
+
cachedTextDecoder = (typeof TextDecoder !== 'undefined' ? new TextDecoder('utf-8', { ignoreBOM: true, fatal: true }) : { decode: () => { throw Error('TextDecoder not available') } } );
22
+
cachedTextDecoder.decode();
23
+
numBytesDecoded = len;
24
+
}
25
+
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
26
+
}
27
+
28
+
function getStringFromWasm0(ptr, len) {
29
+
ptr = ptr >>> 0;
30
+
return decodeText(ptr, len);
31
+
}
32
+
33
+
let WASM_VECTOR_LEN = 0;
34
+
35
+
const cachedTextEncoder = (typeof TextEncoder !== 'undefined' ? new TextEncoder('utf-8') : { encode: () => { throw Error('TextEncoder not available') } } );
36
+
37
+
const encodeString = (typeof cachedTextEncoder.encodeInto === 'function'
38
+
? function (arg, view) {
39
+
return cachedTextEncoder.encodeInto(arg, view);
40
+
}
41
+
: function (arg, view) {
42
+
const buf = cachedTextEncoder.encode(arg);
43
+
view.set(buf);
44
+
return {
45
+
read: arg.length,
46
+
written: buf.length
47
+
};
48
+
});
49
+
50
+
function passStringToWasm0(arg, malloc, realloc) {
51
+
52
+
if (realloc === undefined) {
53
+
const buf = cachedTextEncoder.encode(arg);
54
+
const ptr = malloc(buf.length, 1) >>> 0;
55
+
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
56
+
WASM_VECTOR_LEN = buf.length;
57
+
return ptr;
58
+
}
59
+
60
+
let len = arg.length;
61
+
let ptr = malloc(len, 1) >>> 0;
62
+
63
+
const mem = getUint8ArrayMemory0();
64
+
65
+
let offset = 0;
66
+
67
+
for (; offset < len; offset++) {
68
+
const code = arg.charCodeAt(offset);
69
+
if (code > 0x7F) break;
70
+
mem[ptr + offset] = code;
71
+
}
72
+
73
+
if (offset !== len) {
74
+
if (offset !== 0) {
75
+
arg = arg.slice(offset);
76
+
}
77
+
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
78
+
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
79
+
const ret = encodeString(arg, view);
80
+
81
+
offset += ret.written;
82
+
ptr = realloc(ptr, len, offset, 1) >>> 0;
83
+
}
84
+
85
+
WASM_VECTOR_LEN = offset;
86
+
return ptr;
87
+
}
88
+
89
+
let cachedDataViewMemory0 = null;
90
+
91
+
function getDataViewMemory0() {
92
+
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
93
+
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
94
+
}
95
+
return cachedDataViewMemory0;
96
+
}
97
+
98
+
export function main() {
99
+
wasm.main();
100
+
}
101
+
102
+
function takeFromExternrefTable0(idx) {
103
+
const value = wasm.__wbindgen_export_3.get(idx);
104
+
wasm.__externref_table_dealloc(idx);
105
+
return value;
106
+
}
107
+
/**
108
+
* @param {string} value
109
+
* @param {string} format
110
+
*/
111
+
export function validate_string_format(value, format) {
112
+
const ptr0 = passStringToWasm0(value, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
113
+
const len0 = WASM_VECTOR_LEN;
114
+
const ptr1 = passStringToWasm0(format, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
115
+
const len1 = WASM_VECTOR_LEN;
116
+
const ret = wasm.validate_string_format(ptr0, len0, ptr1, len1);
117
+
if (ret[1]) {
118
+
throw takeFromExternrefTable0(ret[0]);
119
+
}
120
+
}
121
+
122
+
/**
123
+
* @param {string} nsid
124
+
* @returns {boolean}
125
+
*/
126
+
export function is_valid_nsid(nsid) {
127
+
const ptr0 = passStringToWasm0(nsid, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
128
+
const len0 = WASM_VECTOR_LEN;
129
+
const ret = wasm.is_valid_nsid(ptr0, len0);
130
+
return ret !== 0;
131
+
}
132
+
133
+
const WasmLexiconValidatorFinalization = (typeof FinalizationRegistry === 'undefined')
134
+
? { register: () => {}, unregister: () => {} }
135
+
: new FinalizationRegistry(ptr => wasm.__wbg_wasmlexiconvalidator_free(ptr >>> 0, 1));
136
+
137
+
export class WasmLexiconValidator {
138
+
139
+
__destroy_into_raw() {
140
+
const ptr = this.__wbg_ptr;
141
+
this.__wbg_ptr = 0;
142
+
WasmLexiconValidatorFinalization.unregister(this);
143
+
return ptr;
144
+
}
145
+
146
+
free() {
147
+
const ptr = this.__destroy_into_raw();
148
+
wasm.__wbg_wasmlexiconvalidator_free(ptr, 0);
149
+
}
150
+
/**
151
+
* Create a new validator with the given lexicon documents
152
+
* Takes a JSON string containing an array of lexicon documents
153
+
* @param {string} lexicons_json
154
+
*/
155
+
constructor(lexicons_json) {
156
+
const ptr0 = passStringToWasm0(lexicons_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
157
+
const len0 = WASM_VECTOR_LEN;
158
+
const ret = wasm.wasmlexiconvalidator_new(ptr0, len0);
159
+
if (ret[2]) {
160
+
throw takeFromExternrefTable0(ret[1]);
161
+
}
162
+
this.__wbg_ptr = ret[0] >>> 0;
163
+
WasmLexiconValidatorFinalization.register(this, this.__wbg_ptr, this);
164
+
return this;
165
+
}
166
+
/**
167
+
* Validate a record against its collection's lexicon
168
+
* Takes collection name and record as JSON strings
169
+
* @param {string} collection
170
+
* @param {string} record_json
171
+
*/
172
+
validate_record(collection, record_json) {
173
+
const ptr0 = passStringToWasm0(collection, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
174
+
const len0 = WASM_VECTOR_LEN;
175
+
const ptr1 = passStringToWasm0(record_json, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
176
+
const len1 = WASM_VECTOR_LEN;
177
+
const ret = wasm.wasmlexiconvalidator_validate_record(this.__wbg_ptr, ptr0, len0, ptr1, len1);
178
+
if (ret[1]) {
179
+
throw takeFromExternrefTable0(ret[0]);
180
+
}
181
+
}
182
+
/**
183
+
* Validate that all cross-lexicon references can be resolved
184
+
*/
185
+
validate_lexicon_set_completeness() {
186
+
const ret = wasm.wasmlexiconvalidator_validate_lexicon_set_completeness(this.__wbg_ptr);
187
+
if (ret[1]) {
188
+
throw takeFromExternrefTable0(ret[0]);
189
+
}
190
+
}
191
+
}
192
+
193
+
const EXPECTED_RESPONSE_TYPES = new Set(['basic', 'cors', 'default']);
194
+
195
+
async function __wbg_load(module, imports) {
196
+
if (typeof Response === 'function' && module instanceof Response) {
197
+
if (typeof WebAssembly.instantiateStreaming === 'function') {
198
+
try {
199
+
return await WebAssembly.instantiateStreaming(module, imports);
200
+
201
+
} catch (e) {
202
+
const validResponse = module.ok && EXPECTED_RESPONSE_TYPES.has(module.type);
203
+
204
+
if (validResponse && module.headers.get('Content-Type') !== 'application/wasm') {
205
+
console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve Wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n", e);
206
+
207
+
} else {
208
+
throw e;
209
+
}
210
+
}
211
+
}
212
+
213
+
const bytes = await module.arrayBuffer();
214
+
return await WebAssembly.instantiate(bytes, imports);
215
+
216
+
} else {
217
+
const instance = await WebAssembly.instantiate(module, imports);
218
+
219
+
if (instance instanceof WebAssembly.Instance) {
220
+
return { instance, module };
221
+
222
+
} else {
223
+
return instance;
224
+
}
225
+
}
226
+
}
227
+
228
+
function __wbg_get_imports() {
229
+
const imports = {};
230
+
imports.wbg = {};
231
+
imports.wbg.__wbg_error_7534b8e9a36f1ab4 = function(arg0, arg1) {
232
+
let deferred0_0;
233
+
let deferred0_1;
234
+
try {
235
+
deferred0_0 = arg0;
236
+
deferred0_1 = arg1;
237
+
console.error(getStringFromWasm0(arg0, arg1));
238
+
} finally {
239
+
wasm.__wbindgen_free(deferred0_0, deferred0_1, 1);
240
+
}
241
+
};
242
+
imports.wbg.__wbg_new_8a6f238a6ece86ea = function() {
243
+
const ret = new Error();
244
+
return ret;
245
+
};
246
+
imports.wbg.__wbg_stack_0ed75d68575b0f3c = function(arg0, arg1) {
247
+
const ret = arg1.stack;
248
+
const ptr1 = passStringToWasm0(ret, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc);
249
+
const len1 = WASM_VECTOR_LEN;
250
+
getDataViewMemory0().setInt32(arg0 + 4 * 1, len1, true);
251
+
getDataViewMemory0().setInt32(arg0 + 4 * 0, ptr1, true);
252
+
};
253
+
imports.wbg.__wbg_wbindgenthrow_4c11a24fca429ccf = function(arg0, arg1) {
254
+
throw new Error(getStringFromWasm0(arg0, arg1));
255
+
};
256
+
imports.wbg.__wbindgen_cast_2241b6af4c4b2941 = function(arg0, arg1) {
257
+
// Cast intrinsic for `Ref(String) -> Externref`.
258
+
const ret = getStringFromWasm0(arg0, arg1);
259
+
return ret;
260
+
};
261
+
imports.wbg.__wbindgen_init_externref_table = function() {
262
+
const table = wasm.__wbindgen_export_3;
263
+
const offset = table.grow(4);
264
+
table.set(0, undefined);
265
+
table.set(offset + 0, undefined);
266
+
table.set(offset + 1, null);
267
+
table.set(offset + 2, true);
268
+
table.set(offset + 3, false);
269
+
;
270
+
};
271
+
272
+
return imports;
273
+
}
274
+
275
+
function __wbg_init_memory(imports, memory) {
276
+
277
+
}
278
+
279
+
function __wbg_finalize_init(instance, module) {
280
+
wasm = instance.exports;
281
+
__wbg_init.__wbindgen_wasm_module = module;
282
+
cachedDataViewMemory0 = null;
283
+
cachedUint8ArrayMemory0 = null;
284
+
285
+
286
+
wasm.__wbindgen_start();
287
+
return wasm;
288
+
}
289
+
290
+
function initSync(module) {
291
+
if (wasm !== undefined) return wasm;
292
+
293
+
294
+
if (typeof module !== 'undefined') {
295
+
if (Object.getPrototypeOf(module) === Object.prototype) {
296
+
({module} = module)
297
+
} else {
298
+
console.warn('using deprecated parameters for `initSync()`; pass a single object instead')
299
+
}
300
+
}
301
+
302
+
const imports = __wbg_get_imports();
303
+
304
+
__wbg_init_memory(imports);
305
+
306
+
if (!(module instanceof WebAssembly.Module)) {
307
+
module = new WebAssembly.Module(module);
308
+
}
309
+
310
+
const instance = new WebAssembly.Instance(module, imports);
311
+
312
+
return __wbg_finalize_init(instance, module);
313
+
}
314
+
315
+
async function __wbg_init(module_or_path) {
316
+
if (wasm !== undefined) return wasm;
317
+
318
+
319
+
if (typeof module_or_path !== 'undefined') {
320
+
if (Object.getPrototypeOf(module_or_path) === Object.prototype) {
321
+
({module_or_path} = module_or_path)
322
+
} else {
323
+
console.warn('using deprecated parameters for the initialization function; pass a single object instead')
324
+
}
325
+
}
326
+
327
+
if (typeof module_or_path === 'undefined') {
328
+
module_or_path = new URL('slices_lexicon_bg.wasm', import.meta.url);
329
+
}
330
+
const imports = __wbg_get_imports();
331
+
332
+
if (typeof module_or_path === 'string' || (typeof Request === 'function' && module_or_path instanceof Request) || (typeof URL === 'function' && module_or_path instanceof URL)) {
333
+
module_or_path = fetch(module_or_path);
334
+
}
335
+
336
+
__wbg_init_memory(imports);
337
+
338
+
const { instance, module } = await __wbg_load(await module_or_path, imports);
339
+
340
+
return __wbg_finalize_init(instance, module);
341
+
}
342
+
343
+
export { initSync };
344
+
export default __wbg_init;
packages/lexicon-intellisense/wasm/slices_lexicon_bg.wasm
packages/lexicon-intellisense/wasm/slices_lexicon_bg.wasm
This is a binary file and will not be displayed.
+16
packages/lexicon-intellisense/wasm/slices_lexicon_bg.wasm.d.ts
+16
packages/lexicon-intellisense/wasm/slices_lexicon_bg.wasm.d.ts
···
1
+
/* tslint:disable */
2
+
/* eslint-disable */
3
+
export const memory: WebAssembly.Memory;
4
+
export const __wbg_wasmlexiconvalidator_free: (a: number, b: number) => void;
5
+
export const wasmlexiconvalidator_new: (a: number, b: number) => [number, number, number];
6
+
export const wasmlexiconvalidator_validate_record: (a: number, b: number, c: number, d: number, e: number) => [number, number];
7
+
export const wasmlexiconvalidator_validate_lexicon_set_completeness: (a: number) => [number, number];
8
+
export const validate_string_format: (a: number, b: number, c: number, d: number) => [number, number];
9
+
export const is_valid_nsid: (a: number, b: number) => number;
10
+
export const main: () => void;
11
+
export const __wbindgen_free: (a: number, b: number, c: number) => void;
12
+
export const __wbindgen_malloc: (a: number, b: number) => number;
13
+
export const __wbindgen_realloc: (a: number, b: number, c: number, d: number) => number;
14
+
export const __wbindgen_export_3: WebAssembly.Table;
15
+
export const __externref_table_dealloc: (a: number) => void;
16
+
export const __wbindgen_start: () => void;