@jjlmoya/utils-aquarium 1.61.0 → 1.62.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.
Files changed (31) hide show
  1. package/package.json +18 -5
  2. package/scripts/postbuild.mjs +17 -0
  3. package/scripts/postinstall.mjs +2 -4
  4. package/src/components/ProductionBreadcrumb.astro +134 -0
  5. package/src/components/ProductionStructuredData.astro +46 -0
  6. package/src/components/ProductionWidget.astro +182 -0
  7. package/src/i18n/header-ui.ts +15 -0
  8. package/src/i18n/language-ui.ts +9 -0
  9. package/src/i18n/languages.ts +24 -0
  10. package/src/identity/brands.ts +5 -0
  11. package/src/layouts/ProductionCategoryPage.astro +184 -0
  12. package/src/layouts/ProductionPage.astro +218 -0
  13. package/src/layouts/ProductionUtilityPage.astro +283 -0
  14. package/src/mfe/assets.ts +39 -0
  15. package/src/mfe/category-ui.ts +34 -0
  16. package/src/mfe/manifest.ts +45 -0
  17. package/src/mfe/routes.ts +50 -0
  18. package/src/pages/[locale]/[utilities]/[categories]/[category]/[slug]/manifest.json.ts +49 -0
  19. package/src/pages/[locale]/[utilities]/[categories]/[category]/[slug].astro +100 -0
  20. package/src/pages/[locale]/[utilities]/[categories]/[category].astro +44 -0
  21. package/src/pages/index.astro +1 -1
  22. package/src/pages/mfe-sitemaps/[locale]/[vertical]/sitemap.xml.ts +75 -0
  23. package/src/pages/utilidades/[slug]/manifest.json.ts +28 -0
  24. package/src/pages/utilidades/[slug].astro +90 -0
  25. package/src/pages/utilidades/categorias/[category].astro +38 -0
  26. package/src/tests/mfe_cache_contract.test.ts +14 -0
  27. package/src/tests/mfe_manifest.test.ts +28 -0
  28. package/src/types.ts +4 -6
  29. package/src/worker.ts +27 -0
  30. package/src/pages/[locale]/[slug].astro +0 -65
  31. package/src/pages/[locale].astro +0 -69
package/src/types.ts CHANGED
@@ -1,18 +1,16 @@
1
1
  import type { SEOSection } from '@jjlmoya/utils-shared';
2
+ import type { UtilityLocale } from '@jjlmoya/utils-shared/routing';
2
3
  import type { WithContext, Thing } from 'schema-dts';
3
4
 
4
5
  export type { SEOSection };
5
6
 
6
- export type KnownLocale =
7
- | 'ar' | 'da' | 'de' | 'en' | 'es' | 'fi'
8
- | 'fr' | 'id' | 'it' | 'ja' | 'ko' | 'nb' | 'nl'
9
- | 'pl' | 'pt' | 'ru' | 'sv' | 'tr' | 'zh';
7
+ export type KnownLocale = UtilityLocale;
10
8
 
11
9
  export interface FAQItem { question: string; answer: string; }
12
10
  export interface BibliographyEntry { name: string; url: string; }
13
11
  export interface HowToStep { name: string; text: string; }
14
12
 
15
- export interface ToolLocaleContent<TUI extends Record<string, string> = Record<string, string>> {
13
+ export interface ToolLocaleContent<TUI extends object = Record<string, string>> {
16
14
  slug: string;
17
15
  title: string;
18
16
  description: string;
@@ -34,7 +32,7 @@ export interface CategoryLocaleContent {
34
32
  export type LocaleLoader<T> = () => Promise<T>;
35
33
  export type LocaleMap<T> = Partial<Record<KnownLocale, LocaleLoader<T>>>;
36
34
 
37
- export interface AquariumToolEntry<TUI extends Record<string, string> = Record<string, string>> {
35
+ export interface AquariumToolEntry<TUI extends object = Record<string, string>> {
38
36
  id: string;
39
37
  icons: { bg: string; fg: string; };
40
38
  i18n: LocaleMap<ToolLocaleContent<TUI>>;
package/src/worker.ts ADDED
@@ -0,0 +1,27 @@
1
+ interface UtilityMfeEnvironment {
2
+ ASSETS: { fetch(request: Request): Promise<Response> };
3
+ }
4
+
5
+ export const LONG_LIVED_ASSET_CACHE = "public, max-age=31536000, immutable";
6
+ export const SITEMAP_CACHE = "public, max-age=3600, s-maxage=3600, must-revalidate";
7
+
8
+ export const getCacheControl = (pathname: string): string | undefined => {
9
+ if (pathname.endsWith("/sitemap.xml")) return SITEMAP_CACHE;
10
+ if (pathname.endsWith("/manifest.json")) return LONG_LIVED_ASSET_CACHE;
11
+ if (pathname.startsWith("/_utilities/")) {
12
+ return LONG_LIVED_ASSET_CACHE;
13
+ }
14
+ return undefined;
15
+ };
16
+
17
+ export default {
18
+ async fetch(request: Request, environment: UtilityMfeEnvironment): Promise<Response> {
19
+ const response = await environment.ASSETS.fetch(request);
20
+ const cacheControl = getCacheControl(new URL(request.url).pathname);
21
+ if (!cacheControl) return response;
22
+
23
+ const headers = new Headers(response.headers);
24
+ headers.set("Cache-Control", cacheControl);
25
+ return new Response(response.body, { status: response.status, headers });
26
+ },
27
+ };
@@ -1,65 +0,0 @@
1
- ---
2
- import PreviewLayout from '../../layouts/PreviewLayout.astro';
3
- import PreviewNavSidebar from '../../components/PreviewNavSidebar.astro';
4
- import { ALL_TOOLS } from '../../index';
5
- import { UtilityHeader, FAQSection, Bibliography, SEORenderer } from '@jjlmoya/utils-shared';
6
- import type { KnownLocale, ToolLocaleContent } from '../../types';
7
- import type { UtilitySEOContent } from '@jjlmoya/utils-shared';
8
-
9
- export async function getStaticPaths() {
10
- const paths = [];
11
- for (const { entry, Component: lazyComponent } of ALL_TOOLS) {
12
- const { default: Component } = await lazyComponent();
13
- const localeContents = await Promise.all(Object.entries(entry.i18n).map(async ([locale, loader]) => ({
14
- locale: locale as KnownLocale,
15
- content: await loader(),
16
- })));
17
- const localeUrls = Object.fromEntries(localeContents.map(({ locale, content }) => [locale, '/' + locale + '/' + content.slug]));
18
- for (const { locale, content } of localeContents) {
19
- const allToolsNav = (await Promise.all(ALL_TOOLS.map(async ({ entry: navEntry }) => {
20
- const loader = navEntry.i18n[locale] ?? navEntry.i18n.en;
21
- if (loader === undefined) return null;
22
- const navContent = await loader();
23
- return { id: navEntry.id, title: navContent.title, href: '/' + locale + '/' + navContent.slug, isActive: navEntry.id === entry.id };
24
- }))).filter((item): item is { id: string; title: string; href: string; isActive: boolean } => item !== null);
25
- const englishLoader = entry.i18n.en ?? Object.values(entry.i18n)[0];
26
- const englishSlug = englishLoader === undefined ? entry.id : (await englishLoader()).slug;
27
- paths.push({ params: { locale, slug: content.slug }, props: { Component, locale, content, localeUrls, allToolsNav, englishSlug } });
28
- }
29
- }
30
- return paths;
31
- }
32
-
33
- const { Component, locale, content, localeUrls, allToolsNav, englishSlug } = Astro.props as {
34
- Component: (_props: Record<string, unknown>) => unknown;
35
- locale: KnownLocale;
36
- content: ToolLocaleContent;
37
- localeUrls: Partial<Record<KnownLocale, string>>;
38
- allToolsNav: { id: string; title: string; href: string; isActive?: boolean }[];
39
- englishSlug: string;
40
- };
41
-
42
- const cssFiles = import.meta.glob('../../tool/**' + '/' + '*.css', { query: '?raw', import: 'default' });
43
- const cssKey = Object.keys(cssFiles).find((key) => key.endsWith('/' + englishSlug + '.css'));
44
- const cssLoader = cssKey === undefined ? undefined : cssFiles[cssKey];
45
- const toolCss = cssLoader === undefined ? '' : await cssLoader() as string;
46
- const seoContent: UtilitySEOContent = { locale, sections: content.seo };
47
- const words = content.title.split(' ');
48
- const titleHighlight = words[0] ?? '';
49
- const titleBase = words.slice(1).join(' ');
50
- ---
51
-
52
- <PreviewLayout title={content.title} currentLocale={locale} localeUrls={localeUrls} hasSidebar={true}>
53
- <PreviewNavSidebar slot="sidebar" categoryTitle="Aquarium tools" tools={allToolsNav} />
54
- <Fragment slot="head">
55
- {toolCss !== '' && <Fragment set:html={'<style is:inline>' + toolCss + '</style>'} />}
56
- {content.schemas.map((schema: unknown) => <script is:inline type="application/ld+json" set:html={JSON.stringify(schema)} />)}
57
- </Fragment>
58
- <div class="tool-page">
59
- <UtilityHeader titleHighlight={titleHighlight} titleBase={titleBase} description={content.description} />
60
- <section class="section-tool"><Component ui={content.ui} /></section>
61
- <section class="section-seo"><SEORenderer content={seoContent} /></section>
62
- <section class="section-faq"><FAQSection items={content.faq} /></section>
63
- <section class="section-bibliography"><Bibliography links={content.bibliography} /></section>
64
- </div>
65
- </PreviewLayout>
@@ -1,69 +0,0 @@
1
- ---
2
- import PreviewLayout from '../layouts/PreviewLayout.astro';
3
- import PreviewNavSidebar from '../components/PreviewNavSidebar.astro';
4
- import { aquariumCategory, ALL_TOOLS } from '../index';
5
- import { Icon } from 'astro-icon/components';
6
- import type { KnownLocale, ToolLocaleContent } from '../types';
7
-
8
- export async function getStaticPaths() {
9
- const locales = ['en'] as KnownLocale[];
10
- return locales.map((locale) => ({ params: { locale } }));
11
- }
12
-
13
- const currentLocale = (Astro.params.locale ?? 'en') as KnownLocale;
14
- const categoryLoader = aquariumCategory.i18n[currentLocale] ?? aquariumCategory.i18n.en;
15
- const categoryContent = categoryLoader === undefined ? {
16
- slug: 'aquarium',
17
- title: 'Aquarium Tools',
18
- description: 'Aquarium volume tools',
19
- seo: [],
20
- } : await categoryLoader();
21
-
22
- const toolsWithContent = await Promise.all(
23
- ALL_TOOLS.map(async ({ entry, Component }) => {
24
- const localeEntries = await Promise.all(
25
- Object.entries(entry.i18n).map(async ([locale, loader]) => [locale, await loader()] as const),
26
- );
27
- const localeContents = Object.fromEntries(localeEntries) as Record<string, ToolLocaleContent>;
28
- const locale = localeContents[currentLocale] ?? localeContents.en;
29
- const availableLocales: Record<string, string> = {};
30
- for (const [localeCode, content] of Object.entries(localeContents)) {
31
- availableLocales[localeCode] = '/' + localeCode + '/' + content.slug;
32
- }
33
- return { entry, Component, locale, availableLocales };
34
- }),
35
- );
36
- ---
37
-
38
- <PreviewLayout title={categoryContent.title} currentLocale={currentLocale} hasSidebar={true}>
39
- <PreviewNavSidebar
40
- slot="sidebar"
41
- categoryTitle={categoryContent.title}
42
- tools={toolsWithContent.map(({ entry, locale, availableLocales }) => ({
43
- id: entry.id,
44
- title: locale?.title ?? entry.id,
45
- href: availableLocales[currentLocale] ?? '#',
46
- }))}
47
- />
48
- <div class="dashboard">
49
- <header class="preview-header">
50
- <span class="badge">preview · @jjlmoya/utils-aquarium</span>
51
- <h1>{categoryContent.title}</h1>
52
- <p>{categoryContent.description}</p>
53
- </header>
54
- <div class="tool-list">
55
- {toolsWithContent.map(({ entry, locale, availableLocales }) => (
56
- <article class="tool-card">
57
- <a href={availableLocales[currentLocale] ?? '#'} class="tool-card-link">
58
- <div class="tool-icons"><div class="icon-wrapper bg"><Icon name={entry.icons.bg} /></div><div class="icon-wrapper fg"><Icon name={entry.icons.fg} /></div></div>
59
- <div class="tool-card-content"><h2 class="tool-title">{locale?.title}</h2><p class="tool-description">{locale?.description}</p></div>
60
- <div class="tool-card-meta"><span class="tool-id">{entry.id}</span></div>
61
- </a>
62
- <div class="tool-locales">
63
- {Object.entries(availableLocales).map(([localeCode, url]) => <a href={url} class:list={{ active: localeCode === currentLocale }} class="locale-badge">{localeCode.toUpperCase()}</a>)}
64
- </div>
65
- </article>
66
- ))}
67
- </div>
68
- </div>
69
- </PreviewLayout>