modelfusion 0.35.0 → 0.35.2

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.
@@ -2,71 +2,130 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.fixJson = void 0;
4
4
  // Implemented as a scanner with additional fixing
5
- // that performs a single linear time scan pass over the partial JSON:
5
+ // that performs a single linear time scan pass over the partial JSON.
6
+ //
7
+ // The states should ideally match relevant states from the JSON spec:
8
+ // https://www.json.org/json-en.html
9
+ //
10
+ // Please note that invalid JSON is not considered/covered, because it
11
+ // is assumed that the resulting JSON will be processed by a standard
12
+ // JSON parser that will detect any invalid JSON.
6
13
  function fixJson(input) {
7
- const stack = ["BEFORE_VALUE"];
14
+ const stack = ["ROOT"];
8
15
  let lastValidIndex = -1;
9
16
  let literalStart = null;
17
+ function processValueStart(char, i, swapState) {
18
+ {
19
+ switch (char) {
20
+ case '"': {
21
+ lastValidIndex = i;
22
+ stack.pop();
23
+ stack.push(swapState);
24
+ stack.push("INSIDE_STRING");
25
+ break;
26
+ }
27
+ case "f":
28
+ case "t":
29
+ case "n": {
30
+ lastValidIndex = i;
31
+ literalStart = i;
32
+ stack.pop();
33
+ stack.push(swapState);
34
+ stack.push("INSIDE_LITERAL");
35
+ break;
36
+ }
37
+ case "-": {
38
+ stack.pop();
39
+ stack.push(swapState);
40
+ stack.push("INSIDE_NUMBER");
41
+ break;
42
+ }
43
+ case "0":
44
+ case "1":
45
+ case "2":
46
+ case "3":
47
+ case "4":
48
+ case "5":
49
+ case "6":
50
+ case "7":
51
+ case "8":
52
+ case "9": {
53
+ lastValidIndex = i;
54
+ stack.pop();
55
+ stack.push(swapState);
56
+ stack.push("INSIDE_NUMBER");
57
+ break;
58
+ }
59
+ case "{": {
60
+ lastValidIndex = i;
61
+ stack.pop();
62
+ stack.push(swapState);
63
+ stack.push("INSIDE_OBJECT_START");
64
+ break;
65
+ }
66
+ case "[": {
67
+ lastValidIndex = i;
68
+ stack.pop();
69
+ stack.push(swapState);
70
+ stack.push("INSIDE_ARRAY_START");
71
+ break;
72
+ }
73
+ }
74
+ }
75
+ }
76
+ function processAfterObjectValue(char, i) {
77
+ switch (char) {
78
+ case ",": {
79
+ stack.pop();
80
+ stack.push("INSIDE_OBJECT_AFTER_COMMA");
81
+ break;
82
+ }
83
+ case "}": {
84
+ lastValidIndex = i;
85
+ stack.pop();
86
+ break;
87
+ }
88
+ }
89
+ }
90
+ function processAfterArrayValue(char, i) {
91
+ switch (char) {
92
+ case ",": {
93
+ stack.pop();
94
+ stack.push("INSIDE_ARRAY_AFTER_COMMA");
95
+ break;
96
+ }
97
+ case "]": {
98
+ lastValidIndex = i;
99
+ stack.pop();
100
+ break;
101
+ }
102
+ }
103
+ }
10
104
  for (let i = 0; i < input.length; i++) {
11
105
  const char = input[i];
12
106
  const currentState = stack[stack.length - 1];
13
107
  switch (currentState) {
14
- case "BEFORE_VALUE": {
108
+ case "ROOT":
109
+ processValueStart(char, i, "FINISH");
110
+ break;
111
+ case "INSIDE_OBJECT_START": {
15
112
  switch (char) {
16
113
  case '"': {
17
- lastValidIndex = i;
18
- stack.pop();
19
- stack.push("INSIDE_STRING");
20
- break;
21
- }
22
- case "f":
23
- case "t":
24
- case "n": {
25
- lastValidIndex = i;
26
- literalStart = i;
27
- stack.pop();
28
- stack.push("INSIDE_LITERAL");
29
- break;
30
- }
31
- case "-": {
32
114
  stack.pop();
33
- stack.push("INSIDE_NUMBER");
34
- break;
35
- }
36
- case "0":
37
- case "1":
38
- case "2":
39
- case "3":
40
- case "4":
41
- case "5":
42
- case "6":
43
- case "7":
44
- case "8":
45
- case "9": {
46
- lastValidIndex = i;
47
- stack.pop();
48
- stack.push("INSIDE_NUMBER");
49
- break;
50
- }
51
- case "{": {
52
- lastValidIndex = i;
53
- stack.pop();
54
- stack.push("INSIDE_OBJECT");
115
+ stack.push("INSIDE_OBJECT_KEY");
55
116
  break;
56
117
  }
57
- case "[": {
58
- lastValidIndex = i;
118
+ case "}": {
59
119
  stack.pop();
60
- stack.push("INSIDE_ARRAY");
61
- stack.push("BEFORE_VALUE");
62
120
  break;
63
121
  }
64
122
  }
65
123
  break;
66
124
  }
67
- case "INSIDE_OBJECT": {
125
+ case "INSIDE_OBJECT_AFTER_COMMA": {
68
126
  switch (char) {
69
127
  case '"': {
128
+ stack.pop();
70
129
  stack.push("INSIDE_OBJECT_KEY");
71
130
  break;
72
131
  }
@@ -77,22 +136,30 @@ function fixJson(input) {
77
136
  switch (char) {
78
137
  case '"': {
79
138
  stack.pop();
80
- stack.push("AFTER_OBJECT_KEY");
139
+ stack.push("INSIDE_OBJECT_AFTER_KEY");
81
140
  break;
82
141
  }
83
142
  }
84
143
  break;
85
144
  }
86
- case "AFTER_OBJECT_KEY": {
145
+ case "INSIDE_OBJECT_AFTER_KEY": {
87
146
  switch (char) {
88
147
  case ":": {
89
148
  stack.pop();
90
- stack.push("BEFORE_VALUE");
149
+ stack.push("INSIDE_OBJECT_BEFORE_VALUE");
91
150
  break;
92
151
  }
93
152
  }
94
153
  break;
95
154
  }
155
+ case "INSIDE_OBJECT_BEFORE_VALUE": {
156
+ processValueStart(char, i, "INSIDE_OBJECT_AFTER_VALUE");
157
+ break;
158
+ }
159
+ case "INSIDE_OBJECT_AFTER_VALUE": {
160
+ processAfterObjectValue(char, i);
161
+ break;
162
+ }
96
163
  case "INSIDE_STRING": {
97
164
  switch (char) {
98
165
  case '"': {
@@ -110,10 +177,31 @@ function fixJson(input) {
110
177
  }
111
178
  break;
112
179
  }
113
- case "INSIDE_ARRAY": {
180
+ case "INSIDE_ARRAY_START": {
181
+ switch (char) {
182
+ case "]": {
183
+ lastValidIndex = i;
184
+ stack.pop();
185
+ break;
186
+ }
187
+ default: {
188
+ lastValidIndex = i;
189
+ processValueStart(char, i, "INSIDE_ARRAY_AFTER_VALUE");
190
+ break;
191
+ }
192
+ }
193
+ break;
194
+ }
195
+ case "INSIDE_ARRAY_AFTER_VALUE": {
114
196
  switch (char) {
115
197
  case ",": {
116
- stack.push("BEFORE_VALUE");
198
+ stack.pop();
199
+ stack.push("INSIDE_ARRAY_AFTER_COMMA");
200
+ break;
201
+ }
202
+ case "]": {
203
+ lastValidIndex = i;
204
+ stack.pop();
117
205
  break;
118
206
  }
119
207
  default: {
@@ -123,6 +211,10 @@ function fixJson(input) {
123
211
  }
124
212
  break;
125
213
  }
214
+ case "INSIDE_ARRAY_AFTER_COMMA": {
215
+ processValueStart(char, i, "INSIDE_ARRAY_AFTER_VALUE");
216
+ break;
217
+ }
126
218
  case "INSIDE_STRING_ESCAPE": {
127
219
  stack.pop();
128
220
  lastValidIndex = i;
@@ -151,8 +243,25 @@ function fixJson(input) {
151
243
  }
152
244
  case ",": {
153
245
  stack.pop();
154
- if (stack[stack.length - 1] === "INSIDE_ARRAY") {
155
- stack.push("BEFORE_VALUE");
246
+ if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") {
247
+ processAfterArrayValue(char, i);
248
+ }
249
+ if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") {
250
+ processAfterObjectValue(char, i);
251
+ }
252
+ break;
253
+ }
254
+ case "}": {
255
+ stack.pop();
256
+ if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") {
257
+ processAfterObjectValue(char, i);
258
+ }
259
+ break;
260
+ }
261
+ case "]": {
262
+ stack.pop();
263
+ if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") {
264
+ processAfterArrayValue(char, i);
156
265
  }
157
266
  break;
158
267
  }
@@ -164,13 +273,16 @@ function fixJson(input) {
164
273
  break;
165
274
  }
166
275
  case "INSIDE_LITERAL": {
167
- const partialLiteral = input.substring(literalStart, i);
276
+ const partialLiteral = input.substring(literalStart, i + 1);
168
277
  if (!"false".startsWith(partialLiteral) &&
169
278
  !"true".startsWith(partialLiteral) &&
170
279
  !"null".startsWith(partialLiteral)) {
171
280
  stack.pop();
172
- if (stack[stack.length - 1] === "INSIDE_ARRAY") {
173
- stack.push("BEFORE_VALUE");
281
+ if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") {
282
+ processAfterObjectValue(char, i);
283
+ }
284
+ else if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") {
285
+ processAfterArrayValue(char, i);
174
286
  }
175
287
  }
176
288
  else {
@@ -188,11 +300,18 @@ function fixJson(input) {
188
300
  result += '"';
189
301
  break;
190
302
  }
191
- case "INSIDE_OBJECT": {
303
+ case "INSIDE_OBJECT_KEY":
304
+ case "INSIDE_OBJECT_AFTER_KEY":
305
+ case "INSIDE_OBJECT_AFTER_COMMA":
306
+ case "INSIDE_OBJECT_START":
307
+ case "INSIDE_OBJECT_BEFORE_VALUE":
308
+ case "INSIDE_OBJECT_AFTER_VALUE": {
192
309
  result += "}";
193
310
  break;
194
311
  }
195
- case "INSIDE_ARRAY": {
312
+ case "INSIDE_ARRAY_START":
313
+ case "INSIDE_ARRAY_AFTER_COMMA":
314
+ case "INSIDE_ARRAY_AFTER_VALUE": {
196
315
  result += "]";
197
316
  break;
198
317
  }
@@ -1,69 +1,128 @@
1
1
  // Implemented as a scanner with additional fixing
2
- // that performs a single linear time scan pass over the partial JSON:
2
+ // that performs a single linear time scan pass over the partial JSON.
3
+ //
4
+ // The states should ideally match relevant states from the JSON spec:
5
+ // https://www.json.org/json-en.html
6
+ //
7
+ // Please note that invalid JSON is not considered/covered, because it
8
+ // is assumed that the resulting JSON will be processed by a standard
9
+ // JSON parser that will detect any invalid JSON.
3
10
  export function fixJson(input) {
4
- const stack = ["BEFORE_VALUE"];
11
+ const stack = ["ROOT"];
5
12
  let lastValidIndex = -1;
6
13
  let literalStart = null;
14
+ function processValueStart(char, i, swapState) {
15
+ {
16
+ switch (char) {
17
+ case '"': {
18
+ lastValidIndex = i;
19
+ stack.pop();
20
+ stack.push(swapState);
21
+ stack.push("INSIDE_STRING");
22
+ break;
23
+ }
24
+ case "f":
25
+ case "t":
26
+ case "n": {
27
+ lastValidIndex = i;
28
+ literalStart = i;
29
+ stack.pop();
30
+ stack.push(swapState);
31
+ stack.push("INSIDE_LITERAL");
32
+ break;
33
+ }
34
+ case "-": {
35
+ stack.pop();
36
+ stack.push(swapState);
37
+ stack.push("INSIDE_NUMBER");
38
+ break;
39
+ }
40
+ case "0":
41
+ case "1":
42
+ case "2":
43
+ case "3":
44
+ case "4":
45
+ case "5":
46
+ case "6":
47
+ case "7":
48
+ case "8":
49
+ case "9": {
50
+ lastValidIndex = i;
51
+ stack.pop();
52
+ stack.push(swapState);
53
+ stack.push("INSIDE_NUMBER");
54
+ break;
55
+ }
56
+ case "{": {
57
+ lastValidIndex = i;
58
+ stack.pop();
59
+ stack.push(swapState);
60
+ stack.push("INSIDE_OBJECT_START");
61
+ break;
62
+ }
63
+ case "[": {
64
+ lastValidIndex = i;
65
+ stack.pop();
66
+ stack.push(swapState);
67
+ stack.push("INSIDE_ARRAY_START");
68
+ break;
69
+ }
70
+ }
71
+ }
72
+ }
73
+ function processAfterObjectValue(char, i) {
74
+ switch (char) {
75
+ case ",": {
76
+ stack.pop();
77
+ stack.push("INSIDE_OBJECT_AFTER_COMMA");
78
+ break;
79
+ }
80
+ case "}": {
81
+ lastValidIndex = i;
82
+ stack.pop();
83
+ break;
84
+ }
85
+ }
86
+ }
87
+ function processAfterArrayValue(char, i) {
88
+ switch (char) {
89
+ case ",": {
90
+ stack.pop();
91
+ stack.push("INSIDE_ARRAY_AFTER_COMMA");
92
+ break;
93
+ }
94
+ case "]": {
95
+ lastValidIndex = i;
96
+ stack.pop();
97
+ break;
98
+ }
99
+ }
100
+ }
7
101
  for (let i = 0; i < input.length; i++) {
8
102
  const char = input[i];
9
103
  const currentState = stack[stack.length - 1];
10
104
  switch (currentState) {
11
- case "BEFORE_VALUE": {
105
+ case "ROOT":
106
+ processValueStart(char, i, "FINISH");
107
+ break;
108
+ case "INSIDE_OBJECT_START": {
12
109
  switch (char) {
13
110
  case '"': {
14
- lastValidIndex = i;
15
- stack.pop();
16
- stack.push("INSIDE_STRING");
17
- break;
18
- }
19
- case "f":
20
- case "t":
21
- case "n": {
22
- lastValidIndex = i;
23
- literalStart = i;
24
- stack.pop();
25
- stack.push("INSIDE_LITERAL");
26
- break;
27
- }
28
- case "-": {
29
111
  stack.pop();
30
- stack.push("INSIDE_NUMBER");
31
- break;
32
- }
33
- case "0":
34
- case "1":
35
- case "2":
36
- case "3":
37
- case "4":
38
- case "5":
39
- case "6":
40
- case "7":
41
- case "8":
42
- case "9": {
43
- lastValidIndex = i;
44
- stack.pop();
45
- stack.push("INSIDE_NUMBER");
46
- break;
47
- }
48
- case "{": {
49
- lastValidIndex = i;
50
- stack.pop();
51
- stack.push("INSIDE_OBJECT");
112
+ stack.push("INSIDE_OBJECT_KEY");
52
113
  break;
53
114
  }
54
- case "[": {
55
- lastValidIndex = i;
115
+ case "}": {
56
116
  stack.pop();
57
- stack.push("INSIDE_ARRAY");
58
- stack.push("BEFORE_VALUE");
59
117
  break;
60
118
  }
61
119
  }
62
120
  break;
63
121
  }
64
- case "INSIDE_OBJECT": {
122
+ case "INSIDE_OBJECT_AFTER_COMMA": {
65
123
  switch (char) {
66
124
  case '"': {
125
+ stack.pop();
67
126
  stack.push("INSIDE_OBJECT_KEY");
68
127
  break;
69
128
  }
@@ -74,22 +133,30 @@ export function fixJson(input) {
74
133
  switch (char) {
75
134
  case '"': {
76
135
  stack.pop();
77
- stack.push("AFTER_OBJECT_KEY");
136
+ stack.push("INSIDE_OBJECT_AFTER_KEY");
78
137
  break;
79
138
  }
80
139
  }
81
140
  break;
82
141
  }
83
- case "AFTER_OBJECT_KEY": {
142
+ case "INSIDE_OBJECT_AFTER_KEY": {
84
143
  switch (char) {
85
144
  case ":": {
86
145
  stack.pop();
87
- stack.push("BEFORE_VALUE");
146
+ stack.push("INSIDE_OBJECT_BEFORE_VALUE");
88
147
  break;
89
148
  }
90
149
  }
91
150
  break;
92
151
  }
152
+ case "INSIDE_OBJECT_BEFORE_VALUE": {
153
+ processValueStart(char, i, "INSIDE_OBJECT_AFTER_VALUE");
154
+ break;
155
+ }
156
+ case "INSIDE_OBJECT_AFTER_VALUE": {
157
+ processAfterObjectValue(char, i);
158
+ break;
159
+ }
93
160
  case "INSIDE_STRING": {
94
161
  switch (char) {
95
162
  case '"': {
@@ -107,10 +174,31 @@ export function fixJson(input) {
107
174
  }
108
175
  break;
109
176
  }
110
- case "INSIDE_ARRAY": {
177
+ case "INSIDE_ARRAY_START": {
178
+ switch (char) {
179
+ case "]": {
180
+ lastValidIndex = i;
181
+ stack.pop();
182
+ break;
183
+ }
184
+ default: {
185
+ lastValidIndex = i;
186
+ processValueStart(char, i, "INSIDE_ARRAY_AFTER_VALUE");
187
+ break;
188
+ }
189
+ }
190
+ break;
191
+ }
192
+ case "INSIDE_ARRAY_AFTER_VALUE": {
111
193
  switch (char) {
112
194
  case ",": {
113
- stack.push("BEFORE_VALUE");
195
+ stack.pop();
196
+ stack.push("INSIDE_ARRAY_AFTER_COMMA");
197
+ break;
198
+ }
199
+ case "]": {
200
+ lastValidIndex = i;
201
+ stack.pop();
114
202
  break;
115
203
  }
116
204
  default: {
@@ -120,6 +208,10 @@ export function fixJson(input) {
120
208
  }
121
209
  break;
122
210
  }
211
+ case "INSIDE_ARRAY_AFTER_COMMA": {
212
+ processValueStart(char, i, "INSIDE_ARRAY_AFTER_VALUE");
213
+ break;
214
+ }
123
215
  case "INSIDE_STRING_ESCAPE": {
124
216
  stack.pop();
125
217
  lastValidIndex = i;
@@ -148,8 +240,25 @@ export function fixJson(input) {
148
240
  }
149
241
  case ",": {
150
242
  stack.pop();
151
- if (stack[stack.length - 1] === "INSIDE_ARRAY") {
152
- stack.push("BEFORE_VALUE");
243
+ if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") {
244
+ processAfterArrayValue(char, i);
245
+ }
246
+ if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") {
247
+ processAfterObjectValue(char, i);
248
+ }
249
+ break;
250
+ }
251
+ case "}": {
252
+ stack.pop();
253
+ if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") {
254
+ processAfterObjectValue(char, i);
255
+ }
256
+ break;
257
+ }
258
+ case "]": {
259
+ stack.pop();
260
+ if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") {
261
+ processAfterArrayValue(char, i);
153
262
  }
154
263
  break;
155
264
  }
@@ -161,13 +270,16 @@ export function fixJson(input) {
161
270
  break;
162
271
  }
163
272
  case "INSIDE_LITERAL": {
164
- const partialLiteral = input.substring(literalStart, i);
273
+ const partialLiteral = input.substring(literalStart, i + 1);
165
274
  if (!"false".startsWith(partialLiteral) &&
166
275
  !"true".startsWith(partialLiteral) &&
167
276
  !"null".startsWith(partialLiteral)) {
168
277
  stack.pop();
169
- if (stack[stack.length - 1] === "INSIDE_ARRAY") {
170
- stack.push("BEFORE_VALUE");
278
+ if (stack[stack.length - 1] === "INSIDE_OBJECT_AFTER_VALUE") {
279
+ processAfterObjectValue(char, i);
280
+ }
281
+ else if (stack[stack.length - 1] === "INSIDE_ARRAY_AFTER_VALUE") {
282
+ processAfterArrayValue(char, i);
171
283
  }
172
284
  }
173
285
  else {
@@ -185,11 +297,18 @@ export function fixJson(input) {
185
297
  result += '"';
186
298
  break;
187
299
  }
188
- case "INSIDE_OBJECT": {
300
+ case "INSIDE_OBJECT_KEY":
301
+ case "INSIDE_OBJECT_AFTER_KEY":
302
+ case "INSIDE_OBJECT_AFTER_COMMA":
303
+ case "INSIDE_OBJECT_START":
304
+ case "INSIDE_OBJECT_BEFORE_VALUE":
305
+ case "INSIDE_OBJECT_AFTER_VALUE": {
189
306
  result += "}";
190
307
  break;
191
308
  }
192
- case "INSIDE_ARRAY": {
309
+ case "INSIDE_ARRAY_START":
310
+ case "INSIDE_ARRAY_AFTER_COMMA":
311
+ case "INSIDE_ARRAY_AFTER_VALUE": {
193
312
  result += "]";
194
313
  break;
195
314
  }
@@ -66,19 +66,46 @@ const fixJson_1 = require("./fixJson");
66
66
  (0, vitest_1.test)("should handle incomplete array", () => {
67
67
  (0, vitest_1.expect)((0, fixJson_1.fixJson)("[")).toBe("[]");
68
68
  });
69
+ (0, vitest_1.test)("should handle closing bracket after number in array", () => {
70
+ (0, vitest_1.expect)((0, fixJson_1.fixJson)("[[1], [2")).toBe("[[1], [2]]");
71
+ });
72
+ (0, vitest_1.test)("should handle closing bracket after string in array", () => {
73
+ (0, vitest_1.expect)((0, fixJson_1.fixJson)(`[["1"], ["2`)).toBe(`[["1"], ["2"]]`);
74
+ });
75
+ (0, vitest_1.test)("should handle closing bracket after literal in array", () => {
76
+ (0, vitest_1.expect)((0, fixJson_1.fixJson)("[[false], [nu")).toBe("[[false], [null]]");
77
+ });
78
+ (0, vitest_1.test)("should handle closing bracket after array in array", () => {
79
+ (0, vitest_1.expect)((0, fixJson_1.fixJson)("[[[]], [[]")).toBe("[[[]], [[]]]");
80
+ });
81
+ (0, vitest_1.test)("should handle closing bracket after object in array", () => {
82
+ (0, vitest_1.expect)((0, fixJson_1.fixJson)("[[{}], [{")).toBe("[[{}], [{}]]");
83
+ });
69
84
  (0, vitest_1.test)("should handle trailing comma", () => {
70
85
  (0, vitest_1.expect)((0, fixJson_1.fixJson)("[1, ")).toBe("[1]");
71
86
  });
72
87
  (0, vitest_1.test)("should handle closing array", () => {
73
- (0, vitest_1.expect)((0, fixJson_1.fixJson)('["a": [], "b": 123')).toBe('["a": [], "b": 123]');
88
+ (0, vitest_1.expect)((0, fixJson_1.fixJson)("[[], 123")).toBe("[[], 123]");
74
89
  });
75
90
  });
76
91
  (0, vitest_1.describe)("object", () => {
77
92
  (0, vitest_1.test)("should handle keys without values", () => {
78
93
  (0, vitest_1.expect)((0, fixJson_1.fixJson)('{"key":')).toBe("{}");
79
94
  });
80
- (0, vitest_1.test)("should handle closing brace in object", () => {
81
- (0, vitest_1.expect)((0, fixJson_1.fixJson)('{"a": {"b": 1}')).toBe('{"a": {"b": 1}}');
95
+ (0, vitest_1.test)("should handle closing brace after number in object", () => {
96
+ (0, vitest_1.expect)((0, fixJson_1.fixJson)('{"a": {"b": 1}, "c": {"d": 2')).toBe('{"a": {"b": 1}, "c": {"d": 2}}');
97
+ });
98
+ (0, vitest_1.test)("should handle closing brace after string in object", () => {
99
+ (0, vitest_1.expect)((0, fixJson_1.fixJson)('{"a": {"b": "1"}, "c": {"d": 2')).toBe('{"a": {"b": "1"}, "c": {"d": 2}}');
100
+ });
101
+ (0, vitest_1.test)("should handle closing brace after literal in object", () => {
102
+ (0, vitest_1.expect)((0, fixJson_1.fixJson)('{"a": {"b": false}, "c": {"d": 2')).toBe('{"a": {"b": false}, "c": {"d": 2}}');
103
+ });
104
+ (0, vitest_1.test)("should handle closing brace after array in object", () => {
105
+ (0, vitest_1.expect)((0, fixJson_1.fixJson)('{"a": {"b": []}, "c": {"d": 2')).toBe('{"a": {"b": []}, "c": {"d": 2}}');
106
+ });
107
+ (0, vitest_1.test)("should handle closing brace after object in object", () => {
108
+ (0, vitest_1.expect)((0, fixJson_1.fixJson)('{"a": {"b": {}}, "c": {"d": 2')).toBe('{"a": {"b": {}}, "c": {"d": 2}}');
82
109
  });
83
110
  (0, vitest_1.test)("should handle partial keys (first key)", () => {
84
111
  (0, vitest_1.expect)((0, fixJson_1.fixJson)('{"ke')).toBe("{}");
@@ -127,4 +154,33 @@ const fixJson_1 = require("./fixJson");
127
154
  (0, vitest_1.expect)((0, fixJson_1.fixJson)('{"a": 1, "b": "')).toBe('{"a": 1, "b": ""}');
128
155
  });
129
156
  });
157
+ (0, vitest_1.describe)("regression", () => {
158
+ (0, vitest_1.test)("should handle complex nesting 1", () => {
159
+ (0, vitest_1.expect)((0, fixJson_1.fixJson)([
160
+ "{",
161
+ ' "a": [',
162
+ " {",
163
+ ' "a1": "v1",',
164
+ ' "a2": "v2",',
165
+ ` "a3": "v3"`,
166
+ " }",
167
+ " ],",
168
+ ' "b": [',
169
+ " {",
170
+ ' "b1": "n',
171
+ ].join("\n"))).toBe([
172
+ "{",
173
+ ' "a": [',
174
+ " {",
175
+ ' "a1": "v1",',
176
+ ' "a2": "v2",',
177
+ ` "a3": "v3"`,
178
+ " }",
179
+ " ],",
180
+ ' "b": [',
181
+ " {",
182
+ ' "b1": "n"}]}',
183
+ ].join("\n"));
184
+ });
185
+ });
130
186
  });
@@ -64,19 +64,46 @@ describe("fixJson", () => {
64
64
  test("should handle incomplete array", () => {
65
65
  expect(fixJson("[")).toBe("[]");
66
66
  });
67
+ test("should handle closing bracket after number in array", () => {
68
+ expect(fixJson("[[1], [2")).toBe("[[1], [2]]");
69
+ });
70
+ test("should handle closing bracket after string in array", () => {
71
+ expect(fixJson(`[["1"], ["2`)).toBe(`[["1"], ["2"]]`);
72
+ });
73
+ test("should handle closing bracket after literal in array", () => {
74
+ expect(fixJson("[[false], [nu")).toBe("[[false], [null]]");
75
+ });
76
+ test("should handle closing bracket after array in array", () => {
77
+ expect(fixJson("[[[]], [[]")).toBe("[[[]], [[]]]");
78
+ });
79
+ test("should handle closing bracket after object in array", () => {
80
+ expect(fixJson("[[{}], [{")).toBe("[[{}], [{}]]");
81
+ });
67
82
  test("should handle trailing comma", () => {
68
83
  expect(fixJson("[1, ")).toBe("[1]");
69
84
  });
70
85
  test("should handle closing array", () => {
71
- expect(fixJson('["a": [], "b": 123')).toBe('["a": [], "b": 123]');
86
+ expect(fixJson("[[], 123")).toBe("[[], 123]");
72
87
  });
73
88
  });
74
89
  describe("object", () => {
75
90
  test("should handle keys without values", () => {
76
91
  expect(fixJson('{"key":')).toBe("{}");
77
92
  });
78
- test("should handle closing brace in object", () => {
79
- expect(fixJson('{"a": {"b": 1}')).toBe('{"a": {"b": 1}}');
93
+ test("should handle closing brace after number in object", () => {
94
+ expect(fixJson('{"a": {"b": 1}, "c": {"d": 2')).toBe('{"a": {"b": 1}, "c": {"d": 2}}');
95
+ });
96
+ test("should handle closing brace after string in object", () => {
97
+ expect(fixJson('{"a": {"b": "1"}, "c": {"d": 2')).toBe('{"a": {"b": "1"}, "c": {"d": 2}}');
98
+ });
99
+ test("should handle closing brace after literal in object", () => {
100
+ expect(fixJson('{"a": {"b": false}, "c": {"d": 2')).toBe('{"a": {"b": false}, "c": {"d": 2}}');
101
+ });
102
+ test("should handle closing brace after array in object", () => {
103
+ expect(fixJson('{"a": {"b": []}, "c": {"d": 2')).toBe('{"a": {"b": []}, "c": {"d": 2}}');
104
+ });
105
+ test("should handle closing brace after object in object", () => {
106
+ expect(fixJson('{"a": {"b": {}}, "c": {"d": 2')).toBe('{"a": {"b": {}}, "c": {"d": 2}}');
80
107
  });
81
108
  test("should handle partial keys (first key)", () => {
82
109
  expect(fixJson('{"ke')).toBe("{}");
@@ -125,4 +152,33 @@ describe("fixJson", () => {
125
152
  expect(fixJson('{"a": 1, "b": "')).toBe('{"a": 1, "b": ""}');
126
153
  });
127
154
  });
155
+ describe("regression", () => {
156
+ test("should handle complex nesting 1", () => {
157
+ expect(fixJson([
158
+ "{",
159
+ ' "a": [',
160
+ " {",
161
+ ' "a1": "v1",',
162
+ ' "a2": "v2",',
163
+ ` "a3": "v3"`,
164
+ " }",
165
+ " ],",
166
+ ' "b": [',
167
+ " {",
168
+ ' "b1": "n',
169
+ ].join("\n"))).toBe([
170
+ "{",
171
+ ' "a": [',
172
+ " {",
173
+ ' "a1": "v1",',
174
+ ' "a2": "v2",',
175
+ ` "a3": "v3"`,
176
+ " }",
177
+ " ],",
178
+ ' "b": [',
179
+ " {",
180
+ ' "b1": "n"}]}',
181
+ ].join("\n"));
182
+ });
183
+ });
128
184
  });
@@ -3,7 +3,7 @@ import { AsyncIterableResultPromise } from "../AsyncIterableResultPromise.js";
3
3
  import { DeltaEvent } from "../DeltaEvent.js";
4
4
  import { ModelFunctionOptions } from "../ModelFunctionOptions.js";
5
5
  import { StructureGenerationModel, StructureGenerationModelSettings } from "./StructureGenerationModel.js";
6
- type StructureStreamPart<STRUCTURE> = {
6
+ export type StructureStreamPart<STRUCTURE> = {
7
7
  isComplete: false;
8
8
  value: unknown;
9
9
  } | {
@@ -14,4 +14,3 @@ export declare function streamStructure<STRUCTURE, PROMPT, FULL_DELTA, NAME exte
14
14
  generateStructureStreamResponse: (structureDefinition: StructureDefinition<NAME, STRUCTURE>, prompt: PROMPT, options: ModelFunctionOptions<SETTINGS>) => PromiseLike<AsyncIterable<DeltaEvent<FULL_DELTA>>>;
15
15
  extractPartialStructure: (fullDelta: FULL_DELTA) => unknown | undefined;
16
16
  }, structureDefinition: StructureDefinition<NAME, STRUCTURE>, prompt: PROMPT, options?: ModelFunctionOptions<SETTINGS>): AsyncIterableResultPromise<StructureStreamPart<STRUCTURE>>;
17
- export {};
@@ -277,6 +277,10 @@ const openAITextGenerationResponseSchema = zod_1.default.object({
277
277
  }),
278
278
  });
279
279
  async function callOpenAITextGenerationAPI({ api = new OpenAIApiConfiguration_js_1.OpenAIApiConfiguration(), abortSignal, responseFormat, model, prompt, suffix, maxTokens, temperature, topP, n, logprobs, echo, stop, presencePenalty, frequencyPenalty, bestOf, logitBias, user, }) {
280
+ // empty arrays are not allowed for stop:
281
+ if (stop != null && Array.isArray(stop) && stop.length === 0) {
282
+ stop = undefined;
283
+ }
280
284
  return (0, postToApi_js_1.postJsonToApi)({
281
285
  url: api.assembleUrl("/completions"),
282
286
  headers: api.headers,
@@ -267,6 +267,10 @@ const openAITextGenerationResponseSchema = z.object({
267
267
  }),
268
268
  });
269
269
  async function callOpenAITextGenerationAPI({ api = new OpenAIApiConfiguration(), abortSignal, responseFormat, model, prompt, suffix, maxTokens, temperature, topP, n, logprobs, echo, stop, presencePenalty, frequencyPenalty, bestOf, logitBias, user, }) {
270
+ // empty arrays are not allowed for stop:
271
+ if (stop != null && Array.isArray(stop) && stop.length === 0) {
272
+ stop = undefined;
273
+ }
270
274
  return postJsonToApi({
271
275
  url: api.assembleUrl("/completions"),
272
276
  headers: api.headers,
@@ -365,6 +365,10 @@ const openAIChatResponseSchema = zod_1.default.object({
365
365
  }),
366
366
  });
367
367
  async function callOpenAIChatCompletionAPI({ api = new OpenAIApiConfiguration_js_1.OpenAIApiConfiguration(), abortSignal, responseFormat, model, messages, functions, functionCall, temperature, topP, n, stop, maxTokens, presencePenalty, frequencyPenalty, logitBias, user, }) {
368
+ // empty arrays are not allowed for stop:
369
+ if (stop != null && Array.isArray(stop) && stop.length === 0) {
370
+ stop = undefined;
371
+ }
368
372
  return (0, postToApi_js_1.postJsonToApi)({
369
373
  url: api.assembleUrl("/chat/completions"),
370
374
  headers: api.headers,
@@ -355,6 +355,10 @@ const openAIChatResponseSchema = z.object({
355
355
  }),
356
356
  });
357
357
  async function callOpenAIChatCompletionAPI({ api = new OpenAIApiConfiguration(), abortSignal, responseFormat, model, messages, functions, functionCall, temperature, topP, n, stop, maxTokens, presencePenalty, frequencyPenalty, logitBias, user, }) {
358
+ // empty arrays are not allowed for stop:
359
+ if (stop != null && Array.isArray(stop) && stop.length === 0) {
360
+ stop = undefined;
361
+ }
358
362
  return postJsonToApi({
359
363
  url: api.assembleUrl("/chat/completions"),
360
364
  headers: api.headers,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "modelfusion",
3
3
  "description": "Build multimodal applications, chatbots, and agents with JavaScript and TypeScript.",
4
- "version": "0.35.0",
4
+ "version": "0.35.2",
5
5
  "author": "Lars Grammel",
6
6
  "license": "MIT",
7
7
  "keywords": [