@lokascript/domain-flow 2.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/README.md +120 -0
- package/dist/index.cjs +2251 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +643 -0
- package/dist/index.d.ts +643 -0
- package/dist/index.js +2169 -0
- package/dist/index.js.map +1 -0
- package/package.json +66 -0
- package/src/__test__/flow-domain.test.ts +696 -0
- package/src/__test__/hateoas-commands.test.ts +520 -0
- package/src/__test__/htmx-generator.test.ts +100 -0
- package/src/__test__/mcp-workflow-server.test.ts +317 -0
- package/src/__test__/pipeline-parser.test.ts +188 -0
- package/src/__test__/route-extractor.test.ts +94 -0
- package/src/generators/flow-generator.ts +338 -0
- package/src/generators/flow-renderer.ts +262 -0
- package/src/generators/htmx-generator.ts +129 -0
- package/src/generators/route-extractor.ts +105 -0
- package/src/generators/workflow-generator.ts +129 -0
- package/src/index.ts +210 -0
- package/src/parser/pipeline-parser.ts +151 -0
- package/src/profiles/index.ts +186 -0
- package/src/runtime/mcp-workflow-server.ts +409 -0
- package/src/runtime/workflow-executor.ts +171 -0
- package/src/schemas/hateoas-schemas.ts +152 -0
- package/src/schemas/index.ts +320 -0
- package/src/siren-agent.d.ts +14 -0
- package/src/tokenizers/index.ts +592 -0
- package/src/types.ts +108 -0
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FlowScript Tokenizers
|
|
3
|
+
*
|
|
4
|
+
* Language-specific tokenizers for data flow commands (4 languages).
|
|
5
|
+
* Created via the framework's createSimpleTokenizer factory.
|
|
6
|
+
*
|
|
7
|
+
* Custom extractors handle:
|
|
8
|
+
* - CSS selectors (#id, .class)
|
|
9
|
+
* - URL paths (/api/users, /api/user/{id})
|
|
10
|
+
* - Duration literals (5s, 30s, 1m, 500ms)
|
|
11
|
+
* - Latin extended identifiers (diacritics in Spanish)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { createSimpleTokenizer } from '@lokascript/framework';
|
|
15
|
+
import type { LanguageTokenizer, ValueExtractor, ExtractionResult } from '@lokascript/framework';
|
|
16
|
+
|
|
17
|
+
// =============================================================================
|
|
18
|
+
// CSS Selector Extractor (#id, .class)
|
|
19
|
+
// =============================================================================
|
|
20
|
+
|
|
21
|
+
class CSSSelectorExtractor implements ValueExtractor {
|
|
22
|
+
readonly name = 'css-selector';
|
|
23
|
+
|
|
24
|
+
canExtract(input: string, position: number): boolean {
|
|
25
|
+
const char = input[position];
|
|
26
|
+
if (char !== '#' && char !== '.') return false;
|
|
27
|
+
const next = input[position + 1];
|
|
28
|
+
return next !== undefined && /[a-zA-Z_-]/.test(next);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
extract(input: string, position: number): ExtractionResult | null {
|
|
32
|
+
let end = position + 1;
|
|
33
|
+
while (end < input.length && /[a-zA-Z0-9_-]/.test(input[end])) {
|
|
34
|
+
end++;
|
|
35
|
+
}
|
|
36
|
+
if (end === position + 1) return null;
|
|
37
|
+
return { value: input.slice(position, end), length: end - position };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// =============================================================================
|
|
42
|
+
// URL Path Extractor (/api/users, /api/user/{id})
|
|
43
|
+
// =============================================================================
|
|
44
|
+
|
|
45
|
+
class URLPathExtractor implements ValueExtractor {
|
|
46
|
+
readonly name = 'url-path';
|
|
47
|
+
|
|
48
|
+
canExtract(input: string, position: number): boolean {
|
|
49
|
+
if (input[position] !== '/') return false;
|
|
50
|
+
const next = input[position + 1];
|
|
51
|
+
// Must be followed by a letter, digit, or path char — not a space or operator
|
|
52
|
+
return next !== undefined && /[a-zA-Z0-9_:{]/.test(next);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
extract(input: string, position: number): ExtractionResult | null {
|
|
56
|
+
let end = position + 1;
|
|
57
|
+
// URL path characters: letters, digits, /, -, _, ., {, }, :, ?, =, &
|
|
58
|
+
while (end < input.length && /[a-zA-Z0-9/_\-.{}:?=&]/.test(input[end])) {
|
|
59
|
+
end++;
|
|
60
|
+
}
|
|
61
|
+
if (end <= position + 1) return null;
|
|
62
|
+
return { value: input.slice(position, end), length: end - position };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// =============================================================================
|
|
67
|
+
// Duration Extractor (5s, 30s, 1m, 500ms)
|
|
68
|
+
// =============================================================================
|
|
69
|
+
|
|
70
|
+
class DurationExtractor implements ValueExtractor {
|
|
71
|
+
readonly name = 'duration';
|
|
72
|
+
|
|
73
|
+
canExtract(input: string, position: number): boolean {
|
|
74
|
+
return /[0-9]/.test(input[position]);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
extract(input: string, position: number): ExtractionResult | null {
|
|
78
|
+
let end = position;
|
|
79
|
+
// Consume digits
|
|
80
|
+
while (end < input.length && /[0-9]/.test(input[end])) {
|
|
81
|
+
end++;
|
|
82
|
+
}
|
|
83
|
+
if (end === position) return null;
|
|
84
|
+
|
|
85
|
+
// Check for duration suffix: ms, s, m, h
|
|
86
|
+
const remaining = input.slice(end);
|
|
87
|
+
if (remaining.startsWith('ms')) {
|
|
88
|
+
end += 2;
|
|
89
|
+
} else if (/^[smh](?![a-zA-Z])/.test(remaining)) {
|
|
90
|
+
end += 1;
|
|
91
|
+
} else {
|
|
92
|
+
// Plain number — still valid as a literal
|
|
93
|
+
return { value: input.slice(position, end), length: end - position };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return { value: input.slice(position, end), length: end - position };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// =============================================================================
|
|
101
|
+
// Latin Extended Identifier Extractor (diacritics for Spanish)
|
|
102
|
+
// =============================================================================
|
|
103
|
+
|
|
104
|
+
class LatinExtendedIdentifierExtractor implements ValueExtractor {
|
|
105
|
+
readonly name = 'latin-extended-identifier';
|
|
106
|
+
|
|
107
|
+
canExtract(input: string, position: number): boolean {
|
|
108
|
+
return /\p{L}/u.test(input[position]);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
extract(input: string, position: number): ExtractionResult | null {
|
|
112
|
+
let end = position;
|
|
113
|
+
while (end < input.length && /[\p{L}\p{N}_-]/u.test(input[end])) {
|
|
114
|
+
end++;
|
|
115
|
+
}
|
|
116
|
+
if (end === position) return null;
|
|
117
|
+
return { value: input.slice(position, end), length: end - position };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// =============================================================================
|
|
122
|
+
// Shared custom extractors
|
|
123
|
+
// =============================================================================
|
|
124
|
+
|
|
125
|
+
const sharedExtractors = [
|
|
126
|
+
new CSSSelectorExtractor(),
|
|
127
|
+
new URLPathExtractor(),
|
|
128
|
+
new DurationExtractor(),
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
// =============================================================================
|
|
132
|
+
// English FlowScript Tokenizer
|
|
133
|
+
// =============================================================================
|
|
134
|
+
|
|
135
|
+
export const EnglishFlowTokenizer: LanguageTokenizer = createSimpleTokenizer({
|
|
136
|
+
language: 'en',
|
|
137
|
+
customExtractors: sharedExtractors,
|
|
138
|
+
keywords: [
|
|
139
|
+
// Commands
|
|
140
|
+
'fetch',
|
|
141
|
+
'poll',
|
|
142
|
+
'stream',
|
|
143
|
+
'submit',
|
|
144
|
+
'transform',
|
|
145
|
+
// HATEOAS commands
|
|
146
|
+
'enter',
|
|
147
|
+
'follow',
|
|
148
|
+
'perform',
|
|
149
|
+
'capture',
|
|
150
|
+
// Role markers
|
|
151
|
+
'as',
|
|
152
|
+
'into',
|
|
153
|
+
'every',
|
|
154
|
+
'to',
|
|
155
|
+
'with',
|
|
156
|
+
'from',
|
|
157
|
+
'item',
|
|
158
|
+
],
|
|
159
|
+
includeOperators: false,
|
|
160
|
+
caseInsensitive: true,
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
// =============================================================================
|
|
164
|
+
// Spanish FlowScript Tokenizer
|
|
165
|
+
// =============================================================================
|
|
166
|
+
|
|
167
|
+
export const SpanishFlowTokenizer: LanguageTokenizer = createSimpleTokenizer({
|
|
168
|
+
language: 'es',
|
|
169
|
+
customExtractors: [...sharedExtractors, new LatinExtendedIdentifierExtractor()],
|
|
170
|
+
keywords: [
|
|
171
|
+
// Commands
|
|
172
|
+
'obtener',
|
|
173
|
+
'sondear',
|
|
174
|
+
'transmitir',
|
|
175
|
+
'enviar',
|
|
176
|
+
'transformar',
|
|
177
|
+
// HATEOAS commands
|
|
178
|
+
'entrar',
|
|
179
|
+
'seguir',
|
|
180
|
+
'ejecutar',
|
|
181
|
+
'capturar',
|
|
182
|
+
// Role markers
|
|
183
|
+
'como',
|
|
184
|
+
'en',
|
|
185
|
+
'cada',
|
|
186
|
+
'a',
|
|
187
|
+
'con',
|
|
188
|
+
'de',
|
|
189
|
+
'elemento',
|
|
190
|
+
],
|
|
191
|
+
keywordExtras: [
|
|
192
|
+
{ native: 'obtener', normalized: 'fetch' },
|
|
193
|
+
{ native: 'sondear', normalized: 'poll' },
|
|
194
|
+
{ native: 'transmitir', normalized: 'stream' },
|
|
195
|
+
{ native: 'enviar', normalized: 'submit' },
|
|
196
|
+
{ native: 'transformar', normalized: 'transform' },
|
|
197
|
+
{ native: 'entrar', normalized: 'enter' },
|
|
198
|
+
{ native: 'seguir', normalized: 'follow' },
|
|
199
|
+
{ native: 'ejecutar', normalized: 'perform' },
|
|
200
|
+
{ native: 'capturar', normalized: 'capture' },
|
|
201
|
+
{ native: 'como', normalized: 'as' },
|
|
202
|
+
{ native: 'en', normalized: 'into' },
|
|
203
|
+
{ native: 'cada', normalized: 'every' },
|
|
204
|
+
{ native: 'a', normalized: 'to' },
|
|
205
|
+
{ native: 'con', normalized: 'with' },
|
|
206
|
+
{ native: 'elemento', normalized: 'item' },
|
|
207
|
+
],
|
|
208
|
+
keywordProfile: {
|
|
209
|
+
keywords: {
|
|
210
|
+
fetch: { primary: 'obtener' },
|
|
211
|
+
poll: { primary: 'sondear' },
|
|
212
|
+
stream: { primary: 'transmitir' },
|
|
213
|
+
submit: { primary: 'enviar' },
|
|
214
|
+
transform: { primary: 'transformar' },
|
|
215
|
+
enter: { primary: 'entrar' },
|
|
216
|
+
follow: { primary: 'seguir' },
|
|
217
|
+
perform: { primary: 'ejecutar' },
|
|
218
|
+
capture: { primary: 'capturar' },
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
includeOperators: false,
|
|
222
|
+
caseInsensitive: true,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
// =============================================================================
|
|
226
|
+
// Japanese FlowScript Tokenizer
|
|
227
|
+
// =============================================================================
|
|
228
|
+
|
|
229
|
+
export const JapaneseFlowTokenizer: LanguageTokenizer = createSimpleTokenizer({
|
|
230
|
+
language: 'ja',
|
|
231
|
+
customExtractors: sharedExtractors,
|
|
232
|
+
keywords: [
|
|
233
|
+
// Commands
|
|
234
|
+
'取得',
|
|
235
|
+
'ポーリング',
|
|
236
|
+
'ストリーム',
|
|
237
|
+
'送信',
|
|
238
|
+
'変換',
|
|
239
|
+
// HATEOAS commands
|
|
240
|
+
'入る',
|
|
241
|
+
'辿る',
|
|
242
|
+
'実行',
|
|
243
|
+
'取得変数',
|
|
244
|
+
// Role markers / particles
|
|
245
|
+
'で',
|
|
246
|
+
'に',
|
|
247
|
+
'ごとに',
|
|
248
|
+
'を',
|
|
249
|
+
'から',
|
|
250
|
+
'の',
|
|
251
|
+
'として',
|
|
252
|
+
],
|
|
253
|
+
keywordExtras: [
|
|
254
|
+
{ native: '取得', normalized: 'fetch' },
|
|
255
|
+
{ native: 'ポーリング', normalized: 'poll' },
|
|
256
|
+
{ native: 'ストリーム', normalized: 'stream' },
|
|
257
|
+
{ native: '送信', normalized: 'submit' },
|
|
258
|
+
{ native: '変換', normalized: 'transform' },
|
|
259
|
+
{ native: '入る', normalized: 'enter' },
|
|
260
|
+
{ native: '辿る', normalized: 'follow' },
|
|
261
|
+
{ native: '実行', normalized: 'perform' },
|
|
262
|
+
{ native: '取得変数', normalized: 'capture' },
|
|
263
|
+
{ native: 'で', normalized: 'as' },
|
|
264
|
+
{ native: 'に', normalized: 'into' },
|
|
265
|
+
{ native: 'ごとに', normalized: 'every' },
|
|
266
|
+
{ native: 'を', normalized: 'patient' },
|
|
267
|
+
{ native: 'の', normalized: 'item' },
|
|
268
|
+
{ native: 'として', normalized: 'as' },
|
|
269
|
+
],
|
|
270
|
+
keywordProfile: {
|
|
271
|
+
keywords: {
|
|
272
|
+
fetch: { primary: '取得' },
|
|
273
|
+
poll: { primary: 'ポーリング' },
|
|
274
|
+
stream: { primary: 'ストリーム' },
|
|
275
|
+
submit: { primary: '送信' },
|
|
276
|
+
transform: { primary: '変換' },
|
|
277
|
+
enter: { primary: '入る' },
|
|
278
|
+
follow: { primary: '辿る' },
|
|
279
|
+
perform: { primary: '実行' },
|
|
280
|
+
capture: { primary: '取得変数' },
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
includeOperators: false,
|
|
284
|
+
caseInsensitive: false,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
// =============================================================================
|
|
288
|
+
// Arabic FlowScript Tokenizer
|
|
289
|
+
// =============================================================================
|
|
290
|
+
|
|
291
|
+
export const ArabicFlowTokenizer: LanguageTokenizer = createSimpleTokenizer({
|
|
292
|
+
language: 'ar',
|
|
293
|
+
direction: 'rtl',
|
|
294
|
+
customExtractors: sharedExtractors,
|
|
295
|
+
keywords: [
|
|
296
|
+
// Commands
|
|
297
|
+
'جلب',
|
|
298
|
+
'استطلع',
|
|
299
|
+
'بث',
|
|
300
|
+
'أرسل',
|
|
301
|
+
'حوّل',
|
|
302
|
+
// HATEOAS commands
|
|
303
|
+
'ادخل',
|
|
304
|
+
'اتبع',
|
|
305
|
+
'نفّذ',
|
|
306
|
+
'التقط',
|
|
307
|
+
// Role markers
|
|
308
|
+
'ك',
|
|
309
|
+
'في',
|
|
310
|
+
'كل',
|
|
311
|
+
'إلى',
|
|
312
|
+
'ب',
|
|
313
|
+
'من',
|
|
314
|
+
'عنصر',
|
|
315
|
+
],
|
|
316
|
+
keywordExtras: [
|
|
317
|
+
{ native: 'جلب', normalized: 'fetch' },
|
|
318
|
+
{ native: 'استطلع', normalized: 'poll' },
|
|
319
|
+
{ native: 'بث', normalized: 'stream' },
|
|
320
|
+
{ native: 'أرسل', normalized: 'submit' },
|
|
321
|
+
{ native: 'حوّل', normalized: 'transform' },
|
|
322
|
+
{ native: 'ادخل', normalized: 'enter' },
|
|
323
|
+
{ native: 'اتبع', normalized: 'follow' },
|
|
324
|
+
{ native: 'نفّذ', normalized: 'perform' },
|
|
325
|
+
{ native: 'التقط', normalized: 'capture' },
|
|
326
|
+
{ native: 'ك', normalized: 'as' },
|
|
327
|
+
{ native: 'في', normalized: 'into' },
|
|
328
|
+
{ native: 'كل', normalized: 'every' },
|
|
329
|
+
{ native: 'إلى', normalized: 'to' },
|
|
330
|
+
{ native: 'ب', normalized: 'with' },
|
|
331
|
+
{ native: 'عنصر', normalized: 'item' },
|
|
332
|
+
],
|
|
333
|
+
keywordProfile: {
|
|
334
|
+
keywords: {
|
|
335
|
+
fetch: { primary: 'جلب' },
|
|
336
|
+
poll: { primary: 'استطلع' },
|
|
337
|
+
stream: { primary: 'بث' },
|
|
338
|
+
submit: { primary: 'أرسل' },
|
|
339
|
+
transform: { primary: 'حوّل' },
|
|
340
|
+
enter: { primary: 'ادخل' },
|
|
341
|
+
follow: { primary: 'اتبع' },
|
|
342
|
+
perform: { primary: 'نفّذ' },
|
|
343
|
+
capture: { primary: 'التقط' },
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
includeOperators: false,
|
|
347
|
+
caseInsensitive: false,
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
// =============================================================================
|
|
351
|
+
// Korean FlowScript Tokenizer
|
|
352
|
+
// =============================================================================
|
|
353
|
+
|
|
354
|
+
export const KoreanFlowTokenizer: LanguageTokenizer = createSimpleTokenizer({
|
|
355
|
+
language: 'ko',
|
|
356
|
+
customExtractors: sharedExtractors,
|
|
357
|
+
keywords: [
|
|
358
|
+
// Commands
|
|
359
|
+
'가져오기',
|
|
360
|
+
'폴링',
|
|
361
|
+
'스트리밍',
|
|
362
|
+
'제출',
|
|
363
|
+
'변환',
|
|
364
|
+
// HATEOAS commands
|
|
365
|
+
'진입',
|
|
366
|
+
'따라가기',
|
|
367
|
+
'실행',
|
|
368
|
+
'캡처',
|
|
369
|
+
// Role markers / particles
|
|
370
|
+
'로',
|
|
371
|
+
'에',
|
|
372
|
+
'마다',
|
|
373
|
+
'를',
|
|
374
|
+
'에서',
|
|
375
|
+
'항목',
|
|
376
|
+
],
|
|
377
|
+
keywordExtras: [
|
|
378
|
+
{ native: '가져오기', normalized: 'fetch' },
|
|
379
|
+
{ native: '폴링', normalized: 'poll' },
|
|
380
|
+
{ native: '스트리밍', normalized: 'stream' },
|
|
381
|
+
{ native: '제출', normalized: 'submit' },
|
|
382
|
+
{ native: '변환', normalized: 'transform' },
|
|
383
|
+
{ native: '진입', normalized: 'enter' },
|
|
384
|
+
{ native: '따라가기', normalized: 'follow' },
|
|
385
|
+
{ native: '실행', normalized: 'perform' },
|
|
386
|
+
{ native: '캡처', normalized: 'capture' },
|
|
387
|
+
{ native: '로', normalized: 'as' },
|
|
388
|
+
{ native: '에', normalized: 'into' },
|
|
389
|
+
{ native: '마다', normalized: 'every' },
|
|
390
|
+
{ native: '를', normalized: 'patient' },
|
|
391
|
+
{ native: '항목', normalized: 'item' },
|
|
392
|
+
],
|
|
393
|
+
keywordProfile: {
|
|
394
|
+
keywords: {
|
|
395
|
+
fetch: { primary: '가져오기' },
|
|
396
|
+
poll: { primary: '폴링' },
|
|
397
|
+
stream: { primary: '스트리밍' },
|
|
398
|
+
submit: { primary: '제출' },
|
|
399
|
+
transform: { primary: '변환' },
|
|
400
|
+
enter: { primary: '진입' },
|
|
401
|
+
follow: { primary: '따라가기' },
|
|
402
|
+
perform: { primary: '실행' },
|
|
403
|
+
capture: { primary: '캡처' },
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
includeOperators: false,
|
|
407
|
+
caseInsensitive: false,
|
|
408
|
+
});
|
|
409
|
+
|
|
410
|
+
// =============================================================================
|
|
411
|
+
// Chinese FlowScript Tokenizer
|
|
412
|
+
// =============================================================================
|
|
413
|
+
|
|
414
|
+
export const ChineseFlowTokenizer: LanguageTokenizer = createSimpleTokenizer({
|
|
415
|
+
language: 'zh',
|
|
416
|
+
customExtractors: sharedExtractors,
|
|
417
|
+
keywords: [
|
|
418
|
+
// Commands
|
|
419
|
+
'获取',
|
|
420
|
+
'轮询',
|
|
421
|
+
'流式',
|
|
422
|
+
'提交',
|
|
423
|
+
'转换',
|
|
424
|
+
// HATEOAS commands
|
|
425
|
+
'进入',
|
|
426
|
+
'跟随',
|
|
427
|
+
'执行',
|
|
428
|
+
'捕获',
|
|
429
|
+
// Role markers
|
|
430
|
+
'以',
|
|
431
|
+
'到',
|
|
432
|
+
'每',
|
|
433
|
+
'用',
|
|
434
|
+
'从',
|
|
435
|
+
'项',
|
|
436
|
+
'为',
|
|
437
|
+
],
|
|
438
|
+
keywordExtras: [
|
|
439
|
+
{ native: '获取', normalized: 'fetch' },
|
|
440
|
+
{ native: '轮询', normalized: 'poll' },
|
|
441
|
+
{ native: '流式', normalized: 'stream' },
|
|
442
|
+
{ native: '提交', normalized: 'submit' },
|
|
443
|
+
{ native: '转换', normalized: 'transform' },
|
|
444
|
+
{ native: '进入', normalized: 'enter' },
|
|
445
|
+
{ native: '跟随', normalized: 'follow' },
|
|
446
|
+
{ native: '执行', normalized: 'perform' },
|
|
447
|
+
{ native: '捕获', normalized: 'capture' },
|
|
448
|
+
{ native: '以', normalized: 'as' },
|
|
449
|
+
{ native: '到', normalized: 'into' },
|
|
450
|
+
{ native: '每', normalized: 'every' },
|
|
451
|
+
{ native: '用', normalized: 'with' },
|
|
452
|
+
{ native: '项', normalized: 'item' },
|
|
453
|
+
{ native: '为', normalized: 'as' },
|
|
454
|
+
],
|
|
455
|
+
keywordProfile: {
|
|
456
|
+
keywords: {
|
|
457
|
+
fetch: { primary: '获取' },
|
|
458
|
+
poll: { primary: '轮询' },
|
|
459
|
+
stream: { primary: '流式' },
|
|
460
|
+
submit: { primary: '提交' },
|
|
461
|
+
transform: { primary: '转换' },
|
|
462
|
+
enter: { primary: '进入' },
|
|
463
|
+
follow: { primary: '跟随' },
|
|
464
|
+
perform: { primary: '执行' },
|
|
465
|
+
capture: { primary: '捕获' },
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
includeOperators: false,
|
|
469
|
+
caseInsensitive: false,
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
// =============================================================================
|
|
473
|
+
// Turkish FlowScript Tokenizer
|
|
474
|
+
// =============================================================================
|
|
475
|
+
|
|
476
|
+
export const TurkishFlowTokenizer: LanguageTokenizer = createSimpleTokenizer({
|
|
477
|
+
language: 'tr',
|
|
478
|
+
customExtractors: [...sharedExtractors, new LatinExtendedIdentifierExtractor()],
|
|
479
|
+
keywords: [
|
|
480
|
+
// Commands
|
|
481
|
+
'getir',
|
|
482
|
+
'yokla',
|
|
483
|
+
'aktar',
|
|
484
|
+
'gönder',
|
|
485
|
+
'dönüştür',
|
|
486
|
+
// HATEOAS commands
|
|
487
|
+
'gir',
|
|
488
|
+
'izle',
|
|
489
|
+
'yürüt',
|
|
490
|
+
'yakala',
|
|
491
|
+
// Role markers
|
|
492
|
+
'olarak',
|
|
493
|
+
'e',
|
|
494
|
+
'her',
|
|
495
|
+
'ile',
|
|
496
|
+
'dan',
|
|
497
|
+
'öğe',
|
|
498
|
+
],
|
|
499
|
+
keywordExtras: [
|
|
500
|
+
{ native: 'getir', normalized: 'fetch' },
|
|
501
|
+
{ native: 'yokla', normalized: 'poll' },
|
|
502
|
+
{ native: 'aktar', normalized: 'stream' },
|
|
503
|
+
{ native: 'gönder', normalized: 'submit' },
|
|
504
|
+
{ native: 'dönüştür', normalized: 'transform' },
|
|
505
|
+
{ native: 'gir', normalized: 'enter' },
|
|
506
|
+
{ native: 'izle', normalized: 'follow' },
|
|
507
|
+
{ native: 'yürüt', normalized: 'perform' },
|
|
508
|
+
{ native: 'yakala', normalized: 'capture' },
|
|
509
|
+
{ native: 'olarak', normalized: 'as' },
|
|
510
|
+
{ native: 'e', normalized: 'into' },
|
|
511
|
+
{ native: 'her', normalized: 'every' },
|
|
512
|
+
{ native: 'ile', normalized: 'with' },
|
|
513
|
+
{ native: 'öğe', normalized: 'item' },
|
|
514
|
+
],
|
|
515
|
+
keywordProfile: {
|
|
516
|
+
keywords: {
|
|
517
|
+
fetch: { primary: 'getir' },
|
|
518
|
+
poll: { primary: 'yokla' },
|
|
519
|
+
stream: { primary: 'aktar' },
|
|
520
|
+
submit: { primary: 'gönder' },
|
|
521
|
+
transform: { primary: 'dönüştür' },
|
|
522
|
+
enter: { primary: 'gir' },
|
|
523
|
+
follow: { primary: 'izle' },
|
|
524
|
+
perform: { primary: 'yürüt' },
|
|
525
|
+
capture: { primary: 'yakala' },
|
|
526
|
+
},
|
|
527
|
+
},
|
|
528
|
+
includeOperators: false,
|
|
529
|
+
caseInsensitive: true,
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
// =============================================================================
|
|
533
|
+
// French FlowScript Tokenizer
|
|
534
|
+
// =============================================================================
|
|
535
|
+
|
|
536
|
+
export const FrenchFlowTokenizer: LanguageTokenizer = createSimpleTokenizer({
|
|
537
|
+
language: 'fr',
|
|
538
|
+
customExtractors: [...sharedExtractors, new LatinExtendedIdentifierExtractor()],
|
|
539
|
+
keywords: [
|
|
540
|
+
// Commands
|
|
541
|
+
'récupérer',
|
|
542
|
+
'interroger',
|
|
543
|
+
'diffuser',
|
|
544
|
+
'soumettre',
|
|
545
|
+
'transformer',
|
|
546
|
+
// HATEOAS commands
|
|
547
|
+
'entrer',
|
|
548
|
+
'suivre',
|
|
549
|
+
'exécuter',
|
|
550
|
+
'capturer',
|
|
551
|
+
// Role markers
|
|
552
|
+
'comme',
|
|
553
|
+
'dans',
|
|
554
|
+
'chaque',
|
|
555
|
+
'vers',
|
|
556
|
+
'avec',
|
|
557
|
+
'de',
|
|
558
|
+
'élément',
|
|
559
|
+
],
|
|
560
|
+
keywordExtras: [
|
|
561
|
+
{ native: 'récupérer', normalized: 'fetch' },
|
|
562
|
+
{ native: 'interroger', normalized: 'poll' },
|
|
563
|
+
{ native: 'diffuser', normalized: 'stream' },
|
|
564
|
+
{ native: 'soumettre', normalized: 'submit' },
|
|
565
|
+
{ native: 'transformer', normalized: 'transform' },
|
|
566
|
+
{ native: 'entrer', normalized: 'enter' },
|
|
567
|
+
{ native: 'suivre', normalized: 'follow' },
|
|
568
|
+
{ native: 'exécuter', normalized: 'perform' },
|
|
569
|
+
{ native: 'capturer', normalized: 'capture' },
|
|
570
|
+
{ native: 'comme', normalized: 'as' },
|
|
571
|
+
{ native: 'dans', normalized: 'into' },
|
|
572
|
+
{ native: 'chaque', normalized: 'every' },
|
|
573
|
+
{ native: 'vers', normalized: 'to' },
|
|
574
|
+
{ native: 'avec', normalized: 'with' },
|
|
575
|
+
{ native: 'élément', normalized: 'item' },
|
|
576
|
+
],
|
|
577
|
+
keywordProfile: {
|
|
578
|
+
keywords: {
|
|
579
|
+
fetch: { primary: 'récupérer' },
|
|
580
|
+
poll: { primary: 'interroger' },
|
|
581
|
+
stream: { primary: 'diffuser' },
|
|
582
|
+
submit: { primary: 'soumettre' },
|
|
583
|
+
transform: { primary: 'transformer' },
|
|
584
|
+
enter: { primary: 'entrer' },
|
|
585
|
+
follow: { primary: 'suivre' },
|
|
586
|
+
perform: { primary: 'exécuter' },
|
|
587
|
+
capture: { primary: 'capturer' },
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
includeOperators: false,
|
|
591
|
+
caseInsensitive: true,
|
|
592
|
+
});
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FlowScript Domain Types
|
|
3
|
+
*
|
|
4
|
+
* Output types for the FlowScript code generator. FlowSpec captures the
|
|
5
|
+
* semantic intent of a data flow command — URL, method, response format,
|
|
6
|
+
* target element, polling interval — ready for compilation to vanilla JS,
|
|
7
|
+
* HTMX attributes, or route descriptors.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type FlowAction =
|
|
11
|
+
| 'fetch'
|
|
12
|
+
| 'poll'
|
|
13
|
+
| 'stream'
|
|
14
|
+
| 'submit'
|
|
15
|
+
| 'transform'
|
|
16
|
+
| 'enter'
|
|
17
|
+
| 'follow'
|
|
18
|
+
| 'perform'
|
|
19
|
+
| 'capture';
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Structured data flow specification.
|
|
23
|
+
*
|
|
24
|
+
* Output of domain-flow's code generator. Can be used to:
|
|
25
|
+
* - Generate vanilla JS (fetch, EventSource, setInterval)
|
|
26
|
+
* - Generate HTMX attributes (hx-get, hx-trigger, hx-target)
|
|
27
|
+
* - Extract route descriptors for server-bridge
|
|
28
|
+
*/
|
|
29
|
+
export interface FlowSpec {
|
|
30
|
+
/** The command that produced this spec */
|
|
31
|
+
action: FlowAction;
|
|
32
|
+
|
|
33
|
+
/** URL for source commands (fetch, poll, stream, submit destination) */
|
|
34
|
+
url?: string;
|
|
35
|
+
|
|
36
|
+
/** HTTP method inferred from command type */
|
|
37
|
+
method?: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
38
|
+
|
|
39
|
+
/** Response format */
|
|
40
|
+
responseFormat?: 'json' | 'html' | 'text' | 'sse';
|
|
41
|
+
|
|
42
|
+
/** Target CSS selector for DOM insertion */
|
|
43
|
+
target?: string;
|
|
44
|
+
|
|
45
|
+
/** Polling interval in milliseconds (poll command) */
|
|
46
|
+
intervalMs?: number;
|
|
47
|
+
|
|
48
|
+
/** Form selector (submit command) */
|
|
49
|
+
formSelector?: string;
|
|
50
|
+
|
|
51
|
+
/** Transform function or format string (transform command) */
|
|
52
|
+
transformFn?: string;
|
|
53
|
+
|
|
54
|
+
// ----- HATEOAS-specific fields -----
|
|
55
|
+
|
|
56
|
+
/** Link relation name (follow command) */
|
|
57
|
+
linkRel?: string;
|
|
58
|
+
|
|
59
|
+
/** Action name (perform command) */
|
|
60
|
+
actionName?: string;
|
|
61
|
+
|
|
62
|
+
/** Data source selector or inline data (perform command) */
|
|
63
|
+
dataSource?: string;
|
|
64
|
+
|
|
65
|
+
/** Variable name for captured data (capture command) */
|
|
66
|
+
captureAs?: string;
|
|
67
|
+
|
|
68
|
+
/** Property path to capture (capture command) */
|
|
69
|
+
capturePath?: string;
|
|
70
|
+
|
|
71
|
+
/** Metadata for debugging */
|
|
72
|
+
metadata: {
|
|
73
|
+
/** Language the command was written in */
|
|
74
|
+
sourceLanguage: string;
|
|
75
|
+
/** Raw role values extracted from the parsed command */
|
|
76
|
+
roles: Record<string, string | undefined>;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// =============================================================================
|
|
81
|
+
// Workflow Spec — siren-grail compilation target
|
|
82
|
+
// =============================================================================
|
|
83
|
+
|
|
84
|
+
/** A single step in a HATEOAS workflow */
|
|
85
|
+
export type WorkflowStep =
|
|
86
|
+
| { type: 'navigate'; rel: string; capture?: Record<string, string> }
|
|
87
|
+
| {
|
|
88
|
+
type: 'action';
|
|
89
|
+
action: string;
|
|
90
|
+
data?: Record<string, unknown>;
|
|
91
|
+
dataSource?: string;
|
|
92
|
+
capture?: Record<string, string>;
|
|
93
|
+
}
|
|
94
|
+
| { type: 'stop'; result?: string; reason?: string };
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* A complete HATEOAS workflow specification.
|
|
98
|
+
*
|
|
99
|
+
* Compiles to siren-grail's compileWorkflow() step format.
|
|
100
|
+
* Can also drive an MCP server with dynamic tools.
|
|
101
|
+
*/
|
|
102
|
+
export interface WorkflowSpec {
|
|
103
|
+
/** API entry point URL */
|
|
104
|
+
entryPoint: string;
|
|
105
|
+
|
|
106
|
+
/** Ordered workflow steps */
|
|
107
|
+
steps: WorkflowStep[];
|
|
108
|
+
}
|