bridge-syntax-highlight 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.depcheckrc ADDED
@@ -0,0 +1,7 @@
1
+ {
2
+ "ignores": [
3
+ "@tsconfig/node22",
4
+ "typescript",
5
+ "vscode"
6
+ ]
7
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "Run Extension",
6
+ "type": "extensionHost",
7
+ "request": "launch",
8
+ "args": ["--extensionDevelopmentPath=${workspaceFolder}"],
9
+ "preLaunchTask": "build"
10
+ }
11
+ ]
12
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "tasks": [
4
+ {
5
+ "label": "build",
6
+ "type": "shell",
7
+ "command": "node build.mjs",
8
+ "group": { "kind": "build", "isDefault": true },
9
+ "problemMatcher": []
10
+ }
11
+ ]
12
+ }
package/.vscodeignore ADDED
@@ -0,0 +1,7 @@
1
+ .vscode/**
2
+ .vscode-test/**
3
+ .gitignore
4
+ build.mjs
5
+ tsconfig.json
6
+ src/**
7
+ build/*.map
package/CHANGELOG.md ADDED
@@ -0,0 +1,30 @@
1
+ # Change Log
2
+
3
+ All notable changes to the "bridge" extension will be documented in this file.
4
+
5
+ ## [0.5.0] - 2026-02-21
6
+
7
+ ### Added
8
+
9
+ - **Language Server** with real-time diagnostics and hover information
10
+ - Syntax error diagnostics powered by Chevrotain parser with error recovery (partial AST even on broken files)
11
+ - Semantic diagnostics: undeclared handles, unsatisfied `with` dependencies
12
+ - Hover provider: hover over any handle, dependency, or wire name to see its type and declaration context
13
+
14
+ ## [0.0.1] - 2026-02-21
15
+
16
+ ### Added
17
+
18
+ - Syntax highlighting for Bridge language version 1.5
19
+ - Support for all top-level blocks: `version`, `const`, `tool`, `define`, `bridge`
20
+ - Wire operator highlighting: `<-`, `||`, `??`, `catch`, `:`
21
+ - Array mapping syntax: `source[] as iter { … }`
22
+ - Constant assignment highlighting: `.property = value`
23
+ - Built-in handle highlighting: `input`, `output`, `context`
24
+ - HTTP method constants: `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, etc.
25
+ - Unquoted URL path values (e.g. `/search`, `/forecast`)
26
+ - `on error` fallback declarations
27
+ - Block brace matching
28
+ - Comment support (`#` line comments)
29
+ - String, number, and boolean literal highlighting
30
+ - `language-configuration.json` for bracket matching and comment toggling
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stackables
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # The Bridge Language — VS Code Extension
2
+
3
+ Full IDE support for [The Bridge](https://github.com/stackables/bridge): a declarative dataflow language for GraphQL. Wire data between APIs and schema fields using `.bridge` files — no resolvers, no codegen, no plumbing.
4
+
5
+ ![Bridge syntax highlighting and language server in action](./docs/images/syntax-image.png)
6
+
7
+ ## Features
8
+
9
+ ### Language Server (LSP)
10
+
11
+ - **Real-time diagnostics** — syntax errors and semantic issues highlighted as you type
12
+ - **Semantic validation** — undeclared handles, unsatisfied `with` dependencies, and unknown wire targets flagged immediately
13
+ - **Hover information** — hover over any handle, dependency, or declaration to see its type and source
14
+ - Bridge hover: type/field name, handle count, wire count
15
+ - Tool hover: function name, deps, wires
16
+ - Define hover: subgraph details
17
+ - Const hover: name and raw value
18
+ - Error recovery — partial AST is built even on broken files, so diagnostics remain accurate while you're mid-edit
19
+
20
+ ### Syntax Highlighting
21
+
22
+ - Full syntax highlighting for `.bridge` files (language version 1.5)
23
+ - Block keyword highlighting: `version`, `const`, `tool`, `define`, `bridge`
24
+ - Wire operator colouring: `<-` (pull), `||` (falsy-coalesce), `??` (nullish gate), `catch` (error boundary), `:` (pipe)
25
+ - `force <handle>` and `force <handle> catch null` keyword highlighting
26
+ - Distinct colours for GraphQL type/field targets in `bridge` declarations
27
+ - Tool handle and alias highlighting
28
+ - Built-in handle highlighting: `input`, `output`, `context`
29
+ - Constant assignment colouring: `.property = value`
30
+ - HTTP method constants: `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`
31
+ - Unquoted URL path values (e.g. `/search`, `/api/v1/forecast`)
32
+ - Array mapping syntax: `source[] as iter { … }`
33
+ - `on error` fallback declarations inside `tool` blocks
34
+ - String, number, boolean, and `null` literals
35
+
36
+ ### Editor Integration
37
+
38
+ - Line comment toggling (`#`)
39
+ - Bracket matching for `{}` and `[]`
40
+ - Automatic language detection for `*.bridge` files
41
+
42
+ ## Installation
43
+
44
+ Search for **"The Bridge Language"** in the VS Code Extensions panel, or install from the terminal:
45
+
46
+ ```bash
47
+ code --install-extension stackables.bridge-syntax-highlight
48
+ ```
49
+
50
+ Files named `*.bridge` are automatically detected.
51
+
52
+ ## Related
53
+
54
+ - [The Bridge runtime](https://github.com/stackables/bridge) — `@stackables/bridge` on npm
55
+ - [Language reference](https://github.com/stackables/bridge/blob/main/docs/bridge-language-guide.md) — full syntax documentation
56
+
57
+ ---
58
+
59
+ ## Release Notes
60
+
61
+ See [CHANGELOG.md](CHANGELOG.md).
package/build.mjs ADDED
@@ -0,0 +1,57 @@
1
+ /**
2
+ * esbuild bundler for the Bridge Language extension.
3
+ *
4
+ * Produces two outputs:
5
+ * build/extension.js — VS Code extension host entry (CJS, vscode external)
6
+ * build/server.js — Language server process (CJS, fully self-contained)
7
+ *
8
+ * Run: node build.mjs
9
+ * Watch: node build.mjs --watch
10
+ */
11
+ import * as esbuild from "esbuild";
12
+
13
+ const isWatch = process.argv.includes("--watch");
14
+
15
+ const sharedOptions = {
16
+ bundle: true,
17
+ format: "cjs",
18
+ platform: "node",
19
+ target: "node18",
20
+ sourcemap: true,
21
+ minify: false,
22
+ logLevel: "info",
23
+ };
24
+
25
+ if (isWatch) {
26
+ // Watch mode: two separate contexts
27
+ const [ctxExt, ctxSrv] = await Promise.all([
28
+ esbuild.context({
29
+ ...sharedOptions,
30
+ entryPoints: ["src/extension.ts"],
31
+ outfile: "build/extension.js",
32
+ external: ["vscode"],
33
+ }),
34
+ esbuild.context({
35
+ ...sharedOptions,
36
+ entryPoints: ["src/server.ts"],
37
+ outfile: "build/server.js",
38
+ }),
39
+ ]);
40
+ await Promise.all([ctxExt.watch(), ctxSrv.watch()]);
41
+ console.log("Watching for changes…");
42
+ } else {
43
+ await Promise.all([
44
+ esbuild.build({
45
+ ...sharedOptions,
46
+ entryPoints: ["src/extension.ts"],
47
+ outfile: "build/extension.js",
48
+ external: ["vscode"],
49
+ }),
50
+ esbuild.build({
51
+ ...sharedOptions,
52
+ entryPoints: ["src/server.ts"],
53
+ outfile: "build/server.js",
54
+ }),
55
+ ]);
56
+ console.log("Build complete.");
57
+ }
@@ -0,0 +1,28 @@
1
+ {
2
+ "comments": {
3
+ // symbol used for single line comment. Remove this entry if your language does not support line comments
4
+ "lineComment": "#"
5
+ },
6
+ // symbols used as brackets
7
+ "brackets": [
8
+ ["{", "}"],
9
+ ["[", "]"],
10
+ ["(", ")"]
11
+ ],
12
+ // symbols that are auto closed when typing
13
+ "autoClosingPairs": [
14
+ ["{", "}"],
15
+ ["[", "]"],
16
+ ["(", ")"],
17
+ ["\"", "\""],
18
+ ["'", "'"]
19
+ ],
20
+ // symbols that can be used to surround a selection
21
+ "surroundingPairs": [
22
+ ["{", "}"],
23
+ ["[", "]"],
24
+ ["(", ")"],
25
+ ["\"", "\""],
26
+ ["'", "'"]
27
+ ]
28
+ }
package/logo128.png ADDED
Binary file
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "bridge-syntax-highlight",
3
+ "displayName": "The Bridge Language",
4
+ "description": "Syntax highlighting, real-time diagnostics, and hover information for the Bridge declarative dataflow language",
5
+ "version": "0.8.0",
6
+ "publisher": "stackables",
7
+ "icon": "logo128.png",
8
+ "engines": {
9
+ "vscode": "^1.109.0"
10
+ },
11
+ "categories": [
12
+ "Programming Languages"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/stackables/bridge.git"
17
+ },
18
+ "main": "./build/extension.js",
19
+ "activationEvents": [
20
+ "onLanguage:bridge"
21
+ ],
22
+ "contributes": {
23
+ "languages": [
24
+ {
25
+ "id": "bridge",
26
+ "aliases": [
27
+ "The Bridge",
28
+ "bridge"
29
+ ],
30
+ "extensions": [
31
+ ".bridge"
32
+ ],
33
+ "configuration": "./language-configuration.json"
34
+ }
35
+ ],
36
+ "grammars": [
37
+ {
38
+ "language": "bridge",
39
+ "scopeName": "source.bridge",
40
+ "path": "./syntaxes/bridge.tmLanguage.json"
41
+ }
42
+ ]
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^25.3.2",
46
+ "@types/vscode": "^1.109.0",
47
+ "esbuild": "^0.27.3",
48
+ "typescript": "^5.9.3",
49
+ "vscode-languageclient": "^9.0.1",
50
+ "vscode-languageserver": "^9.0.1",
51
+ "vscode-languageserver-textdocument": "^1.0.12",
52
+ "@stackables/bridge": "1.22.1"
53
+ },
54
+ "scripts": {
55
+ "prebuild": "pnpm --filter @stackables/bridge build",
56
+ "build": "node build.mjs",
57
+ "watch": "node build.mjs --watch",
58
+ "prevscode:prepublish": "pnpm --filter @stackables/bridge build",
59
+ "vscode:prepublish": "node build.mjs"
60
+ }
61
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * VS Code extension client — activates and manages the Bridge Language Server.
3
+ *
4
+ * Runs inside the VS Code extension host. Its only job is to spawn the
5
+ * language server process and wire up the LSP client.
6
+ */
7
+ import * as path from "path";
8
+ import { ExtensionContext } from "vscode";
9
+ import {
10
+ LanguageClient,
11
+ LanguageClientOptions,
12
+ ServerOptions,
13
+ TransportKind,
14
+ } from "vscode-languageclient/node";
15
+
16
+ let client: LanguageClient | undefined;
17
+
18
+ export function activate(context: ExtensionContext): void {
19
+ const serverModule = context.asAbsolutePath(path.join("build", "server.js"));
20
+
21
+ const serverOptions: ServerOptions = {
22
+ run: {
23
+ module: serverModule,
24
+ transport: TransportKind.ipc,
25
+ },
26
+ debug: {
27
+ module: serverModule,
28
+ transport: TransportKind.ipc,
29
+ options: { execArgv: ["--nolazy", "--inspect=6009"] },
30
+ },
31
+ };
32
+
33
+ const clientOptions: LanguageClientOptions = {
34
+ // Only activate for .bridge files
35
+ documentSelector: [{ scheme: "file", language: "bridge" }],
36
+ };
37
+
38
+ client = new LanguageClient(
39
+ "bridgeLanguageServer",
40
+ "Bridge Language Server",
41
+ serverOptions,
42
+ clientOptions,
43
+ );
44
+
45
+ client.start();
46
+ }
47
+
48
+ export function deactivate(): Thenable<void> | undefined {
49
+ if (!client) return undefined;
50
+ return client.stop();
51
+ }
package/src/server.ts ADDED
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Bridge Language Server — provides LSP features for .bridge files.
3
+ *
4
+ * Runs as a standalone Node.js process spawned by the extension client.
5
+ * Communicates via IPC using the Language Server Protocol.
6
+ *
7
+ * This file is a thin adapter: all intelligence logic lives in
8
+ * `BridgeLanguageService` from @stackables/bridge. This server just
9
+ * maps its output to the LSP wire format.
10
+ *
11
+ * Features:
12
+ * • Real-time diagnostics (syntax errors + semantic validation)
13
+ * • Hover information (handle bindings, bridge/tool declarations)
14
+ * • Autocomplete (built-in std.* / math.* tool names)
15
+ */
16
+ import {
17
+ createConnection,
18
+ TextDocuments,
19
+ ProposedFeatures,
20
+ type InitializeResult,
21
+ TextDocumentSyncKind,
22
+ DiagnosticSeverity,
23
+ CompletionItemKind,
24
+ MarkupKind,
25
+ } from "vscode-languageserver/node";
26
+ import { TextDocument } from "vscode-languageserver-textdocument";
27
+ import { BridgeLanguageService } from "@stackables/bridge";
28
+ import type { CompletionKind } from "@stackables/bridge";
29
+
30
+ // ── Connection & document manager ──────────────────────────────────────────
31
+
32
+ const connection = createConnection(ProposedFeatures.all);
33
+ const documents = new TextDocuments(TextDocument);
34
+
35
+ /** One language-service instance per open document (keyed by URI). */
36
+ const services = new Map<string, BridgeLanguageService>();
37
+
38
+ function getService(uri: string): BridgeLanguageService {
39
+ let svc = services.get(uri);
40
+ if (!svc) {
41
+ svc = new BridgeLanguageService();
42
+ services.set(uri, svc);
43
+ }
44
+ return svc;
45
+ }
46
+
47
+ connection.onInitialize(
48
+ (): InitializeResult => ({
49
+ capabilities: {
50
+ textDocumentSync: TextDocumentSyncKind.Incremental,
51
+ hoverProvider: true,
52
+ completionProvider: {
53
+ triggerCharacters: ["."],
54
+ },
55
+ },
56
+ }),
57
+ );
58
+
59
+ // ── Mapping helpers ────────────────────────────────────────────────────────
60
+
61
+ const completionKindMap: Record<CompletionKind, CompletionItemKind> = {
62
+ function: CompletionItemKind.Function,
63
+ variable: CompletionItemKind.Variable,
64
+ keyword: CompletionItemKind.Keyword,
65
+ type: CompletionItemKind.Class,
66
+ };
67
+
68
+ // ── Diagnostics ────────────────────────────────────────────────────────────
69
+
70
+ function validate(doc: TextDocument): void {
71
+ const svc = getService(doc.uri);
72
+ svc.update(doc.getText());
73
+
74
+ const diags = svc.getDiagnostics();
75
+
76
+ connection.sendDiagnostics({
77
+ uri: doc.uri,
78
+ diagnostics: diags.map((d) => ({
79
+ severity:
80
+ d.severity === "error"
81
+ ? DiagnosticSeverity.Error
82
+ : DiagnosticSeverity.Warning,
83
+ range: d.range,
84
+ message: d.message,
85
+ source: "bridge",
86
+ })),
87
+ });
88
+ }
89
+
90
+ documents.onDidChangeContent((change) => validate(change.document));
91
+ documents.onDidOpen((e) => validate(e.document));
92
+ documents.onDidClose((e) => {
93
+ services.delete(e.document.uri);
94
+ connection.sendDiagnostics({ uri: e.document.uri, diagnostics: [] });
95
+ });
96
+
97
+ // ── Hover ──────────────────────────────────────────────────────────────────
98
+
99
+ connection.onHover((params) => {
100
+ const doc = documents.get(params.textDocument.uri);
101
+ if (!doc) return null;
102
+
103
+ const svc = getService(doc.uri);
104
+ svc.update(doc.getText());
105
+
106
+ const hover = svc.getHover(params.position);
107
+ if (!hover) return null;
108
+
109
+ return {
110
+ contents: { kind: MarkupKind.Markdown, value: hover.content },
111
+ ...(hover.range ? { range: hover.range } : {}),
112
+ };
113
+ });
114
+
115
+ // ── Completion ─────────────────────────────────────────────────────────────
116
+
117
+ connection.onCompletion((params) => {
118
+ const doc = documents.get(params.textDocument.uri);
119
+ if (!doc) return [];
120
+
121
+ const svc = getService(doc.uri);
122
+ svc.update(doc.getText());
123
+
124
+ const completions = svc.getCompletions(params.position);
125
+
126
+ return completions.map((c) => ({
127
+ label: c.label,
128
+ kind: completionKindMap[c.kind] ?? CompletionItemKind.Text,
129
+ detail: c.detail,
130
+ documentation: c.documentation,
131
+ }));
132
+ });
133
+
134
+ // ── Start ──────────────────────────────────────────────────────────────────
135
+
136
+ documents.listen(connection);
137
+ connection.listen();
@@ -0,0 +1,279 @@
1
+ {
2
+ "$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
3
+ "name": "bridge",
4
+ "scopeName": "source.bridge",
5
+ "patterns": [
6
+ { "include": "#comments" },
7
+ { "include": "#version-line" },
8
+ { "include": "#block-headers" },
9
+ { "include": "#block-braces" },
10
+ { "include": "#force-lines" },
11
+ { "include": "#wire-lines" },
12
+ { "include": "#const-lines" },
13
+ { "include": "#reserved-handles" },
14
+ { "include": "#strings" },
15
+ { "include": "#numbers" },
16
+ { "include": "#booleans" },
17
+ { "include": "#http-methods" },
18
+ { "include": "#path-values" }
19
+ ],
20
+ "repository": {
21
+
22
+ "version-line": {
23
+ "comment": "version 1.5",
24
+ "match": "^\\s*(version)\\s+(\\d+\\.\\d+)",
25
+ "captures": {
26
+ "1": { "name": "keyword.other.version.bridge" },
27
+ "2": { "name": "constant.numeric.version.bridge" }
28
+ }
29
+ },
30
+
31
+ "block-braces": {
32
+ "comment": "Optional block delimiters { }",
33
+ "patterns": [{
34
+ "name": "punctuation.definition.block.bridge",
35
+ "match": "[{}]"
36
+ }]
37
+ },
38
+
39
+ "comments": {
40
+ "patterns": [{
41
+ "name": "comment.line.number-sign.bridge",
42
+ "match": "#.*$"
43
+ }]
44
+ },
45
+
46
+ "http-methods": {
47
+ "comment": "HTTP method constants used as bare values",
48
+ "match": "\\b(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\\b",
49
+ "name": "constant.language.http-method.bridge"
50
+ },
51
+
52
+ "path-values": {
53
+ "comment": "Unquoted slash-prefixed URL paths used as values (e.g. /search, /forecast)",
54
+ "match": "(?<==\\s*)/[A-Za-z0-9_/-]*",
55
+ "name": "string.unquoted.path.bridge"
56
+ },
57
+
58
+ "reserved-handles": {
59
+ "comment": "Special injected graph handles",
60
+ "match": "\\b(input|output|context)\\b",
61
+ "name": "variable.language.special.bridge"
62
+ },
63
+
64
+ "barewords": {
65
+ "comment": "Uppercase barewords like EUR or USD",
66
+ "match": "\\b[A-Z_][A-Z0-9_]*\\b",
67
+ "name": "constant.other.enum.bridge"
68
+ },
69
+
70
+ "block-headers": {
71
+ "comment": "Lines starting with bridge/define/tool/const/with keywords",
72
+ "patterns": [
73
+ {
74
+ "comment": "bridge Type.field with <target> (inline composition, no braces)",
75
+ "match": "^\\s*(bridge)\\s+([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z_][A-Za-z0-9_]*)(\\s+with\\s+)([A-Za-z_][A-Za-z0-9_.]*)",
76
+ "captures": {
77
+ "1": { "name": "keyword.control.bridge" },
78
+ "2": { "name": "entity.name.type.bridge" },
79
+ "3": { "name": "entity.name.function.bridge" },
80
+ "4": { "name": "keyword.control.bridge" },
81
+ "5": { "name": "entity.name.function.bridge" }
82
+ }
83
+ },
84
+ {
85
+ "comment": "bridge Type.field (block form)",
86
+ "match": "^\\s*(bridge)\\s+([A-Za-z_][A-Za-z0-9_]*)\\.([A-Za-z_][A-Za-z0-9_]*)",
87
+ "captures": {
88
+ "1": { "name": "keyword.control.bridge" },
89
+ "2": { "name": "entity.name.type.bridge" },
90
+ "3": { "name": "entity.name.function.bridge" }
91
+ }
92
+ },
93
+ {
94
+ "comment": "define <name> (reusable sub-graph)",
95
+ "match": "^\\s*(define)\\s+([A-Za-z_][A-Za-z0-9_]*)",
96
+ "captures": {
97
+ "1": { "name": "keyword.control.bridge" },
98
+ "2": { "name": "entity.name.function.bridge" }
99
+ }
100
+ },
101
+ {
102
+ "comment": "tool <name> from <std.source>",
103
+ "match": "^\\s*(tool)\\s+([A-Za-z_][A-Za-z0-9_]*)(\\s+from\\s+)([A-Za-z_][A-Za-z0-9_.]*)",
104
+ "captures": {
105
+ "1": { "name": "keyword.control.bridge" },
106
+ "2": { "name": "variable.other.handle.bridge" },
107
+ "3": { "name": "keyword.control.bridge" },
108
+ "4": { "name": "entity.name.function.bridge" }
109
+ }
110
+ },
111
+ {
112
+ "comment": "const name = ...",
113
+ "match": "^\\s*(const)\\s+([A-Za-z_][A-Za-z0-9_]*)",
114
+ "captures": {
115
+ "1": { "name": "keyword.control.bridge" },
116
+ "2": { "name": "variable.other.constant.bridge" }
117
+ }
118
+ },
119
+ {
120
+ "comment": "with <tool> as <handle> / with input as i",
121
+ "match": "^\\s*(with)\\s+([A-Za-z_][A-Za-z0-9_.]*)(\\s+as\\s+)([A-Za-z_][A-Za-z0-9_]*)",
122
+ "captures": {
123
+ "1": { "name": "keyword.control.bridge" },
124
+ "2": { "name": "entity.name.function.bridge" },
125
+ "3": { "name": "keyword.control.bridge" },
126
+ "4": { "name": "variable.other.handle.bridge" }
127
+ }
128
+ },
129
+ {
130
+ "comment": "with <tool> (no alias)",
131
+ "match": "^\\s*(with)\\s+([A-Za-z_][A-Za-z0-9_.]*)",
132
+ "captures": {
133
+ "1": { "name": "keyword.control.bridge" },
134
+ "2": { "name": "entity.name.function.bridge" }
135
+ }
136
+ },
137
+ {
138
+ "comment": "alias <source> as <name> (node alias / rename)",
139
+ "match": "^\\s*(alias)\\s+(.+?)\\s+(as)\\s+([A-Za-z_][A-Za-z0-9_]*)",
140
+ "captures": {
141
+ "1": { "name": "keyword.control.bridge" },
142
+ "2": { "name": "entity.name.function.bridge" },
143
+ "3": { "name": "keyword.control.bridge" },
144
+ "4": { "name": "variable.other.handle.bridge" }
145
+ }
146
+ },
147
+ {
148
+ "comment": "on error = value / on error <- source",
149
+ "match": "^\\s*(on error)\\s*(<-|=)",
150
+ "captures": {
151
+ "1": { "name": "keyword.control.bridge" },
152
+ "2": { "name": "keyword.operator.wire.bridge" }
153
+ }
154
+ }
155
+ ]
156
+ },
157
+
158
+ "force-lines": {
159
+ "comment": "Force statement: force <handle> [catch null]",
160
+ "match": "^\\s*(force)\\s+([A-Za-z_][A-Za-z0-9_]*)(?:\\s+(catch)\\s+(null))?",
161
+ "captures": {
162
+ "1": { "name": "keyword.control.bridge" },
163
+ "2": { "name": "variable.other.handle.bridge" },
164
+ "3": { "name": "keyword.control.bridge" },
165
+ "4": { "name": "constant.language.null.bridge" }
166
+ }
167
+ },
168
+
169
+ "wire-lines": {
170
+ "comment": "Wire line: target <- source [|| …] [?? …] [catch …] — target may be dot-prefixed (.field) inside array-map blocks",
171
+ "begin": "^(\\s*)(\\.?[A-Za-z_][A-Za-z0-9_.]*)\\s*(<-)",
172
+ "end": "$",
173
+ "beginCaptures": {
174
+ "2": { "name": "variable.other.target.bridge" },
175
+ "3": { "name": "keyword.operator.wire.bridge" }
176
+ },
177
+ "patterns": [
178
+ { "include": "#comments" },
179
+ {
180
+ "comment": "Array mapping: source[] as <alias>",
181
+ "match": "(\\[\\])(\\s+as\\s+)([A-Za-z_][A-Za-z0-9_]*)",
182
+ "captures": {
183
+ "1": { "name": "keyword.operator.array-map.bridge" },
184
+ "2": { "name": "keyword.control.bridge" },
185
+ "3": { "name": "variable.other.handle.bridge" }
186
+ }
187
+ },
188
+ {
189
+ "comment": "Array index [N]",
190
+ "match": "\\[\\d+\\]",
191
+ "name": "keyword.operator.index.bridge"
192
+ },
193
+ {
194
+ "comment": "Pipe separator: colon between identifiers",
195
+ "match": ":(?=[A-Za-z_])",
196
+ "name": "keyword.operator.pipe.bridge"
197
+ },
198
+ {
199
+ "comment": "|| null-coalesce",
200
+ "match": "\\|\\|",
201
+ "name": "keyword.operator.null-coalesce.bridge"
202
+ },
203
+ {
204
+ "comment": "?? nullish gate",
205
+ "match": "\\?\\?",
206
+ "name": "keyword.operator.nullish.bridge"
207
+ },
208
+ {
209
+ "comment": "catch error boundary",
210
+ "match": "\\bcatch\\b",
211
+ "name": "keyword.control.bridge"
212
+ },
213
+ { "include": "#reserved-handles" },
214
+ { "include": "#strings" },
215
+ { "include": "#numbers" },
216
+ { "include": "#booleans" }
217
+ ]
218
+ },
219
+
220
+ "const-lines": {
221
+ "comment": "Property/param assignments: .name = value or name = value. Hyphens allowed in path segments (e.g. .headers.User-Agent).",
222
+ "patterns": [
223
+ {
224
+ "comment": "Dot-prefixed config param: .property[.segment]* = value (hyphens allowed in segments)",
225
+ "begin": "^\\s*(\\.[A-Za-z_][A-Za-z0-9_-]*(?:\\.[A-Za-z_][A-Za-z0-9_-]*)*)\\s*(=)\\s*",
226
+ "end": "$",
227
+ "beginCaptures": {
228
+ "1": { "name": "variable.other.property.bridge" },
229
+ "2": { "name": "keyword.operator.assignment.bridge" }
230
+ },
231
+ "patterns": [
232
+ { "include": "#comments" },
233
+ { "include": "#barewords" },
234
+ { "include": "#strings" },
235
+ { "include": "#numbers" },
236
+ { "include": "#booleans" },
237
+ { "include": "#http-methods" },
238
+ { "include": "#path-values" }
239
+ ]
240
+ },
241
+ {
242
+ "comment": "Regular param: property = value",
243
+ "begin": "^\\s*([A-Za-z_][A-Za-z0-9_.]*)\\s*(=)\\s*",
244
+ "end": "$",
245
+ "beginCaptures": {
246
+ "1": { "name": "variable.other.property.bridge" },
247
+ "2": { "name": "keyword.operator.assignment.bridge" }
248
+ },
249
+ "patterns": [
250
+ { "include": "#comments" },
251
+ { "include": "#barewords" },
252
+ { "include": "#strings" },
253
+ { "include": "#numbers" },
254
+ { "include": "#booleans" },
255
+ { "include": "#http-methods" },
256
+ { "include": "#path-values" }
257
+ ]
258
+ }
259
+ ]
260
+ },
261
+
262
+ "strings": {
263
+ "name": "string.quoted.double.bridge",
264
+ "begin": "\"",
265
+ "end": "\"",
266
+ "patterns": [{ "name": "constant.character.escape.bridge", "match": "\\\\." }]
267
+ },
268
+
269
+ "numbers": {
270
+ "name": "constant.numeric.bridge",
271
+ "match": "-?\\b\\d+(\\.\\d+)?\\b"
272
+ },
273
+
274
+ "booleans": {
275
+ "name": "constant.language.bridge",
276
+ "match": "\\b(true|false|null)\\b"
277
+ }
278
+ }
279
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "commonjs",
4
+ "target": "ES2022",
5
+ "lib": ["ES2022"],
6
+ "outDir": "build",
7
+ "rootDir": "src",
8
+ "strict": true,
9
+ "esModuleInterop": true,
10
+ "sourceMap": true,
11
+ "skipLibCheck": true,
12
+ "moduleResolution": "node"
13
+ },
14
+ "include": ["src"],
15
+ "exclude": ["node_modules", "out"]
16
+ }