@asteroidcms/core-utils 0.1.7 → 0.1.8

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/dist/client.d.cts CHANGED
@@ -294,4 +294,348 @@ declare function extractHeadingsFromElement(root: HTMLElement, options?: Extract
294
294
  scrollMarginTop?: number;
295
295
  }): ExtractedHeading[];
296
296
 
297
- export { AsteroidCMSProvider, type AsteroidCMSProviderProps, type ExtractHeadingsOptions, type ExtractedHeading, type HeadingLevel, RichTextContent, type UseCmsContentOptions, type UseCmsMutateOptions, extractHeadingsFromElement, extractHeadingsFromHtml, slugify, useAsteroidCMSConfig, useCmsContent, useCmsImage, useCmsMutate };
297
+ type SeoClientProps = {
298
+ title: string;
299
+ description: string;
300
+ url: string;
301
+ siteName?: string;
302
+ keywords?: string;
303
+ twitter?: string;
304
+ image?: string;
305
+ noindex?: boolean;
306
+ };
307
+ /** Theme colors used when generating dynamic OG images. */
308
+ interface AsteroidOgImagePalette {
309
+ background: string;
310
+ foreground: string;
311
+ accent: string;
312
+ accentMuted?: string;
313
+ mutedText?: string;
314
+ }
315
+ interface AsteroidOgImageParams {
316
+ title: string;
317
+ subtitle?: string;
318
+ eyebrow?: string;
319
+ type?: "article" | "listing";
320
+ }
321
+ /** Site-level identity used to build the Organization/WebSite JSON-LD graph. */
322
+ interface AsteroidOrganizationConfig {
323
+ logoUrl?: string;
324
+ contactEmail?: string;
325
+ contactPhone?: string;
326
+ address?: {
327
+ street?: string;
328
+ city?: string;
329
+ country?: string;
330
+ };
331
+ /** Generic list of profile URLs (LinkedIn, X, etc.). Maps to schema.org `sameAs`. */
332
+ socials?: string[];
333
+ }
334
+ /** Site-level SEO config passed into the headless Asteroid content components. */
335
+ interface AsteroidSeoConfig {
336
+ siteName: string;
337
+ baseUrl: string;
338
+ twitter?: string;
339
+ defaultDescription?: string;
340
+ defaultKeywords?: string;
341
+ /** Path prefix for the article collection (e.g. `/blog`, `/news`, `/docs`). Default: `/blog` */
342
+ articlePath?: string;
343
+ /**
344
+ * Human label for the article collection, used in default titles/eyebrows
345
+ * (e.g. "Blog", "News", "Documentation"). Default: `"Articles"`.
346
+ */
347
+ contentLabel?: string;
348
+ /**
349
+ * Override how a raw page/article title becomes the final `<title>`.
350
+ * Default: `(title) => `${title} | ${siteName}``.
351
+ */
352
+ titleTemplate?: (title: string) => string;
353
+ ogImage?: {
354
+ palette: AsteroidOgImagePalette;
355
+ /** OG image API route path. Default: `/api/og` */
356
+ apiPath?: string;
357
+ };
358
+ /** Override dynamic OG image URL generation (e.g. custom CDN or renderer). */
359
+ getOgImageUrl?: (params: AsteroidOgImageParams) => string | undefined;
360
+ /** Site identity for the Organization/WebSite JSON-LD graph. */
361
+ organization?: AsteroidOrganizationConfig;
362
+ /** Extra schema.org @graph nodes (e.g. a ProfessionalService node). */
363
+ extraJsonLdNodes?: object[];
364
+ /** CMS base URL for resolving featured-image URLs. Required server-side for featured images. */
365
+ cmsUrl?: string;
366
+ /** URL to a PWA manifest.json. When set, a `<link rel="manifest">` is emitted. */
367
+ manifestUrl?: string;
368
+ /** Site-wide default robots policy. Per-page overrides are available on builder options. */
369
+ noindex?: boolean;
370
+ }
371
+
372
+ /** Render a JSON-LD <script>. Pass a built graph via `data`; renders nothing if absent. */
373
+ declare function JsonLd({ data }: {
374
+ data?: object;
375
+ }): react_jsx_runtime.JSX.Element | null;
376
+ /**
377
+ * Imperatively sets document title, meta description, canonical URL and social
378
+ * tags. Works in plain React apps and Next.js client components. In a Next.js
379
+ * App Router app, prefer server `generateMetadata` from the `/next` entry.
380
+ */
381
+ declare function Seo({ title, description, url, siteName, keywords, twitter, image, noindex, }: SeoClientProps): null;
382
+
383
+ /** schema.org Article subtypes; pick per content kind. */
384
+ type ArticleJsonLdType = "Article" | "BlogPosting" | "NewsArticle" | "TechArticle" | (string & {});
385
+
386
+ interface AsteroidArticlePagePost {
387
+ slug: string;
388
+ title: string;
389
+ description?: string;
390
+ content?: string;
391
+ featured_image?: string;
392
+ tags?: string;
393
+ published_date?: string | null;
394
+ category?: {
395
+ slug: string;
396
+ name: string;
397
+ };
398
+ author?: {
399
+ name: string;
400
+ bio?: string;
401
+ };
402
+ }
403
+ type AsteroidArticlePageUseArticleResult<TPost extends AsteroidArticlePagePost = AsteroidArticlePagePost> = {
404
+ data?: TPost | null;
405
+ loading: boolean;
406
+ error?: unknown;
407
+ };
408
+ type AsteroidArticlePageUseArticle<TPost extends AsteroidArticlePagePost = AsteroidArticlePagePost> = (slug: string) => AsteroidArticlePageUseArticleResult<TPost>;
409
+ interface AsteroidArticlePageProps<TPost extends AsteroidArticlePagePost = AsteroidArticlePagePost> {
410
+ /** Article post slug to fetch. */
411
+ slug: string;
412
+ /** Inject the app-specific CMS fetching hook. */
413
+ useArticle: AsteroidArticlePageUseArticle<TPost>;
414
+ /** Site SEO config - enables client-side meta tags in React and Next.js apps. */
415
+ seo?: AsteroidSeoConfig;
416
+ /** schema.org Article subtype for the built-in JSON-LD. Default: "Article". */
417
+ articleType?: ArticleJsonLdType;
418
+ /** Mark this page as `noindex` for search engines. */
419
+ noindex?: boolean;
420
+ /** "Back to article" link. */
421
+ backLink?: ReactNode;
422
+ /** Wrap the entire component output. */
423
+ renderRoot?: (params: {
424
+ children: ReactNode;
425
+ }) => ReactNode;
426
+ /** Loading skeleton placeholder. */
427
+ renderSkeleton?: () => ReactNode;
428
+ /** Error or not-found state. */
429
+ renderError?: (params: {
430
+ error?: unknown;
431
+ reason: "error" | "not-found";
432
+ }) => ReactNode;
433
+ /** Article post title / heading area. */
434
+ renderHeader?: (params: {
435
+ post: TPost;
436
+ }) => ReactNode;
437
+ /** Article post metadata line (date, author, reading time, category chip). */
438
+ renderMeta?: (params: {
439
+ post: TPost;
440
+ }) => ReactNode;
441
+ /** Article post description paragraph. */
442
+ renderDescription?: (params: {
443
+ post: TPost;
444
+ }) => ReactNode;
445
+ /** Featured / hero image. */
446
+ renderFeaturedImage?: (params: {
447
+ post: TPost;
448
+ }) => ReactNode;
449
+ /** Table of contents section. */
450
+ renderToc?: (params: {
451
+ post: TPost;
452
+ }) => ReactNode;
453
+ /** Main rich-text content body. */
454
+ renderContent?: (params: {
455
+ post: TPost;
456
+ }) => ReactNode;
457
+ /** Top advertisement slot. */
458
+ renderPreArticle?: (params: {
459
+ post: TPost;
460
+ }) => ReactNode;
461
+ /** Middle advertisement slot. */
462
+ renderMidArticle?: (params: {
463
+ post: TPost;
464
+ }) => ReactNode;
465
+ /** Bottom advertisement slot. */
466
+ renderPostArticle?: (params: {
467
+ post: TPost;
468
+ }) => ReactNode;
469
+ /** Tags section. */
470
+ renderTags?: (params: {
471
+ post: TPost;
472
+ }) => ReactNode;
473
+ /** Author details section. */
474
+ renderAuthorDetails?: (params: {
475
+ post: TPost;
476
+ }) => ReactNode;
477
+ /** Related / similar posts section. */
478
+ renderRelatedPosts?: (params: {
479
+ post: TPost;
480
+ }) => ReactNode;
481
+ /** Call-to-action section. */
482
+ renderCTA?: (params: {
483
+ post: TPost;
484
+ }) => ReactNode;
485
+ /** Override the built-in Article JSON-LD structured data. */
486
+ renderJsonLd?: (params: {
487
+ post: TPost;
488
+ }) => ReactNode;
489
+ /**
490
+ * Escape hatch - receive the full result and render everything yourself.
491
+ * When provided, structural render props above are ignored.
492
+ */
493
+ children?: (state: AsteroidArticlePageUseArticleResult<TPost>) => ReactNode;
494
+ }
495
+ declare function AsteroidArticlePage<TPost extends AsteroidArticlePagePost = AsteroidArticlePagePost>(props: AsteroidArticlePageProps<TPost>): string | number | boolean | Iterable<ReactNode> | react_jsx_runtime.JSX.Element | null | undefined;
496
+
497
+ /** Minimal article post shape shared across Asteroid CMS apps. */
498
+ interface AsteroidArticlePost {
499
+ slug: string;
500
+ title: string;
501
+ description?: string;
502
+ featured_image?: string;
503
+ featured?: boolean;
504
+ published_date?: string | null;
505
+ category?: {
506
+ slug: string;
507
+ name: string;
508
+ description?: string;
509
+ };
510
+ }
511
+ interface AsteroidArticleCategoryGroup<TPost extends AsteroidArticlePost = AsteroidArticlePost> {
512
+ categoryName: string;
513
+ categorySlug: string;
514
+ posts: TPost[];
515
+ }
516
+ interface AsteroidArticlesUsePostsResult<TPost extends AsteroidArticlePost = AsteroidArticlePost> {
517
+ posts: TPost[];
518
+ featured?: TPost | null;
519
+ rest: TPost[];
520
+ loading: boolean;
521
+ error?: unknown;
522
+ }
523
+ type AsteroidArticlesUsePosts<TPost extends AsteroidArticlePost = AsteroidArticlePost> = (searchQuery: string) => AsteroidArticlesUsePostsResult<TPost>;
524
+ interface AsteroidArticlesSearchParams {
525
+ value: string;
526
+ onChange: (value: string) => void;
527
+ onSubmit: (event: React.FormEvent) => void;
528
+ }
529
+ interface AsteroidArticlesHeaderParams {
530
+ eyebrow: ReactNode;
531
+ title: ReactNode;
532
+ description?: ReactNode;
533
+ search: ReactNode;
534
+ }
535
+ interface AsteroidArticlesFeaturedCardParams<TPost extends AsteroidArticlePost = AsteroidArticlePost> {
536
+ post: TPost;
537
+ }
538
+ interface AsteroidArticlesPostCardParams<TPost extends AsteroidArticlePost = AsteroidArticlePost> {
539
+ post: TPost;
540
+ index: number;
541
+ group: AsteroidArticleCategoryGroup<TPost>;
542
+ }
543
+ interface AsteroidArticlesPostGridParams<TPost extends AsteroidArticlePost = AsteroidArticlePost> {
544
+ posts: TPost[];
545
+ group: AsteroidArticleCategoryGroup<TPost>;
546
+ /** Pre-built post cards - render as-is or wrap. */
547
+ children: ReactNode;
548
+ }
549
+ interface AsteroidArticlesCategoryHeadingParams<TPost extends AsteroidArticlePost = AsteroidArticlePost> {
550
+ group: AsteroidArticleCategoryGroup<TPost>;
551
+ }
552
+ interface AsteroidArticlesCategoryGroupParams<TPost extends AsteroidArticlePost = AsteroidArticlePost> {
553
+ group: AsteroidArticleCategoryGroup<TPost>;
554
+ /** Default heading + grid - use it, wrap it, or replace entirely. */
555
+ defaultContent: ReactNode;
556
+ }
557
+ type AsteroidArticlesEmptyReason = "error" | "no-posts" | "no-results";
558
+ interface AsteroidArticlesEmptyParams {
559
+ reason: AsteroidArticlesEmptyReason;
560
+ searchQuery?: string;
561
+ error?: unknown;
562
+ }
563
+ interface AsteroidArticlesContentParams {
564
+ featured: ReactNode;
565
+ groups: ReactNode;
566
+ noSearchResults: ReactNode;
567
+ }
568
+ interface AsteroidArticlesState<TPost extends AsteroidArticlePost = AsteroidArticlePost> {
569
+ posts: TPost[];
570
+ featured: TPost | null | undefined;
571
+ rest: TPost[];
572
+ categoryGroups: AsteroidArticleCategoryGroup<TPost>[];
573
+ loading: boolean;
574
+ error: unknown;
575
+ hasError: boolean;
576
+ isEmpty: boolean;
577
+ isSearching: boolean;
578
+ searchQuery: string;
579
+ debouncedSearchQuery: string;
580
+ setSearchQuery: (value: string) => void;
581
+ }
582
+ interface AsteroidArticlesProps<TPost extends AsteroidArticlePost = AsteroidArticlePost> {
583
+ /** Inject app-specific CMS fetching. Receives the debounced search query. */
584
+ usePosts: AsteroidArticlesUsePosts<TPost>;
585
+ /** Limit listing to a single category slug. */
586
+ categorySlug?: string;
587
+ /** Limit listing to a single post slug. */
588
+ articleSlug?: string;
589
+ /** Debounce delay for search in ms. Default: 800 */
590
+ searchDebounceMs?: number;
591
+ /** Site SEO config - enables client-side meta tags in React and Next.js apps. */
592
+ seo?: AsteroidSeoConfig;
593
+ /** Mark this page as `noindex` for search engines. */
594
+ noindex?: boolean;
595
+ eyebrow?: ReactNode;
596
+ title?: ReactNode;
597
+ description?: ReactNode;
598
+ /** Wrap the entire component output. */
599
+ renderRoot?: (params: {
600
+ children: ReactNode;
601
+ }) => ReactNode;
602
+ /** Replace the default header layout (eyebrow + title + description + search). */
603
+ renderHeader?: (params: AsteroidArticlesHeaderParams) => ReactNode;
604
+ /** Replace the search control. */
605
+ renderSearch?: (params: AsteroidArticlesSearchParams) => ReactNode;
606
+ /** Featured hero card. Return `null` to suppress. Hidden while searching. */
607
+ renderFeaturedCard?: (params: AsteroidArticlesFeaturedCardParams<TPost>) => ReactNode;
608
+ /** Individual post card inside a grid. */
609
+ renderPostCard: (params: AsteroidArticlesPostCardParams<TPost>) => ReactNode;
610
+ /** Category section heading. */
611
+ renderCategoryHeading?: (params: AsteroidArticlesCategoryHeadingParams<TPost>) => ReactNode;
612
+ /** Grid wrapper for post cards within a category. */
613
+ renderPostGrid?: (params: AsteroidArticlesPostGridParams<TPost>) => ReactNode;
614
+ /**
615
+ * Full category section (heading + grid).
616
+ * Receives `defaultContent` so you can wrap rather than fully replace.
617
+ */
618
+ renderCategoryGroup?: (params: AsteroidArticlesCategoryGroupParams<TPost>) => ReactNode;
619
+ /** Loading state. */
620
+ renderSkeleton?: () => ReactNode;
621
+ /** Error, empty feed, or zero search results. Return `null` to suppress. */
622
+ renderEmpty?: (params: AsteroidArticlesEmptyParams) => ReactNode;
623
+ /** Wrap featured card + category groups. */
624
+ renderContent?: (params: AsteroidArticlesContentParams) => ReactNode;
625
+ /** Override the built-in CollectionPage JSON-LD structured data. */
626
+ renderJsonLd?: (params: AsteroidArticlesState<TPost>) => ReactNode;
627
+ /**
628
+ * Escape hatch - receive full computed state and render everything yourself.
629
+ * When provided, structural render props above are ignored (slot render props
630
+ * like `renderPostCard` are still used if you call them manually).
631
+ */
632
+ children?: (state: AsteroidArticlesState<TPost>) => ReactNode;
633
+ /** Custom category grouping. */
634
+ groupPostsByCategory?: (posts: TPost[]) => AsteroidArticleCategoryGroup<TPost>[];
635
+ }
636
+ declare function defaultGetCategoryName(post: Pick<AsteroidArticlePost, "category">): string | undefined;
637
+ declare function defaultGroupPostsByCategory<TPost extends AsteroidArticlePost = AsteroidArticlePost>(posts: TPost[]): AsteroidArticleCategoryGroup<TPost>[];
638
+ declare function useAsteroidArticlesState<TPost extends AsteroidArticlePost = AsteroidArticlePost>({ usePosts, categorySlug, articleSlug, searchDebounceMs, groupPostsByCategory, }: Pick<AsteroidArticlesProps<TPost>, "usePosts" | "categorySlug" | "articleSlug" | "searchDebounceMs" | "groupPostsByCategory">): AsteroidArticlesState<TPost>;
639
+ declare function AsteroidArticlesListing<TPost extends AsteroidArticlePost = AsteroidArticlePost>(props: AsteroidArticlesProps<TPost>): string | number | boolean | Iterable<ReactNode> | react_jsx_runtime.JSX.Element | null | undefined;
640
+
641
+ export { type AsteroidArticleCategoryGroup, AsteroidArticlePage, type AsteroidArticlePagePost, type AsteroidArticlePageProps, type AsteroidArticlePageUseArticle, type AsteroidArticlePageUseArticleResult, type AsteroidArticlePost, AsteroidArticlesListing, type AsteroidArticlesProps, type AsteroidArticlesState, type AsteroidArticlesUsePosts, type AsteroidArticlesUsePostsResult, AsteroidCMSProvider, type AsteroidCMSProviderProps, type ExtractHeadingsOptions, type ExtractedHeading, type HeadingLevel, JsonLd, RichTextContent, Seo, type UseCmsContentOptions, type UseCmsMutateOptions, defaultGetCategoryName, defaultGroupPostsByCategory, extractHeadingsFromElement, extractHeadingsFromHtml, slugify, useAsteroidArticlesState, useAsteroidCMSConfig, useCmsContent, useCmsImage, useCmsMutate };