@promptbook/remote-server 0.89.0-8 → 0.89.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 (50) hide show
  1. package/README.md +9 -11
  2. package/esm/index.es.js +570 -420
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/typings/servers.d.ts +40 -0
  5. package/esm/typings/src/_packages/core.index.d.ts +12 -4
  6. package/esm/typings/src/_packages/remote-client.index.d.ts +6 -6
  7. package/esm/typings/src/_packages/remote-server.index.d.ts +6 -6
  8. package/esm/typings/src/_packages/types.index.d.ts +24 -14
  9. package/esm/typings/src/_packages/utils.index.d.ts +4 -0
  10. package/esm/typings/src/cli/cli-commands/login.d.ts +0 -1
  11. package/esm/typings/src/cli/common/$provideLlmToolsForCli.d.ts +16 -3
  12. package/esm/typings/src/cli/test/ptbk.d.ts +1 -1
  13. package/esm/typings/src/commands/EXPECT/expectCommandParser.d.ts +2 -0
  14. package/esm/typings/src/config.d.ts +10 -19
  15. package/esm/typings/src/errors/0-index.d.ts +8 -2
  16. package/esm/typings/src/errors/PipelineExecutionError.d.ts +1 -1
  17. package/esm/typings/src/errors/PromptbookFetchError.d.ts +9 -0
  18. package/esm/typings/src/errors/WrappedError.d.ts +10 -0
  19. package/esm/typings/src/errors/assertsError.d.ts +11 -0
  20. package/esm/typings/src/execution/PromptbookFetch.d.ts +1 -1
  21. package/esm/typings/src/formats/csv/utils/isValidCsvString.d.ts +9 -0
  22. package/esm/typings/src/formats/csv/utils/isValidCsvString.test.d.ts +1 -0
  23. package/esm/typings/src/formats/json/utils/isValidJsonString.d.ts +3 -0
  24. package/esm/typings/src/formats/xml/utils/isValidXmlString.d.ts +9 -0
  25. package/esm/typings/src/formats/xml/utils/isValidXmlString.test.d.ts +1 -0
  26. package/esm/typings/src/llm-providers/_common/register/$provideEnvFilename.d.ts +12 -0
  27. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsConfigurationFromEnv.d.ts +2 -8
  28. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForTestingAndScriptsAndPlayground.d.ts +2 -0
  29. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsForWizzardOrCli.d.ts +15 -4
  30. package/esm/typings/src/llm-providers/_common/register/$provideLlmToolsFromEnv.d.ts +1 -0
  31. package/esm/typings/src/remote-server/openapi-types.d.ts +284 -0
  32. package/esm/typings/src/remote-server/openapi.d.ts +187 -0
  33. package/esm/typings/src/remote-server/socket-types/_subtypes/{PromptbookServer_Identification.d.ts → Identification.d.ts} +9 -3
  34. package/esm/typings/src/remote-server/socket-types/_subtypes/identificationToPromptbookToken.d.ts +11 -0
  35. package/esm/typings/src/remote-server/socket-types/_subtypes/promptbookTokenToIdentification.d.ts +10 -0
  36. package/esm/typings/src/remote-server/socket-types/listModels/PromptbookServer_ListModels_Request.d.ts +2 -2
  37. package/esm/typings/src/remote-server/socket-types/prepare/PromptbookServer_PreparePipeline_Request.d.ts +2 -2
  38. package/esm/typings/src/remote-server/socket-types/prompt/PromptbookServer_Prompt_Request.d.ts +2 -2
  39. package/esm/typings/src/remote-server/startRemoteServer.d.ts +1 -2
  40. package/esm/typings/src/remote-server/types/RemoteClientOptions.d.ts +2 -2
  41. package/esm/typings/src/remote-server/types/RemoteServerOptions.d.ts +57 -38
  42. package/esm/typings/src/scrapers/_common/utils/{scraperFetch.d.ts → promptbookFetch.d.ts} +2 -2
  43. package/esm/typings/src/storage/env-storage/$EnvStorage.d.ts +40 -0
  44. package/esm/typings/src/types/typeAliases.d.ts +26 -0
  45. package/package.json +11 -7
  46. package/umd/index.umd.js +492 -324
  47. package/umd/index.umd.js.map +1 -1
  48. package/esm/typings/src/cli/test/ptbk2.d.ts +0 -5
  49. package/esm/typings/src/playground/BrjappConnector.d.ts +0 -67
  50. package/esm/typings/src/playground/brjapp-api-schema.d.ts +0 -12879
package/esm/index.es.js CHANGED
@@ -2,8 +2,8 @@ import colors from 'colors';
2
2
  import express from 'express';
3
3
  import http from 'http';
4
4
  import { Server } from 'socket.io';
5
- import spaceTrim$1, { spaceTrim } from 'spacetrim';
6
- import swaggerJsdoc from 'swagger-jsdoc';
5
+ import spaceTrim, { spaceTrim as spaceTrim$1 } from 'spacetrim';
6
+ import * as OpenApiValidator from 'express-openapi-validator';
7
7
  import swaggerUi from 'swagger-ui-express';
8
8
  import { forTime } from 'waitasecond';
9
9
  import { randomBytes } from 'crypto';
@@ -33,7 +33,7 @@ const BOOK_LANGUAGE_VERSION = '1.0.0';
33
33
  * @generated
34
34
  * @see https://github.com/webgptorg/promptbook
35
35
  */
36
- const PROMPTBOOK_ENGINE_VERSION = '0.89.0-8';
36
+ const PROMPTBOOK_ENGINE_VERSION = '0.89.0';
37
37
  /**
38
38
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
39
39
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -87,6 +87,7 @@ const ADMIN_GITHUB_NAME = 'hejny';
87
87
  * @public exported from `@promptbook/core`
88
88
  */
89
89
  const CLAIM = `It's time for a paradigm shift. The future of software in plain English, French or Latin`;
90
+ // <- TODO: [🐊] Pick the best claim
90
91
  /**
91
92
  * When the title is not provided, the default title is used
92
93
  *
@@ -119,6 +120,7 @@ const VALUE_STRINGS = {
119
120
  infinity: '(infinity; ∞)',
120
121
  negativeInfinity: '(negative infinity; -∞)',
121
122
  unserializable: '(unserializable value)',
123
+ circular: '(circular JSON)',
122
124
  };
123
125
  /**
124
126
  * Small number limit
@@ -158,7 +160,7 @@ const DEFAULT_MAX_PARALLEL_COUNT = 5; // <- TODO: [🤹‍♂️]
158
160
  */
159
161
  const DEFAULT_MAX_EXECUTION_ATTEMPTS = 10; // <- TODO: [🤹‍♂️]
160
162
  // <- TODO: [🕝] Make also `BOOKS_DIRNAME_ALTERNATIVES`
161
- // TODO: !!!!!! Just .promptbook dir, hardocode others
163
+ // TODO: Just `.promptbook` in config, hardcode subfolders like `download-cache` or `execution-cache`
162
164
  /**
163
165
  * Where to store the temporary downloads
164
166
  *
@@ -213,6 +215,122 @@ true);
213
215
  * TODO: [🧠][🧜‍♂️] Maybe join remoteServerUrl and path into single value
214
216
  */
215
217
 
218
+ /**
219
+ * Make error report URL for the given error
220
+ *
221
+ * @private private within the repository
222
+ */
223
+ function getErrorReportUrl(error) {
224
+ const report = {
225
+ title: `🐜 Error report from ${NAME}`,
226
+ body: spaceTrim((block) => `
227
+
228
+
229
+ \`${error.name || 'Error'}\` has occurred in the [${NAME}], please look into it @${ADMIN_GITHUB_NAME}.
230
+
231
+ \`\`\`
232
+ ${block(error.message || '(no error message)')}
233
+ \`\`\`
234
+
235
+
236
+ ## More info:
237
+
238
+ - **Promptbook engine version:** ${PROMPTBOOK_ENGINE_VERSION}
239
+ - **Book language version:** ${BOOK_LANGUAGE_VERSION}
240
+ - **Time:** ${new Date().toISOString()}
241
+
242
+ <details>
243
+ <summary>Stack trace:</summary>
244
+
245
+ ## Stack trace:
246
+
247
+ \`\`\`stacktrace
248
+ ${block(error.stack || '(empty)')}
249
+ \`\`\`
250
+ </details>
251
+
252
+ `),
253
+ };
254
+ const reportUrl = new URL(`https://github.com/webgptorg/promptbook/issues/new`);
255
+ reportUrl.searchParams.set('labels', 'bug');
256
+ reportUrl.searchParams.set('assignees', ADMIN_GITHUB_NAME);
257
+ reportUrl.searchParams.set('title', report.title);
258
+ reportUrl.searchParams.set('body', report.body);
259
+ return reportUrl;
260
+ }
261
+
262
+ /**
263
+ * This error type indicates that the error should not happen and its last check before crashing with some other error
264
+ *
265
+ * @public exported from `@promptbook/core`
266
+ */
267
+ class UnexpectedError extends Error {
268
+ constructor(message) {
269
+ super(spaceTrim$1((block) => `
270
+ ${block(message)}
271
+
272
+ Note: This error should not happen.
273
+ It's probbably a bug in the pipeline collection
274
+
275
+ Please report issue:
276
+ ${block(getErrorReportUrl(new Error(message)).href)}
277
+
278
+ Or contact us on ${ADMIN_EMAIL}
279
+
280
+ `));
281
+ this.name = 'UnexpectedError';
282
+ Object.setPrototypeOf(this, UnexpectedError.prototype);
283
+ }
284
+ }
285
+
286
+ /**
287
+ * This error type indicates that somewhere in the code non-Error object was thrown and it was wrapped into the `WrappedError`
288
+ *
289
+ * @public exported from `@promptbook/core`
290
+ */
291
+ class WrappedError extends Error {
292
+ constructor(whatWasThrown) {
293
+ const tag = `[🤮]`;
294
+ console.error(tag, whatWasThrown);
295
+ super(spaceTrim$1(`
296
+ Non-Error object was thrown
297
+
298
+ Note: Look for ${tag} in the console for more details
299
+ Please report issue on ${ADMIN_EMAIL}
300
+ `));
301
+ this.name = 'WrappedError';
302
+ Object.setPrototypeOf(this, WrappedError.prototype);
303
+ }
304
+ }
305
+
306
+ /**
307
+ * Helper used in catch blocks to assert that the error is an instance of `Error`
308
+ *
309
+ * @param whatWasThrown Any object that was thrown
310
+ * @returns Nothing if the error is an instance of `Error`
311
+ * @throws `WrappedError` or `UnexpectedError` if the error is not standard
312
+ *
313
+ * @private within the repository
314
+ */
315
+ function assertsError(whatWasThrown) {
316
+ // Case 1: Handle error which was rethrown as `WrappedError`
317
+ if (whatWasThrown instanceof WrappedError) {
318
+ const wrappedError = whatWasThrown;
319
+ throw wrappedError;
320
+ }
321
+ // Case 2: Handle unexpected errors
322
+ if (whatWasThrown instanceof UnexpectedError) {
323
+ const unexpectedError = whatWasThrown;
324
+ throw unexpectedError;
325
+ }
326
+ // Case 3: Handle standard errors - keep them up to consumer
327
+ if (whatWasThrown instanceof Error) {
328
+ return;
329
+ }
330
+ // Case 4: Handle non-standard errors - wrap them into `WrappedError` and throw
331
+ throw new WrappedError(whatWasThrown);
332
+ }
333
+
216
334
  /**
217
335
  * AuthenticationError is thrown from login function which is dependency of remote server
218
336
  *
@@ -257,7 +375,7 @@ class PipelineExecutionError extends Error {
257
375
  }
258
376
  }
259
377
  /**
260
- * TODO: !!!!!! Add id to all errors
378
+ * TODO: [🧠][🌂] Add id to all errors
261
379
  */
262
380
 
263
381
  /**
@@ -365,7 +483,7 @@ class LimitReachedError extends Error {
365
483
  */
366
484
  class MissingToolsError extends Error {
367
485
  constructor(message) {
368
- super(spaceTrim((block) => `
486
+ super(spaceTrim$1((block) => `
369
487
  ${block(message)}
370
488
 
371
489
  Note: You have probbably forgot to provide some tools for pipeline execution or preparation
@@ -396,7 +514,7 @@ class NotFoundError extends Error {
396
514
  */
397
515
  class NotYetImplementedError extends Error {
398
516
  constructor(message) {
399
- super(spaceTrim((block) => `
517
+ super(spaceTrim$1((block) => `
400
518
  ${block(message)}
401
519
 
402
520
  Note: This feature is not implemented yet but it will be soon.
@@ -455,70 +573,15 @@ class PipelineUrlError extends Error {
455
573
  }
456
574
 
457
575
  /**
458
- * Make error report URL for the given error
459
- *
460
- * @private private within the repository
461
- */
462
- function getErrorReportUrl(error) {
463
- const report = {
464
- title: `🐜 Error report from ${NAME}`,
465
- body: spaceTrim$1((block) => `
466
-
467
-
468
- \`${error.name || 'Error'}\` has occurred in the [${NAME}], please look into it @${ADMIN_GITHUB_NAME}.
469
-
470
- \`\`\`
471
- ${block(error.message || '(no error message)')}
472
- \`\`\`
473
-
474
-
475
- ## More info:
476
-
477
- - **Promptbook engine version:** ${PROMPTBOOK_ENGINE_VERSION}
478
- - **Book language version:** ${BOOK_LANGUAGE_VERSION}
479
- - **Time:** ${new Date().toISOString()}
480
-
481
- <details>
482
- <summary>Stack trace:</summary>
483
-
484
- ## Stack trace:
485
-
486
- \`\`\`stacktrace
487
- ${block(error.stack || '(empty)')}
488
- \`\`\`
489
- </details>
490
-
491
- `),
492
- };
493
- const reportUrl = new URL(`https://github.com/webgptorg/promptbook/issues/new`);
494
- reportUrl.searchParams.set('labels', 'bug');
495
- reportUrl.searchParams.set('assignees', ADMIN_GITHUB_NAME);
496
- reportUrl.searchParams.set('title', report.title);
497
- reportUrl.searchParams.set('body', report.body);
498
- return reportUrl;
499
- }
500
-
501
- /**
502
- * This error type indicates that the error should not happen and its last check before crashing with some other error
576
+ * Error thrown when a fetch request fails
503
577
  *
504
578
  * @public exported from `@promptbook/core`
505
579
  */
506
- class UnexpectedError extends Error {
580
+ class PromptbookFetchError extends Error {
507
581
  constructor(message) {
508
- super(spaceTrim((block) => `
509
- ${block(message)}
510
-
511
- Note: This error should not happen.
512
- It's probbably a bug in the pipeline collection
513
-
514
- Please report issue:
515
- ${block(getErrorReportUrl(new Error(message)).href)}
516
-
517
- Or contact us on ${ADMIN_EMAIL}
518
-
519
- `));
520
- this.name = 'UnexpectedError';
521
- Object.setPrototypeOf(this, UnexpectedError.prototype);
582
+ super(message);
583
+ this.name = 'PromptbookFetchError';
584
+ Object.setPrototypeOf(this, PromptbookFetchError.prototype);
522
585
  }
523
586
  }
524
587
 
@@ -542,7 +605,10 @@ const PROMPTBOOK_ERRORS = {
542
605
  PipelineExecutionError,
543
606
  PipelineLogicError,
544
607
  PipelineUrlError,
608
+ AuthenticationError,
609
+ PromptbookFetchError,
545
610
  UnexpectedError,
611
+ WrappedError,
546
612
  // TODO: [🪑]> VersionMismatchError,
547
613
  };
548
614
  /**
@@ -559,7 +625,6 @@ const COMMON_JAVASCRIPT_ERRORS = {
559
625
  TypeError,
560
626
  URIError,
561
627
  AggregateError,
562
- AuthenticationError,
563
628
  /*
564
629
  Note: Not widely supported
565
630
  > InternalError,
@@ -591,7 +656,7 @@ function serializeError(error) {
591
656
  const { name, message, stack } = error;
592
657
  const { id } = error;
593
658
  if (!Object.keys(ALL_ERRORS).includes(name)) {
594
- console.error(spaceTrim$1((block) => `
659
+ console.error(spaceTrim((block) => `
595
660
 
596
661
  Cannot serialize error with name "${name}"
597
662
 
@@ -755,11 +820,11 @@ function $execCommand(options) {
755
820
  console.warn(`Command "${humanReadableCommand}" exited with code ${code}`);
756
821
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
757
822
  }
758
- resolve(spaceTrim(output.join('\n')));
823
+ resolve(spaceTrim$1(output.join('\n')));
759
824
  }
760
825
  }
761
826
  else {
762
- resolve(spaceTrim(output.join('\n')));
827
+ resolve(spaceTrim$1(output.join('\n')));
763
828
  }
764
829
  };
765
830
  commandProcess.on('close', finishWithCode);
@@ -777,7 +842,7 @@ function $execCommand(options) {
777
842
  console.warn(error);
778
843
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
779
844
  }
780
- resolve(spaceTrim(output.join('\n')));
845
+ resolve(spaceTrim$1(output.join('\n')));
781
846
  }
782
847
  });
783
848
  }
@@ -802,9 +867,7 @@ async function locateAppOnLinux({ linuxWhich, }) {
802
867
  return result.trim();
803
868
  }
804
869
  catch (error) {
805
- if (!(error instanceof Error)) {
806
- throw error;
807
- }
870
+ assertsError(error);
808
871
  return null;
809
872
  }
810
873
  }
@@ -882,9 +945,7 @@ async function locateAppOnMacOs({ macOsName, }) {
882
945
  return result.trim() + toExec;
883
946
  }
884
947
  catch (error) {
885
- if (!(error instanceof Error)) {
886
- throw error;
887
- }
948
+ assertsError(error);
888
949
  return null;
889
950
  }
890
951
  }
@@ -915,9 +976,7 @@ async function locateAppOnWindows({ appName, windowsSuffix, }) {
915
976
  throw new Error(`Can not locate app ${appName} on Windows.`);
916
977
  }
917
978
  catch (error) {
918
- if (!(error instanceof Error)) {
919
- throw error;
920
- }
979
+ assertsError(error);
921
980
  return null;
922
981
  }
923
982
  }
@@ -1114,7 +1173,7 @@ function checkSerializableAsJson(options) {
1114
1173
  }
1115
1174
  else if (typeof value === 'object') {
1116
1175
  if (value instanceof Date) {
1117
- throw new UnexpectedError(spaceTrim$1((block) => `
1176
+ throw new UnexpectedError(spaceTrim((block) => `
1118
1177
  \`${name}\` is Date
1119
1178
 
1120
1179
  Use \`string_date_iso8601\` instead
@@ -1133,7 +1192,7 @@ function checkSerializableAsJson(options) {
1133
1192
  throw new UnexpectedError(`${name} is RegExp`);
1134
1193
  }
1135
1194
  else if (value instanceof Error) {
1136
- throw new UnexpectedError(spaceTrim$1((block) => `
1195
+ throw new UnexpectedError(spaceTrim((block) => `
1137
1196
  \`${name}\` is unserialized Error
1138
1197
 
1139
1198
  Use function \`serializeError\`
@@ -1155,10 +1214,8 @@ function checkSerializableAsJson(options) {
1155
1214
  JSON.stringify(value); // <- TODO: [0]
1156
1215
  }
1157
1216
  catch (error) {
1158
- if (!(error instanceof Error)) {
1159
- throw error;
1160
- }
1161
- throw new UnexpectedError(spaceTrim$1((block) => `
1217
+ assertsError(error);
1218
+ throw new UnexpectedError(spaceTrim((block) => `
1162
1219
  \`${name}\` is not serializable
1163
1220
 
1164
1221
  ${block(error.stack || error.message)}
@@ -1190,7 +1247,7 @@ function checkSerializableAsJson(options) {
1190
1247
  }
1191
1248
  }
1192
1249
  else {
1193
- throw new UnexpectedError(spaceTrim$1((block) => `
1250
+ throw new UnexpectedError(spaceTrim((block) => `
1194
1251
  \`${name}\` is unknown type
1195
1252
 
1196
1253
  Additional message for \`${name}\`:
@@ -1453,7 +1510,7 @@ function validatePipeline(pipeline) {
1453
1510
  if (!(error instanceof PipelineLogicError)) {
1454
1511
  throw error;
1455
1512
  }
1456
- console.error(spaceTrim((block) => `
1513
+ console.error(spaceTrim$1((block) => `
1457
1514
  Pipeline is not valid but logic errors are temporarily disabled via \`IS_PIPELINE_LOGIC_VALIDATED\`
1458
1515
 
1459
1516
  ${block(error.message)}
@@ -1480,7 +1537,7 @@ function validatePipeline_InnerFunction(pipeline) {
1480
1537
  })();
1481
1538
  if (pipeline.pipelineUrl !== undefined && !isValidPipelineUrl(pipeline.pipelineUrl)) {
1482
1539
  // <- Note: [🚲]
1483
- throw new PipelineLogicError(spaceTrim((block) => `
1540
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1484
1541
  Invalid promptbook URL "${pipeline.pipelineUrl}"
1485
1542
 
1486
1543
  ${block(pipelineIdentification)}
@@ -1488,7 +1545,7 @@ function validatePipeline_InnerFunction(pipeline) {
1488
1545
  }
1489
1546
  if (pipeline.bookVersion !== undefined && !isValidPromptbookVersion(pipeline.bookVersion)) {
1490
1547
  // <- Note: [🚲]
1491
- throw new PipelineLogicError(spaceTrim((block) => `
1548
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1492
1549
  Invalid Promptbook Version "${pipeline.bookVersion}"
1493
1550
 
1494
1551
  ${block(pipelineIdentification)}
@@ -1497,7 +1554,7 @@ function validatePipeline_InnerFunction(pipeline) {
1497
1554
  // TODO: [🧠] Maybe do here some propper JSON-schema / ZOD checking
1498
1555
  if (!Array.isArray(pipeline.parameters)) {
1499
1556
  // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
1500
- throw new ParseError(spaceTrim((block) => `
1557
+ throw new ParseError(spaceTrim$1((block) => `
1501
1558
  Pipeline is valid JSON but with wrong structure
1502
1559
 
1503
1560
  \`PipelineJson.parameters\` expected to be an array, but got ${typeof pipeline.parameters}
@@ -1508,7 +1565,7 @@ function validatePipeline_InnerFunction(pipeline) {
1508
1565
  // TODO: [🧠] Maybe do here some propper JSON-schema / ZOD checking
1509
1566
  if (!Array.isArray(pipeline.tasks)) {
1510
1567
  // TODO: [🧠] what is the correct error tp throw - maybe PromptbookSchemaError
1511
- throw new ParseError(spaceTrim((block) => `
1568
+ throw new ParseError(spaceTrim$1((block) => `
1512
1569
  Pipeline is valid JSON but with wrong structure
1513
1570
 
1514
1571
  \`PipelineJson.tasks\` expected to be an array, but got ${typeof pipeline.tasks}
@@ -1534,7 +1591,7 @@ function validatePipeline_InnerFunction(pipeline) {
1534
1591
  // Note: Check each parameter individually
1535
1592
  for (const parameter of pipeline.parameters) {
1536
1593
  if (parameter.isInput && parameter.isOutput) {
1537
- throw new PipelineLogicError(spaceTrim((block) => `
1594
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1538
1595
 
1539
1596
  Parameter \`{${parameter.name}}\` can not be both input and output
1540
1597
 
@@ -1545,7 +1602,7 @@ function validatePipeline_InnerFunction(pipeline) {
1545
1602
  if (!parameter.isInput &&
1546
1603
  !parameter.isOutput &&
1547
1604
  !pipeline.tasks.some((task) => task.dependentParameterNames.includes(parameter.name))) {
1548
- throw new PipelineLogicError(spaceTrim((block) => `
1605
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1549
1606
  Parameter \`{${parameter.name}}\` is created but not used
1550
1607
 
1551
1608
  You can declare {${parameter.name}} as output parameter by adding in the header:
@@ -1557,7 +1614,7 @@ function validatePipeline_InnerFunction(pipeline) {
1557
1614
  }
1558
1615
  // Note: Testing that parameter is either input or result of some task
1559
1616
  if (!parameter.isInput && !pipeline.tasks.some((task) => task.resultingParameterName === parameter.name)) {
1560
- throw new PipelineLogicError(spaceTrim((block) => `
1617
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1561
1618
  Parameter \`{${parameter.name}}\` is declared but not defined
1562
1619
 
1563
1620
  You can do one of these:
@@ -1573,14 +1630,14 @@ function validatePipeline_InnerFunction(pipeline) {
1573
1630
  // Note: Checking each task individually
1574
1631
  for (const task of pipeline.tasks) {
1575
1632
  if (definedParameters.has(task.resultingParameterName)) {
1576
- throw new PipelineLogicError(spaceTrim((block) => `
1633
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1577
1634
  Parameter \`{${task.resultingParameterName}}\` is defined multiple times
1578
1635
 
1579
1636
  ${block(pipelineIdentification)}
1580
1637
  `));
1581
1638
  }
1582
1639
  if (RESERVED_PARAMETER_NAMES.includes(task.resultingParameterName)) {
1583
- throw new PipelineLogicError(spaceTrim((block) => `
1640
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1584
1641
  Parameter name {${task.resultingParameterName}} is reserved, please use different name
1585
1642
 
1586
1643
  ${block(pipelineIdentification)}
@@ -1590,7 +1647,7 @@ function validatePipeline_InnerFunction(pipeline) {
1590
1647
  if (task.jokerParameterNames && task.jokerParameterNames.length > 0) {
1591
1648
  if (!task.format &&
1592
1649
  !task.expectations /* <- TODO: Require at least 1 -> min <- expectation to use jokers */) {
1593
- throw new PipelineLogicError(spaceTrim((block) => `
1650
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1594
1651
  Joker parameters are used for {${task.resultingParameterName}} but no expectations are defined
1595
1652
 
1596
1653
  ${block(pipelineIdentification)}
@@ -1598,7 +1655,7 @@ function validatePipeline_InnerFunction(pipeline) {
1598
1655
  }
1599
1656
  for (const joker of task.jokerParameterNames) {
1600
1657
  if (!task.dependentParameterNames.includes(joker)) {
1601
- throw new PipelineLogicError(spaceTrim((block) => `
1658
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1602
1659
  Parameter \`{${joker}}\` is used for {${task.resultingParameterName}} as joker but not in \`dependentParameterNames\`
1603
1660
 
1604
1661
  ${block(pipelineIdentification)}
@@ -1609,21 +1666,21 @@ function validatePipeline_InnerFunction(pipeline) {
1609
1666
  if (task.expectations) {
1610
1667
  for (const [unit, { min, max }] of Object.entries(task.expectations)) {
1611
1668
  if (min !== undefined && max !== undefined && min > max) {
1612
- throw new PipelineLogicError(spaceTrim((block) => `
1669
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1613
1670
  Min expectation (=${min}) of ${unit} is higher than max expectation (=${max})
1614
1671
 
1615
1672
  ${block(pipelineIdentification)}
1616
1673
  `));
1617
1674
  }
1618
1675
  if (min !== undefined && min < 0) {
1619
- throw new PipelineLogicError(spaceTrim((block) => `
1676
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1620
1677
  Min expectation of ${unit} must be zero or positive
1621
1678
 
1622
1679
  ${block(pipelineIdentification)}
1623
1680
  `));
1624
1681
  }
1625
1682
  if (max !== undefined && max <= 0) {
1626
- throw new PipelineLogicError(spaceTrim((block) => `
1683
+ throw new PipelineLogicError(spaceTrim$1((block) => `
1627
1684
  Max expectation of ${unit} must be positive
1628
1685
 
1629
1686
  ${block(pipelineIdentification)}
@@ -1645,7 +1702,7 @@ function validatePipeline_InnerFunction(pipeline) {
1645
1702
  while (unresovedTasks.length > 0) {
1646
1703
  if (loopLimit-- < 0) {
1647
1704
  // Note: Really UnexpectedError not LimitReachedError - this should not happen and be caught below
1648
- throw new UnexpectedError(spaceTrim((block) => `
1705
+ throw new UnexpectedError(spaceTrim$1((block) => `
1649
1706
  Loop limit reached during detection of circular dependencies in \`validatePipeline\`
1650
1707
 
1651
1708
  ${block(pipelineIdentification)}
@@ -1655,7 +1712,7 @@ function validatePipeline_InnerFunction(pipeline) {
1655
1712
  if (currentlyResovedTasks.length === 0) {
1656
1713
  throw new PipelineLogicError(
1657
1714
  // TODO: [🐎] DRY
1658
- spaceTrim((block) => `
1715
+ spaceTrim$1((block) => `
1659
1716
 
1660
1717
  Can not resolve some parameters:
1661
1718
  Either you are using a parameter that is not defined, or there are some circular dependencies.
@@ -1757,6 +1814,9 @@ function isPipelinePrepared(pipeline) {
1757
1814
  /**
1758
1815
  * Function isValidJsonString will tell you if the string is valid JSON or not
1759
1816
  *
1817
+ * @param value The string to check
1818
+ * @returns True if the string is a valid JSON string, false otherwise
1819
+ *
1760
1820
  * @public exported from `@promptbook/utils`
1761
1821
  */
1762
1822
  function isValidJsonString(value /* <- [👨‍⚖️] */) {
@@ -1765,9 +1825,7 @@ function isValidJsonString(value /* <- [👨‍⚖️] */) {
1765
1825
  return true;
1766
1826
  }
1767
1827
  catch (error) {
1768
- if (!(error instanceof Error)) {
1769
- throw error;
1770
- }
1828
+ assertsError(error);
1771
1829
  if (error.message.includes('Unexpected token')) {
1772
1830
  return false;
1773
1831
  }
@@ -1819,7 +1877,7 @@ function deserializeError(error) {
1819
1877
  message = `${name}: ${message}`;
1820
1878
  }
1821
1879
  if (stack !== undefined && stack !== '') {
1822
- message = spaceTrim$1((block) => `
1880
+ message = spaceTrim((block) => `
1823
1881
  ${block(message)}
1824
1882
 
1825
1883
  Original stack trace:
@@ -1856,11 +1914,11 @@ function assertsTaskSuccessful(executionResult) {
1856
1914
  throw deserializeError(errors[0]);
1857
1915
  }
1858
1916
  else {
1859
- throw new PipelineExecutionError(spaceTrim((block) => `
1917
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
1860
1918
  Multiple errors occurred during Promptbook execution
1861
1919
 
1862
1920
  ${block(errors
1863
- .map(({ name, stack, message }, index) => spaceTrim((block) => `
1921
+ .map(({ name, stack, message }, index) => spaceTrim$1((block) => `
1864
1922
  ${name} ${index + 1}:
1865
1923
  ${block(stack || message)}
1866
1924
  `))
@@ -1905,8 +1963,8 @@ function createTask(options) {
1905
1963
  updatedAt = new Date();
1906
1964
  errors.push(...executionResult.errors);
1907
1965
  warnings.push(...executionResult.warnings);
1908
- // <- TODO: !!! Only unique errors and warnings should be added (or filtered)
1909
- // TODO: [🧠] !!! errors, warning, isSuccessful are redundant both in `ExecutionTask` and `ExecutionTask.currentValue`
1966
+ // <- TODO: [🌂] Only unique errors and warnings should be added (or filtered)
1967
+ // TODO: [🧠] !! errors, warning, isSuccessful are redundant both in `ExecutionTask` and `ExecutionTask.currentValue`
1910
1968
  // Also maybe move `ExecutionTask.currentValue.usage` -> `ExecutionTask.usage`
1911
1969
  // And delete `ExecutionTask.currentValue.preparedPipeline`
1912
1970
  assertsTaskSuccessful(executionResult);
@@ -1916,6 +1974,7 @@ function createTask(options) {
1916
1974
  partialResultSubject.next(executionResult);
1917
1975
  }
1918
1976
  catch (error) {
1977
+ assertsError(error);
1919
1978
  status = 'ERROR';
1920
1979
  errors.push(error);
1921
1980
  partialResultSubject.error(error);
@@ -2218,7 +2277,7 @@ function pipelineJsonToString(pipelineJson) {
2218
2277
  pipelineString += '\n\n';
2219
2278
  pipelineString += '```' + contentLanguage;
2220
2279
  pipelineString += '\n';
2221
- pipelineString += spaceTrim$1(content);
2280
+ pipelineString += spaceTrim(content);
2222
2281
  // <- TODO: [main] !!3 Escape
2223
2282
  // <- TODO: [🧠] Some clear strategy how to spaceTrim the blocks
2224
2283
  pipelineString += '\n';
@@ -2323,7 +2382,7 @@ class SimplePipelineCollection {
2323
2382
  for (const pipeline of pipelines) {
2324
2383
  // TODO: [👠] DRY
2325
2384
  if (pipeline.pipelineUrl === undefined) {
2326
- throw new PipelineUrlError(spaceTrim(`
2385
+ throw new PipelineUrlError(spaceTrim$1(`
2327
2386
  Pipeline with name "${pipeline.title}" does not have defined URL
2328
2387
 
2329
2388
  File:
@@ -2345,7 +2404,7 @@ class SimplePipelineCollection {
2345
2404
  pipelineJsonToString(unpreparePipeline(pipeline)) !==
2346
2405
  pipelineJsonToString(unpreparePipeline(this.collection.get(pipeline.pipelineUrl)))) {
2347
2406
  const existing = this.collection.get(pipeline.pipelineUrl);
2348
- throw new PipelineUrlError(spaceTrim(`
2407
+ throw new PipelineUrlError(spaceTrim$1(`
2349
2408
  Pipeline with URL ${pipeline.pipelineUrl} is already in the collection 🍎
2350
2409
 
2351
2410
  Conflicting files:
@@ -2377,13 +2436,13 @@ class SimplePipelineCollection {
2377
2436
  const pipeline = this.collection.get(url);
2378
2437
  if (!pipeline) {
2379
2438
  if (this.listPipelines().length === 0) {
2380
- throw new NotFoundError(spaceTrim(`
2439
+ throw new NotFoundError(spaceTrim$1(`
2381
2440
  Pipeline with url "${url}" not found
2382
2441
 
2383
2442
  No pipelines available
2384
2443
  `));
2385
2444
  }
2386
- throw new NotFoundError(spaceTrim((block) => `
2445
+ throw new NotFoundError(spaceTrim$1((block) => `
2387
2446
  Pipeline with url "${url}" not found
2388
2447
 
2389
2448
  Available pipelines:
@@ -2726,14 +2785,15 @@ class MultipleLlmExecutionTools {
2726
2785
  }
2727
2786
  }
2728
2787
  catch (error) {
2729
- if (!(error instanceof Error) || error instanceof UnexpectedError) {
2788
+ assertsError(error);
2789
+ if (error instanceof UnexpectedError) {
2730
2790
  throw error;
2731
2791
  }
2732
2792
  errors.push({ llmExecutionTools, error });
2733
2793
  }
2734
2794
  }
2735
2795
  if (errors.length === 1) {
2736
- throw errors[0];
2796
+ throw errors[0].error;
2737
2797
  }
2738
2798
  else if (errors.length > 1) {
2739
2799
  throw new PipelineExecutionError(
@@ -2741,7 +2801,7 @@ class MultipleLlmExecutionTools {
2741
2801
  // 1) OpenAI throw PipelineExecutionError: Parameter `{knowledge}` is not defined
2742
2802
  // 2) AnthropicClaude throw PipelineExecutionError: Parameter `{knowledge}` is not defined
2743
2803
  // 3) ...
2744
- spaceTrim$1((block) => `
2804
+ spaceTrim((block) => `
2745
2805
  All execution tools failed:
2746
2806
 
2747
2807
  ${block(errors
@@ -2754,7 +2814,7 @@ class MultipleLlmExecutionTools {
2754
2814
  throw new PipelineExecutionError(`You have not provided any \`LlmExecutionTools\``);
2755
2815
  }
2756
2816
  else {
2757
- throw new PipelineExecutionError(spaceTrim$1((block) => `
2817
+ throw new PipelineExecutionError(spaceTrim((block) => `
2758
2818
  You have not provided any \`LlmExecutionTools\` that support model variant "${prompt.modelRequirements.modelVariant}"
2759
2819
 
2760
2820
  Available \`LlmExecutionTools\`:
@@ -2787,7 +2847,7 @@ class MultipleLlmExecutionTools {
2787
2847
  */
2788
2848
  function joinLlmExecutionTools(...llmExecutionTools) {
2789
2849
  if (llmExecutionTools.length === 0) {
2790
- const warningMessage = spaceTrim$1(`
2850
+ const warningMessage = spaceTrim(`
2791
2851
  You have not provided any \`LlmExecutionTools\`
2792
2852
  This means that you won't be able to execute any prompts that require large language models like GPT-4 or Anthropic's Claude.
2793
2853
 
@@ -3078,14 +3138,14 @@ function $registeredScrapersMessage(availableScrapers) {
3078
3138
  return { ...metadata, isMetadataAviailable, isInstalled, isAvilableInTools };
3079
3139
  });
3080
3140
  if (metadata.length === 0) {
3081
- return spaceTrim$1(`
3141
+ return spaceTrim(`
3082
3142
  **No scrapers are available**
3083
3143
 
3084
3144
  This is a unexpected behavior, you are probably using some broken version of Promptbook
3085
3145
  At least there should be available the metadata of the scrapers
3086
3146
  `);
3087
3147
  }
3088
- return spaceTrim$1((block) => `
3148
+ return spaceTrim((block) => `
3089
3149
  Available scrapers are:
3090
3150
  ${block(metadata
3091
3151
  .map(({ packageName, className, isMetadataAviailable, isInstalled, mimeTypes, isAvilableInBrowser, isAvilableInTools, }, i) => {
@@ -3567,17 +3627,22 @@ function titleToName(value) {
3567
3627
  /**
3568
3628
  * The built-in `fetch' function with a lightweight error handling wrapper as default fetch function used in Promptbook scrapers
3569
3629
  *
3570
- * @private as default `fetch` function used in Promptbook scrapers
3630
+ * @public exported from `@promptbook/core`
3571
3631
  */
3572
- const scraperFetch = async (url, init) => {
3632
+ const promptbookFetch = async (urlOrRequest, init) => {
3573
3633
  try {
3574
- return await fetch(url, init);
3634
+ return await fetch(urlOrRequest, init);
3575
3635
  }
3576
3636
  catch (error) {
3577
- if (!(error instanceof Error)) {
3578
- throw error;
3637
+ assertsError(error);
3638
+ let url;
3639
+ if (typeof urlOrRequest === 'string') {
3640
+ url = urlOrRequest;
3641
+ }
3642
+ else if (urlOrRequest instanceof Request) {
3643
+ url = urlOrRequest.url;
3579
3644
  }
3580
- throw new KnowledgeScrapeError(spaceTrim$1((block) => `
3645
+ throw new PromptbookFetchError(spaceTrim((block) => `
3581
3646
  Can not fetch "${url}"
3582
3647
 
3583
3648
  Fetch error:
@@ -3598,7 +3663,7 @@ const scraperFetch = async (url, init) => {
3598
3663
  async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
3599
3664
  // console.log('!! makeKnowledgeSourceHandler', knowledgeSource);
3600
3665
  var _a;
3601
- const { fetch = scraperFetch } = tools;
3666
+ const { fetch = promptbookFetch } = tools;
3602
3667
  const { knowledgeSourceContent } = knowledgeSource;
3603
3668
  let { name } = knowledgeSource;
3604
3669
  const { rootDirname = null,
@@ -3668,7 +3733,7 @@ async function makeKnowledgeSourceHandler(knowledgeSource, tools, options) {
3668
3733
  const fileExtension = getFileExtension(filename);
3669
3734
  const mimeType = extensionToMimeType(fileExtension || '');
3670
3735
  if (!(await isFileExisting(filename, tools.fs))) {
3671
- throw new NotFoundError(spaceTrim$1((block) => `
3736
+ throw new NotFoundError(spaceTrim((block) => `
3672
3737
  Can not make source handler for file which does not exist:
3673
3738
 
3674
3739
  File:
@@ -3755,7 +3820,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
3755
3820
  // <- TODO: [🪓] Here should be no need for spreading new array, just `partialPieces = partialPiecesUnchecked`
3756
3821
  break;
3757
3822
  }
3758
- console.warn(spaceTrim$1((block) => `
3823
+ console.warn(spaceTrim((block) => `
3759
3824
  Cannot scrape knowledge from source despite the scraper \`${scraper.metadata.className}\` supports the mime type "${sourceHandler.mimeType}".
3760
3825
 
3761
3826
  The source:
@@ -3771,7 +3836,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
3771
3836
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
3772
3837
  }
3773
3838
  if (partialPieces === null) {
3774
- throw new KnowledgeScrapeError(spaceTrim$1((block) => `
3839
+ throw new KnowledgeScrapeError(spaceTrim((block) => `
3775
3840
  Cannot scrape knowledge
3776
3841
 
3777
3842
  The source:
@@ -3800,9 +3865,7 @@ async function prepareKnowledgePieces(knowledgeSources, tools, options) {
3800
3865
  knowledgePreparedUnflatten[index] = pieces;
3801
3866
  }
3802
3867
  catch (error) {
3803
- if (!(error instanceof Error)) {
3804
- throw error;
3805
- }
3868
+ assertsError(error);
3806
3869
  console.warn(error);
3807
3870
  // <- TODO: [🏮] Some standard way how to transform errors into warnings and how to handle non-critical fails during the tasks
3808
3871
  }
@@ -3849,7 +3912,7 @@ async function prepareTasks(pipeline, tools, options) {
3849
3912
  if (task.taskType === 'PROMPT_TASK' &&
3850
3913
  knowledgePiecesCount > 0 &&
3851
3914
  !dependentParameterNames.includes('knowledge')) {
3852
- preparedContent = spaceTrim(`
3915
+ preparedContent = spaceTrim$1(`
3853
3916
  {content}
3854
3917
 
3855
3918
  ## Knowledge
@@ -4094,13 +4157,19 @@ function valueToString(value) {
4094
4157
  return value.toISOString();
4095
4158
  }
4096
4159
  else {
4097
- return JSON.stringify(value);
4160
+ try {
4161
+ return JSON.stringify(value);
4162
+ }
4163
+ catch (error) {
4164
+ if (error instanceof TypeError && error.message.includes('circular structure')) {
4165
+ return VALUE_STRINGS.circular;
4166
+ }
4167
+ throw error;
4168
+ }
4098
4169
  }
4099
4170
  }
4100
4171
  catch (error) {
4101
- if (!(error instanceof Error)) {
4102
- throw error;
4103
- }
4172
+ assertsError(error);
4104
4173
  console.error(error);
4105
4174
  return VALUE_STRINGS.unserializable;
4106
4175
  }
@@ -4157,10 +4226,8 @@ function extractVariablesFromJavascript(script) {
4157
4226
  }
4158
4227
  }
4159
4228
  catch (error) {
4160
- if (!(error instanceof Error)) {
4161
- throw error;
4162
- }
4163
- throw new ParseError(spaceTrim((block) => `
4229
+ assertsError(error);
4230
+ throw new ParseError(spaceTrim$1((block) => `
4164
4231
  Can not extract variables from the script
4165
4232
  ${block(error.stack || error.message)}
4166
4233
 
@@ -4278,6 +4345,28 @@ const MANDATORY_CSV_SETTINGS = Object.freeze({
4278
4345
  // encoding: 'utf-8',
4279
4346
  });
4280
4347
 
4348
+ /**
4349
+ * Function to check if a string is valid CSV
4350
+ *
4351
+ * @param value The string to check
4352
+ * @returns True if the string is a valid CSV string, false otherwise
4353
+ *
4354
+ * @public exported from `@promptbook/utils`
4355
+ */
4356
+ function isValidCsvString(value) {
4357
+ try {
4358
+ // A simple check for CSV format: at least one comma and no invalid characters
4359
+ if (value.includes(',') && /^[\w\s,"']+$/.test(value)) {
4360
+ return true;
4361
+ }
4362
+ return false;
4363
+ }
4364
+ catch (error) {
4365
+ assertsError(error);
4366
+ return false;
4367
+ }
4368
+ }
4369
+
4281
4370
  /**
4282
4371
  * Definition for CSV spreadsheet
4283
4372
  *
@@ -4288,7 +4377,7 @@ const CsvFormatDefinition = {
4288
4377
  formatName: 'CSV',
4289
4378
  aliases: ['SPREADSHEET', 'TABLE'],
4290
4379
  isValid(value, settings, schema) {
4291
- return true;
4380
+ return isValidCsvString(value);
4292
4381
  },
4293
4382
  canBeValid(partialValue, settings, schema) {
4294
4383
  return true;
@@ -4303,7 +4392,7 @@ const CsvFormatDefinition = {
4303
4392
  // TODO: [👨🏾‍🤝‍👨🏼] DRY csv parsing
4304
4393
  const csv = parse(value, { ...settings, ...MANDATORY_CSV_SETTINGS });
4305
4394
  if (csv.errors.length !== 0) {
4306
- throw new CsvFormatError(spaceTrim$1((block) => `
4395
+ throw new CsvFormatError(spaceTrim((block) => `
4307
4396
  CSV parsing error
4308
4397
 
4309
4398
  Error(s) from CSV parsing:
@@ -4334,7 +4423,7 @@ const CsvFormatDefinition = {
4334
4423
  // TODO: [👨🏾‍🤝‍👨🏼] DRY csv parsing
4335
4424
  const csv = parse(value, { ...settings, ...MANDATORY_CSV_SETTINGS });
4336
4425
  if (csv.errors.length !== 0) {
4337
- throw new CsvFormatError(spaceTrim$1((block) => `
4426
+ throw new CsvFormatError(spaceTrim((block) => `
4338
4427
  CSV parsing error
4339
4428
 
4340
4429
  Error(s) from CSV parsing:
@@ -4442,6 +4531,30 @@ const TextFormatDefinition = {
4442
4531
  * TODO: [🏢] Allow to expect something inside each item of list and other formats
4443
4532
  */
4444
4533
 
4534
+ /**
4535
+ * Function to check if a string is valid XML
4536
+ *
4537
+ * @param value
4538
+ * @returns True if the string is a valid XML string, false otherwise
4539
+ *
4540
+ * @public exported from `@promptbook/utils`
4541
+ */
4542
+ function isValidXmlString(value) {
4543
+ try {
4544
+ const parser = new DOMParser();
4545
+ const parsedDocument = parser.parseFromString(value, 'application/xml');
4546
+ const parserError = parsedDocument.getElementsByTagName('parsererror');
4547
+ if (parserError.length > 0) {
4548
+ return false;
4549
+ }
4550
+ return true;
4551
+ }
4552
+ catch (error) {
4553
+ assertsError(error);
4554
+ return false;
4555
+ }
4556
+ }
4557
+
4445
4558
  /**
4446
4559
  * Definition for XML format
4447
4560
  *
@@ -4451,7 +4564,7 @@ const XmlFormatDefinition = {
4451
4564
  formatName: 'XML',
4452
4565
  mimeType: 'application/xml',
4453
4566
  isValid(value, settings, schema) {
4454
- return true;
4567
+ return isValidXmlString(value);
4455
4568
  },
4456
4569
  canBeValid(partialValue, settings, schema) {
4457
4570
  return true;
@@ -4524,7 +4637,7 @@ function mapAvailableToExpectedParameters(options) {
4524
4637
  }
4525
4638
  // Phase 2️⃣: Non-matching mapping
4526
4639
  if (expectedParameterNames.size !== availableParametersNames.size) {
4527
- throw new PipelineExecutionError(spaceTrim$1((block) => `
4640
+ throw new PipelineExecutionError(spaceTrim((block) => `
4528
4641
  Can not map available parameters to expected parameters
4529
4642
 
4530
4643
  Mapped parameters:
@@ -4926,7 +5039,7 @@ async function executeAttempts(options) {
4926
5039
  const jokerParameterName = jokerParameterNames[jokerParameterNames.length + attempt];
4927
5040
  // TODO: [🧠][🍭] JOKERS, EXPECTATIONS, POSTPROCESSING and FOREACH
4928
5041
  if (isJokerAttempt && !jokerParameterName) {
4929
- throw new UnexpectedError(spaceTrim((block) => `
5042
+ throw new UnexpectedError(spaceTrim$1((block) => `
4930
5043
  Joker not found in attempt ${attempt}
4931
5044
 
4932
5045
  ${block(pipelineIdentification)}
@@ -4937,7 +5050,7 @@ async function executeAttempts(options) {
4937
5050
  $ongoingTaskResult.$expectError = null;
4938
5051
  if (isJokerAttempt) {
4939
5052
  if (parameters[jokerParameterName] === undefined) {
4940
- throw new PipelineExecutionError(spaceTrim((block) => `
5053
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
4941
5054
  Joker parameter {${jokerParameterName}} not defined
4942
5055
 
4943
5056
  ${block(pipelineIdentification)}
@@ -4995,7 +5108,7 @@ async function executeAttempts(options) {
4995
5108
  $ongoingTaskResult.$resultString = $ongoingTaskResult.$completionResult.content;
4996
5109
  break variant;
4997
5110
  case 'EMBEDDING':
4998
- throw new PipelineExecutionError(spaceTrim((block) => `
5111
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
4999
5112
  Embedding model can not be used in pipeline
5000
5113
 
5001
5114
  This should be catched during parsing
@@ -5006,7 +5119,7 @@ async function executeAttempts(options) {
5006
5119
  break variant;
5007
5120
  // <- case [🤖]:
5008
5121
  default:
5009
- throw new PipelineExecutionError(spaceTrim((block) => `
5122
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
5010
5123
  Unknown model variant "${task.modelRequirements.modelVariant}"
5011
5124
 
5012
5125
  ${block(pipelineIdentification)}
@@ -5017,14 +5130,14 @@ async function executeAttempts(options) {
5017
5130
  break;
5018
5131
  case 'SCRIPT_TASK':
5019
5132
  if (arrayableToArray(tools.script).length === 0) {
5020
- throw new PipelineExecutionError(spaceTrim((block) => `
5133
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
5021
5134
  No script execution tools are available
5022
5135
 
5023
5136
  ${block(pipelineIdentification)}
5024
5137
  `));
5025
5138
  }
5026
5139
  if (!task.contentLanguage) {
5027
- throw new PipelineExecutionError(spaceTrim((block) => `
5140
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
5028
5141
  Script language is not defined for SCRIPT TASK "${task.name}"
5029
5142
 
5030
5143
  ${block(pipelineIdentification)}
@@ -5041,9 +5154,7 @@ async function executeAttempts(options) {
5041
5154
  break scripts;
5042
5155
  }
5043
5156
  catch (error) {
5044
- if (!(error instanceof Error)) {
5045
- throw error;
5046
- }
5157
+ assertsError(error);
5047
5158
  if (error instanceof UnexpectedError) {
5048
5159
  throw error;
5049
5160
  }
@@ -5057,7 +5168,7 @@ async function executeAttempts(options) {
5057
5168
  throw $ongoingTaskResult.$scriptPipelineExecutionErrors[0];
5058
5169
  }
5059
5170
  else {
5060
- throw new PipelineExecutionError(spaceTrim((block) => `
5171
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
5061
5172
  Script execution failed ${$ongoingTaskResult.$scriptPipelineExecutionErrors.length}x
5062
5173
 
5063
5174
  ${block(pipelineIdentification)}
@@ -5071,7 +5182,7 @@ async function executeAttempts(options) {
5071
5182
  break taskType;
5072
5183
  case 'DIALOG_TASK':
5073
5184
  if (tools.userInterface === undefined) {
5074
- throw new PipelineExecutionError(spaceTrim((block) => `
5185
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
5075
5186
  User interface tools are not available
5076
5187
 
5077
5188
  ${block(pipelineIdentification)}
@@ -5089,7 +5200,7 @@ async function executeAttempts(options) {
5089
5200
  break taskType;
5090
5201
  // <- case: [🅱]
5091
5202
  default:
5092
- throw new PipelineExecutionError(spaceTrim((block) => `
5203
+ throw new PipelineExecutionError(spaceTrim$1((block) => `
5093
5204
  Unknown execution type "${task.taskType}"
5094
5205
 
5095
5206
  ${block(pipelineIdentification)}
@@ -5113,9 +5224,7 @@ async function executeAttempts(options) {
5113
5224
  break scripts;
5114
5225
  }
5115
5226
  catch (error) {
5116
- if (!(error instanceof Error)) {
5117
- throw error;
5118
- }
5227
+ assertsError(error);
5119
5228
  if (error instanceof UnexpectedError) {
5120
5229
  throw error;
5121
5230
  }
@@ -5138,7 +5247,7 @@ async function executeAttempts(options) {
5138
5247
  }
5139
5248
  catch (error) {
5140
5249
  keepUnused(error);
5141
- throw new ExpectError(spaceTrim((block) => `
5250
+ throw new ExpectError(spaceTrim$1((block) => `
5142
5251
  Expected valid JSON string
5143
5252
 
5144
5253
  ${block(
@@ -5148,7 +5257,7 @@ async function executeAttempts(options) {
5148
5257
  }
5149
5258
  }
5150
5259
  else {
5151
- throw new UnexpectedError(spaceTrim((block) => `
5260
+ throw new UnexpectedError(spaceTrim$1((block) => `
5152
5261
  Unknown format "${task.format}"
5153
5262
 
5154
5263
  ${block(pipelineIdentification)}
@@ -5188,7 +5297,7 @@ async function executeAttempts(options) {
5188
5297
  }
5189
5298
  }
5190
5299
  if ($ongoingTaskResult.$expectError !== null && attempt === maxAttempts - 1) {
5191
- throw new PipelineExecutionError(spaceTrim((block) => {
5300
+ throw new PipelineExecutionError(spaceTrim$1((block) => {
5192
5301
  var _a, _b, _c;
5193
5302
  return `
5194
5303
  LLM execution failed ${maxExecutionAttempts}x
@@ -5211,7 +5320,7 @@ async function executeAttempts(options) {
5211
5320
  Last result:
5212
5321
  ${block($ongoingTaskResult.$resultString === null
5213
5322
  ? 'null'
5214
- : spaceTrim($ongoingTaskResult.$resultString)
5323
+ : spaceTrim$1($ongoingTaskResult.$resultString)
5215
5324
  .split('\n')
5216
5325
  .map((line) => `> ${line}`)
5217
5326
  .join('\n'))}
@@ -5221,7 +5330,7 @@ async function executeAttempts(options) {
5221
5330
  }
5222
5331
  }
5223
5332
  if ($ongoingTaskResult.$resultString === null) {
5224
- throw new UnexpectedError(spaceTrim((block) => `
5333
+ throw new UnexpectedError(spaceTrim$1((block) => `
5225
5334
  Something went wrong and prompt result is null
5226
5335
 
5227
5336
  ${block(pipelineIdentification)}
@@ -5244,7 +5353,7 @@ async function executeFormatSubvalues(options) {
5244
5353
  return /* not await */ executeAttempts(options);
5245
5354
  }
5246
5355
  if (jokerParameterNames.length !== 0) {
5247
- throw new UnexpectedError(spaceTrim$1((block) => `
5356
+ throw new UnexpectedError(spaceTrim((block) => `
5248
5357
  JOKER parameters are not supported together with FOREACH command
5249
5358
 
5250
5359
  [🧞‍♀️] This should be prevented in \`validatePipeline\`
@@ -5257,7 +5366,7 @@ async function executeFormatSubvalues(options) {
5257
5366
  if (formatDefinition === undefined) {
5258
5367
  throw new UnexpectedError(
5259
5368
  // <- TODO: [🧠][🧐] Should be formats fixed per promptbook version or behave as plugins (=> change UnexpectedError)
5260
- spaceTrim$1((block) => `
5369
+ spaceTrim((block) => `
5261
5370
  Unsupported format "${task.foreach.formatName}"
5262
5371
 
5263
5372
  Available formats:
@@ -5274,7 +5383,7 @@ async function executeFormatSubvalues(options) {
5274
5383
  if (subvalueDefinition === undefined) {
5275
5384
  throw new UnexpectedError(
5276
5385
  // <- TODO: [🧠][🧐] Should be formats fixed per promptbook version or behave as plugins (=> change UnexpectedError)
5277
- spaceTrim$1((block) => `
5386
+ spaceTrim((block) => `
5278
5387
  Unsupported subformat name "${task.foreach.subformatName}" for format "${task.foreach.formatName}"
5279
5388
 
5280
5389
  Available subformat names for format "${formatDefinition.formatName}":
@@ -5307,7 +5416,7 @@ async function executeFormatSubvalues(options) {
5307
5416
  if (!(error instanceof PipelineExecutionError)) {
5308
5417
  throw error;
5309
5418
  }
5310
- throw new PipelineExecutionError(spaceTrim$1((block) => `
5419
+ throw new PipelineExecutionError(spaceTrim((block) => `
5311
5420
  ${error.message}
5312
5421
 
5313
5422
  This is error in FOREACH command
@@ -5327,7 +5436,7 @@ async function executeFormatSubvalues(options) {
5327
5436
  ...options,
5328
5437
  priority: priority + index,
5329
5438
  parameters: allSubparameters,
5330
- pipelineIdentification: spaceTrim$1((block) => `
5439
+ pipelineIdentification: spaceTrim((block) => `
5331
5440
  ${block(pipelineIdentification)}
5332
5441
  Subparameter index: ${index}
5333
5442
  `),
@@ -5389,7 +5498,7 @@ async function getReservedParametersForTask(options) {
5389
5498
  // Note: Doublecheck that ALL reserved parameters are defined:
5390
5499
  for (const parameterName of RESERVED_PARAMETER_NAMES) {
5391
5500
  if (reservedParameters[parameterName] === undefined) {
5392
- throw new UnexpectedError(spaceTrim((block) => `
5501
+ throw new UnexpectedError(spaceTrim$1((block) => `
5393
5502
  Reserved parameter {${parameterName}} is not defined
5394
5503
 
5395
5504
  ${block(pipelineIdentification)}
@@ -5417,7 +5526,7 @@ async function executeTask(options) {
5417
5526
  const dependentParameterNames = new Set(currentTask.dependentParameterNames);
5418
5527
  // TODO: [👩🏾‍🤝‍👩🏻] Use here `mapAvailableToExpectedParameters`
5419
5528
  if (union(difference(usedParameterNames, dependentParameterNames), difference(dependentParameterNames, usedParameterNames)).size !== 0) {
5420
- throw new UnexpectedError(spaceTrim((block) => `
5529
+ throw new UnexpectedError(spaceTrim$1((block) => `
5421
5530
  Dependent parameters are not consistent with used parameters:
5422
5531
 
5423
5532
  Dependent parameters:
@@ -5457,7 +5566,7 @@ async function executeTask(options) {
5457
5566
  else if (!definedParameterNames.has(parameterName) && usedParameterNames.has(parameterName)) {
5458
5567
  // Houston, we have a problem
5459
5568
  // Note: Checking part is also done in `validatePipeline`, but it’s good to doublecheck
5460
- throw new UnexpectedError(spaceTrim((block) => `
5569
+ throw new UnexpectedError(spaceTrim$1((block) => `
5461
5570
  Parameter \`{${parameterName}}\` is NOT defined
5462
5571
  BUT used in task "${currentTask.title || currentTask.name}"
5463
5572
 
@@ -5523,7 +5632,7 @@ function filterJustOutputParameters(options) {
5523
5632
  for (const parameter of preparedPipeline.parameters.filter(({ isOutput }) => isOutput)) {
5524
5633
  if (parametersToPass[parameter.name] === undefined) {
5525
5634
  // [4]
5526
- $warnings.push(new PipelineExecutionError(spaceTrim((block) => `
5635
+ $warnings.push(new PipelineExecutionError(spaceTrim$1((block) => `
5527
5636
  Parameter \`{${parameter.name}}\` should be an output parameter, but it was not generated during pipeline execution
5528
5637
 
5529
5638
  ${block(pipelineIdentification)}
@@ -5598,7 +5707,7 @@ async function executePipeline(options) {
5598
5707
  for (const parameterName of Object.keys(inputParameters)) {
5599
5708
  const parameter = preparedPipeline.parameters.find(({ name }) => name === parameterName);
5600
5709
  if (parameter === undefined) {
5601
- warnings.push(new PipelineExecutionError(spaceTrim((block) => `
5710
+ warnings.push(new PipelineExecutionError(spaceTrim$1((block) => `
5602
5711
  Extra parameter {${parameterName}} is being passed which is not part of the pipeline.
5603
5712
 
5604
5713
  ${block(pipelineIdentification)}
@@ -5613,7 +5722,7 @@ async function executePipeline(options) {
5613
5722
  // TODO: [🧠] This should be also non-critical error
5614
5723
  return exportJson({
5615
5724
  name: 'pipelineExecutorResult',
5616
- message: spaceTrim((block) => `
5725
+ message: spaceTrim$1((block) => `
5617
5726
  Unuccessful PipelineExecutorResult (with extra parameter {${parameter.name}}) PipelineExecutorResult
5618
5727
 
5619
5728
  ${block(pipelineIdentification)}
@@ -5622,7 +5731,7 @@ async function executePipeline(options) {
5622
5731
  value: {
5623
5732
  isSuccessful: false,
5624
5733
  errors: [
5625
- new PipelineExecutionError(spaceTrim((block) => `
5734
+ new PipelineExecutionError(spaceTrim$1((block) => `
5626
5735
  Parameter \`{${parameter.name}}\` is passed as input parameter but it is not input
5627
5736
 
5628
5737
  ${block(pipelineIdentification)}
@@ -5649,7 +5758,7 @@ async function executePipeline(options) {
5649
5758
  while (unresovedTasks.length > 0) {
5650
5759
  if (loopLimit-- < 0) {
5651
5760
  // Note: Really UnexpectedError not LimitReachedError - this should be catched during validatePipeline
5652
- throw new UnexpectedError(spaceTrim((block) => `
5761
+ throw new UnexpectedError(spaceTrim$1((block) => `
5653
5762
  Loop limit reached during resolving parameters pipeline execution
5654
5763
 
5655
5764
  ${block(pipelineIdentification)}
@@ -5659,7 +5768,7 @@ async function executePipeline(options) {
5659
5768
  if (!currentTask && resolving.length === 0) {
5660
5769
  throw new UnexpectedError(
5661
5770
  // TODO: [🐎] DRY
5662
- spaceTrim((block) => `
5771
+ spaceTrim$1((block) => `
5663
5772
  Can not resolve some parameters:
5664
5773
 
5665
5774
  ${block(pipelineIdentification)}
@@ -5699,7 +5808,7 @@ async function executePipeline(options) {
5699
5808
  tools,
5700
5809
  onProgress(newOngoingResult) {
5701
5810
  if (isReturned) {
5702
- throw new UnexpectedError(spaceTrim((block) => `
5811
+ throw new UnexpectedError(spaceTrim$1((block) => `
5703
5812
  Can not call \`onProgress\` after pipeline execution is finished
5704
5813
 
5705
5814
  ${block(pipelineIdentification)}
@@ -5715,7 +5824,7 @@ async function executePipeline(options) {
5715
5824
  }
5716
5825
  },
5717
5826
  $executionReport: executionReport,
5718
- pipelineIdentification: spaceTrim((block) => `
5827
+ pipelineIdentification: spaceTrim$1((block) => `
5719
5828
  ${block(pipelineIdentification)}
5720
5829
  Task name: ${currentTask.name}
5721
5830
  Task title: ${currentTask.title}
@@ -5736,9 +5845,7 @@ async function executePipeline(options) {
5736
5845
  await Promise.all(resolving);
5737
5846
  }
5738
5847
  catch (error /* <- Note: [3] */) {
5739
- if (!(error instanceof Error)) {
5740
- throw error;
5741
- }
5848
+ assertsError(error);
5742
5849
  // Note: No need to rethrow UnexpectedError
5743
5850
  // if (error instanceof UnexpectedError) {
5744
5851
  // Note: Count usage, [🧠] Maybe put to separate function executionReportJsonToUsage + DRY [🤹‍♂️]
@@ -5826,7 +5933,7 @@ function createPipelineExecutor(options) {
5826
5933
  preparedPipeline = pipeline;
5827
5934
  }
5828
5935
  else if (isNotPreparedWarningSupressed !== true) {
5829
- console.warn(spaceTrim((block) => `
5936
+ console.warn(spaceTrim$1((block) => `
5830
5937
  Pipeline is not prepared
5831
5938
 
5832
5939
  ${block(pipelineIdentification)}
@@ -5850,7 +5957,7 @@ function createPipelineExecutor(options) {
5850
5957
  inputParameters,
5851
5958
  tools,
5852
5959
  onProgress,
5853
- pipelineIdentification: spaceTrim((block) => `
5960
+ pipelineIdentification: spaceTrim$1((block) => `
5854
5961
  ${block(pipelineIdentification)}
5855
5962
  ${runCount === 1 ? '' : `Run #${runCount}`}
5856
5963
  `),
@@ -5948,13 +6055,13 @@ function $registeredLlmToolsMessage() {
5948
6055
  });
5949
6056
  const usedEnvMessage = `Unknown \`.env\` file` ;
5950
6057
  if (metadata.length === 0) {
5951
- return spaceTrim$1((block) => `
6058
+ return spaceTrim((block) => `
5952
6059
  No LLM providers are available.
5953
6060
 
5954
6061
  ${block(usedEnvMessage)}
5955
6062
  `);
5956
6063
  }
5957
- return spaceTrim$1((block) => `
6064
+ return spaceTrim((block) => `
5958
6065
 
5959
6066
  ${block(usedEnvMessage)}
5960
6067
 
@@ -6000,7 +6107,7 @@ function $registeredLlmToolsMessage() {
6000
6107
  morePieces.push(`Not configured`); // <- Note: Can not be configured via environment variables
6001
6108
  }
6002
6109
  }
6003
- let providerMessage = spaceTrim$1(`
6110
+ let providerMessage = spaceTrim(`
6004
6111
  ${i + 1}) **${title}** \`${className}\` from \`${packageName}\`
6005
6112
  ${morePieces.join('; ')}
6006
6113
  `);
@@ -6040,7 +6147,7 @@ function createLlmToolsFromConfiguration(configuration, options = {}) {
6040
6147
  .list()
6041
6148
  .find(({ packageName, className }) => llmConfiguration.packageName === packageName && llmConfiguration.className === className);
6042
6149
  if (registeredItem === undefined) {
6043
- throw new Error(spaceTrim$1((block) => `
6150
+ throw new Error(spaceTrim((block) => `
6044
6151
  There is no constructor for LLM provider \`${llmConfiguration.className}\` from \`${llmConfiguration.packageName}\`
6045
6152
 
6046
6153
  You have probably forgotten install and import the provider package.
@@ -6324,13 +6431,13 @@ function removeQuotes(text) {
6324
6431
  * @public exported from `@promptbook/utils`
6325
6432
  */
6326
6433
  function trimCodeBlock(value) {
6327
- value = spaceTrim(value);
6434
+ value = spaceTrim$1(value);
6328
6435
  if (!/^```[a-z]*(.*)```$/is.test(value)) {
6329
6436
  return value;
6330
6437
  }
6331
6438
  value = value.replace(/^```[a-z]*/i, '');
6332
6439
  value = value.replace(/```$/i, '');
6333
- value = spaceTrim(value);
6440
+ value = spaceTrim$1(value);
6334
6441
  return value;
6335
6442
  }
6336
6443
 
@@ -6343,9 +6450,9 @@ function trimCodeBlock(value) {
6343
6450
  * @public exported from `@promptbook/utils`
6344
6451
  */
6345
6452
  function trimEndOfCodeBlock(value) {
6346
- value = spaceTrim(value);
6453
+ value = spaceTrim$1(value);
6347
6454
  value = value.replace(/```$/g, '');
6348
- value = spaceTrim(value);
6455
+ value = spaceTrim$1(value);
6349
6456
  return value;
6350
6457
  }
6351
6458
 
@@ -6367,7 +6474,7 @@ function unwrapResult(text, options) {
6367
6474
  let trimmedText = text;
6368
6475
  // Remove leading and trailing spaces and newlines
6369
6476
  if (isTrimmed) {
6370
- trimmedText = spaceTrim(trimmedText);
6477
+ trimmedText = spaceTrim$1(trimmedText);
6371
6478
  }
6372
6479
  let processedText = trimmedText;
6373
6480
  if (isIntroduceSentenceRemoved) {
@@ -6376,7 +6483,7 @@ function unwrapResult(text, options) {
6376
6483
  // Remove the introduce sentence and quotes by replacing it with an empty string
6377
6484
  processedText = processedText.replace(introduceSentenceRegex, '');
6378
6485
  }
6379
- processedText = spaceTrim(processedText);
6486
+ processedText = spaceTrim$1(processedText);
6380
6487
  }
6381
6488
  if (processedText.length < 3) {
6382
6489
  return trimmedText;
@@ -6439,7 +6546,7 @@ function unwrapResult(text, options) {
6439
6546
  function extractOneBlockFromMarkdown(markdown) {
6440
6547
  const codeBlocks = extractAllBlocksFromMarkdown(markdown);
6441
6548
  if (codeBlocks.length !== 1) {
6442
- throw new ParseError(spaceTrim$1((block) => `
6549
+ throw new ParseError(spaceTrim((block) => `
6443
6550
  There should be exactly 1 code block in task section, found ${codeBlocks.length} code blocks
6444
6551
 
6445
6552
  ${block(codeBlocks.map((block, i) => `Block ${i + 1}:\n${block.content}`).join('\n\n\n'))}
@@ -6521,8 +6628,8 @@ class JavascriptEvalExecutionTools {
6521
6628
  }
6522
6629
  // Note: [💎]
6523
6630
  // Note: Using direct eval, following variables are in same scope as eval call so they are accessible from inside the evaluated script:
6524
- const spaceTrim = (_) => spaceTrim$1(_);
6525
- preserve(spaceTrim);
6631
+ const spaceTrim$1 = (_) => spaceTrim(_);
6632
+ preserve(spaceTrim$1);
6526
6633
  const removeQuotes$1 = removeQuotes;
6527
6634
  preserve(removeQuotes$1);
6528
6635
  const unwrapResult$1 = unwrapResult;
@@ -6575,7 +6682,7 @@ class JavascriptEvalExecutionTools {
6575
6682
  // TODO: DRY [🍯]
6576
6683
  const buildinFunctions = {
6577
6684
  // TODO: [🍯] DRY all these functions across the file
6578
- spaceTrim,
6685
+ spaceTrim: spaceTrim$1,
6579
6686
  removeQuotes: removeQuotes$1,
6580
6687
  unwrapResult: unwrapResult$1,
6581
6688
  trimEndOfCodeBlock: trimEndOfCodeBlock$1,
@@ -6612,7 +6719,7 @@ class JavascriptEvalExecutionTools {
6612
6719
  .join('\n');
6613
6720
  // script = templateParameters(script, parameters);
6614
6721
  // <- TODO: [🧠][🥳] Should be this is one of two variants how to use parameters in script
6615
- const statementToEvaluate = spaceTrim$1((block) => `
6722
+ const statementToEvaluate = spaceTrim((block) => `
6616
6723
 
6617
6724
  // Build-in functions:
6618
6725
  ${block(buildinFunctionsStatement)}
@@ -6627,7 +6734,7 @@ class JavascriptEvalExecutionTools {
6627
6734
  (()=>{ ${script} })()
6628
6735
  `);
6629
6736
  if (this.options.isVerbose) {
6630
- console.info(spaceTrim$1((block) => `
6737
+ console.info(spaceTrim((block) => `
6631
6738
  🚀 Evaluating ${scriptLanguage} script:
6632
6739
 
6633
6740
  ${block(statementToEvaluate)}`));
@@ -6640,9 +6747,7 @@ class JavascriptEvalExecutionTools {
6640
6747
  }
6641
6748
  }
6642
6749
  catch (error) {
6643
- if (!(error instanceof Error)) {
6644
- throw error;
6645
- }
6750
+ assertsError(error);
6646
6751
  if (error instanceof ReferenceError) {
6647
6752
  const undefinedName = error.message.split(' ')[0];
6648
6753
  /*
@@ -6651,7 +6756,7 @@ class JavascriptEvalExecutionTools {
6651
6756
  To: [PipelineExecutionError: Parameter `{thing}` is not defined],
6652
6757
  */
6653
6758
  if (!statementToEvaluate.includes(undefinedName + '(')) {
6654
- throw new PipelineExecutionError(spaceTrim$1((block) => `
6759
+ throw new PipelineExecutionError(spaceTrim((block) => `
6655
6760
 
6656
6761
  Parameter \`{${undefinedName}}\` is not defined
6657
6762
 
@@ -6673,7 +6778,7 @@ class JavascriptEvalExecutionTools {
6673
6778
  `));
6674
6779
  }
6675
6780
  else {
6676
- throw new PipelineExecutionError(spaceTrim$1((block) => `
6781
+ throw new PipelineExecutionError(spaceTrim((block) => `
6677
6782
  Function ${undefinedName}() is not defined
6678
6783
 
6679
6784
  - Make sure that the function is one of built-in functions
@@ -6722,6 +6827,198 @@ async function $provideScriptingForNode(options) {
6722
6827
  * Note: [🟢] Code in this file should never be never released in packages that could be imported into browser environment
6723
6828
  */
6724
6829
 
6830
+ // TODO: !!!! List running services from REMOTE_SERVER_URLS
6831
+ // TODO: !!!! Import directly from YML
6832
+ /**
6833
+ * @private !!!! Decide how to expose this
6834
+ */
6835
+ const openapiJson = {
6836
+ openapi: '3.0.0',
6837
+ info: {
6838
+ title: 'Promptbook Remote Server API (!!!! From TS)',
6839
+ version: '1.0.0',
6840
+ description: 'API documentation for the Promptbook Remote Server',
6841
+ },
6842
+ paths: {
6843
+ '/': {
6844
+ get: {
6845
+ summary: 'Get server details',
6846
+ description: 'Returns details about the Promptbook server.',
6847
+ responses: {
6848
+ '200': {
6849
+ description: 'Server details in markdown format.',
6850
+ },
6851
+ },
6852
+ },
6853
+ },
6854
+ '/login': {
6855
+ post: {
6856
+ summary: 'Login to the server',
6857
+ description: 'Login to the server and get identification.',
6858
+ requestBody: {
6859
+ required: true,
6860
+ content: {
6861
+ 'application/json': {
6862
+ schema: {
6863
+ type: 'object',
6864
+ properties: {
6865
+ username: {
6866
+ type: 'string',
6867
+ },
6868
+ password: {
6869
+ type: 'string',
6870
+ },
6871
+ appId: {
6872
+ type: 'string',
6873
+ },
6874
+ },
6875
+ },
6876
+ },
6877
+ },
6878
+ },
6879
+ responses: {
6880
+ '200': {
6881
+ description: 'Successful login',
6882
+ content: {
6883
+ 'application/json': {
6884
+ schema: {
6885
+ type: 'object',
6886
+ properties: {
6887
+ identification: {
6888
+ type: 'object',
6889
+ },
6890
+ },
6891
+ },
6892
+ },
6893
+ },
6894
+ },
6895
+ },
6896
+ },
6897
+ },
6898
+ '/books': {
6899
+ get: {
6900
+ summary: 'List all books',
6901
+ description: 'Returns a list of all available books in the collection.',
6902
+ responses: {
6903
+ '200': {
6904
+ description: 'A list of books.',
6905
+ content: {
6906
+ 'application/json': {
6907
+ schema: {
6908
+ type: 'array',
6909
+ items: {
6910
+ type: 'string',
6911
+ },
6912
+ },
6913
+ },
6914
+ },
6915
+ },
6916
+ },
6917
+ },
6918
+ },
6919
+ '/books/{bookId}': {
6920
+ get: {
6921
+ summary: 'Get book content',
6922
+ description: 'Returns the content of a specific book.',
6923
+ parameters: [
6924
+ {
6925
+ in: 'path',
6926
+ name: 'bookId',
6927
+ required: true,
6928
+ schema: {
6929
+ type: 'string',
6930
+ },
6931
+ description: 'The ID of the book to retrieve.',
6932
+ },
6933
+ ],
6934
+ responses: {
6935
+ '200': {
6936
+ description: 'The content of the book.',
6937
+ content: {
6938
+ 'text/markdown': {
6939
+ schema: {
6940
+ type: 'string',
6941
+ },
6942
+ },
6943
+ },
6944
+ },
6945
+ '404': {
6946
+ description: 'Book not found.',
6947
+ },
6948
+ },
6949
+ },
6950
+ },
6951
+ '/executions': {
6952
+ get: {
6953
+ summary: 'List all executions',
6954
+ description: 'Returns a list of all running execution tasks.',
6955
+ responses: {
6956
+ '200': {
6957
+ description: 'A list of execution tasks.',
6958
+ content: {
6959
+ 'application/json': {
6960
+ schema: {
6961
+ type: 'array',
6962
+ items: {
6963
+ type: 'object',
6964
+ },
6965
+ },
6966
+ },
6967
+ },
6968
+ },
6969
+ },
6970
+ },
6971
+ },
6972
+ '/executions/new': {
6973
+ post: {
6974
+ summary: 'Start a new execution',
6975
+ description: 'Starts a new execution task for a given pipeline.',
6976
+ requestBody: {
6977
+ required: true,
6978
+ content: {
6979
+ 'application/json': {
6980
+ schema: {
6981
+ type: 'object',
6982
+ properties: {
6983
+ pipelineUrl: {
6984
+ type: 'string',
6985
+ },
6986
+ inputParameters: {
6987
+ type: 'object',
6988
+ },
6989
+ identification: {
6990
+ type: 'object',
6991
+ },
6992
+ },
6993
+ },
6994
+ },
6995
+ },
6996
+ },
6997
+ responses: {
6998
+ '200': {
6999
+ description: 'The newly created execution task.',
7000
+ content: {
7001
+ 'application/json': {
7002
+ schema: {
7003
+ type: 'object',
7004
+ },
7005
+ },
7006
+ },
7007
+ },
7008
+ '400': {
7009
+ description: 'Invalid input.',
7010
+ },
7011
+ },
7012
+ },
7013
+ },
7014
+ },
7015
+ components: {},
7016
+ tags: [],
7017
+ };
7018
+ /**
7019
+ * Note: [💞] Ignore a discrepancy between file name and entity name
7020
+ */
7021
+
6725
7022
  /**
6726
7023
  * Remote server is a proxy server that uses its execution tools internally and exposes the executor interface externally.
6727
7024
  *
@@ -6732,7 +7029,7 @@ async function $provideScriptingForNode(options) {
6732
7029
  * @public exported from `@promptbook/remote-server`
6733
7030
  */
6734
7031
  function startRemoteServer(options) {
6735
- const { port, collection, createLlmExecutionTools, isAnonymousModeAllowed, isApplicationModeAllowed, isVerbose = DEFAULT_IS_VERBOSE, login, } = {
7032
+ const { port, collection, createLlmExecutionTools, createExecutionTools, isAnonymousModeAllowed, isApplicationModeAllowed, isVerbose = DEFAULT_IS_VERBOSE, login, } = {
6736
7033
  isAnonymousModeAllowed: false,
6737
7034
  isApplicationModeAllowed: false,
6738
7035
  collection: null,
@@ -6740,22 +7037,6 @@ function startRemoteServer(options) {
6740
7037
  login: null,
6741
7038
  ...options,
6742
7039
  };
6743
- // <- TODO: [🦪] Some helper type to be able to use discriminant union types with destructuring
6744
- let { rootPath = '/' } = options;
6745
- if (!rootPath.startsWith('/')) {
6746
- rootPath = `/${rootPath}`;
6747
- } /* not else */
6748
- if (rootPath.endsWith('/')) {
6749
- rootPath = rootPath.slice(0, -1);
6750
- } /* not else */
6751
- if (rootPath === '/') {
6752
- rootPath = '';
6753
- }
6754
- const socketioPath = '/' +
6755
- `${rootPath}/socket.io`
6756
- .split('/')
6757
- .filter((part) => part !== '')
6758
- .join('/');
6759
7040
  const startupDate = new Date();
6760
7041
  async function getExecutionToolsFromIdentification(identification) {
6761
7042
  if (identification === null || identification === undefined) {
@@ -6778,23 +7059,25 @@ function startRemoteServer(options) {
6778
7059
  }
6779
7060
  else if (isAnonymous === false && createLlmExecutionTools !== null) {
6780
7061
  // Note: Application mode
6781
- const { appId, userId, customOptions } = identification;
6782
- llm = await createLlmExecutionTools({
6783
- appId,
6784
- userId,
6785
- customOptions,
6786
- });
7062
+ llm = await createLlmExecutionTools(identification);
6787
7063
  }
6788
7064
  else {
6789
7065
  throw new PipelineExecutionError(`You must provide either llmToolsConfiguration or non-anonymous mode must be propperly configured`);
6790
7066
  }
6791
- const fs = $provideFilesystemForNode();
6792
- const executables = await $provideExecutablesForNode();
7067
+ const customExecutionTools = createExecutionTools ? await createExecutionTools(identification) : {};
7068
+ const fs = customExecutionTools.fs || $provideFilesystemForNode();
7069
+ const executables = customExecutionTools.executables || (await $provideExecutablesForNode());
7070
+ const scrapers = customExecutionTools.scrapers || (await $provideScrapersForNode({ fs, llm, executables }));
7071
+ const script = customExecutionTools.script || (await $provideScriptingForNode({}));
7072
+ const fetch = customExecutionTools.fetch || promptbookFetch;
7073
+ const userInterface = customExecutionTools.userInterface || undefined;
6793
7074
  const tools = {
6794
7075
  llm,
6795
7076
  fs,
6796
- scrapers: await $provideScrapersForNode({ fs, llm, executables }),
6797
- script: await $provideScriptingForNode({}),
7077
+ scrapers,
7078
+ script,
7079
+ fetch,
7080
+ userInterface,
6798
7081
  };
6799
7082
  return tools;
6800
7083
  }
@@ -6804,44 +7087,32 @@ function startRemoteServer(options) {
6804
7087
  response.setHeader('X-Powered-By', 'Promptbook engine');
6805
7088
  next();
6806
7089
  });
6807
- const swaggerOptions = {
6808
- definition: {
6809
- openapi: '3.0.0',
6810
- info: {
6811
- title: 'Promptbook Remote Server API',
6812
- version: '1.0.0',
6813
- description: 'API documentation for the Promptbook Remote Server',
6814
- },
6815
- servers: [
6816
- {
6817
- url: `http://localhost:${port}${rootPath}`,
6818
- // <- TODO: !!!!! Probbably: Pass `remoteServerUrl` instead of `port` and `rootPath`
6819
- },
6820
- ],
7090
+ // TODO: !!!! Expose openapiJson to consumer and also allow to add new routes
7091
+ app.use(OpenApiValidator.middleware({
7092
+ apiSpec: openapiJson,
7093
+ ignorePaths(path) {
7094
+ return path.startsWith('/api-docs') || path.startsWith('/swagger') || path.startsWith('/openapi');
6821
7095
  },
6822
- apis: ['./src/remote-server/**/*.ts'], // Adjust path as needed
6823
- };
6824
- const swaggerSpec = swaggerJsdoc(swaggerOptions);
6825
- app.use([`/api-docs`, `${rootPath}/api-docs`], swaggerUi.serve, swaggerUi.setup(swaggerSpec));
7096
+ validateRequests: true,
7097
+ validateResponses: true,
7098
+ }));
7099
+ app.use([`/api-docs`, `/swagger`], swaggerUi.serve, swaggerUi.setup(openapiJson, {
7100
+ // customCss: '.swagger-ui .topbar { display: none }',
7101
+ // customSiteTitle: 'BRJ API',
7102
+ // customfavIcon: 'https://brj.app/favicon.ico',
7103
+ }));
7104
+ app.get(`/openapi`, (request, response) => {
7105
+ response.json(openapiJson);
7106
+ });
6826
7107
  const runningExecutionTasks = [];
6827
7108
  // <- TODO: [🤬] Identify the users
6828
7109
  // TODO: [🧠] Do here some garbage collection of finished tasks
6829
- /**
6830
- * @swagger
6831
- * /:
6832
- * get:
6833
- * summary: Get server details
6834
- * description: Returns details about the Promptbook server.
6835
- * responses:
6836
- * 200:
6837
- * description: Server details in markdown format.
6838
- */
6839
- app.get(['/', rootPath], async (request, response) => {
7110
+ app.get('/', async (request, response) => {
6840
7111
  var _a;
6841
7112
  if ((_a = request.url) === null || _a === void 0 ? void 0 : _a.includes('socket.io')) {
6842
7113
  return;
6843
7114
  }
6844
- response.type('text/markdown').send(await spaceTrim(async (block) => `
7115
+ response.type('text/markdown').send(await spaceTrim$1(async (block) => `
6845
7116
  # Promptbook
6846
7117
 
6847
7118
  > ${block(CLAIM)}
@@ -6855,8 +7126,6 @@ function startRemoteServer(options) {
6855
7126
  ## Details
6856
7127
 
6857
7128
  **Server port:** ${port}
6858
- **Server root path:** ${rootPath}
6859
- **Socket.io path:** ${socketioPath}
6860
7129
  **Startup date:** ${startupDate.toISOString()}
6861
7130
  **Anonymouse mode:** ${isAnonymousModeAllowed ? 'enabled' : 'disabled'}
6862
7131
  **Application mode:** ${isApplicationModeAllowed ? 'enabled' : 'disabled'}
@@ -6895,38 +7164,7 @@ function startRemoteServer(options) {
6895
7164
  https://github.com/webgptorg/promptbook
6896
7165
  `));
6897
7166
  });
6898
- /**
6899
- * @swagger
6900
- *
6901
- * /login:
6902
- * post:
6903
- * summary: Login to the server
6904
- * description: Login to the server and get identification.
6905
- * requestBody:
6906
- * required: true
6907
- * content:
6908
- * application/json:
6909
- * schema:
6910
- * type: object
6911
- * properties:
6912
- * username:
6913
- * type: string
6914
- * password:
6915
- * type: string
6916
- * appId:
6917
- * type: string
6918
- * responses:
6919
- * 200:
6920
- * description: Successful login
6921
- * content:
6922
- * application/json:
6923
- * schema:
6924
- * type: object
6925
- * properties:
6926
- * identification:
6927
- * type: object
6928
- */
6929
- app.post([`/login`, `${rootPath}/login`], async (request, response) => {
7167
+ app.post(`/login`, async (request, response) => {
6930
7168
  if (!isApplicationModeAllowed || login === null) {
6931
7169
  response.status(400).send('Application mode is not allowed');
6932
7170
  return;
@@ -6951,9 +7189,7 @@ function startRemoteServer(options) {
6951
7189
  return;
6952
7190
  }
6953
7191
  catch (error) {
6954
- if (!(error instanceof Error)) {
6955
- throw error;
6956
- }
7192
+ assertsError(error);
6957
7193
  if (error instanceof AuthenticationError) {
6958
7194
  response.status(401).send({
6959
7195
  isSuccess: false,
@@ -6968,23 +7204,7 @@ function startRemoteServer(options) {
6968
7204
  response.status(400).send({ error: serializeError(error) });
6969
7205
  }
6970
7206
  });
6971
- /**
6972
- * @swagger
6973
- * /books:
6974
- * get:
6975
- * summary: List all books
6976
- * description: Returns a list of all available books in the collection.
6977
- * responses:
6978
- * 200:
6979
- * description: A list of books.
6980
- * content:
6981
- * application/json:
6982
- * schema:
6983
- * type: array
6984
- * items:
6985
- * type: string
6986
- */
6987
- app.get([`/books`, `${rootPath}/books`], async (request, response) => {
7207
+ app.get(`/books`, async (request, response) => {
6988
7208
  if (collection === null) {
6989
7209
  response.status(500).send('No collection available');
6990
7210
  return;
@@ -6994,30 +7214,7 @@ function startRemoteServer(options) {
6994
7214
  response.send(pipelines);
6995
7215
  });
6996
7216
  // TODO: [🧠] Is it secure / good idea to expose source codes of hosted books
6997
- /**
6998
- * @swagger
6999
- * /books/{bookId}:
7000
- * get:
7001
- * summary: Get book content
7002
- * description: Returns the content of a specific book.
7003
- * parameters:
7004
- * - in: path
7005
- * name: bookId
7006
- * required: true
7007
- * schema:
7008
- * type: string
7009
- * description: The ID of the book to retrieve.
7010
- * responses:
7011
- * 200:
7012
- * description: The content of the book.
7013
- * content:
7014
- * text/markdown:
7015
- * schema:
7016
- * type: string
7017
- * 404:
7018
- * description: Book not found.
7019
- */
7020
- app.get([`/books/*`, `${rootPath}/books/*`], async (request, response) => {
7217
+ app.get(`/books/*`, async (request, response) => {
7021
7218
  try {
7022
7219
  if (collection === null) {
7023
7220
  response.status(500).send('No collection nor books available');
@@ -7036,9 +7233,7 @@ function startRemoteServer(options) {
7036
7233
  .send(source.content);
7037
7234
  }
7038
7235
  catch (error) {
7039
- if (!(error instanceof Error)) {
7040
- throw error;
7041
- }
7236
+ assertsError(error);
7042
7237
  response
7043
7238
  .status(404)
7044
7239
  .send({ error: serializeError(error) });
@@ -7071,26 +7266,10 @@ function startRemoteServer(options) {
7071
7266
  };
7072
7267
  }
7073
7268
  }
7074
- /**
7075
- * @swagger
7076
- * /executions:
7077
- * get:
7078
- * summary: List all executions
7079
- * description: Returns a list of all running execution tasks.
7080
- * responses:
7081
- * 200:
7082
- * description: A list of execution tasks.
7083
- * content:
7084
- * application/json:
7085
- * schema:
7086
- * type: array
7087
- * items:
7088
- * type: object
7089
- */
7090
- app.get([`/executions`, `${rootPath}/executions`], async (request, response) => {
7091
- response.send(runningExecutionTasks.map((runningExecutionTask) => exportExecutionTask(runningExecutionTask, false)));
7269
+ app.get(`/executions`, async (request, response) => {
7270
+ response.send(runningExecutionTasks.map((runningExecutionTask) => exportExecutionTask(runningExecutionTask, false)) /* <- TODO: satisfies paths['/executions']['get']['responses']['200']['content']['application/json'] */);
7092
7271
  });
7093
- app.get([`/executions/last`, `${rootPath}/executions/last`], async (request, response) => {
7272
+ app.get(`/executions/last`, async (request, response) => {
7094
7273
  // TODO: [🤬] Filter only for user
7095
7274
  if (runningExecutionTasks.length === 0) {
7096
7275
  response.status(404).send('No execution tasks found');
@@ -7099,7 +7278,7 @@ function startRemoteServer(options) {
7099
7278
  const lastExecutionTask = runningExecutionTasks[runningExecutionTasks.length - 1];
7100
7279
  response.send(exportExecutionTask(lastExecutionTask, true));
7101
7280
  });
7102
- app.get([`/executions/:taskId`, `${rootPath}/executions/:taskId`], async (request, response) => {
7281
+ app.get(`/executions/:taskId`, async (request, response) => {
7103
7282
  const { taskId } = request.params;
7104
7283
  // TODO: [🤬] Filter only for user
7105
7284
  const executionTask = runningExecutionTasks.find((executionTask) => executionTask.taskId === taskId);
@@ -7111,39 +7290,12 @@ function startRemoteServer(options) {
7111
7290
  }
7112
7291
  response.send(exportExecutionTask(executionTask, true));
7113
7292
  });
7114
- /**
7115
- * @swagger
7116
- * /executions/new:
7117
- * post:
7118
- * summary: Start a new execution
7119
- * description: Starts a new execution task for a given pipeline.
7120
- * requestBody:
7121
- * required: true
7122
- * content:
7123
- * application/json:
7124
- * schema:
7125
- * type: object
7126
- * properties:
7127
- * pipelineUrl:
7128
- * type: string
7129
- * inputParameters:
7130
- * type: object
7131
- * identification:
7132
- * type: object
7133
- * responses:
7134
- * 200:
7135
- * description: The newly created execution task.
7136
- * content:
7137
- * application/json:
7138
- * schema:
7139
- * type: object
7140
- * 400:
7141
- * description: Invalid input.
7142
- */
7143
- app.post([`/executions/new`, `${rootPath}/executions/new`], async (request, response) => {
7293
+ app.post(`/executions/new`, async (request, response) => {
7144
7294
  try {
7145
7295
  const { inputParameters, identification /* <- [🤬] */ } = request.body;
7146
- const pipelineUrl = request.body.pipelineUrl || request.body.book;
7296
+ const pipelineUrl = request.body
7297
+ .pipelineUrl /* <- TODO: as paths['/executions/new']['post']['requestBody']['content']['application/json'] */ ||
7298
+ request.body.book;
7147
7299
  // TODO: [🧠] Check `pipelineUrl` and `inputParameters` here or it should be responsibility of `collection.getPipelineByUrl` and `pipelineExecutor`
7148
7300
  const pipeline = await (collection === null || collection === void 0 ? void 0 : collection.getPipelineByUrl(pipelineUrl));
7149
7301
  if (pipeline === undefined) {
@@ -7157,7 +7309,7 @@ function startRemoteServer(options) {
7157
7309
  await forTime(10);
7158
7310
  // <- Note: Wait for a while to wait for quick responses or sudden but asynchronous errors
7159
7311
  // <- TODO: Put this into configuration
7160
- response.send(executionTask);
7312
+ response.send(executionTask /* <- TODO: satisfies paths['/executions/new']['post']['responses']['200']['content']['application/json'] */);
7161
7313
  /*/
7162
7314
  executionTask.asObservable().subscribe({
7163
7315
  next(partialResult) {
@@ -7177,19 +7329,24 @@ function startRemoteServer(options) {
7177
7329
  */
7178
7330
  }
7179
7331
  catch (error) {
7180
- if (!(error instanceof Error)) {
7181
- throw error;
7182
- }
7332
+ assertsError(error);
7183
7333
  response.status(400).send({ error: serializeError(error) });
7184
7334
  }
7185
7335
  });
7336
+ /**
7337
+ * Catch-all handler for unmatched routes
7338
+ */
7339
+ app.use((request, response) => {
7340
+ response.status(404).send(`URL "${request.originalUrl}" was not found on Promptbook server.`);
7341
+ });
7186
7342
  const httpServer = http.createServer(app);
7187
7343
  const server = new Server(httpServer, {
7188
- path: socketioPath,
7189
- transports: [/*'websocket', <- TODO: [🌬] Make websocket transport work */ 'polling'],
7344
+ path: '/socket.io',
7345
+ transports: ['polling', 'websocket' /*, <- TODO: [🌬] Allow to pass `transports`, add 'webtransport' */],
7190
7346
  cors: {
7191
7347
  origin: '*',
7192
7348
  methods: ['GET', 'POST'],
7349
+ // <- TODO: [🌡] Allow to pass
7193
7350
  },
7194
7351
  });
7195
7352
  server.on('connection', (socket) => {
@@ -7243,9 +7400,7 @@ function startRemoteServer(options) {
7243
7400
  socket.emit('prompt-response', { promptResult } /* <- Note: [🤛] */);
7244
7401
  }
7245
7402
  catch (error) {
7246
- if (!(error instanceof Error)) {
7247
- throw error;
7248
- }
7403
+ assertsError(error);
7249
7404
  socket.emit('error', serializeError(error) /* <- Note: [🤛] */);
7250
7405
  }
7251
7406
  finally {
@@ -7267,9 +7422,7 @@ function startRemoteServer(options) {
7267
7422
  socket.emit('listModels-response', { models } /* <- Note: [🤛] */);
7268
7423
  }
7269
7424
  catch (error) {
7270
- if (!(error instanceof Error)) {
7271
- throw error;
7272
- }
7425
+ assertsError(error);
7273
7426
  socket.emit('error', serializeError(error));
7274
7427
  }
7275
7428
  finally {
@@ -7290,9 +7443,7 @@ function startRemoteServer(options) {
7290
7443
  socket.emit('preparePipeline-response', { preparedPipeline } /* <- Note: [🤛] */);
7291
7444
  }
7292
7445
  catch (error) {
7293
- if (!(error instanceof Error)) {
7294
- throw error;
7295
- }
7446
+ assertsError(error);
7296
7447
  socket.emit('error', serializeError(error));
7297
7448
  // <- TODO: [🚋] There is a problem with the remote server handling errors and sending them back to the client
7298
7449
  }
@@ -7340,8 +7491,7 @@ function startRemoteServer(options) {
7340
7491
  };
7341
7492
  }
7342
7493
  /**
7343
- * TODO: !! Add CORS and security - probbably via `helmet`
7344
- * TODO: [👩🏾‍🤝‍🧑🏾] Allow to pass custom fetch function here - PromptbookFetch
7494
+ * TODO: [🌡] Add CORS and security - probbably via `helmet`
7345
7495
  * TODO: Split this file into multiple functions - handler for each request
7346
7496
  * TODO: Maybe use `$exportJson`
7347
7497
  * TODO: [🧠][🛍] Maybe not `isAnonymous: boolean` BUT `mode: 'ANONYMOUS'|'COLLECTION'`