@acms-subir/core 0.3.11 → 0.3.12

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,11 @@
1
1
  # @acms-subir/core
2
2
 
3
+ ## 0.3.12
4
+
5
+ ### Patch Changes
6
+
7
+ - 8e70593: Expose upcomingEvents(Astro, options), calendarConfig(), and eventUrl() through the public theme SDK so coded templates can load calendar events without importing internal repository modules. Include public option/result types and migration documentation.
8
+
3
9
  ## 0.3.11
4
10
 
5
11
  ### Patch Changes
@@ -34,6 +34,31 @@ const { page, posts } = Astro.props;
34
34
 
35
35
  Public types: ChromeProps (site), HomeProps (settings, page, posts, tags), PageProps (settings, page, html), PostProps (settings, post, html, authors, related), EventProps (settings, event, occurrence, calendars, html, upcoming, origin, preview, rsvp), SectionProps (settings, page, variant, data). Public components: PostCard with card/row/feature variants, Pager with base/page/count, TagLabel with name/href/accent. Helpers include collectionUrl, postUrl, postsIndexUrl, tagUrl, formatDate, slugify, authorSlug, localeFromPath, localeHomeUrl and CONTENT. Use collectionUrl for non-default collections; postUrl is the built-in posts alias.
36
36
 
37
+ ### Upcoming events in coded templates
38
+
39
+ Use the server-side calendar helpers from the public theme entry point. Replace imports from `@acms-subir/core/lib/repo/*` and `@acms-subir/core/lib/calendar/*`; internal folders are not package exports.
40
+
41
+ ```astro
42
+ ---
43
+ import { upcomingEvents, calendarConfig, eventUrl } from '@acms-subir/core/theme';
44
+ const calendar = calendarConfig();
45
+ const upcoming = await upcomingEvents(Astro, { limit: 4, horizonDays: 90 });
46
+ const date = new Intl.DateTimeFormat('en', { dateStyle: 'medium', timeZone: Astro.props.settings.timezone || 'UTC' });
47
+ ---
48
+ {calendar.enabled && <section>
49
+ <a href={calendar.base}>Calendar</a>
50
+ {upcoming.map(({ event, start }) => (
51
+ <a href={eventUrl(event.slug)}>
52
+ {event.title} <time datetime={new Date(start).toISOString()}>{date.format(start)}</time>
53
+ </a>
54
+ ))}
55
+ </section>}
56
+ ```
57
+
58
+ `calendarConfig()` returns a fresh `{ enabled, base, eventsBase }` object; `eventUrl(slug)` uses the configured event base. `upcomingEvents(Astro, options?)` resolves storage through `Astro.locals` and loads the site's timezone settings. It returns the next or ongoing occurrence of each distinct published, publicly listed native event, soonest first, including recurring and all-day events. Private/unlisted calendars and external feed entries are excluded; cancelled public events remain visible so templates can label them. Disabled calendars and a zero limit return `[]` without accessing storage. Storage errors propagate to the template.
59
+
60
+ Options default to `{ limit: 6, horizonDays: 400 }`. The limit must be a non-negative safe integer and the horizon a positive finite number. Each `UpcomingEvent` contains `{ event, start, end, allDay, days }`, with start/end in Unix milliseconds. Format occurrence timestamps using the site's timezone rather than the authored `event.start` when displaying recurring events. `UpcomingEvent`, `UpcomingEventsOptions`, and `ThemeCalendarConfig` are exported types.
61
+
37
62
  Template filenames: `home.astro`, `event.astro`, `header.astro`, `footer.astro`, `post-<name>.astro`, `page-<name>.astro`.
38
63
 
39
64
  `header.astro` and `footer.astro` replace the site header and footer on every public page. They receive `ChromeProps` — one `site` prop (`SiteChrome`) with everything already resolved for the request: `title`, `tagline`, `logo`, `logoDark`, `homeUrl`, `links` (header nav, each `{ label, url, current, external }`, already localized), `footerLinks`, `social` (with inline SVG `icon`), `readLinks`, `footerHtml`, `languages` (`{ code, label, url, current }`, where `label` is the language's own name, e.g. "Español"), `search` (`{ url, label }` or null), `schemeToggle`, `liveHeader`, `headerStyle`, `footerStyle`. Core behaviors attach through data hooks, so keep them on your own elements: `data-search-open` (opens search), `data-scheme-toggle` (light/dark), `data-menu-open` (phone menu sheet, built from `site.links`) and a hidden `data-live-header` link (shown during a live stream).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@acms-subir/core",
3
- "version": "0.3.11",
3
+ "version": "0.3.12",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -0,0 +1,48 @@
1
+ import { CALENDAR } from '../lib/calendar/config';
2
+ import type { Occurrence } from '../lib/calendar/events';
3
+
4
+ export { eventUrl } from '../lib/calendar/config';
5
+
6
+ export interface ThemeCalendarConfig {
7
+ enabled: boolean;
8
+ base: string;
9
+ eventsBase: string;
10
+ }
11
+
12
+ /** Public calendar routes configured by the site's integration. */
13
+ export function calendarConfig(): ThemeCalendarConfig {
14
+ const { enabled, base, eventsBase } = CALENDAR;
15
+ return { enabled, base, eventsBase };
16
+ }
17
+
18
+ export interface UpcomingEventsOptions {
19
+ /** Maximum distinct events; defaults to 6. Zero returns an empty list. */
20
+ limit?: number;
21
+ /** Look-ahead window in days; defaults to 400. */
22
+ horizonDays?: number;
23
+ }
24
+
25
+ /** Occurrence start/end are Unix milliseconds; event retains its authored fields. */
26
+ export type UpcomingEvent = Pick<Occurrence, 'event' | 'start' | 'end' | 'allDay' | 'days'>;
27
+
28
+ /** Server-side helper for Astro templates and endpoints. No repository access needed. */
29
+ export async function upcomingEvents(
30
+ context: { locals: unknown },
31
+ { limit = 6, horizonDays = 400 }: UpcomingEventsOptions = {},
32
+ ): Promise<UpcomingEvent[]> {
33
+ if (!Number.isSafeInteger(limit) || limit < 0) {
34
+ throw new RangeError('upcomingEvents limit must be a non-negative safe integer');
35
+ }
36
+ if (!Number.isFinite(horizonDays) || horizonDays <= 0) {
37
+ throw new RangeError('upcomingEvents horizonDays must be a positive finite number');
38
+ }
39
+ if (!CALENDAR.enabled || limit === 0) return [];
40
+
41
+ const [{ getRepo }, { upcomingEvents: loadUpcomingEvents }] = await Promise.all([
42
+ import('../lib/repo'),
43
+ import('../lib/calendar/cards'),
44
+ ]);
45
+ const repo = await getRepo(context.locals);
46
+ const settings = await repo.getSettings();
47
+ return loadUpcomingEvents(repo, settings, Date.now(), { limit, horizonDays });
48
+ }
@@ -1,5 +1,7 @@
1
1
  export { ThemeTokens, ThemeManifest, PostOptions, SectionDefinition, SectionInstance, SectionLayout, SectionPages, PagePreset, BUILTIN_SECTIONS, sectionDefinitions, sectionIssues, THEME_API_VERSION } from './contract.mjs';
2
2
  export { formatTime, formatDay } from './fields.mjs';
3
+ export { upcomingEvents, calendarConfig, eventUrl } from './calendar';
4
+ export type { UpcomingEventsOptions, UpcomingEvent, ThemeCalendarConfig } from './calendar';
3
5
  export { CONTENT, postsIndexUrl, postUrl, tagUrl, collectionUrl } from '../lib/content';
4
6
  export { formatDate } from '../lib/dates';
5
7
  export type { Page, Post, Settings, Tag, CalEvent, Calendar, User } from '../lib/schema';