@websolutespa/bom-core 0.1.1 → 0.2.0

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/CHANGELOG.md CHANGED
@@ -1,20 +1,32 @@
1
- # @websolutespa/bom-core
2
-
3
- ## 0.1.1
4
-
5
- ### Patch Changes
6
-
7
- - Modified: ILocalizable.
8
-
9
- ## 0.1.0
10
-
11
- ### Minor Changes
12
-
13
- - Added: splat routes.
14
- - Added: variants.
15
-
16
- ## 0.0.1
17
-
18
- ### Patch Changes
19
-
20
- - Added: first release.
1
+ # @websolutespa/bom-core
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Modified routes, added redirects.
8
+
9
+ ## 0.1.2
10
+
11
+ ### Patch Changes
12
+
13
+ - Modified: MockMenuService.
14
+
15
+ ## 0.1.1
16
+
17
+ ### Patch Changes
18
+
19
+ - Modified: ILocalizable.
20
+
21
+ ## 0.1.0
22
+
23
+ ### Minor Changes
24
+
25
+ - Added: splat routes.
26
+ - Added: variants.
27
+
28
+ ## 0.0.1
29
+
30
+ ### Patch Changes
31
+
32
+ - Added: first release.
package/dist/index.d.ts CHANGED
@@ -95,13 +95,14 @@ type ICategory = IEntity & {
95
95
  id: IEquatable;
96
96
  media?: IMedia;
97
97
  order?: number;
98
- slug?: ILocalizable;
99
- title?: ILocalizable;
98
+ slug: ILocalizable;
99
+ title: ILocalizable;
100
100
  };
101
101
  type ICategoryItem = ICategory & {
102
102
  items: ICategory[];
103
103
  };
104
104
  type ICategorized = {
105
+ abstract?: ILocalizable;
105
106
  category: IEquatable | ICategory;
106
107
  id: IEquatable;
107
108
  isDefault?: boolean;
@@ -109,10 +110,12 @@ type ICategorized = {
109
110
  media?: IMedia;
110
111
  order?: number;
111
112
  schema?: string;
112
- slug?: ILocalizable;
113
+ slug: ILocalizable;
113
114
  template?: string;
114
- title?: ILocalizable;
115
+ title: ILocalizable;
115
116
  useSplat?: boolean;
117
+ createdAt?: Date;
118
+ updatedAt?: Date;
116
119
  };
117
120
 
118
121
  type IClassNameValue = string | boolean | number | null | undefined;
@@ -147,16 +150,18 @@ type ILocale = IEntity & {
147
150
  title?: string;
148
151
  isActive?: boolean;
149
152
  isDefault?: boolean;
153
+ isHidden?: boolean;
150
154
  };
151
155
 
152
156
  type IMarket = IEntity & {
153
157
  id: string;
154
158
  title?: string;
155
- isActive?: boolean;
156
- isDefault?: boolean;
157
159
  countries?: string[];
158
160
  languages?: string[];
159
161
  defaultLanguage?: string;
162
+ isActive?: boolean;
163
+ isDefault?: boolean;
164
+ isHidden?: boolean;
160
165
  };
161
166
 
162
167
  type IMeta = {
@@ -177,7 +182,12 @@ type IRoute = {
177
182
  schema: string;
178
183
  template?: string;
179
184
  title: string;
185
+ media?: IMedia;
180
186
  useSplat?: boolean;
187
+ updatedAt?: Date;
188
+ };
189
+ type IRouteCanonical = IRoute & {
190
+ alternates: IRoute[];
181
191
  };
182
192
  type IRouteLink = {
183
193
  href: string;
@@ -193,18 +203,18 @@ type IRouteParams = Record<string, any> & {
193
203
  };
194
204
 
195
205
  type IPage = Record<string, any> & ICategorized & {
196
- slug?: string;
197
- href: string;
206
+ abstract?: string;
198
207
  alternates: IRoute[];
199
208
  breadcrumb: IRouteLink[];
200
- parentRoute?: IRouteLink;
201
- title?: string;
202
- abstract?: string;
203
- description?: string;
204
209
  content?: string;
205
- createdAt?: Date;
206
- updatedAt?: Date;
210
+ description?: string;
211
+ href: string;
212
+ locale: string;
213
+ market: string;
207
214
  meta?: IMeta;
215
+ parentRoute?: IRouteLink;
216
+ slug: string;
217
+ title: string;
208
218
  };
209
219
  type IPageRelation = {
210
220
  relationTo: string;
@@ -221,37 +231,34 @@ type PageProps<T extends ICategorized = ICategorized> = {
221
231
  };
222
232
 
223
233
  type IMenuBase = {
234
+ abstract?: ILocalizable;
235
+ customClass?: string;
224
236
  id: IEquatable;
225
- abstract?: string;
226
- media?: IMedia;
227
237
  items?: IMenuItem[];
238
+ media?: IMedia;
239
+ title: ILocalizable;
228
240
  };
229
241
  type IMenuCategory = IMenuBase & {
230
242
  type: 'category';
231
- title: ILocalizable;
232
243
  href: string;
233
244
  category: IEquatable;
234
245
  };
235
246
  type IMenuGroup = IMenuBase & {
236
247
  type: 'group';
237
- title: ILocalizable;
238
248
  };
239
249
  type IMenuLink = IMenuBase & {
240
250
  type: 'link';
241
- title: ILocalizable;
242
251
  href: string;
243
252
  target: string;
244
253
  };
245
254
  type IMenuRoute = IMenuBase & {
246
255
  type: 'route';
247
- title: ILocalizable;
248
256
  href: string;
249
257
  page: IEquatable;
250
258
  schema: string;
251
259
  };
252
260
  type IMenuPage = IMenuBase & {
253
261
  type: 'page';
254
- title: ILocalizable;
255
262
  href: string;
256
263
  page: IEquatable;
257
264
  schema: string;
@@ -270,6 +277,7 @@ type IMenu = {
270
277
  */
271
278
  type IUndecoratedMenuBase = {
272
279
  abstract?: ILocalizable;
280
+ customClass?: string;
273
281
  id?: string;
274
282
  items: IUndecoratedMenuItem[];
275
283
  markets?: string[];
@@ -383,7 +391,24 @@ type FindWhereParams = {
383
391
  };
384
392
  declare function toFindParams(idOrParams: IEquatable | FindWhereParams): FindWhereParams;
385
393
 
386
- type IMemoryStore = Record<string, IEntity[]>;
394
+ declare enum RedirectReason {
395
+ MovedPermanently = "301",
396
+ Found = "302",
397
+ TemporaryRedirect = "307",
398
+ PermanentRedirect = "308",
399
+ Gone = "410"
400
+ }
401
+ type IRedirect = {
402
+ id: IEquatable;
403
+ status: RedirectReason;
404
+ from: string;
405
+ to?: string;
406
+ name?: string;
407
+ order?: number;
408
+ isActive: boolean;
409
+ };
410
+
411
+ type IMemoryStore<T extends IEntity = IEntity> = Record<string, T[]>;
387
412
  type IStore = Record<string, IQuerable<IEntity>>;
388
413
  type IConfig = {
389
414
  collections: string[];
@@ -417,6 +442,12 @@ type ITemplate = {
417
442
  collections: string[];
418
443
  };
419
444
 
445
+ declare function isCategory(value?: ICategory | IEquatable): value is ICategory;
446
+ declare function asCategoryId(value?: ICategory | IEquatable): string | undefined;
447
+ declare function getParentCategoriesById(id: string | undefined, categories: ICategory[]): ICategory[];
448
+ declare function getCategorySegments(item: ICategorized, categories: ICategory[]): ICategory[];
449
+ declare function getCategorySegmentsByCategory(item: ICategory, categories: ICategory[]): ICategory[];
450
+
420
451
  declare function getClassNames(...props: Array<IClassName>): string;
421
452
 
422
453
  declare function getCollectionDescription(text: string): ICollectionDescription;
@@ -466,6 +497,7 @@ declare class JsonService<T extends IEntity = IEntity> implements IQuerable<T> {
466
497
 
467
498
  declare function isLocalizedString(value: any): value is ILocalizedString;
468
499
  declare function localizedToString(json: ILocalizedString, locale?: string, defaultLocale?: string): string;
500
+ declare function localize(value: ILocalizable, locale?: string, defaultLocale?: string): string;
469
501
  declare function localizeValue(value: any, locale?: string, defaultLocale?: string): any;
470
502
  declare function localizeItem(item: any, locale?: string, defaultLocale?: string): any;
471
503
 
@@ -475,10 +507,23 @@ declare function deserializeValue(value: string): string | boolean | number;
475
507
  declare function deserializeKey(value: string): string | number;
476
508
  declare function qsDeserialize(query?: string, deserializedParams?: {}): QueryParams;
477
509
 
478
- declare function titleToSlug(title: string): string;
510
+ declare function collectRoutes(store: IMemoryStore<ICategorized>, categories: ICategory[], markets: IMarket[], locales: ILocale[]): IRoute[];
511
+ declare function eachMarketLocale(markets: IMarket[], locales: ILocale[], callback: (market: IMarket, locale: ILocale, markets: IMarket[], locales: ILocale[]) => void): void;
512
+ declare function eachCategorized(store: IMemoryStore<ICategorized>, callback: (item: ICategorized, key: string) => void): void;
513
+ declare enum RouteStrategy {
514
+ Auto = "auto",
515
+ Default = "default",
516
+ Manual = "manual"
517
+ }
518
+ declare function getRouteHref(segments: ICategory[], market: IMarket, locale: ILocale, markets: IMarket[], locales: ILocale[]): string;
519
+
520
+ declare function textToSlug(title: string): string;
479
521
 
480
522
  declare function getIsDevelopment(): boolean;
523
+ declare function getIsProduction(): boolean;
481
524
  declare const isDevelopment: boolean;
525
+ declare const isProduction: boolean;
526
+ declare const isStage: boolean;
482
527
  declare const isBrowser: boolean;
483
528
  declare function withSchema<Component extends {}, Schema>(component: Component, schema: Schema): Component & Schema;
484
529
  declare function asServerProps(props: any): any;
@@ -486,4 +531,4 @@ declare function awaitAll(array: any, callback: (item: any, index: number) => Pr
486
531
  declare function merge(target: any, source: any): any;
487
532
  declare function deepCopy<T>(source: T): T;
488
533
 
489
- export { FetchRequestOptions, FetchService, FindParams, FindWhereParams, ICategorized, ICategory, ICategoryItem, IClassName, IClassNameCollection, IClassNameValue, ICollectionDescription, IConfig, IContextBaseParams, IContextParams, IEntity, IEquatable, ILabel, ILayout, ILocale, ILocalizable, ILocalized, ILocalizedString, IMarket, IMedia, IMediaType, IMemoryStore, IMenu, IMenuBase, IMenuCategory, IMenuCustom, IMenuGroup, IMenuItem, IMenuLink, IMenuPage, IMenuRoute, INamedEntity, IOption, IOverride, IPackages, IPage, IPageRelation, IPageResult, IPartial, IPrettify, IQuerable, IRoute, IRouteLink, IRouteParams, ISchema, ISerializedCollection, ISerializedStore, IServerSideContext, IServerSideSplatContext, IStaticContext, IStore, ITemplate, ITitledEntity, IUndecoratedMenu, IUndecoratedMenuBase, IUndecoratedMenuCategory, IUndecoratedMenuCustom, IUndecoratedMenuGroup, IUndecoratedMenuItem, IUndecoratedMenuLink, IUndecoratedMenuPage, Json, JsonService, LiteralUnion, NextApiRequestCookies, Operator, PageProps, Pagination, PreviewData, QueryParams, SchemaType, ServerSidePropsContext, StateValue, StaticPropsContext, ValueOf, Where, WhereAndOr, WhereBase, WhereField, asEquatable, asServerProps, awaitAll, camelToSnakeCase, consoleWarn, deepCopy, deserializeKey, deserializeValue, getClassNames, getCollectionDescription, getIsDevelopment, httpDelete, httpFetch, httpGet, httpPatch, httpPost, httpPut, isBrowser, isDevelopment, isLocalizedString, isValidCollectionDescription, localizeItem, localizeValue, localizedToString, merge, qsDeserialize, qsSerialize, titleToSlug, toDisplayName, toFindParams, toPluralName, toTitleCase, toValidCollectionName, withSchema };
534
+ export { FetchRequestOptions, FetchService, FindParams, FindWhereParams, ICategorized, ICategory, ICategoryItem, IClassName, IClassNameCollection, IClassNameValue, ICollectionDescription, IConfig, IContextBaseParams, IContextParams, IEntity, IEquatable, ILabel, ILayout, ILocale, ILocalizable, ILocalized, ILocalizedString, IMarket, IMedia, IMediaType, IMemoryStore, IMenu, IMenuBase, IMenuCategory, IMenuCustom, IMenuGroup, IMenuItem, IMenuLink, IMenuPage, IMenuRoute, INamedEntity, IOption, IOverride, IPackages, IPage, IPageRelation, IPageResult, IPartial, IPrettify, IQuerable, IRedirect, IRoute, IRouteCanonical, IRouteLink, IRouteParams, ISchema, ISerializedCollection, ISerializedStore, IServerSideContext, IServerSideSplatContext, IStaticContext, IStore, ITemplate, ITitledEntity, IUndecoratedMenu, IUndecoratedMenuBase, IUndecoratedMenuCategory, IUndecoratedMenuCustom, IUndecoratedMenuGroup, IUndecoratedMenuItem, IUndecoratedMenuLink, IUndecoratedMenuPage, Json, JsonService, LiteralUnion, NextApiRequestCookies, Operator, PageProps, Pagination, PreviewData, QueryParams, RedirectReason, RouteStrategy, SchemaType, ServerSidePropsContext, StateValue, StaticPropsContext, ValueOf, Where, WhereAndOr, WhereBase, WhereField, asCategoryId, asEquatable, asServerProps, awaitAll, camelToSnakeCase, collectRoutes, consoleWarn, deepCopy, deserializeKey, deserializeValue, eachCategorized, eachMarketLocale, getCategorySegments, getCategorySegmentsByCategory, getClassNames, getCollectionDescription, getIsDevelopment, getIsProduction, getParentCategoriesById, getRouteHref, httpDelete, httpFetch, httpGet, httpPatch, httpPost, httpPut, isBrowser, isCategory, isDevelopment, isLocalizedString, isProduction, isStage, isValidCollectionDescription, localize, localizeItem, localizeValue, localizedToString, merge, qsDeserialize, qsSerialize, textToSlug, toDisplayName, toFindParams, toPluralName, toTitleCase, toValidCollectionName, withSchema };
package/dist/index.js CHANGED
@@ -31,17 +31,28 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var src_exports = {};
32
32
  __export(src_exports, {
33
33
  JsonService: () => JsonService,
34
+ RedirectReason: () => RedirectReason,
35
+ RouteStrategy: () => RouteStrategy,
36
+ asCategoryId: () => asCategoryId,
34
37
  asEquatable: () => asEquatable,
35
38
  asServerProps: () => asServerProps,
36
39
  awaitAll: () => awaitAll,
37
40
  camelToSnakeCase: () => camelToSnakeCase,
41
+ collectRoutes: () => collectRoutes,
38
42
  consoleWarn: () => consoleWarn,
39
43
  deepCopy: () => deepCopy,
40
44
  deserializeKey: () => deserializeKey,
41
45
  deserializeValue: () => deserializeValue,
46
+ eachCategorized: () => eachCategorized,
47
+ eachMarketLocale: () => eachMarketLocale,
48
+ getCategorySegments: () => getCategorySegments,
49
+ getCategorySegmentsByCategory: () => getCategorySegmentsByCategory,
42
50
  getClassNames: () => getClassNames,
43
51
  getCollectionDescription: () => getCollectionDescription,
44
52
  getIsDevelopment: () => getIsDevelopment,
53
+ getIsProduction: () => getIsProduction,
54
+ getParentCategoriesById: () => getParentCategoriesById,
55
+ getRouteHref: () => getRouteHref,
45
56
  httpDelete: () => httpDelete,
46
57
  httpFetch: () => httpFetch,
47
58
  httpGet: () => httpGet,
@@ -49,16 +60,20 @@ __export(src_exports, {
49
60
  httpPost: () => httpPost,
50
61
  httpPut: () => httpPut,
51
62
  isBrowser: () => isBrowser,
63
+ isCategory: () => isCategory,
52
64
  isDevelopment: () => isDevelopment,
53
65
  isLocalizedString: () => isLocalizedString,
66
+ isProduction: () => isProduction,
67
+ isStage: () => isStage,
54
68
  isValidCollectionDescription: () => isValidCollectionDescription,
69
+ localize: () => localize,
55
70
  localizeItem: () => localizeItem,
56
71
  localizeValue: () => localizeValue,
57
72
  localizedToString: () => localizedToString,
58
73
  merge: () => merge,
59
74
  qsDeserialize: () => qsDeserialize,
60
75
  qsSerialize: () => qsSerialize,
61
- titleToSlug: () => titleToSlug,
76
+ textToSlug: () => textToSlug,
62
77
  toDisplayName: () => toDisplayName,
63
78
  toFindParams: () => toFindParams,
64
79
  toPluralName: () => toPluralName,
@@ -68,6 +83,65 @@ __export(src_exports, {
68
83
  });
69
84
  module.exports = __toCommonJS(src_exports);
70
85
 
86
+ // src/category/category.ts
87
+ function isCategory(value) {
88
+ return Boolean(value) && typeof value === "object";
89
+ }
90
+ function asCategoryId(value) {
91
+ return isCategory(value) ? value.id : value;
92
+ }
93
+ function getParentCategoriesById(id, categories) {
94
+ const routeCategories = [];
95
+ const segments = [];
96
+ let parentId = id || null;
97
+ while (parentId != null) {
98
+ const parentCategory = categories.find((c) => c.id === parentId);
99
+ if (parentCategory) {
100
+ if (parentCategory.slug) {
101
+ const segment = { ...parentCategory };
102
+ segments.unshift(segment);
103
+ }
104
+ parentId = asCategoryId(parentCategory.category) || null;
105
+ if (parentId) {
106
+ if (routeCategories.includes(parentId)) {
107
+ console.log("ICategory.getParentCategoriesById", "circular reference found", parentId);
108
+ parentId = null;
109
+ } else {
110
+ routeCategories.push(parentId);
111
+ }
112
+ }
113
+ } else {
114
+ parentId = null;
115
+ }
116
+ }
117
+ return segments;
118
+ }
119
+ function getCategorySegments(item, categories) {
120
+ const categoryId = asCategoryId(item.category);
121
+ const segments = getParentCategoriesById(categoryId, categories);
122
+ [];
123
+ if (item.isDefault !== true) {
124
+ const category = categories.find((x) => x.id === item.category);
125
+ if (category) {
126
+ const segment = {
127
+ id: item.id,
128
+ title: item.title,
129
+ slug: item.slug,
130
+ media: item.media || category.media,
131
+ category
132
+ };
133
+ segments.push(segment);
134
+ }
135
+ }
136
+ return segments;
137
+ }
138
+ function getCategorySegmentsByCategory(item, categories) {
139
+ const categoryId = asCategoryId(item.category);
140
+ const segments = getParentCategoriesById(categoryId, categories);
141
+ segments.push(item);
142
+ return segments;
143
+ }
144
+
71
145
  // src/classNames/classNames.ts
72
146
  function isClassNameCollection(value) {
73
147
  return typeof value === "object" && !Array.isArray(value);
@@ -169,7 +243,12 @@ function consoleWarn(message, component) {
169
243
  function getIsDevelopment() {
170
244
  return process && (!process.env.NODE_ENV || process.env.NODE_ENV === "development");
171
245
  }
246
+ function getIsProduction() {
247
+ return process && process.env && String(process.env.PRODUCTION) === "true";
248
+ }
172
249
  var isDevelopment = getIsDevelopment();
250
+ var isProduction = getIsProduction();
251
+ var isStage = !isProduction && !isDevelopment;
173
252
  var isBrowser = typeof window !== "undefined";
174
253
  function withSchema(component, schema) {
175
254
  return Object.assign(component, schema);
@@ -445,14 +524,14 @@ var JsonService = class {
445
524
  const key = sort.replace(/^-/, "");
446
525
  items = [...items];
447
526
  items.sort((a, b) => {
448
- const aValue = String(a[key]);
449
- const bValue = String(b[key]);
450
- if (aValue.localeCompare(bValue) < 0) {
451
- return -1 * reverse;
452
- } else if (aValue.localeCompare(bValue) > 0) {
453
- return 1 * reverse;
527
+ const aValue = a[key];
528
+ const bValue = b[key];
529
+ if (typeof aValue === "number" && typeof bValue === "number") {
530
+ return (aValue - bValue) * reverse;
454
531
  }
455
- return 0;
532
+ const aStringValue = String(aValue);
533
+ const bStringValue = String(bValue);
534
+ return aStringValue.localeCompare(bStringValue) * reverse;
456
535
  });
457
536
  }
458
537
  return items;
@@ -501,6 +580,9 @@ function localizedToString(json, locale = "en", defaultLocale = "en") {
501
580
  const localizedString = json[locale] || json[defaultLocale] || Object.values(json)[0];
502
581
  return localizedString;
503
582
  }
583
+ function localize(value, locale = "en", defaultLocale = "en") {
584
+ return isLocalizedString(value) ? localizedToString(value, locale, defaultLocale) : value;
585
+ }
504
586
  function localizeValue(value, locale = "en", defaultLocale = "en") {
505
587
  if (value) {
506
588
  if (isLocalizedString(value)) {
@@ -622,8 +704,124 @@ function qsDeserialize(query = "", deserializedParams = {}) {
622
704
  return deserializedParams;
623
705
  }
624
706
 
707
+ // src/route/route.ts
708
+ function collectRoutes(store, categories, markets, locales) {
709
+ const routes = [];
710
+ if (locales.length > 0 && markets.length > 0) {
711
+ const firstTopCategory = categories.find((x) => x.category === void 0);
712
+ eachCategorized(store, (item, key) => {
713
+ const segments = getCategorySegments(item, categories);
714
+ eachMarketLocale(markets, locales, (market, locale, markets2, locales2) => {
715
+ if (!item.markets || item.markets.includes(market.id)) {
716
+ const title = localize(item.title, locale.id);
717
+ const id = getRouteHref(segments, market, locale, markets2, locales2);
718
+ const route = {
719
+ id,
720
+ market: market.id,
721
+ locale: locale.id,
722
+ category: item.category,
723
+ page: item.id,
724
+ schema: key,
725
+ template: item.template,
726
+ title,
727
+ isDefault: item.isDefault,
728
+ order: item.order,
729
+ useSplat: item.useSplat,
730
+ updatedAt: item.updatedAt
731
+ };
732
+ routes.push(route);
733
+ }
734
+ });
735
+ if (firstTopCategory && item.category === firstTopCategory.id) {
736
+ const defaultLocale = locales.find((x) => x.isDefault) || locales[0];
737
+ const defaultMarket = markets.find((x) => x.isDefault) || markets[0];
738
+ const defaultMarketLocale = defaultMarket && defaultMarket.defaultLanguage ? defaultMarket.defaultLanguage : defaultLocale.id;
739
+ const title = localize(item.title, defaultMarketLocale);
740
+ const route = {
741
+ id: "/",
742
+ market: defaultMarket.id,
743
+ locale: defaultMarketLocale,
744
+ category: item.category,
745
+ page: item.id,
746
+ schema: key,
747
+ template: item.template,
748
+ title,
749
+ isDefault: item.isDefault,
750
+ order: item.order,
751
+ useSplat: item.useSplat,
752
+ updatedAt: item.updatedAt
753
+ };
754
+ routes.push(route);
755
+ }
756
+ });
757
+ }
758
+ return routes;
759
+ }
760
+ function eachMarketLocale(markets, locales, callback) {
761
+ locales = locales.filter((x) => x.isActive !== false);
762
+ markets = markets.filter((x) => x.isActive !== false);
763
+ markets.forEach((market) => {
764
+ const marketLocales = market.languages && market.languages.length > 0 ? market.languages.map((id) => locales.find((x) => x.id === id)).filter((x) => x !== void 0) : locales;
765
+ marketLocales.forEach((locale) => {
766
+ callback(market, locale, markets, locales);
767
+ });
768
+ });
769
+ }
770
+ function eachCategorized(store, callback) {
771
+ const keys = Object.keys(store);
772
+ for (const key of keys) {
773
+ const items = store[key];
774
+ for (const item of items) {
775
+ callback(item, key);
776
+ }
777
+ }
778
+ }
779
+ var RouteStrategy = /* @__PURE__ */ ((RouteStrategy2) => {
780
+ RouteStrategy2["Auto"] = "auto";
781
+ RouteStrategy2["Default"] = "default";
782
+ RouteStrategy2["Manual"] = "manual";
783
+ return RouteStrategy2;
784
+ })(RouteStrategy || {});
785
+ var ROUTE_STRATEGY = process.env.ROUTE_STRATEGY || "auto" /* Auto */;
786
+ function getRouteHref(segments, market, locale, markets, locales) {
787
+ const href = segments.reduce((p, c, i) => {
788
+ let slug = localize(c.slug || "", locale.id);
789
+ slug = `${p}/${slug}`;
790
+ return slug === "/" ? "" : slug;
791
+ }, "");
792
+ const prefixes = [];
793
+ switch (ROUTE_STRATEGY) {
794
+ case "auto" /* Auto */:
795
+ if (locales.length > 1 || markets.length > 1) {
796
+ prefixes.push(locale.id);
797
+ }
798
+ if (markets.length > 1) {
799
+ prefixes.push(market.id);
800
+ }
801
+ break;
802
+ case "default" /* Default */:
803
+ if (locale.isDefault !== true) {
804
+ prefixes.push(locale.id);
805
+ }
806
+ if (market.isDefault !== true) {
807
+ prefixes.push(market.id);
808
+ }
809
+ break;
810
+ case "manual" /* Manual */:
811
+ if (locale.isHidden !== true) {
812
+ prefixes.push(locale.id);
813
+ }
814
+ if (market.isHidden !== true) {
815
+ prefixes.push(market.id);
816
+ }
817
+ break;
818
+ }
819
+ const prefix = prefixes.length ? `/${prefixes.join("-")}` : "";
820
+ return `${prefix}${href}`;
821
+ }
822
+
625
823
  // src/slug/slug.ts
626
- function titleToSlug(title) {
824
+ function textToSlug(title) {
627
825
  return title.toString().normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/\s+/g, "-").replace(/&/g, "-and-").replace(/[^\w-]+/g, "").replace(/--+/g, "-").replace(/^-+/, "").replace(/-+$/, "");
628
826
  }
629
827
 
@@ -635,20 +833,41 @@ function toFindParams(idOrParams) {
635
833
  return idOrParams;
636
834
  }
637
835
  }
836
+
837
+ // src/types/redirect.ts
838
+ var RedirectReason = /* @__PURE__ */ ((RedirectReason2) => {
839
+ RedirectReason2["MovedPermanently"] = "301";
840
+ RedirectReason2["Found"] = "302";
841
+ RedirectReason2["TemporaryRedirect"] = "307";
842
+ RedirectReason2["PermanentRedirect"] = "308";
843
+ RedirectReason2["Gone"] = "410";
844
+ return RedirectReason2;
845
+ })(RedirectReason || {});
638
846
  // Annotate the CommonJS export names for ESM import in node:
639
847
  0 && (module.exports = {
640
848
  JsonService,
849
+ RedirectReason,
850
+ RouteStrategy,
851
+ asCategoryId,
641
852
  asEquatable,
642
853
  asServerProps,
643
854
  awaitAll,
644
855
  camelToSnakeCase,
856
+ collectRoutes,
645
857
  consoleWarn,
646
858
  deepCopy,
647
859
  deserializeKey,
648
860
  deserializeValue,
861
+ eachCategorized,
862
+ eachMarketLocale,
863
+ getCategorySegments,
864
+ getCategorySegmentsByCategory,
649
865
  getClassNames,
650
866
  getCollectionDescription,
651
867
  getIsDevelopment,
868
+ getIsProduction,
869
+ getParentCategoriesById,
870
+ getRouteHref,
652
871
  httpDelete,
653
872
  httpFetch,
654
873
  httpGet,
@@ -656,16 +875,20 @@ function toFindParams(idOrParams) {
656
875
  httpPost,
657
876
  httpPut,
658
877
  isBrowser,
878
+ isCategory,
659
879
  isDevelopment,
660
880
  isLocalizedString,
881
+ isProduction,
882
+ isStage,
661
883
  isValidCollectionDescription,
884
+ localize,
662
885
  localizeItem,
663
886
  localizeValue,
664
887
  localizedToString,
665
888
  merge,
666
889
  qsDeserialize,
667
890
  qsSerialize,
668
- titleToSlug,
891
+ textToSlug,
669
892
  toDisplayName,
670
893
  toFindParams,
671
894
  toPluralName,
package/dist/index.mjs CHANGED
@@ -1,3 +1,62 @@
1
+ // src/category/category.ts
2
+ function isCategory(value) {
3
+ return Boolean(value) && typeof value === "object";
4
+ }
5
+ function asCategoryId(value) {
6
+ return isCategory(value) ? value.id : value;
7
+ }
8
+ function getParentCategoriesById(id, categories) {
9
+ const routeCategories = [];
10
+ const segments = [];
11
+ let parentId = id || null;
12
+ while (parentId != null) {
13
+ const parentCategory = categories.find((c) => c.id === parentId);
14
+ if (parentCategory) {
15
+ if (parentCategory.slug) {
16
+ const segment = { ...parentCategory };
17
+ segments.unshift(segment);
18
+ }
19
+ parentId = asCategoryId(parentCategory.category) || null;
20
+ if (parentId) {
21
+ if (routeCategories.includes(parentId)) {
22
+ console.log("ICategory.getParentCategoriesById", "circular reference found", parentId);
23
+ parentId = null;
24
+ } else {
25
+ routeCategories.push(parentId);
26
+ }
27
+ }
28
+ } else {
29
+ parentId = null;
30
+ }
31
+ }
32
+ return segments;
33
+ }
34
+ function getCategorySegments(item, categories) {
35
+ const categoryId = asCategoryId(item.category);
36
+ const segments = getParentCategoriesById(categoryId, categories);
37
+ [];
38
+ if (item.isDefault !== true) {
39
+ const category = categories.find((x) => x.id === item.category);
40
+ if (category) {
41
+ const segment = {
42
+ id: item.id,
43
+ title: item.title,
44
+ slug: item.slug,
45
+ media: item.media || category.media,
46
+ category
47
+ };
48
+ segments.push(segment);
49
+ }
50
+ }
51
+ return segments;
52
+ }
53
+ function getCategorySegmentsByCategory(item, categories) {
54
+ const categoryId = asCategoryId(item.category);
55
+ const segments = getParentCategoriesById(categoryId, categories);
56
+ segments.push(item);
57
+ return segments;
58
+ }
59
+
1
60
  // src/classNames/classNames.ts
2
61
  function isClassNameCollection(value) {
3
62
  return typeof value === "object" && !Array.isArray(value);
@@ -99,7 +158,12 @@ function consoleWarn(message, component) {
99
158
  function getIsDevelopment() {
100
159
  return process && (!process.env.NODE_ENV || process.env.NODE_ENV === "development");
101
160
  }
161
+ function getIsProduction() {
162
+ return process && process.env && String(process.env.PRODUCTION) === "true";
163
+ }
102
164
  var isDevelopment = getIsDevelopment();
165
+ var isProduction = getIsProduction();
166
+ var isStage = !isProduction && !isDevelopment;
103
167
  var isBrowser = typeof window !== "undefined";
104
168
  function withSchema(component, schema) {
105
169
  return Object.assign(component, schema);
@@ -375,14 +439,14 @@ var JsonService = class {
375
439
  const key = sort.replace(/^-/, "");
376
440
  items = [...items];
377
441
  items.sort((a, b) => {
378
- const aValue = String(a[key]);
379
- const bValue = String(b[key]);
380
- if (aValue.localeCompare(bValue) < 0) {
381
- return -1 * reverse;
382
- } else if (aValue.localeCompare(bValue) > 0) {
383
- return 1 * reverse;
442
+ const aValue = a[key];
443
+ const bValue = b[key];
444
+ if (typeof aValue === "number" && typeof bValue === "number") {
445
+ return (aValue - bValue) * reverse;
384
446
  }
385
- return 0;
447
+ const aStringValue = String(aValue);
448
+ const bStringValue = String(bValue);
449
+ return aStringValue.localeCompare(bStringValue) * reverse;
386
450
  });
387
451
  }
388
452
  return items;
@@ -431,6 +495,9 @@ function localizedToString(json, locale = "en", defaultLocale = "en") {
431
495
  const localizedString = json[locale] || json[defaultLocale] || Object.values(json)[0];
432
496
  return localizedString;
433
497
  }
498
+ function localize(value, locale = "en", defaultLocale = "en") {
499
+ return isLocalizedString(value) ? localizedToString(value, locale, defaultLocale) : value;
500
+ }
434
501
  function localizeValue(value, locale = "en", defaultLocale = "en") {
435
502
  if (value) {
436
503
  if (isLocalizedString(value)) {
@@ -552,8 +619,124 @@ function qsDeserialize(query = "", deserializedParams = {}) {
552
619
  return deserializedParams;
553
620
  }
554
621
 
622
+ // src/route/route.ts
623
+ function collectRoutes(store, categories, markets, locales) {
624
+ const routes = [];
625
+ if (locales.length > 0 && markets.length > 0) {
626
+ const firstTopCategory = categories.find((x) => x.category === void 0);
627
+ eachCategorized(store, (item, key) => {
628
+ const segments = getCategorySegments(item, categories);
629
+ eachMarketLocale(markets, locales, (market, locale, markets2, locales2) => {
630
+ if (!item.markets || item.markets.includes(market.id)) {
631
+ const title = localize(item.title, locale.id);
632
+ const id = getRouteHref(segments, market, locale, markets2, locales2);
633
+ const route = {
634
+ id,
635
+ market: market.id,
636
+ locale: locale.id,
637
+ category: item.category,
638
+ page: item.id,
639
+ schema: key,
640
+ template: item.template,
641
+ title,
642
+ isDefault: item.isDefault,
643
+ order: item.order,
644
+ useSplat: item.useSplat,
645
+ updatedAt: item.updatedAt
646
+ };
647
+ routes.push(route);
648
+ }
649
+ });
650
+ if (firstTopCategory && item.category === firstTopCategory.id) {
651
+ const defaultLocale = locales.find((x) => x.isDefault) || locales[0];
652
+ const defaultMarket = markets.find((x) => x.isDefault) || markets[0];
653
+ const defaultMarketLocale = defaultMarket && defaultMarket.defaultLanguage ? defaultMarket.defaultLanguage : defaultLocale.id;
654
+ const title = localize(item.title, defaultMarketLocale);
655
+ const route = {
656
+ id: "/",
657
+ market: defaultMarket.id,
658
+ locale: defaultMarketLocale,
659
+ category: item.category,
660
+ page: item.id,
661
+ schema: key,
662
+ template: item.template,
663
+ title,
664
+ isDefault: item.isDefault,
665
+ order: item.order,
666
+ useSplat: item.useSplat,
667
+ updatedAt: item.updatedAt
668
+ };
669
+ routes.push(route);
670
+ }
671
+ });
672
+ }
673
+ return routes;
674
+ }
675
+ function eachMarketLocale(markets, locales, callback) {
676
+ locales = locales.filter((x) => x.isActive !== false);
677
+ markets = markets.filter((x) => x.isActive !== false);
678
+ markets.forEach((market) => {
679
+ const marketLocales = market.languages && market.languages.length > 0 ? market.languages.map((id) => locales.find((x) => x.id === id)).filter((x) => x !== void 0) : locales;
680
+ marketLocales.forEach((locale) => {
681
+ callback(market, locale, markets, locales);
682
+ });
683
+ });
684
+ }
685
+ function eachCategorized(store, callback) {
686
+ const keys = Object.keys(store);
687
+ for (const key of keys) {
688
+ const items = store[key];
689
+ for (const item of items) {
690
+ callback(item, key);
691
+ }
692
+ }
693
+ }
694
+ var RouteStrategy = /* @__PURE__ */ ((RouteStrategy2) => {
695
+ RouteStrategy2["Auto"] = "auto";
696
+ RouteStrategy2["Default"] = "default";
697
+ RouteStrategy2["Manual"] = "manual";
698
+ return RouteStrategy2;
699
+ })(RouteStrategy || {});
700
+ var ROUTE_STRATEGY = process.env.ROUTE_STRATEGY || "auto" /* Auto */;
701
+ function getRouteHref(segments, market, locale, markets, locales) {
702
+ const href = segments.reduce((p, c, i) => {
703
+ let slug = localize(c.slug || "", locale.id);
704
+ slug = `${p}/${slug}`;
705
+ return slug === "/" ? "" : slug;
706
+ }, "");
707
+ const prefixes = [];
708
+ switch (ROUTE_STRATEGY) {
709
+ case "auto" /* Auto */:
710
+ if (locales.length > 1 || markets.length > 1) {
711
+ prefixes.push(locale.id);
712
+ }
713
+ if (markets.length > 1) {
714
+ prefixes.push(market.id);
715
+ }
716
+ break;
717
+ case "default" /* Default */:
718
+ if (locale.isDefault !== true) {
719
+ prefixes.push(locale.id);
720
+ }
721
+ if (market.isDefault !== true) {
722
+ prefixes.push(market.id);
723
+ }
724
+ break;
725
+ case "manual" /* Manual */:
726
+ if (locale.isHidden !== true) {
727
+ prefixes.push(locale.id);
728
+ }
729
+ if (market.isHidden !== true) {
730
+ prefixes.push(market.id);
731
+ }
732
+ break;
733
+ }
734
+ const prefix = prefixes.length ? `/${prefixes.join("-")}` : "";
735
+ return `${prefix}${href}`;
736
+ }
737
+
555
738
  // src/slug/slug.ts
556
- function titleToSlug(title) {
739
+ function textToSlug(title) {
557
740
  return title.toString().normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/\s+/g, "-").replace(/&/g, "-and-").replace(/[^\w-]+/g, "").replace(/--+/g, "-").replace(/^-+/, "").replace(/-+$/, "");
558
741
  }
559
742
 
@@ -565,19 +748,40 @@ function toFindParams(idOrParams) {
565
748
  return idOrParams;
566
749
  }
567
750
  }
751
+
752
+ // src/types/redirect.ts
753
+ var RedirectReason = /* @__PURE__ */ ((RedirectReason2) => {
754
+ RedirectReason2["MovedPermanently"] = "301";
755
+ RedirectReason2["Found"] = "302";
756
+ RedirectReason2["TemporaryRedirect"] = "307";
757
+ RedirectReason2["PermanentRedirect"] = "308";
758
+ RedirectReason2["Gone"] = "410";
759
+ return RedirectReason2;
760
+ })(RedirectReason || {});
568
761
  export {
569
762
  JsonService,
763
+ RedirectReason,
764
+ RouteStrategy,
765
+ asCategoryId,
570
766
  asEquatable,
571
767
  asServerProps,
572
768
  awaitAll,
573
769
  camelToSnakeCase,
770
+ collectRoutes,
574
771
  consoleWarn,
575
772
  deepCopy,
576
773
  deserializeKey,
577
774
  deserializeValue,
775
+ eachCategorized,
776
+ eachMarketLocale,
777
+ getCategorySegments,
778
+ getCategorySegmentsByCategory,
578
779
  getClassNames,
579
780
  getCollectionDescription,
580
781
  getIsDevelopment,
782
+ getIsProduction,
783
+ getParentCategoriesById,
784
+ getRouteHref,
581
785
  httpDelete,
582
786
  httpFetch,
583
787
  httpGet,
@@ -585,16 +789,20 @@ export {
585
789
  httpPost,
586
790
  httpPut,
587
791
  isBrowser,
792
+ isCategory,
588
793
  isDevelopment,
589
794
  isLocalizedString,
795
+ isProduction,
796
+ isStage,
590
797
  isValidCollectionDescription,
798
+ localize,
591
799
  localizeItem,
592
800
  localizeValue,
593
801
  localizedToString,
594
802
  merge,
595
803
  qsDeserialize,
596
804
  qsSerialize,
597
- titleToSlug,
805
+ textToSlug,
598
806
  toDisplayName,
599
807
  toFindParams,
600
808
  toPluralName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@websolutespa/bom-core",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "Core module of the BOM Repository",
5
5
  "keywords": [
6
6
  "bom",
@@ -22,9 +22,11 @@
22
22
  "prepack": "bom prepack",
23
23
  "postpack": "bom postpack"
24
24
  },
25
+ "dependencies": {
26
+ "pluralize": "latest"
27
+ },
25
28
  "peerDependencies": {
26
- "next": ">= 12.3.2",
27
- "pluralize": ">= 8.0.0"
29
+ "next": ">= 12.3.2"
28
30
  },
29
31
  "devDependencies": {
30
32
  "@websolutespa/bom-cli": "*",