@se-studio/skills 1.1.0 → 1.1.3
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 +50 -0
- package/package.json +1 -1
- package/skills/se-marketing-sites-redirects/SKILL.md +44 -19
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# @se-studio/skills
|
|
2
2
|
|
|
3
|
+
## 1.1.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Bulk version bump: patch for all packages
|
|
8
|
+
|
|
9
|
+
## 1.1.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Exclude redirect sources from sitemaps + robust protection against self and circular redirects.
|
|
14
|
+
|
|
15
|
+
- `buildRedirectMap` now normalizes internal paths (leading + trailing `/` to match urlCalculators + sitemap conventions) and reliably drops self-redirects (A → A). It also detects longer redirect cycles (A → B → A, etc.), drops the participating `from` rules from the baked map, and emits a `console.warn` during construction (visible in build/generate scripts).
|
|
16
|
+
- New exported utility: `filterSitemapEntriesExcludingRedirects(entries, redirectMap)`. Works on any `{ url: string }[]` (SitemapEntry, ISitemapEntry, etc.).
|
|
17
|
+
- The standard marketing site sitemap helper now accepts an optional `redirectMap` in `getSitemapEntries({ includeUnindexed, redirectMap })`. When provided, any URL that is the source of an active redirect is excluded from the generated sitemap entries. This is the recommended way to keep sitemaps clean when using editor-managed redirects.
|
|
18
|
+
- Widened the public `getSitemapEntries` option type in app helpers (additive; existing callers unaffected).
|
|
19
|
+
- Updated example app sitemaps (main + unindexed) to demonstrate fetching the map and passing it.
|
|
20
|
+
- Skill docs (`se-marketing-sites-redirects`) now include a "Redirects and sitemaps" section with usage patterns + notes on the improved cycle/self protection. Also updated routing docs.
|
|
21
|
+
- `buildRedirectMap` / `RedirectMap` / `getRedirectMap` behaviour for the middleware baked map is now safer (no self-loops or cycles will be baked).
|
|
22
|
+
|
|
23
|
+
Consumers using the `marketingSiteSitemap` helper + `getRedirectMap` at build time can now easily exclude redirect sources from both their static redirect map (for middleware) and their sitemaps in one consistent way.
|
|
24
|
+
|
|
25
|
+
See the redirect skill for the full recommended build-time pattern and the new filter helper.
|
|
26
|
+
|
|
27
|
+
## 1.1.1
|
|
28
|
+
|
|
29
|
+
### Patch Changes
|
|
30
|
+
|
|
31
|
+
- Simplify Redirect content model + add tagType as a first-class internal link target.
|
|
32
|
+
|
|
33
|
+
The original redirect content type (added in the previous minor) used a verbose Contentful model with separate `fromPage`/`fromArticle`/etc. reference fields (and same for "to"). This has been replaced with the clean model:
|
|
34
|
+
|
|
35
|
+
- `fromPath` (raw/custom) + `fromInternal` (single Link→Entry)
|
|
36
|
+
- `toPath` (raw/external) + `toInternal` (single Link→Entry)
|
|
37
|
+
|
|
38
|
+
Both internal fields are restricted via `linkContentType` validation to: page, article, articleType, person, tag, customType, tagType.
|
|
39
|
+
|
|
40
|
+
Updated:
|
|
41
|
+
|
|
42
|
+
- Migration `scripts/migrations/18-create-redirect-content-type.js` (now a clean create for the good model; delete any previous "Redirect" CT first if you ran the old multi-field version).
|
|
43
|
+
- `BaseRedirectSkeleton`, `baseRedirectConverter`, and `IRedirect` (provenance is now `fromInternal`/`toInternal` as `IInternalLink`).
|
|
44
|
+
- `UrlCalculators` now requires a `tagType(slug)` function (to support selecting tag type indexes as redirect targets).
|
|
45
|
+
- Added `baseTagTypeLinkConverter`, registered the resolver, extended `BaseLink` / `InternalType` / exported `ITagTypeLink` + guard.
|
|
46
|
+
|
|
47
|
+
Docs and the `se-marketing-sites-redirects` skill have been updated with the new CT shape, help text guidance, and testing steps.
|
|
48
|
+
|
|
49
|
+
Consumers will need to implement `tagType` in their urlCalculators (and pass it through `createBaseConverterContext`) to select Tag Type entries in redirects and to satisfy the updated type.
|
|
50
|
+
|
|
51
|
+
The runtime redirect map, middleware, and `buildRedirectMap` behaviour are unchanged.
|
|
52
|
+
|
|
3
53
|
## 1.1.0
|
|
4
54
|
|
|
5
55
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
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
|
|
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 official migration script (create the clean CT with fromInternal/toInternal single-ref pickers)."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# SE Marketing Sites — Redirects (rebuild pattern)
|
|
@@ -8,7 +8,7 @@ description: "Add support for editor-managed URL redirects using a dedicated red
|
|
|
8
8
|
This skill adds the ability for **non-technical content editors** to manage redirects entirely from Contentful.
|
|
9
9
|
|
|
10
10
|
**Core idea (matches how your A/B tests already work):**
|
|
11
|
-
- Editors create `redirect` entries (they can **pick** existing Pages/Articles/etc.
|
|
11
|
+
- Editors create `redirect` entries (they can **pick** existing Pages/Articles/etc. via the single From/To internal pickers or type raw paths).
|
|
12
12
|
- On publish, a Contentful webhook calls your Vercel **Deploy Hook**.
|
|
13
13
|
- The next build fetches the current redirects (via the normal CDA helpers), runs `buildRedirectMap`, and writes a tiny static file.
|
|
14
14
|
- Your `middleware.ts` imports that file and does the redirect **synchronously with zero runtime cost**.
|
|
@@ -16,33 +16,32 @@ This skill adds the ability for **non-technical content editors** to manage redi
|
|
|
16
16
|
|
|
17
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
18
|
|
|
19
|
-
## 1. Run the
|
|
19
|
+
## 1. Run the migration (create the content type)
|
|
20
20
|
|
|
21
|
-
The repo contains
|
|
21
|
+
The repo contains the official migration:
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
#
|
|
24
|
+
# Create the redirect content type (clean fromPath + fromInternal / toPath + toInternal model)
|
|
25
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
26
|
```
|
|
30
27
|
|
|
31
|
-
Run
|
|
28
|
+
Run it with the Contentful CLI (or the contentful-cms package tools) and the usual `CONTENTFUL_SPACE_ID` + `CONTENTFUL_MANAGEMENT_TOKEN` + environment.
|
|
29
|
+
|
|
30
|
+
If you previously created a Redirect CT using an older multi-field version of the migration, **delete the "Redirect" content type first**, then re-run to get the clean single-ref model.
|
|
32
31
|
|
|
33
|
-
After
|
|
32
|
+
After running you will have a new (or repaired) content type called **Redirect**.
|
|
34
33
|
|
|
35
34
|
## 2. The Redirect content type (what editors see)
|
|
36
35
|
|
|
37
36
|
Key fields (all documented with help text in the migration):
|
|
38
37
|
|
|
39
38
|
**From (source)**
|
|
40
|
-
- `fromPath` (raw path) — editors type `/old-url/` when they are not using
|
|
41
|
-
- `
|
|
39
|
+
- `fromPath` (raw path) — editors type `/old-url/` when they are not using the picker.
|
|
40
|
+
- `fromInternal` (single reference picker) — restricted to page, article, articleType, person, tag, customType, tagType. 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
41
|
|
|
43
42
|
**To (destination)**
|
|
44
43
|
- `toPath` (raw path or full external `https://...` URL)
|
|
45
|
-
- `
|
|
44
|
+
- `toInternal` (single reference picker, same allowed types as fromInternal)
|
|
46
45
|
|
|
47
46
|
**Other**
|
|
48
47
|
- `statusCode` — dropdown: 301 (permanent — recommended), 302, 307, 308.
|
|
@@ -51,7 +50,7 @@ Key fields (all documented with help text in the migration):
|
|
|
51
50
|
- `cmsLabel` — required internal label.
|
|
52
51
|
|
|
53
52
|
**Editor guidance (the help text says this):**
|
|
54
|
-
> For redirects
|
|
53
|
+
> For redirects from existing CMS content, use the "From internal" / "To internal" picker instead of typing the slug. Only use the raw path fields for old/vanity URLs that no longer exist in the CMS or for external sites.
|
|
55
54
|
|
|
56
55
|
The converter (`baseRedirectConverter`) resolves any chosen references into concrete `fromPath` / `to` strings at fetch time. The baked map only ever contains simple strings.
|
|
57
56
|
|
|
@@ -59,9 +58,9 @@ The converter (`baseRedirectConverter`) resolves any chosen references into conc
|
|
|
59
58
|
|
|
60
59
|
The following are already in the packages:
|
|
61
60
|
|
|
62
|
-
- `IRedirect` + `isRedirect` in `@se-studio/core-data-types`
|
|
61
|
+
- `IRedirect` + `isRedirect` in `@se-studio/core-data-types` (now uses `fromInternal` / `toInternal`)
|
|
63
62
|
- `baseRedirectConverter` + `BaseRedirectSkeleton`
|
|
64
|
-
- `contentfulRedirectsRest` + `buildRedirectMap` + `getRedirectsWithErrors` / `getRedirectMap` (via `createAppHelpers`)
|
|
63
|
+
- `contentfulRedirectsRest` + `buildRedirectMap` (now with robust self/cycle guards + normalization) + `getRedirectsWithErrors` / `getRedirectMap` (via `createAppHelpers`)
|
|
65
64
|
- Revalidation tag `redirect` (so the existing webhook machinery knows about the new type)
|
|
66
65
|
|
|
67
66
|
You only need to wire the **rebuild** side in your app.
|
|
@@ -175,6 +174,30 @@ export const config = {
|
|
|
175
174
|
|
|
176
175
|
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
176
|
|
|
177
|
+
### 4.2.1 Redirects and sitemaps
|
|
178
|
+
|
|
179
|
+
When you create a redirect whose `from` (raw or resolved via internal picker) matches a URL that would otherwise be listed in your sitemap, that URL should generally be omitted from the sitemap (search engines should not be told to crawl a source that 301s away).
|
|
180
|
+
|
|
181
|
+
The shared libraries now support this easily:
|
|
182
|
+
|
|
183
|
+
- The `getSitemapEntries(...)` function returned by the marketing sitemap helper (when you configure `marketingSiteSitemap` in `createAppHelpers`) accepts an optional `redirectMap` in its options. When supplied, any entry whose URL is a redirect source is filtered out before the entries are returned to `buildSitemap`.
|
|
184
|
+
- Standalone: `filterSitemapEntriesExcludingRedirects(entries, redirectMap)` (exported from `@se-studio/contentful-rest-api`) works on any `{ url: string }[]` list for custom flows.
|
|
185
|
+
|
|
186
|
+
Recommended pattern (in `app/sitemap.ts` and the unindexed route):
|
|
187
|
+
|
|
188
|
+
```ts
|
|
189
|
+
import { getRedirectMap, getSitemapEntries } from '@/lib/cms-server';
|
|
190
|
+
import { buildSitemap } from '@se-studio/core-ui';
|
|
191
|
+
|
|
192
|
+
export default async function sitemap() {
|
|
193
|
+
const redirectMap = await getRedirectMap({});
|
|
194
|
+
const entries = await getSitemapEntries({ includeUnindexed: false, redirectMap });
|
|
195
|
+
return buildSitemap([() => Promise.resolve(entries)], { baseUrl, ... });
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Pure `fromPath` vanity redirects were already absent from sitemaps (no backing content entry). This mainly helps when you redirect *away from* live CMS pages/articles.
|
|
200
|
+
|
|
178
201
|
### 4.3 Webhook = Deploy Hook (the trigger)
|
|
179
202
|
|
|
180
203
|
In Contentful → Settings → Webhooks, create (or reuse) a webhook that fires on:
|
|
@@ -191,15 +214,15 @@ That's it. Publish a redirect → Vercel starts a build → the generate step ru
|
|
|
191
214
|
|
|
192
215
|
1. Run the create migration.
|
|
193
216
|
2. In Contentful create a Redirect:
|
|
194
|
-
- Pick a real Page
|
|
195
|
-
-
|
|
217
|
+
- Pick a real Page via the single "From internal" picker.
|
|
218
|
+
- Pick (or type) a destination via "To internal" or "To path".
|
|
196
219
|
- Status 301, Active on.
|
|
197
220
|
3. Publish.
|
|
198
221
|
4. Watch the deploy hook fire a build in Vercel.
|
|
199
222
|
5. After the build finishes: `curl -I https://your-site/from-page-url/` → 301 to the target.
|
|
200
223
|
6. Also test a raw-path rule and an external URL.
|
|
201
224
|
7. Edit the rule → republish → new build → new behaviour.
|
|
202
|
-
8. (Optional)
|
|
225
|
+
8. (Optional) If you also want to clean up legacy per-page `redirectTo` fields, run migration 19.
|
|
203
226
|
|
|
204
227
|
## 6. Preview / draft deployments
|
|
205
228
|
|
|
@@ -227,6 +250,8 @@ Then commit the updated `.agents/skills/...` (the monorepo keeps them in sync wi
|
|
|
227
250
|
|
|
228
251
|
The `redirect` content type and the `buildRedirectMap` / `getRedirectMap` helpers are intentionally stable so a future faster path can consume the same data.
|
|
229
252
|
|
|
253
|
+
`buildRedirectMap` automatically drops self-redirects (A → A after normalization) and any rules that participate in longer cycles, emitting a console warning for cycles during build/generate. Internal paths are normalized with a trailing `/` to match your urlCalculators and sitemap hrefs.
|
|
254
|
+
|
|
230
255
|
---
|
|
231
256
|
|
|
232
257
|
**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.**
|