@promptbook/utils 0.92.0-8 → 0.92.0

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 (207) hide show
  1. package/README.md +4 -8
  2. package/esm/index.es.js +864 -827
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/typings/src/_packages/browser.index.d.ts +2 -0
  5. package/esm/typings/src/_packages/core.index.d.ts +26 -14
  6. package/esm/typings/src/_packages/types.index.d.ts +6 -2
  7. package/esm/typings/src/collection/PipelineCollection.d.ts +0 -2
  8. package/esm/typings/src/collection/SimplePipelineCollection.d.ts +1 -1
  9. package/esm/typings/src/commands/FOREACH/ForeachJson.d.ts +6 -6
  10. package/esm/typings/src/commands/FOREACH/foreachCommandParser.d.ts +0 -2
  11. package/esm/typings/src/commands/FORMFACTOR/formfactorCommandParser.d.ts +1 -1
  12. package/esm/typings/src/commands/_BOILERPLATE/boilerplateCommandParser.d.ts +1 -1
  13. package/esm/typings/src/commands/_common/types/CommandParser.d.ts +36 -28
  14. package/esm/typings/src/config.d.ts +41 -11
  15. package/esm/typings/src/constants.d.ts +43 -2
  16. package/esm/typings/src/conversion/parsePipeline.d.ts +2 -2
  17. package/esm/typings/src/errors/0-BoilerplateError.d.ts +2 -2
  18. package/esm/typings/src/errors/CollectionError.d.ts +1 -1
  19. package/esm/typings/src/executables/$provideExecutablesForNode.d.ts +1 -1
  20. package/esm/typings/src/executables/apps/locateLibreoffice.d.ts +2 -1
  21. package/esm/typings/src/executables/apps/locatePandoc.d.ts +2 -1
  22. package/esm/typings/src/executables/locateApp.d.ts +2 -2
  23. package/esm/typings/src/executables/platforms/locateAppOnLinux.d.ts +2 -1
  24. package/esm/typings/src/executables/platforms/locateAppOnMacOs.d.ts +2 -1
  25. package/esm/typings/src/executables/platforms/locateAppOnWindows.d.ts +2 -1
  26. package/esm/typings/src/execution/AbstractTaskResult.d.ts +1 -1
  27. package/esm/typings/src/execution/CommonToolsOptions.d.ts +5 -1
  28. package/esm/typings/src/execution/ExecutionTask.d.ts +19 -1
  29. package/esm/typings/src/execution/LlmExecutionToolsConstructor.d.ts +2 -1
  30. package/esm/typings/src/execution/PipelineExecutorResult.d.ts +4 -2
  31. package/esm/typings/src/execution/PromptbookFetch.d.ts +1 -1
  32. package/esm/typings/src/execution/ScriptExecutionTools.d.ts +1 -1
  33. package/esm/typings/src/execution/createPipelineExecutor/$OngoingTaskResult.d.ts +12 -9
  34. package/esm/typings/src/execution/createPipelineExecutor/10-executePipeline.d.ts +13 -10
  35. package/esm/typings/src/execution/createPipelineExecutor/20-executeTask.d.ts +12 -9
  36. package/esm/typings/src/execution/createPipelineExecutor/30-executeFormatSubvalues.d.ts +15 -3
  37. package/esm/typings/src/execution/createPipelineExecutor/40-executeAttempts.d.ts +21 -15
  38. package/esm/typings/src/execution/createPipelineExecutor/computeCosineSimilarity.d.ts +13 -0
  39. package/esm/typings/src/execution/createPipelineExecutor/filterJustOutputParameters.d.ts +7 -6
  40. package/esm/typings/src/execution/createPipelineExecutor/getContextForTask.d.ts +5 -1
  41. package/esm/typings/src/execution/createPipelineExecutor/getExamplesForTask.d.ts +1 -1
  42. package/esm/typings/src/execution/createPipelineExecutor/getKnowledgeForTask.d.ts +12 -9
  43. package/esm/typings/src/execution/createPipelineExecutor/getReservedParametersForTask.d.ts +18 -5
  44. package/esm/typings/src/execution/createPipelineExecutor/knowledgePiecesToString.d.ts +9 -0
  45. package/esm/typings/src/execution/execution-report/ExecutionReportJson.d.ts +1 -1
  46. package/esm/typings/src/execution/execution-report/ExecutionReportString.d.ts +1 -1
  47. package/esm/typings/src/execution/translation/automatic-translate/automatic-translators/LindatAutomaticTranslator.d.ts +4 -4
  48. package/esm/typings/src/execution/utils/checkExpectations.d.ts +3 -3
  49. package/esm/typings/src/execution/utils/uncertainNumber.d.ts +3 -2
  50. package/esm/typings/src/execution/utils/usageToWorktime.d.ts +1 -1
  51. package/esm/typings/src/formats/_common/{FormatDefinition.d.ts → FormatParser.d.ts} +8 -6
  52. package/esm/typings/src/formats/_common/FormatSubvalueParser.d.ts +66 -0
  53. package/esm/typings/src/formats/csv/CsvFormatParser.d.ts +17 -0
  54. package/esm/typings/src/formats/csv/CsvSettings.d.ts +2 -2
  55. package/esm/typings/src/formats/csv/utils/csvParse.d.ts +12 -0
  56. package/esm/typings/src/formats/csv/utils/isValidCsvString.d.ts +1 -1
  57. package/esm/typings/src/formats/index.d.ts +2 -2
  58. package/esm/typings/src/formats/json/JsonFormatParser.d.ts +19 -0
  59. package/esm/typings/src/formats/json/utils/isValidJsonString.d.ts +1 -1
  60. package/esm/typings/src/formats/json/utils/jsonParse.d.ts +0 -3
  61. package/esm/typings/src/formats/text/{TextFormatDefinition.d.ts → TextFormatParser.d.ts} +7 -7
  62. package/esm/typings/src/formats/xml/XmlFormatParser.d.ts +19 -0
  63. package/esm/typings/src/formats/xml/utils/isValidXmlString.d.ts +1 -1
  64. package/esm/typings/src/formfactors/_boilerplate/BoilerplateFormfactorDefinition.d.ts +3 -2
  65. package/esm/typings/src/formfactors/_common/AbstractFormfactorDefinition.d.ts +16 -7
  66. package/esm/typings/src/formfactors/_common/FormfactorDefinition.d.ts +3 -1
  67. package/esm/typings/src/formfactors/_common/string_formfactor_name.d.ts +2 -1
  68. package/esm/typings/src/formfactors/chatbot/ChatbotFormfactorDefinition.d.ts +2 -2
  69. package/esm/typings/src/formfactors/completion/CompletionFormfactorDefinition.d.ts +29 -0
  70. package/esm/typings/src/formfactors/generator/GeneratorFormfactorDefinition.d.ts +2 -1
  71. package/esm/typings/src/formfactors/generic/GenericFormfactorDefinition.d.ts +2 -2
  72. package/esm/typings/src/formfactors/index.d.ts +33 -8
  73. package/esm/typings/src/formfactors/matcher/MatcherFormfactorDefinition.d.ts +4 -2
  74. package/esm/typings/src/formfactors/sheets/SheetsFormfactorDefinition.d.ts +3 -2
  75. package/esm/typings/src/formfactors/translator/TranslatorFormfactorDefinition.d.ts +3 -2
  76. package/esm/typings/src/high-level-abstractions/index.d.ts +2 -2
  77. package/esm/typings/src/llm-providers/_common/register/$llmToolsMetadataRegister.d.ts +3 -3
  78. package/esm/typings/src/llm-providers/_common/register/$llmToolsRegister.d.ts +3 -3
  79. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +4 -4
  80. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForTestingAndScriptsAndPlayground.d.ts +4 -3
  81. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +18 -5
  82. package/esm/typings/src/llm-providers/_common/register/LlmToolsConfiguration.d.ts +11 -4
  83. package/esm/typings/src/llm-providers/_common/register/LlmToolsMetadata.d.ts +21 -42
  84. package/esm/typings/src/llm-providers/_common/register/LlmToolsOptions.d.ts +9 -2
  85. package/esm/typings/src/llm-providers/_common/register/createLlmToolsFromConfiguration.d.ts +13 -4
  86. package/esm/typings/src/llm-providers/_common/utils/cache/CacheItem.d.ts +10 -5
  87. package/esm/typings/src/llm-providers/_common/utils/cache/CacheLlmToolsOptions.d.ts +11 -3
  88. package/esm/typings/src/llm-providers/_common/utils/cache/cacheLlmTools.d.ts +3 -3
  89. package/esm/typings/src/llm-providers/_common/utils/count-total-usage/limitTotalUsage.d.ts +5 -5
  90. package/esm/typings/src/llm-providers/anthropic-claude/AnthropicClaudeExecutionTools.d.ts +6 -0
  91. package/esm/typings/src/llm-providers/anthropic-claude/anthropic-claude-models.d.ts +1 -1
  92. package/esm/typings/src/llm-providers/azure-openai/AzureOpenAiExecutionTools.d.ts +5 -1
  93. package/esm/typings/src/llm-providers/azure-openai/AzureOpenAiExecutionToolsOptions.d.ts +4 -4
  94. package/esm/typings/src/llm-providers/deepseek/deepseek-models.d.ts +1 -1
  95. package/esm/typings/src/llm-providers/google/google-models.d.ts +1 -1
  96. package/esm/typings/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +1 -1
  97. package/esm/typings/src/llm-providers/openai/OpenAiExecutionTools.d.ts +4 -0
  98. package/esm/typings/src/llm-providers/openai/openai-models.d.ts +1 -1
  99. package/esm/typings/src/llm-providers/openai/register-configuration.d.ts +2 -2
  100. package/esm/typings/src/llm-providers/openai/register-constructor.d.ts +2 -2
  101. package/esm/typings/src/migrations/migratePipeline.d.ts +9 -0
  102. package/esm/typings/src/other/templates/getBookTemplates.d.ts +2 -2
  103. package/esm/typings/src/pipeline/PipelineInterface/PipelineInterface.d.ts +3 -3
  104. package/esm/typings/src/pipeline/PipelineInterface/constants.d.ts +1 -1
  105. package/esm/typings/src/pipeline/PipelineInterface/getPipelineInterface.d.ts +1 -1
  106. package/esm/typings/src/pipeline/PipelineInterface/isPipelineImplementingInterface.d.ts +5 -4
  107. package/esm/typings/src/pipeline/PipelineInterface/isPipelineInterfacesEqual.d.ts +1 -1
  108. package/esm/typings/src/pipeline/PipelineJson/CommonTaskJson.d.ts +10 -7
  109. package/esm/typings/src/pipeline/PipelineJson/ParameterJson.d.ts +1 -1
  110. package/esm/typings/src/pipeline/PipelineJson/PipelineJson.d.ts +3 -2
  111. package/esm/typings/src/pipeline/PipelineString.d.ts +3 -1
  112. package/esm/typings/src/pipeline/book-notation.d.ts +2 -2
  113. package/esm/typings/src/postprocessing/utils/extractBlock.d.ts +1 -1
  114. package/esm/typings/src/postprocessing/utils/extractJsonBlock.d.ts +2 -2
  115. package/esm/typings/src/prepare/prepareTasks.d.ts +8 -5
  116. package/esm/typings/src/remote-server/openapi.d.ts +1 -1
  117. package/esm/typings/src/remote-server/socket-types/listModels/PromptbookServer_ListModels_Response.d.ts +1 -1
  118. package/esm/typings/src/remote-server/startRemoteServer.d.ts +1 -1
  119. package/esm/typings/src/remote-server/types/RemoteServerOptions.d.ts +7 -6
  120. package/esm/typings/src/scrapers/_boilerplate/BoilerplateScraper.d.ts +3 -3
  121. package/esm/typings/src/scrapers/_boilerplate/createBoilerplateScraper.d.ts +1 -1
  122. package/esm/typings/src/scrapers/_boilerplate/register-metadata.d.ts +1 -1
  123. package/esm/typings/src/scrapers/_common/Converter.d.ts +3 -1
  124. package/esm/typings/src/scrapers/_common/Scraper.d.ts +4 -3
  125. package/esm/typings/src/scrapers/_common/ScraperIntermediateSource.d.ts +4 -2
  126. package/esm/typings/src/scrapers/_common/prepareKnowledgePieces.d.ts +2 -2
  127. package/esm/typings/src/scrapers/_common/register/$provideFilesystemForNode.d.ts +2 -1
  128. package/esm/typings/src/scrapers/_common/register/$provideScrapersForBrowser.d.ts +6 -3
  129. package/esm/typings/src/scrapers/_common/register/$provideScrapersForNode.d.ts +3 -5
  130. package/esm/typings/src/scrapers/_common/register/$scrapersMetadataRegister.d.ts +3 -3
  131. package/esm/typings/src/scrapers/_common/register/$scrapersRegister.d.ts +3 -2
  132. package/esm/typings/src/scrapers/_common/register/ScraperAndConverterMetadata.d.ts +8 -5
  133. package/esm/typings/src/scrapers/_common/register/ScraperConstructor.d.ts +2 -1
  134. package/esm/typings/src/scrapers/_common/utils/getScraperIntermediateSource.d.ts +6 -5
  135. package/esm/typings/src/scrapers/_common/utils/makeKnowledgeSourceHandler.d.ts +3 -1
  136. package/esm/typings/src/scrapers/document/createDocumentScraper.d.ts +1 -1
  137. package/esm/typings/src/scrapers/document-legacy/createLegacyDocumentScraper.d.ts +2 -1
  138. package/esm/typings/src/scrapers/markdown/createMarkdownScraper.d.ts +4 -1
  139. package/esm/typings/src/scrapers/markitdown/MarkitdownScraper.d.ts +1 -1
  140. package/esm/typings/src/scrapers/pdf/createPdfScraper.d.ts +2 -1
  141. package/esm/typings/src/scrapers/website/createWebsiteScraper.d.ts +3 -4
  142. package/esm/typings/src/scripting/javascript/JavascriptExecutionTools.d.ts +1 -1
  143. package/esm/typings/src/scripting/javascript/postprocessing-functions.d.ts +5 -1
  144. package/esm/typings/src/scripting/javascript/utils/preserve.d.ts +1 -1
  145. package/esm/typings/src/storage/_common/PromptbookStorage.d.ts +1 -1
  146. package/esm/typings/src/storage/file-cache-storage/FileCacheStorage.d.ts +12 -5
  147. package/esm/typings/src/storage/file-cache-storage/FileCacheStorageOptions.d.ts +4 -2
  148. package/esm/typings/src/storage/file-cache-storage/utils/nameToSubfolderPath.d.ts +2 -1
  149. package/esm/typings/src/storage/local-storage/getIndexedDbStorage.d.ts +11 -0
  150. package/esm/typings/src/storage/local-storage/utils/IndexedDbStorageOptions.d.ts +14 -0
  151. package/esm/typings/src/storage/local-storage/utils/makePromptbookStorageFromIndexedDb.d.ts +8 -0
  152. package/esm/typings/src/storage/local-storage/utils/makePromptbookStorageFromWebStorage.d.ts +2 -1
  153. package/esm/typings/src/types/IntermediateFilesStrategy.d.ts +2 -1
  154. package/esm/typings/src/types/ModelRequirements.d.ts +2 -2
  155. package/esm/typings/src/types/ModelVariant.d.ts +5 -5
  156. package/esm/typings/src/types/typeAliases.d.ts +22 -19
  157. package/esm/typings/src/utils/$Register.d.ts +8 -7
  158. package/esm/typings/src/utils/editable/edit-pipeline-string/addPipelineCommand.d.ts +2 -2
  159. package/esm/typings/src/utils/editable/edit-pipeline-string/deflatePipeline.d.ts +4 -1
  160. package/esm/typings/src/utils/editable/utils/isFlatPipeline.d.ts +2 -1
  161. package/esm/typings/src/utils/editable/utils/stringifyPipelineJson.d.ts +1 -1
  162. package/esm/typings/src/utils/environment/$getGlobalScope.d.ts +2 -1
  163. package/esm/typings/src/utils/expectation-counters/index.d.ts +1 -1
  164. package/esm/typings/src/utils/markdown/extractAllBlocksFromMarkdown.d.ts +2 -2
  165. package/esm/typings/src/utils/markdown/extractAllListItemsFromMarkdown.d.ts +1 -1
  166. package/esm/typings/src/utils/markdown/extractOneBlockFromMarkdown.d.ts +2 -2
  167. package/esm/typings/src/utils/normalization/nameToUriPart.d.ts +4 -4
  168. package/esm/typings/src/utils/normalization/nameToUriParts.d.ts +4 -4
  169. package/esm/typings/src/utils/normalization/normalize-to-kebab-case.d.ts +3 -3
  170. package/esm/typings/src/utils/normalization/normalizeTo_SCREAMING_CASE.d.ts +3 -3
  171. package/esm/typings/src/utils/normalization/normalizeTo_camelCase.d.ts +4 -4
  172. package/esm/typings/src/utils/normalization/normalizeTo_snake_case.d.ts +3 -3
  173. package/esm/typings/src/utils/normalization/removeDiacritics.d.ts +3 -3
  174. package/esm/typings/src/utils/normalization/searchKeywords.d.ts +4 -1
  175. package/esm/typings/src/utils/normalization/titleToName.d.ts +4 -4
  176. package/esm/typings/src/utils/organization/TODO_USE.d.ts +1 -1
  177. package/esm/typings/src/utils/organization/empty_object.d.ts +2 -2
  178. package/esm/typings/src/utils/organization/just.d.ts +1 -1
  179. package/esm/typings/src/utils/organization/just_empty_object.d.ts +4 -4
  180. package/esm/typings/src/utils/organization/keepUnused.d.ts +1 -1
  181. package/esm/typings/src/utils/parameters/mapAvailableToExpectedParameters.d.ts +7 -7
  182. package/esm/typings/src/utils/removeQuotes.d.ts +2 -2
  183. package/esm/typings/src/utils/serialization/clonePipeline.d.ts +4 -3
  184. package/esm/typings/src/utils/serialization/deepClone.d.ts +5 -1
  185. package/esm/typings/src/utils/trimCodeBlock.d.ts +1 -1
  186. package/esm/typings/src/utils/trimEndOfCodeBlock.d.ts +1 -1
  187. package/esm/typings/src/utils/unwrapResult.d.ts +2 -2
  188. package/esm/typings/src/utils/validators/javascriptName/isValidJavascriptName.d.ts +3 -3
  189. package/esm/typings/src/utils/validators/parameterName/validateParameterName.d.ts +5 -4
  190. package/esm/typings/src/utils/validators/semanticVersion/isValidPromptbookVersion.d.ts +1 -1
  191. package/esm/typings/src/utils/validators/semanticVersion/isValidSemanticVersion.d.ts +1 -1
  192. package/esm/typings/src/utils/validators/url/isHostnameOnPrivateNetwork.d.ts +1 -1
  193. package/esm/typings/src/utils/validators/url/isUrlOnPrivateNetwork.d.ts +1 -1
  194. package/esm/typings/src/utils/validators/url/isValidPipelineUrl.d.ts +1 -1
  195. package/esm/typings/src/utils/validators/url/isValidUrl.d.ts +1 -1
  196. package/esm/typings/src/version.d.ts +2 -1
  197. package/esm/typings/src/wizzard/wizzard.d.ts +1 -1
  198. package/package.json +13 -1
  199. package/umd/index.umd.js +871 -834
  200. package/umd/index.umd.js.map +1 -1
  201. package/esm/typings/src/formats/_common/FormatSubvalueDefinition.d.ts +0 -31
  202. package/esm/typings/src/formats/csv/CsvFormatDefinition.d.ts +0 -17
  203. package/esm/typings/src/formats/json/JsonFormatDefinition.d.ts +0 -19
  204. package/esm/typings/src/formats/xml/XmlFormatDefinition.d.ts +0 -19
  205. /package/esm/typings/src/llm-providers/{multiple → _multiple}/MultipleLlmExecutionTools.d.ts +0 -0
  206. /package/esm/typings/src/llm-providers/{multiple → _multiple}/joinLlmExecutionTools.d.ts +0 -0
  207. /package/esm/typings/src/llm-providers/{multiple → _multiple}/playground/playground.d.ts +0 -0
package/esm/index.es.js CHANGED
@@ -16,7 +16,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
16
16
  * @generated
17
17
  * @see https://github.com/webgptorg/promptbook
18
18
  */
19
- const PROMPTBOOK_ENGINE_VERSION = '0.92.0-8';
19
+ const PROMPTBOOK_ENGINE_VERSION = '0.92.0';
20
20
  /**
21
21
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
22
22
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -72,7 +72,7 @@ const VALUE_STRINGS = {
72
72
  const SMALL_NUMBER = 0.001;
73
73
  // <- TODO: [🧜‍♂️]
74
74
  /**
75
- * @@@
75
+ * Default settings for parsing and generating CSV files in Promptbook.
76
76
  *
77
77
  * @public exported from `@promptbook/core`
78
78
  */
@@ -87,6 +87,48 @@ Object.freeze({
87
87
  * TODO: [🧠][🧜‍♂️] Maybe join remoteServerUrl and path into single value
88
88
  */
89
89
 
90
+ /**
91
+ * Orders JSON object by keys
92
+ *
93
+ * @returns The same type of object as the input re-ordered
94
+ * @public exported from `@promptbook/utils`
95
+ */
96
+ function orderJson(options) {
97
+ const { value, order } = options;
98
+ const orderedValue = {
99
+ ...(order === undefined ? {} : Object.fromEntries(order.map((key) => [key, undefined]))),
100
+ ...value,
101
+ };
102
+ return orderedValue;
103
+ }
104
+
105
+ /**
106
+ * Freezes the given object and all its nested objects recursively
107
+ *
108
+ * Note: `$` is used to indicate that this function is not a pure function - it mutates given object
109
+ * Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
110
+ *
111
+ * @returns The same object as the input, but deeply frozen
112
+ * @public exported from `@promptbook/utils`
113
+ */
114
+ function $deepFreeze(objectValue) {
115
+ if (Array.isArray(objectValue)) {
116
+ return Object.freeze(objectValue.map((item) => $deepFreeze(item)));
117
+ }
118
+ const propertyNames = Object.getOwnPropertyNames(objectValue);
119
+ for (const propertyName of propertyNames) {
120
+ const value = objectValue[propertyName];
121
+ if (value && typeof value === 'object') {
122
+ $deepFreeze(value);
123
+ }
124
+ }
125
+ Object.freeze(objectValue);
126
+ return objectValue;
127
+ }
128
+ /**
129
+ * TODO: [🧠] Is there a way how to meaningfully test this utility
130
+ */
131
+
90
132
  /**
91
133
  * Make error report URL for the given error
92
134
  *
@@ -142,7 +184,7 @@ class UnexpectedError extends Error {
142
184
  ${block(message)}
143
185
 
144
186
  Note: This error should not happen.
145
- It's probbably a bug in the pipeline collection
187
+ It's probably a bug in the pipeline collection
146
188
 
147
189
  Please report issue:
148
190
  ${block(getErrorReportUrl(new Error(message)).href)}
@@ -156,158 +198,447 @@ class UnexpectedError extends Error {
156
198
  }
157
199
 
158
200
  /**
159
- * @@@
201
+ * This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
160
202
  *
161
- * @param text @@@
162
- * @param _isFirstLetterCapital @@@
163
- * @returns @@@
164
- * @example 'helloWorld'
165
- * @example 'iLovePromptbook'
166
- * @public exported from `@promptbook/utils`
203
+ * @public exported from `@promptbook/core`
167
204
  */
168
- function normalizeTo_camelCase(text, _isFirstLetterCapital = false) {
169
- let charType;
170
- let lastCharType = null;
171
- let normalizedName = '';
172
- for (const char of text) {
173
- let normalizedChar;
174
- if (/^[a-z]$/.test(char)) {
175
- charType = 'LOWERCASE';
176
- normalizedChar = char;
177
- }
178
- else if (/^[A-Z]$/.test(char)) {
179
- charType = 'UPPERCASE';
180
- normalizedChar = char.toLowerCase();
181
- }
182
- else if (/^[0-9]$/.test(char)) {
183
- charType = 'NUMBER';
184
- normalizedChar = char;
185
- }
186
- else {
187
- charType = 'OTHER';
188
- normalizedChar = '';
189
- }
190
- if (!lastCharType) {
191
- if (_isFirstLetterCapital) {
192
- normalizedChar = normalizedChar.toUpperCase(); //TODO: DRY
193
- }
194
- }
195
- else if (charType !== lastCharType &&
196
- !(charType === 'LOWERCASE' && lastCharType === 'UPPERCASE') &&
197
- !(lastCharType === 'NUMBER') &&
198
- !(charType === 'NUMBER')) {
199
- normalizedChar = normalizedChar.toUpperCase(); //TODO: [🌺] DRY
200
- }
201
- normalizedName += normalizedChar;
202
- lastCharType = charType;
205
+ class WrappedError extends Error {
206
+ constructor(whatWasThrown) {
207
+ const tag = `[🤮]`;
208
+ console.error(tag, whatWasThrown);
209
+ super(spaceTrim$2(`
210
+ Non-Error object was thrown
211
+
212
+ Note: Look for ${tag} in the console for more details
213
+ Please report issue on ${ADMIN_EMAIL}
214
+ `));
215
+ this.name = 'WrappedError';
216
+ Object.setPrototypeOf(this, WrappedError.prototype);
203
217
  }
204
- return normalizedName;
205
218
  }
206
- /**
207
- * TODO: [🌺] Use some intermediate util splitWords
208
- */
209
219
 
210
220
  /**
211
- * Removes emojis from a string and fix whitespaces
221
+ * Helper used in catch blocks to assert that the error is an instance of `Error`
212
222
  *
213
- * @param text with emojis
214
- * @returns text without emojis
215
- * @public exported from `@promptbook/utils`
223
+ * @param whatWasThrown Any object that was thrown
224
+ * @returns Nothing if the error is an instance of `Error`
225
+ * @throws `WrappedError` or `UnexpectedError` if the error is not standard
226
+ *
227
+ * @private within the repository
216
228
  */
217
- function removeEmojis(text) {
218
- // Replace emojis (and also ZWJ sequence) with hyphens
219
- text = text.replace(/(\p{Extended_Pictographic})\p{Modifier_Symbol}/gu, '$1');
220
- text = text.replace(/(\p{Extended_Pictographic})[\u{FE00}-\u{FE0F}]/gu, '$1');
221
- text = text.replace(/(\p{Extended_Pictographic})(\u{200D}\p{Extended_Pictographic})*/gu, '$1');
222
- text = text.replace(/\p{Extended_Pictographic}/gu, '');
223
- return text;
229
+ function assertsError(whatWasThrown) {
230
+ // Case 1: Handle error which was rethrown as `WrappedError`
231
+ if (whatWasThrown instanceof WrappedError) {
232
+ const wrappedError = whatWasThrown;
233
+ throw wrappedError;
234
+ }
235
+ // Case 2: Handle unexpected errors
236
+ if (whatWasThrown instanceof UnexpectedError) {
237
+ const unexpectedError = whatWasThrown;
238
+ throw unexpectedError;
239
+ }
240
+ // Case 3: Handle standard errors - keep them up to consumer
241
+ if (whatWasThrown instanceof Error) {
242
+ return;
243
+ }
244
+ // Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
245
+ throw new WrappedError(whatWasThrown);
224
246
  }
225
247
 
226
248
  /**
227
- * Tests if given string is valid URL.
249
+ * Checks if the value is [🚉] serializable as JSON
250
+ * If not, throws an UnexpectedError with a rich error message and tracking
228
251
  *
229
- * Note: This does not check if the file exists only if the path is valid
252
+ * - Almost all primitives are serializable BUT:
253
+ * - `undefined` is not serializable
254
+ * - `NaN` is not serializable
255
+ * - Objects and arrays are serializable if all their properties are serializable
256
+ * - Functions are not serializable
257
+ * - Circular references are not serializable
258
+ * - `Date` objects are not serializable
259
+ * - `Map` and `Set` objects are not serializable
260
+ * - `RegExp` objects are not serializable
261
+ * - `Error` objects are not serializable
262
+ * - `Symbol` objects are not serializable
263
+ * - And much more...
264
+ *
265
+ * @throws UnexpectedError if the value is not serializable as JSON
230
266
  * @public exported from `@promptbook/utils`
231
267
  */
232
- function isValidFilePath(filename) {
233
- if (typeof filename !== 'string') {
234
- return false;
268
+ function checkSerializableAsJson(options) {
269
+ const { value, name, message } = options;
270
+ if (value === undefined) {
271
+ throw new UnexpectedError(`${name} is undefined`);
235
272
  }
236
- if (filename.split('\n').length > 1) {
237
- return false;
273
+ else if (value === null) {
274
+ return;
238
275
  }
239
- if (filename.split(' ').length >
240
- 5 /* <- TODO: [🧠][🈷] Make some better non-arbitrary way how to distinct filenames from informational texts */) {
241
- return false;
276
+ else if (typeof value === 'boolean') {
277
+ return;
242
278
  }
243
- const filenameSlashes = filename.split('\\').join('/');
244
- // Absolute Unix path: /hello.txt
245
- if (/^(\/)/i.test(filenameSlashes)) {
246
- // console.log(filename, 'Absolute Unix path: /hello.txt');
247
- return true;
279
+ else if (typeof value === 'number' && !isNaN(value)) {
280
+ return;
248
281
  }
249
- // Absolute Windows path: /hello.txt
250
- if (/^([A-Z]{1,2}:\/?)\//i.test(filenameSlashes)) {
251
- // console.log(filename, 'Absolute Windows path: /hello.txt');
252
- return true;
282
+ else if (typeof value === 'string') {
283
+ return;
253
284
  }
254
- // Relative path: ./hello.txt
255
- if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
256
- // console.log(filename, 'Relative path: ./hello.txt');
257
- return true;
285
+ else if (typeof value === 'symbol') {
286
+ throw new UnexpectedError(`${name} is symbol`);
258
287
  }
259
- // Allow paths like foo/hello
260
- if (/^[^/]+\/[^/]+/i.test(filenameSlashes)) {
261
- // console.log(filename, 'Allow paths like foo/hello');
262
- return true;
288
+ else if (typeof value === 'function') {
289
+ throw new UnexpectedError(`${name} is function`);
263
290
  }
264
- // Allow paths like hello.book
265
- if (/^[^/]+\.[^/]+$/i.test(filenameSlashes)) {
266
- // console.log(filename, 'Allow paths like hello.book');
267
- return true;
291
+ else if (typeof value === 'object' && Array.isArray(value)) {
292
+ for (let i = 0; i < value.length; i++) {
293
+ checkSerializableAsJson({ name: `${name}[${i}]`, value: value[i], message });
294
+ }
268
295
  }
269
- return false;
270
- }
271
- /**
272
- * TODO: [🍏] Implement for MacOs
273
- */
296
+ else if (typeof value === 'object') {
297
+ if (value instanceof Date) {
298
+ throw new UnexpectedError(spaceTrim$1((block) => `
299
+ \`${name}\` is Date
274
300
 
275
- /**
276
- * Tests if given string is valid URL.
277
- *
278
- * Note: Dataurl are considered perfectly valid.
279
- * Note: There are two simmilar functions:
280
- * - `isValidUrl` which tests any URL
281
- * - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
282
- *
283
- * @public exported from `@promptbook/utils`
284
- */
285
- function isValidUrl(url) {
286
- if (typeof url !== 'string') {
287
- return false;
288
- }
289
- try {
290
- if (url.startsWith('blob:')) {
291
- url = url.replace(/^blob:/, '');
301
+ Use \`string_date_iso8601\` instead
302
+
303
+ Additional message for \`${name}\`:
304
+ ${block(message || '(nothing)')}
305
+ `));
292
306
  }
293
- const urlObject = new URL(url /* because fail is handled */);
294
- if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
295
- return false;
307
+ else if (value instanceof Map) {
308
+ throw new UnexpectedError(`${name} is Map`);
296
309
  }
297
- return true;
298
- }
299
- catch (error) {
300
- return false;
301
- }
302
- }
310
+ else if (value instanceof Set) {
311
+ throw new UnexpectedError(`${name} is Set`);
312
+ }
313
+ else if (value instanceof RegExp) {
314
+ throw new UnexpectedError(`${name} is RegExp`);
315
+ }
316
+ else if (value instanceof Error) {
317
+ throw new UnexpectedError(spaceTrim$1((block) => `
318
+ \`${name}\` is unserialized Error
303
319
 
304
- const defaultDiacriticsRemovalMap = [
305
- {
306
- base: 'A',
307
- letters: '\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F',
308
- },
309
- { base: 'AA', letters: '\uA732' },
310
- { base: 'AE', letters: '\u00C6\u01FC\u01E2' },
320
+ Use function \`serializeError\`
321
+
322
+ Additional message for \`${name}\`:
323
+ ${block(message || '(nothing)')}
324
+
325
+ `));
326
+ }
327
+ else {
328
+ for (const [subName, subValue] of Object.entries(value)) {
329
+ if (subValue === undefined) {
330
+ // Note: undefined in object is serializable - it is just omited
331
+ continue;
332
+ }
333
+ checkSerializableAsJson({ name: `${name}.${subName}`, value: subValue, message });
334
+ }
335
+ try {
336
+ JSON.stringify(value); // <- TODO: [0]
337
+ }
338
+ catch (error) {
339
+ assertsError(error);
340
+ throw new UnexpectedError(spaceTrim$1((block) => `
341
+ \`${name}\` is not serializable
342
+
343
+ ${block(error.stack || error.message)}
344
+
345
+ Additional message for \`${name}\`:
346
+ ${block(message || '(nothing)')}
347
+ `));
348
+ }
349
+ /*
350
+ TODO: [0] Is there some more elegant way to check circular references?
351
+ const seen = new Set();
352
+ const stack = [{ value }];
353
+ while (stack.length > 0) {
354
+ const { value } = stack.pop()!;
355
+ if (typeof value === 'object' && value !== null) {
356
+ if (seen.has(value)) {
357
+ throw new UnexpectedError(`${name} has circular reference`);
358
+ }
359
+ seen.add(value);
360
+ if (Array.isArray(value)) {
361
+ stack.push(...value.map((value) => ({ value })));
362
+ } else {
363
+ stack.push(...Object.values(value).map((value) => ({ value })));
364
+ }
365
+ }
366
+ }
367
+ */
368
+ return;
369
+ }
370
+ }
371
+ else {
372
+ throw new UnexpectedError(spaceTrim$1((block) => `
373
+ \`${name}\` is unknown type
374
+
375
+ Additional message for \`${name}\`:
376
+ ${block(message || '(nothing)')}
377
+ `));
378
+ }
379
+ }
380
+ /**
381
+ * TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
382
+ * TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
383
+ * Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
384
+ */
385
+
386
+ /**
387
+ * Creates a deep clone of the given object
388
+ *
389
+ * Note: This method only works for objects that are fully serializable to JSON and do not contain functions, Dates, or special types.
390
+ *
391
+ * @param objectValue The object to clone.
392
+ * @returns A deep, writable clone of the input object.
393
+ * @public exported from `@promptbook/utils`
394
+ */
395
+ function deepClone(objectValue) {
396
+ return JSON.parse(JSON.stringify(objectValue));
397
+ /*
398
+ TODO: [🧠] Is there a better implementation?
399
+ > const propertyNames = Object.getOwnPropertyNames(objectValue);
400
+ > for (const propertyName of propertyNames) {
401
+ > const value = (objectValue as really_any)[propertyName];
402
+ > if (value && typeof value === 'object') {
403
+ > deepClone(value);
404
+ > }
405
+ > }
406
+ > return Object.assign({}, objectValue);
407
+ */
408
+ }
409
+ /**
410
+ * TODO: [🧠] Is there a way how to meaningfully test this utility
411
+ */
412
+
413
+ /**
414
+ * Utility to export a JSON object from a function
415
+ *
416
+ * 1) Checks if the value is serializable as JSON
417
+ * 2) Makes a deep clone of the object
418
+ * 2) Orders the object properties
419
+ * 2) Deeply freezes the cloned object
420
+ *
421
+ * Note: This function does not mutates the given object
422
+ *
423
+ * @returns The same type of object as the input but read-only and re-ordered
424
+ * @public exported from `@promptbook/utils`
425
+ */
426
+ function exportJson(options) {
427
+ const { name, value, order, message } = options;
428
+ checkSerializableAsJson({ name, value, message });
429
+ const orderedValue =
430
+ // TODO: Fix error "Type instantiation is excessively deep and possibly infinite."
431
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
432
+ // @ts-ignore
433
+ order === undefined
434
+ ? deepClone(value)
435
+ : orderJson({
436
+ value: value,
437
+ // <- Note: checkSerializableAsJson asserts that the value is serializable as JSON
438
+ order: order,
439
+ });
440
+ $deepFreeze(orderedValue);
441
+ return orderedValue;
442
+ }
443
+ /**
444
+ * TODO: [🧠] Is there a way how to meaningfully test this utility
445
+ */
446
+
447
+ /**
448
+ * Nonce which is used for replacing things in strings
449
+ *
450
+ * @private within the repository
451
+ */
452
+ const REPLACING_NONCE = 'ptbkauk42kV2dzao34faw7FudQUHYPtW';
453
+ /**
454
+ * Placeholder value indicating a parameter is missing its value.
455
+ *
456
+ * @private within the repository
457
+ */
458
+ const RESERVED_PARAMETER_MISSING_VALUE = 'MISSING-' + REPLACING_NONCE;
459
+ /**
460
+ * Placeholder value indicating a parameter is restricted and cannot be used directly.
461
+ *
462
+ * @private within the repository
463
+ */
464
+ const RESERVED_PARAMETER_RESTRICTED = 'RESTRICTED-' + REPLACING_NONCE;
465
+ /**
466
+ * The names of the parameters that are reserved for special purposes
467
+ *
468
+ * @public exported from `@promptbook/core`
469
+ */
470
+ const RESERVED_PARAMETER_NAMES = exportJson({
471
+ name: 'RESERVED_PARAMETER_NAMES',
472
+ message: `The names of the parameters that are reserved for special purposes`,
473
+ value: [
474
+ 'content',
475
+ 'context',
476
+ 'knowledge',
477
+ 'examples',
478
+ 'modelName',
479
+ 'currentDate',
480
+ // <- TODO: list here all command names
481
+ // <- TODO: Add more like 'date', 'modelName',...
482
+ // <- TODO: Add [emoji] + instructions ACRY when adding new reserved parameter
483
+ ],
484
+ });
485
+ /**
486
+ * Note: [💞] Ignore a discrepancy between file name and entity name
487
+ */
488
+
489
+ /**
490
+ * Normalizes a given text to camelCase format.
491
+ *
492
+ * @param text The text to be normalized.
493
+ * @param _isFirstLetterCapital Whether the first letter should be capitalized.
494
+ * @returns The camelCase formatted string.
495
+ * @example 'helloWorld'
496
+ * @example 'iLovePromptbook'
497
+ * @public exported from `@promptbook/utils`
498
+ */
499
+ function normalizeTo_camelCase(text, _isFirstLetterCapital = false) {
500
+ let charType;
501
+ let lastCharType = null;
502
+ let normalizedName = '';
503
+ for (const char of text) {
504
+ let normalizedChar;
505
+ if (/^[a-z]$/.test(char)) {
506
+ charType = 'LOWERCASE';
507
+ normalizedChar = char;
508
+ }
509
+ else if (/^[A-Z]$/.test(char)) {
510
+ charType = 'UPPERCASE';
511
+ normalizedChar = char.toLowerCase();
512
+ }
513
+ else if (/^[0-9]$/.test(char)) {
514
+ charType = 'NUMBER';
515
+ normalizedChar = char;
516
+ }
517
+ else {
518
+ charType = 'OTHER';
519
+ normalizedChar = '';
520
+ }
521
+ if (!lastCharType) {
522
+ if (_isFirstLetterCapital) {
523
+ normalizedChar = normalizedChar.toUpperCase(); //TODO: DRY
524
+ }
525
+ }
526
+ else if (charType !== lastCharType &&
527
+ !(charType === 'LOWERCASE' && lastCharType === 'UPPERCASE') &&
528
+ !(lastCharType === 'NUMBER') &&
529
+ !(charType === 'NUMBER')) {
530
+ normalizedChar = normalizedChar.toUpperCase(); //TODO: [🌺] DRY
531
+ }
532
+ normalizedName += normalizedChar;
533
+ lastCharType = charType;
534
+ }
535
+ return normalizedName;
536
+ }
537
+ /**
538
+ * TODO: [🌺] Use some intermediate util splitWords
539
+ */
540
+
541
+ /**
542
+ * Removes emojis from a string and fix whitespaces
543
+ *
544
+ * @param text with emojis
545
+ * @returns text without emojis
546
+ * @public exported from `@promptbook/utils`
547
+ */
548
+ function removeEmojis(text) {
549
+ // Replace emojis (and also ZWJ sequence) with hyphens
550
+ text = text.replace(/(\p{Extended_Pictographic})\p{Modifier_Symbol}/gu, '$1');
551
+ text = text.replace(/(\p{Extended_Pictographic})[\u{FE00}-\u{FE0F}]/gu, '$1');
552
+ text = text.replace(/(\p{Extended_Pictographic})(\u{200D}\p{Extended_Pictographic})*/gu, '$1');
553
+ text = text.replace(/\p{Extended_Pictographic}/gu, '');
554
+ return text;
555
+ }
556
+
557
+ /**
558
+ * Tests if given string is valid URL.
559
+ *
560
+ * Note: This does not check if the file exists only if the path is valid
561
+ * @public exported from `@promptbook/utils`
562
+ */
563
+ function isValidFilePath(filename) {
564
+ if (typeof filename !== 'string') {
565
+ return false;
566
+ }
567
+ if (filename.split('\n').length > 1) {
568
+ return false;
569
+ }
570
+ if (filename.split(' ').length >
571
+ 5 /* <- TODO: [🧠][🈷] Make some better non-arbitrary way how to distinct filenames from informational texts */) {
572
+ return false;
573
+ }
574
+ const filenameSlashes = filename.split('\\').join('/');
575
+ // Absolute Unix path: /hello.txt
576
+ if (/^(\/)/i.test(filenameSlashes)) {
577
+ // console.log(filename, 'Absolute Unix path: /hello.txt');
578
+ return true;
579
+ }
580
+ // Absolute Windows path: /hello.txt
581
+ if (/^([A-Z]{1,2}:\/?)\//i.test(filenameSlashes)) {
582
+ // console.log(filename, 'Absolute Windows path: /hello.txt');
583
+ return true;
584
+ }
585
+ // Relative path: ./hello.txt
586
+ if (/^(\.\.?\/)+/i.test(filenameSlashes)) {
587
+ // console.log(filename, 'Relative path: ./hello.txt');
588
+ return true;
589
+ }
590
+ // Allow paths like foo/hello
591
+ if (/^[^/]+\/[^/]+/i.test(filenameSlashes)) {
592
+ // console.log(filename, 'Allow paths like foo/hello');
593
+ return true;
594
+ }
595
+ // Allow paths like hello.book
596
+ if (/^[^/]+\.[^/]+$/i.test(filenameSlashes)) {
597
+ // console.log(filename, 'Allow paths like hello.book');
598
+ return true;
599
+ }
600
+ return false;
601
+ }
602
+ /**
603
+ * TODO: [🍏] Implement for MacOs
604
+ */
605
+
606
+ /**
607
+ * Tests if given string is valid URL.
608
+ *
609
+ * Note: Dataurl are considered perfectly valid.
610
+ * Note: There are two similar functions:
611
+ * - `isValidUrl` which tests any URL
612
+ * - `isValidPipelineUrl` *(this one)* which tests just promptbook URL
613
+ *
614
+ * @public exported from `@promptbook/utils`
615
+ */
616
+ function isValidUrl(url) {
617
+ if (typeof url !== 'string') {
618
+ return false;
619
+ }
620
+ try {
621
+ if (url.startsWith('blob:')) {
622
+ url = url.replace(/^blob:/, '');
623
+ }
624
+ const urlObject = new URL(url /* because fail is handled */);
625
+ if (!['http:', 'https:', 'data:'].includes(urlObject.protocol)) {
626
+ return false;
627
+ }
628
+ return true;
629
+ }
630
+ catch (error) {
631
+ return false;
632
+ }
633
+ }
634
+
635
+ const defaultDiacriticsRemovalMap = [
636
+ {
637
+ base: 'A',
638
+ letters: '\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F',
639
+ },
640
+ { base: 'AA', letters: '\uA732' },
641
+ { base: 'AE', letters: '\u00C6\u01FC\u01E2' },
311
642
  { base: 'AO', letters: '\uA734' },
312
643
  { base: 'AU', letters: '\uA736' },
313
644
  { base: 'AV', letters: '\uA738\uA73A' },
@@ -546,10 +877,10 @@ for (let i = 0; i < defaultDiacriticsRemovalMap.length; i++) {
546
877
  */
547
878
 
548
879
  /**
549
- * @@@
880
+ * Removes diacritic marks (accents) from characters in a string.
550
881
  *
551
- * @param input @@@
552
- * @returns @@@
882
+ * @param input The string containing diacritics to be normalized.
883
+ * @returns The string with diacritics removed or normalized.
553
884
  * @public exported from `@promptbook/utils`
554
885
  */
555
886
  function removeDiacritics(input) {
@@ -563,10 +894,10 @@ function removeDiacritics(input) {
563
894
  */
564
895
 
565
896
  /**
566
- * @@@
897
+ * Converts a given text to kebab-case format.
567
898
  *
568
- * @param text @@@
569
- * @returns @@@
899
+ * @param text The text to be converted.
900
+ * @returns The kebab-case formatted string.
570
901
  * @example 'hello-world'
571
902
  * @example 'i-love-promptbook'
572
903
  * @public exported from `@promptbook/utils`
@@ -614,11 +945,11 @@ function normalizeToKebabCase(text) {
614
945
  */
615
946
 
616
947
  /**
617
- * @@@
948
+ * Converts a title string into a normalized name.
618
949
  *
619
- * @param value @@@
620
- * @returns @@@
621
- * @example @@@
950
+ * @param value The title string to be converted to a name.
951
+ * @returns A normalized name derived from the input title.
952
+ * @example 'Hello World!' -> 'hello-world'
622
953
  * @public exported from `@promptbook/utils`
623
954
  */
624
955
  function titleToName(value) {
@@ -646,45 +977,43 @@ function titleToName(value) {
646
977
  */
647
978
  function renderPromptbookMermaid(pipelineJson, options) {
648
979
  const { linkTask = () => null } = options || {};
980
+ const MERMAID_PREFIX = 'pipeline_';
981
+ const MERMAID_KNOWLEDGE_NAME = MERMAID_PREFIX + 'knowledge';
982
+ const MERMAID_RESERVED_NAME = MERMAID_PREFIX + 'reserved';
983
+ const MERMAID_INPUT_NAME = MERMAID_PREFIX + 'input';
984
+ const MERMAID_OUTPUT_NAME = MERMAID_PREFIX + 'output';
649
985
  const parameterNameToTaskName = (parameterName) => {
986
+ if (parameterName === 'knowledge') {
987
+ return MERMAID_KNOWLEDGE_NAME;
988
+ }
989
+ else if (RESERVED_PARAMETER_NAMES.includes(parameterName)) {
990
+ return MERMAID_RESERVED_NAME;
991
+ }
650
992
  const parameter = pipelineJson.parameters.find((parameter) => parameter.name === parameterName);
651
993
  if (!parameter) {
652
994
  throw new UnexpectedError(`Could not find {${parameterName}}`);
653
- // <- TODO: !!6 This causes problems when {knowledge} and other reserved parameters are used
995
+ // <- TODO: This causes problems when {knowledge} and other reserved parameters are used
654
996
  }
655
997
  if (parameter.isInput) {
656
- return 'input';
998
+ return MERMAID_INPUT_NAME;
657
999
  }
658
1000
  const task = pipelineJson.tasks.find((task) => task.resultingParameterName === parameterName);
659
1001
  if (!task) {
660
1002
  throw new Error(`Could not find task for {${parameterName}}`);
661
1003
  }
662
- return task.name || normalizeTo_camelCase('task-' + titleToName(task.title));
1004
+ return MERMAID_PREFIX + (task.name || normalizeTo_camelCase('task-' + titleToName(task.title)));
663
1005
  };
664
- const promptbookMermaid = spaceTrim$2((block) => `
665
-
666
- %% 🔮 Tip: Open this on GitHub or in the VSCode website to see the Mermaid graph visually
667
-
668
- flowchart LR
669
- subgraph "${pipelineJson.title}"
670
-
671
- direction TB
672
-
673
- input((Input)):::input
674
- ${block(pipelineJson.tasks
1006
+ const inputAndIntermediateParametersMermaid = pipelineJson.tasks
675
1007
  .flatMap(({ title, dependentParameterNames, resultingParameterName }) => [
676
1008
  `${parameterNameToTaskName(resultingParameterName)}("${title}")`,
677
1009
  ...dependentParameterNames.map((dependentParameterName) => `${parameterNameToTaskName(dependentParameterName)}--"{${dependentParameterName}}"-->${parameterNameToTaskName(resultingParameterName)}`),
678
1010
  ])
679
- .join('\n'))}
680
-
681
- ${block(pipelineJson.parameters
1011
+ .join('\n');
1012
+ const outputParametersMermaid = pipelineJson.parameters
682
1013
  .filter(({ isOutput }) => isOutput)
683
- .map(({ name }) => `${parameterNameToTaskName(name)}--"{${name}}"-->output`)
684
- .join('\n'))}
685
- output((Output)):::output
686
-
687
- ${block(pipelineJson.tasks
1014
+ .map(({ name }) => `${parameterNameToTaskName(name)}--"{${name}}"-->${MERMAID_OUTPUT_NAME}`)
1015
+ .join('\n');
1016
+ const linksMermaid = pipelineJson.tasks
688
1017
  .map((task) => {
689
1018
  const link = linkTask(task);
690
1019
  if (link === null) {
@@ -695,10 +1024,44 @@ function renderPromptbookMermaid(pipelineJson, options) {
695
1024
  return `click ${taskName} href "${href}" "${title}";`;
696
1025
  })
697
1026
  .filter((line) => line !== '')
698
- .join('\n'))}
1027
+ .join('\n');
1028
+ const interactionPointsMermaid = Object.entries({
1029
+ [MERMAID_INPUT_NAME]: 'Input',
1030
+ [MERMAID_OUTPUT_NAME]: 'Output',
1031
+ [MERMAID_RESERVED_NAME]: 'Other',
1032
+ [MERMAID_KNOWLEDGE_NAME]: 'Knowledge',
1033
+ })
1034
+ .filter(([MERMAID_NAME]) => (inputAndIntermediateParametersMermaid + outputParametersMermaid).includes(MERMAID_NAME))
1035
+ .map(([MERMAID_NAME, title]) => `${MERMAID_NAME}((${title})):::${MERMAID_NAME}`)
1036
+ .join('\n');
1037
+ const promptbookMermaid = spaceTrim$2((block) => `
1038
+
1039
+ %% 🔮 Tip: Open this on GitHub or in the VSCode website to see the Mermaid graph visually
1040
+
1041
+ flowchart LR
1042
+ subgraph "${pipelineJson.title}"
1043
+
1044
+ %% Basic configuration
1045
+ direction TB
1046
+
1047
+ %% Interaction points from pipeline to outside
1048
+ ${block(interactionPointsMermaid)}
1049
+
1050
+ %% Input and intermediate parameters
1051
+ ${block(inputAndIntermediateParametersMermaid)}
699
1052
 
700
- classDef input color: grey;
701
- classDef output color: grey;
1053
+
1054
+ %% Output parameters
1055
+ ${block(outputParametersMermaid)}
1056
+
1057
+ %% Links
1058
+ ${block(linksMermaid)}
1059
+
1060
+ %% Styles
1061
+ classDef ${MERMAID_INPUT_NAME} color: grey;
1062
+ classDef ${MERMAID_OUTPUT_NAME} color: grey;
1063
+ classDef ${MERMAID_RESERVED_NAME} color: grey;
1064
+ classDef ${MERMAID_KNOWLEDGE_NAME} color: grey;
702
1065
 
703
1066
  end;
704
1067
 
@@ -757,7 +1120,7 @@ class AuthenticationError extends Error {
757
1120
  }
758
1121
 
759
1122
  /**
760
- * This error indicates that the pipeline collection cannot be propperly loaded
1123
+ * This error indicates that the pipeline collection cannot be properly loaded
761
1124
  *
762
1125
  * @public exported from `@promptbook/core`
763
1126
  */
@@ -778,774 +1141,444 @@ class EnvironmentMismatchError extends Error {
778
1141
  constructor(message) {
779
1142
  super(message);
780
1143
  this.name = 'EnvironmentMismatchError';
781
- Object.setPrototypeOf(this, EnvironmentMismatchError.prototype);
782
- }
783
- }
784
-
785
- /**
786
- * This error occurs when some expectation is not met in the execution of the pipeline
787
- *
788
- * @public exported from `@promptbook/core`
789
- * Note: Do not throw this error, its reserved for `checkExpectations` and `createPipelineExecutor` and public ONLY to be serializable through remote server
790
- * Note: Always thrown in `checkExpectations` and catched in `createPipelineExecutor` and rethrown as `PipelineExecutionError`
791
- * Note: This is a kindof subtype of PipelineExecutionError
792
- */
793
- class ExpectError extends Error {
794
- constructor(message) {
795
- super(message);
796
- this.name = 'ExpectError';
797
- Object.setPrototypeOf(this, ExpectError.prototype);
798
- }
799
- }
800
-
801
- /**
802
- * This error indicates that the promptbook can not retrieve knowledge from external sources
803
- *
804
- * @public exported from `@promptbook/core`
805
- */
806
- class KnowledgeScrapeError extends Error {
807
- constructor(message) {
808
- super(message);
809
- this.name = 'KnowledgeScrapeError';
810
- Object.setPrototypeOf(this, KnowledgeScrapeError.prototype);
811
- }
812
- }
813
-
814
- /**
815
- * This error type indicates that some limit was reached
816
- *
817
- * @public exported from `@promptbook/core`
818
- */
819
- class LimitReachedError extends Error {
820
- constructor(message) {
821
- super(message);
822
- this.name = 'LimitReachedError';
823
- Object.setPrototypeOf(this, LimitReachedError.prototype);
824
- }
825
- }
826
-
827
- /**
828
- * This error type indicates that some tools are missing for pipeline execution or preparation
829
- *
830
- * @public exported from `@promptbook/core`
831
- */
832
- class MissingToolsError extends Error {
833
- constructor(message) {
834
- super(spaceTrim$2((block) => `
835
- ${block(message)}
836
-
837
- Note: You have probbably forgot to provide some tools for pipeline execution or preparation
838
-
839
- `));
840
- this.name = 'MissingToolsError';
841
- Object.setPrototypeOf(this, MissingToolsError.prototype);
842
- }
843
- }
844
-
845
- /**
846
- * This error indicates that promptbook not found in the collection
847
- *
848
- * @public exported from `@promptbook/core`
849
- */
850
- class NotFoundError extends Error {
851
- constructor(message) {
852
- super(message);
853
- this.name = 'NotFoundError';
854
- Object.setPrototypeOf(this, NotFoundError.prototype);
855
- }
856
- }
857
-
858
- /**
859
- * This error type indicates that some part of the code is not implemented yet
860
- *
861
- * @public exported from `@promptbook/core`
862
- */
863
- class NotYetImplementedError extends Error {
864
- constructor(message) {
865
- super(spaceTrim$2((block) => `
866
- ${block(message)}
867
-
868
- Note: This feature is not implemented yet but it will be soon.
869
-
870
- If you want speed up the implementation or just read more, look here:
871
- https://github.com/webgptorg/promptbook
872
-
873
- Or contact us on pavol@ptbk.io
874
-
875
- `));
876
- this.name = 'NotYetImplementedError';
877
- Object.setPrototypeOf(this, NotYetImplementedError.prototype);
878
- }
879
- }
880
-
881
- /**
882
- * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
883
- *
884
- * @public exported from `@promptbook/core`
885
- */
886
- class ParseError extends Error {
887
- constructor(message) {
888
- super(message);
889
- this.name = 'ParseError';
890
- Object.setPrototypeOf(this, ParseError.prototype);
891
- }
892
- }
893
- /**
894
- * TODO: Maybe split `ParseError` and `ApplyError`
895
- */
896
-
897
- /**
898
- * Generates random token
899
- *
900
- * Note: This function is cryptographically secure (it uses crypto.randomBytes internally)
901
- *
902
- * @private internal helper function
903
- * @returns secure random token
904
- */
905
- function $randomToken(randomness) {
906
- return randomBytes(randomness).toString('hex');
907
- }
908
- /**
909
- * TODO: Maybe use nanoid instead https://github.com/ai/nanoid
910
- */
911
-
912
- /**
913
- * This error indicates errors during the execution of the pipeline
914
- *
915
- * @public exported from `@promptbook/core`
916
- */
917
- class PipelineExecutionError extends Error {
918
- constructor(message) {
919
- // Added id parameter
920
- super(message);
921
- this.name = 'PipelineExecutionError';
922
- // TODO: [🐙] DRY - Maybe $randomId
923
- this.id = `error-${$randomToken(8 /* <- TODO: To global config + Use Base58 to avoid simmilar char conflicts */)}`;
924
- Object.setPrototypeOf(this, PipelineExecutionError.prototype);
1144
+ Object.setPrototypeOf(this, EnvironmentMismatchError.prototype);
925
1145
  }
926
1146
  }
927
- /**
928
- * TODO: [🧠][🌂] Add id to all errors
929
- */
930
1147
 
931
1148
  /**
932
- * This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
1149
+ * This error occurs when some expectation is not met in the execution of the pipeline
933
1150
  *
934
1151
  * @public exported from `@promptbook/core`
1152
+ * Note: Do not throw this error, its reserved for `checkExpectations` and `createPipelineExecutor` and public ONLY to be serializable through remote server
1153
+ * Note: Always thrown in `checkExpectations` and catched in `createPipelineExecutor` and rethrown as `PipelineExecutionError`
1154
+ * Note: This is a kindof subtype of PipelineExecutionError
935
1155
  */
936
- class PipelineLogicError extends Error {
1156
+ class ExpectError extends Error {
937
1157
  constructor(message) {
938
1158
  super(message);
939
- this.name = 'PipelineLogicError';
940
- Object.setPrototypeOf(this, PipelineLogicError.prototype);
1159
+ this.name = 'ExpectError';
1160
+ Object.setPrototypeOf(this, ExpectError.prototype);
941
1161
  }
942
1162
  }
943
1163
 
944
1164
  /**
945
- * This error indicates errors in referencing promptbooks between each other
1165
+ * This error indicates that the promptbook can not retrieve knowledge from external sources
946
1166
  *
947
1167
  * @public exported from `@promptbook/core`
948
1168
  */
949
- class PipelineUrlError extends Error {
1169
+ class KnowledgeScrapeError extends Error {
950
1170
  constructor(message) {
951
1171
  super(message);
952
- this.name = 'PipelineUrlError';
953
- Object.setPrototypeOf(this, PipelineUrlError.prototype);
1172
+ this.name = 'KnowledgeScrapeError';
1173
+ Object.setPrototypeOf(this, KnowledgeScrapeError.prototype);
954
1174
  }
955
1175
  }
956
1176
 
957
1177
  /**
958
- * Error thrown when a fetch request fails
1178
+ * This error type indicates that some limit was reached
959
1179
  *
960
1180
  * @public exported from `@promptbook/core`
961
1181
  */
962
- class PromptbookFetchError extends Error {
1182
+ class LimitReachedError extends Error {
963
1183
  constructor(message) {
964
1184
  super(message);
965
- this.name = 'PromptbookFetchError';
966
- Object.setPrototypeOf(this, PromptbookFetchError.prototype);
1185
+ this.name = 'LimitReachedError';
1186
+ Object.setPrototypeOf(this, LimitReachedError.prototype);
967
1187
  }
968
1188
  }
969
1189
 
970
1190
  /**
971
- * This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
1191
+ * This error type indicates that some tools are missing for pipeline execution or preparation
972
1192
  *
973
1193
  * @public exported from `@promptbook/core`
974
1194
  */
975
- class WrappedError extends Error {
976
- constructor(whatWasThrown) {
977
- const tag = `[🤮]`;
978
- console.error(tag, whatWasThrown);
979
- super(spaceTrim$2(`
980
- Non-Error object was thrown
1195
+ class MissingToolsError extends Error {
1196
+ constructor(message) {
1197
+ super(spaceTrim$2((block) => `
1198
+ ${block(message)}
981
1199
 
982
- Note: Look for ${tag} in the console for more details
983
- Please report issue on ${ADMIN_EMAIL}
984
- `));
985
- this.name = 'WrappedError';
986
- Object.setPrototypeOf(this, WrappedError.prototype);
1200
+ Note: You have probably forgot to provide some tools for pipeline execution or preparation
1201
+
1202
+ `));
1203
+ this.name = 'MissingToolsError';
1204
+ Object.setPrototypeOf(this, MissingToolsError.prototype);
987
1205
  }
988
1206
  }
989
1207
 
990
1208
  /**
991
- * Index of all custom errors
1209
+ * This error indicates that promptbook not found in the collection
992
1210
  *
993
1211
  * @public exported from `@promptbook/core`
994
1212
  */
995
- const PROMPTBOOK_ERRORS = {
996
- AbstractFormatError,
997
- CsvFormatError,
998
- CollectionError,
999
- EnvironmentMismatchError,
1000
- ExpectError,
1001
- KnowledgeScrapeError,
1002
- LimitReachedError,
1003
- MissingToolsError,
1004
- NotFoundError,
1005
- NotYetImplementedError,
1006
- ParseError,
1007
- PipelineExecutionError,
1008
- PipelineLogicError,
1009
- PipelineUrlError,
1010
- AuthenticationError,
1011
- PromptbookFetchError,
1012
- UnexpectedError,
1013
- WrappedError,
1014
- // TODO: [🪑]> VersionMismatchError,
1015
- };
1016
- /**
1017
- * Index of all javascript errors
1018
- *
1019
- * @private for internal usage
1020
- */
1021
- const COMMON_JAVASCRIPT_ERRORS = {
1022
- Error,
1023
- EvalError,
1024
- RangeError,
1025
- ReferenceError,
1026
- SyntaxError,
1027
- TypeError,
1028
- URIError,
1029
- AggregateError,
1030
- /*
1031
- Note: Not widely supported
1032
- > InternalError,
1033
- > ModuleError,
1034
- > HeapError,
1035
- > WebAssemblyCompileError,
1036
- > WebAssemblyRuntimeError,
1037
- */
1038
- };
1039
- /**
1040
- * Index of all errors
1041
- *
1042
- * @private for internal usage
1043
- */
1044
- const ALL_ERRORS = {
1045
- ...PROMPTBOOK_ERRORS,
1046
- ...COMMON_JAVASCRIPT_ERRORS,
1047
- };
1048
- /**
1049
- * Note: [💞] Ignore a discrepancy between file name and entity name
1050
- */
1051
-
1052
- /**
1053
- * Deserializes the error object
1054
- *
1055
- * @public exported from `@promptbook/utils`
1056
- */
1057
- function deserializeError(error) {
1058
- const { name, stack, id } = error; // Added id
1059
- let { message } = error;
1060
- let ErrorClass = ALL_ERRORS[error.name];
1061
- if (ErrorClass === undefined) {
1062
- ErrorClass = Error;
1063
- message = `${name}: ${message}`;
1064
- }
1065
- if (stack !== undefined && stack !== '') {
1066
- message = spaceTrim$1((block) => `
1067
- ${block(message)}
1068
-
1069
- Original stack trace:
1070
- ${block(stack || '')}
1071
- `);
1213
+ class NotFoundError extends Error {
1214
+ constructor(message) {
1215
+ super(message);
1216
+ this.name = 'NotFoundError';
1217
+ Object.setPrototypeOf(this, NotFoundError.prototype);
1072
1218
  }
1073
- const deserializedError = new ErrorClass(message);
1074
- deserializedError.id = id; // Assign id to the error object
1075
- return deserializedError;
1076
1219
  }
1077
1220
 
1078
1221
  /**
1079
- * Serializes an error into a [🚉] JSON-serializable object
1222
+ * This error type indicates that some part of the code is not implemented yet
1080
1223
  *
1081
- * @public exported from `@promptbook/utils`
1224
+ * @public exported from `@promptbook/core`
1082
1225
  */
1083
- function serializeError(error) {
1084
- const { name, message, stack } = error;
1085
- const { id } = error;
1086
- if (!Object.keys(ALL_ERRORS).includes(name)) {
1087
- console.error(spaceTrim$1((block) => `
1088
-
1089
- Cannot serialize error with name "${name}"
1226
+ class NotYetImplementedError extends Error {
1227
+ constructor(message) {
1228
+ super(spaceTrim$2((block) => `
1229
+ ${block(message)}
1090
1230
 
1091
- Authors of Promptbook probably forgot to add this error into the list of errors:
1092
- https://github.com/webgptorg/promptbook/blob/main/src/errors/0-index.ts
1231
+ Note: This feature is not implemented yet but it will be soon.
1093
1232
 
1233
+ If you want speed up the implementation or just read more, look here:
1234
+ https://github.com/webgptorg/promptbook
1094
1235
 
1095
- ${block(stack || message)}
1236
+ Or contact us on pavol@ptbk.io
1096
1237
 
1097
1238
  `));
1239
+ this.name = 'NotYetImplementedError';
1240
+ Object.setPrototypeOf(this, NotYetImplementedError.prototype);
1098
1241
  }
1099
- return {
1100
- name: name,
1101
- message,
1102
- stack,
1103
- id, // Include id in the serialized object
1104
- };
1105
1242
  }
1106
1243
 
1107
1244
  /**
1108
- * Async version of Array.forEach
1245
+ * This error indicates that the promptbook in a markdown format cannot be parsed into a valid promptbook object
1109
1246
  *
1110
- * @param array - Array to iterate over
1111
- * @param options - Options for the function
1112
- * @param callbackfunction - Function to call for each item
1113
- * @public exported from `@promptbook/utils`
1114
- * @deprecated [🪂] Use queues instead
1247
+ * @public exported from `@promptbook/core`
1115
1248
  */
1116
- async function forEachAsync(array, options, callbackfunction) {
1117
- const { maxParallelCount = Infinity } = options;
1118
- let index = 0;
1119
- let runningTasks = [];
1120
- const tasks = [];
1121
- for (const item of array) {
1122
- const currentIndex = index++;
1123
- const task = callbackfunction(item, currentIndex, array);
1124
- tasks.push(task);
1125
- runningTasks.push(task);
1126
- /* not await */ Promise.resolve(task).then(() => {
1127
- runningTasks = runningTasks.filter((t) => t !== task);
1128
- });
1129
- if (maxParallelCount < runningTasks.length) {
1130
- await Promise.race(runningTasks);
1131
- }
1249
+ class ParseError extends Error {
1250
+ constructor(message) {
1251
+ super(message);
1252
+ this.name = 'ParseError';
1253
+ Object.setPrototypeOf(this, ParseError.prototype);
1132
1254
  }
1133
- await Promise.all(tasks);
1134
1255
  }
1256
+ /**
1257
+ * TODO: Maybe split `ParseError` and `ApplyError`
1258
+ */
1135
1259
 
1136
1260
  /**
1137
- * Helper used in catch blocks to assert that the error is an instance of `Error`
1261
+ * Generates random token
1138
1262
  *
1139
- * @param whatWasThrown Any object that was thrown
1140
- * @returns Nothing if the error is an instance of `Error`
1141
- * @throws `WrappedError` or `UnexpectedError` if the error is not standard
1263
+ * Note: This function is cryptographically secure (it uses crypto.randomBytes internally)
1142
1264
  *
1143
- * @private within the repository
1265
+ * @private internal helper function
1266
+ * @returns secure random token
1144
1267
  */
1145
- function assertsError(whatWasThrown) {
1146
- // Case 1: Handle error which was rethrown as `WrappedError`
1147
- if (whatWasThrown instanceof WrappedError) {
1148
- const wrappedError = whatWasThrown;
1149
- throw wrappedError;
1150
- }
1151
- // Case 2: Handle unexpected errors
1152
- if (whatWasThrown instanceof UnexpectedError) {
1153
- const unexpectedError = whatWasThrown;
1154
- throw unexpectedError;
1155
- }
1156
- // Case 3: Handle standard errors - keep them up to consumer
1157
- if (whatWasThrown instanceof Error) {
1158
- return;
1159
- }
1160
- // Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
1161
- throw new WrappedError(whatWasThrown);
1268
+ function $randomToken(randomness) {
1269
+ return randomBytes(randomness).toString('hex');
1162
1270
  }
1271
+ /**
1272
+ * TODO: Maybe use nanoid instead https://github.com/ai/nanoid
1273
+ */
1163
1274
 
1164
1275
  /**
1165
- * Function to check if a string is valid CSV
1166
- *
1167
- * @param value The string to check
1168
- * @returns True if the string is a valid CSV string, false otherwise
1276
+ * This error indicates errors during the execution of the pipeline
1169
1277
  *
1170
- * @public exported from `@promptbook/utils`
1278
+ * @public exported from `@promptbook/core`
1171
1279
  */
1172
- function isValidCsvString(value) {
1173
- try {
1174
- // A simple check for CSV format: at least one comma and no invalid characters
1175
- if (value.includes(',') && /^[\w\s,"']+$/.test(value)) {
1176
- return true;
1177
- }
1178
- return false;
1179
- }
1180
- catch (error) {
1181
- assertsError(error);
1182
- return false;
1280
+ class PipelineExecutionError extends Error {
1281
+ constructor(message) {
1282
+ // Added id parameter
1283
+ super(message);
1284
+ this.name = 'PipelineExecutionError';
1285
+ // TODO: [🐙] DRY - Maybe $randomId
1286
+ this.id = `error-${$randomToken(8 /* <- TODO: To global config + Use Base58 to avoid similar char conflicts */)}`;
1287
+ Object.setPrototypeOf(this, PipelineExecutionError.prototype);
1183
1288
  }
1184
1289
  }
1290
+ /**
1291
+ * TODO: [🧠][🌂] Add id to all errors
1292
+ */
1185
1293
 
1186
1294
  /**
1187
- * Function isValidJsonString will tell you if the string is valid JSON or not
1188
- *
1189
- * @param value The string to check
1190
- * @returns True if the string is a valid JSON string, false otherwise
1295
+ * This error indicates that the promptbook object has valid syntax (=can be parsed) but contains logical errors (like circular dependencies)
1191
1296
  *
1192
- * @public exported from `@promptbook/utils`
1297
+ * @public exported from `@promptbook/core`
1193
1298
  */
1194
- function isValidJsonString(value /* <- [👨‍⚖️] */) {
1195
- try {
1196
- JSON.parse(value);
1197
- return true;
1198
- }
1199
- catch (error) {
1200
- assertsError(error);
1201
- if (error.message.includes('Unexpected token')) {
1202
- return false;
1203
- }
1204
- return false;
1299
+ class PipelineLogicError extends Error {
1300
+ constructor(message) {
1301
+ super(message);
1302
+ this.name = 'PipelineLogicError';
1303
+ Object.setPrototypeOf(this, PipelineLogicError.prototype);
1205
1304
  }
1206
1305
  }
1207
1306
 
1208
1307
  /**
1209
- * Converts a JavaScript Object Notation (JSON) string into an object.
1210
- *
1211
- * Note: This is wrapper around `JSON.parse()` with better error and type handling
1308
+ * This error indicates errors in referencing promptbooks between each other
1212
1309
  *
1213
- * @public exported from `@promptbook/utils`
1310
+ * @public exported from `@promptbook/core`
1214
1311
  */
1215
- function jsonParse(value) {
1216
- if (value === undefined) {
1217
- throw new Error(`Can not parse JSON from undefined value.`);
1218
- }
1219
- else if (typeof value !== 'string') {
1220
- console.error('Can not parse JSON from non-string value.', { text: value });
1221
- throw new Error(spaceTrim$1(`
1222
- Can not parse JSON from non-string value.
1223
-
1224
- The value type: ${typeof value}
1225
- See more in console.
1226
- `));
1227
- }
1228
- try {
1229
- return JSON.parse(value);
1230
- }
1231
- catch (error) {
1232
- if (!(error instanceof Error)) {
1233
- throw error;
1234
- }
1235
- throw new Error(spaceTrim$1((block) => `
1236
- ${block(error.message)}
1237
-
1238
- The JSON text:
1239
- ${block(value)}
1240
- `));
1312
+ class PipelineUrlError extends Error {
1313
+ constructor(message) {
1314
+ super(message);
1315
+ this.name = 'PipelineUrlError';
1316
+ Object.setPrototypeOf(this, PipelineUrlError.prototype);
1241
1317
  }
1242
1318
  }
1243
- /**
1244
- * TODO: !!!! Use in Promptbook.studio
1245
- */
1246
1319
 
1247
1320
  /**
1248
- * Function to check if a string is valid XML
1249
- *
1250
- * @param value
1251
- * @returns True if the string is a valid XML string, false otherwise
1321
+ * Error thrown when a fetch request fails
1252
1322
  *
1253
- * @public exported from `@promptbook/utils`
1323
+ * @public exported from `@promptbook/core`
1254
1324
  */
1255
- function isValidXmlString(value) {
1256
- try {
1257
- const parser = new DOMParser();
1258
- const parsedDocument = parser.parseFromString(value, 'application/xml');
1259
- const parserError = parsedDocument.getElementsByTagName('parsererror');
1260
- if (parserError.length > 0) {
1261
- return false;
1262
- }
1263
- return true;
1264
- }
1265
- catch (error) {
1266
- assertsError(error);
1267
- return false;
1325
+ class PromptbookFetchError extends Error {
1326
+ constructor(message) {
1327
+ super(message);
1328
+ this.name = 'PromptbookFetchError';
1329
+ Object.setPrototypeOf(this, PromptbookFetchError.prototype);
1268
1330
  }
1269
1331
  }
1270
1332
 
1271
1333
  /**
1272
- * Orders JSON object by keys
1334
+ * Index of all custom errors
1273
1335
  *
1274
- * @returns The same type of object as the input re-ordered
1275
- * @public exported from `@promptbook/utils`
1336
+ * @public exported from `@promptbook/core`
1276
1337
  */
1277
- function orderJson(options) {
1278
- const { value, order } = options;
1279
- const orderedValue = {
1280
- ...(order === undefined ? {} : Object.fromEntries(order.map((key) => [key, undefined]))),
1281
- ...value,
1282
- };
1283
- return orderedValue;
1284
- }
1285
-
1338
+ const PROMPTBOOK_ERRORS = {
1339
+ AbstractFormatError,
1340
+ CsvFormatError,
1341
+ CollectionError,
1342
+ EnvironmentMismatchError,
1343
+ ExpectError,
1344
+ KnowledgeScrapeError,
1345
+ LimitReachedError,
1346
+ MissingToolsError,
1347
+ NotFoundError,
1348
+ NotYetImplementedError,
1349
+ ParseError,
1350
+ PipelineExecutionError,
1351
+ PipelineLogicError,
1352
+ PipelineUrlError,
1353
+ AuthenticationError,
1354
+ PromptbookFetchError,
1355
+ UnexpectedError,
1356
+ WrappedError,
1357
+ // TODO: [🪑]> VersionMismatchError,
1358
+ };
1286
1359
  /**
1287
- * Freezes the given object and all its nested objects recursively
1360
+ * Index of all javascript errors
1288
1361
  *
1289
- * Note: `$` is used to indicate that this function is not a pure function - it mutates given object
1290
- * Note: This function mutates the object and returns the original (but mutated-deep-freezed) object
1362
+ * @private for internal usage
1363
+ */
1364
+ const COMMON_JAVASCRIPT_ERRORS = {
1365
+ Error,
1366
+ EvalError,
1367
+ RangeError,
1368
+ ReferenceError,
1369
+ SyntaxError,
1370
+ TypeError,
1371
+ URIError,
1372
+ AggregateError,
1373
+ /*
1374
+ Note: Not widely supported
1375
+ > InternalError,
1376
+ > ModuleError,
1377
+ > HeapError,
1378
+ > WebAssemblyCompileError,
1379
+ > WebAssemblyRuntimeError,
1380
+ */
1381
+ };
1382
+ /**
1383
+ * Index of all errors
1291
1384
  *
1292
- * @returns The same object as the input, but deeply frozen
1293
- * @public exported from `@promptbook/utils`
1385
+ * @private for internal usage
1294
1386
  */
1295
- function $deepFreeze(objectValue) {
1296
- if (Array.isArray(objectValue)) {
1297
- return Object.freeze(objectValue.map((item) => $deepFreeze(item)));
1298
- }
1299
- const propertyNames = Object.getOwnPropertyNames(objectValue);
1300
- for (const propertyName of propertyNames) {
1301
- const value = objectValue[propertyName];
1302
- if (value && typeof value === 'object') {
1303
- $deepFreeze(value);
1304
- }
1305
- }
1306
- Object.freeze(objectValue);
1307
- return objectValue;
1308
- }
1387
+ const ALL_ERRORS = {
1388
+ ...PROMPTBOOK_ERRORS,
1389
+ ...COMMON_JAVASCRIPT_ERRORS,
1390
+ };
1309
1391
  /**
1310
- * TODO: [🧠] Is there a way how to meaningfully test this utility
1392
+ * Note: [💞] Ignore a discrepancy between file name and entity name
1311
1393
  */
1312
1394
 
1313
1395
  /**
1314
- * Checks if the value is [🚉] serializable as JSON
1315
- * If not, throws an UnexpectedError with a rich error message and tracking
1316
- *
1317
- * - Almost all primitives are serializable BUT:
1318
- * - `undefined` is not serializable
1319
- * - `NaN` is not serializable
1320
- * - Objects and arrays are serializable if all their properties are serializable
1321
- * - Functions are not serializable
1322
- * - Circular references are not serializable
1323
- * - `Date` objects are not serializable
1324
- * - `Map` and `Set` objects are not serializable
1325
- * - `RegExp` objects are not serializable
1326
- * - `Error` objects are not serializable
1327
- * - `Symbol` objects are not serializable
1328
- * - And much more...
1396
+ * Deserializes the error object
1329
1397
  *
1330
- * @throws UnexpectedError if the value is not serializable as JSON
1331
1398
  * @public exported from `@promptbook/utils`
1332
1399
  */
1333
- function checkSerializableAsJson(options) {
1334
- const { value, name, message } = options;
1335
- if (value === undefined) {
1336
- throw new UnexpectedError(`${name} is undefined`);
1337
- }
1338
- else if (value === null) {
1339
- return;
1340
- }
1341
- else if (typeof value === 'boolean') {
1342
- return;
1343
- }
1344
- else if (typeof value === 'number' && !isNaN(value)) {
1345
- return;
1346
- }
1347
- else if (typeof value === 'string') {
1348
- return;
1349
- }
1350
- else if (typeof value === 'symbol') {
1351
- throw new UnexpectedError(`${name} is symbol`);
1352
- }
1353
- else if (typeof value === 'function') {
1354
- throw new UnexpectedError(`${name} is function`);
1355
- }
1356
- else if (typeof value === 'object' && Array.isArray(value)) {
1357
- for (let i = 0; i < value.length; i++) {
1358
- checkSerializableAsJson({ name: `${name}[${i}]`, value: value[i], message });
1359
- }
1400
+ function deserializeError(error) {
1401
+ const { name, stack, id } = error; // Added id
1402
+ let { message } = error;
1403
+ let ErrorClass = ALL_ERRORS[error.name];
1404
+ if (ErrorClass === undefined) {
1405
+ ErrorClass = Error;
1406
+ message = `${name}: ${message}`;
1360
1407
  }
1361
- else if (typeof value === 'object') {
1362
- if (value instanceof Date) {
1363
- throw new UnexpectedError(spaceTrim$1((block) => `
1364
- \`${name}\` is Date
1365
-
1366
- Use \`string_date_iso8601\` instead
1408
+ if (stack !== undefined && stack !== '') {
1409
+ message = spaceTrim$1((block) => `
1410
+ ${block(message)}
1367
1411
 
1368
- Additional message for \`${name}\`:
1369
- ${block(message || '(nothing)')}
1370
- `));
1371
- }
1372
- else if (value instanceof Map) {
1373
- throw new UnexpectedError(`${name} is Map`);
1374
- }
1375
- else if (value instanceof Set) {
1376
- throw new UnexpectedError(`${name} is Set`);
1377
- }
1378
- else if (value instanceof RegExp) {
1379
- throw new UnexpectedError(`${name} is RegExp`);
1380
- }
1381
- else if (value instanceof Error) {
1382
- throw new UnexpectedError(spaceTrim$1((block) => `
1383
- \`${name}\` is unserialized Error
1412
+ Original stack trace:
1413
+ ${block(stack || '')}
1414
+ `);
1415
+ }
1416
+ const deserializedError = new ErrorClass(message);
1417
+ deserializedError.id = id; // Assign id to the error object
1418
+ return deserializedError;
1419
+ }
1384
1420
 
1385
- Use function \`serializeError\`
1421
+ /**
1422
+ * Serializes an error into a [🚉] JSON-serializable object
1423
+ *
1424
+ * @public exported from `@promptbook/utils`
1425
+ */
1426
+ function serializeError(error) {
1427
+ const { name, message, stack } = error;
1428
+ const { id } = error;
1429
+ if (!Object.keys(ALL_ERRORS).includes(name)) {
1430
+ console.error(spaceTrim$1((block) => `
1386
1431
 
1387
- Additional message for \`${name}\`:
1388
- ${block(message || '(nothing)')}
1432
+ Cannot serialize error with name "${name}"
1389
1433
 
1390
- `));
1391
- }
1392
- else {
1393
- for (const [subName, subValue] of Object.entries(value)) {
1394
- if (subValue === undefined) {
1395
- // Note: undefined in object is serializable - it is just omited
1396
- continue;
1397
- }
1398
- checkSerializableAsJson({ name: `${name}.${subName}`, value: subValue, message });
1399
- }
1400
- try {
1401
- JSON.stringify(value); // <- TODO: [0]
1402
- }
1403
- catch (error) {
1404
- assertsError(error);
1405
- throw new UnexpectedError(spaceTrim$1((block) => `
1406
- \`${name}\` is not serializable
1434
+ Authors of Promptbook probably forgot to add this error into the list of errors:
1435
+ https://github.com/webgptorg/promptbook/blob/main/src/errors/0-index.ts
1407
1436
 
1408
- ${block(error.stack || error.message)}
1409
1437
 
1410
- Additional message for \`${name}\`:
1411
- ${block(message || '(nothing)')}
1412
- `));
1413
- }
1414
- /*
1415
- TODO: [0] Is there some more elegant way to check circular references?
1416
- const seen = new Set();
1417
- const stack = [{ value }];
1418
- while (stack.length > 0) {
1419
- const { value } = stack.pop()!;
1420
- if (typeof value === 'object' && value !== null) {
1421
- if (seen.has(value)) {
1422
- throw new UnexpectedError(`${name} has circular reference`);
1423
- }
1424
- seen.add(value);
1425
- if (Array.isArray(value)) {
1426
- stack.push(...value.map((value) => ({ value })));
1427
- } else {
1428
- stack.push(...Object.values(value).map((value) => ({ value })));
1429
- }
1430
- }
1431
- }
1432
- */
1433
- return;
1434
- }
1435
- }
1436
- else {
1437
- throw new UnexpectedError(spaceTrim$1((block) => `
1438
- \`${name}\` is unknown type
1438
+ ${block(stack || message)}
1439
1439
 
1440
- Additional message for \`${name}\`:
1441
- ${block(message || '(nothing)')}
1442
1440
  `));
1443
1441
  }
1442
+ return {
1443
+ name: name,
1444
+ message,
1445
+ stack,
1446
+ id, // Include id in the serialized object
1447
+ };
1444
1448
  }
1445
- /**
1446
- * TODO: Can be return type more type-safe? like `asserts options.value is JsonValue`
1447
- * TODO: [🧠][main] !!3 In-memory cache of same values to prevent multiple checks
1448
- * Note: [🐠] This is how `checkSerializableAsJson` + `isSerializableAsJson` together can just retun true/false or rich error message
1449
- */
1450
1449
 
1451
1450
  /**
1452
- * @@@
1451
+ * Async version of Array.forEach
1453
1452
  *
1453
+ * @param array - Array to iterate over
1454
+ * @param options - Options for the function
1455
+ * @param callbackfunction - Function to call for each item
1454
1456
  * @public exported from `@promptbook/utils`
1457
+ * @deprecated [🪂] Use queues instead
1455
1458
  */
1456
- function deepClone(objectValue) {
1457
- return JSON.parse(JSON.stringify(objectValue));
1458
- /*
1459
- TODO: [🧠] Is there a better implementation?
1460
- > const propertyNames = Object.getOwnPropertyNames(objectValue);
1461
- > for (const propertyName of propertyNames) {
1462
- > const value = (objectValue as really_any)[propertyName];
1463
- > if (value && typeof value === 'object') {
1464
- > deepClone(value);
1465
- > }
1466
- > }
1467
- > return Object.assign({}, objectValue);
1468
- */
1459
+ async function forEachAsync(array, options, callbackfunction) {
1460
+ const { maxParallelCount = Infinity } = options;
1461
+ let index = 0;
1462
+ let runningTasks = [];
1463
+ const tasks = [];
1464
+ for (const item of array) {
1465
+ const currentIndex = index++;
1466
+ const task = callbackfunction(item, currentIndex, array);
1467
+ tasks.push(task);
1468
+ runningTasks.push(task);
1469
+ /* not await */ Promise.resolve(task).then(() => {
1470
+ runningTasks = runningTasks.filter((t) => t !== task);
1471
+ });
1472
+ if (maxParallelCount < runningTasks.length) {
1473
+ await Promise.race(runningTasks);
1474
+ }
1475
+ }
1476
+ await Promise.all(tasks);
1469
1477
  }
1470
- /**
1471
- * TODO: [🧠] Is there a way how to meaningfully test this utility
1472
- */
1473
1478
 
1474
1479
  /**
1475
- * Utility to export a JSON object from a function
1476
- *
1477
- * 1) Checks if the value is serializable as JSON
1478
- * 2) Makes a deep clone of the object
1479
- * 2) Orders the object properties
1480
- * 2) Deeply freezes the cloned object
1480
+ * Function to check if a string is valid CSV
1481
1481
  *
1482
- * Note: This function does not mutates the given object
1482
+ * @param value The string to check
1483
+ * @returns `true` if the string is a valid CSV string, false otherwise
1483
1484
  *
1484
- * @returns The same type of object as the input but read-only and re-ordered
1485
1485
  * @public exported from `@promptbook/utils`
1486
1486
  */
1487
- function exportJson(options) {
1488
- const { name, value, order, message } = options;
1489
- checkSerializableAsJson({ name, value, message });
1490
- const orderedValue =
1491
- // TODO: Fix error "Type instantiation is excessively deep and possibly infinite."
1492
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
1493
- // @ts-ignore
1494
- order === undefined
1495
- ? deepClone(value)
1496
- : orderJson({
1497
- value: value,
1498
- // <- Note: checkSerializableAsJson asserts that the value is serializable as JSON
1499
- order: order,
1500
- });
1501
- $deepFreeze(orderedValue);
1502
- return orderedValue;
1487
+ function isValidCsvString(value) {
1488
+ try {
1489
+ // A simple check for CSV format: at least one comma and no invalid characters
1490
+ if (value.includes(',') && /^[\w\s,"']+$/.test(value)) {
1491
+ return true;
1492
+ }
1493
+ return false;
1494
+ }
1495
+ catch (error) {
1496
+ assertsError(error);
1497
+ return false;
1498
+ }
1503
1499
  }
1504
- /**
1505
- * TODO: [🧠] Is there a way how to meaningfully test this utility
1506
- */
1507
1500
 
1508
1501
  /**
1509
- * Nonce which is used for replacing things in strings
1502
+ * Function isValidJsonString will tell you if the string is valid JSON or not
1510
1503
  *
1511
- * @private within the repository
1512
- */
1513
- const REPLACING_NONCE = 'ptbkauk42kV2dzao34faw7FudQUHYPtW';
1514
- /**
1515
- * @@@
1504
+ * @param value The string to check
1505
+ * @returns `true` if the string is a valid JSON string, false otherwise
1516
1506
  *
1517
- * @private within the repository
1507
+ * @public exported from `@promptbook/utils`
1518
1508
  */
1519
- const RESERVED_PARAMETER_MISSING_VALUE = 'MISSING-' + REPLACING_NONCE;
1509
+ function isValidJsonString(value /* <- [👨‍⚖️] */) {
1510
+ try {
1511
+ JSON.parse(value);
1512
+ return true;
1513
+ }
1514
+ catch (error) {
1515
+ assertsError(error);
1516
+ if (error.message.includes('Unexpected token')) {
1517
+ return false;
1518
+ }
1519
+ return false;
1520
+ }
1521
+ }
1522
+
1520
1523
  /**
1521
- * @@@
1524
+ * Converts a JavaScript Object Notation (JSON) string into an object.
1522
1525
  *
1523
- * @private within the repository
1524
- */
1525
- const RESERVED_PARAMETER_RESTRICTED = 'RESTRICTED-' + REPLACING_NONCE;
1526
- /**
1527
- * The names of the parameters that are reserved for special purposes
1526
+ * Note: This is wrapper around `JSON.parse()` with better error and type handling
1528
1527
  *
1529
- * @public exported from `@promptbook/core`
1528
+ * @public exported from `@promptbook/utils`
1530
1529
  */
1531
- exportJson({
1532
- name: 'RESERVED_PARAMETER_NAMES',
1533
- message: `The names of the parameters that are reserved for special purposes`,
1534
- value: [
1535
- 'content',
1536
- 'context',
1537
- 'knowledge',
1538
- 'examples',
1539
- 'modelName',
1540
- 'currentDate',
1541
- // <- TODO: list here all command names
1542
- // <- TODO: Add more like 'date', 'modelName',...
1543
- // <- TODO: Add [emoji] + instructions ACRY when adding new reserved parameter
1544
- ],
1545
- });
1530
+ function jsonParse(value) {
1531
+ if (value === undefined) {
1532
+ throw new Error(`Can not parse JSON from undefined value.`);
1533
+ }
1534
+ else if (typeof value !== 'string') {
1535
+ console.error('Can not parse JSON from non-string value.', { text: value });
1536
+ throw new Error(spaceTrim$1(`
1537
+ Can not parse JSON from non-string value.
1538
+
1539
+ The value type: ${typeof value}
1540
+ See more in console.
1541
+ `));
1542
+ }
1543
+ try {
1544
+ return JSON.parse(value);
1545
+ }
1546
+ catch (error) {
1547
+ if (!(error instanceof Error)) {
1548
+ throw error;
1549
+ }
1550
+ throw new Error(spaceTrim$1((block) => `
1551
+ ${block(error.message)}
1552
+
1553
+ The JSON text:
1554
+ ${block(value)}
1555
+ `));
1556
+ }
1557
+ }
1558
+
1546
1559
  /**
1547
- * Note: [💞] Ignore a discrepancy between file name and entity name
1560
+ * Function to check if a string is valid XML
1561
+ *
1562
+ * @param value
1563
+ * @returns `true` if the string is a valid XML string, false otherwise
1564
+ *
1565
+ * @public exported from `@promptbook/utils`
1548
1566
  */
1567
+ function isValidXmlString(value) {
1568
+ try {
1569
+ const parser = new DOMParser();
1570
+ const parsedDocument = parser.parseFromString(value, 'application/xml');
1571
+ const parserError = parsedDocument.getElementsByTagName('parsererror');
1572
+ if (parserError.length > 0) {
1573
+ return false;
1574
+ }
1575
+ return true;
1576
+ }
1577
+ catch (error) {
1578
+ assertsError(error);
1579
+ return false;
1580
+ }
1581
+ }
1549
1582
 
1550
1583
  /**
1551
1584
  * Format either small or big number
@@ -1982,7 +2015,7 @@ const CountUtils = {
1982
2015
  PAGES: countPages,
1983
2016
  };
1984
2017
  /**
1985
- * TODO: [🧠][🤠] This should be probbably as part of `TextFormatDefinition`
2018
+ * TODO: [🧠][🤠] This should be probably as part of `TextFormatParser`
1986
2019
  * Note: [💞] Ignore a discrepancy between file name and entity name
1987
2020
  */
1988
2021
 
@@ -2005,10 +2038,10 @@ function decapitalize(word) {
2005
2038
  }
2006
2039
 
2007
2040
  /**
2008
- * @@@
2041
+ * Normalizes a text string to SCREAMING_CASE (all uppercase with underscores).
2009
2042
  *
2010
- * @param text @@@
2011
- * @returns @@@
2043
+ * @param text The text string to be converted to SCREAMING_CASE format.
2044
+ * @returns The normalized text in SCREAMING_CASE format.
2012
2045
  * @example 'HELLO_WORLD'
2013
2046
  * @example 'I_LOVE_PROMPTBOOK'
2014
2047
  * @public exported from `@promptbook/utils`
@@ -2093,11 +2126,11 @@ function isValidKeyword(keyword) {
2093
2126
  }
2094
2127
 
2095
2128
  /**
2096
- * @@@
2129
+ * Converts a name string into a URI-compatible format.
2097
2130
  *
2098
- * @param name @@@
2099
- * @returns @@@
2100
- * @example @@@
2131
+ * @param name The string to be converted to a URI-compatible format.
2132
+ * @returns A URI-compatible string derived from the input name.
2133
+ * @example 'Hello World' -> 'hello-world'
2101
2134
  * @public exported from `@promptbook/utils`
2102
2135
  */
2103
2136
  function nameToUriPart(name) {
@@ -2111,11 +2144,11 @@ function nameToUriPart(name) {
2111
2144
  }
2112
2145
 
2113
2146
  /**
2114
- * @@@
2147
+ * Converts a given name into URI-compatible parts.
2115
2148
  *
2116
- * @param name @@@
2117
- * @returns @@@
2118
- * @example @@@
2149
+ * @param name The name to be converted into URI parts.
2150
+ * @returns An array of URI-compatible parts derived from the name.
2151
+ * @example 'Example Name' -> ['example', 'name']
2119
2152
  * @public exported from `@promptbook/utils`
2120
2153
  */
2121
2154
  function nameToUriParts(name) {
@@ -2137,10 +2170,10 @@ function normalizeTo_PascalCase(text) {
2137
2170
  }
2138
2171
 
2139
2172
  /**
2140
- * @@@
2173
+ * Normalizes a text string to snake_case format.
2141
2174
  *
2142
- * @param text @@@
2143
- * @returns @@@
2175
+ * @param text The text string to be converted to snake_case format.
2176
+ * @returns The normalized text in snake_case format.
2144
2177
  * @example 'hello_world'
2145
2178
  * @example 'i_love_promptbook'
2146
2179
  * @public exported from `@promptbook/utils`
@@ -2192,7 +2225,7 @@ function parseKeywords(input) {
2192
2225
  */
2193
2226
 
2194
2227
  /**
2195
- * @@@
2228
+ * Searches set of keywords for a specific keyword
2196
2229
  *
2197
2230
  * @param haystack
2198
2231
  * @param needle
@@ -2207,6 +2240,9 @@ function searchKeywords(haystack, needle) {
2207
2240
  }
2208
2241
  return true;
2209
2242
  }
2243
+ /**
2244
+ * TODO: Rename to `isKeywordInKeywords`
2245
+ */
2210
2246
 
2211
2247
  /**
2212
2248
  * Adds suffix to the URL
@@ -2318,9 +2354,9 @@ function parseNumber(value) {
2318
2354
  /**
2319
2355
  * Removes quotes from a string
2320
2356
  *
2321
- * Tip: This is very usefull for post-processing of the result of the LLM model
2357
+ * Tip: This is very useful for post-processing of the result of the LLM model
2322
2358
  * Note: This function removes only the same quotes from the beginning and the end of the string
2323
- * Note: There are two simmilar functions:
2359
+ * Note: There are two similar functions:
2324
2360
  * - `removeQuotes` which removes only bounding quotes
2325
2361
  * - `unwrapResult` which removes whole introduce sentence
2326
2362
  *
@@ -2332,22 +2368,23 @@ function removeQuotes(text) {
2332
2368
  if (text.startsWith('"') && text.endsWith('"')) {
2333
2369
  return text.slice(1, -1);
2334
2370
  }
2335
- if (text.startsWith('\'') && text.endsWith('\'')) {
2371
+ if (text.startsWith("'") && text.endsWith("'")) {
2336
2372
  return text.slice(1, -1);
2337
2373
  }
2338
2374
  return text;
2339
2375
  }
2340
2376
 
2341
2377
  /**
2342
- * @@@
2378
+ * Creates a deep clone of a PipelineJson object, copying all properties explicitly.
2343
2379
  *
2344
- * Note: It is usefull @@@
2380
+ * Note: It is useful for ensuring that modifications to the returned pipeline do not affect the original.
2345
2381
  *
2346
- * @param pipeline
2382
+ * @param pipeline The pipeline to clone.
2383
+ * @returns A new PipelineJson object with the same properties as the input.
2347
2384
  * @public exported from `@promptbook/utils`
2348
2385
  */
2349
2386
  function clonePipeline(pipeline) {
2350
- // Note: Not using spread operator (...) because @@@
2387
+ // Note: Not using spread operator (...) because it does not deeply copy nested objects and may miss non-enumerable properties.
2351
2388
  const { pipelineUrl, sourceFile, title, bookVersion, description, formfactorName, parameters, tasks, knowledgeSources, knowledgePieces, personas, preparations, sources, } = pipeline;
2352
2389
  return {
2353
2390
  pipelineUrl,
@@ -2496,7 +2533,7 @@ function union(...sets) {
2496
2533
  /**
2497
2534
  * Function trimCodeBlock will trim starting and ending code block from the string if it is present.
2498
2535
  *
2499
- * Note: This is usefull for post-processing of the result of the chat LLM model
2536
+ * Note: This is useful for post-processing of the result of the chat LLM model
2500
2537
  * when the model wraps the result in the (markdown) code block.
2501
2538
  *
2502
2539
  * @public exported from `@promptbook/utils`
@@ -2515,7 +2552,7 @@ function trimCodeBlock(value) {
2515
2552
  /**
2516
2553
  * Function trimEndOfCodeBlock will remove ending code block from the string if it is present.
2517
2554
  *
2518
- * Note: This is usefull for post-processing of the result of the completion LLM model
2555
+ * Note: This is useful for post-processing of the result of the completion LLM model
2519
2556
  * if you want to start code block in the prompt but you don't want to end it in the result.
2520
2557
  *
2521
2558
  * @public exported from `@promptbook/utils`
@@ -2530,9 +2567,9 @@ function trimEndOfCodeBlock(value) {
2530
2567
  /**
2531
2568
  * Removes quotes and optional introduce text from a string
2532
2569
  *
2533
- * Tip: This is very usefull for post-processing of the result of the LLM model
2570
+ * Tip: This is very useful for post-processing of the result of the LLM model
2534
2571
  * Note: This function trims the text and removes whole introduce sentence if it is present
2535
- * Note: There are two simmilar functions:
2572
+ * Note: There are two similar functions:
2536
2573
  * - `removeQuotes` which removes only bounding quotes
2537
2574
  * - `unwrapResult` which removes whole introduce sentence
2538
2575
  *
@@ -2633,10 +2670,10 @@ function isRootPath(value) {
2633
2670
  */
2634
2671
 
2635
2672
  /**
2636
- * @@@
2673
+ * Checks if the given value is a valid JavaScript identifier name.
2637
2674
  *
2638
- * @param javascriptName @@@
2639
- * @returns @@@
2675
+ * @param javascriptName The value to check for JavaScript identifier validity.
2676
+ * @returns `true` if the value is a valid JavaScript name, false otherwise.
2640
2677
  * @public exported from `@promptbook/utils`
2641
2678
  */
2642
2679
  function isValidJavascriptName(javascriptName) {
@@ -2649,7 +2686,7 @@ function isValidJavascriptName(javascriptName) {
2649
2686
  /**
2650
2687
  * Tests if given string is valid semantic version
2651
2688
  *
2652
- * Note: There are two simmilar functions:
2689
+ * Note: There are two similar functions:
2653
2690
  * - `isValidSemanticVersion` which tests any semantic version
2654
2691
  * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
2655
2692
  *
@@ -2671,7 +2708,7 @@ function isValidSemanticVersion(version) {
2671
2708
  *
2672
2709
  * @see https://www.npmjs.com/package/promptbook?activeTab=versions
2673
2710
  * Note: When you are using for example promptbook 2.0.0 and there already is promptbook 3.0.0 it don`t know about it.
2674
- * Note: There are two simmilar functions:
2711
+ * Note: There are two similar functions:
2675
2712
  * - `isValidSemanticVersion` which tests any semantic version
2676
2713
  * - `isValidPromptbookVersion` *(this one)* which tests just Promptbook versions
2677
2714
  *
@@ -2691,7 +2728,7 @@ function isValidPromptbookVersion(version) {
2691
2728
  /**
2692
2729
  * Checks if an URL is reserved for private networks or localhost.
2693
2730
  *
2694
- * Note: There are two simmilar functions:
2731
+ * Note: There are two similar functions:
2695
2732
  * - `isUrlOnPrivateNetwork` which tests full URL
2696
2733
  * - `isHostnameOnPrivateNetwork` *(this one)* which tests just hostname
2697
2734
  *
@@ -2724,7 +2761,7 @@ function isHostnameOnPrivateNetwork(hostname) {
2724
2761
  /**
2725
2762
  * Checks if an IP address or hostname is reserved for private networks or localhost.
2726
2763
  *
2727
- * Note: There are two simmilar functions:
2764
+ * Note: There are two similar functions:
2728
2765
  * - `isUrlOnPrivateNetwork` *(this one)* which tests full URL
2729
2766
  * - `isHostnameOnPrivateNetwork` which tests just hostname
2730
2767
  *
@@ -2742,7 +2779,7 @@ function isUrlOnPrivateNetwork(url) {
2742
2779
  /**
2743
2780
  * Tests if given string is valid pipeline URL URL.
2744
2781
  *
2745
- * Note: There are two simmilar functions:
2782
+ * Note: There are two similar functions:
2746
2783
  * - `isValidUrl` which tests any URL
2747
2784
  * - `isValidPipelineUrl` *(this one)* which tests just pipeline URL
2748
2785
  *