@agentix-e/spel-editor 0.1.1 → 1.1.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/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.1.1] - 2026-07-14
6
+
7
+ ### Changed
8
+ - Remove unused imports across source files
9
+ - Add `.prettierrc` configuration for consistent formatting
10
+ - Tighten `tsconfig.json` strictness checks
11
+
12
+ ### Added
13
+ - Playwright browser integration tests (`test:browser` / `test:browser:ui`)
14
+ - nl2spel integration tests with DeepSeek provider
15
+ - `DEEPSEEK_API_KEY` secret handling for integration tests
16
+
5
17
  ## [0.1.0] - 2026-07-12
6
18
 
7
19
  ### Added
@@ -16,4 +28,5 @@ All notable changes to this project will be documented in this file.
16
28
  - `change` event with `value` and `isValid` detail
17
29
  - Framework-agnostic — works with React, Vue, Angular, Svelte, or plain HTML
18
30
 
31
+ [0.1.1]: https://github.com/AgentiX-E/spel-editor/releases/tag/v0.1.1
19
32
  [0.1.0]: https://github.com/AgentiX-E/spel-editor/releases/tag/v0.1.0
package/README.md CHANGED
@@ -85,6 +85,145 @@ npm install @agentix-e/spel-editor
85
85
  | `--spel-border-color` | `#d0d5dd` | Editor border color |
86
86
  | `--spel-border-radius` | `6px` | Editor border radius |
87
87
 
88
+ ## NL Integration — Natural Language → SpEL
89
+
90
+ `@agentix-e/spel-editor` accepts `@agentix-e/nl2spel` as an **optional peer dependency**.
91
+ You can wire nl2spel into the editor to translate natural language input into valid SpEL expressions.
92
+
93
+ ### Pattern Matching (Zero Dependencies, No API Key)
94
+
95
+ nl2spel ships with 63 built-in patterns that cover common SpEL constructs without any LLM call:
96
+
97
+ ```html
98
+ <spel-editor id="editor" min-height="100px"></spel-editor>
99
+
100
+ <script type="module">
101
+ import '@agentix-e/spel-editor';
102
+ import { NL2SpelEngine } from '@agentix-e/nl2spel';
103
+
104
+ const editor = document.querySelector('#editor');
105
+ const engine = new NL2SpelEngine();
106
+
107
+ async function generateSpEL(naturalLanguage) {
108
+ const result = await engine.generate(naturalLanguage, { offlineOnly: true });
109
+ if (result.expression) {
110
+ editor.setValue(result.expression);
111
+ }
112
+ }
113
+
114
+ // Usage:
115
+ await generateSpEL('amount greater than 500');
116
+ // Editor now shows: #amount > 500
117
+ </script>
118
+ ```
119
+
120
+ ### LLM-Powered: DeepSeek / OpenAI-Compatible
121
+
122
+ ```bash
123
+ npm install @agentix-e/nl2spel-openai
124
+ ```
125
+
126
+ ```html
127
+ <spel-editor id="editor" min-height="100px"></spel-editor>
128
+
129
+ <script type="module">
130
+ import '@agentix-e/spel-editor';
131
+ import { NL2SpelEngine } from '@agentix-e/nl2spel';
132
+ import { OpenAICompatibleProvider } from '@agentix-e/nl2spel-openai';
133
+
134
+ const editor = document.querySelector('#editor');
135
+ const engine = new NL2SpelEngine();
136
+
137
+ // Register DeepSeek as the LLM provider
138
+ engine.registerProvider(
139
+ new OpenAICompatibleProvider({
140
+ provider: 'deepseek',
141
+ apiKey: 'sk-your-deepseek-key-here',
142
+ })
143
+ );
144
+
145
+ async function generateSpEL(naturalLanguage, contextSchema) {
146
+ const result = await engine.generate(naturalLanguage, { contextSchema });
147
+ if (result.expression) {
148
+ editor.setValue(result.expression);
149
+ }
150
+ }
151
+
152
+ // With context schema for smarter completions:
153
+ const schema = {
154
+ root: {
155
+ name: 'order',
156
+ type: 'Order',
157
+ fields: {
158
+ amount: { type: 'number', description: 'Order amount' },
159
+ status: { type: 'string', description: 'Order status' },
160
+ },
161
+ methods: {},
162
+ },
163
+ variables: {},
164
+ beans: {},
165
+ types: {},
166
+ functions: {},
167
+ };
168
+
169
+ // Wire the schema for context-aware diagnostics
170
+ editor.contextSchema = schema;
171
+
172
+ // Generate SpEL with full context
173
+ await generateSpEL('orders where amount exceeds one thousand and status is active', schema);
174
+ // Editor shows: #order.amount > 1000 and #order.status == 'active'
175
+ </script>
176
+ ```
177
+
178
+ ### Browser-Local LLM: WebLLM (Gemma)
179
+
180
+ ```bash
181
+ npm install @agentix-e/nl2spel-webllm @mlc-ai/web-llm
182
+ ```
183
+
184
+ ```html
185
+ <spel-editor id="editor" min-height="100px"></spel-editor>
186
+
187
+ <script type="module">
188
+ import '@agentix-e/spel-editor';
189
+ import { NL2SpelEngine } from '@agentix-e/nl2spel';
190
+ import { WebLLMProvider } from '@agentix-e/nl2spel-webllm';
191
+
192
+ const editor = document.querySelector('#editor');
193
+ const engine = new NL2SpelEngine();
194
+
195
+ const provider = new WebLLMProvider({
196
+ modelId: 'gemma-2-2b-it', // 2B parameter model
197
+ maxTokens: 256,
198
+ temperature: 0.1,
199
+ debug: false, // Set true to see model logs
200
+ });
201
+
202
+ await provider.initialize();
203
+ engine.registerProvider(provider);
204
+
205
+ async function generateSpEL(naturalLanguage) {
206
+ const result = await engine.generate(naturalLanguage);
207
+ if (result.expression) {
208
+ editor.setValue(result.expression);
209
+ }
210
+ }
211
+
212
+ // First call loads the model (~1.5 GB download, cached in IndexedDB)
213
+ await generateSpEL('amount greater than 500');
214
+ // Subsequent calls are fast — model stays in memory
215
+ await generateSpEL('status is not null and not empty');
216
+ </script>
217
+ ```
218
+
219
+ ### Integration Pattern Summary
220
+
221
+ | Mode | Package | API Key | Data Leaves Browser | Setup |
222
+ |------|---------|---------|-------------------|-------|
223
+ | **Pattern matching** | `@agentix-e/nl2spel` | ❌ None | ❌ No | `npm install` |
224
+ | **DeepSeek / OpenAI** | `+ nl2spel-openai` | ✅ Required | ✅ Yes | `npm install` + API key |
225
+ | **Browser-local LLM** | `+ nl2spel-webllm` | ❌ None | ❌ No | 1.5 GB model download |
226
+
88
227
  ## License
89
228
 
90
229
  MIT © AgentiX-E
package/dist/index.js CHANGED
@@ -113,64 +113,66 @@ function tokenKindToStyle(kind) {
113
113
  }
114
114
  }
115
115
  function createSpelStreamParser() {
116
+ return StreamLanguage.define(createTokenParser());
117
+ }
118
+ function createTokenParser() {
116
119
  let _tokenizer = null;
117
120
  let _tokens = [];
118
121
  let _tokenIndex = 0;
119
- return StreamLanguage.define({
120
- startState: () => null,
121
- token: (stream) => {
122
- if (!_tokenizer || _tokenIndex === 0) {
123
- _tokenizer = new SpelTokenizer(stream.string);
124
- let rawTokens;
125
- try {
126
- rawTokens = _tokenizer.tokenize();
127
- } catch {
128
- _tokens = [];
129
- _tokenIndex = 0;
130
- stream.skipToEnd();
131
- return null;
132
- }
122
+ const startState = () => null;
123
+ const token = (stream) => {
124
+ if (!_tokenizer || _tokenIndex === 0) {
125
+ _tokenizer = new SpelTokenizer(stream.string);
126
+ let rawTokens;
127
+ try {
128
+ rawTokens = _tokenizer.tokenize();
129
+ } catch {
133
130
  _tokens = [];
134
131
  _tokenIndex = 0;
135
- for (let i = 0; i < rawTokens.length - 1; i++) {
136
- const tok = rawTokens[i];
137
- const style = tokenKindToStyle(tok.kind);
138
- if (tok.kind === TokenKind.IDENTIFIER) {
139
- const prevTok = i > 0 ? rawTokens[i - 1] : null;
140
- if (prevTok?.kind === TokenKind.HASH) {
141
- _tokens.push({
142
- from: tok.startPos,
143
- to: tok.endPos,
144
- style: "variableName"
145
- });
146
- continue;
147
- }
148
- if (prevTok?.kind === TokenKind.DOT || prevTok?.kind === TokenKind.SAFE_NAV) {
149
- _tokens.push({
150
- from: tok.startPos,
151
- to: tok.endPos,
152
- style: "propertyName"
153
- });
154
- continue;
155
- }
132
+ stream.skipToEnd();
133
+ return null;
134
+ }
135
+ _tokens = [];
136
+ _tokenIndex = 0;
137
+ for (let i = 0; i < rawTokens.length - 1; i++) {
138
+ const tok = rawTokens[i];
139
+ const style = tokenKindToStyle(tok.kind);
140
+ if (tok.kind === TokenKind.IDENTIFIER) {
141
+ const prevTok = i > 0 ? rawTokens[i - 1] : null;
142
+ if (prevTok?.kind === TokenKind.HASH) {
143
+ _tokens.push({
144
+ from: tok.startPos,
145
+ to: tok.endPos,
146
+ style: "variableName"
147
+ });
148
+ continue;
149
+ }
150
+ if (prevTok?.kind === TokenKind.DOT || prevTok?.kind === TokenKind.SAFE_NAV) {
151
+ _tokens.push({
152
+ from: tok.startPos,
153
+ to: tok.endPos,
154
+ style: "propertyName"
155
+ });
156
+ continue;
156
157
  }
157
- _tokens.push({ from: tok.startPos, to: tok.endPos, style });
158
158
  }
159
+ _tokens.push({ from: tok.startPos, to: tok.endPos, style });
159
160
  }
160
- while (_tokenIndex < _tokens.length) {
161
- const t = _tokens[_tokenIndex];
162
- if (t.from >= stream.pos) {
163
- stream.pos = t.to;
164
- _tokenIndex++;
165
- return t.style || null;
166
- }
161
+ }
162
+ while (_tokenIndex < _tokens.length) {
163
+ const t = _tokens[_tokenIndex];
164
+ if (t.from >= stream.pos) {
165
+ stream.pos = t.to;
167
166
  _tokenIndex++;
167
+ return t.style || null;
168
168
  }
169
- stream.skipToEnd();
170
- _tokenIndex = 0;
171
- return null;
169
+ _tokenIndex++;
172
170
  }
173
- });
171
+ stream.skipToEnd();
172
+ _tokenIndex = 0;
173
+ return null;
174
+ };
175
+ return { startState, token };
174
176
  }
175
177
 
176
178
  // src/cm6/spel-language.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentix-e/spel-editor",
3
- "version": "0.1.1",
3
+ "version": "1.1.0",
4
4
  "description": "Web-embeddable Spring Expression Language (SpEL) editor — CodeMirror 6 + spel-ts powered",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -20,6 +20,9 @@
20
20
  "scripts": {
21
21
  "build": "tsup src/index.ts --format esm --dts --clean",
22
22
  "test": "vitest run --coverage",
23
+ "test:browser": "playwright test",
24
+ "test:browser:ui": "playwright test --ui",
25
+ "test:all": "pnpm test && pnpm test:browser",
23
26
  "test:watch": "vitest",
24
27
  "typecheck": "tsc --noEmit",
25
28
  "format": "prettier --write 'src/**/*.ts' 'tests/**/*.ts'",
@@ -49,11 +52,15 @@
49
52
  }
50
53
  },
51
54
  "devDependencies": {
55
+ "@agentix-e/nl2spel": "^1.1.2",
56
+ "@agentix-e/nl2spel-openai": "^1.1.2",
57
+ "@playwright/test": "^1.61.1",
52
58
  "@vitest/coverage-v8": "^3.0.0",
53
59
  "jsdom": "^29.1.1",
54
60
  "prettier": "^3.9.5",
55
61
  "tsup": "^8.0.0",
56
62
  "typescript": "^5.8.3",
63
+ "vite": "^8.1.4",
57
64
  "vitest": "^3.0.0"
58
65
  },
59
66
  "keywords": [
@@ -75,6 +82,9 @@
75
82
  "url": "https://github.com/AgentiX-E/spel-editor/issues"
76
83
  },
77
84
  "homepage": "https://github.com/AgentiX-E/spel-editor#readme",
85
+ "pnpm": {
86
+ "onlyBuiltDependencies": ["esbuild"]
87
+ },
78
88
  "publishConfig": {
79
89
  "access": "public"
80
90
  }