apify-schema-tools 3.1.0 → 3.2.1

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 (130) hide show
  1. package/.node-version +1 -1
  2. package/CHANGELOG.md +7 -1
  3. package/biome.json +8 -2
  4. package/dist/apify-schema-tools.js +12 -9
  5. package/dist/apify-schema-tools.js.map +1 -1
  6. package/dist/apify.d.ts +1 -1
  7. package/dist/apify.d.ts.map +1 -1
  8. package/dist/apify.js +19 -5
  9. package/dist/apify.js.map +1 -1
  10. package/dist/cli/check.d.ts +5 -0
  11. package/dist/cli/check.d.ts.map +1 -0
  12. package/dist/cli/check.js +86 -0
  13. package/dist/cli/check.js.map +1 -0
  14. package/dist/cli/init.d.ts +5 -0
  15. package/dist/cli/init.d.ts.map +1 -0
  16. package/dist/cli/init.js +92 -0
  17. package/dist/cli/init.js.map +1 -0
  18. package/dist/cli/sync.d.ts +5 -0
  19. package/dist/cli/sync.d.ts.map +1 -0
  20. package/dist/cli/sync.js +112 -0
  21. package/dist/cli/sync.js.map +1 -0
  22. package/dist/configuration.d.ts +14 -5
  23. package/dist/configuration.d.ts.map +1 -1
  24. package/dist/configuration.js +9 -5
  25. package/dist/configuration.js.map +1 -1
  26. package/dist/main.d.ts +4 -0
  27. package/dist/main.d.ts.map +1 -0
  28. package/dist/main.js +19 -0
  29. package/dist/main.js.map +1 -0
  30. package/dist/middle-schema/compare-schemas.d.ts +3 -0
  31. package/dist/middle-schema/compare-schemas.d.ts.map +1 -0
  32. package/dist/middle-schema/compare-schemas.js +90 -0
  33. package/dist/middle-schema/compare-schemas.js.map +1 -0
  34. package/dist/middle-schema/generate-typescript.d.ts +7 -0
  35. package/dist/middle-schema/generate-typescript.d.ts.map +1 -0
  36. package/dist/middle-schema/generate-typescript.js +70 -0
  37. package/dist/middle-schema/generate-typescript.js.map +1 -0
  38. package/dist/middle-schema/parse-json-schema.d.ts +4 -0
  39. package/dist/middle-schema/parse-json-schema.d.ts.map +1 -0
  40. package/dist/middle-schema/parse-json-schema.js +65 -0
  41. package/dist/middle-schema/parse-json-schema.js.map +1 -0
  42. package/dist/middle-schema/parse-typescript.d.ts +4 -0
  43. package/dist/middle-schema/parse-typescript.d.ts.map +1 -0
  44. package/dist/middle-schema/parse-typescript.js +199 -0
  45. package/dist/middle-schema/parse-typescript.js.map +1 -0
  46. package/dist/middle-schema/schema-types.d.ts +24 -0
  47. package/dist/middle-schema/schema-types.d.ts.map +1 -0
  48. package/dist/middle-schema/schema-types.js +14 -0
  49. package/dist/middle-schema/schema-types.js.map +1 -0
  50. package/dist/middle-schema/schema.d.ts +24 -0
  51. package/dist/middle-schema/schema.d.ts.map +1 -0
  52. package/dist/middle-schema/schema.js +14 -0
  53. package/dist/middle-schema/schema.js.map +1 -0
  54. package/dist/schema/entities/abstract-entity.d.ts +5 -0
  55. package/dist/schema/entities/abstract-entity.d.ts.map +1 -0
  56. package/dist/schema/entities/abstract-entity.js +3 -0
  57. package/dist/schema/entities/abstract-entity.js.map +1 -0
  58. package/dist/schema/entities/primitive-union.d.ts +12 -0
  59. package/dist/schema/entities/primitive-union.d.ts.map +1 -0
  60. package/dist/schema/entities/primitive-union.js +74 -0
  61. package/dist/schema/entities/primitive-union.js.map +1 -0
  62. package/dist/schema/entities/primitive.d.ts +15 -0
  63. package/dist/schema/entities/primitive.d.ts.map +1 -0
  64. package/dist/schema/entities/primitive.js +54 -0
  65. package/dist/schema/entities/primitive.js.map +1 -0
  66. package/dist/schema/parsers/json-schema.d.ts +4 -0
  67. package/dist/schema/parsers/json-schema.d.ts.map +1 -0
  68. package/dist/schema/parsers/json-schema.js +12 -0
  69. package/dist/schema/parsers/json-schema.js.map +1 -0
  70. package/dist/schema/parsers/typescript.d.ts +3 -0
  71. package/dist/schema/parsers/typescript.d.ts.map +1 -0
  72. package/dist/schema/parsers/typescript.js +24 -0
  73. package/dist/schema/parsers/typescript.js.map +1 -0
  74. package/dist/schemas/input.d.ts +840 -0
  75. package/dist/schemas/input.d.ts.map +1 -0
  76. package/dist/schemas/input.js +349 -0
  77. package/dist/schemas/input.js.map +1 -0
  78. package/dist/utils/filesystem.d.ts +8 -0
  79. package/dist/utils/filesystem.d.ts.map +1 -0
  80. package/dist/utils/filesystem.js +16 -0
  81. package/dist/utils/filesystem.js.map +1 -0
  82. package/dist/utils/json-schemas-interactive-conflict.d.ts +16 -0
  83. package/dist/utils/json-schemas-interactive-conflict.d.ts.map +1 -0
  84. package/dist/utils/json-schemas-interactive-conflict.js +165 -0
  85. package/dist/utils/json-schemas-interactive-conflict.js.map +1 -0
  86. package/dist/utils/json-schemas.d.ts +42 -0
  87. package/dist/utils/json-schemas.d.ts.map +1 -0
  88. package/dist/utils/json-schemas.js +162 -0
  89. package/dist/utils/json-schemas.js.map +1 -0
  90. package/dist/zod/schemas/input.d.ts +840 -0
  91. package/dist/zod/schemas/input.d.ts.map +1 -0
  92. package/dist/zod/schemas/input.js +393 -0
  93. package/dist/zod/schemas/input.js.map +1 -0
  94. package/package.json +12 -12
  95. package/samples/all-defaults/.actor/input_schema.json +32 -3
  96. package/samples/all-defaults/src-schemas/input.json +2 -1
  97. package/samples/deep-merged-schemas/.actor/input_schema.json +36 -3
  98. package/samples/merged-schemas/.actor/input_schema.json +27 -3
  99. package/samples/package-json-config/.actor/input_schema.json +32 -3
  100. package/samples/package-json-config-merged/.actor/input_schema.json +36 -3
  101. package/src/apify.ts +21 -6
  102. package/src/cli/check.ts +114 -0
  103. package/src/cli/init.ts +125 -0
  104. package/src/cli/sync.ts +164 -0
  105. package/src/configuration.ts +17 -7
  106. package/src/main.ts +25 -0
  107. package/src/middle-schema/compare-schemas.ts +113 -0
  108. package/src/middle-schema/generate-typescript.ts +88 -0
  109. package/src/middle-schema/parse-json-schema.ts +104 -0
  110. package/src/middle-schema/parse-typescript.ts +239 -0
  111. package/src/middle-schema/schema-types.ts +40 -0
  112. package/test/apify.test.ts +410 -2
  113. package/test/cli/check.test.ts +1571 -0
  114. package/test/cli/init.test.ts +459 -0
  115. package/test/cli/sync.test.ts +341 -0
  116. package/test/common.ts +68 -0
  117. package/test/configuration.test.ts +8 -8
  118. package/test/middle-schema/compare-schemas.test.ts +585 -0
  119. package/test/middle-schema/generate-typescript.test.ts +191 -0
  120. package/test/middle-schema/parse-json-schema.test.ts +178 -0
  121. package/test/middle-schema/parse-typescript.test.ts +143 -0
  122. package/test/{json-schema-conflicts.test.ts → utils/json-schemas-interactive-conflict.test.ts} +2 -2
  123. package/test/{json-schemas.test.ts → utils/json-schemas.test.ts} +3 -3
  124. package/src/apify-schema-tools.ts +0 -420
  125. package/src/typescript.ts +0 -563
  126. package/test/apify-schema-tools.test.ts +0 -2216
  127. package/test/typescript.test.ts +0 -1079
  128. /package/src/{filesystem.ts → utils/filesystem.ts} +0 -0
  129. /package/src/{json-schema-conflicts.ts → utils/json-schemas-interactive-conflict.ts} +0 -0
  130. /package/src/{json-schemas.ts → utils/json-schemas.ts} +0 -0
@@ -1,2216 +0,0 @@
1
- import { execSync } from "node:child_process";
2
- import { existsSync, readFileSync } from "node:fs";
3
- import { join } from "node:path";
4
- import type { JSONSchema4 } from "json-schema";
5
- import { afterEach, beforeAll, describe, expect, it } from "vitest";
6
- import { ACTOR_CONFIG_PATH, DATASET_SCHEMA_FIELD } from "../src/apify.js";
7
- import { writeFile } from "../src/filesystem.js";
8
- import { type ObjectSchema, readJsonSchema, writeJsonSchema, writeSchemaToField } from "../src/json-schemas.js";
9
- import { cleanupTestDirectory, getTestDir, setupTestDirectory } from "./common.js";
10
-
11
- const EXEC_CMD = `node ${import.meta.dirname}/../dist/apify-schema-tools.js`;
12
-
13
- const TEST_DIR = getTestDir("apify-schema-tools");
14
- const ACTOR_DIR = join(TEST_DIR, ".actor");
15
- const INPUT_SCHEMA_PATH = join(ACTOR_DIR, "input_schema.json");
16
- const DATASET_SCHEMA_PATH = join(ACTOR_DIR, "dataset_schema.json");
17
-
18
- const ADDITIONAL_PROPERTIES_ERROR_PATTERN =
19
- /If you generated an interface from a JSON schema, pay attention that "additionalProperties" is set to an empty schema by default/;
20
-
21
- function setupTestDirectoryFiles(
22
- srcInputPath: string,
23
- srcInput: ObjectSchema,
24
- srcDatasetPath: string,
25
- srcDataset: ObjectSchema,
26
- addDir?: string,
27
- addInputPath?: string,
28
- addInput?: ObjectSchema,
29
- addDatasetPath?: string,
30
- addDataset?: ObjectSchema,
31
- ): void {
32
- const baseInputSchema: JSONSchema4 = {
33
- title: "Input Schema",
34
- type: "object",
35
- properties: {},
36
- };
37
- writeFile(INPUT_SCHEMA_PATH, JSON.stringify(baseInputSchema, null, 4));
38
-
39
- const datasetSchemaWrapper: JSONSchema4 = {
40
- title: "Dataset Schema",
41
- fields: {},
42
- };
43
- writeFile(DATASET_SCHEMA_PATH, JSON.stringify(datasetSchemaWrapper, null, 4));
44
-
45
- writeJsonSchema(srcInputPath, srcInput);
46
- writeJsonSchema(srcDatasetPath, srcDataset);
47
-
48
- if (addDir) {
49
- if (addInputPath && addInput) {
50
- writeJsonSchema(addInputPath, addInput);
51
- }
52
- if (addDatasetPath && addDataset) {
53
- writeJsonSchema(addDatasetPath, addDataset);
54
- }
55
- }
56
- }
57
-
58
- describe("The apify-schema-tools cli", () => {
59
- beforeAll(() => {
60
- execSync("npm run build", { stdio: "inherit" });
61
- });
62
- afterEach(() => {
63
- cleanupTestDirectory(TEST_DIR);
64
- });
65
-
66
- describe("init command", () => {
67
- it("should initialize configuration and schemas with default values", () => {
68
- setupTestDirectory(TEST_DIR);
69
-
70
- // Create package.json for config writing
71
- const packageJsonPath = join(TEST_DIR, "package.json");
72
- writeFile(
73
- packageJsonPath,
74
- JSON.stringify(
75
- {
76
- name: "test-project",
77
- version: "1.0.0",
78
- },
79
- null,
80
- 2,
81
- ),
82
- );
83
-
84
- // Create existing input schema that will be copied
85
- const existingInputSchema: ObjectSchema = {
86
- title: "Existing Input Schema",
87
- type: "object",
88
- properties: {
89
- name: { type: "string" },
90
- age: { type: "integer" },
91
- },
92
- required: ["name"],
93
- };
94
- writeJsonSchema(INPUT_SCHEMA_PATH, existingInputSchema);
95
-
96
- // Create existing actor config for dataset initialization
97
- const actorConfigPath = join(TEST_DIR, ACTOR_CONFIG_PATH);
98
- const existingActorConfig = {
99
- actorSpecification: 1,
100
- name: "test-actor",
101
- version: "1.0.0",
102
- };
103
- writeFile(actorConfigPath, JSON.stringify(existingActorConfig, null, 4));
104
-
105
- // Create the src-schemas directory that the init command will use
106
-
107
- execSync(`${EXEC_CMD} init`, {
108
- cwd: TEST_DIR,
109
- stdio: "inherit",
110
- });
111
-
112
- // Check that configuration was written to package.json
113
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
114
- expect(packageJson["apify-schema-tools"]).toBeDefined();
115
- expect(packageJson["apify-schema-tools"].input).toEqual(["input", "dataset"]);
116
- expect(packageJson["apify-schema-tools"].output).toEqual(["json-schemas", "ts-types"]);
117
-
118
- // Check that input schema was initialized
119
- const srcInputPath = join(TEST_DIR, "src-schemas", "input.json");
120
- expect(existsSync(srcInputPath)).toBe(true);
121
- const initializedInputSchema = readJsonSchema(srcInputPath);
122
- expect(initializedInputSchema).toEqual(existingInputSchema);
123
-
124
- // Check that dataset schema was initialized
125
- const srcDatasetPath = join(TEST_DIR, "src-schemas", "dataset-item.json");
126
- expect(existsSync(srcDatasetPath)).toBe(true);
127
- const initializedDatasetSchema = readJsonSchema(srcDatasetPath);
128
- expect(initializedDatasetSchema).toEqual({ type: "object", properties: {} });
129
-
130
- // Check that actor config was updated
131
- const updatedActorConfig = JSON.parse(readFileSync(actorConfigPath, "utf8"));
132
- expect(updatedActorConfig.storages?.dataset).toBe("./dataset_schema.json");
133
-
134
- // Check that dataset schema file was created
135
- expect(existsSync(DATASET_SCHEMA_PATH)).toBe(true);
136
- const datasetSchemaFile = JSON.parse(readFileSync(DATASET_SCHEMA_PATH, "utf8"));
137
- expect(datasetSchemaFile).toEqual({
138
- actorSpecification: 1,
139
- fields: { type: "object", properties: {} },
140
- });
141
- });
142
-
143
- it("should initialize with custom options", () => {
144
- setupTestDirectory(TEST_DIR);
145
-
146
- const packageJsonPath = join(TEST_DIR, "package.json");
147
- writeFile(
148
- packageJsonPath,
149
- JSON.stringify(
150
- {
151
- name: "test-project",
152
- version: "1.0.0",
153
- },
154
- null,
155
- 2,
156
- ),
157
- );
158
-
159
- // Create existing input schema
160
- const existingInputSchema: ObjectSchema = {
161
- title: "Custom Input Schema",
162
- type: "object",
163
- properties: {
164
- customField: { type: "string" },
165
- },
166
- required: ["customField"],
167
- };
168
- writeJsonSchema(INPUT_SCHEMA_PATH, existingInputSchema);
169
-
170
- // Create existing actor config
171
- const actorConfigPath = join(TEST_DIR, ACTOR_CONFIG_PATH);
172
- const existingActorConfig = {
173
- actorSpecification: 1,
174
- name: "custom-actor",
175
- version: "2.0.0",
176
- };
177
- writeFile(actorConfigPath, JSON.stringify(existingActorConfig, null, 4));
178
-
179
- // Create the necessary directories that the init command will use
180
-
181
- execSync(
182
- [
183
- `${EXEC_CMD} init`,
184
- "-i input dataset",
185
- "-o json-schemas ts-types",
186
- "--src-input custom-src/custom-input.json",
187
- "--src-dataset custom-src/custom-dataset.json",
188
- "--output-ts-dir custom-output",
189
- "--add-input custom-add/add-input.json",
190
- "--add-dataset custom-add/add-dataset.json",
191
- ].join(" "),
192
- {
193
- cwd: TEST_DIR,
194
- stdio: "inherit",
195
- },
196
- );
197
-
198
- // Check that configuration was written with custom values
199
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
200
- expect(packageJson["apify-schema-tools"]).toBeDefined();
201
- expect(packageJson["apify-schema-tools"].input).toEqual(["input", "dataset"]);
202
- expect(packageJson["apify-schema-tools"].output).toEqual(["json-schemas", "ts-types"]);
203
- expect(packageJson["apify-schema-tools"].srcInput).toBe("custom-src/custom-input.json");
204
- expect(packageJson["apify-schema-tools"].srcDataset).toBe("custom-src/custom-dataset.json");
205
- expect(packageJson["apify-schema-tools"].outputTSDir).toBe("custom-output");
206
- expect(packageJson["apify-schema-tools"].addInput).toBe("custom-add/add-input.json");
207
- expect(packageJson["apify-schema-tools"].addDataset).toBe("custom-add/add-dataset.json");
208
-
209
- // Check that input schema was initialized at custom location
210
- const customSrcInputPath = join(TEST_DIR, "custom-src", "custom-input.json");
211
- expect(existsSync(customSrcInputPath)).toBe(true);
212
- const initializedInputSchema = readJsonSchema(customSrcInputPath);
213
- expect(initializedInputSchema).toEqual(existingInputSchema);
214
-
215
- // Check that additional input schema was initialized
216
- const customAddInputPath = join(TEST_DIR, "custom-add", "add-input.json");
217
- expect(existsSync(customAddInputPath)).toBe(true);
218
- const initializedAddInputSchema = readJsonSchema(customAddInputPath);
219
- expect(initializedAddInputSchema).toEqual({ type: "object", properties: {} });
220
- });
221
-
222
- it("should only create config file when --only-config-file is specified", () => {
223
- setupTestDirectory(TEST_DIR);
224
-
225
- const packageJsonPath = join(TEST_DIR, "package.json");
226
- writeFile(
227
- packageJsonPath,
228
- JSON.stringify(
229
- {
230
- name: "test-project",
231
- version: "1.0.0",
232
- },
233
- null,
234
- 2,
235
- ),
236
- );
237
-
238
- execSync(`${EXEC_CMD} init --only-config-file`, {
239
- cwd: TEST_DIR,
240
- stdio: "inherit",
241
- });
242
-
243
- // Check that configuration was written to package.json
244
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
245
- expect(packageJson["apify-schema-tools"]).toBeDefined();
246
-
247
- // Check that schemas were NOT initialized
248
- const srcInputPath = join(TEST_DIR, "src-schemas", "input.json");
249
- const srcDatasetPath = join(TEST_DIR, "src-schemas", "dataset-item.json");
250
- expect(existsSync(srcInputPath)).toBe(false);
251
- expect(existsSync(srcDatasetPath)).toBe(false);
252
- });
253
-
254
- it("should not create config file when --no-config-file is specified", () => {
255
- setupTestDirectory(TEST_DIR);
256
-
257
- const packageJsonPath = join(TEST_DIR, "package.json");
258
- writeFile(
259
- packageJsonPath,
260
- JSON.stringify(
261
- {
262
- name: "test-project",
263
- version: "1.0.0",
264
- },
265
- null,
266
- 2,
267
- ),
268
- );
269
-
270
- // Create existing input schema
271
- const existingInputSchema: ObjectSchema = {
272
- title: "Test Input Schema",
273
- type: "object",
274
- properties: {
275
- testField: { type: "string" },
276
- },
277
- required: ["testField"],
278
- };
279
- writeJsonSchema(INPUT_SCHEMA_PATH, existingInputSchema);
280
-
281
- // Create existing actor config
282
- const actorConfigPath = join(TEST_DIR, ACTOR_CONFIG_PATH);
283
- const existingActorConfig = {
284
- actorSpecification: 1,
285
- name: "test-actor",
286
- version: "1.0.0",
287
- };
288
- writeFile(actorConfigPath, JSON.stringify(existingActorConfig, null, 4));
289
-
290
- // Create the src-schemas directory that the init command will use
291
-
292
- execSync(`${EXEC_CMD} init --no-config-file`, {
293
- cwd: TEST_DIR,
294
- stdio: "inherit",
295
- });
296
-
297
- // Check that configuration was NOT written to package.json
298
- const packageJson = JSON.parse(readFileSync(packageJsonPath, "utf8"));
299
- expect(packageJson["apify-schema-tools"]).toBeUndefined();
300
-
301
- // Check that schemas were initialized
302
- const srcInputPath = join(TEST_DIR, "src-schemas", "input.json");
303
- expect(existsSync(srcInputPath)).toBe(true);
304
- const initializedInputSchema = readJsonSchema(srcInputPath);
305
- expect(initializedInputSchema).toEqual(existingInputSchema);
306
- });
307
-
308
- it("should throw error when both --only-config-file and --no-config-file are specified", () => {
309
- setupTestDirectory(TEST_DIR);
310
-
311
- const packageJsonPath = join(TEST_DIR, "package.json");
312
- writeFile(
313
- packageJsonPath,
314
- JSON.stringify(
315
- {
316
- name: "test-project",
317
- version: "1.0.0",
318
- },
319
- null,
320
- 2,
321
- ),
322
- );
323
-
324
- expect(() => {
325
- execSync(`${EXEC_CMD} init --only-config-file --no-config-file`, {
326
- cwd: TEST_DIR,
327
- stdio: "inherit",
328
- });
329
- }).toThrow();
330
- });
331
-
332
- it("should handle partial initialization when some files already exist", () => {
333
- setupTestDirectory(TEST_DIR);
334
-
335
- const packageJsonPath = join(TEST_DIR, "package.json");
336
- writeFile(
337
- packageJsonPath,
338
- JSON.stringify(
339
- {
340
- name: "test-project",
341
- version: "1.0.0",
342
- },
343
- null,
344
- 2,
345
- ),
346
- );
347
-
348
- // Create existing input schema
349
- const existingInputSchema: ObjectSchema = {
350
- title: "Existing Input Schema",
351
- type: "object",
352
- properties: {
353
- existingField: { type: "string" },
354
- },
355
- required: ["existingField"],
356
- };
357
- writeJsonSchema(INPUT_SCHEMA_PATH, existingInputSchema);
358
-
359
- // Create existing actor config
360
- const actorConfigPath = join(TEST_DIR, ACTOR_CONFIG_PATH);
361
- const existingActorConfig = {
362
- actorSpecification: 1,
363
- name: "existing-actor",
364
- version: "1.0.0",
365
- };
366
- writeFile(actorConfigPath, JSON.stringify(existingActorConfig, null, 4));
367
-
368
- // Pre-create one of the source schema files
369
- const srcInputPath = join(TEST_DIR, "src-schemas", "input.json");
370
- const preExistingSchema: ObjectSchema = {
371
- title: "Pre-existing Schema",
372
- type: "object",
373
- properties: {
374
- preExistingField: { type: "boolean" },
375
- },
376
- required: ["preExistingField"],
377
- };
378
- writeJsonSchema(srcInputPath, preExistingSchema);
379
-
380
- execSync(`${EXEC_CMD} init`, {
381
- cwd: TEST_DIR,
382
- stdio: "inherit",
383
- });
384
-
385
- // Check that the pre-existing schema was preserved (not overwritten)
386
- const preservedSchema = readJsonSchema(srcInputPath);
387
- expect(preservedSchema).toEqual(preExistingSchema);
388
-
389
- // Check that the dataset schema was still initialized
390
- const srcDatasetPath = join(TEST_DIR, "src-schemas", "dataset-item.json");
391
- expect(existsSync(srcDatasetPath)).toBe(true);
392
- const initializedDatasetSchema = readJsonSchema(srcDatasetPath);
393
- expect(initializedDatasetSchema).toEqual({ type: "object", properties: {} });
394
- });
395
-
396
- it("should throw error when input schema doesn't exist", () => {
397
- setupTestDirectory(TEST_DIR);
398
-
399
- const packageJsonPath = join(TEST_DIR, "package.json");
400
- writeFile(
401
- packageJsonPath,
402
- JSON.stringify(
403
- {
404
- name: "test-project",
405
- version: "1.0.0",
406
- },
407
- null,
408
- 2,
409
- ),
410
- );
411
-
412
- // Don't create the input schema file
413
-
414
- expect(() => {
415
- execSync(`${EXEC_CMD} init`, {
416
- cwd: TEST_DIR,
417
- stdio: "inherit",
418
- });
419
- }).toThrow();
420
- });
421
-
422
- it("should throw error when actor config doesn't exist", () => {
423
- setupTestDirectory(TEST_DIR);
424
-
425
- const packageJsonPath = join(TEST_DIR, "package.json");
426
- writeFile(
427
- packageJsonPath,
428
- JSON.stringify(
429
- {
430
- name: "test-project",
431
- version: "1.0.0",
432
- },
433
- null,
434
- 2,
435
- ),
436
- );
437
-
438
- // Create existing input schema
439
- const existingInputSchema: ObjectSchema = {
440
- title: "Test Input Schema",
441
- type: "object",
442
- properties: {
443
- testField: { type: "string" },
444
- },
445
- required: ["testField"],
446
- };
447
- writeJsonSchema(INPUT_SCHEMA_PATH, existingInputSchema);
448
-
449
- // Don't create the actor config file
450
-
451
- expect(() => {
452
- execSync(`${EXEC_CMD} init`, {
453
- cwd: TEST_DIR,
454
- stdio: "inherit",
455
- });
456
- }).toThrow();
457
- });
458
-
459
- it("should fix the dataset path in actor.json", () => {
460
- setupTestDirectory(TEST_DIR);
461
-
462
- const packageJsonPath = join(TEST_DIR, "package.json");
463
- writeFile(
464
- packageJsonPath,
465
- JSON.stringify(
466
- {
467
- name: "test-project",
468
- version: "1.0.0",
469
- },
470
- null,
471
- 2,
472
- ),
473
- );
474
-
475
- // Create existing actor config with incorrect dataset path
476
- const actorConfigPath = join(TEST_DIR, ACTOR_CONFIG_PATH);
477
- const existingActorConfig = {
478
- actorSpecification: 1,
479
- name: "test-actor",
480
- version: "1.0.0",
481
- storages: {
482
- dataset: "wrong/dataset/path.json",
483
- },
484
- };
485
- writeFile(actorConfigPath, JSON.stringify(existingActorConfig, null, 4));
486
-
487
- execSync(`${EXEC_CMD} init --input dataset`, {
488
- cwd: TEST_DIR,
489
- stdio: "inherit",
490
- });
491
-
492
- // Check that actor config was updated with the correct dataset path
493
- const updatedActorConfig = JSON.parse(readFileSync(actorConfigPath, "utf8"));
494
- expect(updatedActorConfig.storages?.dataset).toBe("./dataset_schema.json");
495
- });
496
- });
497
-
498
- describe("sync command", () => {
499
- it("should sync input schema to JSON Schema and TypeScript types, with default values", async () => {
500
- setupTestDirectory(TEST_DIR);
501
- const srcDir = join(TEST_DIR, "src-schemas");
502
- const srcInputPath = join(srcDir, "input.json");
503
- const srcDatasetPath = join(srcDir, "dataset-item.json");
504
- const outputTsDir = join(TEST_DIR, "src", "generated");
505
- const srcInput: ObjectSchema = {
506
- title: "Input Schema",
507
- type: "object",
508
- properties: {
509
- name: { type: "string" },
510
- age: { type: "integer" },
511
- },
512
- required: ["name"],
513
- };
514
- const srcDataset: ObjectSchema = {
515
- title: "Dataset Item Schema",
516
- type: "object",
517
- properties: {
518
- id: { type: "string" },
519
- value: { type: "number" },
520
- },
521
- required: ["id"],
522
- };
523
- setupTestDirectoryFiles(srcInputPath, srcInput, srcDatasetPath, srcDataset);
524
- execSync(
525
- // Using the default options
526
- `${EXEC_CMD} sync`,
527
- {
528
- cwd: TEST_DIR,
529
- stdio: "inherit",
530
- },
531
- );
532
-
533
- expect(existsSync(join(outputTsDir, "input.ts"))).toBe(true);
534
- expect(existsSync(join(outputTsDir, "input-utils.ts"))).toBe(true);
535
- expect(existsSync(INPUT_SCHEMA_PATH)).toBe(true);
536
-
537
- const generatedInputSchema = readJsonSchema(INPUT_SCHEMA_PATH);
538
- expect(generatedInputSchema).toEqual({
539
- title: "Input Schema",
540
- type: "object",
541
- properties: {
542
- name: { type: "string" },
543
- age: { type: "integer" },
544
- },
545
- required: ["name"],
546
- });
547
-
548
- const generatedDatasetSchema = readJsonSchema(DATASET_SCHEMA_PATH);
549
- expect(generatedDatasetSchema).toEqual({
550
- title: "Dataset Schema",
551
- fields: {
552
- title: "Dataset Item Schema",
553
- type: "object",
554
- properties: {
555
- id: { type: "string" },
556
- value: { type: "number" },
557
- },
558
- required: ["id"],
559
- },
560
- });
561
- });
562
-
563
- it("should sync input schema to JSON Schema and TypeScript types, with custom options", async () => {
564
- setupTestDirectory(TEST_DIR);
565
-
566
- const srcDir = join(TEST_DIR, "custom-src");
567
- const srcInputPath = join(srcDir, "custom-input.json");
568
- const srcDatasetPath = join(srcDir, "custom-dataset-item.json");
569
- const outputTsDir = join(TEST_DIR, "src", "custom-generated");
570
- const addDir = join(TEST_DIR, "add-schemas");
571
- const addInputPath = join(addDir, "input.json");
572
- const addDatasetPath = join(addDir, "dataset-item.json");
573
- const srcInput: ObjectSchema = {
574
- title: "Input Schema",
575
- type: "object",
576
- properties: {
577
- name: { type: "string" },
578
- age: { type: "integer" },
579
- },
580
- required: ["name"],
581
- };
582
- const addInput: ObjectSchema = {
583
- type: "object",
584
- properties: {
585
- email: { type: "string", format: "email" },
586
- isActive: { type: "boolean" },
587
- },
588
- required: ["email"],
589
- };
590
- const srcDataset: ObjectSchema = {
591
- title: "Dataset Item Schema",
592
- type: "object",
593
- properties: {
594
- id: { type: "string" },
595
- value: { type: "number" },
596
- },
597
- required: ["id"],
598
- };
599
- const addDataset: ObjectSchema = {
600
- type: "object",
601
- properties: {
602
- description: { type: "string" },
603
- timestamp: { type: "string", format: "date-time" },
604
- },
605
- required: ["description"],
606
- };
607
-
608
- setupTestDirectoryFiles(
609
- srcInputPath,
610
- srcInput,
611
- srcDatasetPath,
612
- srcDataset,
613
- addDir,
614
- addInputPath,
615
- addInput,
616
- addDatasetPath,
617
- addDataset,
618
- );
619
-
620
- execSync(
621
- [
622
- `${EXEC_CMD} sync`,
623
- "-i input dataset",
624
- "-o json-schemas ts-types",
625
- `--src-input ${srcInputPath}`,
626
- `--src-dataset ${srcDatasetPath}`,
627
- `--output-ts-dir ${outputTsDir}`,
628
- `--add-input ${addInputPath}`,
629
- `--add-dataset ${addDatasetPath}`,
630
- "--deep-merge",
631
- "--include-input-utils false",
632
- ].join(" "),
633
- {
634
- cwd: TEST_DIR,
635
- stdio: "inherit",
636
- },
637
- );
638
-
639
- expect(existsSync(join(outputTsDir, "input.ts"))).toBe(true);
640
- expect(existsSync(INPUT_SCHEMA_PATH)).toBe(true);
641
-
642
- // Not generated because we set the option to false
643
- expect(existsSync(join(outputTsDir, "input-utils.ts"))).toBe(false);
644
-
645
- const generatedInputSchema = readJsonSchema(INPUT_SCHEMA_PATH);
646
- expect(generatedInputSchema).toEqual({
647
- title: "Input Schema",
648
- type: "object",
649
- properties: {
650
- name: { type: "string" },
651
- age: { type: "integer" },
652
- email: { type: "string" }, // "format" was removed because it is an invalid property in Apify input schema
653
- isActive: { type: "boolean" },
654
- },
655
- required: ["name", "email"],
656
- });
657
-
658
- const generatedDatasetSchema = readJsonSchema(DATASET_SCHEMA_PATH);
659
- expect(generatedDatasetSchema).toEqual({
660
- title: "Dataset Schema",
661
- fields: {
662
- title: "Dataset Item Schema",
663
- type: "object",
664
- properties: {
665
- id: { type: "string" },
666
- value: { type: "number" },
667
- description: { type: "string" },
668
- timestamp: { type: "string", format: "date-time" },
669
- },
670
- required: ["id", "description"],
671
- },
672
- });
673
- });
674
-
675
- it("throws an error when there is a conflict and the --fail-on-conflict option is used", () => {
676
- setupTestDirectory(TEST_DIR);
677
- const srcDir = join(TEST_DIR, "src-schemas");
678
- const srcInputPath = join(srcDir, "input.json");
679
- const srcDatasetPath = join(srcDir, "dataset-item.json");
680
-
681
- // Create source schema with one title
682
- const srcInput: ObjectSchema = {
683
- title: "Source Input Schema", // This will conflict with existing schema
684
- type: "object",
685
- properties: {
686
- name: { type: "string" },
687
- },
688
- required: ["name"],
689
- };
690
- const srcDataset: ObjectSchema = {
691
- title: "Dataset Item Schema",
692
- type: "object",
693
- properties: {
694
- id: { type: "string" },
695
- },
696
- required: ["id"],
697
- };
698
-
699
- setupTestDirectoryFiles(srcInputPath, srcInput, srcDatasetPath, srcDataset);
700
-
701
- // Create existing schema with different title to cause conflict
702
- const existingInputSchema: ObjectSchema = {
703
- title: "Existing Input Schema", // Different title causes conflict
704
- type: "object",
705
- properties: {
706
- name: { type: "string" },
707
- },
708
- required: ["name"],
709
- };
710
- writeJsonSchema(INPUT_SCHEMA_PATH, existingInputSchema);
711
-
712
- // Should throw an error due to --fail-on-conflict
713
- expect(() =>
714
- execSync(
715
- [
716
- `${EXEC_CMD} sync`,
717
- "--fail-on-conflict",
718
- "-i input",
719
- "-o json-schemas",
720
- `--src-input ${srcInputPath}`,
721
- `--src-dataset ${srcDatasetPath}`,
722
- ].join(" "),
723
- {
724
- cwd: TEST_DIR,
725
- stdio: "inherit",
726
- },
727
- ),
728
- ).toThrow();
729
-
730
- // Verify the existing schema was not overwritten
731
- expect(readJsonSchema(INPUT_SCHEMA_PATH)).toEqual(existingInputSchema);
732
- });
733
-
734
- it("logs a warning when there is a conflict and the --force option is used", () => {
735
- setupTestDirectory(TEST_DIR);
736
- const srcDir = join(TEST_DIR, "src-schemas");
737
- const srcInputPath = join(srcDir, "input.json");
738
- const srcDatasetPath = join(srcDir, "dataset-item.json");
739
-
740
- // Create source schema with one title
741
- const srcInput: ObjectSchema = {
742
- title: "Source Input Schema", // This will conflict with existing schema
743
- type: "object",
744
- properties: {
745
- name: { type: "string" },
746
- },
747
- required: ["name"],
748
- };
749
- const srcDataset: ObjectSchema = {
750
- title: "Dataset Item Schema",
751
- type: "object",
752
- properties: {
753
- id: { type: "string" },
754
- },
755
- required: ["id"],
756
- };
757
-
758
- setupTestDirectoryFiles(srcInputPath, srcInput, srcDatasetPath, srcDataset);
759
-
760
- // Create existing schema with different title to cause conflict
761
- const existingInputSchema: ObjectSchema = {
762
- title: "Existing Input Schema", // Different title causes conflict
763
- type: "object",
764
- properties: {
765
- name: { type: "string" },
766
- },
767
- required: ["name"],
768
- };
769
- writeJsonSchema(INPUT_SCHEMA_PATH, existingInputSchema);
770
-
771
- // Should NOT throw an error due to --force, just log warning
772
- expect(() =>
773
- execSync(
774
- [
775
- `${EXEC_CMD} sync`,
776
- "--force",
777
- "-i input",
778
- "-o json-schemas",
779
- `--src-input ${srcInputPath}`,
780
- `--src-dataset ${srcDatasetPath}`,
781
- ].join(" "),
782
- {
783
- cwd: TEST_DIR,
784
- stdio: "inherit",
785
- },
786
- ),
787
- ).not.toThrow();
788
-
789
- // Verify the existing schema was overwritten
790
- expect(readJsonSchema(INPUT_SCHEMA_PATH)).toEqual(srcInput);
791
- });
792
- });
793
-
794
- describe("check command", () => {
795
- it("should pass if the schemas match the source schemas", () => {
796
- const inputSchema: ObjectSchema = {
797
- title: "Input Schema",
798
- type: "object",
799
- properties: {
800
- name: { type: "string" },
801
- age: { type: "integer" },
802
- },
803
- required: ["name"],
804
- };
805
- const datasetSchema: ObjectSchema = {
806
- title: "Dataset Item Schema",
807
- type: "object",
808
- properties: {
809
- id: { type: "string" },
810
- value: { type: "number" },
811
- },
812
- required: ["id"],
813
- };
814
- setupTestDirectory(TEST_DIR);
815
- const srcDir = join(TEST_DIR, "src-schemas");
816
- const srcInputPath = join(srcDir, "input.json");
817
- const srcDatasetPath = join(srcDir, "dataset-item.json");
818
- setupTestDirectoryFiles(srcInputPath, inputSchema, srcDatasetPath, datasetSchema);
819
- writeJsonSchema(INPUT_SCHEMA_PATH, inputSchema);
820
- writeSchemaToField(DATASET_SCHEMA_PATH, datasetSchema, DATASET_SCHEMA_FIELD);
821
- expect(() =>
822
- execSync(
823
- [
824
- `${EXEC_CMD} check`,
825
- "-i input dataset",
826
- "-o json-schemas",
827
- `--src-input ${srcInputPath}`,
828
- `--src-dataset ${srcDatasetPath}`,
829
- ].join(" "),
830
- {
831
- cwd: TEST_DIR,
832
- stdio: "inherit",
833
- },
834
- ),
835
- ).not.toThrow();
836
- });
837
-
838
- it("should consider additional schemas", () => {
839
- const inputSchema: ObjectSchema = {
840
- title: "Input Schema",
841
- type: "object",
842
- properties: {
843
- name: { type: "string" },
844
- },
845
- required: ["name"],
846
- };
847
- const additionalSchema: ObjectSchema = {
848
- type: "object",
849
- properties: {
850
- age: { type: "integer" },
851
- },
852
- };
853
- const fullSchema: ObjectSchema = {
854
- title: "Input Schema",
855
- type: "object",
856
- properties: {
857
- name: { type: "string" },
858
- age: { type: "integer" },
859
- },
860
- required: ["name"],
861
- };
862
- setupTestDirectory(TEST_DIR);
863
- const srcDir = join(TEST_DIR, "src-schemas");
864
- const srcInputPath = join(srcDir, "input.json");
865
- const srcDatasetPath = join(srcDir, "dataset-item.json");
866
- const addDir = join(TEST_DIR, "add-schemas");
867
- const addInputPath = join(addDir, "add-input.json");
868
- setupTestDirectoryFiles(
869
- srcInputPath,
870
- inputSchema,
871
- srcDatasetPath,
872
- {} as ObjectSchema,
873
- addDir,
874
- addInputPath,
875
- additionalSchema,
876
- );
877
- writeJsonSchema(INPUT_SCHEMA_PATH, fullSchema);
878
- expect(() =>
879
- execSync(
880
- [
881
- `${EXEC_CMD} check`,
882
- "-i input",
883
- "-o json-schemas",
884
- `--src-input ${srcInputPath}`,
885
- `--src-dataset ${srcDatasetPath}`,
886
- `--add-input ${addInputPath}`,
887
- ].join(" "),
888
- {
889
- cwd: TEST_DIR,
890
- stdio: "inherit",
891
- },
892
- ),
893
- ).not.toThrow();
894
- });
895
-
896
- it("should pass when source schema has position fields that are removed in output", () => {
897
- const inputSchemaWithPositions: ObjectSchema = {
898
- title: "Input Schema",
899
- type: "object",
900
- properties: {
901
- name: { type: "string", position: 1 },
902
- age: { type: "integer", position: 2 },
903
- email: { type: "string", position: 0 },
904
- },
905
- required: ["name"],
906
- };
907
- // Expected output schema (positions are removed by the filtering process)
908
- const expectedOutputSchema: ObjectSchema = {
909
- title: "Input Schema",
910
- type: "object",
911
- properties: {
912
- email: { type: "string" }, // position 0 comes first
913
- name: { type: "string" }, // position 1 comes second
914
- age: { type: "integer" }, // position 2 comes last
915
- },
916
- required: ["name"],
917
- };
918
- setupTestDirectory(TEST_DIR);
919
- const srcDir = join(TEST_DIR, "src-schemas");
920
- const srcInputPath = join(srcDir, "input.json");
921
- const srcDatasetPath = join(srcDir, "dataset-item.json");
922
- setupTestDirectoryFiles(srcInputPath, inputSchemaWithPositions, srcDatasetPath, {} as ObjectSchema);
923
- writeJsonSchema(INPUT_SCHEMA_PATH, expectedOutputSchema);
924
- expect(() =>
925
- execSync(
926
- [
927
- `${EXEC_CMD} check`,
928
- "-i input",
929
- "-o json-schemas",
930
- `--src-input ${srcInputPath}`,
931
- `--src-dataset ${srcDatasetPath}`,
932
- ].join(" "),
933
- {
934
- cwd: TEST_DIR,
935
- stdio: "inherit",
936
- },
937
- ),
938
- ).not.toThrow();
939
- });
940
-
941
- it("should pass when source schema has invalid input schema fields that are filtered out", () => {
942
- const inputSchemaWithInvalidFields: ObjectSchema = {
943
- title: "Input Schema",
944
- type: "object",
945
- properties: {
946
- name: {
947
- type: "string",
948
- format: "email", // Invalid field for Apify input schemas - will be removed
949
- custom: "field", // Invalid field - will be removed
950
- },
951
- age: {
952
- type: "integer",
953
- invalidField: "value", // Invalid field - will be removed
954
- },
955
- description: {
956
- type: "string",
957
- pattern: "^[a-z]+$", // Valid field - will be kept
958
- },
959
- },
960
- required: ["name"],
961
- invalidRootField: "should be removed", // Invalid root field - will be removed
962
- };
963
- // Expected output schema (invalid fields are removed by the filtering process)
964
- const expectedOutputSchema: ObjectSchema = {
965
- title: "Input Schema",
966
- type: "object",
967
- properties: {
968
- name: { type: "string" },
969
- age: { type: "integer" },
970
- description: {
971
- type: "string",
972
- pattern: "^[a-z]+$",
973
- },
974
- },
975
- required: ["name"],
976
- };
977
- setupTestDirectory(TEST_DIR);
978
- const srcDir = join(TEST_DIR, "src-schemas");
979
- const srcInputPath = join(srcDir, "input.json");
980
- const srcDatasetPath = join(srcDir, "dataset-item.json");
981
- setupTestDirectoryFiles(srcInputPath, inputSchemaWithInvalidFields, srcDatasetPath, {} as ObjectSchema);
982
- writeJsonSchema(INPUT_SCHEMA_PATH, expectedOutputSchema);
983
- expect(() =>
984
- execSync(
985
- [
986
- `${EXEC_CMD} check`,
987
- "-i input",
988
- "-o json-schemas",
989
- `--src-input ${srcInputPath}`,
990
- `--src-dataset ${srcDatasetPath}`,
991
- ].join(" "),
992
- {
993
- cwd: TEST_DIR,
994
- stdio: "inherit",
995
- },
996
- ),
997
- ).not.toThrow();
998
- });
999
-
1000
- it("should pass when source schema has both position and invalid fields", () => {
1001
- const complexSourceSchema: ObjectSchema = {
1002
- title: "Complex Input Schema",
1003
- type: "object",
1004
- properties: {
1005
- priority: {
1006
- type: "string",
1007
- position: 0,
1008
- format: "email", // Invalid - will be removed
1009
- },
1010
- name: {
1011
- type: "string",
1012
- position: 2,
1013
- customField: "invalid", // Invalid - will be removed
1014
- },
1015
- age: {
1016
- type: "integer",
1017
- position: 1,
1018
- maximum: 100, // Valid - will be kept
1019
- },
1020
- settings: {
1021
- type: "object",
1022
- position: 3,
1023
- patternKey: "^[a-z]+$", // Valid for object type - will be kept
1024
- invalidObjectField: "remove", // Invalid - will be removed
1025
- },
1026
- },
1027
- required: ["priority", "name"],
1028
- customRootField: "invalid", // Invalid root field - will be removed
1029
- };
1030
- // Expected output schema (positions removed, invalid fields removed, properties ordered by position)
1031
- const expectedOutputSchema: ObjectSchema = {
1032
- title: "Complex Input Schema",
1033
- type: "object",
1034
- properties: {
1035
- priority: { type: "string" }, // position 0
1036
- age: { type: "integer", maximum: 100 }, // position 1
1037
- name: { type: "string" }, // position 2
1038
- settings: {
1039
- type: "object",
1040
- patternKey: "^[a-z]+$",
1041
- }, // position 3
1042
- },
1043
- required: ["priority", "name"],
1044
- };
1045
- setupTestDirectory(TEST_DIR);
1046
- const srcDir = join(TEST_DIR, "src-schemas");
1047
- const srcInputPath = join(srcDir, "input.json");
1048
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1049
- setupTestDirectoryFiles(srcInputPath, complexSourceSchema, srcDatasetPath, {} as ObjectSchema);
1050
- writeJsonSchema(INPUT_SCHEMA_PATH, expectedOutputSchema);
1051
- expect(() =>
1052
- execSync(
1053
- [
1054
- `${EXEC_CMD} check`,
1055
- "-i input",
1056
- "-o json-schemas",
1057
- `--src-input ${srcInputPath}`,
1058
- `--src-dataset ${srcDatasetPath}`,
1059
- ].join(" "),
1060
- {
1061
- cwd: TEST_DIR,
1062
- stdio: "inherit",
1063
- },
1064
- ),
1065
- ).not.toThrow();
1066
- });
1067
-
1068
- it("should pass when additional schemas also have fields that get filtered out", () => {
1069
- const baseInputSchema: ObjectSchema = {
1070
- title: "Base Input Schema",
1071
- type: "object",
1072
- properties: {
1073
- name: { type: "string", position: 1 },
1074
- },
1075
- required: ["name"],
1076
- };
1077
- const additionalSchemaWithInvalidFields: ObjectSchema = {
1078
- type: "object",
1079
- properties: {
1080
- email: {
1081
- type: "string",
1082
- position: 0,
1083
- format: "email", // Invalid field - will be removed
1084
- },
1085
- age: {
1086
- type: "integer",
1087
- position: 2,
1088
- customField: "invalid", // Invalid field - will be removed
1089
- },
1090
- },
1091
- required: ["email"],
1092
- invalidAdditionalField: "remove", // Invalid root field - will be removed
1093
- };
1094
- // Expected merged output schema (positions and invalid fields removed, properties ordered by position)
1095
- const expectedMergedSchema: ObjectSchema = {
1096
- title: "Base Input Schema",
1097
- type: "object",
1098
- properties: {
1099
- email: { type: "string" }, // position 0
1100
- name: { type: "string" }, // position 1
1101
- age: { type: "integer" }, // position 2
1102
- },
1103
- required: ["name", "email"],
1104
- };
1105
- setupTestDirectory(TEST_DIR);
1106
- const srcDir = join(TEST_DIR, "src-schemas");
1107
- const srcInputPath = join(srcDir, "input.json");
1108
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1109
- const addDir = join(TEST_DIR, "add-schemas");
1110
- const addInputPath = join(addDir, "add-input.json");
1111
- setupTestDirectoryFiles(
1112
- srcInputPath,
1113
- baseInputSchema,
1114
- srcDatasetPath,
1115
- {} as ObjectSchema,
1116
- addDir,
1117
- addInputPath,
1118
- additionalSchemaWithInvalidFields,
1119
- );
1120
- writeJsonSchema(INPUT_SCHEMA_PATH, expectedMergedSchema);
1121
- expect(() =>
1122
- execSync(
1123
- [
1124
- `${EXEC_CMD} check`,
1125
- "-i input",
1126
- "-o json-schemas",
1127
- `--src-input ${srcInputPath}`,
1128
- `--src-dataset ${srcDatasetPath}`,
1129
- `--add-input ${addInputPath}`,
1130
- ].join(" "),
1131
- {
1132
- cwd: TEST_DIR,
1133
- stdio: "inherit",
1134
- },
1135
- ),
1136
- ).not.toThrow();
1137
- });
1138
-
1139
- it("should fail when output schema doesn't match after filtering", () => {
1140
- const sourceSchemaWithInvalidFields: ObjectSchema = {
1141
- title: "Input Schema",
1142
- type: "object",
1143
- properties: {
1144
- name: {
1145
- type: "string",
1146
- format: "email", // This will be filtered out
1147
- },
1148
- },
1149
- required: ["name"],
1150
- };
1151
- // Incorrect output schema (includes the field that should have been filtered)
1152
- const incorrectOutputSchema: ObjectSchema = {
1153
- title: "Input Schema",
1154
- type: "object",
1155
- properties: {
1156
- name: {
1157
- type: "string",
1158
- format: "email", // This field should not be in the output
1159
- },
1160
- },
1161
- required: ["name"],
1162
- };
1163
- setupTestDirectory(TEST_DIR);
1164
- const srcDir = join(TEST_DIR, "src-schemas");
1165
- const srcInputPath = join(srcDir, "input.json");
1166
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1167
- setupTestDirectoryFiles(srcInputPath, sourceSchemaWithInvalidFields, srcDatasetPath, {} as ObjectSchema);
1168
- writeJsonSchema(INPUT_SCHEMA_PATH, incorrectOutputSchema);
1169
- expect(() =>
1170
- execSync(
1171
- [
1172
- `${EXEC_CMD} check`,
1173
- "-i input",
1174
- "-o json-schemas",
1175
- `--src-input ${srcInputPath}`,
1176
- `--src-dataset ${srcDatasetPath}`,
1177
- ].join(" "),
1178
- {
1179
- cwd: TEST_DIR,
1180
- stdio: "inherit",
1181
- },
1182
- ),
1183
- ).toThrow();
1184
- });
1185
-
1186
- it("should pass when descriptions differ but --ignore-descriptions is set", () => {
1187
- const sourceInputSchema: ObjectSchema = {
1188
- title: "Source Input Schema",
1189
- description: "Original description for input schema",
1190
- type: "object",
1191
- properties: {
1192
- name: {
1193
- type: "string",
1194
- description: "Name of the user",
1195
- },
1196
- age: {
1197
- type: "integer",
1198
- description: "Age in years",
1199
- },
1200
- },
1201
- required: ["name"],
1202
- };
1203
- // Output schema with different descriptions but same structure
1204
- const outputInputSchema: ObjectSchema = {
1205
- title: "Generated Input Schema", // Different title
1206
- description: "Auto-generated description for input schema", // Different description
1207
- type: "object",
1208
- properties: {
1209
- name: {
1210
- type: "string",
1211
- description: "User's full name", // Different description
1212
- },
1213
- age: {
1214
- type: "integer",
1215
- description: "User's age", // Different description
1216
- },
1217
- },
1218
- required: ["name"],
1219
- };
1220
- setupTestDirectory(TEST_DIR);
1221
- const srcDir = join(TEST_DIR, "src-schemas");
1222
- const srcInputPath = join(srcDir, "input.json");
1223
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1224
- setupTestDirectoryFiles(srcInputPath, sourceInputSchema, srcDatasetPath, {} as ObjectSchema);
1225
- writeJsonSchema(INPUT_SCHEMA_PATH, outputInputSchema);
1226
- expect(() =>
1227
- execSync(
1228
- [
1229
- `${EXEC_CMD} check`,
1230
- "-i input",
1231
- "-o json-schemas",
1232
- `--src-input ${srcInputPath}`,
1233
- `--src-dataset ${srcDatasetPath}`,
1234
- "--ignore-descriptions",
1235
- ].join(" "),
1236
- {
1237
- cwd: TEST_DIR,
1238
- stdio: "inherit",
1239
- },
1240
- ),
1241
- ).not.toThrow();
1242
- });
1243
-
1244
- it("should fail when descriptions differ and --ignore-descriptions is not set", () => {
1245
- const sourceInputSchema: ObjectSchema = {
1246
- title: "Source Input Schema",
1247
- description: "Original description",
1248
- type: "object",
1249
- properties: {
1250
- name: { type: "string" },
1251
- },
1252
- required: ["name"],
1253
- };
1254
- // Output schema with different title (same structure otherwise)
1255
- const outputInputSchema: ObjectSchema = {
1256
- title: "Different Title", // Different title should cause failure
1257
- description: "Original description",
1258
- type: "object",
1259
- properties: {
1260
- name: { type: "string" },
1261
- },
1262
- required: ["name"],
1263
- };
1264
- setupTestDirectory(TEST_DIR);
1265
- const srcDir = join(TEST_DIR, "src-schemas");
1266
- const srcInputPath = join(srcDir, "input.json");
1267
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1268
- setupTestDirectoryFiles(srcInputPath, sourceInputSchema, srcDatasetPath, {} as ObjectSchema);
1269
- writeJsonSchema(INPUT_SCHEMA_PATH, outputInputSchema);
1270
- expect(() =>
1271
- execSync(
1272
- [
1273
- `${EXEC_CMD} check`,
1274
- "-i input",
1275
- "-o json-schemas",
1276
- `--src-input ${srcInputPath}`,
1277
- `--src-dataset ${srcDatasetPath}`,
1278
- // Note: --ignore-descriptions is NOT set
1279
- ].join(" "),
1280
- {
1281
- cwd: TEST_DIR,
1282
- stdio: "inherit",
1283
- },
1284
- ),
1285
- ).toThrow();
1286
- });
1287
-
1288
- it("should pass with --ignore-descriptions when additional schema has different descriptions", () => {
1289
- const baseInputSchema: ObjectSchema = {
1290
- title: "Base Input Schema",
1291
- description: "Base description",
1292
- type: "object",
1293
- properties: {
1294
- name: {
1295
- type: "string",
1296
- description: "Base name description",
1297
- },
1298
- },
1299
- required: ["name"],
1300
- };
1301
- const additionalSchema: ObjectSchema = {
1302
- title: "Additional Schema", // Different title
1303
- description: "Additional description", // Different description
1304
- type: "object",
1305
- properties: {
1306
- age: {
1307
- type: "integer",
1308
- description: "Additional age description",
1309
- },
1310
- },
1311
- required: ["age"],
1312
- };
1313
- // Expected merged output with different descriptions
1314
- const expectedOutputSchema: ObjectSchema = {
1315
- title: "Generated Combined Schema", // Different from both source schemas
1316
- description: "Auto-generated combined description", // Different from both
1317
- type: "object",
1318
- properties: {
1319
- name: {
1320
- type: "string",
1321
- description: "Generated name description", // Different description
1322
- },
1323
- age: {
1324
- type: "integer",
1325
- description: "Generated age description", // Different description
1326
- },
1327
- },
1328
- required: ["name", "age"],
1329
- };
1330
- setupTestDirectory(TEST_DIR);
1331
- const srcDir = join(TEST_DIR, "src-schemas");
1332
- const srcInputPath = join(srcDir, "input.json");
1333
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1334
- const addDir = join(TEST_DIR, "add-schemas");
1335
- const addInputPath = join(addDir, "add-input.json");
1336
- setupTestDirectoryFiles(
1337
- srcInputPath,
1338
- baseInputSchema,
1339
- srcDatasetPath,
1340
- {} as ObjectSchema,
1341
- addDir,
1342
- addInputPath,
1343
- additionalSchema,
1344
- );
1345
- writeJsonSchema(INPUT_SCHEMA_PATH, expectedOutputSchema);
1346
- expect(() =>
1347
- execSync(
1348
- [
1349
- `${EXEC_CMD} check`,
1350
- "-i input",
1351
- "-o json-schemas",
1352
- `--src-input ${srcInputPath}`,
1353
- `--src-dataset ${srcDatasetPath}`,
1354
- `--add-input ${addInputPath}`,
1355
- "--ignore-descriptions",
1356
- ].join(" "),
1357
- {
1358
- cwd: TEST_DIR,
1359
- stdio: "inherit",
1360
- },
1361
- ),
1362
- ).not.toThrow();
1363
- });
1364
-
1365
- it("should pass with --ignore-descriptions when dataset schema has different descriptions", () => {
1366
- const sourceDatasetSchema: ObjectSchema = {
1367
- title: "Source Dataset Item Schema",
1368
- description: "Original dataset description",
1369
- type: "object",
1370
- properties: {
1371
- id: {
1372
- type: "string",
1373
- description: "Unique identifier",
1374
- },
1375
- value: {
1376
- type: "number",
1377
- description: "Numeric value",
1378
- },
1379
- },
1380
- required: ["id"],
1381
- };
1382
- // Output dataset schema with different descriptions
1383
- const outputDatasetSchema: ObjectSchema = {
1384
- title: "Generated Dataset Item Schema", // Different title
1385
- description: "Auto-generated dataset description", // Different description
1386
- type: "object",
1387
- properties: {
1388
- id: {
1389
- type: "string",
1390
- description: "Item identifier", // Different description
1391
- },
1392
- value: {
1393
- type: "number",
1394
- description: "Item value", // Different description
1395
- },
1396
- },
1397
- required: ["id"],
1398
- };
1399
- setupTestDirectory(TEST_DIR);
1400
- const srcDir = join(TEST_DIR, "src-schemas");
1401
- const srcInputPath = join(srcDir, "input.json");
1402
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1403
- setupTestDirectoryFiles(srcInputPath, {} as ObjectSchema, srcDatasetPath, sourceDatasetSchema);
1404
- // Write a simple input schema to avoid validation issues
1405
- writeJsonSchema(INPUT_SCHEMA_PATH, {
1406
- title: "Simple Input",
1407
- type: "object",
1408
- properties: {},
1409
- });
1410
- writeSchemaToField(DATASET_SCHEMA_PATH, outputDatasetSchema, DATASET_SCHEMA_FIELD);
1411
- expect(() =>
1412
- execSync(
1413
- [
1414
- `${EXEC_CMD} check`,
1415
- "-i dataset",
1416
- "-o json-schemas",
1417
- `--src-input ${srcInputPath}`,
1418
- `--src-dataset ${srcDatasetPath}`,
1419
- "--ignore-descriptions",
1420
- ].join(" "),
1421
- {
1422
- cwd: TEST_DIR,
1423
- stdio: "inherit",
1424
- },
1425
- ),
1426
- ).not.toThrow();
1427
- });
1428
-
1429
- it("should fail with --ignore-descriptions when there are structural differences beyond descriptions", () => {
1430
- const sourceInputSchema: ObjectSchema = {
1431
- title: "Source Input Schema",
1432
- description: "Original description",
1433
- type: "object",
1434
- properties: {
1435
- name: {
1436
- type: "string",
1437
- description: "Name of the user",
1438
- },
1439
- age: {
1440
- type: "integer",
1441
- description: "Age in years",
1442
- },
1443
- },
1444
- required: ["name"],
1445
- };
1446
- // Output schema with different structure (missing age property)
1447
- const outputInputSchema: ObjectSchema = {
1448
- title: "Different Title", // Different title (should be ignored)
1449
- description: "Different description", // Different description (should be ignored)
1450
- type: "object",
1451
- properties: {
1452
- name: {
1453
- type: "string",
1454
- description: "Different name description", // Different description (should be ignored)
1455
- },
1456
- // Missing age property - this is a structural difference that should cause failure
1457
- },
1458
- required: ["name"],
1459
- };
1460
- setupTestDirectory(TEST_DIR);
1461
- const srcDir = join(TEST_DIR, "src-schemas");
1462
- const srcInputPath = join(srcDir, "input.json");
1463
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1464
- setupTestDirectoryFiles(srcInputPath, sourceInputSchema, srcDatasetPath, {} as ObjectSchema);
1465
- writeJsonSchema(INPUT_SCHEMA_PATH, outputInputSchema);
1466
- expect(() =>
1467
- execSync(
1468
- [
1469
- `${EXEC_CMD} check`,
1470
- "-i input",
1471
- "-o json-schemas",
1472
- `--src-input ${srcInputPath}`,
1473
- `--src-dataset ${srcDatasetPath}`,
1474
- "--ignore-descriptions", // Should still fail despite this flag
1475
- ].join(" "),
1476
- {
1477
- cwd: TEST_DIR,
1478
- stdio: "inherit",
1479
- },
1480
- ),
1481
- ).toThrow();
1482
- });
1483
-
1484
- it("should pass when TypeScript files match the source schemas", () => {
1485
- const sourceInputSchema: ObjectSchema = {
1486
- title: "Input Schema",
1487
- description: "Schema for actor input",
1488
- type: "object",
1489
- properties: {
1490
- name: {
1491
- type: "string",
1492
- description: "User's name",
1493
- },
1494
- age: {
1495
- type: "integer",
1496
- description: "User's age",
1497
- },
1498
- isActive: {
1499
- type: "boolean",
1500
- description: "Whether user is active",
1501
- },
1502
- },
1503
- required: ["name"],
1504
- additionalProperties: false,
1505
- };
1506
- const sourceDatasetSchema: ObjectSchema = {
1507
- title: "Dataset Item Schema",
1508
- description: "Schema for dataset items",
1509
- type: "object",
1510
- properties: {
1511
- id: {
1512
- type: "string",
1513
- description: "Unique identifier",
1514
- },
1515
- value: {
1516
- type: "number",
1517
- description: "Numeric value",
1518
- },
1519
- },
1520
- required: ["id"],
1521
- };
1522
- setupTestDirectory(TEST_DIR);
1523
- const srcDir = join(TEST_DIR, "src-schemas");
1524
- const srcInputPath = join(srcDir, "input.json");
1525
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1526
- const outputTsDir = join(TEST_DIR, "src", "generated");
1527
- setupTestDirectoryFiles(srcInputPath, sourceInputSchema, srcDatasetPath, sourceDatasetSchema);
1528
-
1529
- // Generate the TypeScript files that should match the schemas
1530
- const inputTsContent = `/**
1531
- * This file was automatically generated by apify-schema-tools.
1532
- * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
1533
- * and run apify-schema-tools' "sync" command to regenerate this file.
1534
- */
1535
-
1536
- /**
1537
- * Schema for actor input
1538
- */
1539
- export interface Input {
1540
- /**
1541
- * User's name
1542
- */
1543
- name: string;
1544
- /**
1545
- * User's age
1546
- */
1547
- age?: number;
1548
- /**
1549
- * Whether user is active
1550
- */
1551
- isActive?: boolean;
1552
- }
1553
- `;
1554
- const datasetTsContent = `/**
1555
- * This file was automatically generated by apify-schema-tools.
1556
- * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
1557
- * and run apify-schema-tools' "sync" command to regenerate this file.
1558
- */
1559
-
1560
- /**
1561
- * Schema for dataset items
1562
- */
1563
- export interface DatasetItem {
1564
- /**
1565
- * Unique identifier
1566
- */
1567
- id: string;
1568
- /**
1569
- * Numeric value
1570
- */
1571
- value?: number;
1572
- [key: string]: unknown;
1573
- }
1574
- `;
1575
- writeFile(join(outputTsDir, "input.ts"), inputTsContent);
1576
- writeFile(join(outputTsDir, "dataset.ts"), datasetTsContent);
1577
-
1578
- expect(() =>
1579
- execSync(
1580
- [
1581
- `${EXEC_CMD} check`,
1582
- "-i input dataset",
1583
- "-o ts-types",
1584
- `--src-input ${srcInputPath}`,
1585
- `--src-dataset ${srcDatasetPath}`,
1586
- ].join(" "),
1587
- {
1588
- cwd: TEST_DIR,
1589
- stdio: "inherit",
1590
- },
1591
- ),
1592
- ).not.toThrow();
1593
- });
1594
-
1595
- it("should fail when TypeScript interface has different property types", () => {
1596
- const sourceInputSchema: ObjectSchema = {
1597
- title: "Input Schema",
1598
- type: "object",
1599
- properties: {
1600
- name: { type: "string" },
1601
- age: { type: "integer" },
1602
- },
1603
- required: ["name"],
1604
- };
1605
- setupTestDirectory(TEST_DIR);
1606
- const srcDir = join(TEST_DIR, "src-schemas");
1607
- const srcInputPath = join(srcDir, "input.json");
1608
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1609
- const outputTsDir = join(TEST_DIR, "src", "generated");
1610
- setupTestDirectoryFiles(srcInputPath, sourceInputSchema, srcDatasetPath, {} as ObjectSchema);
1611
-
1612
- // Generate TypeScript file with wrong type (age should be number, not string)
1613
- const incorrectInputTsContent = `/**
1614
- * This file was automatically generated by apify-schema-tools.
1615
- * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
1616
- * and run apify-schema-tools' "sync" command to regenerate this file.
1617
- */
1618
-
1619
- export interface Input {
1620
- name: string;
1621
- age?: string;
1622
- }
1623
- `;
1624
- writeFile(join(outputTsDir, "input.ts"), incorrectInputTsContent);
1625
-
1626
- expect(() =>
1627
- execSync(
1628
- [
1629
- `${EXEC_CMD} check`,
1630
- "-i input",
1631
- "-o ts-types",
1632
- `--src-input ${srcInputPath}`,
1633
- `--src-dataset ${srcDatasetPath}`,
1634
- ].join(" "),
1635
- {
1636
- cwd: TEST_DIR,
1637
- stdio: "inherit",
1638
- },
1639
- ),
1640
- ).toThrow();
1641
- });
1642
-
1643
- it("should fail when TypeScript interface has different required properties", () => {
1644
- const sourceInputSchema: ObjectSchema = {
1645
- title: "Input Schema",
1646
- type: "object",
1647
- properties: {
1648
- name: { type: "string" },
1649
- age: { type: "integer" },
1650
- },
1651
- required: ["name", "age"], // Both required
1652
- };
1653
- setupTestDirectory(TEST_DIR);
1654
- const srcDir = join(TEST_DIR, "src-schemas");
1655
- const srcInputPath = join(srcDir, "input.json");
1656
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1657
- const outputTsDir = join(TEST_DIR, "src", "generated");
1658
- setupTestDirectoryFiles(srcInputPath, sourceInputSchema, srcDatasetPath, {} as ObjectSchema);
1659
-
1660
- // Generate TypeScript file where age is optional but should be required
1661
- const incorrectInputTsContent = `/**
1662
- * This file was automatically generated by apify-schema-tools.
1663
- * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
1664
- * and run apify-schema-tools' "sync" command to regenerate this file.
1665
- */
1666
-
1667
- export interface Input {
1668
- name: string;
1669
- age?: number;
1670
- }
1671
- `;
1672
- writeFile(join(outputTsDir, "input.ts"), incorrectInputTsContent);
1673
-
1674
- expect(() =>
1675
- execSync(
1676
- [
1677
- `${EXEC_CMD} check`,
1678
- "-i input",
1679
- "-o ts-types",
1680
- `--src-input ${srcInputPath}`,
1681
- `--src-dataset ${srcDatasetPath}`,
1682
- ].join(" "),
1683
- {
1684
- cwd: TEST_DIR,
1685
- stdio: "inherit",
1686
- },
1687
- ),
1688
- ).toThrow();
1689
- });
1690
-
1691
- it("should fail when TypeScript interface has missing properties", () => {
1692
- const sourceInputSchema: ObjectSchema = {
1693
- title: "Input Schema",
1694
- type: "object",
1695
- properties: {
1696
- name: { type: "string" },
1697
- age: { type: "integer" },
1698
- email: { type: "string" },
1699
- },
1700
- required: ["name"],
1701
- };
1702
- setupTestDirectory(TEST_DIR);
1703
- const srcDir = join(TEST_DIR, "src-schemas");
1704
- const srcInputPath = join(srcDir, "input.json");
1705
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1706
- const outputTsDir = join(TEST_DIR, "src", "generated");
1707
- setupTestDirectoryFiles(srcInputPath, sourceInputSchema, srcDatasetPath, {} as ObjectSchema);
1708
-
1709
- // Generate TypeScript file missing the email property
1710
- const incompleteInputTsContent = `/**
1711
- * This file was automatically generated by apify-schema-tools.
1712
- * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
1713
- * and run apify-schema-tools' "sync" command to regenerate this file.
1714
- */
1715
-
1716
- export interface Input {
1717
- name: string;
1718
- age?: number;
1719
- }
1720
- `;
1721
- writeFile(join(outputTsDir, "input.ts"), incompleteInputTsContent);
1722
-
1723
- expect(() =>
1724
- execSync(
1725
- [
1726
- `${EXEC_CMD} check`,
1727
- "-i input",
1728
- "-o ts-types",
1729
- `--src-input ${srcInputPath}`,
1730
- `--src-dataset ${srcDatasetPath}`,
1731
- ].join(" "),
1732
- {
1733
- cwd: TEST_DIR,
1734
- stdio: "inherit",
1735
- },
1736
- ),
1737
- ).toThrow();
1738
- });
1739
-
1740
- it("should fail when additionalProperties differ", () => {
1741
- const sourceInputSchema: ObjectSchema = {
1742
- title: "Input Schema",
1743
- type: "object",
1744
- properties: {
1745
- name: { type: "string" },
1746
- age: { type: "integer" },
1747
- },
1748
- required: ["name"],
1749
- additionalProperties: false, // Original schema does not allow additional properties
1750
- };
1751
- setupTestDirectory(TEST_DIR);
1752
- const srcDir = join(TEST_DIR, "src-schemas");
1753
- const srcInputPath = join(srcDir, "input.json");
1754
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1755
- const outputTsDir = join(TEST_DIR, "src", "generated");
1756
- setupTestDirectoryFiles(srcInputPath, sourceInputSchema, srcDatasetPath, {} as ObjectSchema);
1757
-
1758
- // Generate TypeScript file that allows additional properties
1759
- const inputTsContentWithAdditionalProperties = `/**
1760
- * This file was automatically generated by apify-schema-tools.
1761
- * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
1762
- * and run apify-schema-tools' "sync" command to regenerate this file.
1763
- */
1764
-
1765
- export interface Input {
1766
- name: string;
1767
- age?: number;
1768
- [key: string]: unknown;
1769
- }
1770
- `;
1771
- writeFile(join(outputTsDir, "input.ts"), inputTsContentWithAdditionalProperties);
1772
- let error: Error | null = null;
1773
- let stderr = "";
1774
- try {
1775
- execSync(
1776
- [
1777
- `${EXEC_CMD} check`,
1778
- "-i input",
1779
- "-o ts-types",
1780
- `--src-input ${srcInputPath}`,
1781
- `--src-dataset ${srcDatasetPath}`,
1782
- ].join(" "),
1783
- {
1784
- cwd: TEST_DIR,
1785
- stdio: ["ignore", "pipe", "pipe"],
1786
- encoding: "utf8",
1787
- },
1788
- );
1789
- // biome-ignore lint/suspicious/noExplicitAny: for testing purposes
1790
- } catch (e: any) {
1791
- error = e;
1792
- stderr = e.stderr ?? "";
1793
- }
1794
- expect(error).toBeTruthy();
1795
- expect(stderr).toMatch(ADDITIONAL_PROPERTIES_ERROR_PATTERN);
1796
- });
1797
-
1798
- it("should pass when TypeScript descriptions differ but --ignore-descriptions is set", () => {
1799
- const sourceInputSchema: ObjectSchema = {
1800
- title: "Input Schema",
1801
- description: "Original input description",
1802
- type: "object",
1803
- properties: {
1804
- name: {
1805
- type: "string",
1806
- description: "Original name description",
1807
- },
1808
- age: {
1809
- type: "integer",
1810
- description: "Original age description",
1811
- },
1812
- },
1813
- required: ["name"],
1814
- additionalProperties: false,
1815
- };
1816
- setupTestDirectory(TEST_DIR);
1817
- const srcDir = join(TEST_DIR, "src-schemas");
1818
- const srcInputPath = join(srcDir, "input.json");
1819
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1820
- const outputTsDir = join(TEST_DIR, "src", "generated");
1821
- setupTestDirectoryFiles(srcInputPath, sourceInputSchema, srcDatasetPath, {} as ObjectSchema);
1822
-
1823
- // Generate TypeScript file with different descriptions
1824
- const inputTsContentWithDifferentDescriptions = `/**
1825
- * This file was automatically generated by apify-schema-tools.
1826
- * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
1827
- * and run apify-schema-tools' "sync" command to regenerate this file.
1828
- */
1829
-
1830
- /**
1831
- * Different input description
1832
- */
1833
- export interface Input {
1834
- /**
1835
- * Different name description
1836
- */
1837
- name: string;
1838
- /**
1839
- * Different age description
1840
- */
1841
- age?: number;
1842
- }
1843
- `;
1844
- writeFile(join(outputTsDir, "input.ts"), inputTsContentWithDifferentDescriptions);
1845
-
1846
- expect(() =>
1847
- execSync(
1848
- [
1849
- `${EXEC_CMD} check`,
1850
- "-i input",
1851
- "-o ts-types",
1852
- `--src-input ${srcInputPath}`,
1853
- `--src-dataset ${srcDatasetPath}`,
1854
- "--ignore-descriptions",
1855
- ].join(" "),
1856
- {
1857
- cwd: TEST_DIR,
1858
- stdio: "inherit",
1859
- },
1860
- ),
1861
- ).not.toThrow();
1862
- });
1863
-
1864
- it("should fail when TypeScript descriptions differ and --ignore-descriptions is not set", () => {
1865
- const sourceInputSchema: ObjectSchema = {
1866
- title: "Input Schema",
1867
- description: "Original input description",
1868
- type: "object",
1869
- properties: {
1870
- name: {
1871
- type: "string",
1872
- description: "Original name description",
1873
- },
1874
- },
1875
- required: ["name"],
1876
- };
1877
- setupTestDirectory(TEST_DIR);
1878
- const srcDir = join(TEST_DIR, "src-schemas");
1879
- const srcInputPath = join(srcDir, "input.json");
1880
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1881
- const outputTsDir = join(TEST_DIR, "src", "generated");
1882
- setupTestDirectoryFiles(srcInputPath, sourceInputSchema, srcDatasetPath, {} as ObjectSchema);
1883
-
1884
- // Generate TypeScript file with different descriptions
1885
- const inputTsContentWithDifferentDescriptions = `/**
1886
- * This file was automatically generated by apify-schema-tools.
1887
- * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
1888
- * and run apify-schema-tools' "sync" command to regenerate this file.
1889
- */
1890
-
1891
- /**
1892
- * Different input description
1893
- */
1894
- export interface Input {
1895
- /**
1896
- * Different name description
1897
- */
1898
- name: string;
1899
- }
1900
- `;
1901
- writeFile(join(outputTsDir, "input.ts"), inputTsContentWithDifferentDescriptions);
1902
-
1903
- expect(() =>
1904
- execSync(
1905
- [
1906
- `${EXEC_CMD} check`,
1907
- "-i input",
1908
- "-o ts-types",
1909
- `--src-input ${srcInputPath}`,
1910
- `--src-dataset ${srcDatasetPath}`,
1911
- // Note: --ignore-descriptions is NOT set
1912
- ].join(" "),
1913
- {
1914
- cwd: TEST_DIR,
1915
- stdio: "inherit",
1916
- },
1917
- ),
1918
- ).toThrow();
1919
- });
1920
-
1921
- it("should pass when TypeScript files match schemas with additional merged schemas", () => {
1922
- const baseInputSchema: ObjectSchema = {
1923
- title: "Base Input Schema",
1924
- type: "object",
1925
- properties: {
1926
- name: { type: "string" },
1927
- },
1928
- required: ["name"],
1929
- additionalProperties: false,
1930
- };
1931
- const additionalSchema: ObjectSchema = {
1932
- type: "object",
1933
- properties: {
1934
- age: { type: "integer" },
1935
- email: { type: "string" },
1936
- },
1937
- required: ["email"],
1938
- };
1939
- setupTestDirectory(TEST_DIR);
1940
- const srcDir = join(TEST_DIR, "src-schemas");
1941
- const srcInputPath = join(srcDir, "input.json");
1942
- const srcDatasetPath = join(srcDir, "dataset-item.json");
1943
- const outputTsDir = join(TEST_DIR, "src", "generated");
1944
- const addDir = join(TEST_DIR, "add-schemas");
1945
- const addInputPath = join(addDir, "add-input.json");
1946
- setupTestDirectoryFiles(
1947
- srcInputPath,
1948
- baseInputSchema,
1949
- srcDatasetPath,
1950
- {} as ObjectSchema,
1951
- addDir,
1952
- addInputPath,
1953
- additionalSchema,
1954
- );
1955
-
1956
- // Generate TypeScript file that matches the merged schema
1957
- const mergedInputTsContent = `/**
1958
- * This file was automatically generated by apify-schema-tools.
1959
- * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
1960
- * and run apify-schema-tools' "sync" command to regenerate this file.
1961
- */
1962
-
1963
- /**
1964
- * Base Input Schema
1965
- */
1966
- export interface Input {
1967
- name: string;
1968
- age?: number;
1969
- email: string;
1970
- }
1971
- `;
1972
- writeFile(join(outputTsDir, "input.ts"), mergedInputTsContent);
1973
-
1974
- expect(() =>
1975
- execSync(
1976
- [
1977
- `${EXEC_CMD} check`,
1978
- "-i input",
1979
- "-o ts-types",
1980
- `--src-input ${srcInputPath}`,
1981
- `--src-dataset ${srcDatasetPath}`,
1982
- `--add-input ${addInputPath}`,
1983
- ].join(" "),
1984
- {
1985
- cwd: TEST_DIR,
1986
- stdio: "inherit",
1987
- },
1988
- ),
1989
- ).not.toThrow();
1990
- });
1991
-
1992
- it("should handle complex nested object structures in TypeScript files", () => {
1993
- const sourceInputSchema: ObjectSchema = {
1994
- title: "Complex Input Schema",
1995
- type: "object",
1996
- properties: {
1997
- user: {
1998
- type: "object",
1999
- properties: {
2000
- name: { type: "string" },
2001
- contacts: {
2002
- type: "object",
2003
- properties: {
2004
- email: { type: "string" },
2005
- phone: { type: "string" },
2006
- },
2007
- required: ["email"],
2008
- additionalProperties: { type: "boolean" },
2009
- },
2010
- },
2011
- required: ["name"],
2012
- },
2013
- settings: {
2014
- type: "array",
2015
- items: {
2016
- type: "object",
2017
- properties: {
2018
- key: { type: "string" },
2019
- value: { type: "string" },
2020
- },
2021
- required: ["key"],
2022
- additionalProperties: true,
2023
- },
2024
- },
2025
- },
2026
- required: ["user"],
2027
- additionalProperties: false,
2028
- };
2029
- setupTestDirectory(TEST_DIR);
2030
- const srcDir = join(TEST_DIR, "src-schemas");
2031
- const srcInputPath = join(srcDir, "input.json");
2032
- const srcDatasetPath = join(srcDir, "dataset-item.json");
2033
- const outputTsDir = join(TEST_DIR, "src", "generated");
2034
- setupTestDirectoryFiles(srcInputPath, sourceInputSchema, srcDatasetPath, {} as ObjectSchema);
2035
-
2036
- // Generate TypeScript file with correct nested structure
2037
- const complexInputTsContent = `/**
2038
- * This file was automatically generated by apify-schema-tools.
2039
- * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
2040
- * and run apify-schema-tools' "sync" command to regenerate this file.
2041
- */
2042
-
2043
- /**
2044
- * Complex Input Schema
2045
- */
2046
- export interface Input {
2047
- user: {
2048
- name: string;
2049
- contacts?: {
2050
- email: string;
2051
- phone?: string;
2052
- [key: string]: boolean;
2053
- };
2054
- [key: string]: unknown;
2055
- };
2056
- settings?: {
2057
- key: string;
2058
- value?: string;
2059
- [key: string]: unknown;
2060
- }[];
2061
- }
2062
- `;
2063
- writeFile(join(outputTsDir, "input.ts"), complexInputTsContent);
2064
-
2065
- expect(() =>
2066
- execSync(
2067
- [
2068
- `${EXEC_CMD} check`,
2069
- "-i input",
2070
- "-o ts-types",
2071
- `--src-input ${srcInputPath}`,
2072
- `--src-dataset ${srcDatasetPath}`,
2073
- ].join(" "),
2074
- {
2075
- cwd: TEST_DIR,
2076
- stdio: "inherit",
2077
- },
2078
- ),
2079
- ).not.toThrow();
2080
- });
2081
-
2082
- it("should handle enum types in TypeScript files", () => {
2083
- const sourceInputSchema: ObjectSchema = {
2084
- title: "Input Schema with Enums",
2085
- type: "object",
2086
- properties: {
2087
- status: {
2088
- type: "string",
2089
- enum: ["active", "inactive", "pending"],
2090
- },
2091
- priorities: {
2092
- type: "array",
2093
- items: {
2094
- type: "string",
2095
- enum: ["high", "medium", "low"],
2096
- },
2097
- },
2098
- },
2099
- required: ["status"],
2100
- additionalProperties: false,
2101
- };
2102
- setupTestDirectory(TEST_DIR);
2103
- const srcDir = join(TEST_DIR, "src-schemas");
2104
- const srcInputPath = join(srcDir, "input.json");
2105
- const srcDatasetPath = join(srcDir, "dataset-item.json");
2106
- const outputTsDir = join(TEST_DIR, "src", "generated");
2107
- setupTestDirectoryFiles(srcInputPath, sourceInputSchema, srcDatasetPath, {} as ObjectSchema);
2108
-
2109
- // Generate TypeScript file with correct enum types
2110
- const enumInputTsContent = `/**
2111
- * This file was automatically generated by apify-schema-tools.
2112
- * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
2113
- * and run apify-schema-tools' "sync" command to regenerate this file.
2114
- */
2115
-
2116
- /**
2117
- * Input Schema with Enums
2118
- */
2119
- export interface Input {
2120
- status: "active" | "inactive" | "pending";
2121
- priorities?: ("high" | "medium" | "low")[];
2122
- }
2123
- `;
2124
- writeFile(join(outputTsDir, "input.ts"), enumInputTsContent);
2125
-
2126
- expect(() =>
2127
- execSync(
2128
- [
2129
- `${EXEC_CMD} check`,
2130
- "-i input",
2131
- "-o ts-types",
2132
- `--src-input ${srcInputPath}`,
2133
- `--src-dataset ${srcDatasetPath}`,
2134
- ].join(" "),
2135
- {
2136
- cwd: TEST_DIR,
2137
- stdio: "inherit",
2138
- },
2139
- ),
2140
- ).not.toThrow();
2141
- });
2142
-
2143
- it("should check both input and dataset TypeScript files when both are specified", () => {
2144
- const sourceInputSchema: ObjectSchema = {
2145
- title: "Input Schema",
2146
- type: "object",
2147
- properties: {
2148
- inputField: { type: "string" },
2149
- },
2150
- required: ["inputField"],
2151
- additionalProperties: false,
2152
- };
2153
- const sourceDatasetSchema: ObjectSchema = {
2154
- title: "Dataset Item Schema",
2155
- type: "object",
2156
- properties: {
2157
- dataField: { type: "number" },
2158
- },
2159
- required: ["dataField"],
2160
- };
2161
- setupTestDirectory(TEST_DIR);
2162
- const srcDir = join(TEST_DIR, "src-schemas");
2163
- const srcInputPath = join(srcDir, "input.json");
2164
- const srcDatasetPath = join(srcDir, "dataset-item.json");
2165
- const outputTsDir = join(TEST_DIR, "src", "generated");
2166
- setupTestDirectoryFiles(srcInputPath, sourceInputSchema, srcDatasetPath, sourceDatasetSchema);
2167
-
2168
- // Generate both TypeScript files correctly
2169
- const inputTsContent = `/**
2170
- * This file was automatically generated by apify-schema-tools.
2171
- * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
2172
- * and run apify-schema-tools' "sync" command to regenerate this file.
2173
- */
2174
-
2175
- /**
2176
- * Input Schema
2177
- */
2178
- export interface Input {
2179
- inputField: string;
2180
- }
2181
- `;
2182
- const datasetTsContent = `/**
2183
- * This file was automatically generated by apify-schema-tools.
2184
- * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
2185
- * and run apify-schema-tools' "sync" command to regenerate this file.
2186
- */
2187
-
2188
- /**
2189
- * Dataset Item Schema
2190
- */
2191
- export interface DatasetItem {
2192
- dataField: number;
2193
- [key: string]: unknown;
2194
- }
2195
- `;
2196
- writeFile(join(outputTsDir, "input.ts"), inputTsContent);
2197
- writeFile(join(outputTsDir, "dataset.ts"), datasetTsContent);
2198
-
2199
- expect(() =>
2200
- execSync(
2201
- [
2202
- `${EXEC_CMD} check`,
2203
- "-i input dataset",
2204
- "-o ts-types",
2205
- `--src-input ${srcInputPath}`,
2206
- `--src-dataset ${srcDatasetPath}`,
2207
- ].join(" "),
2208
- {
2209
- cwd: TEST_DIR,
2210
- stdio: "inherit",
2211
- },
2212
- ),
2213
- ).not.toThrow();
2214
- });
2215
- });
2216
- });