@bytebrand/export-cars 1.26.6 → 1.26.8

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.
@@ -1,3 +1,4 @@
1
1
  export declare function convertToCSV(data: unknown): string;
2
2
  export declare const xmlStringReplacer: (str: string) => string;
3
+ export declare function normalizeTitle(title: string): string;
3
4
  export declare function parseCsvToObjects<T>(csv: string): T[];
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseCsvToObjects = exports.xmlStringReplacer = exports.convertToCSV = void 0;
3
+ exports.parseCsvToObjects = exports.normalizeTitle = exports.xmlStringReplacer = exports.convertToCSV = void 0;
4
4
  const lodash_1 = require("lodash");
5
5
  function convertToCSV(data) {
6
6
  const converted = [];
@@ -16,6 +16,16 @@ const xmlStringReplacer = (str) => {
16
16
  return typeof str === 'string' ? str.replace(/&/g, '&amp;').replace(/[<>]/g, '') : '';
17
17
  };
18
18
  exports.xmlStringReplacer = xmlStringReplacer;
19
+ const TITLE_DISALLOWED_CHARS = /[^\p{L}\p{N} .,\-/()%"°&]/gu;
20
+ const TITLE_EDGE_SEPARATORS = /^[\s.,\-/&]+|[\s.,\-/&]+$/g;
21
+ function normalizeTitle(title) {
22
+ return (title || '')
23
+ .replace(TITLE_DISALLOWED_CHARS, ' ')
24
+ .replace(/([.,\-/])\1+/g, '$1')
25
+ .replace(/\s+/g, ' ')
26
+ .replace(TITLE_EDGE_SEPARATORS, '');
27
+ }
28
+ exports.normalizeTitle = normalizeTitle;
19
29
  function parseCsvToObjects(csv) {
20
30
  const rows = [];
21
31
  let row = [];
@@ -1 +1 @@
1
- {"version":3,"file":"converter.utils.js","sourceRoot":"","sources":["../../src/utils/converter.utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"converter.utils.js","sourceRoot":"","sources":["../../src/utils/converter.utils.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytebrand/export-cars",
3
- "version": "1.26.6",
3
+ "version": "1.26.8",
4
4
  "description": "",
5
5
  "author": "bytebrand",
6
6
  "license": "UNLICENSED",
@@ -189,12 +189,13 @@ export class ImagesService {
189
189
  return ignoreDefImages ? [] : [...this.DEFAULT_IMAGES];
190
190
  }
191
191
 
192
- // marketingImageUrl (config.ts environmentConfig[env].imageUrl) points at /cache_test on
193
- // dev — a leftover from the old "fb" channel, not where image-generator publishes meta_aia
194
- // composites (always under /generated_images on both dev and prod). Build the meta_aia base
195
- // from the plain image host instead of trusting that per-env path.
192
+ // marketingImageUrl AND config.imageUrl (FbConfig overrides the latter to the same
193
+ // per-env value right after super()) both point at the old "fb" channel's base path
194
+ // (/cache_test on dev, already /generated_images on prod) — not where image-generator
195
+ // publishes meta_aia composites. baseImageUrl is the one field neither Config nor FbConfig
196
+ // ever suffixes with a path, so it's the only safe base to build /generated_images on.
196
197
  const imageUrl = config.channelCode === 'meta_aia'
197
- ? `${this.configService.config.imageUrl}/generated_images`
198
+ ? `${this.configService.config.baseImageUrl}/generated_images`
198
199
  : this.configService.config.marketingImageUrl;
199
200
 
200
201
  const marketingImageConfigs = config.marketingImages.map(i => ({ marketingImageCodes: i.imageCodes }));
@@ -46,13 +46,8 @@ export class FbDescription {
46
46
  key: get(this.car, 'engineData.cylinders', '') as string,
47
47
  },
48
48
  },
49
- {
50
- title: 'accordion.captionCapacity',
51
- value: {
52
- key: 'title.cubicCapacity',
53
- data: { cubicCapacity: get(this.car, 'engineData.cubicCapacity') },
54
- },
55
- },
49
+ // "Hubraum" is not exported: the value key resolved to the label itself
50
+ // (title.cubicCapacity === "Hubraum"), so every listing read "Hubraum: Hubraum".
56
51
  {
57
52
  title: 'accordion.captionPower',
58
53
  value: {
@@ -166,12 +161,8 @@ export class FbDescription {
166
161
  },
167
162
  },
168
163
  },
169
- {
170
- title: 'title.base',
171
- value: {
172
- key: get(this.car, 'driveSuspension.wheelBase', '') as string,
173
- },
174
- },
164
+ // "Radstand" is not exported: driveSuspension.wheelBase is stored as null across the
165
+ // catalogue, so every listing read "Radstand: ;".
175
166
  {
176
167
  title: 'title.loadingVolume',
177
168
  value: {
@@ -1,7 +1,6 @@
1
1
  import { Cache } from 'cache-manager';
2
2
  import * as moment from 'moment';
3
3
  import {
4
- escape,
5
4
  findIndex,
6
5
  get,
7
6
  includes,
@@ -12,7 +11,6 @@ import {
12
11
  join,
13
12
  toSafeInteger,
14
13
  toString,
15
- trim,
16
14
  truncate,
17
15
  } from 'lodash';
18
16
 
@@ -30,7 +28,7 @@ import { Description } from '../interfaces/description.interface';
30
28
  import { FbDescription } from './fb-description.transformer';
31
29
  import { ConfigService, ImagesService, MappingService } from '../services';
32
30
  import { FbEquipment } from './fb-equipment.transformer';
33
- import { isValidVin } from '../utils';
31
+ import { isValidVin, normalizeTitle } from '../utils';
34
32
  import { UserService } from 'src/services';
35
33
  import { ImageType } from '../enums';
36
34
  import {
@@ -188,14 +186,16 @@ export class FbTransformer {
188
186
  }
189
187
 
190
188
  private getTitle(car: DataSchemes.Car): string {
191
- let year = toString(get(car, 'mainData.constructionYear', ''));
192
- if (year === '0') {
193
- year = '';
194
- }
189
+ // mainData.constructionYear holds a raw epoch for part of the catalogue — the same
190
+ // corruption getFirstValidYear() already guards <year> against. Without the guard titles
191
+ // ship as "1609459200 Ford Galaxy …"; an invalid year is dropped instead.
192
+ const year = toString(getFirstValidYear(toString(get(car, 'mainData.constructionYear', ''))));
195
193
  const make = toString(get(car, 'mainData.make', ''));
196
194
  const model = toString(get(car, 'mainData.model', ''));
197
195
  const subModel = toString(get(car, 'mainData.subModel', ''));
198
- return trim(escape(join([year, make, model, subModel], ' ')));
196
+ // No escape() here: FbXmlBuilder writes <title> inside CDATA, so HTML entities are not
197
+ // decoded by the consumer and leak literally ("Monte Carlo &amp; KESSY").
198
+ return normalizeTitle(join([year, make, model, subModel], ' '));
199
199
  }
200
200
 
201
201
  private getTrim(car: DataSchemes.Car): string {
@@ -14,6 +14,23 @@ export const xmlStringReplacer = (str: string): string => {
14
14
  return typeof str === 'string' ? str.replace(/&/g, '&amp;').replace(/[<>]/g, '') : '';
15
15
  };
16
16
 
17
+ // Dealer-supplied subModel strings are full of decorative punctuation ("*AHK*Leder*Navi*",
18
+ // "|ACC|PANO/AHK", "#PANO #NAVI #SONY #") which must not reach Meta. Keep letters, digits and
19
+ // the punctuation that carries meaning in German trim names — "." "," "-" "/" "(" ")" "%",
20
+ // "°" (360° Kamera), '"' (Zoll) and "&" (audio brands: B&O, h&k) — and turn everything else
21
+ // into a space so neighbouring words never fuse.
22
+ const TITLE_DISALLOWED_CHARS = /[^\p{L}\p{N} .,\-/()%"°&]/gu;
23
+ // Leading/trailing separators only; '"' and "%" are legitimate title endings (`LM18"`, `-35%`).
24
+ const TITLE_EDGE_SEPARATORS = /^[\s.,\-/&]+|[\s.,\-/&]+$/g;
25
+
26
+ export function normalizeTitle(title: string): string {
27
+ return (title || '')
28
+ .replace(TITLE_DISALLOWED_CHARS, ' ')
29
+ .replace(/([.,\-/])\1+/g, '$1') // "L4H2 //Klimaanlage" -> "L4H2 /Klimaanlage"
30
+ .replace(/\s+/g, ' ')
31
+ .replace(TITLE_EDGE_SEPARATORS, '');
32
+ }
33
+
17
34
  // Parses a "Publish to web" Google Sheets CSV export (RFC4180-ish, quoted fields may
18
35
  // contain commas/newlines) into row arrays, then maps rows to objects keyed by header.
19
36
  export function parseCsvToObjects<T>(csv: string): T[] {