@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
package/dist/index.js
ADDED
|
@@ -0,0 +1,2169 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { createMultilingualDSL } from "@lokascript/framework";
|
|
3
|
+
|
|
4
|
+
// src/schemas/index.ts
|
|
5
|
+
import { defineCommand as defineCommand2, defineRole as defineRole2 } from "@lokascript/framework";
|
|
6
|
+
|
|
7
|
+
// src/schemas/hateoas-schemas.ts
|
|
8
|
+
import { defineCommand, defineRole } from "@lokascript/framework";
|
|
9
|
+
var enterSchema = defineCommand({
|
|
10
|
+
action: "enter",
|
|
11
|
+
description: "Connect to a HATEOAS API entry point",
|
|
12
|
+
category: "source",
|
|
13
|
+
primaryRole: "source",
|
|
14
|
+
roles: [
|
|
15
|
+
defineRole({
|
|
16
|
+
role: "source",
|
|
17
|
+
description: "API entry point URL",
|
|
18
|
+
required: true,
|
|
19
|
+
expectedTypes: ["expression"],
|
|
20
|
+
svoPosition: 1,
|
|
21
|
+
sovPosition: 1
|
|
22
|
+
})
|
|
23
|
+
]
|
|
24
|
+
});
|
|
25
|
+
var followSchema = defineCommand({
|
|
26
|
+
action: "follow",
|
|
27
|
+
description: "Navigate to a resource by following a link relation",
|
|
28
|
+
category: "action",
|
|
29
|
+
primaryRole: "patient",
|
|
30
|
+
roles: [
|
|
31
|
+
defineRole({
|
|
32
|
+
role: "patient",
|
|
33
|
+
description: 'Link relation name to follow (e.g., "orders", "next")',
|
|
34
|
+
required: true,
|
|
35
|
+
expectedTypes: ["expression"],
|
|
36
|
+
svoPosition: 2,
|
|
37
|
+
sovPosition: 2
|
|
38
|
+
}),
|
|
39
|
+
defineRole({
|
|
40
|
+
role: "instrument",
|
|
41
|
+
description: "Parameter or sub-resource identifier (e.g., item {id})",
|
|
42
|
+
required: false,
|
|
43
|
+
expectedTypes: ["expression"],
|
|
44
|
+
svoPosition: 1,
|
|
45
|
+
sovPosition: 1,
|
|
46
|
+
markerOverride: {
|
|
47
|
+
en: "item",
|
|
48
|
+
es: "elemento",
|
|
49
|
+
ja: "\u306E",
|
|
50
|
+
ar: "\u0639\u0646\u0635\u0631",
|
|
51
|
+
ko: "\uD56D\uBAA9",
|
|
52
|
+
zh: "\u9879",
|
|
53
|
+
tr: "\xF6\u011Fe",
|
|
54
|
+
fr: "\xE9l\xE9ment"
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
]
|
|
58
|
+
});
|
|
59
|
+
var performSchema = defineCommand({
|
|
60
|
+
action: "perform",
|
|
61
|
+
description: "Execute a server-defined action (Siren affordance)",
|
|
62
|
+
category: "action",
|
|
63
|
+
primaryRole: "patient",
|
|
64
|
+
roles: [
|
|
65
|
+
defineRole({
|
|
66
|
+
role: "patient",
|
|
67
|
+
description: 'Action name to execute (e.g., "create-order", "ship-order")',
|
|
68
|
+
required: true,
|
|
69
|
+
expectedTypes: ["expression"],
|
|
70
|
+
svoPosition: 2,
|
|
71
|
+
sovPosition: 1
|
|
72
|
+
}),
|
|
73
|
+
defineRole({
|
|
74
|
+
role: "source",
|
|
75
|
+
description: "Data source \u2014 form selector or inline data",
|
|
76
|
+
required: false,
|
|
77
|
+
expectedTypes: ["selector", "expression"],
|
|
78
|
+
svoPosition: 1,
|
|
79
|
+
sovPosition: 2,
|
|
80
|
+
markerOverride: {
|
|
81
|
+
en: "with",
|
|
82
|
+
es: "con",
|
|
83
|
+
ja: "\u3067",
|
|
84
|
+
ar: "\u0628",
|
|
85
|
+
ko: "\uB85C",
|
|
86
|
+
zh: "\u7528",
|
|
87
|
+
tr: "ile",
|
|
88
|
+
fr: "avec"
|
|
89
|
+
}
|
|
90
|
+
})
|
|
91
|
+
]
|
|
92
|
+
});
|
|
93
|
+
var captureSchema = defineCommand({
|
|
94
|
+
action: "capture",
|
|
95
|
+
description: "Bind response data to a named variable for later use",
|
|
96
|
+
category: "action",
|
|
97
|
+
primaryRole: "destination",
|
|
98
|
+
roles: [
|
|
99
|
+
defineRole({
|
|
100
|
+
role: "patient",
|
|
101
|
+
description: "Property path to capture (optional \u2014 captures entire entity if omitted)",
|
|
102
|
+
required: false,
|
|
103
|
+
expectedTypes: ["expression"],
|
|
104
|
+
svoPosition: 2,
|
|
105
|
+
sovPosition: 2
|
|
106
|
+
}),
|
|
107
|
+
defineRole({
|
|
108
|
+
role: "destination",
|
|
109
|
+
description: "Variable name to bind the captured value to",
|
|
110
|
+
required: true,
|
|
111
|
+
expectedTypes: ["expression"],
|
|
112
|
+
svoPosition: 1,
|
|
113
|
+
sovPosition: 1,
|
|
114
|
+
markerOverride: {
|
|
115
|
+
en: "as",
|
|
116
|
+
es: "como",
|
|
117
|
+
ja: "\u3068\u3057\u3066",
|
|
118
|
+
ar: "\u0643",
|
|
119
|
+
ko: "\uB85C",
|
|
120
|
+
zh: "\u4E3A",
|
|
121
|
+
tr: "olarak",
|
|
122
|
+
fr: "comme"
|
|
123
|
+
}
|
|
124
|
+
})
|
|
125
|
+
]
|
|
126
|
+
});
|
|
127
|
+
var hateoasSchemas = [enterSchema, followSchema, performSchema, captureSchema];
|
|
128
|
+
|
|
129
|
+
// src/schemas/index.ts
|
|
130
|
+
var fetchSchema = defineCommand2({
|
|
131
|
+
action: "fetch",
|
|
132
|
+
description: "Fetch data from a URL and deliver to a target element",
|
|
133
|
+
category: "source",
|
|
134
|
+
primaryRole: "source",
|
|
135
|
+
roles: [
|
|
136
|
+
defineRole2({
|
|
137
|
+
role: "source",
|
|
138
|
+
description: "URL to fetch from",
|
|
139
|
+
required: true,
|
|
140
|
+
expectedTypes: ["expression"],
|
|
141
|
+
svoPosition: 2,
|
|
142
|
+
sovPosition: 2
|
|
143
|
+
}),
|
|
144
|
+
defineRole2({
|
|
145
|
+
role: "style",
|
|
146
|
+
description: "Response format (json, html, text)",
|
|
147
|
+
required: false,
|
|
148
|
+
expectedTypes: ["expression"],
|
|
149
|
+
svoPosition: 1,
|
|
150
|
+
sovPosition: 1,
|
|
151
|
+
markerOverride: {
|
|
152
|
+
en: "as",
|
|
153
|
+
es: "como",
|
|
154
|
+
ja: "\u3067",
|
|
155
|
+
ar: "\u0643",
|
|
156
|
+
ko: "\uB85C",
|
|
157
|
+
zh: "\u4EE5",
|
|
158
|
+
tr: "olarak",
|
|
159
|
+
fr: "comme"
|
|
160
|
+
}
|
|
161
|
+
}),
|
|
162
|
+
defineRole2({
|
|
163
|
+
role: "destination",
|
|
164
|
+
description: "Target element to deliver data to",
|
|
165
|
+
required: false,
|
|
166
|
+
expectedTypes: ["selector", "expression"],
|
|
167
|
+
svoPosition: 0,
|
|
168
|
+
sovPosition: 0,
|
|
169
|
+
markerOverride: {
|
|
170
|
+
en: "into",
|
|
171
|
+
es: "en",
|
|
172
|
+
ja: "\u306B",
|
|
173
|
+
ar: "\u0641\u064A",
|
|
174
|
+
ko: "\uC5D0",
|
|
175
|
+
zh: "\u5230",
|
|
176
|
+
tr: "e",
|
|
177
|
+
fr: "dans"
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
]
|
|
181
|
+
});
|
|
182
|
+
var pollSchema = defineCommand2({
|
|
183
|
+
action: "poll",
|
|
184
|
+
description: "Repeatedly fetch data at a specified interval",
|
|
185
|
+
category: "source",
|
|
186
|
+
primaryRole: "source",
|
|
187
|
+
roles: [
|
|
188
|
+
defineRole2({
|
|
189
|
+
role: "source",
|
|
190
|
+
description: "URL to poll",
|
|
191
|
+
required: true,
|
|
192
|
+
expectedTypes: ["expression"],
|
|
193
|
+
svoPosition: 4,
|
|
194
|
+
sovPosition: 4
|
|
195
|
+
}),
|
|
196
|
+
defineRole2({
|
|
197
|
+
role: "duration",
|
|
198
|
+
description: "Polling interval (e.g., 5s, 30s, 1m)",
|
|
199
|
+
required: true,
|
|
200
|
+
expectedTypes: ["expression", "literal"],
|
|
201
|
+
svoPosition: 3,
|
|
202
|
+
sovPosition: 3,
|
|
203
|
+
markerOverride: {
|
|
204
|
+
en: "every",
|
|
205
|
+
es: "cada",
|
|
206
|
+
ja: "\u3054\u3068\u306B",
|
|
207
|
+
ar: "\u0643\u0644",
|
|
208
|
+
ko: "\uB9C8\uB2E4",
|
|
209
|
+
zh: "\u6BCF",
|
|
210
|
+
tr: "her",
|
|
211
|
+
fr: "chaque"
|
|
212
|
+
}
|
|
213
|
+
}),
|
|
214
|
+
defineRole2({
|
|
215
|
+
role: "style",
|
|
216
|
+
description: "Response format (json, html, text)",
|
|
217
|
+
required: false,
|
|
218
|
+
expectedTypes: ["expression"],
|
|
219
|
+
svoPosition: 2,
|
|
220
|
+
sovPosition: 2,
|
|
221
|
+
markerOverride: {
|
|
222
|
+
en: "as",
|
|
223
|
+
es: "como",
|
|
224
|
+
ja: "\u3067",
|
|
225
|
+
ar: "\u0643",
|
|
226
|
+
ko: "\uB85C",
|
|
227
|
+
zh: "\u4EE5",
|
|
228
|
+
tr: "olarak",
|
|
229
|
+
fr: "comme"
|
|
230
|
+
}
|
|
231
|
+
}),
|
|
232
|
+
defineRole2({
|
|
233
|
+
role: "destination",
|
|
234
|
+
description: "Target element for poll results",
|
|
235
|
+
required: false,
|
|
236
|
+
expectedTypes: ["selector", "expression"],
|
|
237
|
+
svoPosition: 1,
|
|
238
|
+
sovPosition: 1,
|
|
239
|
+
markerOverride: {
|
|
240
|
+
en: "into",
|
|
241
|
+
es: "en",
|
|
242
|
+
ja: "\u306B",
|
|
243
|
+
ar: "\u0641\u064A",
|
|
244
|
+
ko: "\uC5D0",
|
|
245
|
+
zh: "\u5230",
|
|
246
|
+
tr: "e",
|
|
247
|
+
fr: "dans"
|
|
248
|
+
}
|
|
249
|
+
})
|
|
250
|
+
]
|
|
251
|
+
});
|
|
252
|
+
var streamSchema = defineCommand2({
|
|
253
|
+
action: "stream",
|
|
254
|
+
description: "Open a streaming connection (SSE) to a URL",
|
|
255
|
+
category: "source",
|
|
256
|
+
primaryRole: "source",
|
|
257
|
+
roles: [
|
|
258
|
+
defineRole2({
|
|
259
|
+
role: "source",
|
|
260
|
+
description: "URL for SSE stream",
|
|
261
|
+
required: true,
|
|
262
|
+
expectedTypes: ["expression"],
|
|
263
|
+
svoPosition: 2,
|
|
264
|
+
sovPosition: 2
|
|
265
|
+
}),
|
|
266
|
+
defineRole2({
|
|
267
|
+
role: "style",
|
|
268
|
+
description: "Stream type (sse)",
|
|
269
|
+
required: false,
|
|
270
|
+
expectedTypes: ["expression"],
|
|
271
|
+
svoPosition: 1,
|
|
272
|
+
sovPosition: 1,
|
|
273
|
+
markerOverride: {
|
|
274
|
+
en: "as",
|
|
275
|
+
es: "como",
|
|
276
|
+
ja: "\u3067",
|
|
277
|
+
ar: "\u0643",
|
|
278
|
+
ko: "\uB85C",
|
|
279
|
+
zh: "\u4EE5",
|
|
280
|
+
tr: "olarak",
|
|
281
|
+
fr: "comme"
|
|
282
|
+
}
|
|
283
|
+
}),
|
|
284
|
+
defineRole2({
|
|
285
|
+
role: "destination",
|
|
286
|
+
description: "Target element for streamed data",
|
|
287
|
+
required: false,
|
|
288
|
+
expectedTypes: ["selector", "expression"],
|
|
289
|
+
svoPosition: 0,
|
|
290
|
+
sovPosition: 0,
|
|
291
|
+
markerOverride: {
|
|
292
|
+
en: "into",
|
|
293
|
+
es: "en",
|
|
294
|
+
ja: "\u306B",
|
|
295
|
+
ar: "\u0641\u064A",
|
|
296
|
+
ko: "\uC5D0",
|
|
297
|
+
zh: "\u5230",
|
|
298
|
+
tr: "e",
|
|
299
|
+
fr: "dans"
|
|
300
|
+
}
|
|
301
|
+
})
|
|
302
|
+
]
|
|
303
|
+
});
|
|
304
|
+
var submitSchema = defineCommand2({
|
|
305
|
+
action: "submit",
|
|
306
|
+
description: "Submit form data to a URL",
|
|
307
|
+
category: "action",
|
|
308
|
+
primaryRole: "patient",
|
|
309
|
+
roles: [
|
|
310
|
+
defineRole2({
|
|
311
|
+
role: "patient",
|
|
312
|
+
description: "Form element to submit",
|
|
313
|
+
required: true,
|
|
314
|
+
expectedTypes: ["selector", "expression"],
|
|
315
|
+
svoPosition: 2,
|
|
316
|
+
sovPosition: 1
|
|
317
|
+
}),
|
|
318
|
+
defineRole2({
|
|
319
|
+
role: "destination",
|
|
320
|
+
description: "URL to submit to",
|
|
321
|
+
required: true,
|
|
322
|
+
expectedTypes: ["expression"],
|
|
323
|
+
svoPosition: 1,
|
|
324
|
+
sovPosition: 2,
|
|
325
|
+
markerOverride: {
|
|
326
|
+
en: "to",
|
|
327
|
+
es: "a",
|
|
328
|
+
ja: "\u306B",
|
|
329
|
+
ar: "\u0625\u0644\u0649",
|
|
330
|
+
ko: "\uB85C",
|
|
331
|
+
zh: "\u5230",
|
|
332
|
+
tr: "e",
|
|
333
|
+
fr: "vers"
|
|
334
|
+
}
|
|
335
|
+
}),
|
|
336
|
+
defineRole2({
|
|
337
|
+
role: "style",
|
|
338
|
+
description: "Request encoding (json, form, multipart)",
|
|
339
|
+
required: false,
|
|
340
|
+
expectedTypes: ["expression"],
|
|
341
|
+
svoPosition: 0,
|
|
342
|
+
sovPosition: 0,
|
|
343
|
+
markerOverride: {
|
|
344
|
+
en: "as",
|
|
345
|
+
es: "como",
|
|
346
|
+
ja: "\u3067",
|
|
347
|
+
ar: "\u0643",
|
|
348
|
+
ko: "\uB85C",
|
|
349
|
+
zh: "\u4EE5",
|
|
350
|
+
tr: "olarak",
|
|
351
|
+
fr: "comme"
|
|
352
|
+
}
|
|
353
|
+
})
|
|
354
|
+
]
|
|
355
|
+
});
|
|
356
|
+
var transformSchema = defineCommand2({
|
|
357
|
+
action: "transform",
|
|
358
|
+
description: "Transform data using a function or format string",
|
|
359
|
+
category: "transform",
|
|
360
|
+
primaryRole: "patient",
|
|
361
|
+
roles: [
|
|
362
|
+
defineRole2({
|
|
363
|
+
role: "patient",
|
|
364
|
+
description: "Data to transform",
|
|
365
|
+
required: true,
|
|
366
|
+
expectedTypes: ["expression"],
|
|
367
|
+
svoPosition: 2,
|
|
368
|
+
sovPosition: 1
|
|
369
|
+
}),
|
|
370
|
+
defineRole2({
|
|
371
|
+
role: "instrument",
|
|
372
|
+
description: "Transform function, format string, or mapping",
|
|
373
|
+
required: true,
|
|
374
|
+
expectedTypes: ["expression", "literal"],
|
|
375
|
+
svoPosition: 1,
|
|
376
|
+
sovPosition: 2,
|
|
377
|
+
markerOverride: {
|
|
378
|
+
en: "with",
|
|
379
|
+
es: "con",
|
|
380
|
+
ja: "\u3067",
|
|
381
|
+
ar: "\u0628",
|
|
382
|
+
ko: "\uB85C",
|
|
383
|
+
zh: "\u7528",
|
|
384
|
+
tr: "ile",
|
|
385
|
+
fr: "avec"
|
|
386
|
+
}
|
|
387
|
+
})
|
|
388
|
+
]
|
|
389
|
+
});
|
|
390
|
+
var allSchemas = [
|
|
391
|
+
fetchSchema,
|
|
392
|
+
pollSchema,
|
|
393
|
+
streamSchema,
|
|
394
|
+
submitSchema,
|
|
395
|
+
transformSchema,
|
|
396
|
+
...hateoasSchemas
|
|
397
|
+
];
|
|
398
|
+
|
|
399
|
+
// src/profiles/index.ts
|
|
400
|
+
var englishProfile = {
|
|
401
|
+
code: "en",
|
|
402
|
+
wordOrder: "SVO",
|
|
403
|
+
keywords: {
|
|
404
|
+
fetch: { primary: "fetch" },
|
|
405
|
+
poll: { primary: "poll" },
|
|
406
|
+
stream: { primary: "stream" },
|
|
407
|
+
submit: { primary: "submit" },
|
|
408
|
+
transform: { primary: "transform" },
|
|
409
|
+
enter: { primary: "enter" },
|
|
410
|
+
follow: { primary: "follow" },
|
|
411
|
+
perform: { primary: "perform" },
|
|
412
|
+
capture: { primary: "capture" }
|
|
413
|
+
}
|
|
414
|
+
};
|
|
415
|
+
var spanishProfile = {
|
|
416
|
+
code: "es",
|
|
417
|
+
wordOrder: "SVO",
|
|
418
|
+
keywords: {
|
|
419
|
+
fetch: { primary: "obtener" },
|
|
420
|
+
poll: { primary: "sondear" },
|
|
421
|
+
stream: { primary: "transmitir" },
|
|
422
|
+
submit: { primary: "enviar" },
|
|
423
|
+
transform: { primary: "transformar" },
|
|
424
|
+
enter: { primary: "entrar" },
|
|
425
|
+
follow: { primary: "seguir" },
|
|
426
|
+
perform: { primary: "ejecutar" },
|
|
427
|
+
capture: { primary: "capturar" }
|
|
428
|
+
}
|
|
429
|
+
};
|
|
430
|
+
var japaneseProfile = {
|
|
431
|
+
code: "ja",
|
|
432
|
+
wordOrder: "SOV",
|
|
433
|
+
keywords: {
|
|
434
|
+
fetch: { primary: "\u53D6\u5F97" },
|
|
435
|
+
poll: { primary: "\u30DD\u30FC\u30EA\u30F3\u30B0" },
|
|
436
|
+
stream: { primary: "\u30B9\u30C8\u30EA\u30FC\u30E0" },
|
|
437
|
+
submit: { primary: "\u9001\u4FE1" },
|
|
438
|
+
transform: { primary: "\u5909\u63DB" },
|
|
439
|
+
enter: { primary: "\u5165\u308B" },
|
|
440
|
+
follow: { primary: "\u8FBF\u308B" },
|
|
441
|
+
perform: { primary: "\u5B9F\u884C" },
|
|
442
|
+
capture: { primary: "\u53D6\u5F97\u5909\u6570" }
|
|
443
|
+
}
|
|
444
|
+
};
|
|
445
|
+
var arabicProfile = {
|
|
446
|
+
code: "ar",
|
|
447
|
+
wordOrder: "VSO",
|
|
448
|
+
keywords: {
|
|
449
|
+
fetch: { primary: "\u062C\u0644\u0628" },
|
|
450
|
+
poll: { primary: "\u0627\u0633\u062A\u0637\u0644\u0639" },
|
|
451
|
+
stream: { primary: "\u0628\u062B" },
|
|
452
|
+
submit: { primary: "\u0623\u0631\u0633\u0644" },
|
|
453
|
+
transform: { primary: "\u062D\u0648\u0651\u0644" },
|
|
454
|
+
enter: { primary: "\u0627\u062F\u062E\u0644" },
|
|
455
|
+
follow: { primary: "\u0627\u062A\u0628\u0639" },
|
|
456
|
+
perform: { primary: "\u0646\u0641\u0651\u0630" },
|
|
457
|
+
capture: { primary: "\u0627\u0644\u062A\u0642\u0637" }
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
var koreanProfile = {
|
|
461
|
+
code: "ko",
|
|
462
|
+
wordOrder: "SOV",
|
|
463
|
+
keywords: {
|
|
464
|
+
fetch: { primary: "\uAC00\uC838\uC624\uAE30" },
|
|
465
|
+
poll: { primary: "\uD3F4\uB9C1" },
|
|
466
|
+
stream: { primary: "\uC2A4\uD2B8\uB9AC\uBC0D" },
|
|
467
|
+
submit: { primary: "\uC81C\uCD9C" },
|
|
468
|
+
transform: { primary: "\uBCC0\uD658" },
|
|
469
|
+
enter: { primary: "\uC9C4\uC785" },
|
|
470
|
+
follow: { primary: "\uB530\uB77C\uAC00\uAE30" },
|
|
471
|
+
perform: { primary: "\uC2E4\uD589" },
|
|
472
|
+
capture: { primary: "\uCEA1\uCC98" }
|
|
473
|
+
}
|
|
474
|
+
};
|
|
475
|
+
var chineseProfile = {
|
|
476
|
+
code: "zh",
|
|
477
|
+
wordOrder: "SVO",
|
|
478
|
+
keywords: {
|
|
479
|
+
fetch: { primary: "\u83B7\u53D6" },
|
|
480
|
+
poll: { primary: "\u8F6E\u8BE2" },
|
|
481
|
+
stream: { primary: "\u6D41\u5F0F" },
|
|
482
|
+
submit: { primary: "\u63D0\u4EA4" },
|
|
483
|
+
transform: { primary: "\u8F6C\u6362" },
|
|
484
|
+
enter: { primary: "\u8FDB\u5165" },
|
|
485
|
+
follow: { primary: "\u8DDF\u968F" },
|
|
486
|
+
perform: { primary: "\u6267\u884C" },
|
|
487
|
+
capture: { primary: "\u6355\u83B7" }
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
var turkishProfile = {
|
|
491
|
+
code: "tr",
|
|
492
|
+
wordOrder: "SOV",
|
|
493
|
+
keywords: {
|
|
494
|
+
fetch: { primary: "getir" },
|
|
495
|
+
poll: { primary: "yokla" },
|
|
496
|
+
stream: { primary: "aktar" },
|
|
497
|
+
submit: { primary: "g\xF6nder" },
|
|
498
|
+
transform: { primary: "d\xF6n\xFC\u015Ft\xFCr" },
|
|
499
|
+
enter: { primary: "gir" },
|
|
500
|
+
follow: { primary: "izle" },
|
|
501
|
+
perform: { primary: "y\xFCr\xFCt" },
|
|
502
|
+
capture: { primary: "yakala" }
|
|
503
|
+
}
|
|
504
|
+
};
|
|
505
|
+
var frenchProfile = {
|
|
506
|
+
code: "fr",
|
|
507
|
+
wordOrder: "SVO",
|
|
508
|
+
keywords: {
|
|
509
|
+
fetch: { primary: "r\xE9cup\xE9rer" },
|
|
510
|
+
poll: { primary: "interroger" },
|
|
511
|
+
stream: { primary: "diffuser" },
|
|
512
|
+
submit: { primary: "soumettre" },
|
|
513
|
+
transform: { primary: "transformer" },
|
|
514
|
+
enter: { primary: "entrer" },
|
|
515
|
+
follow: { primary: "suivre" },
|
|
516
|
+
perform: { primary: "ex\xE9cuter" },
|
|
517
|
+
capture: { primary: "capturer" }
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
// src/tokenizers/index.ts
|
|
522
|
+
import { createSimpleTokenizer } from "@lokascript/framework";
|
|
523
|
+
var CSSSelectorExtractor = class {
|
|
524
|
+
name = "css-selector";
|
|
525
|
+
canExtract(input, position) {
|
|
526
|
+
const char = input[position];
|
|
527
|
+
if (char !== "#" && char !== ".") return false;
|
|
528
|
+
const next = input[position + 1];
|
|
529
|
+
return next !== void 0 && /[a-zA-Z_-]/.test(next);
|
|
530
|
+
}
|
|
531
|
+
extract(input, position) {
|
|
532
|
+
let end = position + 1;
|
|
533
|
+
while (end < input.length && /[a-zA-Z0-9_-]/.test(input[end])) {
|
|
534
|
+
end++;
|
|
535
|
+
}
|
|
536
|
+
if (end === position + 1) return null;
|
|
537
|
+
return { value: input.slice(position, end), length: end - position };
|
|
538
|
+
}
|
|
539
|
+
};
|
|
540
|
+
var URLPathExtractor = class {
|
|
541
|
+
name = "url-path";
|
|
542
|
+
canExtract(input, position) {
|
|
543
|
+
if (input[position] !== "/") return false;
|
|
544
|
+
const next = input[position + 1];
|
|
545
|
+
return next !== void 0 && /[a-zA-Z0-9_:{]/.test(next);
|
|
546
|
+
}
|
|
547
|
+
extract(input, position) {
|
|
548
|
+
let end = position + 1;
|
|
549
|
+
while (end < input.length && /[a-zA-Z0-9/_\-.{}:?=&]/.test(input[end])) {
|
|
550
|
+
end++;
|
|
551
|
+
}
|
|
552
|
+
if (end <= position + 1) return null;
|
|
553
|
+
return { value: input.slice(position, end), length: end - position };
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
var DurationExtractor = class {
|
|
557
|
+
name = "duration";
|
|
558
|
+
canExtract(input, position) {
|
|
559
|
+
return /[0-9]/.test(input[position]);
|
|
560
|
+
}
|
|
561
|
+
extract(input, position) {
|
|
562
|
+
let end = position;
|
|
563
|
+
while (end < input.length && /[0-9]/.test(input[end])) {
|
|
564
|
+
end++;
|
|
565
|
+
}
|
|
566
|
+
if (end === position) return null;
|
|
567
|
+
const remaining = input.slice(end);
|
|
568
|
+
if (remaining.startsWith("ms")) {
|
|
569
|
+
end += 2;
|
|
570
|
+
} else if (/^[smh](?![a-zA-Z])/.test(remaining)) {
|
|
571
|
+
end += 1;
|
|
572
|
+
} else {
|
|
573
|
+
return { value: input.slice(position, end), length: end - position };
|
|
574
|
+
}
|
|
575
|
+
return { value: input.slice(position, end), length: end - position };
|
|
576
|
+
}
|
|
577
|
+
};
|
|
578
|
+
var LatinExtendedIdentifierExtractor = class {
|
|
579
|
+
name = "latin-extended-identifier";
|
|
580
|
+
canExtract(input, position) {
|
|
581
|
+
return /\p{L}/u.test(input[position]);
|
|
582
|
+
}
|
|
583
|
+
extract(input, position) {
|
|
584
|
+
let end = position;
|
|
585
|
+
while (end < input.length && /[\p{L}\p{N}_-]/u.test(input[end])) {
|
|
586
|
+
end++;
|
|
587
|
+
}
|
|
588
|
+
if (end === position) return null;
|
|
589
|
+
return { value: input.slice(position, end), length: end - position };
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
var sharedExtractors = [
|
|
593
|
+
new CSSSelectorExtractor(),
|
|
594
|
+
new URLPathExtractor(),
|
|
595
|
+
new DurationExtractor()
|
|
596
|
+
];
|
|
597
|
+
var EnglishFlowTokenizer = createSimpleTokenizer({
|
|
598
|
+
language: "en",
|
|
599
|
+
customExtractors: sharedExtractors,
|
|
600
|
+
keywords: [
|
|
601
|
+
// Commands
|
|
602
|
+
"fetch",
|
|
603
|
+
"poll",
|
|
604
|
+
"stream",
|
|
605
|
+
"submit",
|
|
606
|
+
"transform",
|
|
607
|
+
// HATEOAS commands
|
|
608
|
+
"enter",
|
|
609
|
+
"follow",
|
|
610
|
+
"perform",
|
|
611
|
+
"capture",
|
|
612
|
+
// Role markers
|
|
613
|
+
"as",
|
|
614
|
+
"into",
|
|
615
|
+
"every",
|
|
616
|
+
"to",
|
|
617
|
+
"with",
|
|
618
|
+
"from",
|
|
619
|
+
"item"
|
|
620
|
+
],
|
|
621
|
+
includeOperators: false,
|
|
622
|
+
caseInsensitive: true
|
|
623
|
+
});
|
|
624
|
+
var SpanishFlowTokenizer = createSimpleTokenizer({
|
|
625
|
+
language: "es",
|
|
626
|
+
customExtractors: [...sharedExtractors, new LatinExtendedIdentifierExtractor()],
|
|
627
|
+
keywords: [
|
|
628
|
+
// Commands
|
|
629
|
+
"obtener",
|
|
630
|
+
"sondear",
|
|
631
|
+
"transmitir",
|
|
632
|
+
"enviar",
|
|
633
|
+
"transformar",
|
|
634
|
+
// HATEOAS commands
|
|
635
|
+
"entrar",
|
|
636
|
+
"seguir",
|
|
637
|
+
"ejecutar",
|
|
638
|
+
"capturar",
|
|
639
|
+
// Role markers
|
|
640
|
+
"como",
|
|
641
|
+
"en",
|
|
642
|
+
"cada",
|
|
643
|
+
"a",
|
|
644
|
+
"con",
|
|
645
|
+
"de",
|
|
646
|
+
"elemento"
|
|
647
|
+
],
|
|
648
|
+
keywordExtras: [
|
|
649
|
+
{ native: "obtener", normalized: "fetch" },
|
|
650
|
+
{ native: "sondear", normalized: "poll" },
|
|
651
|
+
{ native: "transmitir", normalized: "stream" },
|
|
652
|
+
{ native: "enviar", normalized: "submit" },
|
|
653
|
+
{ native: "transformar", normalized: "transform" },
|
|
654
|
+
{ native: "entrar", normalized: "enter" },
|
|
655
|
+
{ native: "seguir", normalized: "follow" },
|
|
656
|
+
{ native: "ejecutar", normalized: "perform" },
|
|
657
|
+
{ native: "capturar", normalized: "capture" },
|
|
658
|
+
{ native: "como", normalized: "as" },
|
|
659
|
+
{ native: "en", normalized: "into" },
|
|
660
|
+
{ native: "cada", normalized: "every" },
|
|
661
|
+
{ native: "a", normalized: "to" },
|
|
662
|
+
{ native: "con", normalized: "with" },
|
|
663
|
+
{ native: "elemento", normalized: "item" }
|
|
664
|
+
],
|
|
665
|
+
keywordProfile: {
|
|
666
|
+
keywords: {
|
|
667
|
+
fetch: { primary: "obtener" },
|
|
668
|
+
poll: { primary: "sondear" },
|
|
669
|
+
stream: { primary: "transmitir" },
|
|
670
|
+
submit: { primary: "enviar" },
|
|
671
|
+
transform: { primary: "transformar" },
|
|
672
|
+
enter: { primary: "entrar" },
|
|
673
|
+
follow: { primary: "seguir" },
|
|
674
|
+
perform: { primary: "ejecutar" },
|
|
675
|
+
capture: { primary: "capturar" }
|
|
676
|
+
}
|
|
677
|
+
},
|
|
678
|
+
includeOperators: false,
|
|
679
|
+
caseInsensitive: true
|
|
680
|
+
});
|
|
681
|
+
var JapaneseFlowTokenizer = createSimpleTokenizer({
|
|
682
|
+
language: "ja",
|
|
683
|
+
customExtractors: sharedExtractors,
|
|
684
|
+
keywords: [
|
|
685
|
+
// Commands
|
|
686
|
+
"\u53D6\u5F97",
|
|
687
|
+
"\u30DD\u30FC\u30EA\u30F3\u30B0",
|
|
688
|
+
"\u30B9\u30C8\u30EA\u30FC\u30E0",
|
|
689
|
+
"\u9001\u4FE1",
|
|
690
|
+
"\u5909\u63DB",
|
|
691
|
+
// HATEOAS commands
|
|
692
|
+
"\u5165\u308B",
|
|
693
|
+
"\u8FBF\u308B",
|
|
694
|
+
"\u5B9F\u884C",
|
|
695
|
+
"\u53D6\u5F97\u5909\u6570",
|
|
696
|
+
// Role markers / particles
|
|
697
|
+
"\u3067",
|
|
698
|
+
"\u306B",
|
|
699
|
+
"\u3054\u3068\u306B",
|
|
700
|
+
"\u3092",
|
|
701
|
+
"\u304B\u3089",
|
|
702
|
+
"\u306E",
|
|
703
|
+
"\u3068\u3057\u3066"
|
|
704
|
+
],
|
|
705
|
+
keywordExtras: [
|
|
706
|
+
{ native: "\u53D6\u5F97", normalized: "fetch" },
|
|
707
|
+
{ native: "\u30DD\u30FC\u30EA\u30F3\u30B0", normalized: "poll" },
|
|
708
|
+
{ native: "\u30B9\u30C8\u30EA\u30FC\u30E0", normalized: "stream" },
|
|
709
|
+
{ native: "\u9001\u4FE1", normalized: "submit" },
|
|
710
|
+
{ native: "\u5909\u63DB", normalized: "transform" },
|
|
711
|
+
{ native: "\u5165\u308B", normalized: "enter" },
|
|
712
|
+
{ native: "\u8FBF\u308B", normalized: "follow" },
|
|
713
|
+
{ native: "\u5B9F\u884C", normalized: "perform" },
|
|
714
|
+
{ native: "\u53D6\u5F97\u5909\u6570", normalized: "capture" },
|
|
715
|
+
{ native: "\u3067", normalized: "as" },
|
|
716
|
+
{ native: "\u306B", normalized: "into" },
|
|
717
|
+
{ native: "\u3054\u3068\u306B", normalized: "every" },
|
|
718
|
+
{ native: "\u3092", normalized: "patient" },
|
|
719
|
+
{ native: "\u306E", normalized: "item" },
|
|
720
|
+
{ native: "\u3068\u3057\u3066", normalized: "as" }
|
|
721
|
+
],
|
|
722
|
+
keywordProfile: {
|
|
723
|
+
keywords: {
|
|
724
|
+
fetch: { primary: "\u53D6\u5F97" },
|
|
725
|
+
poll: { primary: "\u30DD\u30FC\u30EA\u30F3\u30B0" },
|
|
726
|
+
stream: { primary: "\u30B9\u30C8\u30EA\u30FC\u30E0" },
|
|
727
|
+
submit: { primary: "\u9001\u4FE1" },
|
|
728
|
+
transform: { primary: "\u5909\u63DB" },
|
|
729
|
+
enter: { primary: "\u5165\u308B" },
|
|
730
|
+
follow: { primary: "\u8FBF\u308B" },
|
|
731
|
+
perform: { primary: "\u5B9F\u884C" },
|
|
732
|
+
capture: { primary: "\u53D6\u5F97\u5909\u6570" }
|
|
733
|
+
}
|
|
734
|
+
},
|
|
735
|
+
includeOperators: false,
|
|
736
|
+
caseInsensitive: false
|
|
737
|
+
});
|
|
738
|
+
var ArabicFlowTokenizer = createSimpleTokenizer({
|
|
739
|
+
language: "ar",
|
|
740
|
+
direction: "rtl",
|
|
741
|
+
customExtractors: sharedExtractors,
|
|
742
|
+
keywords: [
|
|
743
|
+
// Commands
|
|
744
|
+
"\u062C\u0644\u0628",
|
|
745
|
+
"\u0627\u0633\u062A\u0637\u0644\u0639",
|
|
746
|
+
"\u0628\u062B",
|
|
747
|
+
"\u0623\u0631\u0633\u0644",
|
|
748
|
+
"\u062D\u0648\u0651\u0644",
|
|
749
|
+
// HATEOAS commands
|
|
750
|
+
"\u0627\u062F\u062E\u0644",
|
|
751
|
+
"\u0627\u062A\u0628\u0639",
|
|
752
|
+
"\u0646\u0641\u0651\u0630",
|
|
753
|
+
"\u0627\u0644\u062A\u0642\u0637",
|
|
754
|
+
// Role markers
|
|
755
|
+
"\u0643",
|
|
756
|
+
"\u0641\u064A",
|
|
757
|
+
"\u0643\u0644",
|
|
758
|
+
"\u0625\u0644\u0649",
|
|
759
|
+
"\u0628",
|
|
760
|
+
"\u0645\u0646",
|
|
761
|
+
"\u0639\u0646\u0635\u0631"
|
|
762
|
+
],
|
|
763
|
+
keywordExtras: [
|
|
764
|
+
{ native: "\u062C\u0644\u0628", normalized: "fetch" },
|
|
765
|
+
{ native: "\u0627\u0633\u062A\u0637\u0644\u0639", normalized: "poll" },
|
|
766
|
+
{ native: "\u0628\u062B", normalized: "stream" },
|
|
767
|
+
{ native: "\u0623\u0631\u0633\u0644", normalized: "submit" },
|
|
768
|
+
{ native: "\u062D\u0648\u0651\u0644", normalized: "transform" },
|
|
769
|
+
{ native: "\u0627\u062F\u062E\u0644", normalized: "enter" },
|
|
770
|
+
{ native: "\u0627\u062A\u0628\u0639", normalized: "follow" },
|
|
771
|
+
{ native: "\u0646\u0641\u0651\u0630", normalized: "perform" },
|
|
772
|
+
{ native: "\u0627\u0644\u062A\u0642\u0637", normalized: "capture" },
|
|
773
|
+
{ native: "\u0643", normalized: "as" },
|
|
774
|
+
{ native: "\u0641\u064A", normalized: "into" },
|
|
775
|
+
{ native: "\u0643\u0644", normalized: "every" },
|
|
776
|
+
{ native: "\u0625\u0644\u0649", normalized: "to" },
|
|
777
|
+
{ native: "\u0628", normalized: "with" },
|
|
778
|
+
{ native: "\u0639\u0646\u0635\u0631", normalized: "item" }
|
|
779
|
+
],
|
|
780
|
+
keywordProfile: {
|
|
781
|
+
keywords: {
|
|
782
|
+
fetch: { primary: "\u062C\u0644\u0628" },
|
|
783
|
+
poll: { primary: "\u0627\u0633\u062A\u0637\u0644\u0639" },
|
|
784
|
+
stream: { primary: "\u0628\u062B" },
|
|
785
|
+
submit: { primary: "\u0623\u0631\u0633\u0644" },
|
|
786
|
+
transform: { primary: "\u062D\u0648\u0651\u0644" },
|
|
787
|
+
enter: { primary: "\u0627\u062F\u062E\u0644" },
|
|
788
|
+
follow: { primary: "\u0627\u062A\u0628\u0639" },
|
|
789
|
+
perform: { primary: "\u0646\u0641\u0651\u0630" },
|
|
790
|
+
capture: { primary: "\u0627\u0644\u062A\u0642\u0637" }
|
|
791
|
+
}
|
|
792
|
+
},
|
|
793
|
+
includeOperators: false,
|
|
794
|
+
caseInsensitive: false
|
|
795
|
+
});
|
|
796
|
+
var KoreanFlowTokenizer = createSimpleTokenizer({
|
|
797
|
+
language: "ko",
|
|
798
|
+
customExtractors: sharedExtractors,
|
|
799
|
+
keywords: [
|
|
800
|
+
// Commands
|
|
801
|
+
"\uAC00\uC838\uC624\uAE30",
|
|
802
|
+
"\uD3F4\uB9C1",
|
|
803
|
+
"\uC2A4\uD2B8\uB9AC\uBC0D",
|
|
804
|
+
"\uC81C\uCD9C",
|
|
805
|
+
"\uBCC0\uD658",
|
|
806
|
+
// HATEOAS commands
|
|
807
|
+
"\uC9C4\uC785",
|
|
808
|
+
"\uB530\uB77C\uAC00\uAE30",
|
|
809
|
+
"\uC2E4\uD589",
|
|
810
|
+
"\uCEA1\uCC98",
|
|
811
|
+
// Role markers / particles
|
|
812
|
+
"\uB85C",
|
|
813
|
+
"\uC5D0",
|
|
814
|
+
"\uB9C8\uB2E4",
|
|
815
|
+
"\uB97C",
|
|
816
|
+
"\uC5D0\uC11C",
|
|
817
|
+
"\uD56D\uBAA9"
|
|
818
|
+
],
|
|
819
|
+
keywordExtras: [
|
|
820
|
+
{ native: "\uAC00\uC838\uC624\uAE30", normalized: "fetch" },
|
|
821
|
+
{ native: "\uD3F4\uB9C1", normalized: "poll" },
|
|
822
|
+
{ native: "\uC2A4\uD2B8\uB9AC\uBC0D", normalized: "stream" },
|
|
823
|
+
{ native: "\uC81C\uCD9C", normalized: "submit" },
|
|
824
|
+
{ native: "\uBCC0\uD658", normalized: "transform" },
|
|
825
|
+
{ native: "\uC9C4\uC785", normalized: "enter" },
|
|
826
|
+
{ native: "\uB530\uB77C\uAC00\uAE30", normalized: "follow" },
|
|
827
|
+
{ native: "\uC2E4\uD589", normalized: "perform" },
|
|
828
|
+
{ native: "\uCEA1\uCC98", normalized: "capture" },
|
|
829
|
+
{ native: "\uB85C", normalized: "as" },
|
|
830
|
+
{ native: "\uC5D0", normalized: "into" },
|
|
831
|
+
{ native: "\uB9C8\uB2E4", normalized: "every" },
|
|
832
|
+
{ native: "\uB97C", normalized: "patient" },
|
|
833
|
+
{ native: "\uD56D\uBAA9", normalized: "item" }
|
|
834
|
+
],
|
|
835
|
+
keywordProfile: {
|
|
836
|
+
keywords: {
|
|
837
|
+
fetch: { primary: "\uAC00\uC838\uC624\uAE30" },
|
|
838
|
+
poll: { primary: "\uD3F4\uB9C1" },
|
|
839
|
+
stream: { primary: "\uC2A4\uD2B8\uB9AC\uBC0D" },
|
|
840
|
+
submit: { primary: "\uC81C\uCD9C" },
|
|
841
|
+
transform: { primary: "\uBCC0\uD658" },
|
|
842
|
+
enter: { primary: "\uC9C4\uC785" },
|
|
843
|
+
follow: { primary: "\uB530\uB77C\uAC00\uAE30" },
|
|
844
|
+
perform: { primary: "\uC2E4\uD589" },
|
|
845
|
+
capture: { primary: "\uCEA1\uCC98" }
|
|
846
|
+
}
|
|
847
|
+
},
|
|
848
|
+
includeOperators: false,
|
|
849
|
+
caseInsensitive: false
|
|
850
|
+
});
|
|
851
|
+
var ChineseFlowTokenizer = createSimpleTokenizer({
|
|
852
|
+
language: "zh",
|
|
853
|
+
customExtractors: sharedExtractors,
|
|
854
|
+
keywords: [
|
|
855
|
+
// Commands
|
|
856
|
+
"\u83B7\u53D6",
|
|
857
|
+
"\u8F6E\u8BE2",
|
|
858
|
+
"\u6D41\u5F0F",
|
|
859
|
+
"\u63D0\u4EA4",
|
|
860
|
+
"\u8F6C\u6362",
|
|
861
|
+
// HATEOAS commands
|
|
862
|
+
"\u8FDB\u5165",
|
|
863
|
+
"\u8DDF\u968F",
|
|
864
|
+
"\u6267\u884C",
|
|
865
|
+
"\u6355\u83B7",
|
|
866
|
+
// Role markers
|
|
867
|
+
"\u4EE5",
|
|
868
|
+
"\u5230",
|
|
869
|
+
"\u6BCF",
|
|
870
|
+
"\u7528",
|
|
871
|
+
"\u4ECE",
|
|
872
|
+
"\u9879",
|
|
873
|
+
"\u4E3A"
|
|
874
|
+
],
|
|
875
|
+
keywordExtras: [
|
|
876
|
+
{ native: "\u83B7\u53D6", normalized: "fetch" },
|
|
877
|
+
{ native: "\u8F6E\u8BE2", normalized: "poll" },
|
|
878
|
+
{ native: "\u6D41\u5F0F", normalized: "stream" },
|
|
879
|
+
{ native: "\u63D0\u4EA4", normalized: "submit" },
|
|
880
|
+
{ native: "\u8F6C\u6362", normalized: "transform" },
|
|
881
|
+
{ native: "\u8FDB\u5165", normalized: "enter" },
|
|
882
|
+
{ native: "\u8DDF\u968F", normalized: "follow" },
|
|
883
|
+
{ native: "\u6267\u884C", normalized: "perform" },
|
|
884
|
+
{ native: "\u6355\u83B7", normalized: "capture" },
|
|
885
|
+
{ native: "\u4EE5", normalized: "as" },
|
|
886
|
+
{ native: "\u5230", normalized: "into" },
|
|
887
|
+
{ native: "\u6BCF", normalized: "every" },
|
|
888
|
+
{ native: "\u7528", normalized: "with" },
|
|
889
|
+
{ native: "\u9879", normalized: "item" },
|
|
890
|
+
{ native: "\u4E3A", normalized: "as" }
|
|
891
|
+
],
|
|
892
|
+
keywordProfile: {
|
|
893
|
+
keywords: {
|
|
894
|
+
fetch: { primary: "\u83B7\u53D6" },
|
|
895
|
+
poll: { primary: "\u8F6E\u8BE2" },
|
|
896
|
+
stream: { primary: "\u6D41\u5F0F" },
|
|
897
|
+
submit: { primary: "\u63D0\u4EA4" },
|
|
898
|
+
transform: { primary: "\u8F6C\u6362" },
|
|
899
|
+
enter: { primary: "\u8FDB\u5165" },
|
|
900
|
+
follow: { primary: "\u8DDF\u968F" },
|
|
901
|
+
perform: { primary: "\u6267\u884C" },
|
|
902
|
+
capture: { primary: "\u6355\u83B7" }
|
|
903
|
+
}
|
|
904
|
+
},
|
|
905
|
+
includeOperators: false,
|
|
906
|
+
caseInsensitive: false
|
|
907
|
+
});
|
|
908
|
+
var TurkishFlowTokenizer = createSimpleTokenizer({
|
|
909
|
+
language: "tr",
|
|
910
|
+
customExtractors: [...sharedExtractors, new LatinExtendedIdentifierExtractor()],
|
|
911
|
+
keywords: [
|
|
912
|
+
// Commands
|
|
913
|
+
"getir",
|
|
914
|
+
"yokla",
|
|
915
|
+
"aktar",
|
|
916
|
+
"g\xF6nder",
|
|
917
|
+
"d\xF6n\xFC\u015Ft\xFCr",
|
|
918
|
+
// HATEOAS commands
|
|
919
|
+
"gir",
|
|
920
|
+
"izle",
|
|
921
|
+
"y\xFCr\xFCt",
|
|
922
|
+
"yakala",
|
|
923
|
+
// Role markers
|
|
924
|
+
"olarak",
|
|
925
|
+
"e",
|
|
926
|
+
"her",
|
|
927
|
+
"ile",
|
|
928
|
+
"dan",
|
|
929
|
+
"\xF6\u011Fe"
|
|
930
|
+
],
|
|
931
|
+
keywordExtras: [
|
|
932
|
+
{ native: "getir", normalized: "fetch" },
|
|
933
|
+
{ native: "yokla", normalized: "poll" },
|
|
934
|
+
{ native: "aktar", normalized: "stream" },
|
|
935
|
+
{ native: "g\xF6nder", normalized: "submit" },
|
|
936
|
+
{ native: "d\xF6n\xFC\u015Ft\xFCr", normalized: "transform" },
|
|
937
|
+
{ native: "gir", normalized: "enter" },
|
|
938
|
+
{ native: "izle", normalized: "follow" },
|
|
939
|
+
{ native: "y\xFCr\xFCt", normalized: "perform" },
|
|
940
|
+
{ native: "yakala", normalized: "capture" },
|
|
941
|
+
{ native: "olarak", normalized: "as" },
|
|
942
|
+
{ native: "e", normalized: "into" },
|
|
943
|
+
{ native: "her", normalized: "every" },
|
|
944
|
+
{ native: "ile", normalized: "with" },
|
|
945
|
+
{ native: "\xF6\u011Fe", normalized: "item" }
|
|
946
|
+
],
|
|
947
|
+
keywordProfile: {
|
|
948
|
+
keywords: {
|
|
949
|
+
fetch: { primary: "getir" },
|
|
950
|
+
poll: { primary: "yokla" },
|
|
951
|
+
stream: { primary: "aktar" },
|
|
952
|
+
submit: { primary: "g\xF6nder" },
|
|
953
|
+
transform: { primary: "d\xF6n\xFC\u015Ft\xFCr" },
|
|
954
|
+
enter: { primary: "gir" },
|
|
955
|
+
follow: { primary: "izle" },
|
|
956
|
+
perform: { primary: "y\xFCr\xFCt" },
|
|
957
|
+
capture: { primary: "yakala" }
|
|
958
|
+
}
|
|
959
|
+
},
|
|
960
|
+
includeOperators: false,
|
|
961
|
+
caseInsensitive: true
|
|
962
|
+
});
|
|
963
|
+
var FrenchFlowTokenizer = createSimpleTokenizer({
|
|
964
|
+
language: "fr",
|
|
965
|
+
customExtractors: [...sharedExtractors, new LatinExtendedIdentifierExtractor()],
|
|
966
|
+
keywords: [
|
|
967
|
+
// Commands
|
|
968
|
+
"r\xE9cup\xE9rer",
|
|
969
|
+
"interroger",
|
|
970
|
+
"diffuser",
|
|
971
|
+
"soumettre",
|
|
972
|
+
"transformer",
|
|
973
|
+
// HATEOAS commands
|
|
974
|
+
"entrer",
|
|
975
|
+
"suivre",
|
|
976
|
+
"ex\xE9cuter",
|
|
977
|
+
"capturer",
|
|
978
|
+
// Role markers
|
|
979
|
+
"comme",
|
|
980
|
+
"dans",
|
|
981
|
+
"chaque",
|
|
982
|
+
"vers",
|
|
983
|
+
"avec",
|
|
984
|
+
"de",
|
|
985
|
+
"\xE9l\xE9ment"
|
|
986
|
+
],
|
|
987
|
+
keywordExtras: [
|
|
988
|
+
{ native: "r\xE9cup\xE9rer", normalized: "fetch" },
|
|
989
|
+
{ native: "interroger", normalized: "poll" },
|
|
990
|
+
{ native: "diffuser", normalized: "stream" },
|
|
991
|
+
{ native: "soumettre", normalized: "submit" },
|
|
992
|
+
{ native: "transformer", normalized: "transform" },
|
|
993
|
+
{ native: "entrer", normalized: "enter" },
|
|
994
|
+
{ native: "suivre", normalized: "follow" },
|
|
995
|
+
{ native: "ex\xE9cuter", normalized: "perform" },
|
|
996
|
+
{ native: "capturer", normalized: "capture" },
|
|
997
|
+
{ native: "comme", normalized: "as" },
|
|
998
|
+
{ native: "dans", normalized: "into" },
|
|
999
|
+
{ native: "chaque", normalized: "every" },
|
|
1000
|
+
{ native: "vers", normalized: "to" },
|
|
1001
|
+
{ native: "avec", normalized: "with" },
|
|
1002
|
+
{ native: "\xE9l\xE9ment", normalized: "item" }
|
|
1003
|
+
],
|
|
1004
|
+
keywordProfile: {
|
|
1005
|
+
keywords: {
|
|
1006
|
+
fetch: { primary: "r\xE9cup\xE9rer" },
|
|
1007
|
+
poll: { primary: "interroger" },
|
|
1008
|
+
stream: { primary: "diffuser" },
|
|
1009
|
+
submit: { primary: "soumettre" },
|
|
1010
|
+
transform: { primary: "transformer" },
|
|
1011
|
+
enter: { primary: "entrer" },
|
|
1012
|
+
follow: { primary: "suivre" },
|
|
1013
|
+
perform: { primary: "ex\xE9cuter" },
|
|
1014
|
+
capture: { primary: "capturer" }
|
|
1015
|
+
}
|
|
1016
|
+
},
|
|
1017
|
+
includeOperators: false,
|
|
1018
|
+
caseInsensitive: true
|
|
1019
|
+
});
|
|
1020
|
+
|
|
1021
|
+
// src/generators/flow-generator.ts
|
|
1022
|
+
import { extractRoleValue } from "@lokascript/framework";
|
|
1023
|
+
function parseDuration(duration) {
|
|
1024
|
+
const match = duration.match(/^(\d+)(ms|s|m|h)?$/);
|
|
1025
|
+
if (!match) return 0;
|
|
1026
|
+
const value = parseInt(match[1], 10);
|
|
1027
|
+
switch (match[2]) {
|
|
1028
|
+
case "h":
|
|
1029
|
+
return value * 36e5;
|
|
1030
|
+
case "m":
|
|
1031
|
+
return value * 6e4;
|
|
1032
|
+
case "s":
|
|
1033
|
+
return value * 1e3;
|
|
1034
|
+
case "ms":
|
|
1035
|
+
return value;
|
|
1036
|
+
default:
|
|
1037
|
+
return value;
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
function toFlowSpec(node, language) {
|
|
1041
|
+
const action = node.action;
|
|
1042
|
+
const roles = {};
|
|
1043
|
+
for (const [key, val] of node.roles) {
|
|
1044
|
+
roles[key] = typeof val === "string" ? val : val?.value ?? String(val);
|
|
1045
|
+
}
|
|
1046
|
+
const base = {
|
|
1047
|
+
action,
|
|
1048
|
+
metadata: { sourceLanguage: language, roles }
|
|
1049
|
+
};
|
|
1050
|
+
switch (action) {
|
|
1051
|
+
case "fetch": {
|
|
1052
|
+
base.url = extractRoleValue(node, "source") || void 0;
|
|
1053
|
+
base.responseFormat = normalizeFormat(extractRoleValue(node, "style"));
|
|
1054
|
+
base.target = extractRoleValue(node, "destination") || void 0;
|
|
1055
|
+
base.method = "GET";
|
|
1056
|
+
break;
|
|
1057
|
+
}
|
|
1058
|
+
case "poll": {
|
|
1059
|
+
base.url = extractRoleValue(node, "source") || void 0;
|
|
1060
|
+
base.responseFormat = normalizeFormat(extractRoleValue(node, "style"));
|
|
1061
|
+
base.target = extractRoleValue(node, "destination") || void 0;
|
|
1062
|
+
base.method = "GET";
|
|
1063
|
+
const dur = extractRoleValue(node, "duration");
|
|
1064
|
+
if (dur) base.intervalMs = parseDuration(dur);
|
|
1065
|
+
break;
|
|
1066
|
+
}
|
|
1067
|
+
case "stream": {
|
|
1068
|
+
base.url = extractRoleValue(node, "source") || void 0;
|
|
1069
|
+
base.responseFormat = "sse";
|
|
1070
|
+
base.target = extractRoleValue(node, "destination") || void 0;
|
|
1071
|
+
base.method = "GET";
|
|
1072
|
+
break;
|
|
1073
|
+
}
|
|
1074
|
+
case "submit": {
|
|
1075
|
+
base.formSelector = extractRoleValue(node, "patient") || void 0;
|
|
1076
|
+
base.url = extractRoleValue(node, "destination") || void 0;
|
|
1077
|
+
base.responseFormat = normalizeFormat(extractRoleValue(node, "style"));
|
|
1078
|
+
base.method = "POST";
|
|
1079
|
+
break;
|
|
1080
|
+
}
|
|
1081
|
+
case "transform": {
|
|
1082
|
+
base.transformFn = extractRoleValue(node, "instrument") || void 0;
|
|
1083
|
+
break;
|
|
1084
|
+
}
|
|
1085
|
+
case "enter": {
|
|
1086
|
+
base.url = extractRoleValue(node, "source") || void 0;
|
|
1087
|
+
break;
|
|
1088
|
+
}
|
|
1089
|
+
case "follow": {
|
|
1090
|
+
base.linkRel = extractRoleValue(node, "patient") || void 0;
|
|
1091
|
+
break;
|
|
1092
|
+
}
|
|
1093
|
+
case "perform": {
|
|
1094
|
+
base.actionName = extractRoleValue(node, "patient") || void 0;
|
|
1095
|
+
base.dataSource = extractRoleValue(node, "source") || void 0;
|
|
1096
|
+
break;
|
|
1097
|
+
}
|
|
1098
|
+
case "capture": {
|
|
1099
|
+
base.captureAs = extractRoleValue(node, "destination") || void 0;
|
|
1100
|
+
base.capturePath = extractRoleValue(node, "patient") || void 0;
|
|
1101
|
+
break;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
return base;
|
|
1105
|
+
}
|
|
1106
|
+
function normalizeFormat(format) {
|
|
1107
|
+
if (!format) return void 0;
|
|
1108
|
+
const lower = format.toLowerCase();
|
|
1109
|
+
if (lower === "json") return "json";
|
|
1110
|
+
if (lower === "html") return "html";
|
|
1111
|
+
if (lower === "text") return "text";
|
|
1112
|
+
if (lower === "sse") return "sse";
|
|
1113
|
+
return void 0;
|
|
1114
|
+
}
|
|
1115
|
+
function escapeStr(s) {
|
|
1116
|
+
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
1117
|
+
}
|
|
1118
|
+
function generateFetch(node) {
|
|
1119
|
+
const url = extractRoleValue(node, "source") || "/";
|
|
1120
|
+
const format = extractRoleValue(node, "style")?.toLowerCase() || "text";
|
|
1121
|
+
const target = extractRoleValue(node, "destination");
|
|
1122
|
+
const parseMethod = format === "json" ? ".json()" : ".text()";
|
|
1123
|
+
const lines = [`fetch('${escapeStr(url)}')`, ` .then(r => r${parseMethod})`];
|
|
1124
|
+
if (target) {
|
|
1125
|
+
lines.push(` .then(data => {`);
|
|
1126
|
+
if (format === "json") {
|
|
1127
|
+
lines.push(
|
|
1128
|
+
` document.querySelector('${escapeStr(target)}').innerHTML = typeof data === 'string' ? data : JSON.stringify(data);`
|
|
1129
|
+
);
|
|
1130
|
+
} else {
|
|
1131
|
+
lines.push(` document.querySelector('${escapeStr(target)}').innerHTML = data;`);
|
|
1132
|
+
}
|
|
1133
|
+
lines.push(` })`);
|
|
1134
|
+
}
|
|
1135
|
+
lines.push(` .catch(err => console.error('Fetch error:', err));`);
|
|
1136
|
+
return lines.join("\n");
|
|
1137
|
+
}
|
|
1138
|
+
function generatePoll(node) {
|
|
1139
|
+
const url = extractRoleValue(node, "source") || "/";
|
|
1140
|
+
const duration = extractRoleValue(node, "duration") || "5s";
|
|
1141
|
+
const format = extractRoleValue(node, "style")?.toLowerCase() || "text";
|
|
1142
|
+
const target = extractRoleValue(node, "destination");
|
|
1143
|
+
const ms = parseDuration(duration);
|
|
1144
|
+
const parseMethod = format === "json" ? ".json()" : ".text()";
|
|
1145
|
+
const lines = [
|
|
1146
|
+
`setInterval(async () => {`,
|
|
1147
|
+
` try {`,
|
|
1148
|
+
` const r = await fetch('${escapeStr(url)}');`,
|
|
1149
|
+
` const data = await r${parseMethod};`
|
|
1150
|
+
];
|
|
1151
|
+
if (target) {
|
|
1152
|
+
if (format === "json") {
|
|
1153
|
+
lines.push(
|
|
1154
|
+
` document.querySelector('${escapeStr(target)}').innerHTML = typeof data === 'string' ? data : JSON.stringify(data);`
|
|
1155
|
+
);
|
|
1156
|
+
} else {
|
|
1157
|
+
lines.push(` document.querySelector('${escapeStr(target)}').innerHTML = data;`);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
lines.push(` } catch (err) {`, ` console.error('Poll error:', err);`, ` }`, `}, ${ms});`);
|
|
1161
|
+
return lines.join("\n");
|
|
1162
|
+
}
|
|
1163
|
+
function generateStream(node) {
|
|
1164
|
+
const url = extractRoleValue(node, "source") || "/";
|
|
1165
|
+
const target = extractRoleValue(node, "destination");
|
|
1166
|
+
const lines = [`const es = new EventSource('${escapeStr(url)}');`];
|
|
1167
|
+
if (target) {
|
|
1168
|
+
lines.push(
|
|
1169
|
+
`es.onmessage = (event) => {`,
|
|
1170
|
+
` document.querySelector('${escapeStr(target)}').insertAdjacentHTML('beforeend', event.data);`,
|
|
1171
|
+
`};`
|
|
1172
|
+
);
|
|
1173
|
+
} else {
|
|
1174
|
+
lines.push(`es.onmessage = (event) => {`, ` console.log('Stream data:', event.data);`, `};`);
|
|
1175
|
+
}
|
|
1176
|
+
lines.push(`es.onerror = () => {`, ` console.error('Stream error, reconnecting...');`, `};`);
|
|
1177
|
+
return lines.join("\n");
|
|
1178
|
+
}
|
|
1179
|
+
function generateSubmit(node) {
|
|
1180
|
+
const form = extractRoleValue(node, "patient") || "#form";
|
|
1181
|
+
const url = extractRoleValue(node, "destination") || "/";
|
|
1182
|
+
const format = extractRoleValue(node, "style")?.toLowerCase();
|
|
1183
|
+
const lines = [
|
|
1184
|
+
`const form = document.querySelector('${escapeStr(form)}');`,
|
|
1185
|
+
`const formData = new FormData(form);`
|
|
1186
|
+
];
|
|
1187
|
+
if (format === "json") {
|
|
1188
|
+
lines.push(
|
|
1189
|
+
`fetch('${escapeStr(url)}', {`,
|
|
1190
|
+
` method: 'POST',`,
|
|
1191
|
+
` headers: { 'Content-Type': 'application/json' },`,
|
|
1192
|
+
` body: JSON.stringify(Object.fromEntries(formData)),`,
|
|
1193
|
+
`})`,
|
|
1194
|
+
` .then(r => r.json())`,
|
|
1195
|
+
` .catch(err => console.error('Submit error:', err));`
|
|
1196
|
+
);
|
|
1197
|
+
} else {
|
|
1198
|
+
lines.push(
|
|
1199
|
+
`fetch('${escapeStr(url)}', {`,
|
|
1200
|
+
` method: 'POST',`,
|
|
1201
|
+
` body: formData,`,
|
|
1202
|
+
`})`,
|
|
1203
|
+
` .then(r => r.text())`,
|
|
1204
|
+
` .catch(err => console.error('Submit error:', err));`
|
|
1205
|
+
);
|
|
1206
|
+
}
|
|
1207
|
+
return lines.join("\n");
|
|
1208
|
+
}
|
|
1209
|
+
function generateTransform(node) {
|
|
1210
|
+
const data = extractRoleValue(node, "patient") || "data";
|
|
1211
|
+
const fn = extractRoleValue(node, "instrument") || "identity";
|
|
1212
|
+
return `const result = ${fn}(${data});`;
|
|
1213
|
+
}
|
|
1214
|
+
function generateEnter(node) {
|
|
1215
|
+
const url = extractRoleValue(node, "source") || "/";
|
|
1216
|
+
return [
|
|
1217
|
+
`// HATEOAS: Connect to entry point`,
|
|
1218
|
+
`const agent = new SirenAgent('${escapeStr(url)}');`,
|
|
1219
|
+
`await agent.start();`
|
|
1220
|
+
].join("\n");
|
|
1221
|
+
}
|
|
1222
|
+
function generateFollow(node) {
|
|
1223
|
+
const rel = extractRoleValue(node, "patient") || "self";
|
|
1224
|
+
return [
|
|
1225
|
+
`// HATEOAS: Follow link relation '${escapeStr(rel)}'`,
|
|
1226
|
+
`await agent.followLink('${escapeStr(rel)}');`
|
|
1227
|
+
].join("\n");
|
|
1228
|
+
}
|
|
1229
|
+
function generatePerform(node) {
|
|
1230
|
+
const action = extractRoleValue(node, "patient") || "";
|
|
1231
|
+
const dataSource = extractRoleValue(node, "source");
|
|
1232
|
+
const lines = [`// HATEOAS: Execute action '${escapeStr(action)}'`];
|
|
1233
|
+
if (dataSource) {
|
|
1234
|
+
lines.push(
|
|
1235
|
+
`const data = Object.fromEntries(new FormData(document.querySelector('${escapeStr(dataSource)}')));`
|
|
1236
|
+
);
|
|
1237
|
+
lines.push(`await agent.executeAction('${escapeStr(action)}', data);`);
|
|
1238
|
+
} else {
|
|
1239
|
+
lines.push(`await agent.executeAction('${escapeStr(action)}');`);
|
|
1240
|
+
}
|
|
1241
|
+
return lines.join("\n");
|
|
1242
|
+
}
|
|
1243
|
+
function generateCapture(node) {
|
|
1244
|
+
const varName = extractRoleValue(node, "destination") || "captured";
|
|
1245
|
+
const path = extractRoleValue(node, "patient");
|
|
1246
|
+
if (path) {
|
|
1247
|
+
return `const ${varName} = agent.currentEntity.properties?.['${escapeStr(path)}'];`;
|
|
1248
|
+
}
|
|
1249
|
+
return `const ${varName} = agent.currentEntity.properties;`;
|
|
1250
|
+
}
|
|
1251
|
+
var flowCodeGenerator = {
|
|
1252
|
+
generate(node) {
|
|
1253
|
+
switch (node.action) {
|
|
1254
|
+
case "fetch":
|
|
1255
|
+
return generateFetch(node);
|
|
1256
|
+
case "poll":
|
|
1257
|
+
return generatePoll(node);
|
|
1258
|
+
case "stream":
|
|
1259
|
+
return generateStream(node);
|
|
1260
|
+
case "submit":
|
|
1261
|
+
return generateSubmit(node);
|
|
1262
|
+
case "transform":
|
|
1263
|
+
return generateTransform(node);
|
|
1264
|
+
case "enter":
|
|
1265
|
+
return generateEnter(node);
|
|
1266
|
+
case "follow":
|
|
1267
|
+
return generateFollow(node);
|
|
1268
|
+
case "perform":
|
|
1269
|
+
return generatePerform(node);
|
|
1270
|
+
case "capture":
|
|
1271
|
+
return generateCapture(node);
|
|
1272
|
+
default:
|
|
1273
|
+
throw new Error(`Unknown FlowScript command: ${node.action}`);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
};
|
|
1277
|
+
|
|
1278
|
+
// src/generators/flow-renderer.ts
|
|
1279
|
+
import { extractRoleValue as extractRoleValue2 } from "@lokascript/framework";
|
|
1280
|
+
var COMMAND_KEYWORDS = {
|
|
1281
|
+
fetch: {
|
|
1282
|
+
en: "fetch",
|
|
1283
|
+
es: "obtener",
|
|
1284
|
+
ja: "\u53D6\u5F97",
|
|
1285
|
+
ar: "\u062C\u0644\u0628",
|
|
1286
|
+
ko: "\uAC00\uC838\uC624\uAE30",
|
|
1287
|
+
zh: "\u83B7\u53D6",
|
|
1288
|
+
tr: "getir",
|
|
1289
|
+
fr: "r\xE9cup\xE9rer"
|
|
1290
|
+
},
|
|
1291
|
+
poll: {
|
|
1292
|
+
en: "poll",
|
|
1293
|
+
es: "sondear",
|
|
1294
|
+
ja: "\u30DD\u30FC\u30EA\u30F3\u30B0",
|
|
1295
|
+
ar: "\u0627\u0633\u062A\u0637\u0644\u0639",
|
|
1296
|
+
ko: "\uD3F4\uB9C1",
|
|
1297
|
+
zh: "\u8F6E\u8BE2",
|
|
1298
|
+
tr: "yokla",
|
|
1299
|
+
fr: "interroger"
|
|
1300
|
+
},
|
|
1301
|
+
stream: {
|
|
1302
|
+
en: "stream",
|
|
1303
|
+
es: "transmitir",
|
|
1304
|
+
ja: "\u30B9\u30C8\u30EA\u30FC\u30E0",
|
|
1305
|
+
ar: "\u0628\u062B",
|
|
1306
|
+
ko: "\uC2A4\uD2B8\uB9AC\uBC0D",
|
|
1307
|
+
zh: "\u6D41\u5F0F",
|
|
1308
|
+
tr: "aktar",
|
|
1309
|
+
fr: "diffuser"
|
|
1310
|
+
},
|
|
1311
|
+
submit: {
|
|
1312
|
+
en: "submit",
|
|
1313
|
+
es: "enviar",
|
|
1314
|
+
ja: "\u9001\u4FE1",
|
|
1315
|
+
ar: "\u0623\u0631\u0633\u0644",
|
|
1316
|
+
ko: "\uC81C\uCD9C",
|
|
1317
|
+
zh: "\u63D0\u4EA4",
|
|
1318
|
+
tr: "g\xF6nder",
|
|
1319
|
+
fr: "soumettre"
|
|
1320
|
+
},
|
|
1321
|
+
transform: {
|
|
1322
|
+
en: "transform",
|
|
1323
|
+
es: "transformar",
|
|
1324
|
+
ja: "\u5909\u63DB",
|
|
1325
|
+
ar: "\u062D\u0648\u0651\u0644",
|
|
1326
|
+
ko: "\uBCC0\uD658",
|
|
1327
|
+
zh: "\u8F6C\u6362",
|
|
1328
|
+
tr: "d\xF6n\xFC\u015Ft\xFCr",
|
|
1329
|
+
fr: "transformer"
|
|
1330
|
+
}
|
|
1331
|
+
};
|
|
1332
|
+
var MARKERS = {
|
|
1333
|
+
as: { en: "as", es: "como", ja: "\u3067", ar: "\u0643", ko: "\uB85C", zh: "\u4EE5", tr: "olarak", fr: "comme" },
|
|
1334
|
+
into: { en: "into", es: "en", ja: "\u306B", ar: "\u0641\u064A", ko: "\uC5D0", zh: "\u5230", tr: "e", fr: "dans" },
|
|
1335
|
+
every: {
|
|
1336
|
+
en: "every",
|
|
1337
|
+
es: "cada",
|
|
1338
|
+
ja: "\u3054\u3068\u306B",
|
|
1339
|
+
ar: "\u0643\u0644",
|
|
1340
|
+
ko: "\uB9C8\uB2E4",
|
|
1341
|
+
zh: "\u6BCF",
|
|
1342
|
+
tr: "her",
|
|
1343
|
+
fr: "chaque"
|
|
1344
|
+
},
|
|
1345
|
+
to: { en: "to", es: "a", ja: "\u306B", ar: "\u0625\u0644\u0649", ko: "\uB85C", zh: "\u5230", tr: "e", fr: "vers" },
|
|
1346
|
+
with: { en: "with", es: "con", ja: "\u3067", ar: "\u0628", ko: "\uB85C", zh: "\u7528", tr: "ile", fr: "avec" }
|
|
1347
|
+
};
|
|
1348
|
+
var SOV_LANGUAGES = /* @__PURE__ */ new Set(["ja", "ko", "tr"]);
|
|
1349
|
+
var VSO_LANGUAGES = /* @__PURE__ */ new Set(["ar"]);
|
|
1350
|
+
function isSOV(lang) {
|
|
1351
|
+
return SOV_LANGUAGES.has(lang);
|
|
1352
|
+
}
|
|
1353
|
+
function isVSO(lang) {
|
|
1354
|
+
return VSO_LANGUAGES.has(lang);
|
|
1355
|
+
}
|
|
1356
|
+
function kw(command, lang) {
|
|
1357
|
+
return COMMAND_KEYWORDS[command]?.[lang] ?? command;
|
|
1358
|
+
}
|
|
1359
|
+
function mk(marker, lang) {
|
|
1360
|
+
return MARKERS[marker]?.[lang] ?? marker;
|
|
1361
|
+
}
|
|
1362
|
+
function renderFetch(node, lang) {
|
|
1363
|
+
const source = extractRoleValue2(node, "source") || "/";
|
|
1364
|
+
const style = extractRoleValue2(node, "style");
|
|
1365
|
+
const dest = extractRoleValue2(node, "destination");
|
|
1366
|
+
const verb = kw("fetch", lang);
|
|
1367
|
+
const parts = [];
|
|
1368
|
+
if (isSOV(lang)) {
|
|
1369
|
+
parts.push(source);
|
|
1370
|
+
if (style) parts.push(style, mk("as", lang));
|
|
1371
|
+
parts.push(verb);
|
|
1372
|
+
if (dest) parts.push(dest, mk("into", lang));
|
|
1373
|
+
} else if (isVSO(lang)) {
|
|
1374
|
+
parts.push(verb, source);
|
|
1375
|
+
if (style) parts.push(mk("as", lang), style);
|
|
1376
|
+
if (dest) parts.push(mk("into", lang), dest);
|
|
1377
|
+
} else {
|
|
1378
|
+
parts.push(verb, source);
|
|
1379
|
+
if (style) parts.push(mk("as", lang), style);
|
|
1380
|
+
if (dest) parts.push(mk("into", lang), dest);
|
|
1381
|
+
}
|
|
1382
|
+
return parts.join(" ");
|
|
1383
|
+
}
|
|
1384
|
+
function renderPoll(node, lang) {
|
|
1385
|
+
const source = extractRoleValue2(node, "source") || "/";
|
|
1386
|
+
const duration = extractRoleValue2(node, "duration") || "5s";
|
|
1387
|
+
const style = extractRoleValue2(node, "style");
|
|
1388
|
+
const dest = extractRoleValue2(node, "destination");
|
|
1389
|
+
const verb = kw("poll", lang);
|
|
1390
|
+
const parts = [];
|
|
1391
|
+
if (isSOV(lang)) {
|
|
1392
|
+
parts.push(source);
|
|
1393
|
+
parts.push(duration, mk("every", lang));
|
|
1394
|
+
if (style) parts.push(style, mk("as", lang));
|
|
1395
|
+
parts.push(verb);
|
|
1396
|
+
if (dest) parts.push(dest, mk("into", lang));
|
|
1397
|
+
} else if (isVSO(lang)) {
|
|
1398
|
+
parts.push(verb, source);
|
|
1399
|
+
parts.push(mk("every", lang), duration);
|
|
1400
|
+
if (style) parts.push(mk("as", lang), style);
|
|
1401
|
+
if (dest) parts.push(mk("into", lang), dest);
|
|
1402
|
+
} else {
|
|
1403
|
+
parts.push(verb, source);
|
|
1404
|
+
parts.push(mk("every", lang), duration);
|
|
1405
|
+
if (style) parts.push(mk("as", lang), style);
|
|
1406
|
+
if (dest) parts.push(mk("into", lang), dest);
|
|
1407
|
+
}
|
|
1408
|
+
return parts.join(" ");
|
|
1409
|
+
}
|
|
1410
|
+
function renderStream(node, lang) {
|
|
1411
|
+
const source = extractRoleValue2(node, "source") || "/";
|
|
1412
|
+
const style = extractRoleValue2(node, "style");
|
|
1413
|
+
const dest = extractRoleValue2(node, "destination");
|
|
1414
|
+
const verb = kw("stream", lang);
|
|
1415
|
+
const parts = [];
|
|
1416
|
+
if (isSOV(lang)) {
|
|
1417
|
+
parts.push(source);
|
|
1418
|
+
if (style) parts.push(style, mk("as", lang));
|
|
1419
|
+
parts.push(verb);
|
|
1420
|
+
if (dest) parts.push(dest, mk("into", lang));
|
|
1421
|
+
} else if (isVSO(lang)) {
|
|
1422
|
+
parts.push(verb, source);
|
|
1423
|
+
if (style) parts.push(mk("as", lang), style);
|
|
1424
|
+
if (dest) parts.push(mk("into", lang), dest);
|
|
1425
|
+
} else {
|
|
1426
|
+
parts.push(verb, source);
|
|
1427
|
+
if (style) parts.push(mk("as", lang), style);
|
|
1428
|
+
if (dest) parts.push(mk("into", lang), dest);
|
|
1429
|
+
}
|
|
1430
|
+
return parts.join(" ");
|
|
1431
|
+
}
|
|
1432
|
+
function renderSubmit(node, lang) {
|
|
1433
|
+
const patient = extractRoleValue2(node, "patient") || "#form";
|
|
1434
|
+
const dest = extractRoleValue2(node, "destination") || "/";
|
|
1435
|
+
const style = extractRoleValue2(node, "style");
|
|
1436
|
+
const verb = kw("submit", lang);
|
|
1437
|
+
const parts = [];
|
|
1438
|
+
if (isSOV(lang)) {
|
|
1439
|
+
parts.push(patient);
|
|
1440
|
+
parts.push(dest, mk("to", lang));
|
|
1441
|
+
if (style) parts.push(style, mk("as", lang));
|
|
1442
|
+
parts.push(verb);
|
|
1443
|
+
} else if (isVSO(lang)) {
|
|
1444
|
+
parts.push(verb, patient);
|
|
1445
|
+
parts.push(mk("to", lang), dest);
|
|
1446
|
+
if (style) parts.push(mk("as", lang), style);
|
|
1447
|
+
} else {
|
|
1448
|
+
parts.push(verb, patient);
|
|
1449
|
+
parts.push(mk("to", lang), dest);
|
|
1450
|
+
if (style) parts.push(mk("as", lang), style);
|
|
1451
|
+
}
|
|
1452
|
+
return parts.join(" ");
|
|
1453
|
+
}
|
|
1454
|
+
function renderTransform(node, lang) {
|
|
1455
|
+
const patient = extractRoleValue2(node, "patient") || "data";
|
|
1456
|
+
const instrument = extractRoleValue2(node, "instrument") || "identity";
|
|
1457
|
+
const verb = kw("transform", lang);
|
|
1458
|
+
const parts = [];
|
|
1459
|
+
if (isSOV(lang)) {
|
|
1460
|
+
parts.push(patient);
|
|
1461
|
+
parts.push(instrument, mk("with", lang));
|
|
1462
|
+
parts.push(verb);
|
|
1463
|
+
} else if (isVSO(lang)) {
|
|
1464
|
+
parts.push(verb, patient);
|
|
1465
|
+
parts.push(mk("with", lang), instrument);
|
|
1466
|
+
} else {
|
|
1467
|
+
parts.push(verb, patient);
|
|
1468
|
+
parts.push(mk("with", lang), instrument);
|
|
1469
|
+
}
|
|
1470
|
+
return parts.join(" ");
|
|
1471
|
+
}
|
|
1472
|
+
function renderFlow(node, language) {
|
|
1473
|
+
switch (node.action) {
|
|
1474
|
+
case "fetch":
|
|
1475
|
+
return renderFetch(node, language);
|
|
1476
|
+
case "poll":
|
|
1477
|
+
return renderPoll(node, language);
|
|
1478
|
+
case "stream":
|
|
1479
|
+
return renderStream(node, language);
|
|
1480
|
+
case "submit":
|
|
1481
|
+
return renderSubmit(node, language);
|
|
1482
|
+
case "transform":
|
|
1483
|
+
return renderTransform(node, language);
|
|
1484
|
+
default:
|
|
1485
|
+
return `-- Unknown: ${node.action}`;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
|
|
1489
|
+
// src/generators/htmx-generator.ts
|
|
1490
|
+
function generateHTMX(spec) {
|
|
1491
|
+
switch (spec.action) {
|
|
1492
|
+
case "fetch":
|
|
1493
|
+
return generateFetchHTMX(spec);
|
|
1494
|
+
case "poll":
|
|
1495
|
+
return generatePollHTMX(spec);
|
|
1496
|
+
case "stream":
|
|
1497
|
+
return generateStreamHTMX(spec);
|
|
1498
|
+
case "submit":
|
|
1499
|
+
return generateSubmitHTMX(spec);
|
|
1500
|
+
case "transform":
|
|
1501
|
+
return null;
|
|
1502
|
+
// No HTMX equivalent for data transforms
|
|
1503
|
+
default:
|
|
1504
|
+
return null;
|
|
1505
|
+
}
|
|
1506
|
+
}
|
|
1507
|
+
function generateFetchHTMX(spec) {
|
|
1508
|
+
const attrs = {};
|
|
1509
|
+
const notes = [];
|
|
1510
|
+
if (spec.url) {
|
|
1511
|
+
attrs["hx-get"] = spec.url;
|
|
1512
|
+
}
|
|
1513
|
+
if (spec.target) {
|
|
1514
|
+
attrs["hx-target"] = spec.target;
|
|
1515
|
+
}
|
|
1516
|
+
attrs["hx-swap"] = "innerHTML";
|
|
1517
|
+
if (spec.responseFormat === "json") {
|
|
1518
|
+
notes.push(
|
|
1519
|
+
'HTMX expects HTML responses by default. For JSON, use hx-ext="json-enc" or handle in a hyperscript handler.'
|
|
1520
|
+
);
|
|
1521
|
+
}
|
|
1522
|
+
return { attrs, notes };
|
|
1523
|
+
}
|
|
1524
|
+
function generatePollHTMX(spec) {
|
|
1525
|
+
const attrs = {};
|
|
1526
|
+
const notes = [];
|
|
1527
|
+
if (spec.url) {
|
|
1528
|
+
attrs["hx-get"] = spec.url;
|
|
1529
|
+
}
|
|
1530
|
+
if (spec.intervalMs) {
|
|
1531
|
+
const seconds = spec.intervalMs / 1e3;
|
|
1532
|
+
attrs["hx-trigger"] = `every ${seconds}s`;
|
|
1533
|
+
}
|
|
1534
|
+
if (spec.target) {
|
|
1535
|
+
attrs["hx-target"] = spec.target;
|
|
1536
|
+
}
|
|
1537
|
+
attrs["hx-swap"] = "innerHTML";
|
|
1538
|
+
if (spec.responseFormat === "json") {
|
|
1539
|
+
notes.push(
|
|
1540
|
+
'HTMX expects HTML responses by default. For JSON, use hx-ext="json-enc" or handle in a hyperscript handler.'
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
return { attrs, notes };
|
|
1544
|
+
}
|
|
1545
|
+
function generateStreamHTMX(spec) {
|
|
1546
|
+
const attrs = {};
|
|
1547
|
+
const notes = [];
|
|
1548
|
+
if (spec.url) {
|
|
1549
|
+
attrs["hx-ext"] = "sse";
|
|
1550
|
+
attrs["sse-connect"] = spec.url;
|
|
1551
|
+
attrs["sse-swap"] = "message";
|
|
1552
|
+
}
|
|
1553
|
+
if (spec.target) {
|
|
1554
|
+
attrs["hx-target"] = spec.target;
|
|
1555
|
+
}
|
|
1556
|
+
notes.push('SSE support requires the htmx sse extension (hx-ext="sse").');
|
|
1557
|
+
return { attrs, notes };
|
|
1558
|
+
}
|
|
1559
|
+
function generateSubmitHTMX(spec) {
|
|
1560
|
+
const attrs = {};
|
|
1561
|
+
const notes = [];
|
|
1562
|
+
if (spec.url) {
|
|
1563
|
+
attrs["hx-post"] = spec.url;
|
|
1564
|
+
}
|
|
1565
|
+
if (spec.responseFormat === "json") {
|
|
1566
|
+
attrs["hx-ext"] = "json-enc";
|
|
1567
|
+
attrs["hx-encoding"] = "application/json";
|
|
1568
|
+
}
|
|
1569
|
+
if (spec.target) {
|
|
1570
|
+
attrs["hx-target"] = spec.target;
|
|
1571
|
+
}
|
|
1572
|
+
return { attrs, notes };
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
// src/generators/route-extractor.ts
|
|
1576
|
+
function extractRoute(spec) {
|
|
1577
|
+
if (!spec.url) return null;
|
|
1578
|
+
const path = spec.url;
|
|
1579
|
+
const pathParams = extractPathParams(path);
|
|
1580
|
+
const handlerName = generateHandlerName(spec);
|
|
1581
|
+
return {
|
|
1582
|
+
path,
|
|
1583
|
+
method: spec.method || "GET",
|
|
1584
|
+
responseFormat: spec.responseFormat || "text",
|
|
1585
|
+
pathParams,
|
|
1586
|
+
handlerName,
|
|
1587
|
+
sourceCommand: spec.action
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
function extractRoutes(specs) {
|
|
1591
|
+
return specs.map(extractRoute).filter((r) => r !== null);
|
|
1592
|
+
}
|
|
1593
|
+
function extractPathParams(url) {
|
|
1594
|
+
const params = [];
|
|
1595
|
+
const braceMatches = url.matchAll(/\{(\w+)\}/g);
|
|
1596
|
+
for (const match of braceMatches) {
|
|
1597
|
+
params.push(match[1]);
|
|
1598
|
+
}
|
|
1599
|
+
const colonMatches = url.matchAll(/:(\w+)/g);
|
|
1600
|
+
for (const match of colonMatches) {
|
|
1601
|
+
params.push(match[1]);
|
|
1602
|
+
}
|
|
1603
|
+
return params;
|
|
1604
|
+
}
|
|
1605
|
+
function generateHandlerName(spec) {
|
|
1606
|
+
if (!spec.url) return "handler";
|
|
1607
|
+
const segments = spec.url.split("/").filter((s) => s && !s.startsWith("{") && !s.startsWith(":") && !s.includes("?"));
|
|
1608
|
+
const lastSegment = segments[segments.length - 1] || "data";
|
|
1609
|
+
const prefix = spec.method === "POST" ? "create" : spec.method === "PUT" ? "update" : spec.method === "DELETE" ? "delete" : "get";
|
|
1610
|
+
const name = lastSegment.charAt(0).toUpperCase() + lastSegment.slice(1);
|
|
1611
|
+
return `${prefix}${name}`;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// src/parser/pipeline-parser.ts
|
|
1615
|
+
var ARROW_DELIMITER = /\s*(?:→|->)\s*/;
|
|
1616
|
+
function parseFlowPipeline(dsl, input, language) {
|
|
1617
|
+
const steps = [];
|
|
1618
|
+
const errors = [];
|
|
1619
|
+
const arrowParts = input.split(ARROW_DELIMITER);
|
|
1620
|
+
const segments = [];
|
|
1621
|
+
for (const part of arrowParts) {
|
|
1622
|
+
const lines = part.split(/\n/).map((l) => l.trim()).filter(Boolean);
|
|
1623
|
+
segments.push(...lines);
|
|
1624
|
+
}
|
|
1625
|
+
const commands = segments.filter((s) => !s.startsWith("--"));
|
|
1626
|
+
for (const segment of commands) {
|
|
1627
|
+
try {
|
|
1628
|
+
const node = dsl.parse(segment, language);
|
|
1629
|
+
steps.push({ node });
|
|
1630
|
+
} catch (err) {
|
|
1631
|
+
errors.push(
|
|
1632
|
+
`Failed to parse step: "${segment}" - ${err instanceof Error ? err.message : String(err)}`
|
|
1633
|
+
);
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
return { steps, errors };
|
|
1637
|
+
}
|
|
1638
|
+
function compilePipeline(dsl, input, language) {
|
|
1639
|
+
const { steps, errors } = parseFlowPipeline(dsl, input, language);
|
|
1640
|
+
if (errors.length > 0) {
|
|
1641
|
+
return { ok: false, errors };
|
|
1642
|
+
}
|
|
1643
|
+
if (steps.length === 0) {
|
|
1644
|
+
return { ok: false, errors: ["Empty pipeline"] };
|
|
1645
|
+
}
|
|
1646
|
+
if (steps.length === 1) {
|
|
1647
|
+
const result = dsl.compile(input.replace(/→|->/g, "").trim(), language);
|
|
1648
|
+
return {
|
|
1649
|
+
ok: result.ok,
|
|
1650
|
+
code: result.code ?? void 0,
|
|
1651
|
+
errors: result.ok ? [] : ["Compilation failed"]
|
|
1652
|
+
};
|
|
1653
|
+
}
|
|
1654
|
+
const compileErrors = [];
|
|
1655
|
+
const codes = [];
|
|
1656
|
+
for (const step of steps) {
|
|
1657
|
+
const result = dsl.compile(renderStepBack(step.node, language), language);
|
|
1658
|
+
if (result.ok && result.code) {
|
|
1659
|
+
codes.push(result.code);
|
|
1660
|
+
} else {
|
|
1661
|
+
compileErrors.push(`Failed to compile step: ${step.node.action}`);
|
|
1662
|
+
}
|
|
1663
|
+
}
|
|
1664
|
+
if (compileErrors.length > 0) {
|
|
1665
|
+
return { ok: false, errors: compileErrors };
|
|
1666
|
+
}
|
|
1667
|
+
const combined = codes.join("\n\n// --- next step ---\n\n");
|
|
1668
|
+
return { ok: true, code: combined, errors: [] };
|
|
1669
|
+
}
|
|
1670
|
+
function renderStepBack(node, language) {
|
|
1671
|
+
return renderFlow(node, language);
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
// src/generators/workflow-generator.ts
|
|
1675
|
+
function toWorkflowSpec(specs) {
|
|
1676
|
+
let entryPoint;
|
|
1677
|
+
const steps = [];
|
|
1678
|
+
let lastStep;
|
|
1679
|
+
for (const spec of specs) {
|
|
1680
|
+
switch (spec.action) {
|
|
1681
|
+
case "enter": {
|
|
1682
|
+
if (!spec.url) throw new Error("enter command requires a URL");
|
|
1683
|
+
entryPoint = spec.url;
|
|
1684
|
+
break;
|
|
1685
|
+
}
|
|
1686
|
+
case "follow": {
|
|
1687
|
+
if (!spec.linkRel) throw new Error("follow command requires a link relation");
|
|
1688
|
+
const step = { type: "navigate", rel: spec.linkRel };
|
|
1689
|
+
steps.push(step);
|
|
1690
|
+
lastStep = step;
|
|
1691
|
+
break;
|
|
1692
|
+
}
|
|
1693
|
+
case "perform": {
|
|
1694
|
+
if (!spec.actionName) throw new Error("perform command requires an action name");
|
|
1695
|
+
const step = {
|
|
1696
|
+
type: "action",
|
|
1697
|
+
action: spec.actionName
|
|
1698
|
+
};
|
|
1699
|
+
if (spec.dataSource) {
|
|
1700
|
+
step.dataSource = spec.dataSource;
|
|
1701
|
+
}
|
|
1702
|
+
steps.push(step);
|
|
1703
|
+
lastStep = step;
|
|
1704
|
+
break;
|
|
1705
|
+
}
|
|
1706
|
+
case "capture": {
|
|
1707
|
+
if (!spec.captureAs) throw new Error("capture command requires a variable name");
|
|
1708
|
+
if (lastStep && (lastStep.type === "navigate" || lastStep.type === "action")) {
|
|
1709
|
+
if (!lastStep.capture) lastStep.capture = {};
|
|
1710
|
+
lastStep.capture[spec.captureAs] = spec.capturePath || "properties";
|
|
1711
|
+
} else {
|
|
1712
|
+
const step = {
|
|
1713
|
+
type: "navigate",
|
|
1714
|
+
rel: "self",
|
|
1715
|
+
capture: { [spec.captureAs]: spec.capturePath || "properties" }
|
|
1716
|
+
};
|
|
1717
|
+
steps.push(step);
|
|
1718
|
+
lastStep = step;
|
|
1719
|
+
}
|
|
1720
|
+
break;
|
|
1721
|
+
}
|
|
1722
|
+
default:
|
|
1723
|
+
break;
|
|
1724
|
+
}
|
|
1725
|
+
}
|
|
1726
|
+
if (!entryPoint) {
|
|
1727
|
+
throw new Error('Workflow requires an "enter" command to specify the API entry point');
|
|
1728
|
+
}
|
|
1729
|
+
return { entryPoint, steps };
|
|
1730
|
+
}
|
|
1731
|
+
function toSirenGrailSteps(spec) {
|
|
1732
|
+
return spec.steps.map((step) => {
|
|
1733
|
+
switch (step.type) {
|
|
1734
|
+
case "navigate":
|
|
1735
|
+
return {
|
|
1736
|
+
type: "navigate",
|
|
1737
|
+
rel: step.rel,
|
|
1738
|
+
...step.capture ? { capture: step.capture } : {}
|
|
1739
|
+
};
|
|
1740
|
+
case "action":
|
|
1741
|
+
return {
|
|
1742
|
+
type: "action",
|
|
1743
|
+
action: step.action,
|
|
1744
|
+
...step.data ? { data: step.data } : {},
|
|
1745
|
+
...step.dataSource ? { dataSource: step.dataSource } : {},
|
|
1746
|
+
...step.capture ? { capture: step.capture } : {}
|
|
1747
|
+
};
|
|
1748
|
+
case "stop":
|
|
1749
|
+
return {
|
|
1750
|
+
type: "stop",
|
|
1751
|
+
...step.result ? { result: step.result } : {},
|
|
1752
|
+
...step.reason ? { reason: step.reason } : {}
|
|
1753
|
+
};
|
|
1754
|
+
default:
|
|
1755
|
+
return step;
|
|
1756
|
+
}
|
|
1757
|
+
});
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
// src/runtime/workflow-executor.ts
|
|
1761
|
+
function toSirenSteps(steps) {
|
|
1762
|
+
return steps.map((step) => {
|
|
1763
|
+
switch (step.type) {
|
|
1764
|
+
case "navigate":
|
|
1765
|
+
return {
|
|
1766
|
+
type: "navigate",
|
|
1767
|
+
rel: step.rel,
|
|
1768
|
+
...step.capture ? { capture: step.capture } : {}
|
|
1769
|
+
};
|
|
1770
|
+
case "action":
|
|
1771
|
+
return {
|
|
1772
|
+
type: "action",
|
|
1773
|
+
action: step.action,
|
|
1774
|
+
...step.data ? { data: step.data } : {},
|
|
1775
|
+
...step.dataSource ? { dataSource: step.dataSource } : {},
|
|
1776
|
+
...step.capture ? { capture: step.capture } : {}
|
|
1777
|
+
};
|
|
1778
|
+
case "stop":
|
|
1779
|
+
return {
|
|
1780
|
+
type: "stop",
|
|
1781
|
+
...step.result ? { result: step.result } : {},
|
|
1782
|
+
...step.reason ? { reason: step.reason } : {}
|
|
1783
|
+
};
|
|
1784
|
+
default:
|
|
1785
|
+
return step;
|
|
1786
|
+
}
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
async function executeWorkflow(spec, options = {}) {
|
|
1790
|
+
let sirenAgent;
|
|
1791
|
+
try {
|
|
1792
|
+
sirenAgent = await import("siren-agent");
|
|
1793
|
+
} catch {
|
|
1794
|
+
throw new Error(
|
|
1795
|
+
"siren-agent is required for workflow execution. Install it with: npm install siren-agent"
|
|
1796
|
+
);
|
|
1797
|
+
}
|
|
1798
|
+
const { OODAAgent, compileWorkflow } = sirenAgent;
|
|
1799
|
+
const steps = toSirenSteps(spec.steps);
|
|
1800
|
+
const decide = compileWorkflow(steps);
|
|
1801
|
+
const agent = new OODAAgent(spec.entryPoint, {
|
|
1802
|
+
maxSteps: options.maxSteps ?? 50,
|
|
1803
|
+
headers: options.headers,
|
|
1804
|
+
verbose: options.verbose ?? false,
|
|
1805
|
+
timeout: options.timeout ?? 3e4,
|
|
1806
|
+
decide,
|
|
1807
|
+
...options.autoPursue ? { autoPursue: { maxDepth: 5 } } : {},
|
|
1808
|
+
...options.onEntity ? { onEntity: options.onEntity } : {},
|
|
1809
|
+
...options.onDecision ? { onDecision: options.onDecision } : {}
|
|
1810
|
+
});
|
|
1811
|
+
return agent.run();
|
|
1812
|
+
}
|
|
1813
|
+
function createWorkflowExecutor(defaultOptions = {}) {
|
|
1814
|
+
return {
|
|
1815
|
+
execute(spec, overrides) {
|
|
1816
|
+
return executeWorkflow(spec, { ...defaultOptions, ...overrides });
|
|
1817
|
+
}
|
|
1818
|
+
};
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
// src/runtime/mcp-workflow-server.ts
|
|
1822
|
+
function actionsToTools(actions) {
|
|
1823
|
+
return actions.map((action) => {
|
|
1824
|
+
const properties = {};
|
|
1825
|
+
const required = [];
|
|
1826
|
+
for (const field of action.fields ?? []) {
|
|
1827
|
+
properties[field.name] = {
|
|
1828
|
+
type: fieldTypeToJsonSchema(field.type),
|
|
1829
|
+
...field.title ? { description: field.title } : {},
|
|
1830
|
+
...field.value !== void 0 ? { default: field.value } : {}
|
|
1831
|
+
};
|
|
1832
|
+
if (field.value === void 0) {
|
|
1833
|
+
required.push(field.name);
|
|
1834
|
+
}
|
|
1835
|
+
}
|
|
1836
|
+
return {
|
|
1837
|
+
name: `action__${action.name}`,
|
|
1838
|
+
description: action.title ?? `Execute the "${action.name}" action (${action.method ?? "POST"} ${action.href})`,
|
|
1839
|
+
inputSchema: {
|
|
1840
|
+
type: "object",
|
|
1841
|
+
properties,
|
|
1842
|
+
...required.length > 0 ? { required } : {}
|
|
1843
|
+
}
|
|
1844
|
+
};
|
|
1845
|
+
});
|
|
1846
|
+
}
|
|
1847
|
+
function linksToTools(links) {
|
|
1848
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1849
|
+
const tools = [];
|
|
1850
|
+
for (const link of links) {
|
|
1851
|
+
const rel = link.rel[0];
|
|
1852
|
+
if (!rel || rel === "self" || seen.has(rel)) continue;
|
|
1853
|
+
seen.add(rel);
|
|
1854
|
+
tools.push({
|
|
1855
|
+
name: `navigate__${rel}`,
|
|
1856
|
+
description: link.title ?? `Navigate to "${rel}" (${link.href})`,
|
|
1857
|
+
inputSchema: {
|
|
1858
|
+
type: "object",
|
|
1859
|
+
properties: {}
|
|
1860
|
+
}
|
|
1861
|
+
});
|
|
1862
|
+
}
|
|
1863
|
+
return tools;
|
|
1864
|
+
}
|
|
1865
|
+
function fieldTypeToJsonSchema(type) {
|
|
1866
|
+
switch (type) {
|
|
1867
|
+
case "number":
|
|
1868
|
+
case "range":
|
|
1869
|
+
return "number";
|
|
1870
|
+
case "checkbox":
|
|
1871
|
+
return "boolean";
|
|
1872
|
+
case "hidden":
|
|
1873
|
+
case "text":
|
|
1874
|
+
case "email":
|
|
1875
|
+
case "url":
|
|
1876
|
+
case "password":
|
|
1877
|
+
case "search":
|
|
1878
|
+
case "tel":
|
|
1879
|
+
case "date":
|
|
1880
|
+
case "datetime-local":
|
|
1881
|
+
case "time":
|
|
1882
|
+
case "month":
|
|
1883
|
+
case "week":
|
|
1884
|
+
default:
|
|
1885
|
+
return "string";
|
|
1886
|
+
}
|
|
1887
|
+
}
|
|
1888
|
+
function entityToTools(entity) {
|
|
1889
|
+
return [...actionsToTools(entity.actions ?? []), ...linksToTools(entity.links ?? [])];
|
|
1890
|
+
}
|
|
1891
|
+
var McpWorkflowServer = class {
|
|
1892
|
+
config;
|
|
1893
|
+
currentEntity = null;
|
|
1894
|
+
currentUrl;
|
|
1895
|
+
currentTools = [];
|
|
1896
|
+
toolChangeListeners = [];
|
|
1897
|
+
constructor(config) {
|
|
1898
|
+
this.config = {
|
|
1899
|
+
name: "hateoas-mcp-server",
|
|
1900
|
+
version: "1.0.0",
|
|
1901
|
+
...config
|
|
1902
|
+
};
|
|
1903
|
+
this.currentUrl = config.entryPoint;
|
|
1904
|
+
}
|
|
1905
|
+
/**
|
|
1906
|
+
* Register a listener for tool list changes (maps to tools/list_changed).
|
|
1907
|
+
*/
|
|
1908
|
+
onToolsChanged(listener) {
|
|
1909
|
+
this.toolChangeListeners.push(listener);
|
|
1910
|
+
}
|
|
1911
|
+
/**
|
|
1912
|
+
* Initialize the server by fetching the entry point entity.
|
|
1913
|
+
*/
|
|
1914
|
+
async initialize() {
|
|
1915
|
+
const entity = await this.fetchEntity(this.config.entryPoint);
|
|
1916
|
+
this.updateState(entity, this.config.entryPoint);
|
|
1917
|
+
}
|
|
1918
|
+
/**
|
|
1919
|
+
* Get the current list of available MCP tools.
|
|
1920
|
+
*/
|
|
1921
|
+
listTools() {
|
|
1922
|
+
return this.currentTools;
|
|
1923
|
+
}
|
|
1924
|
+
/**
|
|
1925
|
+
* Get the current entity as an MCP resource.
|
|
1926
|
+
*/
|
|
1927
|
+
getCurrentEntity() {
|
|
1928
|
+
return this.currentEntity;
|
|
1929
|
+
}
|
|
1930
|
+
/**
|
|
1931
|
+
* Get the current URL.
|
|
1932
|
+
*/
|
|
1933
|
+
getCurrentUrl() {
|
|
1934
|
+
return this.currentUrl;
|
|
1935
|
+
}
|
|
1936
|
+
/**
|
|
1937
|
+
* Call a tool (action or navigation).
|
|
1938
|
+
*
|
|
1939
|
+
* Returns the result text and triggers tools/list_changed if the
|
|
1940
|
+
* entity state changed.
|
|
1941
|
+
*/
|
|
1942
|
+
async callTool(name, args) {
|
|
1943
|
+
if (!this.currentEntity) {
|
|
1944
|
+
return { content: "Server not initialized. Call initialize() first.", isError: true };
|
|
1945
|
+
}
|
|
1946
|
+
const [prefix, ...rest] = name.split("__");
|
|
1947
|
+
const identifier = rest.join("__");
|
|
1948
|
+
try {
|
|
1949
|
+
switch (prefix) {
|
|
1950
|
+
case "action": {
|
|
1951
|
+
const action = this.currentEntity.actions?.find((a) => a.name === identifier);
|
|
1952
|
+
if (!action) {
|
|
1953
|
+
return {
|
|
1954
|
+
content: `Action "${identifier}" not available. Available: ${(this.currentEntity.actions ?? []).map((a) => a.name).join(", ")}`,
|
|
1955
|
+
isError: true
|
|
1956
|
+
};
|
|
1957
|
+
}
|
|
1958
|
+
const result = await this.executeAction(action, args);
|
|
1959
|
+
return { content: JSON.stringify(result.properties ?? result, null, 2) };
|
|
1960
|
+
}
|
|
1961
|
+
case "navigate": {
|
|
1962
|
+
const link = this.currentEntity.links?.find((l) => l.rel.includes(identifier));
|
|
1963
|
+
if (!link) {
|
|
1964
|
+
return {
|
|
1965
|
+
content: `Link "${identifier}" not available. Available: ${(this.currentEntity.links ?? []).map((l) => l.rel[0]).join(", ")}`,
|
|
1966
|
+
isError: true
|
|
1967
|
+
};
|
|
1968
|
+
}
|
|
1969
|
+
const entity = await this.fetchEntity(link.href);
|
|
1970
|
+
this.updateState(entity, link.href);
|
|
1971
|
+
return { content: JSON.stringify(entity.properties ?? entity, null, 2) };
|
|
1972
|
+
}
|
|
1973
|
+
default:
|
|
1974
|
+
return { content: `Unknown tool prefix: ${prefix}`, isError: true };
|
|
1975
|
+
}
|
|
1976
|
+
} catch (error) {
|
|
1977
|
+
return {
|
|
1978
|
+
content: `Error: ${error instanceof Error ? error.message : String(error)}`,
|
|
1979
|
+
isError: true
|
|
1980
|
+
};
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
/**
|
|
1984
|
+
* Get MCP server capabilities for the initialize response.
|
|
1985
|
+
*/
|
|
1986
|
+
getCapabilities() {
|
|
1987
|
+
return {
|
|
1988
|
+
tools: { listChanged: true },
|
|
1989
|
+
resources: {}
|
|
1990
|
+
};
|
|
1991
|
+
}
|
|
1992
|
+
/**
|
|
1993
|
+
* Get MCP server info for the initialize response.
|
|
1994
|
+
*/
|
|
1995
|
+
getServerInfo() {
|
|
1996
|
+
return {
|
|
1997
|
+
name: this.config.name,
|
|
1998
|
+
version: this.config.version
|
|
1999
|
+
};
|
|
2000
|
+
}
|
|
2001
|
+
// ===========================================================================
|
|
2002
|
+
// Private
|
|
2003
|
+
// ===========================================================================
|
|
2004
|
+
async fetchEntity(url) {
|
|
2005
|
+
const response = await fetch(url, {
|
|
2006
|
+
headers: {
|
|
2007
|
+
Accept: "application/vnd.siren+json, application/json",
|
|
2008
|
+
...this.config.headers
|
|
2009
|
+
}
|
|
2010
|
+
});
|
|
2011
|
+
if (!response.ok) {
|
|
2012
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
2013
|
+
}
|
|
2014
|
+
return await response.json();
|
|
2015
|
+
}
|
|
2016
|
+
async executeAction(action, data) {
|
|
2017
|
+
const method = action.method?.toUpperCase() ?? "POST";
|
|
2018
|
+
const headers = {
|
|
2019
|
+
Accept: "application/vnd.siren+json, application/json",
|
|
2020
|
+
...this.config.headers
|
|
2021
|
+
};
|
|
2022
|
+
let body;
|
|
2023
|
+
if (method !== "GET" && method !== "HEAD") {
|
|
2024
|
+
headers["Content-Type"] = action.type ?? "application/json";
|
|
2025
|
+
body = JSON.stringify(data);
|
|
2026
|
+
}
|
|
2027
|
+
const response = await fetch(action.href, { method, headers, body });
|
|
2028
|
+
if (!response.ok) {
|
|
2029
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
2030
|
+
}
|
|
2031
|
+
const entity = await response.json();
|
|
2032
|
+
this.updateState(entity, action.href);
|
|
2033
|
+
return entity;
|
|
2034
|
+
}
|
|
2035
|
+
updateState(entity, url) {
|
|
2036
|
+
this.currentEntity = entity;
|
|
2037
|
+
this.currentUrl = url;
|
|
2038
|
+
this.currentTools = entityToTools(entity);
|
|
2039
|
+
for (const listener of this.toolChangeListeners) {
|
|
2040
|
+
listener();
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
};
|
|
2044
|
+
function createMcpWorkflowServer(config) {
|
|
2045
|
+
return new McpWorkflowServer(config);
|
|
2046
|
+
}
|
|
2047
|
+
|
|
2048
|
+
// src/index.ts
|
|
2049
|
+
function createFlowDSL() {
|
|
2050
|
+
return /* @__PURE__ */ createMultilingualDSL({
|
|
2051
|
+
name: "FlowScript",
|
|
2052
|
+
schemas: allSchemas,
|
|
2053
|
+
languages: [
|
|
2054
|
+
{
|
|
2055
|
+
code: "en",
|
|
2056
|
+
name: "English",
|
|
2057
|
+
nativeName: "English",
|
|
2058
|
+
tokenizer: EnglishFlowTokenizer,
|
|
2059
|
+
patternProfile: englishProfile
|
|
2060
|
+
},
|
|
2061
|
+
{
|
|
2062
|
+
code: "es",
|
|
2063
|
+
name: "Spanish",
|
|
2064
|
+
nativeName: "Espa\xF1ol",
|
|
2065
|
+
tokenizer: SpanishFlowTokenizer,
|
|
2066
|
+
patternProfile: spanishProfile
|
|
2067
|
+
},
|
|
2068
|
+
{
|
|
2069
|
+
code: "ja",
|
|
2070
|
+
name: "Japanese",
|
|
2071
|
+
nativeName: "\u65E5\u672C\u8A9E",
|
|
2072
|
+
tokenizer: JapaneseFlowTokenizer,
|
|
2073
|
+
patternProfile: japaneseProfile
|
|
2074
|
+
},
|
|
2075
|
+
{
|
|
2076
|
+
code: "ar",
|
|
2077
|
+
name: "Arabic",
|
|
2078
|
+
nativeName: "\u0627\u0644\u0639\u0631\u0628\u064A\u0629",
|
|
2079
|
+
tokenizer: ArabicFlowTokenizer,
|
|
2080
|
+
patternProfile: arabicProfile
|
|
2081
|
+
},
|
|
2082
|
+
{
|
|
2083
|
+
code: "ko",
|
|
2084
|
+
name: "Korean",
|
|
2085
|
+
nativeName: "\uD55C\uAD6D\uC5B4",
|
|
2086
|
+
tokenizer: KoreanFlowTokenizer,
|
|
2087
|
+
patternProfile: koreanProfile
|
|
2088
|
+
},
|
|
2089
|
+
{
|
|
2090
|
+
code: "zh",
|
|
2091
|
+
name: "Chinese",
|
|
2092
|
+
nativeName: "\u4E2D\u6587",
|
|
2093
|
+
tokenizer: ChineseFlowTokenizer,
|
|
2094
|
+
patternProfile: chineseProfile
|
|
2095
|
+
},
|
|
2096
|
+
{
|
|
2097
|
+
code: "tr",
|
|
2098
|
+
name: "Turkish",
|
|
2099
|
+
nativeName: "T\xFCrk\xE7e",
|
|
2100
|
+
tokenizer: TurkishFlowTokenizer,
|
|
2101
|
+
patternProfile: turkishProfile
|
|
2102
|
+
},
|
|
2103
|
+
{
|
|
2104
|
+
code: "fr",
|
|
2105
|
+
name: "French",
|
|
2106
|
+
nativeName: "Fran\xE7ais",
|
|
2107
|
+
tokenizer: FrenchFlowTokenizer,
|
|
2108
|
+
patternProfile: frenchProfile
|
|
2109
|
+
}
|
|
2110
|
+
],
|
|
2111
|
+
codeGenerator: flowCodeGenerator
|
|
2112
|
+
});
|
|
2113
|
+
}
|
|
2114
|
+
var flowScanConfig = {
|
|
2115
|
+
attributes: ["data-flow", "_flow"],
|
|
2116
|
+
scriptTypes: ["text/flowscript"],
|
|
2117
|
+
defaultLanguage: "en"
|
|
2118
|
+
};
|
|
2119
|
+
export {
|
|
2120
|
+
ArabicFlowTokenizer,
|
|
2121
|
+
ChineseFlowTokenizer,
|
|
2122
|
+
EnglishFlowTokenizer,
|
|
2123
|
+
FrenchFlowTokenizer,
|
|
2124
|
+
JapaneseFlowTokenizer,
|
|
2125
|
+
KoreanFlowTokenizer,
|
|
2126
|
+
McpWorkflowServer,
|
|
2127
|
+
SpanishFlowTokenizer,
|
|
2128
|
+
TurkishFlowTokenizer,
|
|
2129
|
+
actionsToTools,
|
|
2130
|
+
allSchemas,
|
|
2131
|
+
arabicProfile,
|
|
2132
|
+
captureSchema,
|
|
2133
|
+
chineseProfile,
|
|
2134
|
+
compilePipeline,
|
|
2135
|
+
createFlowDSL,
|
|
2136
|
+
createMcpWorkflowServer,
|
|
2137
|
+
createWorkflowExecutor,
|
|
2138
|
+
englishProfile,
|
|
2139
|
+
enterSchema,
|
|
2140
|
+
entityToTools,
|
|
2141
|
+
executeWorkflow,
|
|
2142
|
+
extractRoute,
|
|
2143
|
+
extractRoutes,
|
|
2144
|
+
fetchSchema,
|
|
2145
|
+
flowCodeGenerator,
|
|
2146
|
+
flowScanConfig,
|
|
2147
|
+
followSchema,
|
|
2148
|
+
frenchProfile,
|
|
2149
|
+
generateHTMX,
|
|
2150
|
+
hateoasSchemas,
|
|
2151
|
+
japaneseProfile,
|
|
2152
|
+
koreanProfile,
|
|
2153
|
+
linksToTools,
|
|
2154
|
+
parseDuration,
|
|
2155
|
+
parseFlowPipeline,
|
|
2156
|
+
performSchema,
|
|
2157
|
+
pollSchema,
|
|
2158
|
+
renderFlow,
|
|
2159
|
+
spanishProfile,
|
|
2160
|
+
streamSchema,
|
|
2161
|
+
submitSchema,
|
|
2162
|
+
toFlowSpec,
|
|
2163
|
+
toSirenGrailSteps,
|
|
2164
|
+
toSirenSteps,
|
|
2165
|
+
toWorkflowSpec,
|
|
2166
|
+
transformSchema,
|
|
2167
|
+
turkishProfile
|
|
2168
|
+
};
|
|
2169
|
+
//# sourceMappingURL=index.js.map
|