@toloka-platform/sdk 0.1.0-preview.6 → 0.1.0-preview.7

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 (101) hide show
  1. package/README.md +7 -11
  2. package/dist/cli/toloka.js +208 -123
  3. package/dist/cli/toloka.js.jsc +0 -0
  4. package/dist/cli/toloka.js.jsc.sha256 +1 -1
  5. package/dist/cli/toloka.js.sha256 +1 -1
  6. package/docs/authoring.md +17 -4
  7. package/docs/cli-v2.md +3 -3
  8. package/docs/cli.md +29 -16
  9. package/docs/getting-started.md +7 -12
  10. package/docs/limitations.md +6 -6
  11. package/docs/testing-examples.md +248 -0
  12. package/docs/testing.md +115 -0
  13. package/examples/face-image-collection/README.md +7 -1
  14. package/examples/face-image-collection/fields.ts +20 -8
  15. package/examples/face-image-collection/nodes/annotate-faces/node.ts +1 -9
  16. package/examples/face-image-collection/nodes/order-faces/node.ts +1 -1
  17. package/examples/face-image-collection/pipeline.browser.integration.spec.ts +78 -0
  18. package/examples/video-pair-rating/README.md +14 -6
  19. package/examples/video-pair-rating/nodes/adjudicate/node.ts +1 -3
  20. package/examples/video-pair-rating/nodes/adjudicate_op/node.ts +1 -3
  21. package/examples/video-pair-rating/nodes/overall_preference_pick/node.ts +1 -1
  22. package/examples/video-pair-rating/nodes/rate_pair/node.ts +1 -1
  23. package/examples/video-pair-rating/pipeline.browser.integration.spec.ts +267 -0
  24. package/openapi/gen/api.ts +48 -11
  25. package/openapi/gen/models/createDatasetExportBody.ts +3 -3
  26. package/openapi/gen/models/createDatasetExportBodyOneOf.ts +6 -0
  27. package/openapi/gen/models/createDatasetExportBodyOneOfFormat.ts +2 -0
  28. package/openapi/gen/models/createDatasetExportBodyOneOfThree.ts +10 -0
  29. package/openapi/gen/models/createDatasetExportBodyOneOfThreeContract.ts +2 -0
  30. package/openapi/gen/models/createDatasetExportBodyOneOfThreeFormat.ts +2 -0
  31. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilter.ts +9 -0
  32. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldSourcesItem.ts +6 -0
  33. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldSourcesItemSourcesItem.ts +4 -0
  34. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldSourcesItemSourcesItemOneOf.ts +5 -0
  35. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldSourcesItemSourcesItemOneOfTwo.ts +5 -0
  36. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItem.ts +12 -0
  37. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItemOperator.ts +2 -0
  38. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItemSourcesItem.ts +4 -0
  39. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItemSourcesItemOneOf.ts +5 -0
  40. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItemSourcesItemOneOfTwo.ts +5 -0
  41. package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItemValue.ts +2 -0
  42. package/openapi/gen/models/datasetExportReport.ts +9 -0
  43. package/openapi/gen/models/datasetExportReportErrorsItem.ts +5 -0
  44. package/openapi/gen/models/datasetExportReportTokens.ts +7 -0
  45. package/openapi/gen/models/exportJob.ts +2 -0
  46. package/openapi/gen/models/exportJobReport.ts +3 -0
  47. package/openapi/gen/models/index.ts +48 -0
  48. package/openapi/gen/models/sampleDatasetItemsBody.ts +4 -0
  49. package/openapi/gen/models/sampleDatasetItemsBodyAnyOf.ts +8 -0
  50. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilter.ts +9 -0
  51. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldSourcesItem.ts +6 -0
  52. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldSourcesItemSourcesItem.ts +4 -0
  53. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldSourcesItemSourcesItemOneOf.ts +5 -0
  54. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldSourcesItemSourcesItemOneOfTwo.ts +5 -0
  55. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItem.ts +12 -0
  56. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItemOperator.ts +2 -0
  57. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItemSourcesItem.ts +4 -0
  58. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItemSourcesItemOneOf.ts +5 -0
  59. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItemSourcesItemOneOfTwo.ts +5 -0
  60. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItemValue.ts +2 -0
  61. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethree.ts +8 -0
  62. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilter.ts +9 -0
  63. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldSourcesItem.ts +6 -0
  64. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldSourcesItemSourcesItem.ts +4 -0
  65. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldSourcesItemSourcesItemOneOf.ts +5 -0
  66. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldSourcesItemSourcesItemOneOfTwo.ts +5 -0
  67. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItem.ts +12 -0
  68. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItemOperator.ts +2 -0
  69. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItemSourcesItem.ts +4 -0
  70. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItemSourcesItemOneOf.ts +5 -0
  71. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItemSourcesItemOneOfTwo.ts +5 -0
  72. package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItemValue.ts +2 -0
  73. package/openapi/gen/models/sampleJob.ts +13 -0
  74. package/openapi/gen/models/sampleJobStatus.ts +2 -0
  75. package/openapi/gen/models/sampleResult.ts +5 -0
  76. package/package.json +23 -2
  77. package/skills/catalog.json +14 -6
  78. package/skills/toloka-build/SKILL.md +34 -12
  79. package/skills/toloka-build/references/node-semantics.md +27 -24
  80. package/skills/toloka-build/references/workflow-and-cli.md +55 -26
  81. package/skills/toloka-report-issue/references/report-template.md +1 -1
  82. package/src/checks/ast.ts +32 -0
  83. package/src/checks/index.ts +14 -0
  84. package/src/checks/postprocess-collisions.ts +145 -0
  85. package/src/checks/task-ui-declarations.generated.ts +19 -0
  86. package/src/checks/task-ui-typecheck.ts +345 -0
  87. package/src/checks/typescript-vfs.d.ts +26 -0
  88. package/src/checks/typescript-vfs.js +5 -0
  89. package/src/cli/analytics.ts +1 -0
  90. package/src/cli/check.ts +977 -0
  91. package/src/cli/program.ts +147 -19
  92. package/src/cli/project-context.ts +14 -4
  93. package/src/cli/runs.ts +21 -11
  94. package/src/cli/skill.ts +17 -6
  95. package/src/testing/code-node.ts +160 -0
  96. package/src/testing/index.ts +15 -0
  97. package/src/testing/task-ui-browser.ts +120 -0
  98. package/src/testing/task-ui-validation.ts +135 -0
  99. package/src/testing/task-ui.ts +557 -0
  100. package/src/testing/test-evidence.ts +170 -0
  101. package/src/testing/types.ts +141 -0
@@ -0,0 +1,145 @@
1
+ import type { BlockStatement, Expression, JSXAttribute, ObjectExpression } from "oxc-parser";
2
+ import { Visitor } from "oxc-parser";
3
+
4
+ import { parseProgram, resolveAttributeString } from "./ast";
5
+ import type { TaskUiDiagnostic, TaskUiSource } from "./task-ui-typecheck";
6
+
7
+ // Collisions in conditional return branches are errors too: a submission whose data reaches the
8
+ // branch still dies at getData().
9
+ export const findPostprocessKeyCollisions = (
10
+ ui: Pick<TaskUiSource, "code" | "postprocess">,
11
+ ): TaskUiDiagnostic[] => {
12
+ const returnedKeys = extractPostprocessReturnedKeys(ui.postprocess);
13
+ if (!returnedKeys || returnedKeys.size === 0) return [];
14
+ const bindings = extractFieldPathBindings(ui.code);
15
+ if (!bindings || bindings.size === 0) return [];
16
+
17
+ return [...bindings]
18
+ .filter(([key]) => returnedKeys.has(key))
19
+ .sort(([keyA], [keyB]) => keyA.localeCompare(keyB))
20
+ .map(([key, path]) => {
21
+ const binding =
22
+ path === key
23
+ ? `path="${path}"`
24
+ : `path="${path}" — a dotted path binds its top-level key "${key}"`;
25
+ return {
26
+ severity: "error",
27
+ text:
28
+ `Postprocess returns the key "${key}", but a Field component is bound to it (${binding}). ` +
29
+ `Every submission of this interface fails at runtime with: Postprocess cannot write key '${key}' because a component is bound to it. ` +
30
+ `Fix: bind the Field to a raw key (e.g. path="raw_${key}") and derive "${key}" in postprocess, or remove "${key}" from the postprocess return.`,
31
+ };
32
+ });
33
+ };
34
+
35
+ const extractFieldPathBindings = (code: string): Map<string, string> | null => {
36
+ const program = parseProgram("code.tsx", `(\n${code}\n)`, "tsx");
37
+ if (!program) return null;
38
+ const bindings = new Map<string, string>();
39
+ const visitor = new Visitor({
40
+ JSXOpeningElement(node) {
41
+ let name = node.name;
42
+ while (name.type === "JSXMemberExpression") name = name.object;
43
+ if (name.type !== "JSXIdentifier" || name.name !== "Field") return;
44
+ const pathAttribute = node.attributes.find(
45
+ (attribute): attribute is JSXAttribute =>
46
+ attribute.type === "JSXAttribute" &&
47
+ attribute.name.type === "JSXIdentifier" &&
48
+ attribute.name.name === "path",
49
+ );
50
+ const path = resolveAttributeString(pathAttribute);
51
+ if (!path) return;
52
+ const key = path.split(/[.[]/u)[0];
53
+ if (key && !bindings.has(key)) bindings.set(key, path);
54
+ },
55
+ });
56
+ visitor.visit(program);
57
+ return bindings;
58
+ };
59
+
60
+ const extractPostprocessReturnedKeys = (postprocess: string): Set<string> | null => {
61
+ const program = parseProgram("postprocess.ts", `(() => {\n${postprocess}\n})`, "ts");
62
+ if (!program) return null;
63
+ const statement = program.body.length === 1 ? program.body[0] : undefined;
64
+ // postprocess text can escape the wrapper braces yet still parse — `}) , (() => {` turns the
65
+ // expression into a SequenceExpression — so only the intact wrapper shape is analyzable
66
+ if (
67
+ statement?.type !== "ExpressionStatement" ||
68
+ statement.expression.type !== "ArrowFunctionExpression" ||
69
+ statement.expression.body.type !== "BlockStatement"
70
+ ) {
71
+ return null;
72
+ }
73
+ const keys = new Set<string>();
74
+ for (const returned of collectOwnReturnArguments(statement.expression.body)) {
75
+ collectObjectLiteralKeys(returned, keys);
76
+ }
77
+ return keys;
78
+ };
79
+
80
+ const functionNodeTypes = new Set([
81
+ "ArrowFunctionExpression",
82
+ "FunctionExpression",
83
+ "FunctionDeclaration",
84
+ ]);
85
+
86
+ const collectOwnReturnArguments = (body: BlockStatement): Expression[] => {
87
+ const returned: Expression[] = [];
88
+ const visit = (node: unknown): void => {
89
+ if (!node || typeof node !== "object") return;
90
+ if (Array.isArray(node)) {
91
+ for (const item of node) visit(item);
92
+ return;
93
+ }
94
+ const candidate = node as { type?: unknown } & Record<string, unknown>;
95
+ if (typeof candidate.type === "string") {
96
+ if (functionNodeTypes.has(candidate.type)) return;
97
+ if (candidate.type === "ReturnStatement" && candidate.argument) {
98
+ returned.push(candidate.argument as Expression);
99
+ }
100
+ }
101
+ for (const value of Object.values(candidate)) visit(value);
102
+ };
103
+ visit(body.body);
104
+ return returned;
105
+ };
106
+
107
+ const collectObjectLiteralKeys = (expression: Expression, keys: Set<string>): void => {
108
+ if (expression.type === "ObjectExpression") {
109
+ collectPropertyKeys(expression, keys);
110
+ } else if (expression.type === "ConditionalExpression") {
111
+ collectObjectLiteralKeys(expression.consequent, keys);
112
+ collectObjectLiteralKeys(expression.alternate, keys);
113
+ } else if (expression.type === "LogicalExpression") {
114
+ collectObjectLiteralKeys(expression.left, keys);
115
+ collectObjectLiteralKeys(expression.right, keys);
116
+ } else if (expression.type === "SequenceExpression") {
117
+ const last = expression.expressions.at(-1);
118
+ if (last) collectObjectLiteralKeys(last, keys);
119
+ } else if (
120
+ expression.type === "TSAsExpression" ||
121
+ expression.type === "TSSatisfiesExpression" ||
122
+ expression.type === "TSNonNullExpression" ||
123
+ expression.type === "TSTypeAssertion"
124
+ ) {
125
+ collectObjectLiteralKeys(expression.expression, keys);
126
+ }
127
+ };
128
+
129
+ const collectPropertyKeys = (objectExpression: ObjectExpression, keys: Set<string>): void => {
130
+ for (const property of objectExpression.properties) {
131
+ if (property.type === "SpreadElement") {
132
+ if (property.argument.type === "ObjectExpression")
133
+ collectPropertyKeys(property.argument, keys);
134
+ continue;
135
+ }
136
+ if (!property.computed && property.key.type === "Identifier") {
137
+ keys.add(property.key.name);
138
+ } else if (
139
+ property.key.type === "Literal" &&
140
+ (typeof property.key.value === "string" || typeof property.key.value === "number")
141
+ ) {
142
+ keys.add(String(property.key.value));
143
+ }
144
+ }
145
+ };