@promptbook/javascript 0.92.0-13 → 0.92.0-15

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/umd/index.umd.js CHANGED
@@ -23,7 +23,7 @@
23
23
  * @generated
24
24
  * @see https://github.com/webgptorg/promptbook
25
25
  */
26
- const PROMPTBOOK_ENGINE_VERSION = '0.92.0-13';
26
+ const PROMPTBOOK_ENGINE_VERSION = '0.92.0-15';
27
27
  /**
28
28
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
29
29
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -94,6 +94,48 @@
94
94
  * TODO: [🧠][🧜‍♂️] Maybe join remoteServerUrl and path into single value
95
95
  */
96
96
 
97
+ /**
98
+ * Orders JSON object by keys
99
+ *
100
+ * @returns The same type of object as the input re-ordered
101
+ * @public exported from `@promptbook/utils`
102
+ */
103
+ function orderJson(options) {
104
+ const { value, order } = options;
105
+ const orderedValue = {
106
+ ...(order === undefined ? {} : Object.fromEntries(order.map((key) => [key, undefined]))),
107
+ ...value,
108
+ };
109
+ return orderedValue;
110
+ }
111
+
112
+ /**
113
+ * Freezes the given object and all its nested objects recursively
114
+ *
115
+ * Note: `$` is used to indicate that this function is not a pure function - it mutates given object
116
+ * Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
117
+ *
118
+ * @returns The same object as the input, but deeply frozen
119
+ * @public exported from `@promptbook/utils`
120
+ */
121
+ function $deepFreeze(objectValue) {
122
+ if (Array.isArray(objectValue)) {
123
+ return Object.freeze(objectValue.map((item) => $deepFreeze(item)));
124
+ }
125
+ const propertyNames = Object.getOwnPropertyNames(objectValue);
126
+ for (const propertyName of propertyNames) {
127
+ const value = objectValue[propertyName];
128
+ if (value && typeof value === 'object') {
129
+ $deepFreeze(value);
130
+ }
131
+ }
132
+ Object.freeze(objectValue);
133
+ return objectValue;
134
+ }
135
+ /**
136
+ * TODO: [🧠] Is there a way how to meaningfully test this utility
137
+ */
138
+
97
139
  /**
98
140
  * Make error report URL for the given error
99
141
  *
@@ -163,66 +205,333 @@
163
205
  }
164
206
 
165
207
  /**
166
- * @@@
208
+ * This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
167
209
  *
168
- * @param text @@@
169
- * @param _isFirstLetterCapital @@@
170
- * @returns @@@
171
- * @example 'helloWorld'
172
- * @example 'iLovePromptbook'
210
+ * @public exported from `@promptbook/core`
211
+ */
212
+ class WrappedError extends Error {
213
+ constructor(whatWasThrown) {
214
+ const tag = `[🤮]`;
215
+ console.error(tag, whatWasThrown);
216
+ super(spaceTrim.spaceTrim(`
217
+ Non-Error object was thrown
218
+
219
+ Note: Look for ${tag} in the console for more details
220
+ Please report issue on ${ADMIN_EMAIL}
221
+ `));
222
+ this.name = 'WrappedError';
223
+ Object.setPrototypeOf(this, WrappedError.prototype);
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Helper used in catch blocks to assert that the error is an instance of `Error`
229
+ *
230
+ * @param whatWasThrown Any object that was thrown
231
+ * @returns Nothing if the error is an instance of `Error`
232
+ * @throws `WrappedError` or `UnexpectedError` if the error is not standard
233
+ *
234
+ * @private within the repository
235
+ */
236
+ function assertsError(whatWasThrown) {
237
+ // Case 1: Handle error which was rethrown as `WrappedError`
238
+ if (whatWasThrown instanceof WrappedError) {
239
+ const wrappedError = whatWasThrown;
240
+ throw wrappedError;
241
+ }
242
+ // Case 2: Handle unexpected errors
243
+ if (whatWasThrown instanceof UnexpectedError) {
244
+ const unexpectedError = whatWasThrown;
245
+ throw unexpectedError;
246
+ }
247
+ // Case 3: Handle standard errors - keep them up to consumer
248
+ if (whatWasThrown instanceof Error) {
249
+ return;
250
+ }
251
+ // Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
252
+ throw new WrappedError(whatWasThrown);
253
+ }
254
+
255
+ /**
256
+ * Checks if the value is [🚉] serializable as JSON
257
+ * If not, throws an UnexpectedError with a rich error message and tracking
258
+ *
259
+ * - Almost all primitives are serializable BUT:
260
+ * - `undefined` is not serializable
261
+ * - `NaN` is not serializable
262
+ * - Objects and arrays are serializable if all their properties are serializable
263
+ * - Functions are not serializable
264
+ * - Circular references are not serializable
265
+ * - `Date` objects are not serializable
266
+ * - `Map` and `Set` objects are not serializable
267
+ * - `RegExp` objects are not serializable
268
+ * - `Error` objects are not serializable
269
+ * - `Symbol` objects are not serializable
270
+ * - And much more...
271
+ *
272
+ * @throws UnexpectedError if the value is not serializable as JSON
173
273
  * @public exported from `@promptbook/utils`
174
274
  */
175
- function normalizeTo_camelCase(text, _isFirstLetterCapital = false) {
176
- let charType;
177
- let lastCharType = null;
178
- let normalizedName = '';
179
- for (const char of text) {
180
- let normalizedChar;
181
- if (/^[a-z]$/.test(char)) {
182
- charType = 'LOWERCASE';
183
- normalizedChar = char;
275
+ function checkSerializableAsJson(options) {
276
+ const { value, name, message } = options;
277
+ if (value === undefined) {
278
+ throw new UnexpectedError(`${name} is undefined`);
279
+ }
280
+ else if (value === null) {
281
+ return;
282
+ }
283
+ else if (typeof value === 'boolean') {
284
+ return;
285
+ }
286
+ else if (typeof value === 'number' && !isNaN(value)) {
287
+ return;
288
+ }
289
+ else if (typeof value === 'string') {
290
+ return;
291
+ }
292
+ else if (typeof value === 'symbol') {
293
+ throw new UnexpectedError(`${name} is symbol`);
294
+ }
295
+ else if (typeof value === 'function') {
296
+ throw new UnexpectedError(`${name} is function`);
297
+ }
298
+ else if (typeof value === 'object' && Array.isArray(value)) {
299
+ for (let i = 0; i < value.length; i++) {
300
+ checkSerializableAsJson({ name: `${name}[${i}]`, value: value[i], message });
184
301
  }
185
- else if (/^[A-Z]$/.test(char)) {
186
- charType = 'UPPERCASE';
187
- normalizedChar = char.toLowerCase();
302
+ }
303
+ else if (typeof value === 'object') {
304
+ if (value instanceof Date) {
305
+ throw new UnexpectedError(spaceTrim__default["default"]((block) => `
306
+ \`${name}\` is Date
307
+
308
+ Use \`string_date_iso8601\` instead
309
+
310
+ Additional message for \`${name}\`:
311
+ ${block(message || '(nothing)')}
312
+ `));
188
313
  }
189
- else if (/^[0-9]$/.test(char)) {
190
- charType = 'NUMBER';
191
- normalizedChar = char;
314
+ else if (value instanceof Map) {
315
+ throw new UnexpectedError(`${name} is Map`);
192
316
  }
193
- else {
194
- charType = 'OTHER';
195
- normalizedChar = '';
317
+ else if (value instanceof Set) {
318
+ throw new UnexpectedError(`${name} is Set`);
196
319
  }
197
- if (!lastCharType) {
198
- if (_isFirstLetterCapital) {
199
- normalizedChar = normalizedChar.toUpperCase(); //TODO: DRY
200
- }
320
+ else if (value instanceof RegExp) {
321
+ throw new UnexpectedError(`${name} is RegExp`);
201
322
  }
202
- else if (charType !== lastCharType &&
203
- !(charType === 'LOWERCASE' && lastCharType === 'UPPERCASE') &&
204
- !(lastCharType === 'NUMBER') &&
205
- !(charType === 'NUMBER')) {
206
- normalizedChar = normalizedChar.toUpperCase(); //TODO: [🌺] DRY
323
+ else if (value instanceof Error) {
324
+ throw new UnexpectedError(spaceTrim__default["default"]((block) => `
325
+ \`${name}\` is unserialized Error
326
+
327
+ Use function \`serializeError\`
328
+
329
+ Additional message for \`${name}\`:
330
+ ${block(message || '(nothing)')}
331
+
332
+ `));
333
+ }
334
+ else {
335
+ for (const [subName, subValue] of Object.entries(value)) {
336
+ if (subValue === undefined) {
337
+ // Note: undefined in object is serializable - it is just omited
338
+ continue;
339
+ }
340
+ checkSerializableAsJson({ name: `${name}.${subName}`, value: subValue, message });
341
+ }
342
+ try {
343
+ JSON.stringify(value); // <- TODO: [0]
344
+ }
345
+ catch (error) {
346
+ assertsError(error);
347
+ throw new UnexpectedError(spaceTrim__default["default"]((block) => `
348
+ \`${name}\` is not serializable
349
+
350
+ ${block(error.stack || error.message)}
351
+
352
+ Additional message for \`${name}\`:
353
+ ${block(message || '(nothing)')}
354
+ `));
355
+ }
356
+ /*
357
+ TODO: [0] Is there some more elegant way to check circular references?
358
+ const seen = new Set();
359
+ const stack = [{ value }];
360
+ while (stack.length > 0) {
361
+ const { value } = stack.pop()!;
362
+ if (typeof value === 'object' && value !== null) {
363
+ if (seen.has(value)) {
364
+ throw new UnexpectedError(`${name} has circular reference`);
365
+ }
366
+ seen.add(value);
367
+ if (Array.isArray(value)) {
368
+ stack.push(...value.map((value) => ({ value })));
369
+ } else {
370
+ stack.push(...Object.values(value).map((value) => ({ value })));
371
+ }
372
+ }
373
+ }
374
+ */
375
+ return;
207
376
  }
208
- normalizedName += normalizedChar;
209
- lastCharType = charType;
210
377
  }
211
- return normalizedName;
378
+ else {
379
+ throw new UnexpectedError(spaceTrim__default["default"]((block) => `
380
+ \`${name}\` is unknown type
381
+
382
+ Additional message for \`${name}\`:
383
+ ${block(message || '(nothing)')}
384
+ `));
385
+ }
212
386
  }
213
387
  /**
214
- * TODO: [🌺] Use some intermediate util splitWords
388
+ * TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
389
+ * TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
390
+ * Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
215
391
  */
216
392
 
217
393
  /**
218
- * Removes emojis from a string and fix whitespaces
394
+ * @@@
219
395
  *
220
- * @param text with emojis
221
- * @returns text without emojis
222
396
  * @public exported from `@promptbook/utils`
223
397
  */
224
- function removeEmojis(text) {
225
- // Replace emojis (and also ZWJ sequence) with hyphens
398
+ function deepClone(objectValue) {
399
+ return JSON.parse(JSON.stringify(objectValue));
400
+ /*
401
+ TODO: [🧠] Is there a better implementation?
402
+ > const propertyNames = Object.getOwnPropertyNames(objectValue);
403
+ > for (const propertyName of propertyNames) {
404
+ > const value = (objectValue as really_any)[propertyName];
405
+ > if (value && typeof value === 'object') {
406
+ > deepClone(value);
407
+ > }
408
+ > }
409
+ > return Object.assign({}, objectValue);
410
+ */
411
+ }
412
+ /**
413
+ * TODO: [🧠] Is there a way how to meaningfully test this utility
414
+ */
415
+
416
+ /**
417
+ * Utility to export a JSON object from a function
418
+ *
419
+ * 1) Checks if the value is serializable as JSON
420
+ * 2) Makes a deep clone of the object
421
+ * 2) Orders the object properties
422
+ * 2) Deeply freezes the cloned object
423
+ *
424
+ * Note: This function does not mutates the given object
425
+ *
426
+ * @returns The same type of object as the input but read-only and re-ordered
427
+ * @public exported from `@promptbook/utils`
428
+ */
429
+ function exportJson(options) {
430
+ const { name, value, order, message } = options;
431
+ checkSerializableAsJson({ name, value, message });
432
+ const orderedValue =
433
+ // TODO: Fix error "Type instantiation is excessively deep and possibly infinite."
434
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
435
+ // @ts-ignore
436
+ order === undefined
437
+ ? deepClone(value)
438
+ : orderJson({
439
+ value: value,
440
+ // <- Note: checkSerializableAsJson asserts that the value is serializable as JSON
441
+ order: order,
442
+ });
443
+ $deepFreeze(orderedValue);
444
+ return orderedValue;
445
+ }
446
+ /**
447
+ * TODO: [🧠] Is there a way how to meaningfully test this utility
448
+ */
449
+
450
+ /**
451
+ * The names of the parameters that are reserved for special purposes
452
+ *
453
+ * @public exported from `@promptbook/core`
454
+ */
455
+ exportJson({
456
+ name: 'RESERVED_PARAMETER_NAMES',
457
+ message: `The names of the parameters that are reserved for special purposes`,
458
+ value: [
459
+ 'content',
460
+ 'context',
461
+ 'knowledge',
462
+ 'examples',
463
+ 'modelName',
464
+ 'currentDate',
465
+ // <- TODO: list here all command names
466
+ // <- TODO: Add more like 'date', 'modelName',...
467
+ // <- TODO: Add [emoji] + instructions ACRY when adding new reserved parameter
468
+ ],
469
+ });
470
+ /**
471
+ * Note: [💞] Ignore a discrepancy between file name and entity name
472
+ */
473
+
474
+ /**
475
+ * @@@
476
+ *
477
+ * @param text @@@
478
+ * @param _isFirstLetterCapital @@@
479
+ * @returns @@@
480
+ * @example 'helloWorld'
481
+ * @example 'iLovePromptbook'
482
+ * @public exported from `@promptbook/utils`
483
+ */
484
+ function normalizeTo_camelCase(text, _isFirstLetterCapital = false) {
485
+ let charType;
486
+ let lastCharType = null;
487
+ let normalizedName = '';
488
+ for (const char of text) {
489
+ let normalizedChar;
490
+ if (/^[a-z]$/.test(char)) {
491
+ charType = 'LOWERCASE';
492
+ normalizedChar = char;
493
+ }
494
+ else if (/^[A-Z]$/.test(char)) {
495
+ charType = 'UPPERCASE';
496
+ normalizedChar = char.toLowerCase();
497
+ }
498
+ else if (/^[0-9]$/.test(char)) {
499
+ charType = 'NUMBER';
500
+ normalizedChar = char;
501
+ }
502
+ else {
503
+ charType = 'OTHER';
504
+ normalizedChar = '';
505
+ }
506
+ if (!lastCharType) {
507
+ if (_isFirstLetterCapital) {
508
+ normalizedChar = normalizedChar.toUpperCase(); //TODO: DRY
509
+ }
510
+ }
511
+ else if (charType !== lastCharType &&
512
+ !(charType === 'LOWERCASE' && lastCharType === 'UPPERCASE') &&
513
+ !(lastCharType === 'NUMBER') &&
514
+ !(charType === 'NUMBER')) {
515
+ normalizedChar = normalizedChar.toUpperCase(); //TODO: [🌺] DRY
516
+ }
517
+ normalizedName += normalizedChar;
518
+ lastCharType = charType;
519
+ }
520
+ return normalizedName;
521
+ }
522
+ /**
523
+ * TODO: [🌺] Use some intermediate util splitWords
524
+ */
525
+
526
+ /**
527
+ * Removes emojis from a string and fix whitespaces
528
+ *
529
+ * @param text with emojis
530
+ * @returns text without emojis
531
+ * @public exported from `@promptbook/utils`
532
+ */
533
+ function removeEmojis(text) {
534
+ // Replace emojis (and also ZWJ sequence) with hyphens
226
535
  text = text.replace(/(\p{Extended_Pictographic})\p{Modifier_Symbol}/gu, '$1');
227
536
  text = text.replace(/(\p{Extended_Pictographic})[\u{FE00}-\u{FE0F}]/gu, '$1');
228
537
  text = text.replace(/(\p{Extended_Pictographic})(\u{200D}\p{Extended_Pictographic})*/gu, '$1');
@@ -592,26 +901,6 @@
592
901
  * TODO: [🧠][🌂] Add id to all errors
593
902
  */
594
903
 
595
- /**
596
- * This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
597
- *
598
- * @public exported from `@promptbook/core`
599
- */
600
- class WrappedError extends Error {
601
- constructor(whatWasThrown) {
602
- const tag = `[🤮]`;
603
- console.error(tag, whatWasThrown);
604
- super(spaceTrim.spaceTrim(`
605
- Non-Error object was thrown
606
-
607
- Note: Look for ${tag} in the console for more details
608
- Please report issue on ${ADMIN_EMAIL}
609
- `));
610
- this.name = 'WrappedError';
611
- Object.setPrototypeOf(this, WrappedError.prototype);
612
- }
613
- }
614
-
615
904
  /**
616
905
  * Index of all javascript errors
617
906
  *
@@ -639,295 +928,6 @@
639
928
  * Note: [💞] Ignore a discrepancy between file name and entity name
640
929
  */
641
930
 
642
- /**
643
- * Helper used in catch blocks to assert that the error is an instance of `Error`
644
- *
645
- * @param whatWasThrown Any object that was thrown
646
- * @returns Nothing if the error is an instance of `Error`
647
- * @throws `WrappedError` or `UnexpectedError` if the error is not standard
648
- *
649
- * @private within the repository
650
- */
651
- function assertsError(whatWasThrown) {
652
- // Case 1: Handle error which was rethrown as `WrappedError`
653
- if (whatWasThrown instanceof WrappedError) {
654
- const wrappedError = whatWasThrown;
655
- throw wrappedError;
656
- }
657
- // Case 2: Handle unexpected errors
658
- if (whatWasThrown instanceof UnexpectedError) {
659
- const unexpectedError = whatWasThrown;
660
- throw unexpectedError;
661
- }
662
- // Case 3: Handle standard errors - keep them up to consumer
663
- if (whatWasThrown instanceof Error) {
664
- return;
665
- }
666
- // Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
667
- throw new WrappedError(whatWasThrown);
668
- }
669
-
670
- /**
671
- * Orders JSON object by keys
672
- *
673
- * @returns The same type of object as the input re-ordered
674
- * @public exported from `@promptbook/utils`
675
- */
676
- function orderJson(options) {
677
- const { value, order } = options;
678
- const orderedValue = {
679
- ...(order === undefined ? {} : Object.fromEntries(order.map((key) => [key, undefined]))),
680
- ...value,
681
- };
682
- return orderedValue;
683
- }
684
-
685
- /**
686
- * Freezes the given object and all its nested objects recursively
687
- *
688
- * Note: `$` is used to indicate that this function is not a pure function - it mutates given object
689
- * Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
690
- *
691
- * @returns The same object as the input, but deeply frozen
692
- * @public exported from `@promptbook/utils`
693
- */
694
- function $deepFreeze(objectValue) {
695
- if (Array.isArray(objectValue)) {
696
- return Object.freeze(objectValue.map((item) => $deepFreeze(item)));
697
- }
698
- const propertyNames = Object.getOwnPropertyNames(objectValue);
699
- for (const propertyName of propertyNames) {
700
- const value = objectValue[propertyName];
701
- if (value && typeof value === 'object') {
702
- $deepFreeze(value);
703
- }
704
- }
705
- Object.freeze(objectValue);
706
- return objectValue;
707
- }
708
- /**
709
- * TODO: [🧠] Is there a way how to meaningfully test this utility
710
- */
711
-
712
- /**
713
- * Checks if the value is [🚉] serializable as JSON
714
- * If not, throws an UnexpectedError with a rich error message and tracking
715
- *
716
- * - Almost all primitives are serializable BUT:
717
- * - `undefined` is not serializable
718
- * - `NaN` is not serializable
719
- * - Objects and arrays are serializable if all their properties are serializable
720
- * - Functions are not serializable
721
- * - Circular references are not serializable
722
- * - `Date` objects are not serializable
723
- * - `Map` and `Set` objects are not serializable
724
- * - `RegExp` objects are not serializable
725
- * - `Error` objects are not serializable
726
- * - `Symbol` objects are not serializable
727
- * - And much more...
728
- *
729
- * @throws UnexpectedError if the value is not serializable as JSON
730
- * @public exported from `@promptbook/utils`
731
- */
732
- function checkSerializableAsJson(options) {
733
- const { value, name, message } = options;
734
- if (value === undefined) {
735
- throw new UnexpectedError(`${name} is undefined`);
736
- }
737
- else if (value === null) {
738
- return;
739
- }
740
- else if (typeof value === 'boolean') {
741
- return;
742
- }
743
- else if (typeof value === 'number' && !isNaN(value)) {
744
- return;
745
- }
746
- else if (typeof value === 'string') {
747
- return;
748
- }
749
- else if (typeof value === 'symbol') {
750
- throw new UnexpectedError(`${name} is symbol`);
751
- }
752
- else if (typeof value === 'function') {
753
- throw new UnexpectedError(`${name} is function`);
754
- }
755
- else if (typeof value === 'object' && Array.isArray(value)) {
756
- for (let i = 0; i < value.length; i++) {
757
- checkSerializableAsJson({ name: `${name}[${i}]`, value: value[i], message });
758
- }
759
- }
760
- else if (typeof value === 'object') {
761
- if (value instanceof Date) {
762
- throw new UnexpectedError(spaceTrim__default["default"]((block) => `
763
- \`${name}\` is Date
764
-
765
- Use \`string_date_iso8601\` instead
766
-
767
- Additional message for \`${name}\`:
768
- ${block(message || '(nothing)')}
769
- `));
770
- }
771
- else if (value instanceof Map) {
772
- throw new UnexpectedError(`${name} is Map`);
773
- }
774
- else if (value instanceof Set) {
775
- throw new UnexpectedError(`${name} is Set`);
776
- }
777
- else if (value instanceof RegExp) {
778
- throw new UnexpectedError(`${name} is RegExp`);
779
- }
780
- else if (value instanceof Error) {
781
- throw new UnexpectedError(spaceTrim__default["default"]((block) => `
782
- \`${name}\` is unserialized Error
783
-
784
- Use function \`serializeError\`
785
-
786
- Additional message for \`${name}\`:
787
- ${block(message || '(nothing)')}
788
-
789
- `));
790
- }
791
- else {
792
- for (const [subName, subValue] of Object.entries(value)) {
793
- if (subValue === undefined) {
794
- // Note: undefined in object is serializable - it is just omited
795
- continue;
796
- }
797
- checkSerializableAsJson({ name: `${name}.${subName}`, value: subValue, message });
798
- }
799
- try {
800
- JSON.stringify(value); // <- TODO: [0]
801
- }
802
- catch (error) {
803
- assertsError(error);
804
- throw new UnexpectedError(spaceTrim__default["default"]((block) => `
805
- \`${name}\` is not serializable
806
-
807
- ${block(error.stack || error.message)}
808
-
809
- Additional message for \`${name}\`:
810
- ${block(message || '(nothing)')}
811
- `));
812
- }
813
- /*
814
- TODO: [0] Is there some more elegant way to check circular references?
815
- const seen = new Set();
816
- const stack = [{ value }];
817
- while (stack.length > 0) {
818
- const { value } = stack.pop()!;
819
- if (typeof value === 'object' && value !== null) {
820
- if (seen.has(value)) {
821
- throw new UnexpectedError(`${name} has circular reference`);
822
- }
823
- seen.add(value);
824
- if (Array.isArray(value)) {
825
- stack.push(...value.map((value) => ({ value })));
826
- } else {
827
- stack.push(...Object.values(value).map((value) => ({ value })));
828
- }
829
- }
830
- }
831
- */
832
- return;
833
- }
834
- }
835
- else {
836
- throw new UnexpectedError(spaceTrim__default["default"]((block) => `
837
- \`${name}\` is unknown type
838
-
839
- Additional message for \`${name}\`:
840
- ${block(message || '(nothing)')}
841
- `));
842
- }
843
- }
844
- /**
845
- * TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
846
- * TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
847
- * Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
848
- */
849
-
850
- /**
851
- * @@@
852
- *
853
- * @public exported from `@promptbook/utils`
854
- */
855
- function deepClone(objectValue) {
856
- return JSON.parse(JSON.stringify(objectValue));
857
- /*
858
- TODO: [🧠] Is there a better implementation?
859
- > const propertyNames = Object.getOwnPropertyNames(objectValue);
860
- > for (const propertyName of propertyNames) {
861
- > const value = (objectValue as really_any)[propertyName];
862
- > if (value && typeof value === 'object') {
863
- > deepClone(value);
864
- > }
865
- > }
866
- > return Object.assign({}, objectValue);
867
- */
868
- }
869
- /**
870
- * TODO: [🧠] Is there a way how to meaningfully test this utility
871
- */
872
-
873
- /**
874
- * Utility to export a JSON object from a function
875
- *
876
- * 1) Checks if the value is serializable as JSON
877
- * 2) Makes a deep clone of the object
878
- * 2) Orders the object properties
879
- * 2) Deeply freezes the cloned object
880
- *
881
- * Note: This function does not mutates the given object
882
- *
883
- * @returns The same type of object as the input but read-only and re-ordered
884
- * @public exported from `@promptbook/utils`
885
- */
886
- function exportJson(options) {
887
- const { name, value, order, message } = options;
888
- checkSerializableAsJson({ name, value, message });
889
- const orderedValue =
890
- // TODO: Fix error "Type instantiation is excessively deep and possibly infinite."
891
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
892
- // @ts-ignore
893
- order === undefined
894
- ? deepClone(value)
895
- : orderJson({
896
- value: value,
897
- // <- Note: checkSerializableAsJson asserts that the value is serializable as JSON
898
- order: order,
899
- });
900
- $deepFreeze(orderedValue);
901
- return orderedValue;
902
- }
903
- /**
904
- * TODO: [🧠] Is there a way how to meaningfully test this utility
905
- */
906
-
907
- /**
908
- * The names of the parameters that are reserved for special purposes
909
- *
910
- * @public exported from `@promptbook/core`
911
- */
912
- exportJson({
913
- name: 'RESERVED_PARAMETER_NAMES',
914
- message: `The names of the parameters that are reserved for special purposes`,
915
- value: [
916
- 'content',
917
- 'context',
918
- 'knowledge',
919
- 'examples',
920
- 'modelName',
921
- 'currentDate',
922
- // <- TODO: list here all command names
923
- // <- TODO: Add more like 'date', 'modelName',...
924
- // <- TODO: Add [emoji] + instructions ACRY when adding new reserved parameter
925
- ],
926
- });
927
- /**
928
- * Note: [💞] Ignore a discrepancy between file name and entity name
929
- */
930
-
931
931
  /**
932
932
  * Format either small or big number
933
933
  *