@bytebrand/export-cars 1.26.27 → 1.26.29

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 (112) hide show
  1. package/README.md +128 -0
  2. package/dist/configs/config.d.ts +2 -1
  3. package/dist/configs/config.js +1 -0
  4. package/dist/configs/config.js.map +1 -1
  5. package/dist/configs/google.config.js +16 -0
  6. package/dist/configs/google.config.js.map +1 -1
  7. package/dist/configs/index.d.ts +1 -0
  8. package/dist/configs/index.js +1 -0
  9. package/dist/configs/index.js.map +1 -1
  10. package/dist/configs/openai-ads.config.d.ts +22 -0
  11. package/dist/configs/openai-ads.config.js +56 -0
  12. package/dist/configs/openai-ads.config.js.map +1 -0
  13. package/dist/enums/export-type.enum.d.ts +2 -1
  14. package/dist/enums/export-type.enum.js +1 -0
  15. package/dist/enums/export-type.enum.js.map +1 -1
  16. package/dist/enums/image-type.enum.d.ts +2 -1
  17. package/dist/enums/image-type.enum.js +1 -0
  18. package/dist/enums/image-type.enum.js.map +1 -1
  19. package/dist/export.schema.d.ts +6 -2
  20. package/dist/export.schema.js +4 -0
  21. package/dist/export.schema.js.map +1 -1
  22. package/dist/exporters/google.exporter.js +12 -2
  23. package/dist/exporters/google.exporter.js.map +1 -1
  24. package/dist/exporters/index.d.ts +1 -0
  25. package/dist/exporters/index.js +1 -0
  26. package/dist/exporters/index.js.map +1 -1
  27. package/dist/exporters/openai-ads.exporter.d.ts +13 -0
  28. package/dist/exporters/openai-ads.exporter.js +91 -0
  29. package/dist/exporters/openai-ads.exporter.js.map +1 -0
  30. package/dist/formats/openai-ads-csv.builder.d.ts +15 -0
  31. package/dist/formats/openai-ads-csv.builder.js +48 -0
  32. package/dist/formats/openai-ads-csv.builder.js.map +1 -0
  33. package/dist/interfaces/openai-ads.interface.d.ts +35 -0
  34. package/dist/interfaces/openai-ads.interface.js +37 -0
  35. package/dist/interfaces/openai-ads.interface.js.map +1 -0
  36. package/dist/interfaces/params.interface.d.ts +20 -0
  37. package/dist/services/feed-guard.d.ts +44 -0
  38. package/dist/services/feed-guard.js +156 -0
  39. package/dist/services/feed-guard.js.map +1 -0
  40. package/dist/services/image-url-validator.service.d.ts +45 -0
  41. package/dist/services/image-url-validator.service.js +141 -0
  42. package/dist/services/image-url-validator.service.js.map +1 -0
  43. package/dist/services/images.service.d.ts +12 -0
  44. package/dist/services/images.service.js +46 -1
  45. package/dist/services/images.service.js.map +1 -1
  46. package/dist/services/index.d.ts +1 -0
  47. package/dist/services/index.js +1 -0
  48. package/dist/services/index.js.map +1 -1
  49. package/dist/services/openai-ads-feed-run.d.ts +74 -0
  50. package/dist/services/openai-ads-feed-run.js +151 -0
  51. package/dist/services/openai-ads-feed-run.js.map +1 -0
  52. package/dist/services/openai-ads-ledger.d.ts +19 -0
  53. package/dist/services/openai-ads-ledger.js +72 -0
  54. package/dist/services/openai-ads-ledger.js.map +1 -0
  55. package/dist/services/publication-gate.d.ts +15 -0
  56. package/dist/services/publication-gate.js +107 -0
  57. package/dist/services/publication-gate.js.map +1 -0
  58. package/dist/transformers/google-feed-envkv.d.ts +2 -0
  59. package/dist/transformers/google-feed-envkv.js +62 -31
  60. package/dist/transformers/google-feed-envkv.js.map +1 -1
  61. package/dist/transformers/google-feed-item-row.d.ts +6 -1
  62. package/dist/transformers/google-feed-item-row.js +3 -1
  63. package/dist/transformers/google-feed-item-row.js.map +1 -1
  64. package/dist/transformers/openai-ads.transformer.d.ts +28 -0
  65. package/dist/transformers/openai-ads.transformer.js +144 -0
  66. package/dist/transformers/openai-ads.transformer.js.map +1 -0
  67. package/dist/transformers/openai-image-fields.d.ts +16 -0
  68. package/dist/transformers/openai-image-fields.js +34 -0
  69. package/dist/transformers/openai-image-fields.js.map +1 -0
  70. package/dist/tsconfig.build.tsbuildinfo +1 -1
  71. package/docs/openai-ads/AUDIT.md +235 -0
  72. package/docs/openai-ads/RUNBOOK.md +94 -0
  73. package/package.json +64 -64
  74. package/schema/feed-items.json +134 -122
  75. package/src/configs/config.ts +99 -97
  76. package/src/configs/google.config.ts +41 -26
  77. package/src/configs/index.ts +1 -0
  78. package/src/configs/openai-ads.config.ts +70 -0
  79. package/src/enums/export-type.enum.ts +2 -0
  80. package/src/enums/image-type.enum.ts +5 -0
  81. package/src/export.command.ts +38 -38
  82. package/src/export.schema.ts +6 -0
  83. package/src/exporters/google.exporter.ts +67 -56
  84. package/src/exporters/index.ts +1 -0
  85. package/src/exporters/openai-ads.exporter.ts +93 -0
  86. package/src/formats/openai-ads-csv.builder.ts +55 -0
  87. package/src/interfaces/openai-ads.interface.ts +64 -0
  88. package/src/interfaces/params.interface.ts +56 -29
  89. package/src/services/feed-guard.ts +219 -0
  90. package/src/services/feed-items-bigquery.writer.ts +101 -101
  91. package/src/services/image-url-validator.service.ts +181 -0
  92. package/src/services/images.service.ts +72 -0
  93. package/src/services/index.ts +1 -0
  94. package/src/services/openai-ads-feed-run.ts +226 -0
  95. package/src/services/openai-ads-ledger.ts +116 -0
  96. package/src/services/publication-gate.ts +119 -0
  97. package/src/transformers/google-feed-envkv.ts +81 -33
  98. package/src/transformers/google-feed-item-row.ts +78 -71
  99. package/src/transformers/openai-ads.transformer.ts +211 -0
  100. package/src/transformers/openai-image-fields.ts +60 -0
  101. package/test/clean-images.test.ts +86 -0
  102. package/test/fixtures/images-service.harness.ts +32 -0
  103. package/test/fixtures/images.golden.json +218 -0
  104. package/test/fixtures/imbo-images.fixture.ts +77 -0
  105. package/test/fixtures/openai-ads.fixture.ts +89 -0
  106. package/test/google-feed-item-row.test.ts +135 -121
  107. package/test/image-url-validator.test.ts +110 -0
  108. package/test/images-regression.test.ts +23 -0
  109. package/test/openai-ads-feed-run.test.ts +278 -0
  110. package/test/openai-ads-mapping.test.ts +136 -0
  111. package/test/openai-image-fields.test.ts +99 -0
  112. package/test/publication-gate.test.ts +86 -0
@@ -0,0 +1,32 @@
1
+ import { ImagesService } from '../../src/services/images.service';
2
+ import { ImageType } from '../../src/enums';
3
+ import { IMBO_FIXTURES, MARKETING_CONFIG_FIXTURE, cloneImages } from './imbo-images.fixture';
4
+
5
+ export const buildImagesService = (): ImagesService => {
6
+ const configService: any = {
7
+ config: {
8
+ baseImageUrl: 'https://images.auto.de',
9
+ marketingImageUrl: 'https://images.auto.de/generated_images',
10
+ marketingImageConfigId: 3,
11
+ },
12
+ };
13
+ const userService: any = { getMarketingImageConfig: async () => MARKETING_CONFIG_FIXTURE };
14
+ return new ImagesService(configService, userService);
15
+ };
16
+
17
+ // Every image output the existing partner exporters consume, per fixture car. The regression test
18
+ // compares this against images.golden.json, recorded from the pre-ADV-336 ImagesService.
19
+ export const snapshotExistingImageOutputs = async (): Promise<Record<string, unknown>> => {
20
+ const service = buildImagesService();
21
+ const out: Record<string, unknown> = {};
22
+ for (const [name, { carId, images }] of Object.entries(IMBO_FIXTURES)) {
23
+ const car: any = { _id: carId };
24
+ out[name] = {
25
+ defaultWithPlaceholders: await service.getImages(car, cloneImages(images), ImageType.default, false),
26
+ defaultIgnorePlaceholders: await service.getImages(car, cloneImages(images), ImageType.default, true),
27
+ marketing: images.length ? await service.getImages(car, cloneImages(images), ImageType.marketing, false) : null,
28
+ googleExtInt: await service.getExtIntDefaultImages(car, cloneImages(images), ImageType.default, false),
29
+ };
30
+ }
31
+ return out;
32
+ };
@@ -0,0 +1,218 @@
1
+ {
2
+ "full": {
3
+ "defaultWithPlaceholders": [
4
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extRight0001/large",
5
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extCowl00001/large",
6
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extLeft00001/large",
7
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extSide00001/large",
8
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extBack00001/large",
9
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extOpened001/large",
10
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/conext000001/large",
11
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/engine000001/large",
12
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/intDash00001/large",
13
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/intSeats0001/large",
14
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/intTrunk0001/large",
15
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/seatHeat0001/large",
16
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/featRims0001/large",
17
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/simple000001/large",
18
+ "https://images.auto.de/carimage/noimage/7pNvWELuWhZX/large-cached.jpeg",
19
+ "https://images.auto.de/carimage/noimage/GvyXmrpaf1D6/large-cached.jpeg",
20
+ "https://images.auto.de/carimage/noimage/pFyfBsqBHEso/large-cached.jpeg"
21
+ ],
22
+ "defaultIgnorePlaceholders": [
23
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extRight0001/large",
24
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extCowl00001/large",
25
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extLeft00001/large",
26
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extSide00001/large",
27
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extBack00001/large",
28
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extOpened001/large",
29
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/conext000001/large",
30
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/engine000001/large",
31
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/intDash00001/large",
32
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/intSeats0001/large",
33
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/intTrunk0001/large",
34
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/seatHeat0001/large",
35
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/featRims0001/large",
36
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/simple000001/large"
37
+ ],
38
+ "marketing": [
39
+ "https://images.auto.de/generated_images/2/c/e/2ce1d71f-851f-4123-ae16-187ddd7923c5/criteo-channel-extRight0001.jpg",
40
+ "https://images.auto.de/generated_images/2/c/e/2ce1d71f-851f-4123-ae16-187ddd7923c5/criteo-channel-extBack00001.jpg",
41
+ "https://images.auto.de/generated_images/2/c/e/2ce1d71f-851f-4123-ae16-187ddd7923c5/criteo-channel-intDash00001.jpg",
42
+ "https://images.auto.de/carimage/noimage/7pNvWELuWhZX/large-cached.jpeg",
43
+ "https://images.auto.de/carimage/noimage/GvyXmrpaf1D6/large-cached.jpeg",
44
+ "https://images.auto.de/carimage/noimage/pFyfBsqBHEso/large-cached.jpeg"
45
+ ],
46
+ "googleExtInt": {
47
+ "extMain": [
48
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extRight0001/small",
49
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extLeft00001/small",
50
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extOpened001/small"
51
+ ],
52
+ "ext": [
53
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extCowl00001/small",
54
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extSide00001/small",
55
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/extBack00001/small"
56
+ ],
57
+ "int": [
58
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/intDash00001/small",
59
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/intSeats0001/small",
60
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/intTrunk0001/small"
61
+ ],
62
+ "feat": [
63
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/conext000001/small",
64
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/engine000001/small",
65
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/seatHeat0001/small",
66
+ "https://images.auto.de/carimage/2ce1d71f-851f-4123-ae16-187ddd7923c5/featRims0001/small"
67
+ ]
68
+ }
69
+ },
70
+ "leftOnly": {
71
+ "defaultWithPlaceholders": [
72
+ "https://images.auto.de/carimage/3703a23a-654d-426e-801e-beeffcc1e9c8/extLeft00002/large",
73
+ "https://images.auto.de/carimage/3703a23a-654d-426e-801e-beeffcc1e9c8/extBack00002/large",
74
+ "https://images.auto.de/carimage/3703a23a-654d-426e-801e-beeffcc1e9c8/intDash00002/large",
75
+ "https://images.auto.de/carimage/noimage/7pNvWELuWhZX/large-cached.jpeg",
76
+ "https://images.auto.de/carimage/noimage/GvyXmrpaf1D6/large-cached.jpeg",
77
+ "https://images.auto.de/carimage/noimage/pFyfBsqBHEso/large-cached.jpeg"
78
+ ],
79
+ "defaultIgnorePlaceholders": [
80
+ "https://images.auto.de/carimage/3703a23a-654d-426e-801e-beeffcc1e9c8/extLeft00002/large",
81
+ "https://images.auto.de/carimage/3703a23a-654d-426e-801e-beeffcc1e9c8/extBack00002/large",
82
+ "https://images.auto.de/carimage/3703a23a-654d-426e-801e-beeffcc1e9c8/intDash00002/large"
83
+ ],
84
+ "marketing": [
85
+ "https://images.auto.de/generated_images/3/7/0/3703a23a-654d-426e-801e-beeffcc1e9c8/criteo-channel-extLeft00002.jpg",
86
+ "https://images.auto.de/generated_images/3/7/0/3703a23a-654d-426e-801e-beeffcc1e9c8/criteo-channel-extBack00002.jpg",
87
+ "https://images.auto.de/generated_images/3/7/0/3703a23a-654d-426e-801e-beeffcc1e9c8/criteo-channel-intDash00002.jpg",
88
+ "https://images.auto.de/carimage/noimage/7pNvWELuWhZX/large-cached.jpeg",
89
+ "https://images.auto.de/carimage/noimage/GvyXmrpaf1D6/large-cached.jpeg",
90
+ "https://images.auto.de/carimage/noimage/pFyfBsqBHEso/large-cached.jpeg"
91
+ ],
92
+ "googleExtInt": {
93
+ "extMain": [
94
+ "https://images.auto.de/carimage/3703a23a-654d-426e-801e-beeffcc1e9c8/extLeft00002/small"
95
+ ],
96
+ "ext": [
97
+ "https://images.auto.de/carimage/3703a23a-654d-426e-801e-beeffcc1e9c8/extBack00002/small"
98
+ ],
99
+ "int": [
100
+ "https://images.auto.de/carimage/3703a23a-654d-426e-801e-beeffcc1e9c8/intDash00002/small"
101
+ ],
102
+ "feat": []
103
+ }
104
+ },
105
+ "noHero": {
106
+ "defaultWithPlaceholders": [
107
+ "https://images.auto.de/carimage/2c91c177-ddd3-4224-8b04-bdb96bf1b64c/extBack00003/large",
108
+ "https://images.auto.de/carimage/2c91c177-ddd3-4224-8b04-bdb96bf1b64c/intDash00003/large",
109
+ "https://images.auto.de/carimage/2c91c177-ddd3-4224-8b04-bdb96bf1b64c/featRims0003/large",
110
+ "https://images.auto.de/carimage/noimage/7pNvWELuWhZX/large-cached.jpeg",
111
+ "https://images.auto.de/carimage/noimage/GvyXmrpaf1D6/large-cached.jpeg",
112
+ "https://images.auto.de/carimage/noimage/pFyfBsqBHEso/large-cached.jpeg"
113
+ ],
114
+ "defaultIgnorePlaceholders": [
115
+ "https://images.auto.de/carimage/2c91c177-ddd3-4224-8b04-bdb96bf1b64c/extBack00003/large",
116
+ "https://images.auto.de/carimage/2c91c177-ddd3-4224-8b04-bdb96bf1b64c/intDash00003/large",
117
+ "https://images.auto.de/carimage/2c91c177-ddd3-4224-8b04-bdb96bf1b64c/featRims0003/large"
118
+ ],
119
+ "marketing": [
120
+ "https://images.auto.de/generated_images/2/c/9/2c91c177-ddd3-4224-8b04-bdb96bf1b64c/criteo-channel-extBack00003.jpg",
121
+ "https://images.auto.de/generated_images/2/c/9/2c91c177-ddd3-4224-8b04-bdb96bf1b64c/criteo-channel-intDash00003.jpg",
122
+ "https://images.auto.de/carimage/noimage/7pNvWELuWhZX/large-cached.jpeg",
123
+ "https://images.auto.de/carimage/noimage/GvyXmrpaf1D6/large-cached.jpeg",
124
+ "https://images.auto.de/carimage/noimage/pFyfBsqBHEso/large-cached.jpeg"
125
+ ],
126
+ "googleExtInt": {
127
+ "extMain": [],
128
+ "ext": [
129
+ "https://images.auto.de/carimage/2c91c177-ddd3-4224-8b04-bdb96bf1b64c/extBack00003/small"
130
+ ],
131
+ "int": [
132
+ "https://images.auto.de/carimage/2c91c177-ddd3-4224-8b04-bdb96bf1b64c/intDash00003/small"
133
+ ],
134
+ "feat": [
135
+ "https://images.auto.de/carimage/2c91c177-ddd3-4224-8b04-bdb96bf1b64c/featRims0003/small"
136
+ ]
137
+ }
138
+ },
139
+ "duplicates": {
140
+ "defaultWithPlaceholders": [
141
+ "https://images.auto.de/carimage/2e101cac-fb72-4cd7-a598-2ebba983a4f9/extRight0004/large",
142
+ "https://images.auto.de/carimage/2e101cac-fb72-4cd7-a598-2ebba983a4f9/extRight0004/large",
143
+ "https://images.auto.de/carimage/2e101cac-fb72-4cd7-a598-2ebba983a4f9/extRight0005/large",
144
+ "https://images.auto.de/carimage/2e101cac-fb72-4cd7-a598-2ebba983a4f9/intDash00004/large",
145
+ "https://images.auto.de/carimage/noimage/7pNvWELuWhZX/large-cached.jpeg",
146
+ "https://images.auto.de/carimage/noimage/GvyXmrpaf1D6/large-cached.jpeg",
147
+ "https://images.auto.de/carimage/noimage/pFyfBsqBHEso/large-cached.jpeg"
148
+ ],
149
+ "defaultIgnorePlaceholders": [
150
+ "https://images.auto.de/carimage/2e101cac-fb72-4cd7-a598-2ebba983a4f9/extRight0004/large",
151
+ "https://images.auto.de/carimage/2e101cac-fb72-4cd7-a598-2ebba983a4f9/extRight0004/large",
152
+ "https://images.auto.de/carimage/2e101cac-fb72-4cd7-a598-2ebba983a4f9/extRight0005/large",
153
+ "https://images.auto.de/carimage/2e101cac-fb72-4cd7-a598-2ebba983a4f9/intDash00004/large"
154
+ ],
155
+ "marketing": [
156
+ "https://images.auto.de/generated_images/2/e/1/2e101cac-fb72-4cd7-a598-2ebba983a4f9/criteo-channel-extRight0005.jpg",
157
+ "https://images.auto.de/generated_images/2/e/1/2e101cac-fb72-4cd7-a598-2ebba983a4f9/criteo-channel-intDash00004.jpg",
158
+ "https://images.auto.de/carimage/noimage/7pNvWELuWhZX/large-cached.jpeg",
159
+ "https://images.auto.de/carimage/noimage/GvyXmrpaf1D6/large-cached.jpeg",
160
+ "https://images.auto.de/carimage/noimage/pFyfBsqBHEso/large-cached.jpeg"
161
+ ],
162
+ "googleExtInt": {
163
+ "extMain": [
164
+ "https://images.auto.de/carimage/2e101cac-fb72-4cd7-a598-2ebba983a4f9/extRight0004/small",
165
+ "https://images.auto.de/carimage/2e101cac-fb72-4cd7-a598-2ebba983a4f9/extRight0004/small",
166
+ "https://images.auto.de/carimage/2e101cac-fb72-4cd7-a598-2ebba983a4f9/extRight0005/small"
167
+ ],
168
+ "ext": [],
169
+ "int": [
170
+ "https://images.auto.de/carimage/2e101cac-fb72-4cd7-a598-2ebba983a4f9/intDash00004/small"
171
+ ],
172
+ "feat": []
173
+ }
174
+ },
175
+ "onlyUnknown": {
176
+ "defaultWithPlaceholders": [
177
+ "https://images.auto.de/carimage/61889cf8-2193-4c1e-8cdd-521ca2d38c9a/simple000006/large",
178
+ "https://images.auto.de/carimage/61889cf8-2193-4c1e-8cdd-521ca2d38c9a/promo0000006/large",
179
+ "https://images.auto.de/carimage/noimage/7pNvWELuWhZX/large-cached.jpeg",
180
+ "https://images.auto.de/carimage/noimage/GvyXmrpaf1D6/large-cached.jpeg",
181
+ "https://images.auto.de/carimage/noimage/pFyfBsqBHEso/large-cached.jpeg"
182
+ ],
183
+ "defaultIgnorePlaceholders": [
184
+ "https://images.auto.de/carimage/61889cf8-2193-4c1e-8cdd-521ca2d38c9a/simple000006/large",
185
+ "https://images.auto.de/carimage/61889cf8-2193-4c1e-8cdd-521ca2d38c9a/promo0000006/large"
186
+ ],
187
+ "marketing": [
188
+ "https://images.auto.de/carimage/noimage/7pNvWELuWhZX/large-cached.jpeg",
189
+ "https://images.auto.de/carimage/noimage/GvyXmrpaf1D6/large-cached.jpeg",
190
+ "https://images.auto.de/carimage/noimage/pFyfBsqBHEso/large-cached.jpeg"
191
+ ],
192
+ "googleExtInt": {
193
+ "extMain": [],
194
+ "ext": [],
195
+ "int": [],
196
+ "feat": []
197
+ }
198
+ },
199
+ "empty": {
200
+ "defaultWithPlaceholders": [
201
+ "https://images.auto.de/carimage/noimage/7pNvWELuWhZX/large-cached.jpeg",
202
+ "https://images.auto.de/carimage/noimage/GvyXmrpaf1D6/large-cached.jpeg",
203
+ "https://images.auto.de/carimage/noimage/pFyfBsqBHEso/large-cached.jpeg"
204
+ ],
205
+ "defaultIgnorePlaceholders": [
206
+ "https://images.auto.de/carimage/noimage/7pNvWELuWhZX/large-cached.jpeg",
207
+ "https://images.auto.de/carimage/noimage/GvyXmrpaf1D6/large-cached.jpeg",
208
+ "https://images.auto.de/carimage/noimage/pFyfBsqBHEso/large-cached.jpeg"
209
+ ],
210
+ "marketing": null,
211
+ "googleExtInt": {
212
+ "extMain": [],
213
+ "ext": [],
214
+ "int": [],
215
+ "feat": []
216
+ }
217
+ }
218
+ }
@@ -0,0 +1,77 @@
1
+ import { ImboImageResponseInterface } from '@bytebrand/chunkable';
2
+
3
+ // Imbo image lists as CarBasicDataCarsLoader hands them to the transformers (newest first, as Imbo
4
+ // returns them). Codes mirror real image_code_predicted labels, incl. the "-suffix" form.
5
+ const img = (imageIdentifier: string, imageCodePredicted?: string): ImboImageResponseInterface => ({
6
+ imageIdentifier,
7
+ imageCodePredicted,
8
+ });
9
+
10
+ export const IMBO_FIXTURES: Record<string, { carId: string; images: ImboImageResponseInterface[] }> = {
11
+ full: {
12
+ carId: '2ce1d71f-851f-4123-ae16-187ddd7923c5',
13
+ images: [
14
+ img('intDash00001', 'int_f_ipanel1'),
15
+ img('featRims0001', 'ext_feat_rims'),
16
+ img('extBack00001', 'ext_b_main'),
17
+ img('simple000001', 'simple_01'),
18
+ img('extLeft00001', 'ext_f_left'),
19
+ img('conext000001', 'conext_f_main'),
20
+ img('intSeats0001', 'int_f_driverseats-1'),
21
+ img('extCowl00001', 'ext_f_cowl'),
22
+ img('extOpened001', 'ext_f_main_opened'),
23
+ img('engine000001', 'ext_feat_engine'),
24
+ img('seatHeat0001', 'int_feat_seatheating'),
25
+ img('extRight0001', 'ext_f_right'),
26
+ img('extSide00001', 'ext_l_main'),
27
+ img('intTrunk0001', 'int_b_trunk'),
28
+ ],
29
+ },
30
+ leftOnly: {
31
+ carId: '3703a23a-654d-426e-801e-beeffcc1e9c8',
32
+ images: [
33
+ img('intDash00002', 'int_f_ipanel1'),
34
+ img('extBack00002', 'ext_b_main'),
35
+ img('extLeft00002', 'ext_f_left'),
36
+ ],
37
+ },
38
+ noHero: {
39
+ carId: '2c91c177-ddd3-4224-8b04-bdb96bf1b64c',
40
+ images: [
41
+ img('extBack00003', 'ext_b_main'),
42
+ img('intDash00003', 'int_f_ipanel1'),
43
+ img('featRims0003', 'ext_feat_rims'),
44
+ ],
45
+ },
46
+ duplicates: {
47
+ carId: '2e101cac-fb72-4cd7-a598-2ebba983a4f9',
48
+ images: [
49
+ img('extRight0004', 'ext_f_right'),
50
+ img('extRight0004', 'ext_f_right'),
51
+ img('extRight0005', 'ext_f_right'),
52
+ img('intDash00004', 'int_f_ipanel1'),
53
+ ],
54
+ },
55
+ onlyUnknown: {
56
+ carId: '61889cf8-2193-4c1e-8cdd-521ca2d38c9a',
57
+ images: [img('simple000006', 'simple_01'), img('promo0000006', 'promo_slide')],
58
+ },
59
+ empty: {
60
+ carId: '0b0999c6-2f02-4bc3-8852-2307e8a0232f',
61
+ images: [],
62
+ },
63
+ };
64
+
65
+ // Same shape as a stored MarketingImageConfig (criteo-like) for getMarketingImages.
66
+ export const MARKETING_CONFIG_FIXTURE = {
67
+ channelCode: 'criteo channel',
68
+ imageExtension: 'jpg',
69
+ marketingImages: [
70
+ { imageCodes: ['ext_f_right', 'ext_f_left'] },
71
+ { imageCodes: ['int_f_ipanel1'] },
72
+ { imageCodes: ['ext_b_main'] },
73
+ ],
74
+ };
75
+
76
+ export const cloneImages = (images: ImboImageResponseInterface[]): ImboImageResponseInterface[] =>
77
+ images.map(i => ({ ...i }));
@@ -0,0 +1,89 @@
1
+ import { ImboImageResponseInterface } from '@bytebrand/chunkable';
2
+
3
+ // Minimal RFC 4180 reader for the tests: whole file -> rows of cells (quoted newlines kept).
4
+ export const parseCsv = (text: string): string[][] => {
5
+ const rows: string[][] = [];
6
+ let row: string[] = [];
7
+ let cell = '';
8
+ let quoted = false;
9
+ for (let i = 0; i < text.length; i++) {
10
+ const c = text[i];
11
+ if (quoted) {
12
+ if (c === '"' && text[i + 1] === '"') { cell += '"'; i++; } else if (c === '"') { quoted = false; } else { cell += c; }
13
+ } else if (c === '"') {
14
+ quoted = true;
15
+ } else if (c === ',') {
16
+ row.push(cell); cell = '';
17
+ } else if (c === '\n') {
18
+ row.push(cell); rows.push(row); row = []; cell = '';
19
+ } else {
20
+ cell += c;
21
+ }
22
+ }
23
+ if (cell || row.length) { row.push(cell); rows.push(row); }
24
+ return rows;
25
+ };
26
+
27
+ export const csvObjects = (text: string): Record<string, string>[] => {
28
+ const [header, ...rows] = parseCsv(text);
29
+ return rows.map(cells => Object.fromEntries(header.map((name, i) => [name, cells[i]])));
30
+ };
31
+
32
+ // German labels the TranslatorExtended returns for these selectors (checked against
33
+ // @bytebrand/i18n-dictionaries); anything else comes back as the key itself, like the real one.
34
+ const LABELS: Record<string, string> = {
35
+ selector_fuel_petrol: 'Benzin',
36
+ selector_fuel_diesel: 'Diesel',
37
+ selector_gearbox_manualShift: 'Manuell',
38
+ selector_gearbox_automatic: 'Automatik',
39
+ selector_category_sedan: 'Limousine',
40
+ selector_category_combi: 'Kombi',
41
+ selector_exteriorColor_black: 'Schwarz',
42
+ selector_exteriorColor_grey: 'Grau',
43
+ selector_unknown: 'Unbekannt',
44
+ };
45
+ export const translate = (key: string): string => LABELS[key] ?? key;
46
+
47
+ export const NOW = new Date('2026-10-12T08:00:00Z');
48
+ const MAR_2021 = Date.UTC(2021, 2, 15) / 1000;
49
+
50
+ export const car = (id: string, overrides: Record<string, any> = {}): any => {
51
+ const base: any = {
52
+ _id: id,
53
+ mainData: {
54
+ make: 'Volkswagen',
55
+ model: 'Golf',
56
+ subModel: '1.5 TSI Life',
57
+ condition: 'selector_condition_used',
58
+ firstRegistration: MAR_2021,
59
+ mileage: 45000,
60
+ category: 'selector_category_sedan',
61
+ },
62
+ metaData: { ownerId: 'dealer-1', imagesCount: 2, vehicleId: 'ABC123' },
63
+ price: { currentSalesPrice: 17990 },
64
+ rate: { monthlyInstallment: 199, paybackPeriod: 48 },
65
+ consumption: { fuel: 'selector_fuel_petrol' },
66
+ driveSuspension: { gearbox: 'selector_gearbox_manualShift' },
67
+ engineData: { powerKW: 110, powerPS: 150 },
68
+ exterior: { exteriorColor: 'selector_exteriorColor_black' },
69
+ offer: { offerAvailability: 'selector_unknown' },
70
+ state: { isOnline: true },
71
+ };
72
+ for (const [path, value] of Object.entries(overrides)) {
73
+ const keys = path.split('.');
74
+ let target = base;
75
+ keys.slice(0, -1).forEach((key) => { target[key] = target[key] ?? {}; target = target[key]; });
76
+ if (value === undefined) {
77
+ delete target[keys[keys.length - 1]];
78
+ } else {
79
+ target[keys[keys.length - 1]] = value;
80
+ }
81
+ }
82
+ return base;
83
+ };
84
+
85
+ export const images = (prefix: string): ImboImageResponseInterface[] => [
86
+ { imageIdentifier: `${prefix}Back`, imageCodePredicted: 'ext_b_main' },
87
+ { imageIdentifier: `${prefix}Right`, imageCodePredicted: 'ext_f_right' },
88
+ { imageIdentifier: `${prefix}Dash`, imageCodePredicted: 'int_f_ipanel1' },
89
+ ];