@almadar/extensions 2.0.0 → 2.0.2

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.
@@ -0,0 +1,213 @@
1
+ /**
2
+ * Virtual Document — The TypeScript Wrapper Trick
3
+ *
4
+ * Wraps .orb JSON content inside a TypeScript file that imports
5
+ * OrbitalSchema from @almadar/core. This lets the TypeScript
6
+ * language server validate the entire .orb structure for free.
7
+ *
8
+ * @packageDocumentation
9
+ */
10
+ /**
11
+ * Number of lines the prefix adds before .orb content begins.
12
+ * Used to offset diagnostics back to original .orb line numbers.
13
+ *
14
+ * The prefix is exactly 2 lines:
15
+ * Line 1: import type { OrbitalSchema } from '@almadar/core';
16
+ * Line 2: const _orbital = ... (the .orb content starts on THIS line)
17
+ *
18
+ * So .orb line 1 corresponds to virtual .ts line 2.
19
+ */
20
+ declare const WRAPPER_LINE_OFFSET = 1;
21
+ /**
22
+ * Number of characters on the prefix line before .orb content begins.
23
+ * `const _orbital = ` is 18 characters.
24
+ */
25
+ declare const WRAPPER_COL_OFFSET = 18;
26
+ /**
27
+ * Wrap raw .orb JSON content into a virtual TypeScript file.
28
+ *
29
+ * @param orbContent - The raw JSON string from the .orb file
30
+ * @returns The complete TypeScript source that tsserver can validate
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * const orbJson = fs.readFileSync('app.orb', 'utf-8');
35
+ * const tsContent = wrapOrbContent(orbJson);
36
+ * // tsContent is now a valid .ts file that TypeScript can check
37
+ * ```
38
+ */
39
+ declare function wrapOrbContent(orbContent: string): string;
40
+ /**
41
+ * Extract the original .orb content from a wrapped virtual TypeScript file.
42
+ *
43
+ * @param virtualContent - The virtual .ts file content
44
+ * @returns The original .orb JSON string, or null if not a valid wrapper
45
+ */
46
+ declare function unwrapOrbContent(virtualContent: string): string | null;
47
+ /**
48
+ * Generate the virtual .ts filename for a given .orb file path.
49
+ *
50
+ * @param orbFilePath - Absolute path to the .orb file
51
+ * @returns The virtual .ts file path (same directory, `.orb.ts` extension)
52
+ *
53
+ * @example
54
+ * ```ts
55
+ * getVirtualPath('/projects/app.orb')
56
+ * // → '/projects/app.orb.ts'
57
+ * ```
58
+ */
59
+ declare function getVirtualPath(orbFilePath: string): string;
60
+
61
+ /**
62
+ * Diagnostic Mapper
63
+ *
64
+ * Maps TypeScript diagnostics from the virtual .ts file back to
65
+ * the original .orb file positions, adjusting for the wrapper prefix.
66
+ *
67
+ * @packageDocumentation
68
+ */
69
+ /**
70
+ * A position in a text document (0-indexed line and character).
71
+ */
72
+ interface Position {
73
+ /** 0-indexed line number */
74
+ line: number;
75
+ /** 0-indexed character offset within the line */
76
+ character: number;
77
+ }
78
+ /**
79
+ * A range in a text document.
80
+ */
81
+ interface Range {
82
+ start: Position;
83
+ end: Position;
84
+ }
85
+ /**
86
+ * Severity levels matching both VSCode and Zed conventions.
87
+ */
88
+ type DiagnosticSeverity = 'error' | 'warning' | 'info' | 'hint';
89
+ /**
90
+ * An editor-agnostic diagnostic — the universal unit that
91
+ * VSCode and Zed extensions translate into their native format.
92
+ */
93
+ interface OrbDiagnostic {
94
+ /** Human-readable error message */
95
+ message: string;
96
+ /** Location in the .orb file (already adjusted from virtual .ts) */
97
+ range: Range;
98
+ /** Severity level */
99
+ severity: DiagnosticSeverity;
100
+ /** TypeScript error code (e.g. 2322, 2353) for filtering */
101
+ code?: number;
102
+ /** Source identifier */
103
+ source: 'almadar-ts';
104
+ }
105
+ /**
106
+ * Raw TypeScript diagnostic (minimal shape matching ts.Diagnostic).
107
+ * Extensions should map their TS server output to this shape.
108
+ */
109
+ interface TsDiagnostic {
110
+ /** 0-indexed line in the virtual .ts file */
111
+ line: number;
112
+ /** 0-indexed character in the virtual .ts file */
113
+ character: number;
114
+ /** End line (optional, defaults to same as line) */
115
+ endLine?: number;
116
+ /** End character (optional) */
117
+ endCharacter?: number;
118
+ /** Error message text */
119
+ messageText: string;
120
+ /** TS error code */
121
+ code?: number;
122
+ /** 1 = error, 2 = warning, 3 = info, 4 = hint */
123
+ category?: number;
124
+ }
125
+ /**
126
+ * Map a virtual .ts file position back to the original .orb position.
127
+ *
128
+ * @param virtualLine - 0-indexed line in the virtual .ts file
129
+ * @param virtualChar - 0-indexed character in the virtual .ts file
130
+ * @returns Adjusted position in the .orb file
131
+ */
132
+ declare function mapPositionToOrb(virtualLine: number, virtualChar: number): Position;
133
+ /**
134
+ * Map a single TypeScript diagnostic to an OrbDiagnostic.
135
+ *
136
+ * @param tsDiag - The raw TypeScript diagnostic
137
+ * @returns An editor-agnostic diagnostic with adjusted positions
138
+ */
139
+ declare function mapDiagnostic(tsDiag: TsDiagnostic): OrbDiagnostic;
140
+ /**
141
+ * Map an array of TypeScript diagnostics to OrbDiagnostics.
142
+ * Filters out diagnostics that fall within the wrapper prefix.
143
+ *
144
+ * @param diagnostics - Array of raw TypeScript diagnostics
145
+ * @returns Array of editor-agnostic diagnostics
146
+ */
147
+ declare function mapDiagnostics(diagnostics: TsDiagnostic[]): OrbDiagnostic[];
148
+
149
+ /**
150
+ * S-Expression Grammar Generator
151
+ *
152
+ * Generates a TextMate injection grammar for S-expression syntax
153
+ * highlighting inside .orb files. The grammar is derived from
154
+ * @almadar/operators — the single source of truth for all operator names.
155
+ *
156
+ * This means new operators automatically get highlighting when
157
+ * the package is rebuilt. No manual grammar maintenance.
158
+ *
159
+ * @packageDocumentation
160
+ */
161
+ /**
162
+ * TextMate grammar rule
163
+ */
164
+ interface TmRule {
165
+ name: string;
166
+ match: string;
167
+ }
168
+ /**
169
+ * TextMate injection grammar structure
170
+ */
171
+ interface TmInjectionGrammar {
172
+ scopeName: string;
173
+ injectionSelector: string;
174
+ patterns: TmRule[];
175
+ }
176
+ /**
177
+ * Generate a TextMate injection grammar for S-expression highlighting.
178
+ *
179
+ * The grammar injects into `.orb` files (JSON scope) and highlights:
180
+ * - **Operators** by category (arithmetic, effect, control, etc.)
181
+ * - **Bindings** (`@entity.field`, `@payload.value`)
182
+ * - **Events** (UPPER_CASE_NAMES)
183
+ *
184
+ * @returns A complete TextMate injection grammar object
185
+ *
186
+ * @example
187
+ * ```ts
188
+ * import { generateSExprGrammar } from '@almadar/extensions';
189
+ * import { writeFileSync } from 'fs';
190
+ *
191
+ * const grammar = generateSExprGrammar();
192
+ * writeFileSync('sexpr.injection.json', JSON.stringify(grammar, null, 2));
193
+ * ```
194
+ */
195
+ declare function generateSExprGrammar(): TmInjectionGrammar;
196
+ /**
197
+ * Serialize the grammar to a formatted JSON string.
198
+ * Ready to be written to `sexpr.injection.json`.
199
+ */
200
+ declare function generateSExprGrammarJson(): string;
201
+ /**
202
+ * Get a flat list of all operator names from the registry.
203
+ * Useful for editor extensions that need the operator list without
204
+ * importing @almadar/operators directly.
205
+ */
206
+ declare function getOperatorNames(): string[];
207
+ /**
208
+ * Get operators grouped by category.
209
+ * Useful for autocomplete that groups suggestions by category.
210
+ */
211
+ declare function getOperatorsByCategory(): Record<string, string[]>;
212
+
213
+ export { type DiagnosticSeverity, type OrbDiagnostic, type Position, type Range, type TmInjectionGrammar, type TsDiagnostic, WRAPPER_COL_OFFSET, WRAPPER_LINE_OFFSET, generateSExprGrammar, generateSExprGrammarJson, getOperatorNames, getOperatorsByCategory, getVirtualPath, mapDiagnostic, mapDiagnostics, mapPositionToOrb, unwrapOrbContent, wrapOrbContent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@almadar/extensions",
3
- "version": "2.0.0",
3
+ "version": "2.0.2",
4
4
  "description": "Editor extension utilities for .orb files — virtual document wrapper and S-expression grammar generator",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -25,7 +25,7 @@
25
25
  "devDependencies": {
26
26
  "tsup": "^8.0.0",
27
27
  "typescript": "^5.4.0",
28
- "tsx": "^4.0.0"
28
+ "@types/node": "^22.0.0"
29
29
  },
30
30
  "repository": {
31
31
  "type": "git",
@@ -43,7 +43,7 @@
43
43
  ],
44
44
  "homepage": "https://github.com/almadar-io/almadar#readme",
45
45
  "scripts": {
46
- "build": "tsup && pnpm run generate-grammar && pnpm run build:editors",
46
+ "build": "tsup && pnpm run generate-grammar && pnpm run build:editors || true",
47
47
  "build:core": "tsup && pnpm run generate-grammar",
48
48
  "build:watch": "tsup --watch",
49
49
  "build:editors": "pnpm run build:lsp && pnpm run build:vscode && pnpm run build:zed",
@@ -1,74 +0,0 @@
1
- {
2
- "scopeName": "source.orb.sexpr-injection",
3
- "injectionSelector": "L:source.orb -comment",
4
- "patterns": [
5
- {
6
- "name": "keyword.operator.arithmetic.sexpr",
7
- "match": "(?<=\")(floor|round|clamp|ceil|abs|min|max|\\+|-|\\*|/|%)(?=\")"
8
- },
9
- {
10
- "name": "keyword.operator.comparison.sexpr",
11
- "match": "(?<=\")(!=|<=|>=|=|<|>)(?=\")"
12
- },
13
- {
14
- "name": "keyword.operator.logical.sexpr",
15
- "match": "(?<=\")(and|not|or|if)(?=\")"
16
- },
17
- {
18
- "name": "keyword.control.sexpr",
19
- "match": "(?<=\")(when|let|do|fn)(?=\")"
20
- },
21
- {
22
- "name": "keyword.other.effect.sexpr",
23
- "match": "(?<=\")(call-service|render-ui|navigate|persist|despawn|notify|spawn|emit|set)(?=\")"
24
- },
25
- {
26
- "name": "support.function.collection.sexpr",
27
- "match": "(?<=\")(includes|contains|average|filter|concat|count|first|empty|find|last|map|sum|nth)(?=\")"
28
- },
29
- {
30
- "name": "support.function.math.sexpr",
31
- "match": "(?<=\")(math/randomInt|math/default|math/random|math/clamp|math/floor|math/round|math/ceil|math/sqrt|math/sign|math/lerp|math/abs|math/min|math/max|math/pow|math/mod|math/map)(?=\")"
32
- },
33
- {
34
- "name": "support.function.string.sexpr",
35
- "match": "(?<=\")(str/startsWith|str/replaceAll|str/capitalize|str/includes|str/endsWith|str/template|str/truncate|str/replace|str/reverse|str/default|str/concat|str/repeat|str/upper|str/lower|str/slice|str/split|str/trim|str/join|str/len)(?=\")"
36
- },
37
- {
38
- "name": "support.function.array.sexpr",
39
- "match": "(?<=\")(array/partition|array/findIndex|array/includes|array/dropLast|array/takeLast|array/reverse|array/flatten|array/groupBy|array/prepend|array/shuffle|array/indexOf|array/filter|array/reduce|array/concat|array/unique|array/append|array/insert|array/remove|array/reject|array/repeat|array/every|array/first|array/slice|array/range|array/find|array/some|array/last|array/sort|array/take|array/drop|array/len|array/map|array/nth|array/sum|array/avg|array/min|array/max|array/zip)(?=\")"
40
- },
41
- {
42
- "name": "support.function.object.sexpr",
43
- "match": "(?<=\")(object/entries|object/values|object/equals|object/filter|object/merge|object/clone|object/keys|object/pick|object/omit|object/get|object/set|object/has)(?=\")"
44
- },
45
- {
46
- "name": "support.function.time.sexpr",
47
- "match": "(?<=\")(time/subtract|time/isBefore|time/isFuture|time/isAfter|time/weekday|time/format|time/isPast|time/minute|time/today|time/parse|time/month|time/diff|time/year|time/hour|time/now|time/add|time/day)(?=\")"
48
- },
49
- {
50
- "name": "support.function.validate.sexpr",
51
- "match": "(?<=\")(validate/minLength|validate/maxLength|validate/required|validate/pattern|validate/length|validate/equals|validate/email|validate/phone|validate/range|validate/oneOf|validate/check|validate/uuid|validate/url|validate/min|validate/max)(?=\")"
52
- },
53
- {
54
- "name": "support.function.format.sexpr",
55
- "match": "(?<=\")(format/currency|format/percent|format/ordinal|format/number|format/plural|format/bytes|format/phone|format/list)(?=\")"
56
- },
57
- {
58
- "name": "support.function.async.sexpr",
59
- "match": "(?<=\")(async/debounce|async/throttle|async/sequence|async/timeout|async/delay|async/retry|async/race|async/all)(?=\")"
60
- },
61
- {
62
- "name": "variable.other.binding.sexpr",
63
- "match": "(?<=\")@(entity|payload|context|config)\\.[a-zA-Z_][a-zA-Z0-9_.]*(?=\")"
64
- },
65
- {
66
- "name": "constant.other.event.sexpr",
67
- "match": "(?<=\")[A-Z][A-Z0-9_]{2,}(?=\")"
68
- },
69
- {
70
- "name": "entity.name.tag.slot.sexpr",
71
- "match": "(?<=\")(main|sidebar|modal|drawer|overlay|center|toast|hud-top|hud-bottom|floating|system)(?=\")"
72
- }
73
- ]
74
- }