@promptbook/utils 0.112.0-73 → 0.112.0-80

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 (74) hide show
  1. package/README.md +9 -9
  2. package/esm/index.es.js +310 -204
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  5. package/esm/src/avatars/visuals/octopus3d2AvatarVisual.d.ts +7 -0
  6. package/esm/src/avatars/visuals/octopus3dAvatarVisualShared.d.ts +37 -0
  7. package/esm/src/book-components/Chat/save/_common/chatExportRendering.d.ts +47 -0
  8. package/esm/src/book-components/Chat/save/html/htmlSaveFormatDefinition.d.ts +12 -0
  9. package/esm/src/book-components/Chat/save/index.d.ts +2 -2
  10. package/esm/src/book-components/Chat/save/markdown/mdSaveFormatDefinition.d.ts +5 -3
  11. package/esm/src/book-components/Chat/save/pdf/buildChatPdf.d.ts +3 -3
  12. package/esm/src/book-components/Chat/save/pdf/pdfSaveFormatDefinition.d.ts +1 -1
  13. package/esm/src/cli/cli-commands/agent/agentProjectPaths.d.ts +8 -8
  14. package/esm/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +1 -1
  15. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +56 -0
  16. package/esm/src/cli/cli-commands/agents-server/buildAgentsServer.test.d.ts +1 -0
  17. package/esm/src/cli/cli-commands/agents-server/ensureAgentsServerEnvFile.d.ts +7 -0
  18. package/esm/src/cli/cli-commands/agents-server/ensureAgentsServerGitignoreFile.d.ts +7 -0
  19. package/esm/src/cli/cli-commands/agents-server/init.d.ts +9 -0
  20. package/esm/src/cli/cli-commands/agents-server/init.test.d.ts +1 -0
  21. package/esm/src/cli/cli-commands/agents-server/initializeAgentsServerProjectConfiguration.d.ts +17 -0
  22. package/esm/src/cli/cli-commands/agents-server/printAgentsServerInitializationSummary.d.ts +7 -0
  23. package/esm/src/cli/cli-commands/agents-server/run.d.ts +14 -0
  24. package/esm/src/cli/cli-commands/agents-server/run.test.d.ts +1 -0
  25. package/esm/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +23 -0
  26. package/esm/src/cli/cli-commands/agents-server.d.ts +8 -0
  27. package/esm/src/cli/cli-commands/common/projectInitialization.d.ts +65 -0
  28. package/esm/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +44 -0
  29. package/esm/src/cli/common/$deprecateCliCommand.d.ts +8 -0
  30. package/esm/src/cli/common/$deprecateCliCommand.test.d.ts +1 -0
  31. package/esm/src/utils/color/Color.d.ts +4 -44
  32. package/esm/src/utils/color/ColorValue.d.ts +55 -0
  33. package/esm/src/utils/color/isHexColorString.d.ts +10 -0
  34. package/esm/src/utils/color/parseColorString.d.ts +11 -0
  35. package/esm/src/version.d.ts +1 -1
  36. package/package.json +1 -1
  37. package/umd/index.umd.js +310 -204
  38. package/umd/index.umd.js.map +1 -1
  39. package/umd/src/avatars/types/AvatarVisualDefinition.d.ts +1 -1
  40. package/umd/src/avatars/visuals/octopus3d2AvatarVisual.d.ts +7 -0
  41. package/umd/src/avatars/visuals/octopus3dAvatarVisualShared.d.ts +37 -0
  42. package/umd/src/book-components/Chat/save/_common/chatExportRendering.d.ts +47 -0
  43. package/umd/src/book-components/Chat/save/html/htmlSaveFormatDefinition.d.ts +12 -0
  44. package/umd/src/book-components/Chat/save/index.d.ts +2 -2
  45. package/umd/src/book-components/Chat/save/markdown/mdSaveFormatDefinition.d.ts +5 -3
  46. package/umd/src/book-components/Chat/save/pdf/buildChatPdf.d.ts +3 -3
  47. package/umd/src/book-components/Chat/save/pdf/pdfSaveFormatDefinition.d.ts +1 -1
  48. package/umd/src/cli/cli-commands/agent/agentProjectPaths.d.ts +8 -8
  49. package/umd/src/cli/cli-commands/agent/initializeAgentRunnerCommand.d.ts +1 -1
  50. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer.d.ts +56 -0
  51. package/umd/src/cli/cli-commands/agents-server/buildAgentsServer.test.d.ts +1 -0
  52. package/umd/src/cli/cli-commands/agents-server/ensureAgentsServerEnvFile.d.ts +7 -0
  53. package/umd/src/cli/cli-commands/agents-server/ensureAgentsServerGitignoreFile.d.ts +7 -0
  54. package/umd/src/cli/cli-commands/agents-server/init.d.ts +9 -0
  55. package/umd/src/cli/cli-commands/agents-server/init.test.d.ts +1 -0
  56. package/umd/src/cli/cli-commands/agents-server/initializeAgentsServerProjectConfiguration.d.ts +17 -0
  57. package/umd/src/cli/cli-commands/agents-server/printAgentsServerInitializationSummary.d.ts +7 -0
  58. package/umd/src/cli/cli-commands/agents-server/run.d.ts +14 -0
  59. package/umd/src/cli/cli-commands/agents-server/run.test.d.ts +1 -0
  60. package/umd/src/cli/cli-commands/agents-server/startAgentsServer.d.ts +23 -0
  61. package/umd/src/cli/cli-commands/agents-server.d.ts +8 -0
  62. package/umd/src/cli/cli-commands/common/projectInitialization.d.ts +65 -0
  63. package/umd/src/cli/cli-commands/common/promptRunnerCliOptions.d.ts +44 -0
  64. package/umd/src/cli/common/$deprecateCliCommand.d.ts +8 -0
  65. package/umd/src/cli/common/$deprecateCliCommand.test.d.ts +1 -0
  66. package/umd/src/utils/color/Color.d.ts +4 -44
  67. package/umd/src/utils/color/ColorValue.d.ts +55 -0
  68. package/umd/src/utils/color/isHexColorString.d.ts +10 -0
  69. package/umd/src/utils/color/parseColorString.d.ts +11 -0
  70. package/umd/src/version.d.ts +1 -1
  71. package/esm/src/cli/cli-commands/coder/appendBlock.d.ts +0 -6
  72. package/esm/src/cli/cli-commands/coder/readTextFileIfExists.d.ts +0 -6
  73. package/umd/src/cli/cli-commands/coder/appendBlock.d.ts +0 -6
  74. package/umd/src/cli/cli-commands/coder/readTextFileIfExists.d.ts +0 -6
package/umd/index.umd.js CHANGED
@@ -22,7 +22,7 @@
22
22
  * @generated
23
23
  * @see https://github.com/webgptorg/promptbook
24
24
  */
25
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-73';
25
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-80';
26
26
  /**
27
27
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
28
28
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -272,6 +272,111 @@
272
272
  }
273
273
  }
274
274
 
275
+ /**
276
+ * Shared immutable channel storage and serialization helpers for `Color`.
277
+ *
278
+ * @private base class of Color
279
+ */
280
+ class ColorValue {
281
+ constructor(red, green, blue, alpha = 255) {
282
+ this.red = red;
283
+ this.green = green;
284
+ this.blue = blue;
285
+ this.alpha = alpha;
286
+ checkChannelValue('Red', red);
287
+ checkChannelValue('Green', green);
288
+ checkChannelValue('Blue', blue);
289
+ checkChannelValue('Alpha', alpha);
290
+ }
291
+ /**
292
+ * Shortcut for `red` property
293
+ * Number from 0 to 255
294
+ * @alias red
295
+ */
296
+ get r() {
297
+ return this.red;
298
+ }
299
+ /**
300
+ * Shortcut for `green` property
301
+ * Number from 0 to 255
302
+ * @alias green
303
+ */
304
+ get g() {
305
+ return this.green;
306
+ }
307
+ /**
308
+ * Shortcut for `blue` property
309
+ * Number from 0 to 255
310
+ * @alias blue
311
+ */
312
+ get b() {
313
+ return this.blue;
314
+ }
315
+ /**
316
+ * Shortcut for `alpha` property
317
+ * Number from 0 (transparent) to 255 (opaque)
318
+ * @alias alpha
319
+ */
320
+ get a() {
321
+ return this.alpha;
322
+ }
323
+ /**
324
+ * Shortcut for `alpha` property
325
+ * Number from 0 (transparent) to 255 (opaque)
326
+ * @alias alpha
327
+ */
328
+ get opacity() {
329
+ return this.alpha;
330
+ }
331
+ /**
332
+ * Shortcut for 1-`alpha` property
333
+ */
334
+ get transparency() {
335
+ return 255 - this.alpha;
336
+ }
337
+ clone() {
338
+ return take(this.createColor(this.red, this.green, this.blue, this.alpha));
339
+ }
340
+ toString() {
341
+ return this.toHex();
342
+ }
343
+ toHex() {
344
+ if (this.alpha === 255) {
345
+ return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
346
+ .toString(16)
347
+ .padStart(2, '0')}`;
348
+ }
349
+ else {
350
+ return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
351
+ .toString(16)
352
+ .padStart(2, '0')}${this.alpha.toString(16).padStart(2, '0')}`;
353
+ }
354
+ }
355
+ toRgb() {
356
+ if (this.alpha === 255) {
357
+ return `rgb(${this.red}, ${this.green}, ${this.blue})`;
358
+ }
359
+ else {
360
+ return `rgba(${this.red}, ${this.green}, ${this.blue}, ${Math.round((this.alpha / 255) * 100)}%)`;
361
+ }
362
+ }
363
+ toHsl() {
364
+ throw new Error(`Getting HSL is not implemented`);
365
+ }
366
+ }
367
+
368
+ /**
369
+ * Checks if the given value is a valid hex color string
370
+ *
371
+ * @param value - value to check
372
+ * @returns true if the value is a valid hex color string (e.g., `#009edd`, `#fff`, etc.)
373
+ *
374
+ * @private function of Color
375
+ */
376
+ function isHexColorString(value) {
377
+ return (typeof value === 'string' && /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value));
378
+ }
379
+
275
380
  /**
276
381
  * Constant for short hex lengths.
277
382
  */
@@ -483,16 +588,53 @@
483
588
 
484
589
  /**
485
590
  * Pattern matching hsl regex.
591
+ *
592
+ * @private function of Color
486
593
  */
487
594
  const HSL_REGEX_PATTERN = /^hsl\(\s*([0-9.]+)\s*,\s*([0-9.]+)%\s*,\s*([0-9.]+)%\s*\)$/;
488
595
  /**
489
596
  * Pattern matching RGB regex.
597
+ *
598
+ * @private function of Color
490
599
  */
491
600
  const RGB_REGEX_PATTERN = /^rgb\(\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*\)$/;
492
601
  /**
493
602
  * Pattern matching rgba regex.
603
+ *
604
+ * @private function of Color
494
605
  */
495
606
  const RGBA_REGEX_PATTERN = /^rgba\(\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*\)$/;
607
+ /**
608
+ * Parses a supported color string into RGBA channels.
609
+ *
610
+ * @param color as a string for example `#009edd`, `rgb(0,158,221)`, `rgb(0%,62%,86.7%)`, `hsl(197.1,100%,43.3%)`, `red`, `darkgrey`,...
611
+ * @returns RGBA channel values.
612
+ *
613
+ * @private function of Color
614
+ */
615
+ function parseColorString(color) {
616
+ const trimmed = color.trim();
617
+ const cssColor = CSS_COLORS[trimmed];
618
+ if (cssColor) {
619
+ return parseColorString(cssColor);
620
+ }
621
+ else if (isHexColorString(trimmed)) {
622
+ return parseHexColor(trimmed);
623
+ }
624
+ if (HSL_REGEX_PATTERN.test(trimmed)) {
625
+ return parseHslColor(trimmed);
626
+ }
627
+ else if (RGB_REGEX_PATTERN.test(trimmed)) {
628
+ return parseRgbColor(trimmed);
629
+ }
630
+ else if (RGBA_REGEX_PATTERN.test(trimmed)) {
631
+ return parseRgbaColor(trimmed);
632
+ }
633
+ else {
634
+ throw new Error(`Can not create a new Color instance from string "${trimmed}".`);
635
+ }
636
+ }
637
+
496
638
  /**
497
639
  * Color object represents an RGB color with alpha channel
498
640
  *
@@ -500,7 +642,7 @@
500
642
  *
501
643
  * @public exported from `@promptbook/color`
502
644
  */
503
- class Color {
645
+ class Color extends ColorValue {
504
646
  /**
505
647
  * Creates a new Color instance from miscellaneous formats
506
648
  * - It can receive Color instance and just return the same instance
@@ -573,25 +715,7 @@
573
715
  * @returns Color object
574
716
  */
575
717
  static fromString(color) {
576
- const trimmed = color.trim();
577
- if (CSS_COLORS[trimmed]) {
578
- return Color.fromString(CSS_COLORS[trimmed]);
579
- }
580
- else if (Color.isHexColorString(trimmed)) {
581
- return Color.fromHex(trimmed);
582
- }
583
- if (HSL_REGEX_PATTERN.test(trimmed)) {
584
- return Color.fromHsl(trimmed);
585
- }
586
- else if (RGB_REGEX_PATTERN.test(trimmed)) {
587
- return Color.fromRgbString(trimmed);
588
- }
589
- else if (RGBA_REGEX_PATTERN.test(trimmed)) {
590
- return Color.fromRgbaString(trimmed);
591
- }
592
- else {
593
- throw new Error(`Can not create a new Color instance from string "${trimmed}".`);
594
- }
718
+ return Color.fromColorChannels(parseColorString(color));
595
719
  }
596
720
  /**
597
721
  * Gets common color
@@ -621,8 +745,7 @@
621
745
  * @returns Color object
622
746
  */
623
747
  static fromHex(hex) {
624
- const { red, green, blue, alpha } = parseHexColor(hex);
625
- return take(new Color(red, green, blue, alpha));
748
+ return Color.fromColorChannels(parseHexColor(hex));
626
749
  }
627
750
  /**
628
751
  * Creates a new Color instance from color in hsl format
@@ -631,8 +754,7 @@
631
754
  * @returns Color object
632
755
  */
633
756
  static fromHsl(hsl) {
634
- const { red, green, blue, alpha } = parseHslColor(hsl);
635
- return take(new Color(red, green, blue, alpha));
757
+ return Color.fromColorChannels(parseHslColor(hsl));
636
758
  }
637
759
  /**
638
760
  * Creates a new Color instance from color in rgb format
@@ -641,8 +763,7 @@
641
763
  * @returns Color object
642
764
  */
643
765
  static fromRgbString(rgb) {
644
- const { red, green, blue, alpha } = parseRgbColor(rgb);
645
- return take(new Color(red, green, blue, alpha));
766
+ return Color.fromColorChannels(parseRgbColor(rgb));
646
767
  }
647
768
  /**
648
769
  * Creates a new Color instance from color in rbga format
@@ -651,8 +772,7 @@
651
772
  * @returns Color object
652
773
  */
653
774
  static fromRgbaString(rgba) {
654
- const { red, green, blue, alpha } = parseRgbaColor(rgba);
655
- return take(new Color(red, green, blue, alpha));
775
+ return Color.fromColorChannels(parseRgbaColor(rgba));
656
776
  }
657
777
  /**
658
778
  * Creates a new Color for color channels values
@@ -664,7 +784,7 @@
664
784
  * @returns Color object
665
785
  */
666
786
  static fromValues(red, green, blue, alpha = 255) {
667
- return take(new Color(red, green, blue, alpha));
787
+ return Color.fromColorChannels({ red, green, blue, alpha });
668
788
  }
669
789
  /**
670
790
  * Checks if the given value is a valid Color object.
@@ -697,8 +817,7 @@
697
817
  * @returns true if the value is a valid hex color string (e.g., `#009edd`, `#fff`, etc.)
698
818
  */
699
819
  static isHexColorString(value) {
700
- return (typeof value === 'string' &&
701
- /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value));
820
+ return isHexColorString(value);
702
821
  }
703
822
  /**
704
823
  * Creates new Color object
@@ -711,89 +830,13 @@
711
830
  * @param alpha number from 0 (transparent) to 255 (opaque)
712
831
  */
713
832
  constructor(red, green, blue, alpha = 255) {
714
- this.red = red;
715
- this.green = green;
716
- this.blue = blue;
717
- this.alpha = alpha;
718
- checkChannelValue('Red', red);
719
- checkChannelValue('Green', green);
720
- checkChannelValue('Blue', blue);
721
- checkChannelValue('Alpha', alpha);
722
- }
723
- /**
724
- * Shortcut for `red` property
725
- * Number from 0 to 255
726
- * @alias red
727
- */
728
- get r() {
729
- return this.red;
730
- }
731
- /**
732
- * Shortcut for `green` property
733
- * Number from 0 to 255
734
- * @alias green
735
- */
736
- get g() {
737
- return this.green;
738
- }
739
- /**
740
- * Shortcut for `blue` property
741
- * Number from 0 to 255
742
- * @alias blue
743
- */
744
- get b() {
745
- return this.blue;
746
- }
747
- /**
748
- * Shortcut for `alpha` property
749
- * Number from 0 (transparent) to 255 (opaque)
750
- * @alias alpha
751
- */
752
- get a() {
753
- return this.alpha;
833
+ super(red, green, blue, alpha);
754
834
  }
755
- /**
756
- * Shortcut for `alpha` property
757
- * Number from 0 (transparent) to 255 (opaque)
758
- * @alias alpha
759
- */
760
- get opacity() {
761
- return this.alpha;
835
+ createColor(red, green, blue, alpha) {
836
+ return new Color(red, green, blue, alpha);
762
837
  }
763
- /**
764
- * Shortcut for 1-`alpha` property
765
- */
766
- get transparency() {
767
- return 255 - this.alpha;
768
- }
769
- clone() {
770
- return take(new Color(this.red, this.green, this.blue, this.alpha));
771
- }
772
- toString() {
773
- return this.toHex();
774
- }
775
- toHex() {
776
- if (this.alpha === 255) {
777
- return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
778
- .toString(16)
779
- .padStart(2, '0')}`;
780
- }
781
- else {
782
- return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
783
- .toString(16)
784
- .padStart(2, '0')}${this.alpha.toString(16).padStart(2, '0')}`;
785
- }
786
- }
787
- toRgb() {
788
- if (this.alpha === 255) {
789
- return `rgb(${this.red}, ${this.green}, ${this.blue})`;
790
- }
791
- else {
792
- return `rgba(${this.red}, ${this.green}, ${this.blue}, ${Math.round((this.alpha / 255) * 100)}%)`;
793
- }
794
- }
795
- toHsl() {
796
- throw new Error(`Getting HSL is not implemented`);
838
+ static fromColorChannels({ red, green, blue, alpha }) {
839
+ return take(new Color(red, green, blue, alpha));
797
840
  }
798
841
  }
799
842
 
@@ -1293,120 +1336,183 @@
1293
1336
  * @public exported from `@promptbook/utils`
1294
1337
  */
1295
1338
  function checkSerializableAsJson(options) {
1296
- const { value, name, message } = options;
1339
+ checkSerializableValue(options);
1340
+ }
1341
+ // TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
1342
+ // TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
1343
+ // Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
1344
+ /**
1345
+ * Checks one value and dispatches to the appropriate specialized validator.
1346
+ *
1347
+ * @private function of `checkSerializableAsJson`
1348
+ */
1349
+ function checkSerializableValue(options) {
1350
+ const { value } = options;
1351
+ if (isSerializablePrimitive(value)) {
1352
+ return;
1353
+ }
1297
1354
  if (value === undefined) {
1298
- throw new UnexpectedError(`${name} is undefined`);
1355
+ throw new UnexpectedError(`${options.name} is undefined`);
1299
1356
  }
1300
- else if (value === null) {
1301
- return;
1357
+ if (typeof value === 'symbol') {
1358
+ throw new UnexpectedError(`${options.name} is symbol`);
1302
1359
  }
1303
- else if (typeof value === 'boolean') {
1304
- return;
1360
+ if (typeof value === 'function') {
1361
+ throw new UnexpectedError(`${options.name} is function`);
1305
1362
  }
1306
- else if (typeof value === 'number' && !isNaN(value)) {
1363
+ if (Array.isArray(value)) {
1364
+ checkSerializableArray(options, value);
1307
1365
  return;
1308
1366
  }
1309
- else if (typeof value === 'string') {
1367
+ if (value !== null && typeof value === 'object') {
1368
+ checkSerializableObject(options, value);
1310
1369
  return;
1311
1370
  }
1312
- else if (typeof value === 'symbol') {
1313
- throw new UnexpectedError(`${name} is symbol`);
1314
- }
1315
- else if (typeof value === 'function') {
1316
- throw new UnexpectedError(`${name} is function`);
1317
- }
1318
- else if (typeof value === 'object' && Array.isArray(value)) {
1319
- for (let i = 0; i < value.length; i++) {
1320
- checkSerializableAsJson({ name: `${name}[${i}]`, value: value[i], message });
1321
- }
1371
+ throwUnknownTypeError(options);
1372
+ }
1373
+ /**
1374
+ * Checks the primitive values that are directly JSON serializable.
1375
+ *
1376
+ * @private function of `checkSerializableAsJson`
1377
+ */
1378
+ function isSerializablePrimitive(value) {
1379
+ return (value === null ||
1380
+ typeof value === 'boolean' ||
1381
+ (typeof value === 'number' && !isNaN(value)) ||
1382
+ typeof value === 'string');
1383
+ }
1384
+ /**
1385
+ * Recursively checks JSON array items.
1386
+ *
1387
+ * @private function of `checkSerializableAsJson`
1388
+ */
1389
+ function checkSerializableArray(context, arrayValue) {
1390
+ for (let index = 0; index < arrayValue.length; index++) {
1391
+ checkSerializableAsJson({
1392
+ ...context,
1393
+ name: `${context.name}[${index}]`,
1394
+ value: arrayValue[index],
1395
+ });
1322
1396
  }
1323
- else if (typeof value === 'object') {
1324
- if (value instanceof Date) {
1325
- throw new UnexpectedError(spacetrim.spaceTrim((block) => `
1326
- \`${name}\` is Date
1397
+ }
1398
+ /**
1399
+ * Checks object-like values and dispatches special unsupported built-ins.
1400
+ *
1401
+ * @private function of `checkSerializableAsJson`
1402
+ */
1403
+ function checkSerializableObject(context, objectValue) {
1404
+ checkUnsupportedObjectType(context, objectValue);
1405
+ checkSerializableObjectEntries(context, objectValue);
1406
+ assertJsonStringificationSucceeds(context, objectValue);
1407
+ }
1408
+ /**
1409
+ * Rejects built-in objects that must be converted before JSON serialization.
1410
+ *
1411
+ * @private function of `checkSerializableAsJson`
1412
+ */
1413
+ function checkUnsupportedObjectType(context, objectValue) {
1414
+ if (objectValue instanceof Date) {
1415
+ throw new UnexpectedError(spacetrim.spaceTrim((block) => `
1416
+ \`${context.name}\` is Date
1327
1417
 
1328
- Use \`string_date_iso8601\` instead
1418
+ Use \`string_date_iso8601\` instead
1329
1419
 
1330
- Additional message for \`${name}\`:
1331
- ${block(message || '(nothing)')}
1332
- `));
1333
- }
1334
- else if (value instanceof Map) {
1335
- throw new UnexpectedError(`${name} is Map`);
1336
- }
1337
- else if (value instanceof Set) {
1338
- throw new UnexpectedError(`${name} is Set`);
1339
- }
1340
- else if (value instanceof RegExp) {
1341
- throw new UnexpectedError(`${name} is RegExp`);
1342
- }
1343
- else if (value instanceof Error) {
1344
- throw new UnexpectedError(spacetrim.spaceTrim((block) => `
1345
- \`${name}\` is unserialized Error
1420
+ Additional message for \`${context.name}\`:
1421
+ ${block(context.message || '(nothing)')}
1422
+ `));
1423
+ }
1424
+ if (objectValue instanceof Map) {
1425
+ throw new UnexpectedError(`${context.name} is Map`);
1426
+ }
1427
+ if (objectValue instanceof Set) {
1428
+ throw new UnexpectedError(`${context.name} is Set`);
1429
+ }
1430
+ if (objectValue instanceof RegExp) {
1431
+ throw new UnexpectedError(`${context.name} is RegExp`);
1432
+ }
1433
+ if (objectValue instanceof Error) {
1434
+ throw new UnexpectedError(spacetrim.spaceTrim((block) => `
1435
+ \`${context.name}\` is unserialized Error
1346
1436
 
1347
- Use function \`serializeError\`
1437
+ Use function \`serializeError\`
1348
1438
 
1349
- Additional message for \`${name}\`:
1350
- ${block(message || '(nothing)')}
1439
+ Additional message for \`${context.name}\`:
1440
+ ${block(context.message || '(nothing)')}
1351
1441
 
1352
- `));
1442
+ `));
1443
+ }
1444
+ }
1445
+ /**
1446
+ * Recursively checks object properties while preserving omitted `undefined` keys.
1447
+ *
1448
+ * @private function of `checkSerializableAsJson`
1449
+ */
1450
+ function checkSerializableObjectEntries(context, objectValue) {
1451
+ for (const [subName, subValue] of Object.entries(objectValue)) {
1452
+ if (subValue === undefined) {
1453
+ // Note: undefined in object is serializable - it is just omitted
1454
+ continue;
1353
1455
  }
1354
- else {
1355
- for (const [subName, subValue] of Object.entries(value)) {
1356
- if (subValue === undefined) {
1357
- // Note: undefined in object is serializable - it is just omitted
1358
- continue;
1359
- }
1360
- checkSerializableAsJson({ name: `${name}.${subName}`, value: subValue, message });
1361
- }
1362
- try {
1363
- JSON.stringify(value); // <- TODO: [0]
1364
- }
1365
- catch (error) {
1366
- assertsError(error);
1367
- throw new UnexpectedError(spacetrim.spaceTrim((block) => `
1368
- \`${name}\` is not serializable
1456
+ checkSerializableAsJson({
1457
+ ...context,
1458
+ name: `${context.name}.${subName}`,
1459
+ value: subValue,
1460
+ });
1461
+ }
1462
+ }
1463
+ /**
1464
+ * Uses `JSON.stringify` as the final guard for cases like circular references.
1465
+ *
1466
+ * @private function of `checkSerializableAsJson`
1467
+ */
1468
+ function assertJsonStringificationSucceeds(context, objectValue) {
1469
+ try {
1470
+ JSON.stringify(objectValue); // <- TODO: [0]
1471
+ }
1472
+ catch (error) {
1473
+ assertsError(error);
1474
+ throw new UnexpectedError(spacetrim.spaceTrim((block) => `
1475
+ \`${context.name}\` is not serializable
1369
1476
 
1370
- ${block(error.stack || error.message)}
1477
+ ${block(error.stack || error.message)}
1371
1478
 
1372
- Additional message for \`${name}\`:
1373
- ${block(message || '(nothing)')}
1374
- `));
1479
+ Additional message for \`${context.name}\`:
1480
+ ${block(context.message || '(nothing)')}
1481
+ `));
1482
+ }
1483
+ /*
1484
+ TODO: [0] Is there some more elegant way to check circular references?
1485
+ const seen = new Set();
1486
+ const stack = [{ value }];
1487
+ while (stack.length > 0) {
1488
+ const { value } = stack.pop()!;
1489
+ if (typeof value === 'object' && value !== null) {
1490
+ if (seen.has(value)) {
1491
+ throw new UnexpectedError(`${name} has circular reference`);
1375
1492
  }
1376
- /*
1377
- TODO: [0] Is there some more elegant way to check circular references?
1378
- const seen = new Set();
1379
- const stack = [{ value }];
1380
- while (stack.length > 0) {
1381
- const { value } = stack.pop()!;
1382
- if (typeof value === 'object' && value !== null) {
1383
- if (seen.has(value)) {
1384
- throw new UnexpectedError(`${name} has circular reference`);
1385
- }
1386
- seen.add(value);
1387
- if (Array.isArray(value)) {
1388
- stack.push(...value.map((value) => ({ value })));
1389
- } else {
1390
- stack.push(...Object.values(value).map((value) => ({ value })));
1391
- }
1392
- }
1493
+ seen.add(value);
1494
+ if (Array.isArray(value)) {
1495
+ stack.push(...value.map((value) => ({ value })));
1496
+ } else {
1497
+ stack.push(...Object.values(value).map((value) => ({ value })));
1393
1498
  }
1394
- */
1395
- return;
1396
1499
  }
1397
1500
  }
1398
- else {
1399
- throw new UnexpectedError(spacetrim.spaceTrim((block) => `
1400
- \`${name}\` is unknown type
1501
+ */
1502
+ }
1503
+ /**
1504
+ * Throws the fallback error for unsupported value types like `bigint` and `NaN`.
1505
+ *
1506
+ * @private function of `checkSerializableAsJson`
1507
+ */
1508
+ function throwUnknownTypeError(context) {
1509
+ throw new UnexpectedError(spacetrim.spaceTrim((block) => `
1510
+ \`${context.name}\` is unknown type
1401
1511
 
1402
- Additional message for \`${name}\`:
1403
- ${block(message || '(nothing)')}
1404
- `));
1405
- }
1512
+ Additional message for \`${context.name}\`:
1513
+ ${block(context.message || '(nothing)')}
1514
+ `));
1406
1515
  }
1407
- // TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
1408
- // TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
1409
- // Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
1410
1516
 
1411
1517
  /**
1412
1518
  * Creates a deep clone of the given object