@websolutespa/bom-core 3.0.5 → 4.0.0-next.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,20 @@
1
1
  # @websolutespa/bom-core
2
2
 
3
+ ## 4.0.0-next.1
4
+
5
+ ### Patch Changes
6
+
7
+ - `getMetaRobots` reads `meta.robots` or `meta.robot` (the `@payloadcms/plugin-seo` field); `IMeta.robot` is added and route `noindex` uses it.
8
+ - `ILayout.homeHref` is added.
9
+ - `ROOT_CATEGORY` is read when `getRootCategory` is called instead of once at module load.
10
+ - `httpFetch` logs 4xx responses in development only; server errors and network errors are always logged.
11
+
12
+ ## 4.0.0-next.0
13
+
14
+ ### Major Changes
15
+
16
+ - Next: support of the bom-next app package
17
+
3
18
  ## 3.0.5
4
19
 
5
20
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -267,95 +267,11 @@ type IMeta = {
267
267
  description?: string;
268
268
  keywords?: string;
269
269
  robots?: string;
270
+ /** Robots directive as stored by `@payloadcms/plugin-seo` setups. Read through `getMetaRobots`. */
271
+ robot?: string;
270
272
  image?: IMedia;
271
273
  };
272
274
 
273
- type ICategory = IEntity & {
274
- category?: IEquatable | ICategory;
275
- id: IEquatable;
276
- media?: IMedia;
277
- order?: number;
278
- slug: ILocalizable;
279
- title: ILocalizable;
280
- isHidden?: boolean;
281
- };
282
- type ICategoryItem = ICategory & {
283
- items: ICategory[];
284
- };
285
- type ICategorized = IEntity & {
286
- title: ILocalizable;
287
- abstract?: string;
288
- description?: string;
289
- media?: IMedia;
290
- meta?: IMeta;
291
- components?: IComponent[];
292
- category: IEquatable | ICategory;
293
- slug: ILocalizable;
294
- template?: string;
295
- isDefault?: boolean;
296
- useSplat?: boolean;
297
- order?: number;
298
- markets?: string[];
299
- createdAt?: Date;
300
- updatedAt?: Date;
301
- _status?: string;
302
- };
303
-
304
- type IClassNameValue = string | boolean | number | null | undefined;
305
- type IClassNameCollection = Record<string, IClassNameValue>;
306
- type IClassName = IClassNameCollection | IClassNameValue;
307
-
308
- type IEmailConfig = IEntity & {
309
- id: string;
310
- subject: string;
311
- fromAddress: string;
312
- to: [{
313
- address: string;
314
- }];
315
- cc: [{
316
- address: string;
317
- }];
318
- bcc: [{
319
- address: string;
320
- }];
321
- };
322
-
323
- type FetchRequestOptions = {
324
- method?: string;
325
- headers?: {
326
- [key: string]: string;
327
- };
328
- body?: Blob | BufferSource | FormData | URLSearchParams | string | ReadableStream;
329
- mode?: 'cors' | 'no-cors' | 'same-origin';
330
- credentials?: 'omit' | 'same-origin' | 'include';
331
- cache?: 'default' | 'no-store' | 'reload' | 'no-cache' | 'force-cache' | 'only-if-cached';
332
- redirect?: 'follow' | 'error' | 'manual';
333
- referrer?: string;
334
- referrerPolicy?: 'no-referrer, no-referrer-when-downgrade, same-origin, origin, strict-origin, origin-when-cross-origin, strict-origin-when-cross-origin, or unsafe-url';
335
- integrity?: string;
336
- keepalive?: boolean;
337
- signal?: AbortSignal;
338
- };
339
- type FetchService<T = unknown> = (url: string, options?: FetchRequestOptions, serviceOptions?: any) => Promise<T>;
340
- type FetchError = any;
341
-
342
- declare enum ImageStrategy {
343
- None = "none",
344
- Next = "next",
345
- Mixer = "mixer",
346
- Oven = "oven",
347
- Token = "token",
348
- Custom = "custom"
349
- }
350
- type IImageResize = {
351
- q: number;
352
- w: number;
353
- h?: number;
354
- x?: number;
355
- y?: number;
356
- src: string;
357
- };
358
-
359
275
  type ILabel = IEntity & {
360
276
  id: IEquatable;
361
277
  text?: ILocalizable;
@@ -380,111 +296,11 @@ type IMarket = IEntity & {
380
296
  isHidden?: boolean;
381
297
  };
382
298
 
383
- type IRoute = {
384
- category: IEquatable;
385
- id: string;
386
- isDefault?: boolean;
387
- isRoot?: boolean;
388
- locale: string;
389
- market: string;
390
- media?: IMedia;
391
- noindex?: boolean;
392
- order?: number;
393
- page: IEquatable;
394
- resolvedCategory: IEquatable;
395
- schema: string;
396
- template?: string;
397
- title: string;
398
- updatedAt?: Date;
399
- useSplat?: boolean;
400
- };
401
- type IRouteCanonical = IRoute & {
402
- alternates: IRoute[];
403
- };
404
- type IRouteLink = {
405
- category: IEquatable;
406
- href: string;
407
- id: IEquatable;
408
- items?: IRouteLink[];
409
- media?: IMedia;
410
- schema: string;
411
- title: string;
412
- };
413
- type IRouteParams = Record<string, any> & {
414
- id: IEquatable;
415
- locale: string;
416
- market: string;
417
- };
418
-
419
- /**
420
- * Represents a listing item returned from the findMany method of a page collection.
421
- * @template T - The type of the categorized item.
422
- */
423
- type IPageItem<T extends ICategorized = ICategorized> = ILocalized<T> & {
424
- schema: string;
425
- category: ILocalized<ICategory> & {
426
- href: string;
427
- };
428
- href: string;
429
- locale: string;
430
- market: string;
431
- };
432
- /**
433
- * Represents a detail item returned from the findOne method of a page collection.
434
- * @template T - The type of the categorized item.
435
- */
436
- type IPage<T extends ICategorized = ICategorized> = ILocalized<T> & {
437
- schema: string;
438
- alternates: IRoute[];
439
- breadcrumb: IRouteLink[];
440
- category: ILocalized<ICategory> & {
441
- href: string;
442
- };
443
- href: string;
444
- locale: string;
445
- market: string;
446
- parentRoute?: IRouteLink;
447
- slug: string;
448
- title: string;
449
- };
450
- /**
451
- * Represents the props for a page component.
452
- * @template T - The type of the categorized item.
453
- */
454
- type PageProps<T extends ICategorized = ICategorized> = {
455
- layout: ILayout;
456
- page: IPage<T>;
457
- params: IRouteParams;
458
- locales?: string[];
459
- locale?: string;
460
- defaultLocale?: string;
461
- };
462
- /**
463
- * Represents the result of a page relation.
464
- * @template T - The type of the categorized item.
465
- */
466
- type IPageRelationResult<T extends ICategorized = ICategorized> = {
467
- href: string;
468
- } & T;
469
- /**
470
- * Represents a page relation.
471
- * @template T - The type of the categorized item.
472
- */
473
- type IPageRelation<T extends ICategorized = ICategorized> = {
474
- relationTo: string;
475
- value: IPageRelationResult<T> | IPageRelationResult<T>[];
476
- };
477
- /**
478
- * Represents an unresolved page relation.
479
- */
480
- type IPageUnresolvedRelation = {
481
- relationTo: string;
482
- value: IEquatable | IEquatable[];
483
- };
484
-
485
299
  declare enum MenuCategoryStrategy {
486
300
  Default = "default",// show hidden categories recursively,
487
- ExcludeHidden = "exclude-hidden"
301
+ ExcludeHidden = "exclude-hidden",
302
+ ExcludeRoute = "exclude-route",
303
+ ExcludeHiddenAndRoute = "exclude-hidden-and-route"
488
304
  }
489
305
  type IMenuBase = {
490
306
  abstract?: ILocalizable;
@@ -587,6 +403,50 @@ type IUndecoratedMenu = IEntity & {
587
403
  items: IUndecoratedMenuItem[];
588
404
  };
589
405
 
406
+ type IRoute = {
407
+ category: IEquatable;
408
+ id: string;
409
+ locale: string;
410
+ market: string;
411
+ page: IEquatable;
412
+ resolvedCategory: IEquatable;
413
+ schema: string;
414
+ title: string;
415
+ _status?: 'published' | 'draft';
416
+ isDefault?: boolean;
417
+ isProtected?: boolean;
418
+ isRoot?: boolean;
419
+ media?: {
420
+ url: string;
421
+ src?: string;
422
+ type: string;
423
+ updatedAt?: Date | string;
424
+ };
425
+ noindex?: boolean;
426
+ order?: number;
427
+ publicRedirect?: string;
428
+ template?: string;
429
+ updatedAt?: Date | string;
430
+ useSplat?: boolean;
431
+ };
432
+ type IRouteCanonical = IRoute & {
433
+ alternates: IRoute[];
434
+ };
435
+ type IRouteLink = {
436
+ category: IEquatable;
437
+ href: string;
438
+ id: IEquatable;
439
+ items?: IRouteLink[];
440
+ media?: IMedia;
441
+ schema: string;
442
+ title: string;
443
+ };
444
+ type IRouteParams = Record<string, any> & {
445
+ id: IEquatable;
446
+ locale: string;
447
+ market: string;
448
+ };
449
+
590
450
  type ILayout<T extends IAppExtended = IAppExtended> = {
591
451
  labels: ILabel[];
592
452
  locale: string;
@@ -595,9 +455,171 @@ type ILayout<T extends IAppExtended = IAppExtended> = {
595
455
  markets: IMarket[];
596
456
  app: IPrettify<IApp<T>>;
597
457
  menu: Record<string, ILocalizedMenu>;
458
+ /**
459
+ * Root and first-level hrefs keyed by route schema (template or collection
460
+ * slug), so renaming a collection changes the key. Use `homeHref` for the
461
+ * home page.
462
+ */
598
463
  topLevelHrefs: Record<string, string>;
464
+ /** Root and first-level routes, keyed like `topLevelHrefs`. */
599
465
  topLevelRoutes: Record<string, IRouteLink>;
600
466
  tree?: IRouteLink;
467
+ /** Href of the root page for the current market and locale (`tree.href`). */
468
+ homeHref?: string;
469
+ };
470
+
471
+ /**
472
+ * Represents a listing item returned from the findMany method of a page collection.
473
+ * @template T - The type of the categorized item.
474
+ */
475
+ type IPageItem<T extends ICategorized = ICategorized> = ILocalized<T> & {
476
+ schema: string;
477
+ category: ILocalized<ICategory> & {
478
+ href: string;
479
+ };
480
+ href: string;
481
+ locale: string;
482
+ market: string;
483
+ };
484
+ /**
485
+ * Represents a detail item returned from the findOne method of a page collection.
486
+ * @template T - The type of the categorized item.
487
+ */
488
+ type IPage<T extends ICategorized = ICategorized> = ILocalized<T> & {
489
+ schema: string;
490
+ alternates: IRoute[];
491
+ breadcrumb: IRouteLink[];
492
+ category: ILocalized<ICategory> & {
493
+ href: string;
494
+ };
495
+ href: string;
496
+ locale: string;
497
+ market: string;
498
+ parentRoute?: IRouteLink;
499
+ slug: string;
500
+ title: string;
501
+ };
502
+ /**
503
+ * Represents the props for a page component.
504
+ * @template T - The type of the categorized item.
505
+ */
506
+ type PageProps<T extends ICategorized = ICategorized> = {
507
+ layout: ILayout;
508
+ page: IPage<T>;
509
+ params: IRouteParams;
510
+ locales?: string[];
511
+ locale?: string;
512
+ defaultLocale?: string;
513
+ };
514
+ /**
515
+ * Represents the result of a page relation.
516
+ * @template T - The type of the categorized item.
517
+ */
518
+ type IPageRelationResult<T extends ICategorized = ICategorized> = {
519
+ href: string;
520
+ } & T;
521
+ /**
522
+ * Represents a page relation.
523
+ * @template T - The type of the categorized item.
524
+ */
525
+ type IPageRelation<T extends ICategorized = ICategorized> = {
526
+ relationTo: string;
527
+ value: IPageRelationResult<T> | IPageRelationResult<T>[];
528
+ };
529
+ /**
530
+ * Represents an unresolved page relation.
531
+ */
532
+ type IPageUnresolvedRelation = {
533
+ relationTo: string;
534
+ value: IEquatable | IEquatable[];
535
+ };
536
+
537
+ type ICategory = IEntity & {
538
+ category?: IEquatable | ICategory;
539
+ id: IEquatable;
540
+ media?: IMedia;
541
+ order?: number;
542
+ slug: ILocalizable;
543
+ title: ILocalizable;
544
+ isHidden?: boolean;
545
+ };
546
+ type ICategoryItem = ICategory & {
547
+ items: ICategory[];
548
+ };
549
+ type ICategorized = IEntity & {
550
+ category: IEquatable | ICategory;
551
+ slug: ILocalizable;
552
+ title: ILocalizable;
553
+ _status?: string;
554
+ abstract?: string;
555
+ components?: IComponent[];
556
+ createdAt?: Date;
557
+ description?: string;
558
+ isDefault?: boolean;
559
+ isProtected?: boolean;
560
+ markets?: string[];
561
+ media?: IMedia;
562
+ meta?: IMeta;
563
+ order?: number;
564
+ publicRedirect?: IEquatable | IEntity | IPageUnresolvedRelation;
565
+ template?: string;
566
+ updatedAt?: Date;
567
+ useSplat?: boolean;
568
+ };
569
+
570
+ type IClassNameValue = string | boolean | number | null | undefined;
571
+ type IClassNameCollection = Record<string, IClassNameValue>;
572
+ type IClassName = IClassNameCollection | IClassNameValue;
573
+
574
+ type IEmailConfig = IEntity & {
575
+ id: string;
576
+ subject: string;
577
+ fromAddress: string;
578
+ to: [{
579
+ address: string;
580
+ }];
581
+ cc: [{
582
+ address: string;
583
+ }];
584
+ bcc: [{
585
+ address: string;
586
+ }];
587
+ };
588
+
589
+ type FetchRequestOptions = {
590
+ method?: string;
591
+ headers?: {
592
+ [key: string]: string;
593
+ };
594
+ body?: Blob | BufferSource | FormData | URLSearchParams | string | ReadableStream;
595
+ mode?: 'cors' | 'no-cors' | 'same-origin';
596
+ credentials?: 'omit' | 'same-origin' | 'include';
597
+ cache?: 'default' | 'no-store' | 'reload' | 'no-cache' | 'force-cache' | 'only-if-cached';
598
+ redirect?: 'follow' | 'error' | 'manual';
599
+ referrer?: string;
600
+ referrerPolicy?: 'no-referrer, no-referrer-when-downgrade, same-origin, origin, strict-origin, origin-when-cross-origin, strict-origin-when-cross-origin, or unsafe-url';
601
+ integrity?: string;
602
+ keepalive?: boolean;
603
+ signal?: AbortSignal;
604
+ };
605
+ type FetchService<T = unknown> = (url: string, options?: FetchRequestOptions, serviceOptions?: any) => Promise<T>;
606
+ type FetchError = any;
607
+
608
+ declare enum ImageStrategy {
609
+ None = "none",
610
+ Next = "next",
611
+ Mixer = "mixer",
612
+ Oven = "oven",
613
+ Token = "token",
614
+ Custom = "custom"
615
+ }
616
+ type IImageResize = {
617
+ q: number;
618
+ w: number;
619
+ h?: number;
620
+ x?: number;
621
+ y?: number;
622
+ src: string;
601
623
  };
602
624
 
603
625
  type IUndecoratedNav = IUndecoratedMenuPage;
@@ -745,7 +767,10 @@ type ITemplate = {
745
767
  };
746
768
 
747
769
  /**
748
- * Retrieves the root category from the given list of categories.
770
+ * Retrieves the root category from the given list of categories: the one with
771
+ * id `ROOT_CATEGORY` when set and present, otherwise the first category
772
+ * without a parent. The root and hidden categories (`isHidden`) do not add a
773
+ * segment to page URLs.
749
774
  * @param categories - The list of categories.
750
775
  * @returns The root category, if found; otherwise undefined.
751
776
  */
@@ -1038,6 +1063,13 @@ declare class Logger {
1038
1063
 
1039
1064
  declare const defaultMarket: string;
1040
1065
 
1066
+ /**
1067
+ * Returns the robots directive of a page's meta. Bowl's SEO rules write
1068
+ * `meta.robots`, while `@payloadcms/plugin-seo` setups store the editor value
1069
+ * in `meta.robot`; `robots` wins when both are set.
1070
+ */
1071
+ declare function getMetaRobots(meta?: Pick<IMeta, 'robots' | 'robot'> | null): string | undefined;
1072
+
1041
1073
  /**
1042
1074
  * STORE
1043
1075
  */
@@ -1568,4 +1600,4 @@ declare function asServerProps(props: any): any;
1568
1600
  declare function awaitAll(array: any, callback: (item: any, index: number) => Promise<any>): Promise<any[]>;
1569
1601
  declare function merge(target: any, source: any): any;
1570
1602
 
1571
- export { type DeepPartial, type DeepRequired, type DeepRequiredProps, type Dynamic, type FetchError, type FetchRequestOptions, type FetchService, type FindParams, type FindWhereParams, type GlobalQueryParams, type Headings, type IAction, type IActionEmailData, type IAnchor, type IApp, type IAppExtended, type ICategorized, type ICategory, type ICategoryItem, type IClassName, type IClassNameCollection, type IClassNameValue, type ICollectionDescription, type IComponent, type IConfig, type IConsentPreference, type IContextBaseParams, type IContextParams, type IDecoratedNav, type IEmailConfig, type IEndUser, type IEntity, type IEquatable, type IFeature, type IFeatureType, type IImageResize, type ILabel, type ILayout, type ILegalNotice, type ILetter, type ILocale, type ILocaleId, type ILocalizable, type ILocalized, type ILocalizedMenu, type ILocalizedString, type IMarket, type IMedia, type IMediaImage, type IMediaInfo, type IMediaJson, type IMediaPdf, type IMediaType, type IMediaVideo, type IMemoryStore, type IMenu, type IMenuBase, type IMenuCategory, type IMenuCustom, type IMenuGroup, type IMenuHref, type IMenuItem, type IMenuLink, type IMenuPage, type IMenuRoute, type IMockCategory, type IMockCollection, type IMockComponent, type IMockLabel, type IMockLocale, type IMockManyRelation, type IMockMarket, type IMockMedia, type IMockMenu, type IMockMenuBase, type IMockMenuCategory, type IMockMenuCustom, type IMockMenuExtra, type IMockMenuGroup, type IMockMenuItem, type IMockMenuLink, type IMockMenuPage, type IMockMeta, type IMockPage, type IMockRedirect, type IMockRelation, type IMockSingleRelation, type IMockStore, type IMockTemplate, type INamedEntity, type IOption, type IOverride, type IOverrideReturnValue, type IOverrideValue, type IPackages, type IPage, type IPageItem, type IPageRelation, type IPageRelationResult, type IPageUnresolvedRelation, type IPagedResult, type IPaginationResult, type IPartial, type IPreference, type IPrettify, type IQuerable, type IRedirect, type IRoute, type IRouteCanonical, type IRouteLink, type IRouteParams, type ISchema, type ISerializedCollection, type ISerializedStore, type IServerSideContext, type IServerSideSplatContext, type IStaticContext, type IStore, type ITemplate, type ITitledEntity, type IUndecoratedMenu, type IUndecoratedMenuBase, type IUndecoratedMenuCategory, type IUndecoratedMenuCustom, type IUndecoratedMenuGroup, type IUndecoratedMenuItem, type IUndecoratedMenuLink, type IUndecoratedMenuPage, type IUndecoratedNav, ImageStrategy, type Json, JsonService, type LiteralUnion, Logger, LoggerLevel, MenuCategoryStrategy, type NextApiRequestCookies, type Operator, type PageProps, type Pagination, type PreviewData, type Primitive, type QueryParams, RedirectReason, type RequireAtLeastOne, RouteStrategy, type SchemaType, type ServerSidePropsContext, type Shift, type ShiftUnion, type StaticPropsContext, type User, type UserRole, type UserWithRoles, type ValueOf, type Where, type WhereAndOr, type WhereBase, type WhereField, arrayShuffle, asCategoryId, asEntityId, asEquatable, asEquatableNumber, asEquatableString, asServerProps, asSortedOptions, awaitAll, base64ToString, camelToSnakeCase, cloneItem, collectFeatureTypes, collectFeatureTypes__, collectOptions, collectRoutes, consoleWarn, decryptImageResize, deepMerge, defaultLocale, defaultMarket, eachCategorized, eachMarketLocale, eachMedia, eachValue, encryptImageToken, enumToList, excludeComponent, findCategoryInCategories, findChildCategories, findRouteOfCategory, findRoutesOfCategory, getCategorySegments, getCategorySegmentsByCategory, getClassNames, getCollectionDescription, getEnv, getHost, getIsDevelopment, getIsProduction, getOrigin, getParentCategoriesById, getParentCategory, getProto, getRootCategory, getRouteHref, groupComponents, hasComponents, hasMarket, httpDelete, httpFetch, httpGet, httpPatch, httpPost, httpPut, isArray, isBrowser, isComponent, isComponentOfType, isDevelopment, isEntity, isEquatable, isHeaders, isIncomingHttpHeaders, isLocalizedString, isMedia, isMediaImage, isMediaJson, isMediaPdf, isMediaVideo, isMenuCategory, isMenuGroup, isMenuHref, isMenuLink, isMenuPage, isMenuRoute, isNonEmptyArray, isNotNullString, isNumber, isObject, isProduction, isPromise, isRelation, isReservedCollectionDescription, isStage, isString, isUndecoratedCategory, isUndecoratedMenuCategory, isUndecoratedMenuItem, isUndecoratedMenuPage, isUndecoratedNav, isUnresolvedCategory, isUnresolvedRelation, isValidCollectionDescription, isValidString, localize, localizeItem, localizeValue, localizedToString, merge, pascalCaseToCamelCase, pascalCaseToCharCase, pascalCaseToKebabCase, pascalCaseToSnakeCase, passwordStrategy, pickComponent, pickHero, prependHero, qsDeserialize, qsDeserializeRaw, qsSerialize, qsSerializeRaw, resolveMediaType, stringToBase64, toCamelCase, toCharCase, toDisplayName, toFindParams, toKebabCase, toPascalCase, toPluralName, toSnakeCase, toTitleCase, toValidCollectionName, unknownCaseToPascalCase, uuidv4, withSchema };
1603
+ export { type DeepPartial, type DeepRequired, type DeepRequiredProps, type Dynamic, type FetchError, type FetchRequestOptions, type FetchService, type FindParams, type FindWhereParams, type GlobalQueryParams, type Headings, type IAction, type IActionEmailData, type IAnchor, type IApp, type IAppExtended, type ICategorized, type ICategory, type ICategoryItem, type IClassName, type IClassNameCollection, type IClassNameValue, type ICollectionDescription, type IComponent, type IConfig, type IConsentPreference, type IContextBaseParams, type IContextParams, type IDecoratedNav, type IEmailConfig, type IEndUser, type IEntity, type IEquatable, type IFeature, type IFeatureType, type IImageResize, type ILabel, type ILayout, type ILegalNotice, type ILetter, type ILocale, type ILocaleId, type ILocalizable, type ILocalized, type ILocalizedMenu, type ILocalizedString, type IMarket, type IMedia, type IMediaImage, type IMediaInfo, type IMediaJson, type IMediaPdf, type IMediaType, type IMediaVideo, type IMemoryStore, type IMenu, type IMenuBase, type IMenuCategory, type IMenuCustom, type IMenuGroup, type IMenuHref, type IMenuItem, type IMenuLink, type IMenuPage, type IMenuRoute, type IMockCategory, type IMockCollection, type IMockComponent, type IMockLabel, type IMockLocale, type IMockManyRelation, type IMockMarket, type IMockMedia, type IMockMenu, type IMockMenuBase, type IMockMenuCategory, type IMockMenuCustom, type IMockMenuExtra, type IMockMenuGroup, type IMockMenuItem, type IMockMenuLink, type IMockMenuPage, type IMockMeta, type IMockPage, type IMockRedirect, type IMockRelation, type IMockSingleRelation, type IMockStore, type IMockTemplate, type INamedEntity, type IOption, type IOverride, type IOverrideReturnValue, type IOverrideValue, type IPackages, type IPage, type IPageItem, type IPageRelation, type IPageRelationResult, type IPageUnresolvedRelation, type IPagedResult, type IPaginationResult, type IPartial, type IPreference, type IPrettify, type IQuerable, type IRedirect, type IRoute, type IRouteCanonical, type IRouteLink, type IRouteParams, type ISchema, type ISerializedCollection, type ISerializedStore, type IServerSideContext, type IServerSideSplatContext, type IStaticContext, type IStore, type ITemplate, type ITitledEntity, type IUndecoratedMenu, type IUndecoratedMenuBase, type IUndecoratedMenuCategory, type IUndecoratedMenuCustom, type IUndecoratedMenuGroup, type IUndecoratedMenuItem, type IUndecoratedMenuLink, type IUndecoratedMenuPage, type IUndecoratedNav, ImageStrategy, type Json, JsonService, type LiteralUnion, Logger, LoggerLevel, MenuCategoryStrategy, type NextApiRequestCookies, type Operator, type PageProps, type Pagination, type PreviewData, type Primitive, type QueryParams, RedirectReason, type RequireAtLeastOne, RouteStrategy, type SchemaType, type ServerSidePropsContext, type Shift, type ShiftUnion, type StaticPropsContext, type User, type UserRole, type UserWithRoles, type ValueOf, type Where, type WhereAndOr, type WhereBase, type WhereField, arrayShuffle, asCategoryId, asEntityId, asEquatable, asEquatableNumber, asEquatableString, asServerProps, asSortedOptions, awaitAll, base64ToString, camelToSnakeCase, cloneItem, collectFeatureTypes, collectFeatureTypes__, collectOptions, collectRoutes, consoleWarn, decryptImageResize, deepMerge, defaultLocale, defaultMarket, eachCategorized, eachMarketLocale, eachMedia, eachValue, encryptImageToken, enumToList, excludeComponent, findCategoryInCategories, findChildCategories, findRouteOfCategory, findRoutesOfCategory, getCategorySegments, getCategorySegmentsByCategory, getClassNames, getCollectionDescription, getEnv, getHost, getIsDevelopment, getIsProduction, getMetaRobots, getOrigin, getParentCategoriesById, getParentCategory, getProto, getRootCategory, getRouteHref, groupComponents, hasComponents, hasMarket, httpDelete, httpFetch, httpGet, httpPatch, httpPost, httpPut, isArray, isBrowser, isComponent, isComponentOfType, isDevelopment, isEntity, isEquatable, isHeaders, isIncomingHttpHeaders, isLocalizedString, isMedia, isMediaImage, isMediaJson, isMediaPdf, isMediaVideo, isMenuCategory, isMenuGroup, isMenuHref, isMenuLink, isMenuPage, isMenuRoute, isNonEmptyArray, isNotNullString, isNumber, isObject, isProduction, isPromise, isRelation, isReservedCollectionDescription, isStage, isString, isUndecoratedCategory, isUndecoratedMenuCategory, isUndecoratedMenuItem, isUndecoratedMenuPage, isUndecoratedNav, isUnresolvedCategory, isUnresolvedRelation, isValidCollectionDescription, isValidString, localize, localizeItem, localizeValue, localizedToString, merge, pascalCaseToCamelCase, pascalCaseToCharCase, pascalCaseToKebabCase, pascalCaseToSnakeCase, passwordStrategy, pickComponent, pickHero, prependHero, qsDeserialize, qsDeserializeRaw, qsSerialize, qsSerializeRaw, resolveMediaType, stringToBase64, toCamelCase, toCharCase, toDisplayName, toFindParams, toKebabCase, toPascalCase, toPluralName, toSnakeCase, toTitleCase, toValidCollectionName, unknownCaseToPascalCase, uuidv4, withSchema };
package/dist/index.js CHANGED
@@ -417,6 +417,8 @@ function isMedia(item) {
417
417
  var MenuCategoryStrategy = /* @__PURE__ */ ((MenuCategoryStrategy2) => {
418
418
  MenuCategoryStrategy2["Default"] = "default";
419
419
  MenuCategoryStrategy2["ExcludeHidden"] = "exclude-hidden";
420
+ MenuCategoryStrategy2["ExcludeRoute"] = "exclude-route";
421
+ MenuCategoryStrategy2["ExcludeHiddenAndRoute"] = "exclude-hidden-and-route";
420
422
  return MenuCategoryStrategy2;
421
423
  })(MenuCategoryStrategy || {});
422
424
  function isMenuHref(item) {
@@ -554,9 +556,12 @@ function stringToBase64(text) {
554
556
  }
555
557
 
556
558
  // src/category/category.ts
557
- var ROOT_CATEGORY = process.env.ROOT_CATEGORY || "";
559
+ function getRootCategoryId() {
560
+ return (typeof process !== "undefined" ? process.env?.ROOT_CATEGORY : void 0) || "";
561
+ }
558
562
  function getRootCategory(categories) {
559
- const rootCategory = ROOT_CATEGORY && categories.find((x) => x.id == ROOT_CATEGORY);
563
+ const rootCategoryId = getRootCategoryId();
564
+ const rootCategory = rootCategoryId && categories.find((x) => x.id == rootCategoryId);
560
565
  if (rootCategory) {
561
566
  return rootCategory;
562
567
  } else {
@@ -754,9 +759,11 @@ async function httpFetch(url, options = {}, serviceOptions) {
754
759
  return response;
755
760
  } catch (error) {
756
761
  if (typeof error === "object" && error.status) {
757
- console.log("httpFetch.error", options.method, url, error.status, error.statusText);
762
+ if (error.status >= 500 || isDevelopment) {
763
+ console.warn("httpFetch.error", options.method, url, error.status, error.statusText);
764
+ }
758
765
  } else {
759
- console.log("httpFetch.error", options.method, url, error);
766
+ console.warn("httpFetch.error", options.method, url, error);
760
767
  }
761
768
  throw error;
762
769
  }
@@ -1372,6 +1379,11 @@ function getDefaultMarket() {
1372
1379
  }
1373
1380
  var defaultMarket = getDefaultMarket();
1374
1381
 
1382
+ // src/meta/meta.ts
1383
+ function getMetaRobots(meta) {
1384
+ return meta?.robots || meta?.robot || void 0;
1385
+ }
1386
+
1375
1387
  // src/password/password.ts
1376
1388
  function getPasswordStrategy() {
1377
1389
  const passwordStrategy2 = typeof process !== "undefined" && process.env.NEXT_PUBLIC_PASSWORD_STRATEGY ? process.env.NEXT_PUBLIC_PASSWORD_STRATEGY : "^(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])[a-zA-Z0-9._\\-\\W]{8,}$";
@@ -1439,8 +1451,7 @@ function qsDeserializeKey(value) {
1439
1451
  return value;
1440
1452
  }
1441
1453
  function qsDeserializeKeys(key) {
1442
- const keys = key.replace(/[\]]/g, "").split("[").map((x) => qsDeserializeKey(x));
1443
- return keys;
1454
+ return key.replace(/[\]]/g, "").split("[").map((value) => qsDeserializeKey(value)).filter((value) => value !== "");
1444
1455
  }
1445
1456
  function getNextKeyItem(key) {
1446
1457
  if (typeof key === "number") {
@@ -1534,12 +1545,49 @@ var RouteStrategy = /* @__PURE__ */ ((RouteStrategy2) => {
1534
1545
  return RouteStrategy2;
1535
1546
  })(RouteStrategy || {});
1536
1547
  var ROUTE_STRATEGY = process.env.ROUTE_STRATEGY || "manual" /* Manual */;
1548
+ function getPublicRedirectId(value) {
1549
+ if (value && typeof value === "object" && "relationTo" in value && "value" in value) {
1550
+ const relationValue = value.value;
1551
+ return Array.isArray(relationValue) ? void 0 : asEntityId(relationValue);
1552
+ }
1553
+ return asEntityId(value);
1554
+ }
1537
1555
  function collectRoutes(store, categories, markets, locales, medias) {
1538
1556
  const routes = [];
1557
+ const redirects = /* @__PURE__ */ new Map();
1539
1558
  if (locales.length > 0 && markets.length > 0) {
1540
1559
  const rootCategory = getRootCategory(categories);
1560
+ const activeLocales = locales.filter((locale) => locale.isActive !== false);
1561
+ const activeMarkets = markets.filter((market) => market.isActive !== false);
1562
+ const categoryById = new Map(categories.filter((category) => typeof category.id === "string").map((category) => [category.id, category]));
1563
+ const localeById = new Map(activeLocales.filter((locale) => typeof locale.id === "string").map((locale) => [locale.id, locale]));
1564
+ const mediaById = new Map((medias || []).filter((media) => typeof media.id === "string").map((media) => [media.id, media]));
1565
+ const marketLocales = activeMarkets.flatMap((market) => {
1566
+ const localesForMarket = market.languages && market.languages.length > 0 ? market.languages.map((language) => localeById.get(String(asEntityId(language)))).filter((locale) => locale !== void 0) : activeLocales;
1567
+ return localesForMarket.map((locale) => ({ market, locale }));
1568
+ });
1569
+ const getSegments = (item) => {
1570
+ const routeCategories = [];
1571
+ const segments = [];
1572
+ let parentId = asCategoryId(item.category);
1573
+ while (parentId != null) {
1574
+ const parentCategory = categoryById.get(parentId);
1575
+ if (parentCategory) {
1576
+ segments.unshift({ ...parentCategory });
1577
+ routeCategories.push(parentId);
1578
+ parentId = asCategoryId(parentCategory.category);
1579
+ if (parentId && routeCategories.includes(parentId)) {
1580
+ console.log("ICategory.getParentCategoriesById", "circular reference found", parentId);
1581
+ parentId = void 0;
1582
+ }
1583
+ } else {
1584
+ parentId = void 0;
1585
+ }
1586
+ }
1587
+ return segments;
1588
+ };
1541
1589
  eachCategorized(store, (item, key) => {
1542
- const segments = getCategorySegments(categories, item);
1590
+ const segments = getSegments(item);
1543
1591
  const visibleSegments = segments.filter((x) => x.id !== rootCategory?.id && Boolean(x.isHidden) === false);
1544
1592
  const visibleCategory = visibleSegments.length > 0 ? visibleSegments[visibleSegments.length - 1] : rootCategory;
1545
1593
  if (visibleCategory) {
@@ -1548,16 +1596,17 @@ function collectRoutes(store, categories, markets, locales, medias) {
1548
1596
  slugs.push({ slug: item.slug });
1549
1597
  }
1550
1598
  const isRootCategory = rootCategory && item.category === rootCategory.id && item.isDefault;
1551
- eachMarketLocale(markets, locales, (market, locale, markets2, locales2) => {
1599
+ marketLocales.forEach(({ market, locale }) => {
1552
1600
  if (hasMarket(item, market)) {
1553
1601
  const title = localize(item.title, locale.id);
1554
- const id = getRouteHref(slugs, market, locale, markets2, locales2);
1602
+ const id = getRouteHref(slugs, market, locale, activeMarkets, activeLocales);
1555
1603
  const {
1556
1604
  _status,
1557
1605
  category,
1558
1606
  isDefault,
1559
1607
  media,
1560
1608
  order,
1609
+ isProtected,
1561
1610
  template,
1562
1611
  updatedAt,
1563
1612
  useSplat
@@ -1565,11 +1614,10 @@ function collectRoutes(store, categories, markets, locales, medias) {
1565
1614
  const route = {
1566
1615
  _status,
1567
1616
  category,
1568
- // id: isDefaultRootCategory ? '/' : id,
1569
1617
  id,
1570
1618
  locale: locale.id,
1571
1619
  market: market.id,
1572
- media,
1620
+ media: routeMedia(media),
1573
1621
  page: item.id,
1574
1622
  resolvedCategory: visibleCategory.id,
1575
1623
  schema: key,
@@ -1580,10 +1628,17 @@ function collectRoutes(store, categories, markets, locales, medias) {
1580
1628
  if (isDefault) {
1581
1629
  route.isDefault = true;
1582
1630
  }
1631
+ if (isProtected) {
1632
+ route.isProtected = true;
1633
+ const publicRedirect = getPublicRedirectId(item.publicRedirect);
1634
+ if (publicRedirect != null) {
1635
+ redirects.set(item.id, publicRedirect);
1636
+ }
1637
+ }
1583
1638
  if (isRootCategory) {
1584
1639
  route.isRoot = true;
1585
1640
  }
1586
- const noindex = (item.meta?.robots || "").includes("noindex");
1641
+ const noindex = (getMetaRobots(item.meta) || "").includes("noindex");
1587
1642
  if (noindex) {
1588
1643
  route.noindex = true;
1589
1644
  }
@@ -1594,10 +1649,10 @@ function collectRoutes(store, categories, markets, locales, medias) {
1594
1649
  route.useSplat = true;
1595
1650
  }
1596
1651
  const mediaId = typeof item.media === "string" ? item.media : void 0;
1597
- if (Array.isArray(medias) && mediaId) {
1598
- const media2 = item.media && medias.find((x) => x.id === mediaId);
1652
+ if (mediaId) {
1653
+ const media2 = mediaById.get(mediaId);
1599
1654
  if (media2) {
1600
- route.media = media2;
1655
+ route.media = routeMedia(media2);
1601
1656
  }
1602
1657
  }
1603
1658
  routes.push(route);
@@ -1605,9 +1660,34 @@ function collectRoutes(store, categories, markets, locales, medias) {
1605
1660
  });
1606
1661
  }
1607
1662
  });
1663
+ const routeByPageContext = /* @__PURE__ */ new Map();
1664
+ for (const route of routes) {
1665
+ routeByPageContext.set(JSON.stringify([route.page, route.market, route.locale]), route);
1666
+ }
1667
+ for (const route of routes) {
1668
+ const targetPage = redirects.get(route.page);
1669
+ if (targetPage != null) {
1670
+ const target = routeByPageContext.get(JSON.stringify([targetPage, route.market, route.locale]));
1671
+ if (target) {
1672
+ route.publicRedirect = target.id;
1673
+ }
1674
+ }
1675
+ }
1608
1676
  }
1609
1677
  return routes;
1610
1678
  }
1679
+ function routeMedia(media) {
1680
+ const url = media?.url || media?.src;
1681
+ return media && url ? {
1682
+ // createdAt: media.createdAt,
1683
+ updatedAt: media.updatedAt,
1684
+ // filename: media.filename,
1685
+ // mimeType: media.mimeType,
1686
+ // id: media.id,
1687
+ url,
1688
+ type: media.type
1689
+ } : void 0;
1690
+ }
1611
1691
  function eachMarketLocale(markets, locales, callback) {
1612
1692
  locales = locales.filter((x) => x.isActive !== false);
1613
1693
  markets = markets.filter((x) => x.isActive !== false);
@@ -1675,7 +1755,7 @@ function getRouteHref(segments, market, locale, markets, locales) {
1675
1755
  return `${prefix}${href}`;
1676
1756
  }
1677
1757
  function hasMarket(item, market) {
1678
- return !item.markets || item.markets.length === 0 || item.markets.includes(market.id);
1758
+ return !item.markets || item.markets.length === 0 || item.markets.map((x) => asEquatable(x)).includes(market.id);
1679
1759
  }
1680
1760
  export {
1681
1761
  ImageStrategy,
@@ -1725,6 +1805,7 @@ export {
1725
1805
  getHost,
1726
1806
  getIsDevelopment,
1727
1807
  getIsProduction,
1808
+ getMetaRobots,
1728
1809
  getOrigin,
1729
1810
  getParentCategoriesById,
1730
1811
  getParentCategory,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@websolutespa/bom-core",
3
- "version": "3.0.5",
3
+ "version": "4.0.0-next.1",
4
4
  "description": "Core module of the BOM Repository",
5
5
  "keywords": [
6
6
  "bom",
@@ -31,7 +31,7 @@
31
31
  "@types/pluralize": "0.0.33",
32
32
  "@types/react": "19.2.10",
33
33
  "@websolutespa/eslint-config": "^3.0.0",
34
- "@websolutespa/test": "^3.0.1",
34
+ "@websolutespa/test": "^4.0.0-next.0",
35
35
  "@websolutespa/tsconfig": "^3.0.0",
36
36
  "eslint": "9.31.0",
37
37
  "npm-run-all": "4.1.5",