Highly ambitious ATProtocol AppView service and sdks

add lexicon intellisense package for working with .json files in vscode

packages/lexicon-intellisense/.DS_Store

This is a binary file and will not be displayed.

+3
packages/lexicon-intellisense/.gitignore
··· 1 + out 2 + node_modules 3 + *.vsix
+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
··· 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
··· 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
··· 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
··· 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

This is a binary file and will not be displayed.

+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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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

This is a binary file and will not be displayed.

+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
··· 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
··· 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
··· 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

This is a binary file and will not be displayed.

+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;