@se-studio/skills 1.0.41 → 1.1.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.
- package/CHANGELOG.md +59 -0
- package/package.json +1 -1
- package/skills/contentful-cms-core/SKILL.md +20 -0
- package/skills/se-marketing-sites-lib-cms-structure/SKILL.md +17 -0
- package/skills/se-marketing-sites-redirects/SKILL.md +232 -0
- package/skills/site-workflows-contentful-vercel-setup/SKILL.md +15 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,64 @@
|
|
|
1
1
|
# @se-studio/skills
|
|
2
2
|
|
|
3
|
+
## 1.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 31b2200: Add first-class support for managing redirects from Contentful.
|
|
8
|
+
|
|
9
|
+
Editors can now create `redirect` entries (via new content type) using reference pickers for existing Pages/Articles/etc. (for "from" and "to") or raw paths/URLs, plus a status code dropdown (301/302/etc.), active flag, and notes. This makes redirects easy for non-technical users without requiring them to know or type exact slugs.
|
|
10
|
+
|
|
11
|
+
Redirects use the simple rebuild pattern (already proven by A/B tests):
|
|
12
|
+
|
|
13
|
+
- Publish in Contentful → webhook triggers a Vercel Deploy Hook.
|
|
14
|
+
- Build fetches current published redirects (via `getRedirectsWithErrors` / `getRedirectMap` on the helpers from `createAppHelpers`) and bakes a static map (using the new pure `buildRedirectMap`).
|
|
15
|
+
- Middleware performs exact (or lightly normalized) lookups against the baked data with zero runtime cost or external calls.
|
|
16
|
+
|
|
17
|
+
Key additions:
|
|
18
|
+
|
|
19
|
+
- `IRedirect` type + `isRedirect` guard.
|
|
20
|
+
- `BaseRedirectSkeleton`, `baseRedirectConverter` (resolves reference links to concrete `fromPath`/`to` using existing `resolveLink` + project `urlCalculators`).
|
|
21
|
+
- `contentfulRedirectsRest`, revalidation `RedirectTag`, and `buildRedirectMap` / `RedirectMap` types (exported from `@se-studio/contentful-rest-api`).
|
|
22
|
+
- `getRedirectsWithErrors` and `getRedirectMap` (convenience) exposed additively via `createAppHelpers` (like banners).
|
|
23
|
+
- Two Contentful migration scripts: `18-create-redirect-content-type.js` (full CT with rich help text for the picker UX) and `19-remove-redirectTo-fields.js` (cleans up legacy per-page `redirectTo` fields on page/pageVariant).
|
|
24
|
+
- New `@se-studio/skills` entry `se-marketing-sites-redirects` (the canonical guide, including CT spec, migration instructions, generate script example, static middleware example + A/B composition, deploy hook webhook setup, testing, and preview notes).
|
|
25
|
+
- Minimal demo in `example-empty` (middleware + `/api/redirects` route exercising the new helpers at runtime for easy local verification; production apps follow the baked recipe in the skill).
|
|
26
|
+
- Light updates to CLAUDE.md, vercel-setup skill, CONTENT_MODEL.md, and related docs.
|
|
27
|
+
|
|
28
|
+
The model and helpers are forward-compatible if a faster (e.g. Edge Config) path is added later. Legacy `redirectTo` fields are left untouched for now (removal is optional via the migration).
|
|
29
|
+
|
|
30
|
+
This is a new feature (minor bumps).
|
|
31
|
+
|
|
32
|
+
### Patch Changes
|
|
33
|
+
|
|
34
|
+
- Complete support and documentation for filtered full article lists (#51) + fix client boundary regression (#52).
|
|
35
|
+
|
|
36
|
+
- Re-export `filterRelatedArticles` and `RelatedArticlesOptions` **only** from `@se-studio/core-ui/server` (removed from the main barrel). This keeps the primary `@se-studio/core-ui` entry fully client-safe for common imports (`cn`, `TrackedLink`, `AnalyticsProvider`, `Visual`, etc.). The main barrel no longer transitively pulls `'server-only'`.
|
|
37
|
+
|
|
38
|
+
- `getAllArticleLinks(options)` now fully supports the article listing filter fields (`articleTypeSlugs`/`Ids`, `tagSlugs`/`Ids`, `tagType*`, `authorSlugs`/`Ids` + `strictAuthorMatch`, `before`/`after`, `excludeArticleIds`, `count`, `allowUnindexed`) for News Grids, Publications Lists, person-scoped lists, tag filters, etc. The wrapper delegates to the enhanced `filterRelatedArticles` (OR semantics, slug support, strict author mode, distinct tag scoring, etc.).
|
|
39
|
+
|
|
40
|
+
- Added / expanded docs and examples in core-ui and contentful-rest-api READMEs + llms.md, CMS_INFRASTRUCTURE.md, SERVER_CLIENT_BOUNDARIES.md, the `se-marketing-sites-lib-cms-structure` skill (showing the `getAllArticleLinks({ articleTypeSlugs, strictAuthorMatch, ... })` pattern), and this repo's GitHub issues status tracker (new #51 entry + updated upgrade table).
|
|
41
|
+
|
|
42
|
+
- No changes to runtime behavior for existing bare `getAll*Links()` or `getRelated*` callers (sitemaps, search indexing, llms routes, etc. unaffected). Consumers should import filter symbols from the `/server` subpath when using them directly.
|
|
43
|
+
|
|
44
|
+
This finishes the implementation + docs for the DX improvement requested in #51 (reusable core filtering for full article collections instead of per-site `.filter()` shims). The boundary fix prevents the server-only error that appeared in any app importing from core-ui after the initial #51 changes.
|
|
45
|
+
|
|
46
|
+
## 1.0.42
|
|
47
|
+
|
|
48
|
+
### Patch Changes
|
|
49
|
+
|
|
50
|
+
- Resolve person **`bio`** rich text on **`IPersonLink`** so team cards, related-people collections, and search/markdown pipelines can use CMS bios without fetching full person pages.
|
|
51
|
+
|
|
52
|
+
**@se-studio/core-data-types** — Add **`IResolvedRichText`** (`{ json, customStyles? }`) as the shared resolved RTF shape. **`IPersonLink.bio`** and **`ILinkProps.longText`** use it. **`contentfulAllPersonLinks`** consumers get typed **`IPersonLink[]`**.
|
|
53
|
+
|
|
54
|
+
**@se-studio/contentful-rest-api** — Include `fields.bio` in **`PERSON_LINK_FIELDS`**. **`basePersonLinkConverter`** returns **`IPersonLink`** with resolved bio. **`contentfulAllPersonLinks`** return type is **`CmsResponse<IPersonLink[]>`**.
|
|
55
|
+
|
|
56
|
+
**@se-studio/markdown-renderer** — **`MarkdownConverter`** exports person-link **`bio`** to Markdown (search/index pipelines). Uses **`isPersonLink`** type guard.
|
|
57
|
+
|
|
58
|
+
**@se-studio/core-ui** — **`FetchHelpers.getAllPersonLinks`** for collection use via **`rendererConfig.fetchHelpers`** (no cms-server import). **`getAllPersonLinks`** and sitemap deps typed as **`IPersonLink[]`**.
|
|
59
|
+
|
|
60
|
+
**@se-studio/contentful-cms** / **@se-studio/skills** — Document Person **`bio`** RTF authoring in cms-edit skills and functional spec.
|
|
61
|
+
|
|
3
62
|
## 1.0.41
|
|
4
63
|
|
|
5
64
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -437,6 +437,26 @@ cms-edit ensure tag --slug asco-2025 --name "ASCO 2025" --tag-type conference-ve
|
|
|
437
437
|
|
|
438
438
|
See `cms-edit help fields-taxonomy` and `cms-edit help taxonomy-from-json` for field reference and batch schema.
|
|
439
439
|
|
|
440
|
+
## Person entries
|
|
441
|
+
|
|
442
|
+
Key fields: `name`, `slug`, `jobTitle`, `description`, `media` (featured image), **`bio`** (rich text).
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
# Add a person to a collection or page content array
|
|
446
|
+
cms-edit add "Dr. Jane Smith" --content-type person
|
|
447
|
+
|
|
448
|
+
# Scalars
|
|
449
|
+
cms-edit set @c0 name "Dr. Jane Smith"
|
|
450
|
+
cms-edit set @c0 slug jane-smith
|
|
451
|
+
cms-edit set @c0 jobTitle "Chief Medical Officer"
|
|
452
|
+
|
|
453
|
+
# Bio (same RTF workflow as component body)
|
|
454
|
+
printf '## Background\n\nBoard-certified with 20 years of experience.\n' | cms-edit rtf @c0 bio --markdown -
|
|
455
|
+
cms-edit read @c0 bio
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
**Site/runtime:** Resolved **person links** (`IPersonLink` from the REST API — article authors, collection contents, `contentfulAllPersonLinks`, related-people helpers) include **`bio`** when set in CMS. Use for team cards, related-people collections, and markdown/search export — not only full person detail pages.
|
|
459
|
+
|
|
440
460
|
**Idempotent taxonomy:** `--if-not-exists` / `ensure` are check-then-create — safe for sequential imports, not for parallel creates on the same slug.
|
|
441
461
|
|
|
442
462
|
```bash
|
|
@@ -81,6 +81,23 @@ Components and collections in the registration chain (imported by `registrations
|
|
|
81
81
|
|
|
82
82
|
These are passed via `projectRendererConfig` in `cms-server.ts`. `Section` and `SectionLinks` accept `previewHelpers` / `rendererConfig` props. See `docs/SERVER_CLIENT_BOUNDARIES.md`.
|
|
83
83
|
|
|
84
|
+
**Full article lists (News Grid / Publications List / ArticleFilter):** Use the new filter options on `getAllArticleLinks` instead of post-filtering the result in each app:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// inside a collection renderer (server component)
|
|
88
|
+
const articlesRes = await rendererConfig.fetchHelpers?.getAllArticleLinks?.({
|
|
89
|
+
articleTypeSlugs: [NEWS_SLUG], // or articleTypeIds
|
|
90
|
+
// On person pages (strict, so pubs grid doesn't bleed into news grid):
|
|
91
|
+
// authorIds: [person.id],
|
|
92
|
+
// strictAuthorMatch: true,
|
|
93
|
+
// tag context:
|
|
94
|
+
// tagSlugs: contextTag ? [contextTag.slug] : undefined,
|
|
95
|
+
});
|
|
96
|
+
const articles = articlesRes?.data ?? [];
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
`LinkFetchOptions` (and `filterRelatedArticles` / `RelatedArticlesOptions`) are available from `@se-studio/core-ui/server`. This moves the common type/tag/author logic into core (with OR semantics, scoring, indexed rules, legacy author handling still possible client-side or via custom wrapper). See GH #51.
|
|
100
|
+
|
|
84
101
|
## See Also
|
|
85
102
|
|
|
86
103
|
- **register-cms-features** – Adding components and collections
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: se-marketing-sites-redirects
|
|
3
|
+
description: "Add support for editor-managed URL redirects using a dedicated redirect content type + the proven A/B-style rebuild pattern (webhook to Vercel deploy hook, build bakes a static map). Includes the two official migration scripts (create CT, remove legacy redirectTo fields)."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# SE Marketing Sites — Redirects (rebuild pattern)
|
|
7
|
+
|
|
8
|
+
This skill adds the ability for **non-technical content editors** to manage redirects entirely from Contentful.
|
|
9
|
+
|
|
10
|
+
**Core idea (matches how your A/B tests already work):**
|
|
11
|
+
- Editors create `redirect` entries (they can **pick** existing Pages/Articles/etc. from reference fields or type raw paths).
|
|
12
|
+
- On publish, a Contentful webhook calls your Vercel **Deploy Hook**.
|
|
13
|
+
- The next build fetches the current redirects (via the normal CDA helpers), runs `buildRedirectMap`, and writes a tiny static file.
|
|
14
|
+
- Your `middleware.ts` imports that file and does the redirect **synchronously with zero runtime cost**.
|
|
15
|
+
- Propagation time = normal Vercel build (usually 1–3 minutes). Robust, no extra tokens or Edge Config required.
|
|
16
|
+
|
|
17
|
+
A faster "seconds" path (Edge Config) is explicitly **not** included in v1 per product direction — the model and helpers are forward-compatible if you add it later.
|
|
18
|
+
|
|
19
|
+
## 1. Run the migrations (create the content type + clean up legacy)
|
|
20
|
+
|
|
21
|
+
The repo contains two official migrations:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# 1. Create the new redirect content type (with great help text for editors)
|
|
25
|
+
node scripts/migrations/18-create-redirect-content-type.js
|
|
26
|
+
|
|
27
|
+
# 2. (Later, when you are ready) Remove the old per-page redirectTo fields
|
|
28
|
+
node scripts/migrations/19-remove-redirectTo-fields.js
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Run them with the Contentful CLI (or the contentful-cms package tools) and the usual `CONTENTFUL_SPACE_ID` + `CONTENTFUL_MANAGEMENT_TOKEN` + environment.
|
|
32
|
+
|
|
33
|
+
After the create migration you will have a new content type called **Redirect**.
|
|
34
|
+
|
|
35
|
+
## 2. The Redirect content type (what editors see)
|
|
36
|
+
|
|
37
|
+
Key fields (all documented with help text in the migration):
|
|
38
|
+
|
|
39
|
+
**From (source)**
|
|
40
|
+
- `fromPath` (raw path) — editors type `/old-url/` when they are not using a picker.
|
|
41
|
+
- `fromPage`, `fromArticle`, `fromArticleType`, `fromPerson`, `fromTag`, `fromCustomType` — reference pickers. When an editor picks one, the system automatically uses that item's current URL. This is the magic that makes it easy for non-technical people.
|
|
42
|
+
|
|
43
|
+
**To (destination)**
|
|
44
|
+
- `toPath` (raw path or full external `https://...` URL)
|
|
45
|
+
- `toPage`, `toArticle`, ... — same reference pickers as above.
|
|
46
|
+
|
|
47
|
+
**Other**
|
|
48
|
+
- `statusCode` — dropdown: 301 (permanent — recommended), 302, 307, 308.
|
|
49
|
+
- `active` — boolean (default on). Turn off to disable a rule without deleting it.
|
|
50
|
+
- `note` — internal editor note (never shown on the site).
|
|
51
|
+
- `cmsLabel` — required internal label.
|
|
52
|
+
|
|
53
|
+
**Editor guidance (the help text says this):**
|
|
54
|
+
> For redirects **from** an existing CMS page or article, use the reference picker instead of typing the slug. Same for the destination. Only use the raw path fields for old/vanity URLs that no longer exist in the CMS or for external sites.
|
|
55
|
+
|
|
56
|
+
The converter (`baseRedirectConverter`) resolves any chosen references into concrete `fromPath` / `to` strings at fetch time. The baked map only ever contains simple strings.
|
|
57
|
+
|
|
58
|
+
## 3. Add the core support (already done by the time you read this)
|
|
59
|
+
|
|
60
|
+
The following are already in the packages:
|
|
61
|
+
|
|
62
|
+
- `IRedirect` + `isRedirect` in `@se-studio/core-data-types`
|
|
63
|
+
- `baseRedirectConverter` + `BaseRedirectSkeleton`
|
|
64
|
+
- `contentfulRedirectsRest` + `buildRedirectMap` + `getRedirectsWithErrors` / `getRedirectMap` (via `createAppHelpers`)
|
|
65
|
+
- Revalidation tag `redirect` (so the existing webhook machinery knows about the new type)
|
|
66
|
+
|
|
67
|
+
You only need to wire the **rebuild** side in your app.
|
|
68
|
+
|
|
69
|
+
## 4. Wire the rebuild path in your app (the only recipe for v1)
|
|
70
|
+
|
|
71
|
+
### 4.1 Build-time generation (the heart of the simple path)
|
|
72
|
+
|
|
73
|
+
Create (or add to) a small script, e.g. `scripts/generate-redirects.ts`:
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
import 'server-only';
|
|
77
|
+
import { writeFileSync, mkdirSync } from 'node:fs';
|
|
78
|
+
import { dirname, resolve } from 'node:path';
|
|
79
|
+
import { fileURLToPath } from 'node:url';
|
|
80
|
+
|
|
81
|
+
import {
|
|
82
|
+
buildRedirectMap,
|
|
83
|
+
getRedirectMap, // or use the helpers directly
|
|
84
|
+
} from '@se-studio/contentful-rest-api'; // or from your local cms-server re-exports
|
|
85
|
+
|
|
86
|
+
// In a real app you would use your project's buildInformation + converterContext + getConfig
|
|
87
|
+
// exactly like you do for sitemaps or other build-time data pulls.
|
|
88
|
+
// For simplicity many teams just import the same helpers used at runtime.
|
|
89
|
+
|
|
90
|
+
async function main() {
|
|
91
|
+
// Example using the convenience that already exists on the helpers object you already create:
|
|
92
|
+
// const { getRedirectMap } = createAppHelpers(...);
|
|
93
|
+
// const map = await getRedirectMap({});
|
|
94
|
+
|
|
95
|
+
// Minimal standalone version (adapt to your converterContext / getContentfulConfig):
|
|
96
|
+
// const map = buildRedirectMap(await fetchAllPublishedRedirectsSomehow());
|
|
97
|
+
|
|
98
|
+
const map = {}; // TODO: replace with real call using your existing helpers
|
|
99
|
+
|
|
100
|
+
const outPath = resolve(dirname(fileURLToPath(import.meta.url)), '../src/generated/redirects.ts');
|
|
101
|
+
mkdirSync(dirname(outPath), { recursive: true });
|
|
102
|
+
|
|
103
|
+
const file = `// AUTO-GENERATED by scripts/generate-redirects.ts — do not edit by hand
|
|
104
|
+
export const redirects = ${JSON.stringify(map, null, 2)} as const;
|
|
105
|
+
export type RedirectMap = typeof redirects;
|
|
106
|
+
`;
|
|
107
|
+
writeFileSync(outPath, file, 'utf8');
|
|
108
|
+
console.log(`Wrote ${Object.keys(map).length} redirects to ${outPath}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
main().catch((e) => {
|
|
112
|
+
console.error(e);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Wire it into the build:
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
// in the app's package.json
|
|
121
|
+
{
|
|
122
|
+
"scripts": {
|
|
123
|
+
"generate:redirects": "tsx scripts/generate-redirects.ts",
|
|
124
|
+
"prebuild": "pnpm generate:redirects",
|
|
125
|
+
"build": "next build"
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
(You can also call it from a `next.config.ts` `webpack` hook or a turbo pipeline step — whatever is consistent with how you generate the A/B static data.)
|
|
131
|
+
|
|
132
|
+
### 4.2 Static middleware (zero runtime work)
|
|
133
|
+
|
|
134
|
+
Create `middleware.ts` at the root of your Next app (alongside `next.config.ts`):
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { NextResponse, type NextRequest } from 'next/server';
|
|
138
|
+
import { redirects } from './src/generated/redirects'; // the file we just generated
|
|
139
|
+
|
|
140
|
+
// If you also use A/B testing static middleware, import and compose here.
|
|
141
|
+
import { createStaticAbTestMiddleware } from '@se-studio/ab-testing/middleware';
|
|
142
|
+
// import { testsByPath } from './src/generated/abTests';
|
|
143
|
+
|
|
144
|
+
export async function middleware(request: NextRequest) {
|
|
145
|
+
const { pathname } = request.nextUrl;
|
|
146
|
+
|
|
147
|
+
// 1. Redirects first (cheap object lookup)
|
|
148
|
+
const rule = (redirects as Record<string, { to: string; status: number }>)[pathname];
|
|
149
|
+
if (rule) {
|
|
150
|
+
// You can add query param forwarding here if you added a field for it.
|
|
151
|
+
return NextResponse.redirect(new URL(rule.to, request.url), { status: rule.status });
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// 2. A/B (if you use the static version)
|
|
155
|
+
// const ab = createStaticAbTestMiddleware({ testsByPath });
|
|
156
|
+
// const abRes = ab(request);
|
|
157
|
+
// if (abRes) return abRes;
|
|
158
|
+
|
|
159
|
+
return NextResponse.next();
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export const config = {
|
|
163
|
+
matcher: [
|
|
164
|
+
/*
|
|
165
|
+
* Match all request paths except for the ones starting with:
|
|
166
|
+
* - api (API routes)
|
|
167
|
+
* - _next/static (static files)
|
|
168
|
+
* - _next/image (image optimization files)
|
|
169
|
+
* - favicon.ico, sitemap.xml, robots.txt, etc.
|
|
170
|
+
*/
|
|
171
|
+
'/((?!api|_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)',
|
|
172
|
+
],
|
|
173
|
+
};
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
If you already have a `middleware.ts` for A/B or other things, just add the redirect check at the very top (before any other logic).
|
|
177
|
+
|
|
178
|
+
### 4.3 Webhook = Deploy Hook (the trigger)
|
|
179
|
+
|
|
180
|
+
In Contentful → Settings → Webhooks, create (or reuse) a webhook that fires on:
|
|
181
|
+
|
|
182
|
+
- Entry publish / unpublish / delete for content type `redirect`
|
|
183
|
+
|
|
184
|
+
Point the URL at your **Vercel Deploy Hook** (Project → Settings → Deploy Hooks → create one for Production and optionally for Preview).
|
|
185
|
+
|
|
186
|
+
Add the usual headers your deploys need (`x-vercel-protection-bypass` if you use deployment protection, `REVALIDATION_SECRET` if you also want the normal reval to run, etc.).
|
|
187
|
+
|
|
188
|
+
That's it. Publish a redirect → Vercel starts a build → the generate step runs → the new static map is in the bundle → redirects work.
|
|
189
|
+
|
|
190
|
+
## 5. Testing
|
|
191
|
+
|
|
192
|
+
1. Run the create migration.
|
|
193
|
+
2. In Contentful create a Redirect:
|
|
194
|
+
- Pick a real Page as "From Page".
|
|
195
|
+
- Type `/somewhere-else/` as "To path".
|
|
196
|
+
- Status 301, Active on.
|
|
197
|
+
3. Publish.
|
|
198
|
+
4. Watch the deploy hook fire a build in Vercel.
|
|
199
|
+
5. After the build finishes: `curl -I https://your-site/from-page-url/` → 301 to the target.
|
|
200
|
+
6. Also test a raw-path rule and an external URL.
|
|
201
|
+
7. Edit the rule → republish → new build → new behaviour.
|
|
202
|
+
8. (Optional) Run the remove migration and confirm the old `redirectTo` fields are gone.
|
|
203
|
+
|
|
204
|
+
## 6. Preview / draft deployments
|
|
205
|
+
|
|
206
|
+
Because each deployment builds from whatever is **published** in Contentful at build time, your preview deployments automatically see the current published redirects. No extra work.
|
|
207
|
+
|
|
208
|
+
(If you have a `DRAFT_ONLY` preview that shows unpublished content, the redirects list will still be the published ones — which is usually what you want for redirect rules.)
|
|
209
|
+
|
|
210
|
+
## 7. Updating the skill / docs after changes
|
|
211
|
+
|
|
212
|
+
After you edit anything in this area, run:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
pnpm skills:validate
|
|
216
|
+
pnpm skills:sync
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Then commit the updated `.agents/skills/...` (the monorepo keeps them in sync with the canonical source in `packages/skills/skills/`).
|
|
220
|
+
|
|
221
|
+
## 8. Future work (explicitly out of scope for v1)
|
|
222
|
+
|
|
223
|
+
- Edge Config + instant sync route (user request: "don't do the fast version at the moment").
|
|
224
|
+
- Wildcards / prefix matching / query param control.
|
|
225
|
+
- A "Redirects index" page visible to editors on the live site.
|
|
226
|
+
- Host-based or A/B-aware redirects.
|
|
227
|
+
|
|
228
|
+
The `redirect` content type and the `buildRedirectMap` / `getRedirectMap` helpers are intentionally stable so a future faster path can consume the same data.
|
|
229
|
+
|
|
230
|
+
---
|
|
231
|
+
|
|
232
|
+
**You now have editor-managed redirects that are as robust and low-surprise as your A/B tests, using only patterns that already exist in the codebase.**
|
|
@@ -396,4 +396,19 @@ Expected: `200 OK`. A `401` means the secret in the header doesn't match the dep
|
|
|
396
396
|
|
|
397
397
|
- **If inspector mode doesn't highlight fields**: check that `getPreviewFieldProps` is applied to field containers in the components. See `.cursorrules` for the pattern.
|
|
398
398
|
- **For draft content visibility**: set `DRAFT_ONLY=true` in Vercel on a preview/staging environment (not production) so editors can see unpublished content without affecting live users.
|
|
399
|
+
|
|
400
|
+
## (Optional) Redirects via the rebuild pattern
|
|
401
|
+
|
|
402
|
+
If the project uses the `se-marketing-sites-redirects` skill, also configure a webhook that fires on the new `redirect` content type and points at a **Vercel Deploy Hook** (not a revalidation URL).
|
|
403
|
+
|
|
404
|
+
See `.agents/skills/se-marketing-sites-redirects/SKILL.md` (or the source in `packages/skills/skills/se-marketing-sites-redirects/SKILL.md`) for:
|
|
405
|
+
|
|
406
|
+
- Running the two official migration scripts (create the CT + later remove legacy `redirectTo` fields)
|
|
407
|
+
- The exact content type fields + recommended help text
|
|
408
|
+
- The build-time generate script + static middleware
|
|
409
|
+
- Wiring the deploy hook webhook
|
|
410
|
+
|
|
411
|
+
This is deliberately the same "publish → deploy hook → build bakes static data" pattern used for A/B tests. It is the only supported path in v1 (Edge Config fast path is future work).
|
|
412
|
+
|
|
413
|
+
You can have both the normal revalidation webhooks **and** the redirect deploy-hook webhook active at the same time.
|
|
399
414
|
- **To rotate the secret later**: re-run `scripts/setup-contentful-webhooks.ts` — it will generate a new secret and update everything automatically.
|