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