@promptbook/remote-client 0.112.0-73 → 0.112.0-79

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 +2 -2
  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/README.md CHANGED
@@ -455,28 +455,28 @@ npx ts-node ./src/cli/test/ptbk.ts coder verify
455
455
 
456
456
  #### Using `ptbk coder` in an external project
457
457
 
458
- If you want to use the workflow in another repository, install the package and invoke the `ptbk` binary. After local installation, `npx ptbk ...` is the most portable form; plain `ptbk ...` also works when your environment exposes the local binary on `PATH`.
458
+ If you want to use the workflow in another repository, install the package and invoke the `ptbk` binary directly.
459
459
 
460
460
  ```bash
461
461
  npm install ptbk
462
462
 
463
463
  ptbk coder init
464
464
 
465
- npx ptbk coder generate-boilerplates
465
+ ptbk coder generate-boilerplates
466
466
 
467
- npx ptbk coder generate-boilerplates --template prompts/templates/common.md
467
+ ptbk coder generate-boilerplates --template prompts/templates/common.md
468
468
 
469
- npx ptbk coder run --agent github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --test npm run test
469
+ ptbk coder run --agent github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --test npm run test
470
470
 
471
- npx ptbk coder run --agent github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --auto-push
471
+ ptbk coder run --agent github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --auto-push
472
472
 
473
- npx ptbk coder run --agent github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --test npm run test --ignore-git-changes --no-wait
473
+ ptbk coder run --agent github-copilot --model gpt-5.4 --thinking-level xhigh --context AGENTS.md --test npm run test --ignore-git-changes --no-wait
474
474
 
475
- npx ptbk coder find-refactor-candidates
475
+ ptbk coder find-refactor-candidates
476
476
 
477
- npx ptbk coder find-refactor-candidates --level xhigh
477
+ ptbk coder find-refactor-candidates --level xhigh
478
478
 
479
- npx ptbk coder verify
479
+ ptbk coder verify
480
480
  ```
481
481
 
482
482
  `ptbk coder init` also bootstraps a starter `AGENTS.md`, adds `package.json` scripts for the four main coder commands, adds the shared `/.promptbook` temp ignore to `.gitignore`, and configures `.vscode/settings.json` so pasted images from `prompts/*.md` land in `prompts/screenshots/`.
package/esm/index.es.js CHANGED
@@ -20,7 +20,7 @@ const BOOK_LANGUAGE_VERSION = '2.0.0';
20
20
  * @generated
21
21
  * @see https://github.com/webgptorg/promptbook
22
22
  */
23
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-73';
23
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-79';
24
24
  /**
25
25
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
26
26
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -562,6 +562,111 @@ function checkChannelValue(channelName, value) {
562
562
  }
563
563
  }
564
564
 
565
+ /**
566
+ * Shared immutable channel storage and serialization helpers for `Color`.
567
+ *
568
+ * @private base class of Color
569
+ */
570
+ class ColorValue {
571
+ constructor(red, green, blue, alpha = 255) {
572
+ this.red = red;
573
+ this.green = green;
574
+ this.blue = blue;
575
+ this.alpha = alpha;
576
+ checkChannelValue('Red', red);
577
+ checkChannelValue('Green', green);
578
+ checkChannelValue('Blue', blue);
579
+ checkChannelValue('Alpha', alpha);
580
+ }
581
+ /**
582
+ * Shortcut for `red` property
583
+ * Number from 0 to 255
584
+ * @alias red
585
+ */
586
+ get r() {
587
+ return this.red;
588
+ }
589
+ /**
590
+ * Shortcut for `green` property
591
+ * Number from 0 to 255
592
+ * @alias green
593
+ */
594
+ get g() {
595
+ return this.green;
596
+ }
597
+ /**
598
+ * Shortcut for `blue` property
599
+ * Number from 0 to 255
600
+ * @alias blue
601
+ */
602
+ get b() {
603
+ return this.blue;
604
+ }
605
+ /**
606
+ * Shortcut for `alpha` property
607
+ * Number from 0 (transparent) to 255 (opaque)
608
+ * @alias alpha
609
+ */
610
+ get a() {
611
+ return this.alpha;
612
+ }
613
+ /**
614
+ * Shortcut for `alpha` property
615
+ * Number from 0 (transparent) to 255 (opaque)
616
+ * @alias alpha
617
+ */
618
+ get opacity() {
619
+ return this.alpha;
620
+ }
621
+ /**
622
+ * Shortcut for 1-`alpha` property
623
+ */
624
+ get transparency() {
625
+ return 255 - this.alpha;
626
+ }
627
+ clone() {
628
+ return take(this.createColor(this.red, this.green, this.blue, this.alpha));
629
+ }
630
+ toString() {
631
+ return this.toHex();
632
+ }
633
+ toHex() {
634
+ if (this.alpha === 255) {
635
+ return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
636
+ .toString(16)
637
+ .padStart(2, '0')}`;
638
+ }
639
+ else {
640
+ return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
641
+ .toString(16)
642
+ .padStart(2, '0')}${this.alpha.toString(16).padStart(2, '0')}`;
643
+ }
644
+ }
645
+ toRgb() {
646
+ if (this.alpha === 255) {
647
+ return `rgb(${this.red}, ${this.green}, ${this.blue})`;
648
+ }
649
+ else {
650
+ return `rgba(${this.red}, ${this.green}, ${this.blue}, ${Math.round((this.alpha / 255) * 100)}%)`;
651
+ }
652
+ }
653
+ toHsl() {
654
+ throw new Error(`Getting HSL is not implemented`);
655
+ }
656
+ }
657
+
658
+ /**
659
+ * Checks if the given value is a valid hex color string
660
+ *
661
+ * @param value - value to check
662
+ * @returns true if the value is a valid hex color string (e.g., `#009edd`, `#fff`, etc.)
663
+ *
664
+ * @private function of Color
665
+ */
666
+ function isHexColorString(value) {
667
+ 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));
668
+ }
669
+
565
670
  /**
566
671
  * Constant for short hex lengths.
567
672
  */
@@ -773,16 +878,53 @@ function parseAlphaValue(value) {
773
878
 
774
879
  /**
775
880
  * Pattern matching hsl regex.
881
+ *
882
+ * @private function of Color
776
883
  */
777
884
  const HSL_REGEX_PATTERN = /^hsl\(\s*([0-9.]+)\s*,\s*([0-9.]+)%\s*,\s*([0-9.]+)%\s*\)$/;
778
885
  /**
779
886
  * Pattern matching RGB regex.
887
+ *
888
+ * @private function of Color
780
889
  */
781
890
  const RGB_REGEX_PATTERN = /^rgb\(\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*\)$/;
782
891
  /**
783
892
  * Pattern matching rgba regex.
893
+ *
894
+ * @private function of Color
784
895
  */
785
896
  const RGBA_REGEX_PATTERN = /^rgba\(\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*\)$/;
897
+ /**
898
+ * Parses a supported color string into RGBA channels.
899
+ *
900
+ * @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`,...
901
+ * @returns RGBA channel values.
902
+ *
903
+ * @private function of Color
904
+ */
905
+ function parseColorString(color) {
906
+ const trimmed = color.trim();
907
+ const cssColor = CSS_COLORS[trimmed];
908
+ if (cssColor) {
909
+ return parseColorString(cssColor);
910
+ }
911
+ else if (isHexColorString(trimmed)) {
912
+ return parseHexColor(trimmed);
913
+ }
914
+ if (HSL_REGEX_PATTERN.test(trimmed)) {
915
+ return parseHslColor(trimmed);
916
+ }
917
+ else if (RGB_REGEX_PATTERN.test(trimmed)) {
918
+ return parseRgbColor(trimmed);
919
+ }
920
+ else if (RGBA_REGEX_PATTERN.test(trimmed)) {
921
+ return parseRgbaColor(trimmed);
922
+ }
923
+ else {
924
+ throw new Error(`Can not create a new Color instance from string "${trimmed}".`);
925
+ }
926
+ }
927
+
786
928
  /**
787
929
  * Color object represents an RGB color with alpha channel
788
930
  *
@@ -790,7 +932,7 @@ const RGBA_REGEX_PATTERN = /^rgba\(\s*([0-9.%-]+)\s*,\s*([0-9.%-]+)\s*,\s*([0-9.
790
932
  *
791
933
  * @public exported from `@promptbook/color`
792
934
  */
793
- class Color {
935
+ class Color extends ColorValue {
794
936
  /**
795
937
  * Creates a new Color instance from miscellaneous formats
796
938
  * - It can receive Color instance and just return the same instance
@@ -863,25 +1005,7 @@ class Color {
863
1005
  * @returns Color object
864
1006
  */
865
1007
  static fromString(color) {
866
- const trimmed = color.trim();
867
- if (CSS_COLORS[trimmed]) {
868
- return Color.fromString(CSS_COLORS[trimmed]);
869
- }
870
- else if (Color.isHexColorString(trimmed)) {
871
- return Color.fromHex(trimmed);
872
- }
873
- if (HSL_REGEX_PATTERN.test(trimmed)) {
874
- return Color.fromHsl(trimmed);
875
- }
876
- else if (RGB_REGEX_PATTERN.test(trimmed)) {
877
- return Color.fromRgbString(trimmed);
878
- }
879
- else if (RGBA_REGEX_PATTERN.test(trimmed)) {
880
- return Color.fromRgbaString(trimmed);
881
- }
882
- else {
883
- throw new Error(`Can not create a new Color instance from string "${trimmed}".`);
884
- }
1008
+ return Color.fromColorChannels(parseColorString(color));
885
1009
  }
886
1010
  /**
887
1011
  * Gets common color
@@ -911,8 +1035,7 @@ class Color {
911
1035
  * @returns Color object
912
1036
  */
913
1037
  static fromHex(hex) {
914
- const { red, green, blue, alpha } = parseHexColor(hex);
915
- return take(new Color(red, green, blue, alpha));
1038
+ return Color.fromColorChannels(parseHexColor(hex));
916
1039
  }
917
1040
  /**
918
1041
  * Creates a new Color instance from color in hsl format
@@ -921,8 +1044,7 @@ class Color {
921
1044
  * @returns Color object
922
1045
  */
923
1046
  static fromHsl(hsl) {
924
- const { red, green, blue, alpha } = parseHslColor(hsl);
925
- return take(new Color(red, green, blue, alpha));
1047
+ return Color.fromColorChannels(parseHslColor(hsl));
926
1048
  }
927
1049
  /**
928
1050
  * Creates a new Color instance from color in rgb format
@@ -931,8 +1053,7 @@ class Color {
931
1053
  * @returns Color object
932
1054
  */
933
1055
  static fromRgbString(rgb) {
934
- const { red, green, blue, alpha } = parseRgbColor(rgb);
935
- return take(new Color(red, green, blue, alpha));
1056
+ return Color.fromColorChannels(parseRgbColor(rgb));
936
1057
  }
937
1058
  /**
938
1059
  * Creates a new Color instance from color in rbga format
@@ -941,8 +1062,7 @@ class Color {
941
1062
  * @returns Color object
942
1063
  */
943
1064
  static fromRgbaString(rgba) {
944
- const { red, green, blue, alpha } = parseRgbaColor(rgba);
945
- return take(new Color(red, green, blue, alpha));
1065
+ return Color.fromColorChannels(parseRgbaColor(rgba));
946
1066
  }
947
1067
  /**
948
1068
  * Creates a new Color for color channels values
@@ -954,7 +1074,7 @@ class Color {
954
1074
  * @returns Color object
955
1075
  */
956
1076
  static fromValues(red, green, blue, alpha = 255) {
957
- return take(new Color(red, green, blue, alpha));
1077
+ return Color.fromColorChannels({ red, green, blue, alpha });
958
1078
  }
959
1079
  /**
960
1080
  * Checks if the given value is a valid Color object.
@@ -987,8 +1107,7 @@ class Color {
987
1107
  * @returns true if the value is a valid hex color string (e.g., `#009edd`, `#fff`, etc.)
988
1108
  */
989
1109
  static isHexColorString(value) {
990
- return (typeof value === 'string' &&
991
- /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value));
1110
+ return isHexColorString(value);
992
1111
  }
993
1112
  /**
994
1113
  * Creates new Color object
@@ -1001,89 +1120,13 @@ class Color {
1001
1120
  * @param alpha number from 0 (transparent) to 255 (opaque)
1002
1121
  */
1003
1122
  constructor(red, green, blue, alpha = 255) {
1004
- this.red = red;
1005
- this.green = green;
1006
- this.blue = blue;
1007
- this.alpha = alpha;
1008
- checkChannelValue('Red', red);
1009
- checkChannelValue('Green', green);
1010
- checkChannelValue('Blue', blue);
1011
- checkChannelValue('Alpha', alpha);
1012
- }
1013
- /**
1014
- * Shortcut for `red` property
1015
- * Number from 0 to 255
1016
- * @alias red
1017
- */
1018
- get r() {
1019
- return this.red;
1020
- }
1021
- /**
1022
- * Shortcut for `green` property
1023
- * Number from 0 to 255
1024
- * @alias green
1025
- */
1026
- get g() {
1027
- return this.green;
1028
- }
1029
- /**
1030
- * Shortcut for `blue` property
1031
- * Number from 0 to 255
1032
- * @alias blue
1033
- */
1034
- get b() {
1035
- return this.blue;
1036
- }
1037
- /**
1038
- * Shortcut for `alpha` property
1039
- * Number from 0 (transparent) to 255 (opaque)
1040
- * @alias alpha
1041
- */
1042
- get a() {
1043
- return this.alpha;
1123
+ super(red, green, blue, alpha);
1044
1124
  }
1045
- /**
1046
- * Shortcut for `alpha` property
1047
- * Number from 0 (transparent) to 255 (opaque)
1048
- * @alias alpha
1049
- */
1050
- get opacity() {
1051
- return this.alpha;
1125
+ createColor(red, green, blue, alpha) {
1126
+ return new Color(red, green, blue, alpha);
1052
1127
  }
1053
- /**
1054
- * Shortcut for 1-`alpha` property
1055
- */
1056
- get transparency() {
1057
- return 255 - this.alpha;
1058
- }
1059
- clone() {
1060
- return take(new Color(this.red, this.green, this.blue, this.alpha));
1061
- }
1062
- toString() {
1063
- return this.toHex();
1064
- }
1065
- toHex() {
1066
- if (this.alpha === 255) {
1067
- return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
1068
- .toString(16)
1069
- .padStart(2, '0')}`;
1070
- }
1071
- else {
1072
- return `#${this.red.toString(16).padStart(2, '0')}${this.green.toString(16).padStart(2, '0')}${this.blue
1073
- .toString(16)
1074
- .padStart(2, '0')}${this.alpha.toString(16).padStart(2, '0')}`;
1075
- }
1076
- }
1077
- toRgb() {
1078
- if (this.alpha === 255) {
1079
- return `rgb(${this.red}, ${this.green}, ${this.blue})`;
1080
- }
1081
- else {
1082
- return `rgba(${this.red}, ${this.green}, ${this.blue}, ${Math.round((this.alpha / 255) * 100)}%)`;
1083
- }
1084
- }
1085
- toHsl() {
1086
- throw new Error(`Getting HSL is not implemented`);
1128
+ static fromColorChannels({ red, green, blue, alpha }) {
1129
+ return take(new Color(red, green, blue, alpha));
1087
1130
  }
1088
1131
  }
1089
1132
 
@@ -2616,120 +2659,183 @@ function $deepFreeze(objectValue) {
2616
2659
  * @public exported from `@promptbook/utils`
2617
2660
  */
2618
2661
  function checkSerializableAsJson(options) {
2619
- const { value, name, message } = options;
2662
+ checkSerializableValue(options);
2663
+ }
2664
+ // TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
2665
+ // TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
2666
+ // Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
2667
+ /**
2668
+ * Checks one value and dispatches to the appropriate specialized validator.
2669
+ *
2670
+ * @private function of `checkSerializableAsJson`
2671
+ */
2672
+ function checkSerializableValue(options) {
2673
+ const { value } = options;
2674
+ if (isSerializablePrimitive(value)) {
2675
+ return;
2676
+ }
2620
2677
  if (value === undefined) {
2621
- throw new UnexpectedError(`${name} is undefined`);
2678
+ throw new UnexpectedError(`${options.name} is undefined`);
2622
2679
  }
2623
- else if (value === null) {
2624
- return;
2680
+ if (typeof value === 'symbol') {
2681
+ throw new UnexpectedError(`${options.name} is symbol`);
2625
2682
  }
2626
- else if (typeof value === 'boolean') {
2627
- return;
2683
+ if (typeof value === 'function') {
2684
+ throw new UnexpectedError(`${options.name} is function`);
2628
2685
  }
2629
- else if (typeof value === 'number' && !isNaN(value)) {
2686
+ if (Array.isArray(value)) {
2687
+ checkSerializableArray(options, value);
2630
2688
  return;
2631
2689
  }
2632
- else if (typeof value === 'string') {
2690
+ if (value !== null && typeof value === 'object') {
2691
+ checkSerializableObject(options, value);
2633
2692
  return;
2634
2693
  }
2635
- else if (typeof value === 'symbol') {
2636
- throw new UnexpectedError(`${name} is symbol`);
2637
- }
2638
- else if (typeof value === 'function') {
2639
- throw new UnexpectedError(`${name} is function`);
2640
- }
2641
- else if (typeof value === 'object' && Array.isArray(value)) {
2642
- for (let i = 0; i < value.length; i++) {
2643
- checkSerializableAsJson({ name: `${name}[${i}]`, value: value[i], message });
2644
- }
2694
+ throwUnknownTypeError(options);
2695
+ }
2696
+ /**
2697
+ * Checks the primitive values that are directly JSON serializable.
2698
+ *
2699
+ * @private function of `checkSerializableAsJson`
2700
+ */
2701
+ function isSerializablePrimitive(value) {
2702
+ return (value === null ||
2703
+ typeof value === 'boolean' ||
2704
+ (typeof value === 'number' && !isNaN(value)) ||
2705
+ typeof value === 'string');
2706
+ }
2707
+ /**
2708
+ * Recursively checks JSON array items.
2709
+ *
2710
+ * @private function of `checkSerializableAsJson`
2711
+ */
2712
+ function checkSerializableArray(context, arrayValue) {
2713
+ for (let index = 0; index < arrayValue.length; index++) {
2714
+ checkSerializableAsJson({
2715
+ ...context,
2716
+ name: `${context.name}[${index}]`,
2717
+ value: arrayValue[index],
2718
+ });
2645
2719
  }
2646
- else if (typeof value === 'object') {
2647
- if (value instanceof Date) {
2648
- throw new UnexpectedError(spaceTrim$1((block) => `
2649
- \`${name}\` is Date
2720
+ }
2721
+ /**
2722
+ * Checks object-like values and dispatches special unsupported built-ins.
2723
+ *
2724
+ * @private function of `checkSerializableAsJson`
2725
+ */
2726
+ function checkSerializableObject(context, objectValue) {
2727
+ checkUnsupportedObjectType(context, objectValue);
2728
+ checkSerializableObjectEntries(context, objectValue);
2729
+ assertJsonStringificationSucceeds(context, objectValue);
2730
+ }
2731
+ /**
2732
+ * Rejects built-in objects that must be converted before JSON serialization.
2733
+ *
2734
+ * @private function of `checkSerializableAsJson`
2735
+ */
2736
+ function checkUnsupportedObjectType(context, objectValue) {
2737
+ if (objectValue instanceof Date) {
2738
+ throw new UnexpectedError(spaceTrim$1((block) => `
2739
+ \`${context.name}\` is Date
2650
2740
 
2651
- Use \`string_date_iso8601\` instead
2741
+ Use \`string_date_iso8601\` instead
2652
2742
 
2653
- Additional message for \`${name}\`:
2654
- ${block(message || '(nothing)')}
2655
- `));
2656
- }
2657
- else if (value instanceof Map) {
2658
- throw new UnexpectedError(`${name} is Map`);
2659
- }
2660
- else if (value instanceof Set) {
2661
- throw new UnexpectedError(`${name} is Set`);
2662
- }
2663
- else if (value instanceof RegExp) {
2664
- throw new UnexpectedError(`${name} is RegExp`);
2665
- }
2666
- else if (value instanceof Error) {
2667
- throw new UnexpectedError(spaceTrim$1((block) => `
2668
- \`${name}\` is unserialized Error
2743
+ Additional message for \`${context.name}\`:
2744
+ ${block(context.message || '(nothing)')}
2745
+ `));
2746
+ }
2747
+ if (objectValue instanceof Map) {
2748
+ throw new UnexpectedError(`${context.name} is Map`);
2749
+ }
2750
+ if (objectValue instanceof Set) {
2751
+ throw new UnexpectedError(`${context.name} is Set`);
2752
+ }
2753
+ if (objectValue instanceof RegExp) {
2754
+ throw new UnexpectedError(`${context.name} is RegExp`);
2755
+ }
2756
+ if (objectValue instanceof Error) {
2757
+ throw new UnexpectedError(spaceTrim$1((block) => `
2758
+ \`${context.name}\` is unserialized Error
2669
2759
 
2670
- Use function \`serializeError\`
2760
+ Use function \`serializeError\`
2671
2761
 
2672
- Additional message for \`${name}\`:
2673
- ${block(message || '(nothing)')}
2762
+ Additional message for \`${context.name}\`:
2763
+ ${block(context.message || '(nothing)')}
2674
2764
 
2675
- `));
2765
+ `));
2766
+ }
2767
+ }
2768
+ /**
2769
+ * Recursively checks object properties while preserving omitted `undefined` keys.
2770
+ *
2771
+ * @private function of `checkSerializableAsJson`
2772
+ */
2773
+ function checkSerializableObjectEntries(context, objectValue) {
2774
+ for (const [subName, subValue] of Object.entries(objectValue)) {
2775
+ if (subValue === undefined) {
2776
+ // Note: undefined in object is serializable - it is just omitted
2777
+ continue;
2676
2778
  }
2677
- else {
2678
- for (const [subName, subValue] of Object.entries(value)) {
2679
- if (subValue === undefined) {
2680
- // Note: undefined in object is serializable - it is just omitted
2681
- continue;
2682
- }
2683
- checkSerializableAsJson({ name: `${name}.${subName}`, value: subValue, message });
2684
- }
2685
- try {
2686
- JSON.stringify(value); // <- TODO: [0]
2687
- }
2688
- catch (error) {
2689
- assertsError(error);
2690
- throw new UnexpectedError(spaceTrim$1((block) => `
2691
- \`${name}\` is not serializable
2779
+ checkSerializableAsJson({
2780
+ ...context,
2781
+ name: `${context.name}.${subName}`,
2782
+ value: subValue,
2783
+ });
2784
+ }
2785
+ }
2786
+ /**
2787
+ * Uses `JSON.stringify` as the final guard for cases like circular references.
2788
+ *
2789
+ * @private function of `checkSerializableAsJson`
2790
+ */
2791
+ function assertJsonStringificationSucceeds(context, objectValue) {
2792
+ try {
2793
+ JSON.stringify(objectValue); // <- TODO: [0]
2794
+ }
2795
+ catch (error) {
2796
+ assertsError(error);
2797
+ throw new UnexpectedError(spaceTrim$1((block) => `
2798
+ \`${context.name}\` is not serializable
2692
2799
 
2693
- ${block(error.stack || error.message)}
2800
+ ${block(error.stack || error.message)}
2694
2801
 
2695
- Additional message for \`${name}\`:
2696
- ${block(message || '(nothing)')}
2697
- `));
2802
+ Additional message for \`${context.name}\`:
2803
+ ${block(context.message || '(nothing)')}
2804
+ `));
2805
+ }
2806
+ /*
2807
+ TODO: [0] Is there some more elegant way to check circular references?
2808
+ const seen = new Set();
2809
+ const stack = [{ value }];
2810
+ while (stack.length > 0) {
2811
+ const { value } = stack.pop()!;
2812
+ if (typeof value === 'object' && value !== null) {
2813
+ if (seen.has(value)) {
2814
+ throw new UnexpectedError(`${name} has circular reference`);
2698
2815
  }
2699
- /*
2700
- TODO: [0] Is there some more elegant way to check circular references?
2701
- const seen = new Set();
2702
- const stack = [{ value }];
2703
- while (stack.length > 0) {
2704
- const { value } = stack.pop()!;
2705
- if (typeof value === 'object' && value !== null) {
2706
- if (seen.has(value)) {
2707
- throw new UnexpectedError(`${name} has circular reference`);
2708
- }
2709
- seen.add(value);
2710
- if (Array.isArray(value)) {
2711
- stack.push(...value.map((value) => ({ value })));
2712
- } else {
2713
- stack.push(...Object.values(value).map((value) => ({ value })));
2714
- }
2715
- }
2816
+ seen.add(value);
2817
+ if (Array.isArray(value)) {
2818
+ stack.push(...value.map((value) => ({ value })));
2819
+ } else {
2820
+ stack.push(...Object.values(value).map((value) => ({ value })));
2716
2821
  }
2717
- */
2718
- return;
2719
2822
  }
2720
2823
  }
2721
- else {
2722
- throw new UnexpectedError(spaceTrim$1((block) => `
2723
- \`${name}\` is unknown type
2824
+ */
2825
+ }
2826
+ /**
2827
+ * Throws the fallback error for unsupported value types like `bigint` and `NaN`.
2828
+ *
2829
+ * @private function of `checkSerializableAsJson`
2830
+ */
2831
+ function throwUnknownTypeError(context) {
2832
+ throw new UnexpectedError(spaceTrim$1((block) => `
2833
+ \`${context.name}\` is unknown type
2724
2834
 
2725
- Additional message for \`${name}\`:
2726
- ${block(message || '(nothing)')}
2727
- `));
2728
- }
2835
+ Additional message for \`${context.name}\`:
2836
+ ${block(context.message || '(nothing)')}
2837
+ `));
2729
2838
  }
2730
- // TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
2731
- // TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
2732
- // Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
2733
2839
 
2734
2840
  /**
2735
2841
  * Creates a deep clone of the given object