@promptbook/remote-client 0.112.0-39 → 0.112.0-41

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 (56) hide show
  1. package/README.md +23 -21
  2. package/esm/index.es.js +537 -321
  3. package/esm/index.es.js.map +1 -1
  4. package/esm/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.d.ts +8 -2
  5. package/esm/src/book-components/Chat/Chat/ChatInputArea.d.ts +0 -10
  6. package/esm/src/book-components/Chat/Chat/ChatInputUploadedFile.d.ts +10 -0
  7. package/esm/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +46 -0
  8. package/esm/src/book-components/Chat/Chat/resolveRunBrowserToolCallDetailsState.d.ts +146 -0
  9. package/esm/src/book-components/Chat/Chat/useChatInputAreaAttachments.d.ts +1 -1
  10. package/esm/src/book-components/Chat/Chat/useChatInputAreaComposer.d.ts +39 -0
  11. package/esm/src/book-components/Chat/Chat/useChatPostprocessedMessages.d.ts +17 -0
  12. package/esm/src/book-components/Chat/Chat/useChatScrollState.d.ts +34 -0
  13. package/esm/src/book-components/Chat/Chat/useChatToolCallModalState.d.ts +61 -0
  14. package/esm/src/book-components/Chat/Chat/useChatToolCallState.d.ts +35 -0
  15. package/esm/src/book-components/Chat/LlmChat/useLlmChatMessageHandler.d.ts +58 -0
  16. package/esm/src/book-components/Chat/LlmChat/useLlmChatMessages.d.ts +29 -0
  17. package/esm/src/book-components/Chat/LlmChat/useLlmChatState.d.ts +53 -0
  18. package/esm/src/collection/pipeline-collection/constructors/createPipelineCollectionFromDirectory.d.ts +7 -1
  19. package/esm/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +52 -0
  20. package/esm/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +128 -0
  21. package/esm/src/llm-providers/openai/OpenAiCompatibleExecutionTools.d.ts +3 -3
  22. package/esm/src/llm-providers/openai/OpenAiVectorStoreHandler.d.ts +54 -0
  23. package/esm/src/llm-providers/openai/utils/OpenAiCompatibleUnsupportedParameterRetrier.d.ts +29 -0
  24. package/esm/src/llm-providers/openai/utils/callOpenAiCompatibleChatModel.d.ts +28 -0
  25. package/esm/src/types/number_usd.d.ts +1 -1
  26. package/esm/src/types/string_parameter_name.d.ts +2 -2
  27. package/esm/src/types/typeAliases.d.ts +2 -2
  28. package/esm/src/version.d.ts +1 -1
  29. package/package.json +2 -2
  30. package/umd/index.umd.js +537 -321
  31. package/umd/index.umd.js.map +1 -1
  32. package/umd/src/book-2.0/agent-source/createAgentModelRequirementsWithCommitments.d.ts +8 -2
  33. package/umd/src/book-components/Chat/Chat/ChatInputArea.d.ts +0 -10
  34. package/umd/src/book-components/Chat/Chat/ChatInputUploadedFile.d.ts +10 -0
  35. package/umd/src/book-components/Chat/Chat/ChatToolCallModalContent.d.ts +46 -0
  36. package/umd/src/book-components/Chat/Chat/resolveRunBrowserToolCallDetailsState.d.ts +146 -0
  37. package/umd/src/book-components/Chat/Chat/useChatInputAreaAttachments.d.ts +1 -1
  38. package/umd/src/book-components/Chat/Chat/useChatInputAreaComposer.d.ts +39 -0
  39. package/umd/src/book-components/Chat/Chat/useChatPostprocessedMessages.d.ts +17 -0
  40. package/umd/src/book-components/Chat/Chat/useChatScrollState.d.ts +34 -0
  41. package/umd/src/book-components/Chat/Chat/useChatToolCallModalState.d.ts +61 -0
  42. package/umd/src/book-components/Chat/Chat/useChatToolCallState.d.ts +35 -0
  43. package/umd/src/book-components/Chat/LlmChat/useLlmChatMessageHandler.d.ts +58 -0
  44. package/umd/src/book-components/Chat/LlmChat/useLlmChatMessages.d.ts +29 -0
  45. package/umd/src/book-components/Chat/LlmChat/useLlmChatState.d.ts +53 -0
  46. package/umd/src/collection/pipeline-collection/constructors/createPipelineCollectionFromDirectory.d.ts +7 -1
  47. package/umd/src/llm-providers/agent/AgentLlmExecutionTools.d.ts +52 -0
  48. package/umd/src/llm-providers/openai/OpenAiAssistantExecutionTools.d.ts +128 -0
  49. package/umd/src/llm-providers/openai/OpenAiCompatibleExecutionTools.d.ts +3 -3
  50. package/umd/src/llm-providers/openai/OpenAiVectorStoreHandler.d.ts +54 -0
  51. package/umd/src/llm-providers/openai/utils/OpenAiCompatibleUnsupportedParameterRetrier.d.ts +29 -0
  52. package/umd/src/llm-providers/openai/utils/callOpenAiCompatibleChatModel.d.ts +28 -0
  53. package/umd/src/types/number_usd.d.ts +1 -1
  54. package/umd/src/types/string_parameter_name.d.ts +2 -2
  55. package/umd/src/types/typeAliases.d.ts +2 -2
  56. package/umd/src/version.d.ts +1 -1
package/umd/index.umd.js CHANGED
@@ -22,7 +22,7 @@
22
22
  * @generated
23
23
  * @see https://github.com/webgptorg/promptbook
24
24
  */
25
- const PROMPTBOOK_ENGINE_VERSION = '0.112.0-39';
25
+ const PROMPTBOOK_ENGINE_VERSION = '0.112.0-41';
26
26
  /**
27
27
  * TODO: string_promptbook_version should be constrained to the all versions of Promptbook engine
28
28
  * Note: [💞] Ignore a discrepancy between file name and entity name
@@ -6199,6 +6199,42 @@
6199
6199
  }
6200
6200
  // TODO: [🔣] If script require contentLanguage
6201
6201
 
6202
+ /**
6203
+ * Normalizes inline parameter mentions wrapped in code spans before markdown flattening.
6204
+ *
6205
+ * @private internal utility of `parsePipeline`
6206
+ */
6207
+ const INLINE_CODE_PARAMETER_REGEXP = /`\{(?<parameterName>[a-z0-9_]+)\}`/gi;
6208
+ /**
6209
+ * Normalizes inline return statements wrapped in code spans before markdown flattening.
6210
+ *
6211
+ * @private internal utility of `parsePipeline`
6212
+ */
6213
+ const INLINE_CODE_RETURN_PARAMETER_REGEXP = /`->\s+\{(?<parameterName>[a-z0-9_]+)\}`/gi;
6214
+ /**
6215
+ * Matches the trailing return statement of a task section.
6216
+ *
6217
+ * @private internal utility of `parsePipeline`
6218
+ */
6219
+ const RESULTING_PARAMETER_LINE_REGEXP = /^->\s*\{(?<resultingParamName>[a-z0-9_]+)\}/im;
6220
+ /**
6221
+ * Removes fenced code blocks when deriving human-readable section descriptions.
6222
+ *
6223
+ * @private internal utility of `parsePipeline`
6224
+ */
6225
+ const DESCRIPTION_CODE_BLOCK_REGEXP = /^```.*^```/gms;
6226
+ /**
6227
+ * Removes blockquote lines when deriving human-readable section descriptions.
6228
+ *
6229
+ * @private internal utility of `parsePipeline`
6230
+ */
6231
+ const DESCRIPTION_BLOCKQUOTE_REGEXP = /^>.*$/gm;
6232
+ /**
6233
+ * Removes list items and return statements when deriving human-readable section descriptions.
6234
+ *
6235
+ * @private internal utility of `parsePipeline`
6236
+ */
6237
+ const DESCRIPTION_LIST_ITEM_REGEXP = /^(?:(?:-)|(?:\d\))|(?:`?->))\s+.*$/gm;
6202
6238
  /**
6203
6239
  * Compile pipeline from string (markdown) format to JSON format synchronously
6204
6240
  *
@@ -6217,7 +6253,27 @@
6217
6253
  * @public exported from `@promptbook/core`
6218
6254
  */
6219
6255
  function parsePipeline(pipelineString) {
6220
- const $pipelineJson = {
6256
+ const $pipelineJson = createInitialPipelineJson(pipelineString);
6257
+ const preparedPipelineString = preparePipelineString(pipelineString, $pipelineJson);
6258
+ const { pipelineHead, pipelineSections } = parsePreparedPipelineSections(preparedPipelineString, $pipelineJson);
6259
+ const getUniqueSectionName = createUniqueSectionNameResolver(pipelineSections);
6260
+ applyPipelineHead(pipelineHead, $pipelineJson);
6261
+ for (const pipelineSection of pipelineSections) {
6262
+ processPipelineSection(pipelineSection, $pipelineJson, getUniqueSectionName);
6263
+ }
6264
+ applyImplicitParameterDirections($pipelineJson);
6265
+ removeUndefinedValuesFromPipeline($pipelineJson);
6266
+ applySyncHighLevelAbstractions($pipelineJson);
6267
+ ensurePipelineFormfactor($pipelineJson);
6268
+ return exportParsedPipelineJson($pipelineJson);
6269
+ }
6270
+ /**
6271
+ * Creates the mutable pipeline JSON structure used throughout parsing.
6272
+ *
6273
+ * @private internal utility of `parsePipeline`
6274
+ */
6275
+ function createInitialPipelineJson(pipelineString) {
6276
+ return {
6221
6277
  title: DEFAULT_BOOK_TITLE,
6222
6278
  parameters: [],
6223
6279
  tasks: [],
@@ -6234,56 +6290,90 @@
6234
6290
  },
6235
6291
  ],
6236
6292
  };
6237
- function getPipelineIdentification() {
6238
- // Note: This is a 😐 implementation of [🚞]
6239
- const _ = [];
6240
- if ($pipelineJson.sourceFile !== undefined) {
6241
- _.push(`File: ${$pipelineJson.sourceFile}`);
6242
- }
6243
- if ($pipelineJson.pipelineUrl !== undefined) {
6244
- _.push(`Url: ${$pipelineJson.pipelineUrl}`);
6245
- }
6246
- return _.join('\n');
6247
- }
6248
- // =============================================================
6249
- // Note: 1️⃣ Parsing of the markdown into object
6250
- // ==============
6251
- // Note: 1️⃣◽1️⃣ Remove #!shebang and comments
6252
- if (pipelineString.startsWith('#!')) {
6253
- const [shebangLine, ...restLines] = pipelineString.split(/\r?\n/);
6254
- if (!(shebangLine || '').includes('ptbk')) {
6255
- throw new ParseError(spacetrim.spaceTrim((block) => `
6256
- It seems that you try to parse a book file which has non-standard shebang line for book files:
6257
- Shebang line must contain 'ptbk'
6258
-
6259
- You have:
6260
- ${block(shebangLine || '(empty line)')}
6261
-
6262
- It should look like this:
6263
- #!/usr/bin/env ptbk
6264
-
6265
- ${block(getPipelineIdentification())}
6266
- `));
6267
- }
6268
- pipelineString = validatePipelineString(restLines.join('\n'));
6293
+ }
6294
+ /**
6295
+ * Builds a short file/url identification block for parse errors.
6296
+ *
6297
+ * @private internal utility of `parsePipeline`
6298
+ */
6299
+ function getPipelineIdentification($pipelineJson) {
6300
+ // Note: This is a 😐 implementation of [🚞]
6301
+ const pipelineIdentificationParts = [];
6302
+ if ($pipelineJson.sourceFile !== undefined) {
6303
+ pipelineIdentificationParts.push(`File: ${$pipelineJson.sourceFile}`);
6269
6304
  }
6305
+ if ($pipelineJson.pipelineUrl !== undefined) {
6306
+ pipelineIdentificationParts.push(`Url: ${$pipelineJson.pipelineUrl}`);
6307
+ }
6308
+ return pipelineIdentificationParts.join('\n');
6309
+ }
6310
+ /**
6311
+ * Removes shebang/comments and normalizes markdown into a parseable pipeline form.
6312
+ *
6313
+ * @private internal utility of `parsePipeline`
6314
+ */
6315
+ function preparePipelineString(pipelineString, $pipelineJson) {
6316
+ pipelineString = removePipelineShebang(pipelineString, $pipelineJson);
6270
6317
  pipelineString = removeMarkdownComments(pipelineString);
6271
6318
  pipelineString = spacetrim.spaceTrim(pipelineString);
6272
6319
  // <- TODO: [😧] `spaceTrim` should preserve discriminated type *(or at lease `PipelineString`)*
6273
6320
  pipelineString = deflatePipeline(pipelineString);
6274
- // ==============
6275
- // Note: 1️⃣◽2️⃣ Parse the markdown
6276
- pipelineString = flattenMarkdown(pipelineString) /* <- Note: [🥞] */;
6277
- pipelineString = pipelineString.replaceAll(/`\{(?<parameterName>[a-z0-9_]+)\}`/gi, '{$<parameterName>}');
6278
- pipelineString = pipelineString.replaceAll(/`->\s+\{(?<parameterName>[a-z0-9_]+)\}`/gi, '-> {$<parameterName>}');
6321
+ pipelineString = flattenMarkdown(pipelineString);
6322
+ pipelineString = pipelineString.replaceAll(INLINE_CODE_PARAMETER_REGEXP, '{$<parameterName>}');
6323
+ pipelineString = pipelineString.replaceAll(INLINE_CODE_RETURN_PARAMETER_REGEXP, '-> {$<parameterName>}');
6324
+ return pipelineString;
6325
+ }
6326
+ /**
6327
+ * Validates and removes the optional `#!` shebang line for `.book` files.
6328
+ *
6329
+ * @private internal utility of `parsePipeline`
6330
+ */
6331
+ function removePipelineShebang(pipelineString, $pipelineJson) {
6332
+ if (!pipelineString.startsWith('#!')) {
6333
+ return pipelineString;
6334
+ }
6335
+ const [shebangLine, ...restLines] = pipelineString.split(/\r?\n/);
6336
+ const isBookShebang = (shebangLine || '').includes('ptbk');
6337
+ if (!isBookShebang) {
6338
+ throw new ParseError(spacetrim.spaceTrim((block) => `
6339
+ It seems that you try to parse a book file which has non-standard shebang line for book files:
6340
+ Shebang line must contain 'ptbk'
6341
+
6342
+ You have:
6343
+ ${block(shebangLine || '(empty line)')}
6344
+
6345
+ It should look like this:
6346
+ #!/usr/bin/env ptbk
6347
+
6348
+ ${block(getPipelineIdentification($pipelineJson))}
6349
+ `));
6350
+ }
6351
+ return validatePipelineString(restLines.join('\n'));
6352
+ }
6353
+ /**
6354
+ * Splits the prepared markdown into the pipeline head and task sections.
6355
+ *
6356
+ * @private internal utility of `parsePipeline`
6357
+ */
6358
+ function parsePreparedPipelineSections(pipelineString, $pipelineJson) {
6279
6359
  const [pipelineHead, ...pipelineSections] = splitMarkdownIntoSections(pipelineString).map(parseMarkdownSection); /* <- Note: [🥞] */
6280
- // ==============
6281
- // Note: 1️⃣◽4️⃣ Check markdown structure
6360
+ assertPipelineSectionsStructure(pipelineHead, pipelineSections, $pipelineJson);
6361
+ return {
6362
+ pipelineHead,
6363
+ pipelineSections,
6364
+ };
6365
+ }
6366
+ /**
6367
+ * Ensures the flattened markdown has exactly one h1 head followed by only h2 sections.
6368
+ *
6369
+ * @private internal utility of `parsePipeline`
6370
+ */
6371
+ function assertPipelineSectionsStructure(pipelineHead, pipelineSections, $pipelineJson) {
6282
6372
  if (pipelineHead === undefined) {
6283
6373
  throw new UnexpectedError(spacetrim.spaceTrim((block) => `
6284
6374
  Pipeline head is not defined
6285
6375
 
6286
- ${block(getPipelineIdentification())}
6376
+ ${block(getPipelineIdentification($pipelineJson))}
6287
6377
 
6288
6378
  This should never happen, because the pipeline already flattened
6289
6379
  `));
@@ -6292,337 +6382,463 @@
6292
6382
  throw new UnexpectedError(spacetrim.spaceTrim((block) => `
6293
6383
  Pipeline head is not h1
6294
6384
 
6295
- ${block(getPipelineIdentification())}
6385
+ ${block(getPipelineIdentification($pipelineJson))}
6296
6386
 
6297
6387
  This should never happen, because the pipeline already flattened
6298
6388
  `));
6299
6389
  }
6300
- if (!pipelineSections.every((section) => section.level === 2)) {
6390
+ if (!pipelineSections.every((pipelineSection) => pipelineSection.level === 2)) {
6301
6391
  throw new UnexpectedError(spacetrim.spaceTrim((block) => `
6302
6392
  Not every pipeline section is h2
6303
6393
 
6304
- ${block(getPipelineIdentification())}
6394
+ ${block(getPipelineIdentification($pipelineJson))}
6305
6395
 
6306
6396
  This should never happen, because the pipeline already flattened
6307
6397
  `));
6308
6398
  }
6309
- // =============================================================
6310
- ///Note: 2️⃣ Function for defining parameters
6311
- const defineParam = (parameterCommand) => {
6312
- const { parameterName, parameterDescription, isInput, isOutput } = parameterCommand;
6313
- if (RESERVED_PARAMETER_NAMES.includes(parameterName)) {
6314
- throw new ParseError(spacetrim.spaceTrim((block) => `
6315
- Parameter name {${parameterName}} is reserved and cannot be used as resulting parameter name
6399
+ }
6400
+ /**
6401
+ * Applies the pipeline head title, description, and head-level commands.
6402
+ *
6403
+ * @private internal utility of `parsePipeline`
6404
+ */
6405
+ function applyPipelineHead(pipelineHead, $pipelineJson) {
6406
+ $pipelineJson.title = pipelineHead.title;
6407
+ $pipelineJson.description = extractPipelineDescription(pipelineHead.content);
6408
+ for (const listItem of extractAllListItemsFromMarkdown(pipelineHead.content)) {
6409
+ applyPipelineHeadCommand(listItem, $pipelineJson);
6410
+ }
6411
+ }
6412
+ /**
6413
+ * Extracts the plain-text description from a head or task section body.
6414
+ *
6415
+ * @private internal utility of `parsePipeline`
6416
+ */
6417
+ function extractPipelineDescription(sectionContent) {
6418
+ let description = sectionContent;
6419
+ description = description.split(DESCRIPTION_CODE_BLOCK_REGEXP).join('');
6420
+ description = description.split(DESCRIPTION_BLOCKQUOTE_REGEXP).join('');
6421
+ description = description.split(DESCRIPTION_LIST_ITEM_REGEXP).join('');
6422
+ description = spacetrim.spaceTrim(description);
6423
+ if (description === '') {
6424
+ return undefined;
6425
+ }
6426
+ return description;
6427
+ }
6428
+ /**
6429
+ * Parses and applies one command declared in the pipeline head.
6430
+ *
6431
+ * @private internal utility of `parsePipeline`
6432
+ */
6433
+ function applyPipelineHeadCommand(listItem, $pipelineJson) {
6434
+ const command = parseCommand(listItem, 'PIPELINE_HEAD');
6435
+ const commandParser = getParserForCommand(command);
6436
+ if (commandParser.isUsedInPipelineHead !== true /* <- Note: [🦦][4] */) {
6437
+ throw new ParseError(spacetrim.spaceTrim((block) => `
6438
+ Command \`${command.type}\` is not allowed in the head of the pipeline ONLY at the pipeline task
6316
6439
 
6317
- ${block(getPipelineIdentification())}
6318
- `) /* <- TODO: [🚞] */);
6440
+ ${block(getPipelineIdentification($pipelineJson))}
6441
+ `)); // <- TODO: [🚞]
6442
+ }
6443
+ try {
6444
+ commandParser.$applyToPipelineJson(command, $pipelineJson);
6445
+ // <- Note: [🦦] Its strange that this assertion must be here, [🦦][4] should do this assertion implicitly
6446
+ }
6447
+ catch (error) {
6448
+ if (!(error instanceof ParseError)) {
6449
+ throw error;
6319
6450
  }
6320
- const existingParameter = $pipelineJson.parameters.find((parameter) => parameter.name === parameterName);
6321
- if (existingParameter &&
6322
- existingParameter.description &&
6323
- existingParameter.description !== parameterDescription &&
6324
- parameterDescription) {
6325
- throw new ParseError(spacetrim.spaceTrim((block) => `
6326
- Parameter \`{${parameterName}}\` is defined multiple times with different description:
6451
+ throw new ParseError(spacetrim.spaceTrim((block) => `
6452
+ Command ${command.type} failed to apply to the pipeline
6327
6453
 
6328
- ${block(getPipelineIdentification())}
6454
+ The error:
6455
+ ${block(error.message)}
6329
6456
 
6330
- First definition:
6331
- ${block(existingParameter.description || '[undefined]')}
6457
+ Raw command:
6458
+ - ${listItem}
6332
6459
 
6333
- Second definition:
6334
- ${block(parameterDescription || '[undefined]')}
6335
- `));
6336
- }
6337
- if (existingParameter) {
6338
- if (parameterDescription) {
6339
- existingParameter.description = parameterDescription;
6340
- }
6341
- existingParameter.isInput = existingParameter.isInput || isInput;
6342
- existingParameter.isOutput = existingParameter.isOutput || isOutput;
6343
- }
6344
- else {
6345
- $pipelineJson.parameters.push({
6346
- name: parameterName,
6347
- description: parameterDescription || undefined,
6348
- isInput,
6349
- isOutput,
6350
- });
6351
- }
6352
- };
6353
- // =============================================================
6354
- // Note: 3️⃣ Process pipeline head
6355
- $pipelineJson.title = pipelineHead.title;
6356
- // TODO: [🎾][1] DRY description
6357
- let description = pipelineHead.content;
6358
- // Note: Remove codeblocks - TODO: [🎾] Make util removeAllBlocksFromMarkdown (exported from `@promptbool/utils`)
6359
- description = description.split(/^```.*^```/gms).join('');
6360
- description = description.split(/^>.*$/gm).join('');
6361
- //Note: Remove lists and return statement - TODO: [🎾] Make util (exported from `@promptbool/utils`)
6362
- description = description.split(/^(?:(?:-)|(?:\d\))|(?:`?->))\s+.*$/gm).join('');
6363
- description = spacetrim.spaceTrim(description);
6364
- if (description === '') {
6365
- description = undefined;
6366
- }
6367
- $pipelineJson.description = description;
6368
- const listItems = extractAllListItemsFromMarkdown(pipelineHead.content);
6369
- for (const listItem of listItems) {
6370
- // TODO: [🥥] Maybe move this logic to `$parseAndApplyPipelineHeadCommands`
6371
- const command = parseCommand(listItem, 'PIPELINE_HEAD');
6372
- const commandParser = getParserForCommand(command);
6373
- if (commandParser.isUsedInPipelineHead !== true /* <- Note: [🦦][4] */) {
6374
- throw new ParseError(spacetrim.spaceTrim((block) => `
6375
- Command \`${command.type}\` is not allowed in the head of the pipeline ONLY at the pipeline task
6460
+ Usage of ${command.type}:
6461
+ ${block(commandParser.examples.map((example) => `- ${example}`).join('\n'))}
6376
6462
 
6377
- ${block(getPipelineIdentification())}
6378
- `)); // <- TODO: [🚞]
6379
- }
6380
- try {
6381
- commandParser.$applyToPipelineJson(command, $pipelineJson);
6382
- // <- Note: [🦦] Its strange that this assertion must be here, [🦦][4] should do this assertion implicitly
6383
- }
6384
- catch (error) {
6385
- if (!(error instanceof ParseError)) {
6386
- throw error;
6387
- }
6388
- throw new ParseError(spacetrim.spaceTrim((block) => `
6389
- Command ${command.type} failed to apply to the pipeline
6463
+ ${block(getPipelineIdentification($pipelineJson))}
6464
+ `)); // <- TODO: [🚞]
6465
+ }
6466
+ if (command.type === 'PARAMETER') {
6467
+ defineParameter($pipelineJson, command);
6468
+ // <- Note: [🍣]
6469
+ }
6470
+ }
6471
+ /**
6472
+ * Merges one parameter declaration into the mutable pipeline parameter list.
6473
+ *
6474
+ * @private internal utility of `parsePipeline`
6475
+ */
6476
+ function defineParameter($pipelineJson, parameterCommand) {
6477
+ const { parameterName, parameterDescription, isInput, isOutput } = parameterCommand;
6478
+ if (RESERVED_PARAMETER_NAMES.includes(parameterName)) {
6479
+ throw new ParseError(spacetrim.spaceTrim((block) => `
6480
+ Parameter name {${parameterName}} is reserved and cannot be used as resulting parameter name
6390
6481
 
6391
- The error:
6392
- ${block(error.message)}
6482
+ ${block(getPipelineIdentification($pipelineJson))}
6483
+ `) /* <- TODO: [🚞] */);
6484
+ }
6485
+ const existingParameter = $pipelineJson.parameters.find((parameter) => parameter.name === parameterName);
6486
+ if (existingParameter &&
6487
+ existingParameter.description &&
6488
+ existingParameter.description !== parameterDescription &&
6489
+ parameterDescription) {
6490
+ throw new ParseError(spacetrim.spaceTrim((block) => `
6491
+ Parameter \`{${parameterName}}\` is defined multiple times with different description:
6393
6492
 
6394
- Raw command:
6395
- - ${listItem}
6493
+ ${block(getPipelineIdentification($pipelineJson))}
6396
6494
 
6397
- Usage of ${command.type}:
6398
- ${block(commandParser.examples.map((example) => `- ${example}`).join('\n'))}
6495
+ First definition:
6496
+ ${block(existingParameter.description || '[undefined]')}
6399
6497
 
6400
- ${block(getPipelineIdentification())}
6401
- `)); // <- TODO: [🚞]
6402
- }
6403
- if (command.type === 'PARAMETER') {
6404
- defineParam(command);
6405
- // <- Note: [🍣]
6498
+ Second definition:
6499
+ ${block(parameterDescription || '[undefined]')}
6500
+ `));
6501
+ }
6502
+ if (existingParameter) {
6503
+ if (parameterDescription) {
6504
+ existingParameter.description = parameterDescription;
6406
6505
  }
6506
+ existingParameter.isInput = existingParameter.isInput || isInput;
6507
+ existingParameter.isOutput = existingParameter.isOutput || isOutput;
6508
+ return;
6407
6509
  }
6408
- // =============================================================
6409
- // Note: 4️⃣ Prepare unique section names with indexes when needed
6510
+ $pipelineJson.parameters.push({
6511
+ name: parameterName,
6512
+ description: parameterDescription || undefined,
6513
+ isInput,
6514
+ isOutput,
6515
+ });
6516
+ }
6517
+ /**
6518
+ * Creates stable unique task names for duplicate section titles.
6519
+ *
6520
+ * @private internal utility of `parsePipeline`
6521
+ */
6522
+ function createUniqueSectionNameResolver(pipelineSections) {
6410
6523
  const sectionCounts = {};
6411
- for (const section of pipelineSections) {
6412
- const name = titleToName(section.title);
6413
- if (sectionCounts[name] === undefined) {
6414
- sectionCounts[name] = { count: 0, currentIndex: 0 };
6524
+ for (const pipelineSection of pipelineSections) {
6525
+ const sectionName = titleToName(pipelineSection.title);
6526
+ if (sectionCounts[sectionName] === undefined) {
6527
+ sectionCounts[sectionName] = { count: 0, currentIndex: 0 };
6415
6528
  }
6416
- sectionCounts[name].count++;
6529
+ sectionCounts[sectionName].count++;
6417
6530
  }
6418
- const getUniqueSectionName = (title) => {
6419
- const name = titleToName(title);
6420
- const count = sectionCounts[name];
6421
- if (count.count === 1) {
6422
- return name;
6531
+ return (title) => {
6532
+ const sectionName = titleToName(title);
6533
+ const sectionCount = sectionCounts[sectionName];
6534
+ if (sectionCount.count === 1) {
6535
+ return sectionName;
6423
6536
  }
6424
- const nameWithSuffix = `${name}-${count.currentIndex}`;
6425
- count.currentIndex++;
6537
+ const nameWithSuffix = `${sectionName}-${sectionCount.currentIndex}`;
6538
+ sectionCount.currentIndex++;
6426
6539
  return nameWithSuffix;
6427
6540
  };
6428
- // =============================================================
6429
- // Note: 5️⃣ Process each section of the pipeline
6430
- for (const section of pipelineSections) {
6431
- // TODO: Parse section's description (the content out of the codeblock and lists)
6432
- const listItems = extractAllListItemsFromMarkdown(section.content);
6433
- const { language, content } = extractOneBlockFromMarkdown(section.content);
6434
- // TODO: [🎾][1] DRY description
6435
- let description = section.content;
6436
- // Note: Remove codeblocks - TODO: [🎾]
6437
- description = description.split(/^```.*^```/gms).join('');
6438
- description = description.split(/^>.*$/gm).join('');
6439
- //Note: Remove lists and return statement - TODO: [🎾]
6440
- description = description.split(/^(?:(?:-)|(?:\d\))|(?:`?->))\s+.*$/gm).join('');
6441
- description = spacetrim.spaceTrim(description);
6442
- if (description === '') {
6443
- description = undefined;
6444
- }
6445
- const $taskJson = {
6446
- isSectionTypeSet: false,
6447
- isTask: true,
6448
- taskType: undefined /* <- Note: [🍙] Putting here placeholder to keep `taskType` on top at final JSON */,
6449
- name: getUniqueSectionName(section.title || DEFAULT_TASK_TITLE),
6450
- title: section.title || DEFAULT_TASK_TITLE,
6451
- description,
6452
- content,
6453
- // <- TODO: [🍙] Some standard order of properties
6454
- };
6455
- const lastLine = section.content.split(/\r?\n/).pop();
6456
- const resultingParameterNameMatch = /^->\s*\{(?<resultingParamName>[a-z0-9_]+)\}/im.exec(lastLine);
6457
- if (resultingParameterNameMatch &&
6458
- resultingParameterNameMatch.groups !== undefined &&
6459
- resultingParameterNameMatch.groups.resultingParamName !== undefined) {
6460
- $taskJson.resultingParameterName = resultingParameterNameMatch.groups.resultingParamName;
6461
- }
6462
- // TODO: [🥥] Maybe move this logic to `$parseAndApplyPipelineTaskCommands`
6463
- const commands = listItems.map((listItem) => ({
6464
- listItem,
6465
- command: parseCommand(listItem, 'PIPELINE_TASK'),
6466
- }));
6467
- // Note: If block type is not set, set it to 'PROMPT_TASK'
6468
- if (commands.some(({ command }) => command.type === 'SECTION') === false) {
6469
- sectionCommandParser.$applyToTaskJson({ type: 'SECTION', taskType: 'PROMPT_TASK' }, $taskJson, $pipelineJson);
6470
- }
6471
- // TODO [♓️] List commands and before apply order them to achieve order-agnostic commands
6472
- for (const { listItem, command } of commands) {
6473
- const commandParser = getParserForCommand(command);
6474
- if (commandParser.isUsedInPipelineTask !== true /* <- Note: [🦦][4] */) {
6475
- throw new ParseError(spacetrim.spaceTrim((block) => `
6476
- Command \`${command.type}\` is not allowed in the task of the promptbook ONLY at the pipeline head
6477
-
6478
- ${block(getPipelineIdentification())}
6479
- `)); // <- TODO: [🚞]
6480
- }
6481
- try {
6482
- commandParser.$applyToTaskJson(
6483
- // <- Note: [🦦] Its strange that this assertion must be here, [🦦][4] should do this assertion implicitly
6484
- command, $taskJson, $pipelineJson);
6485
- }
6486
- catch (error) {
6487
- if (!(error instanceof ParseError)) {
6488
- throw error;
6489
- }
6490
- throw new ParseError(spacetrim.spaceTrim((block) => `
6491
- Command \`${command.type}\` failed to apply to the task
6492
-
6493
- The error:
6494
- ${block(error.message)}
6541
+ }
6542
+ /**
6543
+ * Parses, applies, and persists one h2 task section.
6544
+ *
6545
+ * @private internal utility of `parsePipeline`
6546
+ */
6547
+ function processPipelineSection(pipelineSection, $pipelineJson, getUniqueSectionName) {
6548
+ const { $taskJson, language } = createTaskJsonFromSection(pipelineSection, getUniqueSectionName);
6549
+ const commands = parsePipelineTaskCommands(pipelineSection.content);
6550
+ applyDefaultTaskSectionType($taskJson, commands, $pipelineJson);
6551
+ for (const commandItem of commands) {
6552
+ applyPipelineTaskCommand(commandItem, $taskJson, $pipelineJson);
6553
+ }
6554
+ applyScriptTaskLanguage($taskJson, language, $pipelineJson);
6555
+ registerTaskDependentParameters($taskJson, $pipelineJson);
6556
+ persistTaskIfNeeded($taskJson, $pipelineJson);
6557
+ }
6558
+ /**
6559
+ * Creates the mutable task JSON shell from one markdown section.
6560
+ *
6561
+ * @private internal utility of `parsePipeline`
6562
+ */
6563
+ function createTaskJsonFromSection(pipelineSection, getUniqueSectionName) {
6564
+ const { language, content } = extractOneBlockFromMarkdown(pipelineSection.content);
6565
+ const normalizedLanguage = language || undefined;
6566
+ const title = pipelineSection.title || DEFAULT_TASK_TITLE;
6567
+ const $taskJson = {
6568
+ isSectionTypeSet: false,
6569
+ isTask: true,
6570
+ taskType: undefined /* <- Note: [🍙] Putting here placeholder to keep `taskType` on top at final JSON */,
6571
+ name: getUniqueSectionName(title),
6572
+ title,
6573
+ description: extractPipelineDescription(pipelineSection.content),
6574
+ content,
6575
+ // <- TODO: [🍙] Some standard order of properties
6576
+ };
6577
+ const resultingParameterName = extractResultingParameterName(pipelineSection.content);
6578
+ if (resultingParameterName !== undefined) {
6579
+ $taskJson.resultingParameterName = resultingParameterName;
6580
+ }
6581
+ return {
6582
+ $taskJson,
6583
+ language: normalizedLanguage,
6584
+ };
6585
+ }
6586
+ /**
6587
+ * Extracts the optional trailing `-> {parameter}` statement from a section body.
6588
+ *
6589
+ * @private internal utility of `parsePipeline`
6590
+ */
6591
+ function extractResultingParameterName(sectionContent) {
6592
+ var _a;
6593
+ const lastLine = sectionContent.split(/\r?\n/).pop();
6594
+ const resultingParameterNameMatch = RESULTING_PARAMETER_LINE_REGEXP.exec(lastLine);
6595
+ return (_a = resultingParameterNameMatch === null || resultingParameterNameMatch === void 0 ? void 0 : resultingParameterNameMatch.groups) === null || _a === void 0 ? void 0 : _a.resultingParamName;
6596
+ }
6597
+ /**
6598
+ * Parses all list-item commands declared inside one task section.
6599
+ *
6600
+ * @private internal utility of `parsePipeline`
6601
+ */
6602
+ function parsePipelineTaskCommands(sectionContent) {
6603
+ return extractAllListItemsFromMarkdown(sectionContent).map((listItem) => ({
6604
+ listItem,
6605
+ command: parseCommand(listItem, 'PIPELINE_TASK'),
6606
+ }));
6607
+ }
6608
+ /**
6609
+ * Applies the implicit default `PROMPT_TASK` section type when no SECTION command is present.
6610
+ *
6611
+ * @private internal utility of `parsePipeline`
6612
+ */
6613
+ function applyDefaultTaskSectionType($taskJson, commands, $pipelineJson) {
6614
+ const isSectionCommandPresent = commands.some(({ command }) => command.type === 'SECTION');
6615
+ if (isSectionCommandPresent) {
6616
+ return;
6617
+ }
6618
+ sectionCommandParser.$applyToTaskJson({ type: 'SECTION', taskType: 'PROMPT_TASK' }, $taskJson, $pipelineJson);
6619
+ }
6620
+ /**
6621
+ * Parses and applies one command declared inside a task section.
6622
+ *
6623
+ * @private internal utility of `parsePipeline`
6624
+ */
6625
+ function applyPipelineTaskCommand(commandItem, $taskJson, $pipelineJson) {
6626
+ const { listItem, command } = commandItem;
6627
+ const commandParser = getParserForCommand(command);
6628
+ if (commandParser.isUsedInPipelineTask !== true /* <- Note: [🦦][4] */) {
6629
+ throw new ParseError(spacetrim.spaceTrim((block) => `
6630
+ Command \`${command.type}\` is not allowed in the task of the promptbook ONLY at the pipeline head
6495
6631
 
6496
- Current state of the task:
6497
- ${block(JSON.stringify($taskJson, null, 4))}
6632
+ ${block(getPipelineIdentification($pipelineJson))}
6633
+ `)); // <- TODO: [🚞]
6634
+ }
6635
+ try {
6636
+ commandParser.$applyToTaskJson(command, $taskJson, $pipelineJson);
6637
+ // <- Note: [🦦] Its strange that this assertion must be here, [🦦][4] should do this assertion implicitly
6638
+ }
6639
+ catch (error) {
6640
+ if (!(error instanceof ParseError)) {
6641
+ throw error;
6642
+ }
6643
+ throw new ParseError(spacetrim.spaceTrim((block) => `
6644
+ Command \`${command.type}\` failed to apply to the task
6498
6645
 
6499
- Command that failed to apply:
6500
- ${block(JSON.stringify(command, null, 4))}
6646
+ The error:
6647
+ ${block(error.message)}
6501
6648
 
6502
- *<- Maybe wrong order of commands in section?*
6649
+ Current state of the task:
6650
+ ${block(JSON.stringify($taskJson, null, 4))}
6503
6651
 
6504
- Raw command:
6505
- - ${listItem}
6652
+ Command that failed to apply:
6653
+ ${block(JSON.stringify(command, null, 4))}
6506
6654
 
6507
- Usage of ${command.type}:
6508
- ${block(commandParser.examples.map((example) => `- ${example}`).join('\n'))}
6655
+ *<- Maybe wrong order of commands in section?*
6509
6656
 
6510
- ${block(getPipelineIdentification())}
6511
- `)); // <- TODO: [🚞]
6512
- }
6513
- if (command.type === 'PARAMETER') {
6514
- defineParam(command);
6515
- // <- Note: [🍣]
6516
- }
6517
- }
6518
- // TODO: [🍧] Should be done in SECTION command
6519
- if ($taskJson.taskType === 'SCRIPT_TASK') {
6520
- if (!language) {
6521
- throw new ParseError(spacetrim.spaceTrim((block) => `
6522
- You must specify the language of the script in the \`SCRIPT\` task
6657
+ Raw command:
6658
+ - ${listItem}
6523
6659
 
6524
- ${block(getPipelineIdentification())}
6525
- `));
6526
- }
6527
- if (!SUPPORTED_SCRIPT_LANGUAGES.includes(language)) {
6528
- throw new ParseError(spacetrim.spaceTrim((block) => `
6529
- Script language ${language} is not supported.
6660
+ Usage of ${command.type}:
6661
+ ${block(commandParser.examples.map((example) => `- ${example}`).join('\n'))}
6530
6662
 
6531
- Supported languages are:
6532
- ${block(SUPPORTED_SCRIPT_LANGUAGES.join(', '))}
6663
+ ${block(getPipelineIdentification($pipelineJson))}
6664
+ `)); // <- TODO: [🚞]
6665
+ }
6666
+ if (command.type === 'PARAMETER') {
6667
+ defineParameter($pipelineJson, command);
6668
+ // <- Note: [🍣]
6669
+ }
6670
+ }
6671
+ /**
6672
+ * Validates and stores the language for SCRIPT tasks.
6673
+ *
6674
+ * @private internal utility of `parsePipeline`
6675
+ */
6676
+ function applyScriptTaskLanguage($taskJson, language, $pipelineJson) {
6677
+ const isScriptTask = $taskJson.taskType === 'SCRIPT_TASK';
6678
+ if (!isScriptTask) {
6679
+ return;
6680
+ }
6681
+ if (!language) {
6682
+ throw new ParseError(spacetrim.spaceTrim((block) => `
6683
+ You must specify the language of the script in the \`SCRIPT\` task
6533
6684
 
6534
- `));
6535
- }
6536
- $taskJson.contentLanguage = language;
6537
- }
6538
- $taskJson.dependentParameterNames = Array.from(extractParameterNamesFromTask($taskJson));
6539
- for (const parameterName of $taskJson.dependentParameterNames) {
6540
- // TODO: [🧠] This definition should be made first in the task
6541
- defineParam({
6542
- parameterName,
6543
- parameterDescription: null,
6544
- isInput: false,
6545
- isOutput: false,
6546
- // <- Note: In this case null+false+false means that we do not know yet if it is input or output and we will set it later
6547
- });
6548
- }
6549
- /*
6550
- // TODO: [🍧] This should be checked in `MODEL` command + better error message
6551
- if ($taskJson.taskType !== 'PROMPT_TASK' && $taskJson.modelRequirements !== undefined) {
6552
- throw new UnexpectedError(
6553
- spaceTrim(
6554
- (block) => `
6555
- Model requirements are defined for the block type ${
6556
- $taskJson.taskType
6557
- } which is not a \`PROMPT\` task
6685
+ ${block(getPipelineIdentification($pipelineJson))}
6686
+ `));
6687
+ }
6688
+ if (!SUPPORTED_SCRIPT_LANGUAGES.includes(language)) {
6689
+ throw new ParseError(spacetrim.spaceTrim((block) => `
6690
+ Script language ${language} is not supported.
6558
6691
 
6559
- This should be avoided by the \`modelCommandParser\`
6692
+ Supported languages are:
6693
+ ${block(SUPPORTED_SCRIPT_LANGUAGES.join(', '))}
6560
6694
 
6561
- ${block(getPipelineIdentification())}
6562
- `,
6563
- ),
6564
- );
6565
- }
6566
- */
6567
- if ($taskJson.isTask) {
6568
- delete $taskJson.isSectionTypeSet;
6569
- delete $taskJson.isTask;
6570
- // TODO: [🍙] Maybe do reorder of `$taskJson` here
6571
- $pipelineJson.tasks.push($taskJson);
6572
- }
6573
- }
6574
- // =============================================================
6575
- // Note: 6️⃣ Mark parameters as INPUT if not explicitly set
6576
- if ($pipelineJson.parameters.every((parameter) => !parameter.isInput)) {
6577
- for (const parameter of $pipelineJson.parameters) {
6578
- const isThisParameterResulting = $pipelineJson.tasks.some((task) => task.resultingParameterName === parameter.name);
6579
- if (!isThisParameterResulting) {
6580
- parameter.isInput = true;
6581
- // <- TODO: [💔] Why this is making typescript error in vscode but not in cli
6582
- // > Type 'true' is not assignable to type 'false'.ts(2322)
6583
- // > (property) isInput: false
6584
- // > The parameter is input of the pipeline The parameter is NOT input of the pipeline
6585
- }
6586
- }
6695
+ `));
6587
6696
  }
6588
- // =============================================================
6589
- // Note: 7️⃣ Mark all non-INPUT parameters as OUTPUT if any OUTPUT is not set
6590
- if ($pipelineJson.parameters.every((parameter) => !parameter.isOutput)) {
6591
- for (const parameter of $pipelineJson.parameters) {
6592
- if (!parameter.isInput) {
6593
- parameter.isOutput = true;
6594
- // <- TODO: [💔]
6595
- }
6697
+ $taskJson.contentLanguage = language;
6698
+ }
6699
+ /**
6700
+ * Extracts task dependencies and ensures referenced parameters exist.
6701
+ *
6702
+ * @private internal utility of `parsePipeline`
6703
+ */
6704
+ function registerTaskDependentParameters($taskJson, $pipelineJson) {
6705
+ $taskJson.dependentParameterNames = Array.from(extractParameterNamesFromTask($taskJson));
6706
+ for (const parameterName of $taskJson.dependentParameterNames) {
6707
+ // TODO: [🧠] This definition should be made first in the task
6708
+ defineParameter($pipelineJson, {
6709
+ parameterName,
6710
+ parameterDescription: null,
6711
+ isInput: false,
6712
+ isOutput: false,
6713
+ // <- Note: In this case null+false+false means that we do not know yet if it is input or output and we will set it later
6714
+ });
6715
+ }
6716
+ }
6717
+ /**
6718
+ * Removes transient parsing flags and persists real tasks into the pipeline JSON.
6719
+ *
6720
+ * @private internal utility of `parsePipeline`
6721
+ */
6722
+ function persistTaskIfNeeded($taskJson, $pipelineJson) {
6723
+ /*
6724
+ // TODO: [🍧] This should be checked in `MODEL` command + better error message
6725
+ if ($taskJson.taskType !== 'PROMPT_TASK' && $taskJson.modelRequirements !== undefined) {
6726
+ throw new UnexpectedError(
6727
+ spaceTrim(
6728
+ (block) => `
6729
+ Model requirements are defined for the block type ${
6730
+ $taskJson.taskType
6731
+ } which is not a \`PROMPT\` task
6732
+
6733
+ This should be avoided by the \`modelCommandParser\`
6734
+
6735
+ ${block(getPipelineIdentification($pipelineJson))}
6736
+ `,
6737
+ ),
6738
+ );
6739
+ }
6740
+ */
6741
+ if (!$taskJson.isTask) {
6742
+ return;
6743
+ }
6744
+ delete $taskJson.isSectionTypeSet;
6745
+ delete $taskJson.isTask;
6746
+ // TODO: [🍙] Maybe do reorder of `$taskJson` here
6747
+ $pipelineJson.tasks.push($taskJson);
6748
+ }
6749
+ /**
6750
+ * Applies default INPUT/OUTPUT flags when the author did not specify them explicitly.
6751
+ *
6752
+ * @private internal utility of `parsePipeline`
6753
+ */
6754
+ function applyImplicitParameterDirections($pipelineJson) {
6755
+ markImplicitInputParameters($pipelineJson);
6756
+ markImplicitOutputParameters($pipelineJson);
6757
+ }
6758
+ /**
6759
+ * Marks non-result parameters as pipeline inputs when no input was declared.
6760
+ *
6761
+ * @private internal utility of `parsePipeline`
6762
+ */
6763
+ function markImplicitInputParameters($pipelineJson) {
6764
+ if ($pipelineJson.parameters.some((parameter) => parameter.isInput)) {
6765
+ return;
6766
+ }
6767
+ for (const parameter of $pipelineJson.parameters) {
6768
+ const isThisParameterResulting = $pipelineJson.tasks.some((task) => task.resultingParameterName === parameter.name);
6769
+ if (!isThisParameterResulting) {
6770
+ parameter.isInput = true;
6771
+ // <- TODO: [💔] Why this is making typescript error in vscode but not in cli
6772
+ // > Type 'true' is not assignable to type 'false'.ts(2322)
6773
+ // > (property) isInput: false
6774
+ // > The parameter is input of the pipeline The parameter is NOT input of the pipeline
6596
6775
  }
6597
6776
  }
6598
- // =============================================================
6599
- // Note: 8️⃣ Cleanup of undefined values
6600
- $pipelineJson.tasks.forEach((tasks) => {
6601
- for (const [key, value] of Object.entries(tasks)) {
6602
- if (value === undefined) {
6603
- delete tasks[key];
6604
- }
6777
+ }
6778
+ /**
6779
+ * Marks every non-input parameter as output when no output was declared.
6780
+ *
6781
+ * @private internal utility of `parsePipeline`
6782
+ */
6783
+ function markImplicitOutputParameters($pipelineJson) {
6784
+ if ($pipelineJson.parameters.some((parameter) => parameter.isOutput)) {
6785
+ return;
6786
+ }
6787
+ for (const parameter of $pipelineJson.parameters) {
6788
+ if (!parameter.isInput) {
6789
+ parameter.isOutput = true;
6790
+ // <- TODO: [💔]
6605
6791
  }
6606
- });
6607
- $pipelineJson.parameters.forEach((parameter) => {
6608
- for (const [key, value] of Object.entries(parameter)) {
6609
- if (value === undefined) {
6610
- delete parameter[key];
6611
- }
6792
+ }
6793
+ }
6794
+ /**
6795
+ * Removes `undefined` properties from serialized tasks and parameters.
6796
+ *
6797
+ * @private internal utility of `parsePipeline`
6798
+ */
6799
+ function removeUndefinedValuesFromPipeline($pipelineJson) {
6800
+ $pipelineJson.tasks.forEach(removeUndefinedProperties);
6801
+ $pipelineJson.parameters.forEach(removeUndefinedProperties);
6802
+ }
6803
+ /**
6804
+ * Deletes all own properties with `undefined` values from a mutable JSON entity.
6805
+ *
6806
+ * @private internal utility of `parsePipeline`
6807
+ */
6808
+ function removeUndefinedProperties(entity) {
6809
+ for (const [key, value] of Object.entries(entity)) {
6810
+ if (value === undefined) {
6811
+ delete entity[key];
6612
6812
  }
6613
- });
6614
- // =============================================================
6615
- // Note: 9️⃣ Apply sync high-level abstractions
6813
+ }
6814
+ }
6815
+ /**
6816
+ * Applies all sync-only high-level abstractions after parsing.
6817
+ *
6818
+ * @private internal utility of `parsePipeline`
6819
+ */
6820
+ function applySyncHighLevelAbstractions($pipelineJson) {
6616
6821
  for (const highLevelAbstraction of HIGH_LEVEL_ABSTRACTIONS.filter(({ type }) => type === 'SYNC')) {
6617
6822
  highLevelAbstraction.$applyToPipelineJson($pipelineJson);
6618
6823
  }
6619
- // =============================================================
6620
- // Note: 🔟 Default formfactor
6824
+ }
6825
+ /**
6826
+ * Ensures parsed pipelines always have the default `GENERIC` formfactor.
6827
+ *
6828
+ * @private internal utility of `parsePipeline`
6829
+ */
6830
+ function ensurePipelineFormfactor($pipelineJson) {
6621
6831
  // Note: [🔆] If formfactor is still not set, set it to 'GENERIC'
6622
6832
  if ($pipelineJson.formfactorName === undefined) {
6623
6833
  $pipelineJson.formfactorName = 'GENERIC';
6624
6834
  }
6625
- // =============================================================
6835
+ }
6836
+ /**
6837
+ * Finalizes ordering and exports the parsed pipeline JSON.
6838
+ *
6839
+ * @private internal utility of `parsePipeline`
6840
+ */
6841
+ function exportParsedPipelineJson($pipelineJson) {
6626
6842
  return exportJson({
6627
6843
  name: 'pipelineJson',
6628
6844
  message: `Result of \`parsePipeline\``,