ropegeo-common 1.10.0 → 1.10.2

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.
package/README.md CHANGED
@@ -105,7 +105,7 @@ Helper tables use columns **Name**, **Description**, **Import**. Model tables ad
105
105
  | `Q_ACA_WATER` | N/A | Query key for ACA water rating list. | `import { Q_ACA_WATER } from 'ropegeo-common/models'` |
106
106
  | `Q_ACA_TIME` | N/A | Query key for ACA time rating list. | `import { Q_ACA_TIME } from 'ropegeo-common/models'` |
107
107
  | `Q_ACA_RISK` | N/A | Query key for ACA risk rating list. | `import { Q_ACA_RISK } from 'ropegeo-common/models'` |
108
- | `RoutesParams` | `PaginationParams` | Validated GET /routes params (region, source, route type, difficulty, `limit`, `page`). | `import { RoutesParams } from 'ropegeo-common/models'` |
108
+ | `RoutesParams` | `PaginationParams` | Validated GET /routes params (region, source, optional `route-types` pipe-list, difficulty, `limit`, `page`). | `import { RoutesParams } from 'ropegeo-common/models'` |
109
109
  | `SearchParams` | `CursorPaginationParams` | Validated GET /search params including cursor pagination and difficulty. | `import { SearchParams } from 'ropegeo-common/models'` |
110
110
  | `SearchOrder` | N/A | Search sort order (`similarity` \| `quality` \| `distance`). | `import type { SearchOrder } from 'ropegeo-common/models'` |
111
111
  | `SearchParamsPosition` | N/A | `{ lat, lon }` for distance search. | `import type { SearchParamsPosition } from 'ropegeo-common/models'` |
@@ -7,6 +7,7 @@ import { DifficultyParams } from './difficultyParams';
7
7
  * Validated params for getRoutes (GET /routes).
8
8
  * Global request: no `region` query param. Region-scoped: `region` id required; optional `source`
9
9
  * pipe-list (omit or empty = all sources). `source` must not appear without `region`.
10
+ * Optional `route-types` query param is a pipe-list of {@link RouteType} values (omit or empty = all types).
10
11
  * Includes page-based `limit` and `page` (defaults {@link PaginationParams.DEFAULT_LIMIT} / {@link PaginationParams.DEFAULT_PAGE}).
11
12
  */
12
13
  export declare class RoutesParams extends PaginationParams {
@@ -18,14 +19,15 @@ export declare class RoutesParams extends PaginationParams {
18
19
  id: string;
19
20
  source: PageDataSource[] | null;
20
21
  } | null;
21
- readonly routeType: RouteType | null;
22
+ /** Null = no route-type filter; non-empty = allow-list (pipe-encoded in query strings as `route-types`). */
23
+ readonly routeTypes: RouteType[] | null;
22
24
  readonly difficulty: DifficultyParams | null;
23
25
  constructor(options: {
24
26
  region: {
25
27
  id: string;
26
28
  source: PageDataSource[] | null;
27
29
  } | null;
28
- routeType?: RouteType | null;
30
+ routeTypes?: RouteType[] | null;
29
31
  difficulty?: DifficultyParams | null;
30
32
  limit?: number;
31
33
  page?: number;
@@ -39,6 +41,10 @@ export declare class RoutesParams extends PaginationParams {
39
41
  private static parsePageQuery;
40
42
  private static normalizeDifficulty;
41
43
  private static parseRouteType;
44
+ /** Dedupes while preserving first-seen order; `null` / empty → `null`. */
45
+ private static normalizeRouteTypeList;
46
+ /** Pipe-separated tokens, same encoding as {@link RoutesParams.toQueryString}. */
47
+ private static parseRouteTypePipe;
42
48
  private static parseSourcePipe;
43
49
  private static parseSourceToken;
44
50
  /**
@@ -48,7 +54,7 @@ export declare class RoutesParams extends PaginationParams {
48
54
  static fromResult(result: unknown, requiredRegion?: boolean): RoutesParams;
49
55
  private static paginationFromResult;
50
56
  private static optionalPositiveInt;
51
- private static optionalRouteTypeFromResult;
57
+ private static optionalRouteTypesFromResult;
52
58
  private static optionalDifficultyFromResult;
53
59
  private static coerceTrimmedString;
54
60
  }
@@ -1 +1 @@
1
- {"version":3,"file":"routesParams.d.ts","sourceRoot":"","sources":["../../../../src/models/api/params/routesParams.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,mCAAmC,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAKtD;;;;;GAKG;AACH,qBAAa,YAAa,SAAQ,gBAAgB;IAC9C;;;OAGG;IACH,SAAgB,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/E,SAAgB,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5C,SAAgB,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;gBAExC,OAAO,EAAE;QACjB,MAAM,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAA;SAAE,GAAG,IAAI,CAAC;QAC/D,SAAS,CAAC,EAAE,SAAS,GAAG,IAAI,CAAC;QAC7B,UAAU,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;QACrC,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;KACjB;IAqCD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;IAUpC,kDAAkD;IAClD,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAclC,aAAa,IAAI,MAAM;IAevB,MAAM,CAAC,qBAAqB,CACxB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC,YAAY;IA0Cf,OAAO,CAAC,MAAM,CAAC,eAAe;IAiB9B,OAAO,CAAC,MAAM,CAAC,cAAc;IAY7B,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAOlC,OAAO,CAAC,MAAM,CAAC,cAAc;IAQ7B,OAAO,CAAC,MAAM,CAAC,eAAe;IAS9B,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAU/B;;;OAGG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,cAAc,UAAQ,GAAG,YAAY;IA8ExE,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAkBnC,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAsBlC,OAAO,CAAC,MAAM,CAAC,2BAA2B;IAW1C,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAkB3C,OAAO,CAAC,MAAM,CAAC,mBAAmB;CAWrC"}
1
+ {"version":3,"file":"routesParams.d.ts","sourceRoot":"","sources":["../../../../src/models/api/params/routesParams.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,mCAAmC,CAAC;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAKtD;;;;;;GAMG;AACH,qBAAa,YAAa,SAAQ,gBAAgB;IAC9C;;;OAGG;IACH,SAAgB,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;IAC/E,4GAA4G;IAC5G,SAAgB,UAAU,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAC/C,SAAgB,UAAU,EAAE,gBAAgB,GAAG,IAAI,CAAC;gBAExC,OAAO,EAAE;QACjB,MAAM,EAAE;YAAE,EAAE,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAA;SAAE,GAAG,IAAI,CAAC;QAC/D,UAAU,CAAC,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;QAChC,UAAU,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAC;QACrC,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;KACjB;IAkCD,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY;IAUpC,kDAAkD;IAClD,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAclC,aAAa,IAAI,MAAM;IAevB,MAAM,CAAC,qBAAqB,CACxB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC,YAAY;IA0Cf,OAAO,CAAC,MAAM,CAAC,eAAe;IAiB9B,OAAO,CAAC,MAAM,CAAC,cAAc;IAY7B,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAOlC,OAAO,CAAC,MAAM,CAAC,cAAc;IAQ7B,0EAA0E;IAC1E,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAcrC,kFAAkF;IAClF,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAUjC,OAAO,CAAC,MAAM,CAAC,eAAe;IAS9B,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAU/B;;;OAGG;IACH,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,EAAE,cAAc,UAAQ,GAAG,YAAY;IAoFxE,OAAO,CAAC,MAAM,CAAC,oBAAoB;IAkBnC,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAsBlC,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAwB3C,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAkB3C,OAAO,CAAC,MAAM,CAAC,mBAAmB;CAWrC"}
@@ -12,16 +12,14 @@ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12
12
12
  * Validated params for getRoutes (GET /routes).
13
13
  * Global request: no `region` query param. Region-scoped: `region` id required; optional `source`
14
14
  * pipe-list (omit or empty = all sources). `source` must not appear without `region`.
15
+ * Optional `route-types` query param is a pipe-list of {@link RouteType} values (omit or empty = all types).
15
16
  * Includes page-based `limit` and `page` (defaults {@link PaginationParams.DEFAULT_LIMIT} / {@link PaginationParams.DEFAULT_PAGE}).
16
17
  */
17
18
  class RoutesParams extends paginationParams_1.PaginationParams {
18
19
  constructor(options) {
19
20
  const limit = options.limit ?? paginationParams_1.PaginationParams.DEFAULT_LIMIT;
20
21
  const page = options.page ?? paginationParams_1.PaginationParams.DEFAULT_PAGE;
21
- const routeType = options.routeType ?? null;
22
- if (routeType !== null && !Object.values(routeType_1.RouteType).includes(routeType)) {
23
- throw new Error(`Invalid route type: ${JSON.stringify(routeType)}`);
24
- }
22
+ const routeTypes = RoutesParams.normalizeRouteTypeList(options.routeTypes ?? null);
25
23
  const diffRaw = options.difficulty ?? null;
26
24
  const diff = diffRaw !== null && diffRaw.isActive() ? diffRaw : null;
27
25
  const reg = options.region;
@@ -42,13 +40,13 @@ class RoutesParams extends paginationParams_1.PaginationParams {
42
40
  }
43
41
  super(limit, page);
44
42
  this.region = regionNorm;
45
- this.routeType = routeType;
43
+ this.routeTypes = routeTypes;
46
44
  this.difficulty = diff;
47
45
  }
48
46
  withPage(page) {
49
47
  return new RoutesParams({
50
48
  region: this.region,
51
- routeType: this.routeType,
49
+ routeTypes: this.routeTypes,
52
50
  difficulty: this.difficulty,
53
51
  limit: this.limit,
54
52
  page,
@@ -76,8 +74,8 @@ class RoutesParams extends paginationParams_1.PaginationParams {
76
74
  p.set('source', this.region.source.join('|'));
77
75
  }
78
76
  }
79
- if (this.routeType != null) {
80
- p.set('route-type', this.routeType);
77
+ if (this.routeTypes != null && this.routeTypes.length > 0) {
78
+ p.set('route-types', this.routeTypes.join('|'));
81
79
  }
82
80
  difficultyParams_1.DifficultyParams.appendToUrlSearchParams(p, this.difficulty);
83
81
  return p.toString();
@@ -87,10 +85,10 @@ class RoutesParams extends paginationParams_1.PaginationParams {
87
85
  const page = RoutesParams.parsePageQuery(q);
88
86
  const regionRaw = (q.region ?? q.Region ?? '').trim();
89
87
  const sourceRaw = (q.source ?? q.Source ?? '').trim();
90
- const routeTypeStr = (q['route-type'] ?? q['Route-Type'] ?? '').trim();
91
- const routeType = routeTypeStr === ''
88
+ const routeTypesStr = (q['route-types'] ?? q['Route-Types'] ?? '').trim();
89
+ const routeTypes = routeTypesStr === ''
92
90
  ? null
93
- : RoutesParams.parseRouteType(routeTypeStr);
91
+ : RoutesParams.parseRouteTypePipe(routeTypesStr);
94
92
  const difficulty = RoutesParams.normalizeDifficulty(difficultyParams_1.DifficultyParams.fromQueryStringParams(q));
95
93
  if (regionRaw === '') {
96
94
  if (sourceRaw !== '') {
@@ -98,7 +96,7 @@ class RoutesParams extends paginationParams_1.PaginationParams {
98
96
  }
99
97
  return new RoutesParams({
100
98
  region: null,
101
- routeType,
99
+ routeTypes,
102
100
  difficulty,
103
101
  limit,
104
102
  page,
@@ -109,7 +107,7 @@ class RoutesParams extends paginationParams_1.PaginationParams {
109
107
  : RoutesParams.parseSourcePipe(sourceRaw);
110
108
  return new RoutesParams({
111
109
  region: { id: regionRaw, source: sources },
112
- routeType,
110
+ routeTypes,
113
111
  difficulty,
114
112
  limit,
115
113
  page,
@@ -149,7 +147,32 @@ class RoutesParams extends paginationParams_1.PaginationParams {
149
147
  const exact = Object.values(routeType_1.RouteType).find((v) => v === value);
150
148
  if (exact !== undefined)
151
149
  return exact;
152
- throw new Error(`Query parameter "route-type" must be one of: ${Object.values(routeType_1.RouteType).join(', ')}`);
150
+ throw new Error(`Query parameter "route-types" must be one of: ${Object.values(routeType_1.RouteType).join(', ')}`);
151
+ }
152
+ /** Dedupes while preserving first-seen order; `null` / empty → `null`. */
153
+ static normalizeRouteTypeList(list) {
154
+ if (list == null || list.length === 0)
155
+ return null;
156
+ const out = [];
157
+ for (const t of list) {
158
+ if (!Object.values(routeType_1.RouteType).includes(t)) {
159
+ throw new Error(`Invalid route type: ${JSON.stringify(t)}`);
160
+ }
161
+ if (!out.includes(t))
162
+ out.push(t);
163
+ }
164
+ return out;
165
+ }
166
+ /** Pipe-separated tokens, same encoding as {@link RoutesParams.toQueryString}. */
167
+ static parseRouteTypePipe(raw) {
168
+ const parts = raw
169
+ .split('|')
170
+ .map((s) => s.trim())
171
+ .filter((s) => s.length > 0);
172
+ if (parts.length === 0)
173
+ return null;
174
+ const types = parts.map((p) => RoutesParams.parseRouteType(p));
175
+ return RoutesParams.normalizeRouteTypeList(types);
153
176
  }
154
177
  static parseSourcePipe(raw) {
155
178
  const parts = raw
@@ -186,9 +209,15 @@ class RoutesParams extends paginationParams_1.PaginationParams {
186
209
  if (requiredRegion) {
187
210
  throw new Error('RoutesParams: region must be a non-null { id, source? } object when requiredRegion is true');
188
211
  }
189
- const routeType = RoutesParams.optionalRouteTypeFromResult(r);
212
+ const routeTypes = RoutesParams.optionalRouteTypesFromResult(r);
190
213
  const difficulty = RoutesParams.normalizeDifficulty(RoutesParams.optionalDifficultyFromResult(r));
191
- return new RoutesParams({ region: null, routeType, difficulty, limit, page });
214
+ return new RoutesParams({
215
+ region: null,
216
+ routeTypes,
217
+ difficulty,
218
+ limit,
219
+ page,
220
+ });
192
221
  }
193
222
  if (typeof raw !== 'object') {
194
223
  throw new Error('RoutesParams.region must be an object or null, got: ' + typeof raw);
@@ -222,14 +251,14 @@ class RoutesParams extends paginationParams_1.PaginationParams {
222
251
  else {
223
252
  throw new Error('RoutesParams.region.source must be string, string[], or null');
224
253
  }
225
- const routeType = RoutesParams.optionalRouteTypeFromResult(r);
254
+ const routeTypes = RoutesParams.optionalRouteTypesFromResult(r);
226
255
  const difficulty = RoutesParams.normalizeDifficulty(RoutesParams.optionalDifficultyFromResult(r));
227
256
  return new RoutesParams({
228
257
  region: {
229
258
  id: idStr,
230
259
  source: RoutesParams.normalizeSourceList(sourceList),
231
260
  },
232
- routeType,
261
+ routeTypes,
233
262
  difficulty,
234
263
  limit,
235
264
  page,
@@ -256,14 +285,23 @@ class RoutesParams extends paginationParams_1.PaginationParams {
256
285
  }
257
286
  return v;
258
287
  }
259
- static optionalRouteTypeFromResult(r) {
260
- const v = r.routeType ?? r['route-type'] ?? r.RouteType;
288
+ static optionalRouteTypesFromResult(r) {
289
+ const v = r.routeTypes ?? r['route-types'] ?? r.RouteTypes;
261
290
  if (v === null || v === undefined || v === '')
262
291
  return null;
263
- if (typeof v !== 'string') {
264
- throw new Error('RoutesParams.routeType must be a string or null');
292
+ if (typeof v === 'string') {
293
+ return RoutesParams.parseRouteTypePipe(v);
294
+ }
295
+ if (Array.isArray(v)) {
296
+ const types = v.map((item, i) => {
297
+ if (typeof item !== 'string') {
298
+ throw new Error(`RoutesParams.routeTypes[${i}] must be a string`);
299
+ }
300
+ return RoutesParams.parseRouteType(item);
301
+ });
302
+ return RoutesParams.normalizeRouteTypeList(types);
265
303
  }
266
- return RoutesParams.parseRouteType(v);
304
+ throw new Error('RoutesParams.routeTypes must be a string, string[], or null');
267
305
  }
268
306
  static optionalDifficultyFromResult(r) {
269
307
  const nested = r.difficulty ?? r.Difficulty;
@@ -9,15 +9,19 @@ import { DifficultyFilterOptions } from './difficultyFilterOptions';
9
9
  export declare class RouteFilter {
10
10
  source: PageDataSource[] | null;
11
11
  regionId: string | null;
12
- routeType: RouteType | null;
12
+ /** Null or empty = no route-type filter (all types). */
13
+ routeTypes: RouteType[] | null;
13
14
  difficultyOptions: DifficultyFilterOptions | null;
14
- constructor(source?: PageDataSource[] | null, regionId?: string | null, routeType?: RouteType | null, difficultyOptions?: DifficultyFilterOptions | null);
15
+ constructor(source?: PageDataSource[] | null, regionId?: string | null, routeTypes?: RouteType[] | null, difficultyOptions?: DifficultyFilterOptions | null);
15
16
  toRoutesParams(): RoutesParams;
16
17
  toJSON(): Record<string, unknown>;
17
18
  toString(): string;
18
19
  static fromJsonString(json: string): RouteFilter;
19
20
  static fromJSON(parsed: unknown): RouteFilter;
21
+ private static normalizeRouteTypesList;
22
+ /** Accepts `routeTypes` array or legacy singular `routeType` string. */
23
+ private static parseRouteTypesField;
24
+ private static parseRouteTypeToken;
20
25
  private static parseSourceField;
21
- private static parseRouteType;
22
26
  }
23
27
  //# sourceMappingURL=routeFilter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"routeFilter.d.ts","sourceRoot":"","sources":["../../../src/models/filters/routeFilter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,0CAA0C,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAEpE;;GAEG;AACH,qBAAa,WAAW;IACpB,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAChC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,SAAS,GAAG,IAAI,CAAC;IAC5B,iBAAiB,EAAE,uBAAuB,GAAG,IAAI,CAAC;gBAG9C,MAAM,GAAE,cAAc,EAAE,GAAG,IAAW,EACtC,QAAQ,GAAE,MAAM,GAAG,IAAW,EAC9B,SAAS,GAAE,SAAS,GAAG,IAAW,EAClC,iBAAiB,GAAE,uBAAuB,GAAG,IAAW;IAQ5D,cAAc,IAAI,YAAY;IAkC9B,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAYjC,QAAQ,IAAI,MAAM;IAIlB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW;IAYhD,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,WAAW;IAqB7C,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAoB/B,OAAO,CAAC,MAAM,CAAC,cAAc;CAMhC"}
1
+ {"version":3,"file":"routeFilter.d.ts","sourceRoot":"","sources":["../../../src/models/filters/routeFilter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAC1D,OAAO,0CAA0C,CAAC;AAClD,OAAO,EAAE,uBAAuB,EAAE,MAAM,2BAA2B,CAAC;AAEpE;;GAEG;AACH,qBAAa,WAAW;IACpB,MAAM,EAAE,cAAc,EAAE,GAAG,IAAI,CAAC;IAChC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,wDAAwD;IACxD,UAAU,EAAE,SAAS,EAAE,GAAG,IAAI,CAAC;IAC/B,iBAAiB,EAAE,uBAAuB,GAAG,IAAI,CAAC;gBAG9C,MAAM,GAAE,cAAc,EAAE,GAAG,IAAW,EACtC,QAAQ,GAAE,MAAM,GAAG,IAAW,EAC9B,UAAU,GAAE,SAAS,EAAE,GAAG,IAAW,EACrC,iBAAiB,GAAE,uBAAuB,GAAG,IAAW;IAQ5D,cAAc,IAAI,YAAY;IAwC9B,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAYjC,QAAQ,IAAI,MAAM;IAIlB,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW;IAYhD,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,GAAG,WAAW;IAkB7C,OAAO,CAAC,MAAM,CAAC,uBAAuB;IActC,wEAAwE;IACxE,OAAO,CAAC,MAAM,CAAC,oBAAoB;IA0BnC,OAAO,CAAC,MAAM,CAAC,mBAAmB;IAOlC,OAAO,CAAC,MAAM,CAAC,gBAAgB;CAmBlC"}
@@ -10,10 +10,10 @@ const difficultyFilterOptions_1 = require("./difficultyFilterOptions");
10
10
  * Persisted explore / minimap route filter. Null fields mean “no constraint” on that axis.
11
11
  */
12
12
  class RouteFilter {
13
- constructor(source = null, regionId = null, routeType = null, difficultyOptions = null) {
13
+ constructor(source = null, regionId = null, routeTypes = null, difficultyOptions = null) {
14
14
  this.source = source;
15
15
  this.regionId = regionId;
16
- this.routeType = routeType;
16
+ this.routeTypes = RouteFilter.normalizeRouteTypesList(routeTypes);
17
17
  this.difficultyOptions = difficultyOptions;
18
18
  }
19
19
  toRoutesParams() {
@@ -26,7 +26,9 @@ class RouteFilter {
26
26
  }
27
27
  return new routesParams_1.RoutesParams({
28
28
  region: null,
29
- routeType: this.routeType,
29
+ routeTypes: this.routeTypes != null && this.routeTypes.length > 0
30
+ ? [...this.routeTypes]
31
+ : null,
30
32
  difficulty: this.difficultyOptions !== null
31
33
  ? this.difficultyOptions.toDifficultyParams()
32
34
  : null,
@@ -37,7 +39,9 @@ class RouteFilter {
37
39
  : [...this.source];
38
40
  return new routesParams_1.RoutesParams({
39
41
  region: { id: rid, source: src },
40
- routeType: this.routeType,
42
+ routeTypes: this.routeTypes != null && this.routeTypes.length > 0
43
+ ? [...this.routeTypes]
44
+ : null,
41
45
  difficulty: this.difficultyOptions !== null
42
46
  ? this.difficultyOptions.toDifficultyParams()
43
47
  : null,
@@ -47,7 +51,7 @@ class RouteFilter {
47
51
  return {
48
52
  source: this.source,
49
53
  regionId: this.regionId,
50
- routeType: this.routeType,
54
+ routeTypes: this.routeTypes,
51
55
  difficultyOptions: this.difficultyOptions !== null
52
56
  ? this.difficultyOptions.toJSON()
53
57
  : null,
@@ -75,14 +79,57 @@ class RouteFilter {
75
79
  const regionId = o.regionId === null || o.regionId === undefined
76
80
  ? null
77
81
  : String(o.regionId);
78
- const routeType = o.routeType === null || o.routeType === undefined
79
- ? null
80
- : RouteFilter.parseRouteType(o.routeType);
82
+ const routeTypes = RouteFilter.parseRouteTypesField(o);
81
83
  let difficultyOptions = null;
82
84
  if (o.difficultyOptions != null && typeof o.difficultyOptions === 'object') {
83
85
  difficultyOptions = difficultyFilterOptions_1.DifficultyFilterOptions.fromResult(o.difficultyOptions);
84
86
  }
85
- return new RouteFilter(source, regionId, routeType, difficultyOptions);
87
+ return new RouteFilter(source, regionId, routeTypes, difficultyOptions);
88
+ }
89
+ static normalizeRouteTypesList(list) {
90
+ if (list == null || list.length === 0)
91
+ return null;
92
+ const out = [];
93
+ for (const t of list) {
94
+ if (!Object.values(routeType_1.RouteType).includes(t)) {
95
+ throw new Error(`Invalid RouteType: ${JSON.stringify(t)}`);
96
+ }
97
+ if (!out.includes(t))
98
+ out.push(t);
99
+ }
100
+ return out;
101
+ }
102
+ /** Accepts `routeTypes` array or legacy singular `routeType` string. */
103
+ static parseRouteTypesField(o) {
104
+ const raw = o.routeTypes;
105
+ if (raw !== null && raw !== undefined) {
106
+ if (!Array.isArray(raw)) {
107
+ throw new Error('RouteFilter.routeTypes must be an array or null');
108
+ }
109
+ const types = raw.map((item, i) => {
110
+ if (typeof item !== 'string') {
111
+ throw new Error(`RouteFilter.routeTypes[${i}] must be a string`);
112
+ }
113
+ return RouteFilter.parseRouteTypeToken(item);
114
+ });
115
+ return RouteFilter.normalizeRouteTypesList(types);
116
+ }
117
+ const legacy = o.routeType;
118
+ if (legacy === null || legacy === undefined) {
119
+ return null;
120
+ }
121
+ if (typeof legacy !== 'string') {
122
+ throw new Error('RouteFilter.routeType must be a string or null');
123
+ }
124
+ return RouteFilter.normalizeRouteTypesList([
125
+ RouteFilter.parseRouteTypeToken(legacy),
126
+ ]);
127
+ }
128
+ static parseRouteTypeToken(v) {
129
+ if (!Object.values(routeType_1.RouteType).includes(v)) {
130
+ throw new Error(`Invalid RouteType: ${JSON.stringify(v)}`);
131
+ }
132
+ return v;
86
133
  }
87
134
  static parseSourceField(v) {
88
135
  if (v === null || v === undefined)
@@ -104,11 +151,5 @@ class RouteFilter {
104
151
  }
105
152
  return out.length === 0 ? null : out;
106
153
  }
107
- static parseRouteType(v) {
108
- if (typeof v !== 'string' || !Object.values(routeType_1.RouteType).includes(v)) {
109
- throw new Error(`Invalid RouteType: ${JSON.stringify(v)}`);
110
- }
111
- return v;
112
- }
113
154
  }
114
155
  exports.RouteFilter = RouteFilter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ropegeo-common",
3
- "version": "1.10.0",
3
+ "version": "1.10.2",
4
4
  "description": "Shared domain models and helpers for RopeGeo and WebScraper",
5
5
  "license": "ISC",
6
6
  "repository": {