@lokascript/framework 2.0.0 → 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.
Files changed (126) hide show
  1. package/dist/aot/index.d.ts +1 -0
  2. package/dist/aot/index.d.ts.map +1 -1
  3. package/dist/aot/registry-bridge.d.ts +27 -0
  4. package/dist/aot/registry-bridge.d.ts.map +1 -0
  5. package/dist/api/create-dsl.d.ts.map +1 -1
  6. package/dist/api/dispatcher.d.ts +22 -0
  7. package/dist/api/dispatcher.d.ts.map +1 -1
  8. package/dist/api/domain-registry.d.ts +41 -0
  9. package/dist/api/domain-registry.d.ts.map +1 -1
  10. package/dist/api/index.js +1351 -21
  11. package/dist/api/index.js.map +1 -1
  12. package/dist/core/index.js +114 -10
  13. package/dist/core/index.js.map +1 -1
  14. package/dist/core/pattern-matching/index.js +74 -10
  15. package/dist/core/pattern-matching/index.js.map +1 -1
  16. package/dist/core/pattern-matching/pattern-matcher.d.ts.map +1 -1
  17. package/dist/core/types.d.ts +80 -2
  18. package/dist/core/types.d.ts.map +1 -1
  19. package/dist/core/types.js +40 -0
  20. package/dist/core/types.js.map +1 -1
  21. package/dist/feedback/confidence-gate.d.ts +28 -0
  22. package/dist/feedback/confidence-gate.d.ts.map +1 -0
  23. package/dist/feedback/feedback-formatter.d.ts +21 -0
  24. package/dist/feedback/feedback-formatter.d.ts.map +1 -0
  25. package/dist/feedback/index.d.ts +11 -0
  26. package/dist/feedback/index.d.ts.map +1 -0
  27. package/dist/feedback/pattern-tracker.d.ts +47 -0
  28. package/dist/feedback/pattern-tracker.d.ts.map +1 -0
  29. package/dist/feedback/types.d.ts +113 -0
  30. package/dist/feedback/types.d.ts.map +1 -0
  31. package/dist/generation/index.js +2 -1
  32. package/dist/generation/index.js.map +1 -1
  33. package/dist/index.cjs +2685 -71
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.ts +6 -2
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +2652 -71
  38. package/dist/index.js.map +1 -1
  39. package/dist/ir/explicit-parser.d.ts +31 -0
  40. package/dist/ir/explicit-parser.d.ts.map +1 -0
  41. package/dist/ir/explicit-renderer.d.ts +17 -0
  42. package/dist/ir/explicit-renderer.d.ts.map +1 -0
  43. package/dist/ir/from-interchange.d.ts +51 -0
  44. package/dist/ir/from-interchange.d.ts.map +1 -0
  45. package/dist/ir/index.d.ts +29 -0
  46. package/dist/ir/index.d.ts.map +1 -0
  47. package/dist/ir/index.js +1557 -0
  48. package/dist/ir/index.js.map +1 -0
  49. package/dist/ir/json-schema.d.ts +40 -0
  50. package/dist/ir/json-schema.d.ts.map +1 -0
  51. package/dist/ir/protocol-json.d.ts +73 -0
  52. package/dist/ir/protocol-json.d.ts.map +1 -0
  53. package/dist/ir/references.d.ts +19 -0
  54. package/dist/ir/references.d.ts.map +1 -0
  55. package/dist/ir/types.d.ts +141 -0
  56. package/dist/ir/types.d.ts.map +1 -0
  57. package/dist/parsing/index.js +74 -10
  58. package/dist/parsing/index.js.map +1 -1
  59. package/dist/prompts/index.d.ts +9 -0
  60. package/dist/prompts/index.d.ts.map +1 -0
  61. package/dist/prompts/prompt-generator.d.ts +49 -0
  62. package/dist/prompts/prompt-generator.d.ts.map +1 -0
  63. package/dist/prompts/types.d.ts +64 -0
  64. package/dist/prompts/types.d.ts.map +1 -0
  65. package/dist/schema/command-schema.d.ts +17 -0
  66. package/dist/schema/command-schema.d.ts.map +1 -1
  67. package/dist/schema/index.js.map +1 -1
  68. package/dist/testing/index.js +3 -0
  69. package/dist/testing/index.js.map +1 -1
  70. package/dist/training/index.d.ts +11 -0
  71. package/dist/training/index.d.ts.map +1 -0
  72. package/dist/training/jsonl-writer.d.ts +42 -0
  73. package/dist/training/jsonl-writer.d.ts.map +1 -0
  74. package/dist/training/schema-synthesizer.d.ts +20 -0
  75. package/dist/training/schema-synthesizer.d.ts.map +1 -0
  76. package/dist/training/types.d.ts +73 -0
  77. package/dist/training/types.d.ts.map +1 -0
  78. package/package.json +5 -1
  79. package/src/aot/index.ts +1 -0
  80. package/src/aot/registry-bridge.test.ts +104 -0
  81. package/src/aot/registry-bridge.ts +66 -0
  82. package/src/api/create-dsl.test.ts +306 -0
  83. package/src/api/create-dsl.ts +21 -0
  84. package/src/api/dispatcher.test.ts +66 -0
  85. package/src/api/dispatcher.ts +77 -9
  86. package/src/api/domain-registry.ts +120 -4
  87. package/src/core/pattern-matching/pattern-matcher.test.ts +131 -0
  88. package/src/core/pattern-matching/pattern-matcher.ts +102 -19
  89. package/src/core/types.ts +145 -2
  90. package/src/feedback/confidence-gate.test.ts +111 -0
  91. package/src/feedback/confidence-gate.ts +70 -0
  92. package/src/feedback/feedback-formatter.test.ts +198 -0
  93. package/src/feedback/feedback-formatter.ts +298 -0
  94. package/src/feedback/index.ts +24 -0
  95. package/src/feedback/pattern-tracker.test.ts +198 -0
  96. package/src/feedback/pattern-tracker.ts +140 -0
  97. package/src/feedback/types.ts +149 -0
  98. package/src/generation/pattern-generator.ts +2 -2
  99. package/src/index.ts +22 -0
  100. package/src/integration/llm-lse-integration.test.ts +457 -0
  101. package/src/ir/explicit-parser.test.ts +306 -0
  102. package/src/ir/explicit-parser.ts +419 -0
  103. package/src/ir/explicit-renderer.test.ts +144 -0
  104. package/src/ir/explicit-renderer.ts +146 -0
  105. package/src/ir/from-interchange.test.ts +853 -0
  106. package/src/ir/from-interchange.ts +594 -0
  107. package/src/ir/index.ts +66 -0
  108. package/src/ir/json-schema.test.ts +281 -0
  109. package/src/ir/json-schema.ts +299 -0
  110. package/src/ir/protocol-json.test.ts +1185 -0
  111. package/src/ir/protocol-json.ts +745 -0
  112. package/src/ir/references.test.ts +38 -0
  113. package/src/ir/references.ts +33 -0
  114. package/src/ir/round-trip.test.ts +557 -0
  115. package/src/ir/types.ts +181 -0
  116. package/src/prompts/index.ts +15 -0
  117. package/src/prompts/prompt-generator.test.ts +386 -0
  118. package/src/prompts/prompt-generator.ts +531 -0
  119. package/src/prompts/types.ts +89 -0
  120. package/src/schema/command-schema.ts +19 -0
  121. package/src/training/index.ts +13 -0
  122. package/src/training/jsonl-writer.test.ts +138 -0
  123. package/src/training/jsonl-writer.ts +67 -0
  124. package/src/training/schema-synthesizer.test.ts +250 -0
  125. package/src/training/schema-synthesizer.ts +306 -0
  126. package/src/training/types.ts +103 -0
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Training Data Generation
3
+ *
4
+ * Generates (natural_language, LSE) pairs from domain schemas for
5
+ * fine-tuning or few-shot prompting LLMs. Exports as JSONL.
6
+ */
7
+ export type { TrainingPair, SynthesisConfig, SynthesisResult, SynthesisMetadata } from './types';
8
+ export { synthesizeFromSchemas } from './schema-synthesizer';
9
+ export { toJSONL, toJSONLRow, parseJSONL } from './jsonl-writer';
10
+ export type { JSONLRow } from './jsonl-writer';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/training/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjG,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAE7D,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AACjE,YAAY,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * JSONL Writer
3
+ *
4
+ * Exports training pairs in JSONL (JSON Lines) format,
5
+ * compatible with standard fine-tuning pipelines.
6
+ */
7
+ import type { TrainingPair } from './types';
8
+ /**
9
+ * Convert training pairs to JSONL format (one JSON object per line).
10
+ *
11
+ * Each line contains:
12
+ * - id: unique pair identifier
13
+ * - prompt: the natural language input
14
+ * - completion: the LSE bracket syntax output
15
+ * - metadata: domain, action, language, source, quality
16
+ */
17
+ export declare function toJSONL(pairs: readonly TrainingPair[]): string;
18
+ /**
19
+ * Convert a single training pair to a JSONL row.
20
+ */
21
+ export declare function toJSONLRow(pair: TrainingPair): JSONLRow;
22
+ /**
23
+ * Parse JSONL back into rows (for validation/roundtrip testing).
24
+ */
25
+ export declare function parseJSONL(text: string): JSONLRow[];
26
+ /**
27
+ * A single row in JSONL format.
28
+ */
29
+ export interface JSONLRow {
30
+ readonly id: string;
31
+ readonly prompt: string;
32
+ readonly completion: string;
33
+ readonly metadata: {
34
+ readonly domain: string;
35
+ readonly action: string;
36
+ readonly language: string;
37
+ readonly source: string;
38
+ readonly quality: number;
39
+ readonly confidence?: number;
40
+ };
41
+ }
42
+ //# sourceMappingURL=jsonl-writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jsonl-writer.d.ts","sourceRoot":"","sources":["../../src/training/jsonl-writer.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAE5C;;;;;;;;GAQG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,GAAG,MAAM,CAE9D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,CAcvD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAKnD;AAED;;GAEG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE;QACjB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;KAC9B,CAAC;CACH"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Schema Synthesizer
3
+ *
4
+ * Generates synthetic (natural_language, LSE) training pairs from
5
+ * CommandSchema[]. Enumerates valid role combinations and produces
6
+ * examples in both bracket syntax and JSON wire format.
7
+ */
8
+ import type { CommandSchema } from '../schema/command-schema';
9
+ import type { SynthesisConfig, SynthesisResult } from './types';
10
+ /**
11
+ * Synthesize training pairs from command schemas.
12
+ *
13
+ * Generates multiple examples per command by enumerating role combinations:
14
+ * 1. Required roles only (minimum viable)
15
+ * 2. Required + each optional role individually
16
+ * 3. Required + all optional roles (maximum)
17
+ * 4. Shuffled role order variants (teaches role-label independence)
18
+ */
19
+ export declare function synthesizeFromSchemas(schemas: readonly CommandSchema[], config: SynthesisConfig): SynthesisResult;
20
+ //# sourceMappingURL=schema-synthesizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema-synthesizer.d.ts","sourceRoot":"","sources":["../../src/training/schema-synthesizer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAY,MAAM,0BAA0B,CAAC;AAIxE,OAAO,KAAK,EAAgB,eAAe,EAAE,eAAe,EAAqB,MAAM,SAAS,CAAC;AAMjG;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,SAAS,aAAa,EAAE,EACjC,MAAM,EAAE,eAAe,GACtB,eAAe,CA2DjB"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Training Data Types
3
+ *
4
+ * Types for generating (natural_language, LSE) training pairs from
5
+ * domain schemas. Used for fine-tuning or few-shot prompting LLMs
6
+ * to output valid LokaScript Explicit Syntax.
7
+ */
8
+ import type { SemanticJSON } from '../ir/types';
9
+ /**
10
+ * A single training pair: natural language ↔ explicit syntax.
11
+ */
12
+ export interface TrainingPair {
13
+ /** Unique identifier for deduplication */
14
+ readonly id: string;
15
+ /** Natural language form (e.g., "fetch /api/users as json into #list") */
16
+ readonly natural: string;
17
+ /** Language code (e.g., "en") */
18
+ readonly language: string;
19
+ /** LSE bracket syntax (e.g., "[fetch source:/api/users style:json destination:#list]") */
20
+ readonly explicit: string;
21
+ /** LLM-simplified JSON wire format */
22
+ readonly json: SemanticJSON;
23
+ /** Domain this pair belongs to */
24
+ readonly domain: string;
25
+ /** Command action */
26
+ readonly action: string;
27
+ /** How this pair was generated */
28
+ readonly source: 'synthetic' | 'test' | 'compilation';
29
+ /** Parse confidence (if from real parsing) */
30
+ readonly confidence?: number;
31
+ /** Quality score 0-1 for filtering */
32
+ readonly quality: number;
33
+ }
34
+ /**
35
+ * Configuration for training data synthesis.
36
+ */
37
+ export interface SynthesisConfig {
38
+ /** Domain identifier */
39
+ readonly domain: string;
40
+ /** Maximum pairs per command */
41
+ readonly maxPairsPerCommand?: number;
42
+ /** Minimum quality threshold (0-1). Default: 0.5 */
43
+ readonly minQuality?: number;
44
+ /** Whether to shuffle role order for some examples. Default: true */
45
+ readonly shuffleRoles?: boolean;
46
+ /** Language codes to generate for. Default: ['en'] */
47
+ readonly languages?: readonly string[];
48
+ }
49
+ /**
50
+ * Result of training data synthesis.
51
+ */
52
+ export interface SynthesisResult {
53
+ /** Generated training pairs */
54
+ readonly pairs: readonly TrainingPair[];
55
+ /** Metadata about the synthesis */
56
+ readonly metadata: SynthesisMetadata;
57
+ }
58
+ /**
59
+ * Metadata about a synthesis run.
60
+ */
61
+ export interface SynthesisMetadata {
62
+ /** Domain the data was synthesized for */
63
+ readonly domain: string;
64
+ /** Number of command schemas processed */
65
+ readonly commandCount: number;
66
+ /** Total pairs generated */
67
+ readonly pairCount: number;
68
+ /** Pairs per source type */
69
+ readonly bySource: Record<string, number>;
70
+ /** Languages included */
71
+ readonly languages: readonly string[];
72
+ }
73
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/training/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAMhD;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IAEpB,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAEzB,iCAAiC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,0FAA0F;IAC1F,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAE1B,sCAAsC;IACtC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;IAE5B,kCAAkC;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,qBAAqB;IACrB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,kCAAkC;IAClC,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,GAAG,aAAa,CAAC;IAEtD,8CAA8C;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAE7B,sCAAsC;IACtC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAMD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,wBAAwB;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,gCAAgC;IAChC,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAErC,oDAAoD;IACpD,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAE7B,qEAAqE;IACrE,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC;IAEhC,sDAAsD;IACtD,QAAQ,CAAC,SAAS,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,+BAA+B;IAC/B,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAC;IAExC,mCAAmC;IACnC,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,0CAA0C;IAC1C,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAExB,0CAA0C;IAC1C,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAE9B,4BAA4B;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,4BAA4B;IAC5B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE1C,yBAAyB;IACzB,QAAQ,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CACvC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lokascript/framework",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "Generic framework for building multilingual DSLs with semantic parsing and grammar transformation",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -55,6 +55,10 @@
55
55
  "./testing": {
56
56
  "types": "./dist/testing/index.d.ts",
57
57
  "import": "./dist/testing/index.js"
58
+ },
59
+ "./ir": {
60
+ "types": "./dist/ir/index.d.ts",
61
+ "import": "./dist/ir/index.js"
58
62
  }
59
63
  },
60
64
  "scripts": {
package/src/aot/index.ts CHANGED
@@ -6,3 +6,4 @@ export * from './types';
6
6
  export { DomainAwareScanner } from './domain-scanner';
7
7
  export { AOTOrchestrator } from './aot-orchestrator';
8
8
  export type { DomainCompilationBackend } from './aot-orchestrator';
9
+ export { registryToAOTBackends } from './registry-bridge';
@@ -0,0 +1,104 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { DomainRegistry } from '../api/domain-registry';
3
+ import { registryToAOTBackends } from './registry-bridge';
4
+
5
+ describe('registryToAOTBackends', () => {
6
+ it('converts domains with scanConfig to backends', async () => {
7
+ const registry = new DomainRegistry();
8
+
9
+ registry.register({
10
+ name: 'test-domain',
11
+ description: 'Test domain',
12
+ languages: ['en'],
13
+ inputLabel: 'input',
14
+ inputDescription: 'Test input',
15
+ getDSL: () => import('@lokascript/domain-sql').then(m => m.createSQLDSL()),
16
+ scanConfig: {
17
+ attributes: ['data-test'],
18
+ scriptTypes: ['text/test'],
19
+ defaultLanguage: 'en',
20
+ },
21
+ });
22
+
23
+ const backends = await registryToAOTBackends(registry);
24
+ expect(backends).toHaveLength(1);
25
+ expect(backends[0].domain).toBe('test-domain');
26
+ expect(backends[0].scanConfig.domain).toBe('test-domain');
27
+ expect(backends[0].scanConfig.attributes).toEqual(['data-test']);
28
+ expect(backends[0].dsl).toBeDefined();
29
+ expect(backends[0].codeGenerator).toBeDefined();
30
+ });
31
+
32
+ it('skips domains without scanConfig', async () => {
33
+ const registry = new DomainRegistry();
34
+
35
+ registry.register({
36
+ name: 'with-config',
37
+ description: 'Has scanConfig',
38
+ languages: ['en'],
39
+ inputLabel: 'input',
40
+ inputDescription: 'Test',
41
+ getDSL: () => import('@lokascript/domain-sql').then(m => m.createSQLDSL()),
42
+ scanConfig: {
43
+ attributes: ['data-with'],
44
+ defaultLanguage: 'en',
45
+ },
46
+ });
47
+
48
+ registry.register({
49
+ name: 'without-config',
50
+ description: 'No scanConfig',
51
+ languages: ['en'],
52
+ inputLabel: 'input',
53
+ inputDescription: 'Test',
54
+ getDSL: () => import('@lokascript/domain-sql').then(m => m.createSQLDSL()),
55
+ // no scanConfig
56
+ });
57
+
58
+ const backends = await registryToAOTBackends(registry);
59
+ expect(backends).toHaveLength(1);
60
+ expect(backends[0].domain).toBe('with-config');
61
+ });
62
+
63
+ it('returns empty array when no domains have scanConfig', async () => {
64
+ const registry = new DomainRegistry();
65
+
66
+ registry.register({
67
+ name: 'no-scan',
68
+ description: 'No scanConfig',
69
+ languages: ['en'],
70
+ inputLabel: 'input',
71
+ inputDescription: 'Test',
72
+ getDSL: () => import('@lokascript/domain-sql').then(m => m.createSQLDSL()),
73
+ });
74
+
75
+ const backends = await registryToAOTBackends(registry);
76
+ expect(backends).toHaveLength(0);
77
+ });
78
+
79
+ it('codeGenerator produces JSON from semantic node', async () => {
80
+ const registry = new DomainRegistry();
81
+
82
+ registry.register({
83
+ name: 'gen-test',
84
+ description: 'Generator test',
85
+ languages: ['en'],
86
+ inputLabel: 'input',
87
+ inputDescription: 'Test',
88
+ getDSL: () => import('@lokascript/domain-sql').then(m => m.createSQLDSL()),
89
+ scanConfig: {
90
+ attributes: ['data-gen'],
91
+ defaultLanguage: 'en',
92
+ },
93
+ });
94
+
95
+ const backends = await registryToAOTBackends(registry);
96
+ const node = {
97
+ action: 'select',
98
+ roles: new Map([['source', { type: 'expression' as const, raw: 'users' }]]),
99
+ };
100
+ const output = backends[0].codeGenerator.generate(node as any);
101
+ const parsed = JSON.parse(output);
102
+ expect(parsed.action).toBe('select');
103
+ });
104
+ });
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Registry-to-AOT Bridge
3
+ *
4
+ * Converts DomainRegistry entries into DomainCompilationBackend instances
5
+ * for use with AOTOrchestrator. Only domains that have scanConfig defined
6
+ * are included.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * const registry = new DomainRegistry();
11
+ * // ... register domains with scanConfig ...
12
+ *
13
+ * const backends = await registryToAOTBackends(registry);
14
+ * const orchestrator = new AOTOrchestrator();
15
+ * for (const backend of backends) {
16
+ * orchestrator.registerBackend(backend);
17
+ * }
18
+ * ```
19
+ */
20
+
21
+ import type { DomainCompilationBackend } from './aot-orchestrator';
22
+ import type { DomainScanConfig } from './types';
23
+ import type { DomainRegistry } from '../api/domain-registry';
24
+ import type { CodeGenerator } from '../api/create-dsl';
25
+ import type { SemanticNode } from '../core/types';
26
+
27
+ /**
28
+ * Convert all registry entries with scanConfig into AOT backends.
29
+ * Domains without scanConfig are skipped.
30
+ */
31
+ export async function registryToAOTBackends(
32
+ registry: DomainRegistry
33
+ ): Promise<DomainCompilationBackend[]> {
34
+ const backends: DomainCompilationBackend[] = [];
35
+
36
+ for (const name of registry.getDomainNames()) {
37
+ const descriptor = registry.getDescriptor(name);
38
+ if (!descriptor?.scanConfig) continue;
39
+
40
+ const dsl = await registry.getDSLForDomain(name);
41
+ if (!dsl) continue;
42
+
43
+ const scanConfig: DomainScanConfig = {
44
+ domain: name,
45
+ attributes: [...descriptor.scanConfig.attributes],
46
+ ...(descriptor.scanConfig.scriptTypes
47
+ ? { scriptTypes: [...descriptor.scanConfig.scriptTypes] }
48
+ : {}),
49
+ defaultLanguage: descriptor.scanConfig.defaultLanguage ?? 'en',
50
+ };
51
+
52
+ // The AOTOrchestrator only calls backend.dsl.compile(), not codeGenerator directly.
53
+ // Provide a thin adapter that parses then generates via the DSL's internal pipeline.
54
+ const codeGenerator: CodeGenerator = {
55
+ generate(node: SemanticNode): string {
56
+ // Fallback: compile through the DSL by reconstructing explicit syntax.
57
+ // In practice, AOTOrchestrator uses dsl.compile() directly.
58
+ return JSON.stringify({ action: node.action, roles: Object.fromEntries(node.roles) });
59
+ },
60
+ };
61
+
62
+ backends.push({ domain: name, dsl, codeGenerator, scanConfig });
63
+ }
64
+
65
+ return backends;
66
+ }
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Tests for MultilingualDSLImpl explicit syntax support.
3
+ *
4
+ * Verifies that createMultilingualDSL() returns a DSL that accepts
5
+ * explicit bracket syntax [command role:value ...] as input, in addition
6
+ * to natural language.
7
+ */
8
+
9
+ import { describe, it, expect } from 'vitest';
10
+ import {
11
+ createMultilingualDSL,
12
+ defineCommand,
13
+ defineRole,
14
+ BaseTokenizer,
15
+ getDefaultExtractors,
16
+ type SemanticNode,
17
+ } from '../index';
18
+
19
+ // =============================================================================
20
+ // Test Helpers
21
+ // =============================================================================
22
+
23
+ const selectSchema = defineCommand({
24
+ action: 'select',
25
+ description: 'Select data',
26
+ category: 'query',
27
+ primaryRole: 'columns',
28
+ roles: [
29
+ defineRole({
30
+ role: 'columns',
31
+ description: 'Columns to select',
32
+ required: true,
33
+ expectedTypes: ['expression'],
34
+ svoPosition: 2,
35
+ }),
36
+ defineRole({
37
+ role: 'source',
38
+ description: 'Table to select from',
39
+ required: true,
40
+ expectedTypes: ['expression'],
41
+ svoPosition: 1,
42
+ markerOverride: { en: 'from' },
43
+ }),
44
+ ],
45
+ });
46
+
47
+ const insertSchema = defineCommand({
48
+ action: 'insert',
49
+ description: 'Insert data',
50
+ category: 'mutation',
51
+ primaryRole: 'values',
52
+ roles: [
53
+ defineRole({
54
+ role: 'values',
55
+ description: 'Values to insert',
56
+ required: true,
57
+ expectedTypes: ['expression'],
58
+ svoPosition: 2,
59
+ }),
60
+ defineRole({
61
+ role: 'destination',
62
+ description: 'Target table',
63
+ required: true,
64
+ expectedTypes: ['expression'],
65
+ svoPosition: 1,
66
+ markerOverride: { en: 'into' },
67
+ }),
68
+ ],
69
+ });
70
+
71
+ class EnglishTestTokenizer extends BaseTokenizer {
72
+ readonly language = 'en';
73
+ readonly direction = 'ltr' as const;
74
+
75
+ constructor() {
76
+ super();
77
+ this.registerExtractors(getDefaultExtractors());
78
+ }
79
+
80
+ classifyToken(token: string): 'keyword' | 'identifier' | 'literal' | 'operator' {
81
+ const keywords = ['select', 'insert', 'from', 'into'];
82
+ if (keywords.includes(token.toLowerCase())) return 'keyword';
83
+ return 'identifier';
84
+ }
85
+ }
86
+
87
+ const englishProfile = {
88
+ code: 'en',
89
+ name: 'English',
90
+ nativeName: 'English',
91
+ wordOrder: 'SVO' as const,
92
+ direction: 'ltr' as const,
93
+ caseMarking: 'preposition' as const,
94
+ keywords: {
95
+ select: { primary: 'select', aliases: [] },
96
+ insert: { primary: 'insert', aliases: [] },
97
+ from: { primary: 'from', aliases: [] },
98
+ into: { primary: 'into', aliases: [] },
99
+ },
100
+ roleMarkers: {},
101
+ };
102
+
103
+ function createTestDSL() {
104
+ return createMultilingualDSL({
105
+ name: 'TestSQL',
106
+ schemas: [selectSchema, insertSchema],
107
+ languages: [
108
+ {
109
+ code: 'en',
110
+ name: 'English',
111
+ nativeName: 'English',
112
+ tokenizer: new EnglishTestTokenizer(),
113
+ patternProfile: englishProfile,
114
+ },
115
+ ],
116
+ codeGenerator: {
117
+ generate(node: SemanticNode): string {
118
+ const action = node.action.toUpperCase();
119
+ const roles: string[] = [];
120
+ for (const [role, value] of node.roles) {
121
+ const v =
122
+ value && 'value' in value
123
+ ? value.value
124
+ : value && 'raw' in value
125
+ ? value.raw
126
+ : String(value);
127
+ roles.push(`${role}=${v}`);
128
+ }
129
+ return `${action} ${roles.join(' ')}`;
130
+ },
131
+ },
132
+ });
133
+ }
134
+
135
+ // =============================================================================
136
+ // Tests
137
+ // =============================================================================
138
+
139
+ describe('MultilingualDSL: Explicit Syntax Support', () => {
140
+ // ---------------------------------------------------------------------------
141
+ // parseWithConfidence
142
+ // ---------------------------------------------------------------------------
143
+
144
+ describe('parseWithConfidence', () => {
145
+ it('parses explicit bracket syntax with confidence 1.0', () => {
146
+ const dsl = createTestDSL();
147
+ const result = dsl.parseWithConfidence('[select columns:name source:users]', 'en');
148
+
149
+ expect(result.confidence).toBe(1.0);
150
+ expect(result.node.action).toBe('select');
151
+ expect(result.node.kind).toBe('command');
152
+ });
153
+
154
+ it('extracts roles from explicit syntax', () => {
155
+ const dsl = createTestDSL();
156
+ const result = dsl.parseWithConfidence('[select columns:name source:users]', 'en');
157
+
158
+ const columns = result.node.roles.get('columns');
159
+ const source = result.node.roles.get('source');
160
+ expect(columns).toBeDefined();
161
+ expect(source).toBeDefined();
162
+ expect(columns && 'value' in columns ? columns.value : undefined).toBe('name');
163
+ expect(source && 'value' in source ? source.value : undefined).toBe('users');
164
+ });
165
+
166
+ it('throws for explicit syntax with unknown action', () => {
167
+ const dsl = createTestDSL();
168
+ expect(() => {
169
+ dsl.parseWithConfidence('[deploy destination:production]', 'en');
170
+ }).toThrow('No schema for action "deploy"');
171
+ });
172
+
173
+ it('validates roles against schema', () => {
174
+ const dsl = createTestDSL();
175
+ expect(() => {
176
+ dsl.parseWithConfidence('[select columns:name bogus:value]', 'en');
177
+ }).toThrow('Unknown role "bogus"');
178
+ });
179
+
180
+ it('validates required roles', () => {
181
+ const dsl = createTestDSL();
182
+ expect(() => {
183
+ dsl.parseWithConfidence('[select columns:name]', 'en');
184
+ }).toThrow('Missing required role "source"');
185
+ });
186
+
187
+ it('works with any language parameter for explicit syntax', () => {
188
+ const dsl = createTestDSL();
189
+ // Explicit syntax is language-agnostic — language param is ignored
190
+ const result = dsl.parseWithConfidence('[select columns:name source:users]', 'ja');
191
+ expect(result.confidence).toBe(1.0);
192
+ expect(result.node.action).toBe('select');
193
+ });
194
+ });
195
+
196
+ // ---------------------------------------------------------------------------
197
+ // parse
198
+ // ---------------------------------------------------------------------------
199
+
200
+ describe('parse', () => {
201
+ it('parses explicit syntax and returns SemanticNode', () => {
202
+ const dsl = createTestDSL();
203
+ const node = dsl.parse('[insert values:data destination:users]', 'en');
204
+
205
+ expect(node.action).toBe('insert');
206
+ expect(node.roles.has('values')).toBe(true);
207
+ expect(node.roles.has('destination')).toBe(true);
208
+ });
209
+ });
210
+
211
+ // ---------------------------------------------------------------------------
212
+ // compile
213
+ // ---------------------------------------------------------------------------
214
+
215
+ describe('compile', () => {
216
+ it('compiles explicit syntax through code generator', () => {
217
+ const dsl = createTestDSL();
218
+ const result = dsl.compile('[select columns:name source:users]', 'en');
219
+
220
+ expect(result.ok).toBe(true);
221
+ expect(result.code).toContain('SELECT');
222
+ expect(result.code).toContain('columns=name');
223
+ expect(result.code).toContain('source=users');
224
+ });
225
+
226
+ it('returns metadata with parser=explicit and confidence=1.0', () => {
227
+ const dsl = createTestDSL();
228
+ const result = dsl.compile('[select columns:name source:users]', 'en');
229
+
230
+ expect(result.metadata?.confidence).toBe(1.0);
231
+ });
232
+
233
+ it('returns error for explicit syntax with unknown action', () => {
234
+ const dsl = createTestDSL();
235
+ const result = dsl.compile('[deploy destination:production]', 'en');
236
+
237
+ expect(result.ok).toBe(false);
238
+ expect(result.errors?.[0]).toContain('No schema for action "deploy"');
239
+ });
240
+ });
241
+
242
+ // ---------------------------------------------------------------------------
243
+ // validate
244
+ // ---------------------------------------------------------------------------
245
+
246
+ describe('validate', () => {
247
+ it('validates correct explicit syntax', () => {
248
+ const dsl = createTestDSL();
249
+ const result = dsl.validate('[select columns:name source:users]', 'en');
250
+
251
+ expect(result.valid).toBe(true);
252
+ expect(result.node?.action).toBe('select');
253
+ });
254
+
255
+ it('returns invalid for explicit syntax with unknown action', () => {
256
+ const dsl = createTestDSL();
257
+ const result = dsl.validate('[deploy destination:production]', 'en');
258
+
259
+ expect(result.valid).toBe(false);
260
+ expect(result.errors?.[0]).toContain('No schema for action "deploy"');
261
+ });
262
+
263
+ it('returns invalid for explicit syntax with bad roles', () => {
264
+ const dsl = createTestDSL();
265
+ const result = dsl.validate('[select columns:name bogus:value]', 'en');
266
+
267
+ expect(result.valid).toBe(false);
268
+ expect(result.errors?.[0]).toContain('Unknown role "bogus"');
269
+ });
270
+ });
271
+
272
+ // ---------------------------------------------------------------------------
273
+ // translate
274
+ // ---------------------------------------------------------------------------
275
+
276
+ describe('translate', () => {
277
+ it('returns explicit syntax unchanged', () => {
278
+ const dsl = createTestDSL();
279
+ const result = dsl.translate('[select columns:name source:users]', 'en', 'ja');
280
+ expect(result).toBe('[select columns:name source:users]');
281
+ });
282
+ });
283
+
284
+ // ---------------------------------------------------------------------------
285
+ // Natural language regression
286
+ // ---------------------------------------------------------------------------
287
+
288
+ describe('natural language (regression)', () => {
289
+ it('still parses natural language input', () => {
290
+ const dsl = createTestDSL();
291
+ const result = dsl.parseWithConfidence('select name from users', 'en');
292
+
293
+ expect(result.node.action).toBe('select');
294
+ expect(result.confidence).toBeGreaterThan(0);
295
+ expect(result.confidence).toBeLessThanOrEqual(1.0);
296
+ });
297
+
298
+ it('still compiles natural language input', () => {
299
+ const dsl = createTestDSL();
300
+ const result = dsl.compile('select name from users', 'en');
301
+
302
+ expect(result.ok).toBe(true);
303
+ expect(result.code).toContain('SELECT');
304
+ });
305
+ });
306
+ });