@lokascript/framework 2.0.0 → 2.2.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 (138) 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 +1438 -21
  11. package/dist/api/index.js.map +1 -1
  12. package/dist/core/index.js +247 -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/tokenization/index.js +133 -0
  18. package/dist/core/tokenization/index.js.map +1 -1
  19. package/dist/core/tokenization/morphology/base-normalizer.d.ts +94 -0
  20. package/dist/core/tokenization/morphology/base-normalizer.d.ts.map +1 -0
  21. package/dist/core/tokenization/morphology/index.d.ts +3 -1
  22. package/dist/core/tokenization/morphology/index.d.ts.map +1 -1
  23. package/dist/core/types.d.ts +81 -2
  24. package/dist/core/types.d.ts.map +1 -1
  25. package/dist/core/types.js +40 -0
  26. package/dist/core/types.js.map +1 -1
  27. package/dist/feedback/confidence-gate.d.ts +28 -0
  28. package/dist/feedback/confidence-gate.d.ts.map +1 -0
  29. package/dist/feedback/feedback-formatter.d.ts +21 -0
  30. package/dist/feedback/feedback-formatter.d.ts.map +1 -0
  31. package/dist/feedback/index.d.ts +11 -0
  32. package/dist/feedback/index.d.ts.map +1 -0
  33. package/dist/feedback/pattern-tracker.d.ts +47 -0
  34. package/dist/feedback/pattern-tracker.d.ts.map +1 -0
  35. package/dist/feedback/types.d.ts +113 -0
  36. package/dist/feedback/types.d.ts.map +1 -0
  37. package/dist/generation/index.js +2 -1
  38. package/dist/generation/index.js.map +1 -1
  39. package/dist/index.cjs +3264 -132
  40. package/dist/index.cjs.map +1 -1
  41. package/dist/index.d.ts +8 -2
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +3223 -132
  44. package/dist/index.js.map +1 -1
  45. package/dist/ir/explicit-parser.d.ts +86 -0
  46. package/dist/ir/explicit-parser.d.ts.map +1 -0
  47. package/dist/ir/explicit-renderer.d.ts +34 -0
  48. package/dist/ir/explicit-renderer.d.ts.map +1 -0
  49. package/dist/ir/from-interchange.d.ts +51 -0
  50. package/dist/ir/from-interchange.d.ts.map +1 -0
  51. package/dist/ir/index.d.ts +31 -0
  52. package/dist/ir/index.d.ts.map +1 -0
  53. package/dist/ir/index.js +1934 -0
  54. package/dist/ir/index.js.map +1 -0
  55. package/dist/ir/json-schema.d.ts +40 -0
  56. package/dist/ir/json-schema.d.ts.map +1 -0
  57. package/dist/ir/protocol-json.d.ts +73 -0
  58. package/dist/ir/protocol-json.d.ts.map +1 -0
  59. package/dist/ir/references.d.ts +19 -0
  60. package/dist/ir/references.d.ts.map +1 -0
  61. package/dist/ir/to-runtime-ast.d.ts +53 -0
  62. package/dist/ir/to-runtime-ast.d.ts.map +1 -0
  63. package/dist/ir/types.d.ts +151 -0
  64. package/dist/ir/types.d.ts.map +1 -0
  65. package/dist/parsing/index.js +74 -10
  66. package/dist/parsing/index.js.map +1 -1
  67. package/dist/prompts/index.d.ts +9 -0
  68. package/dist/prompts/index.d.ts.map +1 -0
  69. package/dist/prompts/prompt-generator.d.ts +49 -0
  70. package/dist/prompts/prompt-generator.d.ts.map +1 -0
  71. package/dist/prompts/types.d.ts +64 -0
  72. package/dist/prompts/types.d.ts.map +1 -0
  73. package/dist/schema/command-schema.d.ts +17 -0
  74. package/dist/schema/command-schema.d.ts.map +1 -1
  75. package/dist/schema/index.js.map +1 -1
  76. package/dist/testing/index.js +8673 -8260
  77. package/dist/testing/index.js.map +1 -1
  78. package/dist/training/index.d.ts +11 -0
  79. package/dist/training/index.d.ts.map +1 -0
  80. package/dist/training/jsonl-writer.d.ts +42 -0
  81. package/dist/training/jsonl-writer.d.ts.map +1 -0
  82. package/dist/training/schema-synthesizer.d.ts +20 -0
  83. package/dist/training/schema-synthesizer.d.ts.map +1 -0
  84. package/dist/training/types.d.ts +73 -0
  85. package/dist/training/types.d.ts.map +1 -0
  86. package/package.json +5 -1
  87. package/src/aot/index.ts +1 -0
  88. package/src/aot/registry-bridge.test.ts +104 -0
  89. package/src/aot/registry-bridge.ts +66 -0
  90. package/src/api/create-dsl.test.ts +306 -0
  91. package/src/api/create-dsl.ts +21 -0
  92. package/src/api/dispatcher.test.ts +66 -0
  93. package/src/api/dispatcher.ts +77 -9
  94. package/src/api/domain-registry.ts +120 -4
  95. package/src/core/pattern-matching/pattern-matcher.test.ts +131 -0
  96. package/src/core/pattern-matching/pattern-matcher.ts +102 -19
  97. package/src/core/tokenization/morphology/base-normalizer.ts +249 -0
  98. package/src/core/tokenization/morphology/index.ts +3 -1
  99. package/src/core/types.ts +147 -2
  100. package/src/feedback/confidence-gate.test.ts +111 -0
  101. package/src/feedback/confidence-gate.ts +70 -0
  102. package/src/feedback/feedback-formatter.test.ts +198 -0
  103. package/src/feedback/feedback-formatter.ts +298 -0
  104. package/src/feedback/index.ts +24 -0
  105. package/src/feedback/pattern-tracker.test.ts +198 -0
  106. package/src/feedback/pattern-tracker.ts +140 -0
  107. package/src/feedback/types.ts +149 -0
  108. package/src/generation/pattern-generator.ts +2 -2
  109. package/src/index.ts +29 -0
  110. package/src/integration/llm-lse-integration.test.ts +457 -0
  111. package/src/ir/conformance.test.ts +120 -0
  112. package/src/ir/explicit-parser.test.ts +612 -0
  113. package/src/ir/explicit-parser.ts +793 -0
  114. package/src/ir/explicit-renderer.test.ts +229 -0
  115. package/src/ir/explicit-renderer.ts +198 -0
  116. package/src/ir/from-interchange.test.ts +853 -0
  117. package/src/ir/from-interchange.ts +594 -0
  118. package/src/ir/index.ts +77 -0
  119. package/src/ir/json-schema.test.ts +281 -0
  120. package/src/ir/json-schema.ts +299 -0
  121. package/src/ir/protocol-json.test.ts +1187 -0
  122. package/src/ir/protocol-json.ts +751 -0
  123. package/src/ir/references.test.ts +38 -0
  124. package/src/ir/references.ts +33 -0
  125. package/src/ir/round-trip.test.ts +557 -0
  126. package/src/ir/to-runtime-ast.ts +203 -0
  127. package/src/ir/types.ts +191 -0
  128. package/src/prompts/index.ts +15 -0
  129. package/src/prompts/prompt-generator.test.ts +386 -0
  130. package/src/prompts/prompt-generator.ts +531 -0
  131. package/src/prompts/types.ts +89 -0
  132. package/src/schema/command-schema.ts +19 -0
  133. package/src/training/index.ts +13 -0
  134. package/src/training/jsonl-writer.test.ts +138 -0
  135. package/src/training/jsonl-writer.ts +67 -0
  136. package/src/training/schema-synthesizer.test.ts +250 -0
  137. package/src/training/schema-synthesizer.ts +306 -0
  138. package/src/training/types.ts +103 -0
@@ -0,0 +1,229 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { renderExplicit, renderDocument } from './explicit-renderer';
3
+ import { parseExplicit, parseCompound } from './explicit-parser';
4
+ import {
5
+ createCommandNode,
6
+ createSelector,
7
+ createLiteral,
8
+ createReference,
9
+ createFlag,
10
+ createEventHandlerNode,
11
+ createCompoundNode,
12
+ } from '../core/types';
13
+ import type { SemanticNode, LSEEnvelope } from '../core/types';
14
+
15
+ describe('renderExplicit', () => {
16
+ it('renders a basic command', () => {
17
+ const node = createCommandNode('toggle', {
18
+ patient: createSelector('.active'),
19
+ });
20
+ expect(renderExplicit(node)).toBe('[toggle patient:.active]');
21
+ });
22
+
23
+ it('renders multiple roles', () => {
24
+ const node = createCommandNode('put', {
25
+ patient: createLiteral('hello', 'string'),
26
+ destination: createSelector('#output', 'id'),
27
+ });
28
+ const result = renderExplicit(node);
29
+ expect(result).toContain('put');
30
+ expect(result).toContain('patient:"hello"');
31
+ expect(result).toContain('destination:#output');
32
+ expect(result.startsWith('[')).toBe(true);
33
+ expect(result.endsWith(']')).toBe(true);
34
+ });
35
+
36
+ it('renders reference values', () => {
37
+ const node = createCommandNode('add', {
38
+ patient: createSelector('.clicked'),
39
+ destination: createReference('me'),
40
+ });
41
+ expect(renderExplicit(node)).toBe('[add patient:.clicked destination:me]');
42
+ });
43
+
44
+ it('renders numeric values', () => {
45
+ const node = createCommandNode('increment', {
46
+ destination: createSelector('#count', 'id'),
47
+ quantity: createLiteral(5, 'number'),
48
+ });
49
+ expect(renderExplicit(node)).toBe('[increment destination:#count quantity:5]');
50
+ });
51
+
52
+ it('renders boolean values', () => {
53
+ const node = createCommandNode('set', {
54
+ destination: createLiteral('myVar', 'string'),
55
+ goal: createLiteral(true, 'boolean'),
56
+ });
57
+ const result = renderExplicit(node);
58
+ expect(result).toContain('goal:true');
59
+ });
60
+
61
+ it('renders event handler with body', () => {
62
+ const bodyNode = createCommandNode('toggle', {
63
+ patient: createSelector('.active'),
64
+ });
65
+ const node = createEventHandlerNode('on', { event: createLiteral('click', 'string') }, [
66
+ bodyNode,
67
+ ]);
68
+ const result = renderExplicit(node);
69
+ expect(result).toContain('[on event:"click"');
70
+ expect(result).toContain('body:[toggle patient:.active]');
71
+ });
72
+
73
+ it('renders compound nodes', () => {
74
+ const node1 = createCommandNode('add', {
75
+ patient: createSelector('.loading'),
76
+ });
77
+ const node2 = createCommandNode('fetch', {
78
+ source: createLiteral('/api/data', 'string'),
79
+ });
80
+ const compound = createCompoundNode([node1, node2], 'then');
81
+ const result = renderExplicit(compound);
82
+ expect(result).toBe('[add patient:.loading] then [fetch source:"/api/data"]');
83
+ });
84
+ });
85
+
86
+ describe('renderExplicit — flags', () => {
87
+ it('renders enabled flags as +name', () => {
88
+ const node = createCommandNode('column', {
89
+ name: createLiteral('id', 'string'),
90
+ 'primary-key': createFlag('primary-key', true),
91
+ });
92
+ const result = renderExplicit(node);
93
+ expect(result).toContain('+primary-key');
94
+ expect(result).not.toContain('primary-key:');
95
+ });
96
+
97
+ it('renders disabled flags as ~name', () => {
98
+ const node = createCommandNode('field', {
99
+ name: createLiteral('email', 'string'),
100
+ nullable: createFlag('nullable', false),
101
+ });
102
+ const result = renderExplicit(node);
103
+ expect(result).toContain('~nullable');
104
+ });
105
+
106
+ it('renders flags alongside role:value pairs', () => {
107
+ const node = createCommandNode('column', {
108
+ name: createLiteral('id', 'string'),
109
+ type: createLiteral('uuid', 'string'),
110
+ 'primary-key': createFlag('primary-key', true),
111
+ 'not-null': createFlag('not-null', true),
112
+ });
113
+ const result = renderExplicit(node);
114
+ expect(result).toContain('name:"id"');
115
+ expect(result).toContain('type:"uuid"');
116
+ expect(result).toContain('+primary-key');
117
+ expect(result).toContain('+not-null');
118
+ });
119
+ });
120
+
121
+ describe('round-trip: parse → render', () => {
122
+ const cases = [
123
+ '[toggle patient:.active]',
124
+ '[add patient:.highlight destination:#output]',
125
+ '[increment destination:#count quantity:5]',
126
+ '[wait duration:500ms]',
127
+ '[fetch source:/api/data responseType:json]',
128
+ '[column name:id +primary-key +not-null]',
129
+ '[field name:email +required ~nullable]',
130
+ ];
131
+
132
+ for (const input of cases) {
133
+ it(`round-trips: ${input}`, () => {
134
+ const node = parseExplicit(input);
135
+ const output = renderExplicit(node);
136
+ // Re-parse to verify semantic equivalence
137
+ const reparsed = parseExplicit(output);
138
+ expect(reparsed.action).toBe(node.action);
139
+ expect(reparsed.roles.size).toBe(node.roles.size);
140
+ for (const [role, value] of node.roles) {
141
+ expect(reparsed.roles.get(role)).toEqual(value);
142
+ }
143
+ });
144
+ }
145
+ });
146
+
147
+ describe('renderExplicit — annotations', () => {
148
+ it('renders a node with a single annotation', () => {
149
+ const node: SemanticNode = {
150
+ ...createCommandNode('fetch', {
151
+ source: createLiteral('/api/users', 'string'),
152
+ }),
153
+ annotations: [{ name: 'timeout', value: '5s' }],
154
+ };
155
+ expect(renderExplicit(node)).toBe('@timeout(5s) [fetch source:"/api/users"]');
156
+ });
157
+
158
+ it('renders annotation without value', () => {
159
+ const node: SemanticNode = {
160
+ ...createCommandNode('toggle', {
161
+ patient: createSelector('.active'),
162
+ }),
163
+ annotations: [{ name: 'deprecated' }],
164
+ };
165
+ expect(renderExplicit(node)).toBe('@deprecated [toggle patient:.active]');
166
+ });
167
+
168
+ it('renders multiple annotations preserving order', () => {
169
+ const node: SemanticNode = {
170
+ ...createCommandNode('fetch', {
171
+ source: createLiteral('/api/data', 'string'),
172
+ }),
173
+ annotations: [
174
+ { name: 'retry', value: '3' },
175
+ { name: 'timeout', value: '10s' },
176
+ ],
177
+ };
178
+ const result = renderExplicit(node);
179
+ expect(result).toBe('@retry(3) @timeout(10s) [fetch source:"/api/data"]');
180
+ });
181
+
182
+ it('renders no prefix when annotations are absent', () => {
183
+ const node = createCommandNode('toggle', {
184
+ patient: createSelector('.active'),
185
+ });
186
+ expect(renderExplicit(node)).toBe('[toggle patient:.active]');
187
+ });
188
+
189
+ it('annotation round-trip: render → parse → render', () => {
190
+ const node: SemanticNode = {
191
+ ...createCommandNode('fetch', {
192
+ source: createLiteral('/api/users', 'string'),
193
+ }),
194
+ annotations: [{ name: 'timeout', value: '5s' }],
195
+ };
196
+ const rendered = renderExplicit(node);
197
+ const reparsed = parseCompound(rendered);
198
+ const rerendered = renderExplicit(reparsed);
199
+ expect(rerendered).toBe(rendered);
200
+ });
201
+ });
202
+
203
+ describe('renderDocument', () => {
204
+ it('renders an envelope with version header', () => {
205
+ const envelope: LSEEnvelope = {
206
+ lseVersion: '1.2',
207
+ nodes: [
208
+ createCommandNode('toggle', { patient: createSelector('.active') }),
209
+ createCommandNode('add', { patient: createSelector('.highlight') }),
210
+ ],
211
+ };
212
+ const result = renderDocument(envelope);
213
+ expect(result).toBe('#!lse 1.2\n[toggle patient:.active]\n[add patient:.highlight]');
214
+ });
215
+
216
+ it('omits version header for version 1.0', () => {
217
+ const envelope: LSEEnvelope = {
218
+ lseVersion: '1.0',
219
+ nodes: [createCommandNode('toggle', { patient: createSelector('.active') })],
220
+ };
221
+ const result = renderDocument(envelope);
222
+ expect(result).toBe('[toggle patient:.active]');
223
+ });
224
+
225
+ it('renders empty envelope', () => {
226
+ const envelope: LSEEnvelope = { lseVersion: '1.2', nodes: [] };
227
+ expect(renderDocument(envelope)).toBe('#!lse 1.2');
228
+ });
229
+ });
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Explicit Syntax Renderer
3
+ *
4
+ * Serializes SemanticNode to the universal [command role:value ...] bracket syntax.
5
+ * Zero dependencies beyond core types — no language-specific logic.
6
+ *
7
+ * Also renders annotations (@name(value)) and multi-line documents.
8
+ */
9
+
10
+ import type {
11
+ SemanticNode,
12
+ SemanticValue,
13
+ CommandSemanticNode,
14
+ CompoundSemanticNode,
15
+ EventHandlerSemanticNode,
16
+ ConditionalSemanticNode,
17
+ LoopSemanticNode,
18
+ LSEEnvelope,
19
+ } from '../core/types';
20
+
21
+ /**
22
+ * Render a semantic node as explicit bracket syntax.
23
+ *
24
+ * Handles annotations (v1.2) — if the node has annotations, they are
25
+ * prepended as @name or @name(value) before the bracket output.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * renderExplicit(node) // "[toggle patient:.active destination:#button]"
30
+ * // With annotations:
31
+ * renderExplicit(annotatedNode) // "@timeout(5s) [fetch source:\"/api/users\"]"
32
+ * ```
33
+ */
34
+ export function renderExplicit(node: SemanticNode): string {
35
+ let result: string;
36
+
37
+ // Handle compound nodes
38
+ if (node.kind === 'compound') {
39
+ const compoundNode = node as CompoundSemanticNode;
40
+ const renderedStatements = compoundNode.statements.map(stmt => renderExplicit(stmt));
41
+ result = renderedStatements.join(` ${compoundNode.chainType} `);
42
+ } else {
43
+ result = renderBracketCommand(node);
44
+ }
45
+
46
+ // Prepend annotations if present
47
+ if (node.annotations && node.annotations.length > 0) {
48
+ const annParts = node.annotations.map(ann =>
49
+ ann.value !== undefined ? `@${ann.name}(${ann.value})` : `@${ann.name}`
50
+ );
51
+ return annParts.join(' ') + ' ' + result;
52
+ }
53
+
54
+ return result;
55
+ }
56
+
57
+ /**
58
+ * Render an LSEEnvelope as a multi-line document string.
59
+ *
60
+ * @example
61
+ * ```typescript
62
+ * renderDocument(envelope)
63
+ * // "#!lse 1.2\n[toggle patient:.active]\n[add patient:.highlight]"
64
+ * ```
65
+ */
66
+ export function renderDocument(envelope: LSEEnvelope): string {
67
+ const lines: string[] = [];
68
+
69
+ // Emit version header if not the default
70
+ if (envelope.lseVersion && envelope.lseVersion !== '1.0') {
71
+ lines.push(`#!lse ${envelope.lseVersion}`);
72
+ }
73
+
74
+ // Emit each node on its own line
75
+ for (const node of envelope.nodes) {
76
+ lines.push(renderExplicit(node));
77
+ }
78
+
79
+ return lines.join('\n');
80
+ }
81
+
82
+ // =============================================================================
83
+ // Internal Helpers
84
+ // =============================================================================
85
+
86
+ /**
87
+ * Render a single bracket command (non-compound node).
88
+ */
89
+ function renderBracketCommand(node: SemanticNode): string {
90
+ const parts: string[] = [node.action];
91
+
92
+ // Add roles
93
+ for (const [role, value] of node.roles) {
94
+ if (value.type === 'flag') {
95
+ parts.push(value.enabled ? `+${value.name}` : `~${value.name}`);
96
+ } else {
97
+ parts.push(`${role}:${valueToString(value)}`);
98
+ }
99
+ }
100
+
101
+ // Handle event handler body
102
+ if (node.kind === 'event-handler') {
103
+ const eventNode = node as EventHandlerSemanticNode;
104
+ if (eventNode.body && eventNode.body.length > 0) {
105
+ const bodyParts = eventNode.body.map(n => renderExplicit(n));
106
+ parts.push(`body:${bodyParts.join(' ')}`);
107
+ }
108
+ }
109
+
110
+ // Handle conditional branches (v1.1)
111
+ if (node.kind === 'conditional') {
112
+ const condNode = node as ConditionalSemanticNode;
113
+ if (condNode.thenBranch && condNode.thenBranch.length > 0) {
114
+ const thenParts = condNode.thenBranch.map(n => renderExplicit(n));
115
+ parts.push(`then:${thenParts.join(' ')}`);
116
+ }
117
+ if (condNode.elseBranch && condNode.elseBranch.length > 0) {
118
+ const elseParts = condNode.elseBranch.map(n => renderExplicit(n));
119
+ parts.push(`else:${elseParts.join(' ')}`);
120
+ }
121
+ }
122
+
123
+ // Handle loop body and metadata (v1.1)
124
+ if (node.kind === 'loop') {
125
+ const loopNode = node as LoopSemanticNode;
126
+ if (loopNode.loopVariant) {
127
+ parts.push(`loop-variant:${loopNode.loopVariant}`);
128
+ }
129
+ if (loopNode.loopVariable) {
130
+ parts.push(`variable:${loopNode.loopVariable}`);
131
+ }
132
+ if (loopNode.indexVariable) {
133
+ parts.push(`index-variable:${loopNode.indexVariable}`);
134
+ }
135
+ if (loopNode.body && loopNode.body.length > 0) {
136
+ const bodyParts = loopNode.body.map(n => renderExplicit(n));
137
+ parts.push(`loop-body:${bodyParts.join(' ')}`);
138
+ }
139
+ }
140
+
141
+ // Handle v1.2 command fields (try/catch/finally, async, match)
142
+ if (node.kind === 'command') {
143
+ const cmd = node as CommandSemanticNode;
144
+ if (cmd.body && cmd.body.length > 0) {
145
+ const bodyParts = cmd.body.map(n => renderExplicit(n));
146
+ parts.push(`body:${bodyParts.join(' ')}`);
147
+ }
148
+ if (cmd.catchBranch && cmd.catchBranch.length > 0) {
149
+ const catchParts = cmd.catchBranch.map(n => renderExplicit(n));
150
+ parts.push(`catch:${catchParts.join(' ')}`);
151
+ }
152
+ if (cmd.finallyBranch && cmd.finallyBranch.length > 0) {
153
+ const finallyParts = cmd.finallyBranch.map(n => renderExplicit(n));
154
+ parts.push(`finally:${finallyParts.join(' ')}`);
155
+ }
156
+ if (cmd.asyncVariant) {
157
+ parts.push(`async-variant:${cmd.asyncVariant}`);
158
+ }
159
+ if (cmd.asyncBody && cmd.asyncBody.length > 0) {
160
+ const asyncParts = cmd.asyncBody.map(n => renderExplicit(n));
161
+ parts.push(`async-body:${asyncParts.join(' ')}`);
162
+ }
163
+ }
164
+
165
+ return `[${parts.join(' ')}]`;
166
+ }
167
+
168
+ /**
169
+ * Convert a semantic value to its explicit syntax string form.
170
+ */
171
+ function valueToString(value: SemanticValue): string {
172
+ switch (value.type) {
173
+ case 'literal':
174
+ if (typeof value.value === 'string') {
175
+ // Quote strings that contain spaces or are explicitly typed as strings
176
+ if (value.dataType === 'string' || /\s/.test(value.value)) {
177
+ return `"${value.value}"`;
178
+ }
179
+ return value.value;
180
+ }
181
+ return String(value.value);
182
+
183
+ case 'selector':
184
+ return value.value;
185
+
186
+ case 'reference':
187
+ return value.value;
188
+
189
+ case 'property-path':
190
+ return `${valueToString(value.object)}'s ${value.property}`;
191
+
192
+ case 'expression':
193
+ return value.raw;
194
+
195
+ case 'flag':
196
+ return value.name;
197
+ }
198
+ }