@websolutespa/bom-core 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @websolutespa/bom-core
2
2
 
3
+ ## 0.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Added: env ROOT_CATEGORY.
8
+
9
+ ## 0.2.0
10
+
11
+ ### Minor Changes
12
+
13
+ - Modified routes, added redirects.
14
+
3
15
  ## 0.1.2
4
16
 
5
17
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -95,8 +95,8 @@ 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[];
@@ -110,10 +110,12 @@ type ICategorized = {
110
110
  media?: IMedia;
111
111
  order?: number;
112
112
  schema?: string;
113
- slug?: ILocalizable;
113
+ slug: ILocalizable;
114
114
  template?: string;
115
- title?: ILocalizable;
115
+ title: ILocalizable;
116
116
  useSplat?: boolean;
117
+ createdAt?: Date;
118
+ updatedAt?: Date;
117
119
  };
118
120
 
119
121
  type IClassNameValue = string | boolean | number | null | undefined;
@@ -148,16 +150,18 @@ type ILocale = IEntity & {
148
150
  title?: string;
149
151
  isActive?: boolean;
150
152
  isDefault?: boolean;
153
+ isHidden?: boolean;
151
154
  };
152
155
 
153
156
  type IMarket = IEntity & {
154
157
  id: string;
155
158
  title?: string;
156
- isActive?: boolean;
157
- isDefault?: boolean;
158
159
  countries?: string[];
159
160
  languages?: string[];
160
161
  defaultLanguage?: string;
162
+ isActive?: boolean;
163
+ isDefault?: boolean;
164
+ isHidden?: boolean;
161
165
  };
162
166
 
163
167
  type IMeta = {
@@ -178,7 +182,12 @@ type IRoute = {
178
182
  schema: string;
179
183
  template?: string;
180
184
  title: string;
185
+ media?: IMedia;
181
186
  useSplat?: boolean;
187
+ updatedAt?: Date;
188
+ };
189
+ type IRouteCanonical = IRoute & {
190
+ alternates: IRoute[];
182
191
  };
183
192
  type IRouteLink = {
184
193
  href: string;
@@ -194,18 +203,18 @@ type IRouteParams = Record<string, any> & {
194
203
  };
195
204
 
196
205
  type IPage = Record<string, any> & ICategorized & {
197
- slug?: string;
198
- href: string;
206
+ abstract?: string;
199
207
  alternates: IRoute[];
200
208
  breadcrumb: IRouteLink[];
201
- parentRoute?: IRouteLink;
202
- title?: string;
203
- abstract?: string;
204
- description?: string;
205
209
  content?: string;
206
- createdAt?: Date;
207
- updatedAt?: Date;
210
+ description?: string;
211
+ href: string;
212
+ locale: string;
213
+ market: string;
208
214
  meta?: IMeta;
215
+ parentRoute?: IRouteLink;
216
+ slug: string;
217
+ title: string;
209
218
  };
210
219
  type IPageRelation = {
211
220
  relationTo: string;
@@ -382,7 +391,24 @@ type FindWhereParams = {
382
391
  };
383
392
  declare function toFindParams(idOrParams: IEquatable | FindWhereParams): FindWhereParams;
384
393
 
385
- 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[]>;
386
412
  type IStore = Record<string, IQuerable<IEntity>>;
387
413
  type IConfig = {
388
414
  collections: string[];
@@ -416,6 +442,13 @@ type ITemplate = {
416
442
  collections: string[];
417
443
  };
418
444
 
445
+ declare function isCategory(value?: ICategory | IEquatable): value is ICategory;
446
+ declare function asCategoryId(value?: ICategory | IEquatable): string | undefined;
447
+ declare function getRootCategory(categories: ICategory[]): ICategory | undefined;
448
+ declare function getParentCategoriesById(id: string | undefined, categories: ICategory[]): ICategory[];
449
+ declare function getCategorySegments(item: ICategorized, categories: ICategory[]): ICategory[];
450
+ declare function getCategorySegmentsByCategory(item: ICategory, categories: ICategory[]): ICategory[];
451
+
419
452
  declare function getClassNames(...props: Array<IClassName>): string;
420
453
 
421
454
  declare function getCollectionDescription(text: string): ICollectionDescription;
@@ -465,6 +498,7 @@ declare class JsonService<T extends IEntity = IEntity> implements IQuerable<T> {
465
498
 
466
499
  declare function isLocalizedString(value: any): value is ILocalizedString;
467
500
  declare function localizedToString(json: ILocalizedString, locale?: string, defaultLocale?: string): string;
501
+ declare function localize(value: ILocalizable, locale?: string, defaultLocale?: string): string;
468
502
  declare function localizeValue(value: any, locale?: string, defaultLocale?: string): any;
469
503
  declare function localizeItem(item: any, locale?: string, defaultLocale?: string): any;
470
504
 
@@ -474,10 +508,36 @@ declare function deserializeValue(value: string): string | boolean | number;
474
508
  declare function deserializeKey(value: string): string | number;
475
509
  declare function qsDeserialize(query?: string, deserializedParams?: {}): QueryParams;
476
510
 
477
- declare function titleToSlug(title: string): string;
511
+ declare function collectRoutes(store: IMemoryStore<ICategorized>, categories: ICategory[], markets: IMarket[], locales: ILocale[]): IRoute[];
512
+ declare function eachMarketLocale(markets: IMarket[], locales: ILocale[], callback: (market: IMarket, locale: ILocale, markets: IMarket[], locales: ILocale[]) => void): void;
513
+ declare function eachCategorized(store: IMemoryStore<ICategorized>, callback: (item: ICategorized, key: string) => void): void;
514
+ declare enum RouteStrategy {
515
+ Auto = "auto",
516
+ Default = "default",
517
+ Manual = "manual"
518
+ }
519
+ declare function getRouteHref(segments: ICategory[], market: IMarket, locale: ILocale, markets: IMarket[], locales: ILocale[]): string;
520
+
521
+ declare function textToSlug(title: string): string;
522
+
523
+ type ListItem<T> = {
524
+ [K in keyof T]: {
525
+ readonly key: string;
526
+ readonly value: T[K];
527
+ };
528
+ }[keyof T];
529
+ /**
530
+ * Helper to produce an array of enum descriptors.
531
+ * @param enumeration Enumeration object.
532
+ * @param separatorRegex Regex that would catch the separator in your enum key.
533
+ */
534
+ declare function enumToList<T extends Record<string, number | string>>(enumeration: T, separatorRegex?: RegExp): ListItem<T>[];
478
535
 
479
536
  declare function getIsDevelopment(): boolean;
537
+ declare function getIsProduction(): boolean;
480
538
  declare const isDevelopment: boolean;
539
+ declare const isProduction: boolean;
540
+ declare const isStage: boolean;
481
541
  declare const isBrowser: boolean;
482
542
  declare function withSchema<Component extends {}, Schema>(component: Component, schema: Schema): Component & Schema;
483
543
  declare function asServerProps(props: any): any;
@@ -485,4 +545,4 @@ declare function awaitAll(array: any, callback: (item: any, index: number) => Pr
485
545
  declare function merge(target: any, source: any): any;
486
546
  declare function deepCopy<T>(source: T): T;
487
547
 
488
- 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 };
548
+ 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, enumToList, getCategorySegments, getCategorySegmentsByCategory, getClassNames, getCollectionDescription, getIsDevelopment, getIsProduction, getParentCategoriesById, getRootCategory, 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,30 @@ 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
+ enumToList: () => enumToList,
49
+ getCategorySegments: () => getCategorySegments,
50
+ getCategorySegmentsByCategory: () => getCategorySegmentsByCategory,
42
51
  getClassNames: () => getClassNames,
43
52
  getCollectionDescription: () => getCollectionDescription,
44
53
  getIsDevelopment: () => getIsDevelopment,
54
+ getIsProduction: () => getIsProduction,
55
+ getParentCategoriesById: () => getParentCategoriesById,
56
+ getRootCategory: () => getRootCategory,
57
+ getRouteHref: () => getRouteHref,
45
58
  httpDelete: () => httpDelete,
46
59
  httpFetch: () => httpFetch,
47
60
  httpGet: () => httpGet,
@@ -49,16 +62,20 @@ __export(src_exports, {
49
62
  httpPost: () => httpPost,
50
63
  httpPut: () => httpPut,
51
64
  isBrowser: () => isBrowser,
65
+ isCategory: () => isCategory,
52
66
  isDevelopment: () => isDevelopment,
53
67
  isLocalizedString: () => isLocalizedString,
68
+ isProduction: () => isProduction,
69
+ isStage: () => isStage,
54
70
  isValidCollectionDescription: () => isValidCollectionDescription,
71
+ localize: () => localize,
55
72
  localizeItem: () => localizeItem,
56
73
  localizeValue: () => localizeValue,
57
74
  localizedToString: () => localizedToString,
58
75
  merge: () => merge,
59
76
  qsDeserialize: () => qsDeserialize,
60
77
  qsSerialize: () => qsSerialize,
61
- titleToSlug: () => titleToSlug,
78
+ textToSlug: () => textToSlug,
62
79
  toDisplayName: () => toDisplayName,
63
80
  toFindParams: () => toFindParams,
64
81
  toPluralName: () => toPluralName,
@@ -68,6 +85,74 @@ __export(src_exports, {
68
85
  });
69
86
  module.exports = __toCommonJS(src_exports);
70
87
 
88
+ // src/category/category.ts
89
+ function isCategory(value) {
90
+ return Boolean(value) && typeof value === "object";
91
+ }
92
+ function asCategoryId(value) {
93
+ return isCategory(value) ? value.id : value;
94
+ }
95
+ var ROOT_CATEGORY = process.env.ROOT_CATEGORY || "";
96
+ function getRootCategory(categories) {
97
+ const rootCategory = ROOT_CATEGORY && categories.find((x) => x.id == ROOT_CATEGORY);
98
+ if (rootCategory) {
99
+ return rootCategory;
100
+ } else {
101
+ return categories.find((x) => x.category == null);
102
+ }
103
+ }
104
+ function getParentCategoriesById(id, categories) {
105
+ const routeCategories = [];
106
+ const segments = [];
107
+ let parentId = id || null;
108
+ while (parentId != null) {
109
+ const parentCategory = categories.find((c) => c.id === parentId);
110
+ if (parentCategory) {
111
+ if (parentCategory.slug) {
112
+ const segment = { ...parentCategory };
113
+ segments.unshift(segment);
114
+ }
115
+ parentId = asCategoryId(parentCategory.category) || null;
116
+ if (parentId) {
117
+ if (routeCategories.includes(parentId)) {
118
+ console.log("ICategory.getParentCategoriesById", "circular reference found", parentId);
119
+ parentId = null;
120
+ } else {
121
+ routeCategories.push(parentId);
122
+ }
123
+ }
124
+ } else {
125
+ parentId = null;
126
+ }
127
+ }
128
+ return segments;
129
+ }
130
+ function getCategorySegments(item, categories) {
131
+ const categoryId = asCategoryId(item.category);
132
+ const segments = getParentCategoriesById(categoryId, categories);
133
+ [];
134
+ if (item.isDefault !== true) {
135
+ const category = categories.find((x) => x.id === item.category);
136
+ if (category) {
137
+ const segment = {
138
+ id: item.id,
139
+ title: item.title,
140
+ slug: item.slug,
141
+ media: item.media || category.media,
142
+ category
143
+ };
144
+ segments.push(segment);
145
+ }
146
+ }
147
+ return segments;
148
+ }
149
+ function getCategorySegmentsByCategory(item, categories) {
150
+ const categoryId = asCategoryId(item.category);
151
+ const segments = getParentCategoriesById(categoryId, categories);
152
+ segments.push(item);
153
+ return segments;
154
+ }
155
+
71
156
  // src/classNames/classNames.ts
72
157
  function isClassNameCollection(value) {
73
158
  return typeof value === "object" && !Array.isArray(value);
@@ -165,11 +250,25 @@ function consoleWarn(message, component) {
165
250
  console.warn(log);
166
251
  }
167
252
 
253
+ // src/utils/enumToList.ts
254
+ function enumToList(enumeration, separatorRegex = /_/g) {
255
+ const array = Object.keys(enumeration);
256
+ return array.filter((key) => isNaN(Number(key))).filter((key) => typeof enumeration[key] === "number" || typeof enumeration[key] === "string").map((key) => ({
257
+ key: String(key).replace(separatorRegex, " "),
258
+ value: enumeration[key]
259
+ }));
260
+ }
261
+
168
262
  // src/utils/index.ts
169
263
  function getIsDevelopment() {
170
264
  return process && (!process.env.NODE_ENV || process.env.NODE_ENV === "development");
171
265
  }
266
+ function getIsProduction() {
267
+ return process && process.env && String(process.env.PRODUCTION) === "true";
268
+ }
172
269
  var isDevelopment = getIsDevelopment();
270
+ var isProduction = getIsProduction();
271
+ var isStage = !isProduction && !isDevelopment;
173
272
  var isBrowser = typeof window !== "undefined";
174
273
  function withSchema(component, schema) {
175
274
  return Object.assign(component, schema);
@@ -445,14 +544,14 @@ var JsonService = class {
445
544
  const key = sort.replace(/^-/, "");
446
545
  items = [...items];
447
546
  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;
547
+ const aValue = a[key];
548
+ const bValue = b[key];
549
+ if (typeof aValue === "number" && typeof bValue === "number") {
550
+ return (aValue - bValue) * reverse;
454
551
  }
455
- return 0;
552
+ const aStringValue = String(aValue);
553
+ const bStringValue = String(bValue);
554
+ return aStringValue.localeCompare(bStringValue) * reverse;
456
555
  });
457
556
  }
458
557
  return items;
@@ -501,6 +600,9 @@ function localizedToString(json, locale = "en", defaultLocale = "en") {
501
600
  const localizedString = json[locale] || json[defaultLocale] || Object.values(json)[0];
502
601
  return localizedString;
503
602
  }
603
+ function localize(value, locale = "en", defaultLocale = "en") {
604
+ return isLocalizedString(value) ? localizedToString(value, locale, defaultLocale) : value;
605
+ }
504
606
  function localizeValue(value, locale = "en", defaultLocale = "en") {
505
607
  if (value) {
506
608
  if (isLocalizedString(value)) {
@@ -622,8 +724,124 @@ function qsDeserialize(query = "", deserializedParams = {}) {
622
724
  return deserializedParams;
623
725
  }
624
726
 
727
+ // src/route/route.ts
728
+ function collectRoutes(store, categories, markets, locales) {
729
+ const routes = [];
730
+ if (locales.length > 0 && markets.length > 0) {
731
+ const rootCategory = getRootCategory(categories);
732
+ eachCategorized(store, (item, key) => {
733
+ const segments = getCategorySegments(item, categories);
734
+ eachMarketLocale(markets, locales, (market, locale, markets2, locales2) => {
735
+ if (!item.markets || item.markets.includes(market.id)) {
736
+ const title = localize(item.title, locale.id);
737
+ const id = getRouteHref(segments, market, locale, markets2, locales2);
738
+ const route = {
739
+ id,
740
+ market: market.id,
741
+ locale: locale.id,
742
+ category: item.category,
743
+ page: item.id,
744
+ schema: key,
745
+ template: item.template,
746
+ title,
747
+ isDefault: item.isDefault,
748
+ order: item.order,
749
+ useSplat: item.useSplat,
750
+ updatedAt: item.updatedAt
751
+ };
752
+ routes.push(route);
753
+ }
754
+ });
755
+ if (rootCategory && item.category === rootCategory.id) {
756
+ const defaultLocale = locales.find((x) => x.isDefault) || locales[0];
757
+ const defaultMarket = markets.find((x) => x.isDefault) || markets[0];
758
+ const defaultMarketLocale = defaultMarket && defaultMarket.defaultLanguage ? defaultMarket.defaultLanguage : defaultLocale.id;
759
+ const title = localize(item.title, defaultMarketLocale);
760
+ const route = {
761
+ id: "/",
762
+ market: defaultMarket.id,
763
+ locale: defaultMarketLocale,
764
+ category: item.category,
765
+ page: item.id,
766
+ schema: key,
767
+ template: item.template,
768
+ title,
769
+ isDefault: item.isDefault,
770
+ order: item.order,
771
+ useSplat: item.useSplat,
772
+ updatedAt: item.updatedAt
773
+ };
774
+ routes.push(route);
775
+ }
776
+ });
777
+ }
778
+ return routes;
779
+ }
780
+ function eachMarketLocale(markets, locales, callback) {
781
+ locales = locales.filter((x) => x.isActive !== false);
782
+ markets = markets.filter((x) => x.isActive !== false);
783
+ markets.forEach((market) => {
784
+ const marketLocales = market.languages && market.languages.length > 0 ? market.languages.map((id) => locales.find((x) => x.id === id)).filter((x) => x !== void 0) : locales;
785
+ marketLocales.forEach((locale) => {
786
+ callback(market, locale, markets, locales);
787
+ });
788
+ });
789
+ }
790
+ function eachCategorized(store, callback) {
791
+ const keys = Object.keys(store);
792
+ for (const key of keys) {
793
+ const items = store[key];
794
+ for (const item of items) {
795
+ callback(item, key);
796
+ }
797
+ }
798
+ }
799
+ var RouteStrategy = /* @__PURE__ */ ((RouteStrategy2) => {
800
+ RouteStrategy2["Auto"] = "auto";
801
+ RouteStrategy2["Default"] = "default";
802
+ RouteStrategy2["Manual"] = "manual";
803
+ return RouteStrategy2;
804
+ })(RouteStrategy || {});
805
+ var ROUTE_STRATEGY = process.env.ROUTE_STRATEGY || "auto" /* Auto */;
806
+ function getRouteHref(segments, market, locale, markets, locales) {
807
+ const href = segments.reduce((p, c, i) => {
808
+ let slug = localize(c.slug || "", locale.id);
809
+ slug = `${p}/${slug}`;
810
+ return slug === "/" ? "" : slug;
811
+ }, "");
812
+ const prefixes = [];
813
+ switch (ROUTE_STRATEGY) {
814
+ case "auto" /* Auto */:
815
+ if (locales.length > 1 || markets.length > 1) {
816
+ prefixes.push(locale.id);
817
+ }
818
+ if (markets.length > 1) {
819
+ prefixes.push(market.id);
820
+ }
821
+ break;
822
+ case "default" /* Default */:
823
+ if (locale.isDefault !== true) {
824
+ prefixes.push(locale.id);
825
+ }
826
+ if (market.isDefault !== true) {
827
+ prefixes.push(market.id);
828
+ }
829
+ break;
830
+ case "manual" /* Manual */:
831
+ if (locale.isHidden !== true) {
832
+ prefixes.push(locale.id);
833
+ }
834
+ if (market.isHidden !== true) {
835
+ prefixes.push(market.id);
836
+ }
837
+ break;
838
+ }
839
+ const prefix = prefixes.length ? `/${prefixes.join("-")}` : "";
840
+ return `${prefix}${href}`;
841
+ }
842
+
625
843
  // src/slug/slug.ts
626
- function titleToSlug(title) {
844
+ function textToSlug(title) {
627
845
  return title.toString().normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/\s+/g, "-").replace(/&/g, "-and-").replace(/[^\w-]+/g, "").replace(/--+/g, "-").replace(/^-+/, "").replace(/-+$/, "");
628
846
  }
629
847
 
@@ -635,20 +853,43 @@ function toFindParams(idOrParams) {
635
853
  return idOrParams;
636
854
  }
637
855
  }
856
+
857
+ // src/types/redirect.ts
858
+ var RedirectReason = /* @__PURE__ */ ((RedirectReason2) => {
859
+ RedirectReason2["MovedPermanently"] = "301";
860
+ RedirectReason2["Found"] = "302";
861
+ RedirectReason2["TemporaryRedirect"] = "307";
862
+ RedirectReason2["PermanentRedirect"] = "308";
863
+ RedirectReason2["Gone"] = "410";
864
+ return RedirectReason2;
865
+ })(RedirectReason || {});
638
866
  // Annotate the CommonJS export names for ESM import in node:
639
867
  0 && (module.exports = {
640
868
  JsonService,
869
+ RedirectReason,
870
+ RouteStrategy,
871
+ asCategoryId,
641
872
  asEquatable,
642
873
  asServerProps,
643
874
  awaitAll,
644
875
  camelToSnakeCase,
876
+ collectRoutes,
645
877
  consoleWarn,
646
878
  deepCopy,
647
879
  deserializeKey,
648
880
  deserializeValue,
881
+ eachCategorized,
882
+ eachMarketLocale,
883
+ enumToList,
884
+ getCategorySegments,
885
+ getCategorySegmentsByCategory,
649
886
  getClassNames,
650
887
  getCollectionDescription,
651
888
  getIsDevelopment,
889
+ getIsProduction,
890
+ getParentCategoriesById,
891
+ getRootCategory,
892
+ getRouteHref,
652
893
  httpDelete,
653
894
  httpFetch,
654
895
  httpGet,
@@ -656,16 +897,20 @@ function toFindParams(idOrParams) {
656
897
  httpPost,
657
898
  httpPut,
658
899
  isBrowser,
900
+ isCategory,
659
901
  isDevelopment,
660
902
  isLocalizedString,
903
+ isProduction,
904
+ isStage,
661
905
  isValidCollectionDescription,
906
+ localize,
662
907
  localizeItem,
663
908
  localizeValue,
664
909
  localizedToString,
665
910
  merge,
666
911
  qsDeserialize,
667
912
  qsSerialize,
668
- titleToSlug,
913
+ textToSlug,
669
914
  toDisplayName,
670
915
  toFindParams,
671
916
  toPluralName,
package/dist/index.mjs CHANGED
@@ -1,3 +1,71 @@
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
+ var ROOT_CATEGORY = process.env.ROOT_CATEGORY || "";
9
+ function getRootCategory(categories) {
10
+ const rootCategory = ROOT_CATEGORY && categories.find((x) => x.id == ROOT_CATEGORY);
11
+ if (rootCategory) {
12
+ return rootCategory;
13
+ } else {
14
+ return categories.find((x) => x.category == null);
15
+ }
16
+ }
17
+ function getParentCategoriesById(id, categories) {
18
+ const routeCategories = [];
19
+ const segments = [];
20
+ let parentId = id || null;
21
+ while (parentId != null) {
22
+ const parentCategory = categories.find((c) => c.id === parentId);
23
+ if (parentCategory) {
24
+ if (parentCategory.slug) {
25
+ const segment = { ...parentCategory };
26
+ segments.unshift(segment);
27
+ }
28
+ parentId = asCategoryId(parentCategory.category) || null;
29
+ if (parentId) {
30
+ if (routeCategories.includes(parentId)) {
31
+ console.log("ICategory.getParentCategoriesById", "circular reference found", parentId);
32
+ parentId = null;
33
+ } else {
34
+ routeCategories.push(parentId);
35
+ }
36
+ }
37
+ } else {
38
+ parentId = null;
39
+ }
40
+ }
41
+ return segments;
42
+ }
43
+ function getCategorySegments(item, categories) {
44
+ const categoryId = asCategoryId(item.category);
45
+ const segments = getParentCategoriesById(categoryId, categories);
46
+ [];
47
+ if (item.isDefault !== true) {
48
+ const category = categories.find((x) => x.id === item.category);
49
+ if (category) {
50
+ const segment = {
51
+ id: item.id,
52
+ title: item.title,
53
+ slug: item.slug,
54
+ media: item.media || category.media,
55
+ category
56
+ };
57
+ segments.push(segment);
58
+ }
59
+ }
60
+ return segments;
61
+ }
62
+ function getCategorySegmentsByCategory(item, categories) {
63
+ const categoryId = asCategoryId(item.category);
64
+ const segments = getParentCategoriesById(categoryId, categories);
65
+ segments.push(item);
66
+ return segments;
67
+ }
68
+
1
69
  // src/classNames/classNames.ts
2
70
  function isClassNameCollection(value) {
3
71
  return typeof value === "object" && !Array.isArray(value);
@@ -95,11 +163,25 @@ function consoleWarn(message, component) {
95
163
  console.warn(log);
96
164
  }
97
165
 
166
+ // src/utils/enumToList.ts
167
+ function enumToList(enumeration, separatorRegex = /_/g) {
168
+ const array = Object.keys(enumeration);
169
+ return array.filter((key) => isNaN(Number(key))).filter((key) => typeof enumeration[key] === "number" || typeof enumeration[key] === "string").map((key) => ({
170
+ key: String(key).replace(separatorRegex, " "),
171
+ value: enumeration[key]
172
+ }));
173
+ }
174
+
98
175
  // src/utils/index.ts
99
176
  function getIsDevelopment() {
100
177
  return process && (!process.env.NODE_ENV || process.env.NODE_ENV === "development");
101
178
  }
179
+ function getIsProduction() {
180
+ return process && process.env && String(process.env.PRODUCTION) === "true";
181
+ }
102
182
  var isDevelopment = getIsDevelopment();
183
+ var isProduction = getIsProduction();
184
+ var isStage = !isProduction && !isDevelopment;
103
185
  var isBrowser = typeof window !== "undefined";
104
186
  function withSchema(component, schema) {
105
187
  return Object.assign(component, schema);
@@ -375,14 +457,14 @@ var JsonService = class {
375
457
  const key = sort.replace(/^-/, "");
376
458
  items = [...items];
377
459
  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;
460
+ const aValue = a[key];
461
+ const bValue = b[key];
462
+ if (typeof aValue === "number" && typeof bValue === "number") {
463
+ return (aValue - bValue) * reverse;
384
464
  }
385
- return 0;
465
+ const aStringValue = String(aValue);
466
+ const bStringValue = String(bValue);
467
+ return aStringValue.localeCompare(bStringValue) * reverse;
386
468
  });
387
469
  }
388
470
  return items;
@@ -431,6 +513,9 @@ function localizedToString(json, locale = "en", defaultLocale = "en") {
431
513
  const localizedString = json[locale] || json[defaultLocale] || Object.values(json)[0];
432
514
  return localizedString;
433
515
  }
516
+ function localize(value, locale = "en", defaultLocale = "en") {
517
+ return isLocalizedString(value) ? localizedToString(value, locale, defaultLocale) : value;
518
+ }
434
519
  function localizeValue(value, locale = "en", defaultLocale = "en") {
435
520
  if (value) {
436
521
  if (isLocalizedString(value)) {
@@ -552,8 +637,124 @@ function qsDeserialize(query = "", deserializedParams = {}) {
552
637
  return deserializedParams;
553
638
  }
554
639
 
640
+ // src/route/route.ts
641
+ function collectRoutes(store, categories, markets, locales) {
642
+ const routes = [];
643
+ if (locales.length > 0 && markets.length > 0) {
644
+ const rootCategory = getRootCategory(categories);
645
+ eachCategorized(store, (item, key) => {
646
+ const segments = getCategorySegments(item, categories);
647
+ eachMarketLocale(markets, locales, (market, locale, markets2, locales2) => {
648
+ if (!item.markets || item.markets.includes(market.id)) {
649
+ const title = localize(item.title, locale.id);
650
+ const id = getRouteHref(segments, market, locale, markets2, locales2);
651
+ const route = {
652
+ id,
653
+ market: market.id,
654
+ locale: locale.id,
655
+ category: item.category,
656
+ page: item.id,
657
+ schema: key,
658
+ template: item.template,
659
+ title,
660
+ isDefault: item.isDefault,
661
+ order: item.order,
662
+ useSplat: item.useSplat,
663
+ updatedAt: item.updatedAt
664
+ };
665
+ routes.push(route);
666
+ }
667
+ });
668
+ if (rootCategory && item.category === rootCategory.id) {
669
+ const defaultLocale = locales.find((x) => x.isDefault) || locales[0];
670
+ const defaultMarket = markets.find((x) => x.isDefault) || markets[0];
671
+ const defaultMarketLocale = defaultMarket && defaultMarket.defaultLanguage ? defaultMarket.defaultLanguage : defaultLocale.id;
672
+ const title = localize(item.title, defaultMarketLocale);
673
+ const route = {
674
+ id: "/",
675
+ market: defaultMarket.id,
676
+ locale: defaultMarketLocale,
677
+ category: item.category,
678
+ page: item.id,
679
+ schema: key,
680
+ template: item.template,
681
+ title,
682
+ isDefault: item.isDefault,
683
+ order: item.order,
684
+ useSplat: item.useSplat,
685
+ updatedAt: item.updatedAt
686
+ };
687
+ routes.push(route);
688
+ }
689
+ });
690
+ }
691
+ return routes;
692
+ }
693
+ function eachMarketLocale(markets, locales, callback) {
694
+ locales = locales.filter((x) => x.isActive !== false);
695
+ markets = markets.filter((x) => x.isActive !== false);
696
+ markets.forEach((market) => {
697
+ const marketLocales = market.languages && market.languages.length > 0 ? market.languages.map((id) => locales.find((x) => x.id === id)).filter((x) => x !== void 0) : locales;
698
+ marketLocales.forEach((locale) => {
699
+ callback(market, locale, markets, locales);
700
+ });
701
+ });
702
+ }
703
+ function eachCategorized(store, callback) {
704
+ const keys = Object.keys(store);
705
+ for (const key of keys) {
706
+ const items = store[key];
707
+ for (const item of items) {
708
+ callback(item, key);
709
+ }
710
+ }
711
+ }
712
+ var RouteStrategy = /* @__PURE__ */ ((RouteStrategy2) => {
713
+ RouteStrategy2["Auto"] = "auto";
714
+ RouteStrategy2["Default"] = "default";
715
+ RouteStrategy2["Manual"] = "manual";
716
+ return RouteStrategy2;
717
+ })(RouteStrategy || {});
718
+ var ROUTE_STRATEGY = process.env.ROUTE_STRATEGY || "auto" /* Auto */;
719
+ function getRouteHref(segments, market, locale, markets, locales) {
720
+ const href = segments.reduce((p, c, i) => {
721
+ let slug = localize(c.slug || "", locale.id);
722
+ slug = `${p}/${slug}`;
723
+ return slug === "/" ? "" : slug;
724
+ }, "");
725
+ const prefixes = [];
726
+ switch (ROUTE_STRATEGY) {
727
+ case "auto" /* Auto */:
728
+ if (locales.length > 1 || markets.length > 1) {
729
+ prefixes.push(locale.id);
730
+ }
731
+ if (markets.length > 1) {
732
+ prefixes.push(market.id);
733
+ }
734
+ break;
735
+ case "default" /* Default */:
736
+ if (locale.isDefault !== true) {
737
+ prefixes.push(locale.id);
738
+ }
739
+ if (market.isDefault !== true) {
740
+ prefixes.push(market.id);
741
+ }
742
+ break;
743
+ case "manual" /* Manual */:
744
+ if (locale.isHidden !== true) {
745
+ prefixes.push(locale.id);
746
+ }
747
+ if (market.isHidden !== true) {
748
+ prefixes.push(market.id);
749
+ }
750
+ break;
751
+ }
752
+ const prefix = prefixes.length ? `/${prefixes.join("-")}` : "";
753
+ return `${prefix}${href}`;
754
+ }
755
+
555
756
  // src/slug/slug.ts
556
- function titleToSlug(title) {
757
+ function textToSlug(title) {
557
758
  return title.toString().normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase().replace(/\s+/g, "-").replace(/&/g, "-and-").replace(/[^\w-]+/g, "").replace(/--+/g, "-").replace(/^-+/, "").replace(/-+$/, "");
558
759
  }
559
760
 
@@ -565,19 +766,42 @@ function toFindParams(idOrParams) {
565
766
  return idOrParams;
566
767
  }
567
768
  }
769
+
770
+ // src/types/redirect.ts
771
+ var RedirectReason = /* @__PURE__ */ ((RedirectReason2) => {
772
+ RedirectReason2["MovedPermanently"] = "301";
773
+ RedirectReason2["Found"] = "302";
774
+ RedirectReason2["TemporaryRedirect"] = "307";
775
+ RedirectReason2["PermanentRedirect"] = "308";
776
+ RedirectReason2["Gone"] = "410";
777
+ return RedirectReason2;
778
+ })(RedirectReason || {});
568
779
  export {
569
780
  JsonService,
781
+ RedirectReason,
782
+ RouteStrategy,
783
+ asCategoryId,
570
784
  asEquatable,
571
785
  asServerProps,
572
786
  awaitAll,
573
787
  camelToSnakeCase,
788
+ collectRoutes,
574
789
  consoleWarn,
575
790
  deepCopy,
576
791
  deserializeKey,
577
792
  deserializeValue,
793
+ eachCategorized,
794
+ eachMarketLocale,
795
+ enumToList,
796
+ getCategorySegments,
797
+ getCategorySegmentsByCategory,
578
798
  getClassNames,
579
799
  getCollectionDescription,
580
800
  getIsDevelopment,
801
+ getIsProduction,
802
+ getParentCategoriesById,
803
+ getRootCategory,
804
+ getRouteHref,
581
805
  httpDelete,
582
806
  httpFetch,
583
807
  httpGet,
@@ -585,16 +809,20 @@ export {
585
809
  httpPost,
586
810
  httpPut,
587
811
  isBrowser,
812
+ isCategory,
588
813
  isDevelopment,
589
814
  isLocalizedString,
815
+ isProduction,
816
+ isStage,
590
817
  isValidCollectionDescription,
818
+ localize,
591
819
  localizeItem,
592
820
  localizeValue,
593
821
  localizedToString,
594
822
  merge,
595
823
  qsDeserialize,
596
824
  qsSerialize,
597
- titleToSlug,
825
+ textToSlug,
598
826
  toDisplayName,
599
827
  toFindParams,
600
828
  toPluralName,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@websolutespa/bom-core",
3
- "version": "0.1.2",
3
+ "version": "0.2.1",
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": "*",