@promptbook/javascript 0.92.0-13 → 0.92.0-14

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