@toloka-platform/sdk 0.1.0-preview.3 → 0.1.0-preview.5

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 (38) hide show
  1. package/README.md +2 -2
  2. package/bin/toloka.js +7 -0
  3. package/dist/cli/toloka.js +119 -114
  4. package/dist/cli/toloka.js.jsc +0 -0
  5. package/dist/cli/toloka.js.jsc.sha256 +1 -1
  6. package/dist/cli/toloka.js.sha256 +1 -1
  7. package/docs/cli-v2.md +4 -0
  8. package/docs/cli.md +56 -5
  9. package/docs/getting-started.md +3 -3
  10. package/docs/limitations.md +1 -1
  11. package/openapi/gen/api.ts +22 -0
  12. package/openapi/gen/models/createDatasetBody.ts +2 -0
  13. package/openapi/gen/models/createDatasetBodyProfile.ts +2 -0
  14. package/openapi/gen/models/createDatasetUploadBodyFormat.ts +1 -1
  15. package/openapi/gen/models/dataset.ts +2 -0
  16. package/openapi/gen/models/datasetProfile.ts +3 -0
  17. package/openapi/gen/models/datasetProfileStamp.ts +6 -0
  18. package/openapi/gen/models/datasetProfileStampName.ts +2 -0
  19. package/openapi/gen/models/index.ts +5 -0
  20. package/openapi/gen/models/runCost.ts +7 -0
  21. package/package.json +4 -2
  22. package/skills/catalog.json +22 -0
  23. package/skills/{SKILL.md → toloka-build/SKILL.md} +1 -1
  24. package/skills/{references → toloka-build/references}/workflow-and-cli.md +14 -4
  25. package/skills/toloka-report-issue/SKILL.md +25 -0
  26. package/skills/toloka-report-issue/references/report-template.md +34 -0
  27. package/src/authoring/index.ts +216 -171
  28. package/src/cli/analytics.ts +53 -1
  29. package/src/cli/auth.ts +5 -5
  30. package/src/cli/credentials.ts +8 -2
  31. package/src/cli/file-system.ts +34 -0
  32. package/src/cli/format-error.ts +19 -0
  33. package/src/cli/init.ts +149 -194
  34. package/src/cli/program.ts +103 -21
  35. package/src/cli/report.ts +144 -0
  36. package/src/cli/skill.ts +363 -0
  37. package/src/pipeline-graph-v1.contract.json +5 -1
  38. /package/skills/{references → toloka-build/references}/node-semantics.md +0 -0
@@ -1,4 +1,4 @@
1
- import { assert } from 'es-toolkit';
1
+ import { assert } from "es-toolkit";
2
2
  import type {
3
3
  CodeNode,
4
4
  CodeNodeSettings,
@@ -11,14 +11,14 @@ import type {
11
11
  QuorumNode,
12
12
  QuorumNodeSettings,
13
13
  RootNode,
14
- RootNodeSettings
15
- } from '../../openapi/gen/models';
16
- import { validate } from '../validate';
14
+ RootNodeSettings,
15
+ } from "../../openapi/gen/models";
16
+ import { validate } from "../validate";
17
17
 
18
- const FIELD_DEFINITION = Symbol('fieldDefinition');
19
- const PIPELINE_DEFINITION = Symbol('pipelineDefinition');
20
- const PORT_DEFINITION = Symbol('portDefinition');
21
- const NODE_FIELD_REFERENCES = Symbol('nodeFieldReferences');
18
+ const FIELD_DEFINITION = Symbol("fieldDefinition");
19
+ const PIPELINE_DEFINITION = Symbol.for("@toloka-platform/sdk/pipeline-definition");
20
+ const PORT_DEFINITION = Symbol("portDefinition");
21
+ const NODE_FIELD_REFERENCES = Symbol("nodeFieldReferences");
22
22
 
23
23
  export interface Field<Name extends string = string> {
24
24
  readonly id: string;
@@ -34,21 +34,21 @@ export type AuthoringIdOptions = {
34
34
  export interface InputPort {
35
35
  readonly node: string;
36
36
  readonly handle: string;
37
- readonly direction: 'input';
38
- readonly [PORT_DEFINITION]: 'input';
37
+ readonly direction: "input";
38
+ readonly [PORT_DEFINITION]: "input";
39
39
  }
40
40
 
41
41
  export interface OutputPort {
42
42
  readonly node: string;
43
43
  readonly handle: string;
44
- readonly direction: 'output';
45
- readonly [PORT_DEFINITION]: 'output';
44
+ readonly direction: "output";
45
+ readonly [PORT_DEFINITION]: "output";
46
46
  }
47
47
 
48
48
  export interface Connection {
49
49
  readonly from: OutputPort;
50
50
  readonly to: InputPort;
51
- readonly settings: PipelineGraphV1['connections'][number]['settings'];
51
+ readonly settings: PipelineGraphV1["connections"][number]["settings"];
52
52
  }
53
53
 
54
54
  type FieldProxyReference = {
@@ -59,12 +59,13 @@ type FieldProxyReference = {
59
59
  type RootFieldReference = Field | { readonly field: Field; readonly optional?: boolean };
60
60
 
61
61
  type FieldRelation<Relation> = Relation extends { id: string }
62
- ? Omit<Relation, 'id'> & { readonly field: Field }
62
+ ? Omit<Relation, "id"> & { readonly field: Field }
63
63
  : never;
64
64
 
65
- type FieldRelations<Relations> = NonNullable<Relations> extends Array<infer Relation>
66
- ? ReadonlyArray<FieldRelation<Relation>>
67
- : never;
65
+ type FieldRelations<Relations> =
66
+ NonNullable<Relations> extends Array<infer Relation>
67
+ ? ReadonlyArray<FieldRelation<Relation>>
68
+ : never;
68
69
 
69
70
  type FieldEntity<Entity> = Entity extends {
70
71
  fieldProxyId: string;
@@ -74,7 +75,7 @@ type FieldEntity<Entity> = Entity extends {
74
75
  relations?: infer Relations;
75
76
  dynamic: true;
76
77
  }
77
- ? Omit<Entity, 'fieldProxyId' | 'optional' | 'jsonPath' | 'description' | 'relations'> & {
78
+ ? Omit<Entity, "fieldProxyId" | "optional" | "jsonPath" | "description" | "relations"> & {
78
79
  readonly field: Field;
79
80
  readonly optional?: boolean;
80
81
  readonly jsonPath?: string;
@@ -89,7 +90,10 @@ type FieldEntity<Entity> = Entity extends {
89
90
  relations?: infer Relations;
90
91
  dynamic: false;
91
92
  }
92
- ? Omit<Entity, 'fieldProxyId' | 'optional' | 'jsonPath' | 'description' | 'relations' | 'dynamic'> & {
93
+ ? Omit<
94
+ Entity,
95
+ "fieldProxyId" | "optional" | "jsonPath" | "description" | "relations" | "dynamic"
96
+ > & {
93
97
  readonly field: Field;
94
98
  readonly optional?: boolean;
95
99
  readonly jsonPath?: string;
@@ -100,36 +104,42 @@ type FieldEntity<Entity> = Entity extends {
100
104
  : never;
101
105
 
102
106
  type FieldTarget<Target> = Target extends { targetEntity: string }
103
- ? Omit<Target, 'targetEntity'> & { readonly targetEntity: Field }
107
+ ? Omit<Target, "targetEntity"> & { readonly targetEntity: Field }
104
108
  : never;
105
109
 
106
- type FieldExam<Exam extends { gradedEntities: Array<{ targetEntity: string }> }> = Omit<Exam, 'gradedEntities'> & {
107
- readonly gradedEntities: ReadonlyArray<FieldTarget<Exam['gradedEntities'][number]>>;
110
+ type FieldExam<Exam extends { gradedEntities: Array<{ targetEntity: string }> }> = Omit<
111
+ Exam,
112
+ "gradedEntities"
113
+ > & {
114
+ readonly gradedEntities: ReadonlyArray<FieldTarget<Exam["gradedEntities"][number]>>;
108
115
  };
109
116
 
110
- type GenerationInputEntity = FieldEntity<GenerationNodeSettings['entities']['input'][number]>;
111
- type GenerationOutputEntity = FieldEntity<GenerationNodeSettings['entities']['output'][number]>;
112
- type GenerationQualityCriterion = FieldTarget<GenerationNodeSettings['qualityCriteria'][number]>;
113
- type GenerationExam = FieldExam<NonNullable<GenerationNodeSettings['exam']>>;
117
+ type GenerationInputEntity = FieldEntity<GenerationNodeSettings["entities"]["input"][number]>;
118
+ type GenerationOutputEntity = FieldEntity<GenerationNodeSettings["entities"]["output"][number]>;
119
+ type GenerationQualityCriterion = FieldTarget<GenerationNodeSettings["qualityCriteria"][number]>;
120
+ type GenerationExam = FieldExam<NonNullable<GenerationNodeSettings["exam"]>>;
114
121
 
115
- type QuorumInputEntity = FieldEntity<QuorumNodeSettings['entities']['input'][number]>;
116
- type QuorumOutputEntity = FieldEntity<QuorumNodeSettings['entities']['output'][number]>;
117
- type QuorumQualityCriterion = FieldTarget<QuorumNodeSettings['qualityCriteria'][number]>;
118
- type QuorumExam = FieldExam<NonNullable<QuorumNodeSettings['exam']>>;
122
+ type QuorumInputEntity = FieldEntity<QuorumNodeSettings["entities"]["input"][number]>;
123
+ type QuorumOutputEntity = FieldEntity<QuorumNodeSettings["entities"]["output"][number]>;
124
+ type QuorumQualityCriterion = FieldTarget<QuorumNodeSettings["qualityCriteria"][number]>;
125
+ type QuorumExam = FieldExam<NonNullable<QuorumNodeSettings["exam"]>>;
119
126
 
120
- type MatchPredicate = Omit<MatchNodeSettings['conditions'][number]['predicates'][number], 'fieldProxyId'> & {
127
+ type MatchPredicate = Omit<
128
+ MatchNodeSettings["conditions"][number]["predicates"][number],
129
+ "fieldProxyId"
130
+ > & {
121
131
  readonly field: Field;
122
132
  };
123
133
 
124
134
  export type RootAuthoringSettings = Omit<
125
135
  RootNodeSettings,
126
- 'datasetId' | 'itemFilter' | 'startedFieldProxies' | 'completedFieldProxies'
136
+ "datasetId" | "itemFilter" | "startedFieldProxies" | "completedFieldProxies"
127
137
  > & {
128
138
  readonly started: ReadonlyArray<RootFieldReference>;
129
139
  readonly completed: ReadonlyArray<RootFieldReference>;
130
140
  };
131
141
 
132
- export type CodeAuthoringSettings = Omit<CodeNodeSettings, 'fieldProxies'> & {
142
+ export type CodeAuthoringSettings = Omit<CodeNodeSettings, "fieldProxies"> & {
133
143
  readonly fieldProxies: {
134
144
  readonly input: ReadonlyArray<FieldProxyReference>;
135
145
  readonly output: ReadonlyArray<FieldProxyReference>;
@@ -138,23 +148,23 @@ export type CodeAuthoringSettings = Omit<CodeNodeSettings, 'fieldProxies'> & {
138
148
 
139
149
  export type GenerationAuthoringSettings = Omit<
140
150
  GenerationNodeSettings,
141
- 'about' | 'entities' | 'qualityCriteria' | 'instruction' | 'ui' | 'onboarding' | 'exam'
151
+ "about" | "entities" | "qualityCriteria" | "instruction" | "ui" | "onboarding" | "exam"
142
152
  > & {
143
- readonly about?: Omit<GenerationNodeSettings['about'], 'name'> & { readonly name?: string };
153
+ readonly about?: Omit<GenerationNodeSettings["about"], "name"> & { readonly name?: string };
144
154
  readonly entities: {
145
155
  readonly input: ReadonlyArray<GenerationInputEntity>;
146
156
  readonly output: ReadonlyArray<GenerationOutputEntity>;
147
157
  };
148
158
  readonly qualityCriteria?: ReadonlyArray<GenerationQualityCriterion>;
149
159
  readonly instruction?: string;
150
- readonly ui?: GenerationNodeSettings['ui'];
151
- readonly onboarding?: GenerationNodeSettings['onboarding'];
160
+ readonly ui?: GenerationNodeSettings["ui"];
161
+ readonly onboarding?: GenerationNodeSettings["onboarding"];
152
162
  readonly exam?: GenerationExam;
153
163
  };
154
164
 
155
- export type MatchAuthoringSettings = Omit<MatchNodeSettings, 'conditions' | 'fieldProxies'> & {
165
+ export type MatchAuthoringSettings = Omit<MatchNodeSettings, "conditions" | "fieldProxies"> & {
156
166
  readonly conditions: ReadonlyArray<
157
- Omit<MatchNodeSettings['conditions'][number], 'predicates'> & {
167
+ Omit<MatchNodeSettings["conditions"][number], "predicates"> & {
158
168
  readonly predicates: ReadonlyArray<MatchPredicate>;
159
169
  }
160
170
  >;
@@ -163,7 +173,10 @@ export type MatchAuthoringSettings = Omit<MatchNodeSettings, 'conditions' | 'fie
163
173
  };
164
174
  };
165
175
 
166
- export type QuorumAuthoringSettings = Omit<QuorumNodeSettings, 'entities' | 'qualityCriteria' | 'exam'> & {
176
+ export type QuorumAuthoringSettings = Omit<
177
+ QuorumNodeSettings,
178
+ "entities" | "qualityCriteria" | "exam"
179
+ > & {
167
180
  readonly entities: {
168
181
  readonly input: ReadonlyArray<QuorumInputEntity>;
169
182
  readonly output: ReadonlyArray<QuorumOutputEntity>;
@@ -172,17 +185,19 @@ export type QuorumAuthoringSettings = Omit<QuorumNodeSettings, 'entities' | 'qua
172
185
  readonly exam?: QuorumExam;
173
186
  };
174
187
 
175
- type WireNode = PipelineGraphV1['nodes'][number];
188
+ type WireNode = PipelineGraphV1["nodes"][number];
176
189
 
177
- interface AuthoredNode<Node extends WireNode, Settings = Node['settings']> {
190
+ interface AuthoredNode<Node extends WireNode, Settings = Node["settings"]> {
178
191
  readonly id: string;
179
- readonly type: Node['type'];
192
+ readonly type: Node["type"];
180
193
  readonly settings: Settings;
181
194
  readonly [NODE_FIELD_REFERENCES]: ReadonlyArray<Field>;
182
195
  }
183
196
 
184
- export interface AuthoredRootNode
185
- extends AuthoredNode<RootNode, Omit<RootNodeSettings, 'datasetId' | 'itemFilter'>> {
197
+ export interface AuthoredRootNode extends AuthoredNode<
198
+ RootNode,
199
+ Omit<RootNodeSettings, "datasetId" | "itemFilter">
200
+ > {
186
201
  readonly started: OutputPort;
187
202
  readonly completed: InputPort;
188
203
  }
@@ -215,15 +230,15 @@ export type PipelineNode =
215
230
  | AuthoredMatchNode
216
231
  | AuthoredQuorumNode;
217
232
 
218
- type UnboundRootNode = Omit<RootNode, 'settings'> & {
219
- readonly settings: Omit<RootNodeSettings, 'datasetId' | 'itemFilter'>;
233
+ type UnboundRootNode = Omit<RootNode, "settings"> & {
234
+ readonly settings: Omit<RootNodeSettings, "datasetId" | "itemFilter">;
220
235
  };
221
236
 
222
237
  export type UnboundPipelineGraphV1 = {
223
238
  readonly version: 1;
224
- readonly fields: ReadonlyArray<PipelineGraphV1['fields'][number]>;
239
+ readonly fields: ReadonlyArray<PipelineGraphV1["fields"][number]>;
225
240
  readonly nodes: ReadonlyArray<UnboundRootNode | Exclude<WireNode, RootNode>>;
226
- readonly connections: ReadonlyArray<PipelineGraphV1['connections'][number]>;
241
+ readonly connections: ReadonlyArray<PipelineGraphV1["connections"][number]>;
227
242
  };
228
243
 
229
244
  export type PipelineDefinitionInput<DatasetName extends string = string> = {
@@ -249,34 +264,36 @@ function hash(value: string): string {
249
264
  result ^= value.charCodeAt(index);
250
265
  result = Math.imul(result, 0x01000193);
251
266
  }
252
- return (result >>> 0).toString(16).padStart(8, '0');
267
+ return (result >>> 0).toString(16).padStart(8, "0");
253
268
  }
254
269
 
255
270
  function readableName(name: string): string {
256
- return name
257
- .normalize('NFKD')
258
- .replace(/[\u0300-\u036f]/g, '')
259
- .toLowerCase()
260
- .replace(/[^a-z0-9]+/g, '_')
261
- .replace(/^_+|_+$/g, '')
262
- .slice(0, 48) || 'unnamed';
263
- }
264
-
265
- function mintedId(kind: 'field' | Exclude<WireNode['type'], 'root'>, name: string): string {
266
- const prefix = kind === 'field' ? 'fp' : kind;
271
+ return (
272
+ name
273
+ .normalize("NFKD")
274
+ .replace(/[\u0300-\u036f]/g, "")
275
+ .toLowerCase()
276
+ .replace(/[^a-z0-9]+/g, "_")
277
+ .replace(/^_+|_+$/g, "")
278
+ .slice(0, 48) || "unnamed"
279
+ );
280
+ }
281
+
282
+ function mintedId(kind: "field" | Exclude<WireNode["type"], "root">, name: string): string {
283
+ const prefix = kind === "field" ? "fp" : kind;
267
284
  return `${prefix}_${readableName(name)}_${hash(`${kind}:${name}`)}`;
268
285
  }
269
286
 
270
287
  function inputPort(node: string, handle: string): InputPort {
271
- return { node, handle, direction: 'input', [PORT_DEFINITION]: 'input' };
288
+ return { node, handle, direction: "input", [PORT_DEFINITION]: "input" };
272
289
  }
273
290
 
274
291
  function outputPort(node: string, handle: string): OutputPort {
275
- return { node, handle, direction: 'output', [PORT_DEFINITION]: 'output' };
292
+ return { node, handle, direction: "output", [PORT_DEFINITION]: "output" };
276
293
  }
277
294
 
278
295
  function isField(value: unknown): value is Field {
279
- return typeof value === 'object' && value !== null && FIELD_DEFINITION in value;
296
+ return typeof value === "object" && value !== null && FIELD_DEFINITION in value;
280
297
  }
281
298
 
282
299
  function collectFieldReferences(value: unknown, references: Field[]): void {
@@ -288,7 +305,7 @@ function collectFieldReferences(value: unknown, references: Field[]): void {
288
305
  for (const item of value) collectFieldReferences(item, references);
289
306
  return;
290
307
  }
291
- if (typeof value === 'object' && value !== null) {
308
+ if (typeof value === "object" && value !== null) {
292
309
  for (const item of Object.values(value)) collectFieldReferences(item, references);
293
310
  }
294
311
  }
@@ -310,19 +327,21 @@ function compileRootFieldProxy(reference: RootFieldReference): { id: string; opt
310
327
  }
311
328
 
312
329
  function compileGenerationInputRelation(
313
- relation: NonNullable<GenerationInputEntity['relations']>[number]
314
- ): NonNullable<GenerationNodeSettings['entities']['input'][number]['relations']>[number] {
330
+ relation: NonNullable<GenerationInputEntity["relations"]>[number],
331
+ ): NonNullable<GenerationNodeSettings["entities"]["input"][number]["relations"]>[number] {
315
332
  const { field: referencedField, ...settings } = relation;
316
333
  return { ...settings, id: referencedField.id };
317
334
  }
318
335
 
319
- function compileGenerationInputEntity(entity: GenerationInputEntity): GenerationNodeSettings['entities']['input'][number] {
336
+ function compileGenerationInputEntity(
337
+ entity: GenerationInputEntity,
338
+ ): GenerationNodeSettings["entities"]["input"][number] {
320
339
  if (entity.dynamic === true) {
321
340
  const {
322
341
  field: referencedField,
323
342
  optional = false,
324
- jsonPath = '$',
325
- description = '',
343
+ jsonPath = "$",
344
+ description = "",
326
345
  relations = [],
327
346
  ...settings
328
347
  } = entity;
@@ -332,14 +351,14 @@ function compileGenerationInputEntity(entity: GenerationInputEntity): Generation
332
351
  optional,
333
352
  jsonPath,
334
353
  description,
335
- relations: relations.map(compileGenerationInputRelation)
354
+ relations: relations.map(compileGenerationInputRelation),
336
355
  };
337
356
  }
338
357
  const {
339
358
  field: referencedField,
340
359
  optional = false,
341
- jsonPath = '$',
342
- description = '',
360
+ jsonPath = "$",
361
+ description = "",
343
362
  relations = [],
344
363
  dynamic = false,
345
364
  ...settings
@@ -351,51 +370,54 @@ function compileGenerationInputEntity(entity: GenerationInputEntity): Generation
351
370
  jsonPath,
352
371
  description,
353
372
  relations: relations.map(compileGenerationInputRelation),
354
- dynamic
373
+ dynamic,
355
374
  };
356
375
  }
357
376
 
358
- function compileGenerationSettings(name: string, settings: GenerationAuthoringSettings): GenerationNodeSettings {
377
+ function compileGenerationSettings(
378
+ name: string,
379
+ settings: GenerationAuthoringSettings,
380
+ ): GenerationNodeSettings {
359
381
  const { exam, ...settingsWithoutExam } = settings;
360
382
  return {
361
383
  ...settingsWithoutExam,
362
384
  about: { ...settings.about, name: settings.about?.name ?? name },
363
385
  entities: {
364
386
  input: settings.entities.input.map(compileGenerationInputEntity),
365
- output: settings.entities.output.map(compileGenerationInputEntity)
387
+ output: settings.entities.output.map(compileGenerationInputEntity),
366
388
  },
367
389
  qualityCriteria: (settings.qualityCriteria ?? []).map((criterion) => ({
368
390
  ...criterion,
369
- targetEntity: criterion.targetEntity.id
391
+ targetEntity: criterion.targetEntity.id,
370
392
  })),
371
- instruction: settings.instruction ?? '',
393
+ instruction: settings.instruction ?? "",
372
394
  ui: settings.ui ?? {
373
- code: '<></>',
374
- data: '{}',
375
- preprocess: 'return {}',
376
- postprocess: 'return {};',
377
- constants: '{}'
395
+ code: "<></>",
396
+ data: "{}",
397
+ preprocess: "return {}",
398
+ postprocess: "return {};",
399
+ constants: "{}",
378
400
  },
379
401
  onboarding: settings.onboarding ?? {
380
402
  enabled: true,
381
403
  steps: [
382
404
  {
383
- id: 'step-1',
405
+ id: "step-1",
384
406
  blocks: [
385
407
  {
386
- id: 'block-1',
387
- type: 'text',
388
- content: [{ type: 'markdown', value: 'Welcome to the onboarding!' }]
389
- }
390
- ]
391
- }
408
+ id: "block-1",
409
+ type: "text",
410
+ content: [{ type: "markdown", value: "Welcome to the onboarding!" }],
411
+ },
412
+ ],
413
+ },
392
414
  ],
393
415
  info: {
394
- estimatedTime: '30',
395
- role: 'Annotator',
396
- goal: 'Successfully complete all onboarding steps',
397
- successCriteria: 'Pass quiz, accept terms, and provide valid explanation'
398
- }
416
+ estimatedTime: "30",
417
+ role: "Annotator",
418
+ goal: "Successfully complete all onboarding steps",
419
+ successCriteria: "Pass quiz, accept terms, and provide valid explanation",
420
+ },
399
421
  },
400
422
  ...(exam === undefined
401
423
  ? {}
@@ -404,132 +426,135 @@ function compileGenerationSettings(name: string, settings: GenerationAuthoringSe
404
426
  ...exam,
405
427
  gradedEntities: exam.gradedEntities.map((entity) => ({
406
428
  ...entity,
407
- targetEntity: entity.targetEntity.id
408
- }))
409
- }
410
- })
429
+ targetEntity: entity.targetEntity.id,
430
+ })),
431
+ },
432
+ }),
411
433
  };
412
434
  }
413
435
 
414
436
  export function field<const Name extends string>(
415
437
  name: Name,
416
438
  jsonSchema: JsonSchema,
417
- options: AuthoringIdOptions = {}
439
+ options: AuthoringIdOptions = {},
418
440
  ): Field<Name> {
419
441
  return {
420
- id: options.id ?? mintedId('field', name),
442
+ id: options.id ?? mintedId("field", name),
421
443
  name,
422
444
  jsonSchema,
423
- [FIELD_DEFINITION]: true
445
+ [FIELD_DEFINITION]: true,
424
446
  };
425
447
  }
426
448
 
427
- function createRoot(settings: RootAuthoringSettings, options: AuthoringIdOptions = {}): AuthoredRootNode {
428
- const id = options.id ?? 'root';
449
+ function createRoot(
450
+ settings: RootAuthoringSettings,
451
+ options: AuthoringIdOptions = {},
452
+ ): AuthoredRootNode {
453
+ const id = options.id ?? "root";
429
454
  const { started, completed, ...settingsWithoutFieldProxies } = settings;
430
455
  return {
431
456
  id,
432
- type: 'root',
457
+ type: "root",
433
458
  settings: {
434
459
  ...settingsWithoutFieldProxies,
435
460
  startedFieldProxies: { output: started.map(compileRootFieldProxy) },
436
- completedFieldProxies: { input: completed.map(compileRootFieldProxy) }
461
+ completedFieldProxies: { input: completed.map(compileRootFieldProxy) },
437
462
  },
438
- started: outputPort(id, 'started'),
439
- completed: inputPort(id, 'completed'),
440
- [NODE_FIELD_REFERENCES]: referencedFields(settings)
463
+ started: outputPort(id, "started"),
464
+ completed: inputPort(id, "completed"),
465
+ [NODE_FIELD_REFERENCES]: referencedFields(settings),
441
466
  };
442
467
  }
443
468
 
444
469
  export const root = Object.assign(createRoot, {
445
- started: outputPort('root', 'started'),
446
- completed: inputPort('root', 'completed')
470
+ started: outputPort("root", "started"),
471
+ completed: inputPort("root", "completed"),
447
472
  });
448
473
 
449
474
  export function code(
450
475
  name: string,
451
476
  settings: CodeAuthoringSettings,
452
- options: AuthoringIdOptions = {}
477
+ options: AuthoringIdOptions = {},
453
478
  ): AuthoredCodeNode {
454
- const id = options.id ?? mintedId('code', name);
479
+ const id = options.id ?? mintedId("code", name);
455
480
  return {
456
481
  id,
457
- type: 'code',
482
+ type: "code",
458
483
  settings: {
459
484
  ...settings,
460
485
  fieldProxies: {
461
486
  input: settings.fieldProxies.input.map(compileFieldProxy),
462
- output: settings.fieldProxies.output.map(compileFieldProxy)
463
- }
487
+ output: settings.fieldProxies.output.map(compileFieldProxy),
488
+ },
464
489
  },
465
- input: inputPort(id, 'input'),
466
- output: outputPort(id, 'output'),
467
- [NODE_FIELD_REFERENCES]: referencedFields(settings)
490
+ input: inputPort(id, "input"),
491
+ output: outputPort(id, "output"),
492
+ [NODE_FIELD_REFERENCES]: referencedFields(settings),
468
493
  };
469
494
  }
470
495
 
471
496
  export function generation(
472
497
  name: string,
473
498
  settings: GenerationAuthoringSettings,
474
- options: AuthoringIdOptions = {}
499
+ options: AuthoringIdOptions = {},
475
500
  ): AuthoredGenerationNode {
476
- const id = options.id ?? mintedId('generation', name);
501
+ const id = options.id ?? mintedId("generation", name);
477
502
  return {
478
503
  id,
479
- type: 'generation',
504
+ type: "generation",
480
505
  settings: compileGenerationSettings(name, settings),
481
- input: inputPort(id, 'input'),
482
- output: outputPort(id, 'output'),
483
- [NODE_FIELD_REFERENCES]: referencedFields(settings)
506
+ input: inputPort(id, "input"),
507
+ output: outputPort(id, "output"),
508
+ [NODE_FIELD_REFERENCES]: referencedFields(settings),
484
509
  };
485
510
  }
486
511
 
487
512
  export function match(
488
513
  name: string,
489
514
  settings: MatchAuthoringSettings,
490
- options: AuthoringIdOptions = {}
515
+ options: AuthoringIdOptions = {},
491
516
  ): AuthoredMatchNode {
492
- const id = options.id ?? mintedId('match', name);
517
+ const id = options.id ?? mintedId("match", name);
493
518
  return {
494
519
  id,
495
- type: 'match',
520
+ type: "match",
496
521
  settings: {
497
522
  ...settings,
498
523
  conditions: settings.conditions.map((condition) => ({
499
524
  ...condition,
500
525
  predicates: condition.predicates.map(({ field: referencedField, ...predicate }) => ({
501
526
  ...predicate,
502
- fieldProxyId: referencedField.id
503
- }))
527
+ fieldProxyId: referencedField.id,
528
+ })),
504
529
  })),
505
- fieldProxies: { input: settings.fieldProxies.input.map(compileFieldProxy) }
530
+ fieldProxies: { input: settings.fieldProxies.input.map(compileFieldProxy) },
506
531
  },
507
- input: inputPort(id, 'input'),
508
- default: outputPort(id, 'default'),
532
+ input: inputPort(id, "input"),
533
+ default: outputPort(id, "default"),
509
534
  matched: (index) => outputPort(id, `matched_${index}`),
510
- [NODE_FIELD_REFERENCES]: referencedFields(settings)
535
+ [NODE_FIELD_REFERENCES]: referencedFields(settings),
511
536
  };
512
537
  }
513
538
 
514
539
  export function quorum(
515
540
  name: string,
516
541
  settings: QuorumAuthoringSettings,
517
- options: AuthoringIdOptions = {}
542
+ options: AuthoringIdOptions = {},
518
543
  ): AuthoredQuorumNode {
519
- const id = options.id ?? mintedId('quorum', name);
544
+ const id = options.id ?? mintedId("quorum", name);
520
545
  const { exam, ...settingsWithoutExam } = settings;
521
546
  return {
522
547
  id,
523
- type: 'quorum',
548
+ type: "quorum",
524
549
  settings: {
525
550
  ...settingsWithoutExam,
526
551
  entities: {
527
552
  input: settings.entities.input.map(compileGenerationInputEntity),
528
- output: settings.entities.output.map(compileGenerationInputEntity)
553
+ output: settings.entities.output.map(compileGenerationInputEntity),
529
554
  },
530
555
  qualityCriteria: settings.qualityCriteria.map((criterion) => ({
531
556
  ...criterion,
532
- targetEntity: criterion.targetEntity.id
557
+ targetEntity: criterion.targetEntity.id,
533
558
  })),
534
559
  ...(exam === undefined
535
560
  ? {}
@@ -538,33 +563,36 @@ export function quorum(
538
563
  ...exam,
539
564
  gradedEntities: exam.gradedEntities.map((entity) => ({
540
565
  ...entity,
541
- targetEntity: entity.targetEntity.id
542
- }))
543
- }
544
- })
566
+ targetEntity: entity.targetEntity.id,
567
+ })),
568
+ },
569
+ }),
545
570
  },
546
- input: inputPort(id, 'input'),
547
- output: outputPort(id, 'output'),
548
- [NODE_FIELD_REFERENCES]: referencedFields(settings)
571
+ input: inputPort(id, "input"),
572
+ output: outputPort(id, "output"),
573
+ [NODE_FIELD_REFERENCES]: referencedFields(settings),
549
574
  };
550
575
  }
551
576
 
552
577
  export function connect(
553
578
  from: OutputPort,
554
579
  to: InputPort,
555
- settings: PipelineGraphV1['connections'][number]['settings'] = {}
580
+ settings: PipelineGraphV1["connections"][number]["settings"] = {},
556
581
  ): Connection {
557
582
  return { from, to, settings };
558
583
  }
559
584
 
560
585
  export function definePipeline<const DatasetName extends string>(
561
- definition: PipelineDefinitionInput<DatasetName>
586
+ definition: PipelineDefinitionInput<DatasetName>,
562
587
  ): PipelineDefinition<DatasetName> {
563
- assert(definition.dataset.length > 0, 'Dataset name must not be empty');
588
+ assert(definition.dataset.length > 0, "Dataset name must not be empty");
564
589
  const declaredFields = new Set(definition.fields);
565
590
  const declaredFieldIds = new Set<string>();
566
591
  for (const declaredField of definition.fields) {
567
- assert(!declaredFieldIds.has(declaredField.id), `Field id "${declaredField.id}" is declared more than once`);
592
+ assert(
593
+ !declaredFieldIds.has(declaredField.id),
594
+ `Field id "${declaredField.id}" is declared more than once`,
595
+ );
568
596
  declaredFieldIds.add(declaredField.id);
569
597
  }
570
598
 
@@ -575,14 +603,20 @@ export function definePipeline<const DatasetName extends string>(
575
603
  for (const referencedField of node[NODE_FIELD_REFERENCES]) {
576
604
  assert(
577
605
  declaredFields.has(referencedField),
578
- `Field "${referencedField.name}" (${referencedField.id}) is referenced by node "${node.id}" but is not declared`
606
+ `Field "${referencedField.name}" (${referencedField.id}) is referenced by node "${node.id}" but is not declared`,
579
607
  );
580
608
  }
581
609
  }
582
610
 
583
611
  for (const connection of definition.connections) {
584
- assert(declaredNodeIds.has(connection.from.node), `Connection source node "${connection.from.node}" is not declared`);
585
- assert(declaredNodeIds.has(connection.to.node), `Connection target node "${connection.to.node}" is not declared`);
612
+ assert(
613
+ declaredNodeIds.has(connection.from.node),
614
+ `Connection source node "${connection.from.node}" is not declared`,
615
+ );
616
+ assert(
617
+ declaredNodeIds.has(connection.to.node),
618
+ `Connection target node "${connection.to.node}" is not declared`,
619
+ );
586
620
  }
587
621
 
588
622
  return {
@@ -593,57 +627,68 @@ export function definePipeline<const DatasetName extends string>(
593
627
  fields: definition.fields.map(({ id, name, jsonSchema }) => ({ id, name, jsonSchema })),
594
628
  nodes: definition.nodes.map((node) => {
595
629
  switch (node.type) {
596
- case 'root':
630
+ case "root":
597
631
  return { id: node.id, type: node.type, settings: node.settings };
598
- case 'code':
632
+ case "code":
599
633
  return { id: node.id, type: node.type, settings: node.settings };
600
- case 'generation':
634
+ case "generation":
601
635
  return { id: node.id, type: node.type, settings: node.settings };
602
- case 'match':
636
+ case "match":
603
637
  return { id: node.id, type: node.type, settings: node.settings };
604
- case 'quorum':
638
+ case "quorum":
605
639
  return { id: node.id, type: node.type, settings: node.settings };
606
640
  }
607
641
  }),
608
642
  connections: definition.connections.map(({ from, to, settings }) => ({
609
643
  from: { node: from.node, handle: from.handle },
610
644
  to: { node: to.node, handle: to.handle },
611
- settings
612
- }))
613
- }
645
+ settings,
646
+ })),
647
+ },
614
648
  };
615
649
  }
616
650
 
617
651
  export function isPipelineDefinition(value: unknown): value is PipelineDefinition {
618
- return typeof value === 'object' && value !== null && PIPELINE_DEFINITION in value;
652
+ return typeof value === "object" && value !== null && PIPELINE_DEFINITION in value;
619
653
  }
620
654
 
621
655
  export function validatePipelineDefinition(value: unknown): asserts value is PipelineDefinition {
622
- assert(isPipelineDefinition(value), 'Invalid pipeline definition: expected a value returned by definePipeline');
623
- assert(value.dataset.length > 0, 'Dataset name must not be empty');
656
+ assert(
657
+ isPipelineDefinition(value),
658
+ "Invalid pipeline definition: expected a value returned by definePipeline",
659
+ );
660
+ assert(value.dataset.length > 0, "Dataset name must not be empty");
624
661
  for (const node of value.graph.nodes) {
625
- if (node.type !== 'root') continue;
626
- assert(!('datasetId' in node.settings), 'An unresolved pipeline definition cannot contain a dataset id');
627
- assert(!('itemFilter' in node.settings), 'An unresolved pipeline definition cannot contain a dataset item filter');
662
+ if (node.type !== "root") continue;
663
+ assert(
664
+ !("datasetId" in node.settings),
665
+ "An unresolved pipeline definition cannot contain a dataset id",
666
+ );
667
+ assert(
668
+ !("itemFilter" in node.settings),
669
+ "An unresolved pipeline definition cannot contain a dataset item filter",
670
+ );
628
671
  }
629
672
  validate(value.graph);
630
673
  }
631
674
 
632
675
  export function resolvePipelineDefinition(
633
676
  definition: PipelineDefinition,
634
- bindings: PipelineDefinitionBindings
677
+ bindings: PipelineDefinitionBindings,
635
678
  ): PipelineGraphV1 {
636
679
  const graph: PipelineGraphV1 = {
637
680
  version: definition.graph.version,
638
681
  fields: definition.graph.fields.map((fieldDefinition) => ({ ...fieldDefinition })),
639
682
  nodes: definition.graph.nodes.map((node) =>
640
- node.type === 'root' ? { ...node, settings: { ...node.settings, datasetId: bindings.datasetId } } : node
683
+ node.type === "root"
684
+ ? { ...node, settings: { ...node.settings, datasetId: bindings.datasetId } }
685
+ : node,
641
686
  ),
642
687
  connections: definition.graph.connections.map((connection) => ({
643
688
  from: { ...connection.from },
644
689
  to: { ...connection.to },
645
- settings: { ...connection.settings }
646
- }))
690
+ settings: { ...connection.settings },
691
+ })),
647
692
  };
648
693
  validate(graph);
649
694
  return graph;