@acms-subir/core 0.3.25 → 0.3.26
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 +6 -0
- package/package.json +1 -1
- package/src/lib/content.ts +6 -0
- package/src/lib/mobile/schemas.ts +1 -1
- package/src/lib/mobile/site.ts +5 -2
- package/src/middleware.ts +2 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @acms-subir/core
|
|
2
2
|
|
|
3
|
+
## 0.3.26
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 89ecf3c: Apps can now follow a collection's old addresses. Each `features.blog` entry in the app API's `/api/v1/site` (and the client's `Site` type) gains `redirectFrom`: the absolute addresses the website 301s to that collection's `url`. It lists the collection's `redirectFrom` bases, plus `/blog` for `posts` once its base moved away from that default, so on a site whose posts live at `/sermon`, an app can open a `/blog` link as the sermons list instead of looking for a page called "blog". The list comes from the same place as the website's redirects, so the two can't drift apart. An address another collection lives at is left out, so a site with sermons at `/sermon` and its own blog at `/blog` keeps `/blog` for the blog.
|
|
8
|
+
|
|
3
9
|
## 0.3.25
|
|
4
10
|
|
|
5
11
|
### Patch Changes
|
package/package.json
CHANGED
package/src/lib/content.ts
CHANGED
|
@@ -33,6 +33,12 @@ export const collectionUrl = (key: string, slug?: string) => {
|
|
|
33
33
|
return slug ? `${base}/${slug}` : base;
|
|
34
34
|
};
|
|
35
35
|
|
|
36
|
+
// Old bases a collection 301s from (middleware.ts), also listed in the app
|
|
37
|
+
// API's site features so apps can follow them: its redirectFrom, plus '/blog'
|
|
38
|
+
// for the built-in collection (its historic default) once it moved away.
|
|
39
|
+
export const legacyBases = (key: string, c: CollectionConfig) =>
|
|
40
|
+
[...new Set([...(key === 'posts' ? ['/blog'] : []), ...c.redirectFrom])].filter((from) => from !== c.base);
|
|
41
|
+
|
|
36
42
|
// Which collection a public URL belongs to — injected routes share one page
|
|
37
43
|
// file per shape (blog/index.astro, blog/[slug].astro), and Astro's
|
|
38
44
|
// injectRoute can't attach props, so pages resolve their collection from the
|
|
@@ -8,7 +8,7 @@ const Palette = z.object({ bg: str, ink: str, muted: str, line: str, accent: str
|
|
|
8
8
|
export const Site = z.object({
|
|
9
9
|
name: str, description: str, logo: str, logoDark: str, icon: str, menu: z.array(z.object({ label: str, href: str })),
|
|
10
10
|
languages: z.object({ default: locale, locales: z.array(z.object({ code: str, name: str })) }),
|
|
11
|
-
features: z.object({ blog: z.array(z.object({ key: str, name: str, url: str })), calendar: bool, bookings: bool, search: bool, bookingChanges: bool, guestSignIn: bool, forms: bool, links: bool }), apiVersion: z.literal(1),
|
|
11
|
+
features: z.object({ blog: z.array(z.object({ key: str, name: str, url: str, redirectFrom: strings })), calendar: bool, bookings: bool, search: bool, bookingChanges: bool, guestSignIn: bool, forms: bool, links: bool }), apiVersion: z.literal(1),
|
|
12
12
|
});
|
|
13
13
|
export const Theme = z.object({
|
|
14
14
|
id: str, name: str, scheme: z.enum(['auto', 'light', 'dark']), colors: z.object({ light: Palette, dark: Palette }),
|
package/src/lib/mobile/site.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { ContentRepository } from '../repo/types';
|
|
3
|
-
import { CONTENT } from '../content';
|
|
3
|
+
import { CONTENT, legacyBases } from '../content';
|
|
4
4
|
import { I18N, localeName } from '../i18n';
|
|
5
5
|
import { CALENDAR, bookingsEnabled } from '../calendar/config';
|
|
6
6
|
import { SEARCH } from '../search-config';
|
|
@@ -15,6 +15,7 @@ export async function getSite(repo: ContentRepository, url: URL, _id = '', local
|
|
|
15
15
|
z.object({}).strict().parse(queryParams(url));
|
|
16
16
|
const [settings, nav, pages] = await Promise.all([repo.getSettings(), repo.listNavItems(), repo.listPages()]);
|
|
17
17
|
const origin = siteOrigin(settings, url);
|
|
18
|
+
const bases = new Set(Object.values(CONTENT.collections).map(c => c.base));
|
|
18
19
|
const header = nav.filter(n => n.area === 'header').sort((a, b) => a.order - b.order);
|
|
19
20
|
const menu = header.length ? header : [
|
|
20
21
|
{ label: 'Home', url: '/' },
|
|
@@ -28,7 +29,9 @@ export async function getSite(repo: ContentRepository, url: URL, _id = '', local
|
|
|
28
29
|
menu: menu.map(n => ({ label: n.label, href: safeUrl(n.url, origin, true) })),
|
|
29
30
|
languages: { default: I18N.defaultLocale || null, locales: I18N.locales.map(code => ({ code, name: localeName(code, true) })) },
|
|
30
31
|
features: {
|
|
31
|
-
|
|
32
|
+
// redirectFrom: old addresses the website 301s to url, so apps can follow them too,
|
|
33
|
+
// minus any another collection now lives at (the website serves that one first)
|
|
34
|
+
blog: Object.entries(CONTENT.collections).map(([key, c]) => ({ key, name: c.plural, url: safeUrl(c.base, origin), redirectFrom: legacyBases(key, c).filter(from => !bases.has(from)).map(from => safeUrl(from, origin)) })), calendar: CALENDAR.enabled, bookings: bookingsEnabled(), search: SEARCH.enabled,
|
|
32
35
|
// Admin → Mobile app → App access
|
|
33
36
|
bookingChanges: CALENDAR.enabled && settings.mobileAccess.bookingChanges, guestSignIn: await guestSignInReady(repo, locals),
|
|
34
37
|
forms: settings.mobileAccess.forms, links: settings.mobileAccess.links,
|
package/src/middleware.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { bindAuthEnvironment, getRequestAuth, requestPerson, shouldResolveClerk,
|
|
|
5
5
|
import { roleHome } from './lib/roles';
|
|
6
6
|
import { AUTH } from './lib/auth/config';
|
|
7
7
|
import { getRepo } from './lib/repo';
|
|
8
|
-
import { CONTENT } from './lib/content';
|
|
8
|
+
import { CONTENT, legacyBases } from './lib/content';
|
|
9
9
|
import { processDueDeliveries, waitUntilFrom } from './lib/webhooks';
|
|
10
10
|
import { dispatchEmail } from './lib/email/service';
|
|
11
11
|
import { decideOverdue, sendDueReminders } from './lib/booking/service';
|
|
@@ -28,10 +28,7 @@ const TAG_SOURCES = [
|
|
|
28
28
|
]),
|
|
29
29
|
];
|
|
30
30
|
const COLLECTION_SOURCES: Array<{ from: string; to: string }> = Object.entries(CONTENT.collections).flatMap(
|
|
31
|
-
([key, c]) =>
|
|
32
|
-
[...new Set([...(key === 'posts' ? ['/blog'] : []), ...c.redirectFrom])]
|
|
33
|
-
.filter((from) => from !== c.base)
|
|
34
|
-
.map((from) => ({ from, to: c.base }))
|
|
31
|
+
([key, c]) => legacyBases(key, c).map((from) => ({ from, to: c.base }))
|
|
35
32
|
);
|
|
36
33
|
|
|
37
34
|
export function legacyContentRedirect(pathname: string): string | null {
|