@postedin/cms-client 0.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.
Files changed (73) hide show
  1. package/README.md +66 -0
  2. package/bin/dissect/cli.mjs +138 -0
  3. package/bin/dissect/dissect.mjs +290 -0
  4. package/bin/profile/build.mjs +106 -0
  5. package/bin/profile/fetch-log.mjs +298 -0
  6. package/bin/profile/format.mjs +90 -0
  7. package/bin/profile/interference-summary.mjs +558 -0
  8. package/bin/profile/interference.mjs +604 -0
  9. package/bin/profile/measure.mjs +137 -0
  10. package/bin/profile/report.mjs +90 -0
  11. package/bin/profile/site-env.mjs +16 -0
  12. package/bin/profile/summarize.mjs +429 -0
  13. package/dist/browser.d.ts +145 -0
  14. package/dist/browser.js +11 -0
  15. package/dist/browser.js.map +1 -0
  16. package/dist/chunk-6V54ITTK.js +197 -0
  17. package/dist/chunk-6V54ITTK.js.map +1 -0
  18. package/dist/chunk-MNZ7DIGC.js +51 -0
  19. package/dist/chunk-MNZ7DIGC.js.map +1 -0
  20. package/dist/form-proxy/upload-policy.d.ts +40 -0
  21. package/dist/form-proxy/upload-policy.js +17 -0
  22. package/dist/form-proxy/upload-policy.js.map +1 -0
  23. package/dist/index.d.ts +570 -0
  24. package/dist/index.js +1636 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/payload-types.d.ts +8985 -0
  27. package/dist/payload-types.js +1 -0
  28. package/dist/payload-types.js.map +1 -0
  29. package/package.json +74 -0
  30. package/src/api.ts +387 -0
  31. package/src/blog-listing.ts +75 -0
  32. package/src/browser.ts +24 -0
  33. package/src/client.ts +144 -0
  34. package/src/cms-to-href.ts +70 -0
  35. package/src/cms.ts +86 -0
  36. package/src/collections/appearance.ts +94 -0
  37. package/src/collections/areas.ts +29 -0
  38. package/src/collections/authors.ts +27 -0
  39. package/src/collections/banners.ts +14 -0
  40. package/src/collections/categories.ts +111 -0
  41. package/src/collections/forms.ts +29 -0
  42. package/src/collections/header-footer.ts +19 -0
  43. package/src/collections/image-links.ts +14 -0
  44. package/src/collections/media.ts +18 -0
  45. package/src/collections/options.ts +10 -0
  46. package/src/collections/pages.ts +83 -0
  47. package/src/collections/posts.ts +249 -0
  48. package/src/collections/project.ts +16 -0
  49. package/src/collections/questions.ts +35 -0
  50. package/src/collections/seo.ts +10 -0
  51. package/src/collections/tags.ts +25 -0
  52. package/src/collections/team-members.ts +79 -0
  53. package/src/config-time.ts +98 -0
  54. package/src/context.ts +12 -0
  55. package/src/decode-html.ts +8 -0
  56. package/src/form-proxy/cms-client.ts +95 -0
  57. package/src/form-proxy/cms-errors.ts +73 -0
  58. package/src/form-proxy/cms-write.ts +44 -0
  59. package/src/form-proxy/http.ts +96 -0
  60. package/src/form-proxy/index.ts +73 -0
  61. package/src/form-proxy/rate-limit.ts +46 -0
  62. package/src/form-proxy/submissions.ts +88 -0
  63. package/src/form-proxy/types.ts +23 -0
  64. package/src/form-proxy/upload-policy.ts +92 -0
  65. package/src/form-proxy/uploads.ts +81 -0
  66. package/src/home-page.ts +83 -0
  67. package/src/index.ts +68 -0
  68. package/src/loader.ts +83 -0
  69. package/src/locales.ts +80 -0
  70. package/src/payload-types.ts +10854 -0
  71. package/src/placeholder.ts +9 -0
  72. package/src/resolve-menu-items.ts +184 -0
  73. package/src/routes.ts +184 -0
@@ -0,0 +1,145 @@
1
+ import { Config, Page, HeaderFooter } from './payload-types.js';
2
+
3
+ /** Every locale the CMS schema knows. A site builds a subset of them. */
4
+ type Locale = Config['locale'];
5
+ interface LocaleOptions {
6
+ /** The locales this site builds, default first or not. */
7
+ locales: readonly Locale[];
8
+ /** The unprefixed locale. */
9
+ defaultLocale: Locale;
10
+ /** URL prefix per locale: `''` for the default, `'/en'` for English. */
11
+ localePrefix: Readonly<Record<Locale, string>>;
12
+ }
13
+ type Locales = ReturnType<typeof defineLocales>;
14
+ /**
15
+ * The locale helpers every URL in a site is built with. Pure, so a hydrated
16
+ * island can use them as well as the server.
17
+ */
18
+ declare function defineLocales({ locales, defaultLocale, localePrefix, }: LocaleOptions): {
19
+ locales: readonly ("es" | "en")[];
20
+ defaultLocale: "es" | "en";
21
+ localePrefix: Readonly<Record<"es" | "en", string>>;
22
+ isLocale: (value: unknown) => value is Locale;
23
+ resolveLocale: (value: unknown) => Locale;
24
+ prefixPath: (path: string, locale: Locale) => string;
25
+ stripLocalePrefix: (path: string) => {
26
+ locale: Locale;
27
+ path: string;
28
+ };
29
+ };
30
+
31
+ /**
32
+ * The page configured as the site homepage. It is served at `/` (or `/en/`) and
33
+ * is deliberately not published at its own slug, so every page URL this module
34
+ * builds for it has to collapse to the site root. Read it with `getHomePageRef`
35
+ * on the server, or take it as a prop inside a hydrated island.
36
+ */
37
+ type HomePageRef = {
38
+ id: string;
39
+ path: string;
40
+ } | null;
41
+ type Type = 'blog' | 'page' | 'category' | 'author' | 'post' | 'tag' | 'team-member';
42
+ type CreateRoutes = (locale: Locale, homePage?: HomePageRef) => ReturnType<typeof buildRoutes>;
43
+ /**
44
+ * The site's route builder, bound to its locale configuration. Pure, so it is
45
+ * as usable inside a hydrated island as on the server.
46
+ */
47
+ declare function defineRoutes({ prefixPath }: Pick<Locales, 'prefixPath'>): (locale: Locale, homePage?: HomePageRef) => {
48
+ readonly locale: "es" | "en";
49
+ readonly root: <T extends "index" | "search" = "index">(name?: T, ...params: Parameters<{
50
+ readonly index: () => string;
51
+ readonly search: () => string;
52
+ }[T]>) => ReturnType<{
53
+ readonly index: () => string;
54
+ readonly search: () => string;
55
+ }[T]>;
56
+ readonly blog: <T extends "page" | "author" | "category" | "post" | "tag" | "index" | "more" = "index">(name?: T, ...params: Parameters<{
57
+ readonly index: () => string;
58
+ readonly more: () => string;
59
+ readonly page: (n: number) => string;
60
+ readonly category: (path: string) => string;
61
+ readonly author: (slug: string) => string;
62
+ readonly post: (slug: string) => string;
63
+ readonly tag: (slug: string) => string;
64
+ }[T]>) => ReturnType<{
65
+ readonly index: () => string;
66
+ readonly more: () => string;
67
+ readonly page: (n: number) => string;
68
+ readonly category: (path: string) => string;
69
+ readonly author: (slug: string) => string;
70
+ readonly post: (slug: string) => string;
71
+ readonly tag: (slug: string) => string;
72
+ }[T]>;
73
+ readonly team: <T extends "index" | "member" | "vcard" = "index">(name?: T, ...params: Parameters<{
74
+ readonly index: () => string;
75
+ readonly member: (slug: string) => string;
76
+ readonly vcard: (slug: string) => string;
77
+ }[T]>) => ReturnType<{
78
+ readonly index: () => string;
79
+ readonly member: (slug: string) => string;
80
+ readonly vcard: (slug: string) => string;
81
+ }[T]>;
82
+ readonly page: (slugOrPage: string | Page) => string;
83
+ readonly search: () => string;
84
+ readonly find: (routeType: Type | string, slugOrPath: string | undefined) => string | undefined;
85
+ readonly fromCMSCollection: (collectionSlug: string, doc: {
86
+ slug: string;
87
+ } & Partial<Page>) => string | undefined;
88
+ };
89
+ declare function buildRoutes(prefixPath: Locales['prefixPath'], locale: Locale, homePage: HomePageRef): {
90
+ readonly locale: "es" | "en";
91
+ readonly root: <T extends "index" | "search" = "index">(name?: T, ...params: Parameters<{
92
+ readonly index: () => string;
93
+ readonly search: () => string;
94
+ }[T]>) => ReturnType<{
95
+ readonly index: () => string;
96
+ readonly search: () => string;
97
+ }[T]>;
98
+ readonly blog: <T extends "page" | "author" | "category" | "post" | "tag" | "index" | "more" = "index">(name?: T, ...params: Parameters<{
99
+ readonly index: () => string;
100
+ readonly more: () => string;
101
+ readonly page: (n: number) => string;
102
+ readonly category: (path: string) => string;
103
+ readonly author: (slug: string) => string;
104
+ readonly post: (slug: string) => string;
105
+ readonly tag: (slug: string) => string;
106
+ }[T]>) => ReturnType<{
107
+ readonly index: () => string;
108
+ readonly more: () => string;
109
+ readonly page: (n: number) => string;
110
+ readonly category: (path: string) => string;
111
+ readonly author: (slug: string) => string;
112
+ readonly post: (slug: string) => string;
113
+ readonly tag: (slug: string) => string;
114
+ }[T]>;
115
+ readonly team: <T extends "index" | "member" | "vcard" = "index">(name?: T, ...params: Parameters<{
116
+ readonly index: () => string;
117
+ readonly member: (slug: string) => string;
118
+ readonly vcard: (slug: string) => string;
119
+ }[T]>) => ReturnType<{
120
+ readonly index: () => string;
121
+ readonly member: (slug: string) => string;
122
+ readonly vcard: (slug: string) => string;
123
+ }[T]>;
124
+ readonly page: (slugOrPage: string | Page) => string;
125
+ readonly search: () => string;
126
+ readonly find: (routeType: Type | string, slugOrPath: string | undefined) => string | undefined;
127
+ readonly fromCMSCollection: (collectionSlug: string, doc: {
128
+ slug: string;
129
+ } & Partial<Page>) => string | undefined;
130
+ };
131
+ type Routes = ReturnType<typeof buildRoutes>;
132
+
133
+ type CmsHeader = NonNullable<NonNullable<HeaderFooter>['header']>;
134
+ type CmsFooter = NonNullable<NonNullable<HeaderFooter>['footer']>;
135
+ type CmsMainMenu = NonNullable<CmsHeader['mainMenu']>;
136
+ type CmsMenuItem = NonNullable<CmsMainMenu[0]['menuItem']>;
137
+ type CmsLink = NonNullable<CmsMenuItem['mainLink']>;
138
+ type CmsSocialLinks = NonNullable<CmsHeader['socialLinks']>;
139
+ type CmsCallToActionLinks = NonNullable<CmsHeader['callToActionLinks']>;
140
+
141
+ type CmsToHref = ReturnType<typeof defineCmsToHref>;
142
+ /** `cmsToHref`, bound to the site's route builder. Pure; islands use it too. */
143
+ declare function defineCmsToHref(createRoutes: CreateRoutes): (link: Partial<CmsLink>, locale: Locale, homePage: HomePageRef) => string | undefined;
144
+
145
+ export { type CmsCallToActionLinks, type CmsFooter, type CmsHeader, type CmsLink, type CmsMainMenu, type CmsMenuItem, type CmsSocialLinks, type CmsToHref, type CreateRoutes, type HomePageRef, type Locale, type LocaleOptions, type Locales, type Routes, defineCmsToHref, defineLocales, defineRoutes };
@@ -0,0 +1,11 @@
1
+ import {
2
+ defineCmsToHref,
3
+ defineLocales,
4
+ defineRoutes
5
+ } from "./chunk-6V54ITTK.js";
6
+ export {
7
+ defineCmsToHref,
8
+ defineLocales,
9
+ defineRoutes
10
+ };
11
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,197 @@
1
+ // src/cms-to-href.ts
2
+ function defineCmsToHref(createRoutes) {
3
+ return (link, locale, homePage) => cmsToHref(createRoutes, link, locale, homePage);
4
+ }
5
+ function cmsToHref(createRoutes, { type, url, reference }, locale, homePage) {
6
+ if (type === "custom") {
7
+ return url ?? void 0;
8
+ }
9
+ if (!reference?.relationTo || !reference.value) {
10
+ return void 0;
11
+ }
12
+ const routes = createRoutes(locale, homePage);
13
+ if (reference.relationTo === "pages") {
14
+ const page2 = reference.value;
15
+ if (typeof page2 === "string") {
16
+ return homePage && page2 === homePage.id ? routes.root() : void 0;
17
+ }
18
+ const rawPath = page2.breadcrumbs?.at(-1)?.url ?? page2.slug;
19
+ if (!rawPath) {
20
+ return void 0;
21
+ }
22
+ const path = rawPath.replace(/^\//, "");
23
+ return routes.find("page", path);
24
+ }
25
+ if (reference.relationTo === "categories") {
26
+ return routes.find("category", reference.value.slug);
27
+ }
28
+ if (reference.relationTo === "posts") {
29
+ return routes.find("post", reference.value.slug);
30
+ }
31
+ if (reference.relationTo === "authors") {
32
+ return routes.find("author", reference.value.slug);
33
+ }
34
+ if (reference.relationTo === "tags") {
35
+ return routes.find("tag", reference.value.slug);
36
+ }
37
+ }
38
+
39
+ // src/locales.ts
40
+ function defineLocales({
41
+ locales,
42
+ defaultLocale,
43
+ localePrefix
44
+ }) {
45
+ function isLocale(value) {
46
+ return typeof value === "string" && locales.includes(value);
47
+ }
48
+ function resolveLocale(value) {
49
+ return isLocale(value) ? value : defaultLocale;
50
+ }
51
+ function prefixPath(path, locale) {
52
+ const prefix = localePrefix[locale];
53
+ if (!prefix) {
54
+ return path;
55
+ }
56
+ if (path === "/") {
57
+ return `${prefix}/`;
58
+ }
59
+ return `${prefix}${path}`;
60
+ }
61
+ function stripLocalePrefix(path) {
62
+ for (const locale of locales) {
63
+ const prefix = localePrefix[locale];
64
+ if (!prefix) {
65
+ continue;
66
+ }
67
+ if (path === prefix) {
68
+ return { locale, path: "/" };
69
+ }
70
+ if (path.startsWith(`${prefix}/`)) {
71
+ return { locale, path: path.slice(prefix.length) };
72
+ }
73
+ }
74
+ return { locale: defaultLocale, path };
75
+ }
76
+ return {
77
+ locales,
78
+ defaultLocale,
79
+ localePrefix,
80
+ isLocale,
81
+ resolveLocale,
82
+ prefixPath,
83
+ stripLocalePrefix
84
+ };
85
+ }
86
+
87
+ // src/routes.ts
88
+ function trimSlashes(str) {
89
+ return str.replace(/^\/+|\/+$/g, "");
90
+ }
91
+ function page(base, dynamic) {
92
+ if (!dynamic) {
93
+ return "/404";
94
+ }
95
+ return base + trimSlashes(dynamic);
96
+ }
97
+ function defineRoutes({ prefixPath }) {
98
+ return ((locale, homePage = null) => buildRoutes(prefixPath, locale, homePage));
99
+ }
100
+ function buildRoutes(prefixPath, locale, homePage) {
101
+ const p = (path) => {
102
+ const prefixed = prefixPath(path, locale);
103
+ return prefixed.endsWith("/") ? prefixed : `${prefixed}/`;
104
+ };
105
+ const rootRoutes = {
106
+ index: () => p("/"),
107
+ search: () => p(locale === "en" ? "/search" : "/buscar")
108
+ };
109
+ const teamBase = locale === "en" ? "/team" : "/equipo";
110
+ const teamRoutes = {
111
+ index: () => p(teamBase),
112
+ member: (slug) => p(page(`${teamBase}/`, slug)),
113
+ // File endpoint — no trailing slash before the extension.
114
+ vcard: (slug) => `${prefixPath(page(`${teamBase}/`, slug), locale)}.vcf`
115
+ };
116
+ const blogRoutes = {
117
+ index: () => p("/blog"),
118
+ more: () => p("/blog/2"),
119
+ page: (n) => p(`/blog/${n}`),
120
+ category: (path) => p(page("/blog/category/", path)),
121
+ author: (slug) => p(page("/blog/author/", slug)),
122
+ post: (slug) => p(page("/blog/", slug)),
123
+ tag: (slug) => p(page("/blog/tag/", slug))
124
+ };
125
+ const root = (name = "index", ...params) => rootRoutes[name](...params);
126
+ const blog = (name = "index", ...params) => blogRoutes[name](...params);
127
+ const team = (name = "index", ...params) => teamRoutes[name](...params);
128
+ const isHomePage = (slugOrPage) => {
129
+ if (!homePage) {
130
+ return false;
131
+ }
132
+ return typeof slugOrPage === "string" ? trimSlashes(slugOrPage) === homePage.path : slugOrPage.id === homePage.id;
133
+ };
134
+ const pageRoute = (slugOrPage) => {
135
+ if (isHomePage(slugOrPage)) {
136
+ return rootRoutes.index();
137
+ }
138
+ if (typeof slugOrPage !== "string") {
139
+ const url = slugOrPage.breadcrumbs?.at(-1)?.url;
140
+ if (url) {
141
+ return p(url);
142
+ }
143
+ return p(page("/", slugOrPage.slug));
144
+ }
145
+ return p(page("/", slugOrPage));
146
+ };
147
+ return {
148
+ locale,
149
+ root,
150
+ blog,
151
+ team,
152
+ page: pageRoute,
153
+ search: rootRoutes.search,
154
+ find(routeType, slugOrPath) {
155
+ const type = routeType;
156
+ if (type === "blog") {
157
+ return blog();
158
+ }
159
+ if (!slugOrPath) {
160
+ return void 0;
161
+ }
162
+ if (type === "page") {
163
+ return pageRoute(slugOrPath);
164
+ }
165
+ if (type === "team-member") {
166
+ return team("member", slugOrPath);
167
+ }
168
+ if (["category", "author", "post", "tag"].includes(type)) {
169
+ return blog(type, slugOrPath);
170
+ }
171
+ },
172
+ fromCMSCollection(collectionSlug, doc) {
173
+ switch (collectionSlug) {
174
+ case "posts":
175
+ return blog("post", doc.slug);
176
+ case "pages":
177
+ return pageRoute(doc);
178
+ case "categories":
179
+ return blog("category", doc.slug);
180
+ case "authors":
181
+ return blog("author", doc.slug);
182
+ case "tags":
183
+ return blog("tag", doc.slug);
184
+ case "team-members":
185
+ return team("member", doc.slug);
186
+ }
187
+ return void 0;
188
+ }
189
+ };
190
+ }
191
+
192
+ export {
193
+ defineCmsToHref,
194
+ defineLocales,
195
+ defineRoutes
196
+ };
197
+ //# sourceMappingURL=chunk-6V54ITTK.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/cms-to-href.ts","../src/locales.ts","../src/routes.ts"],"sourcesContent":["import type { CmsLink } from './collections/header-footer';\nimport type { Locale } from './locales';\nimport type { Author, Category, Page, Post, Tag } from './payload-types';\nimport type { CreateRoutes, HomePageRef } from './routes';\n\nexport type CmsToHref = ReturnType<typeof defineCmsToHref>;\n\n/** `cmsToHref`, bound to the site's route builder. Pure; islands use it too. */\nexport function defineCmsToHref(createRoutes: CreateRoutes) {\n\treturn (\n\t\tlink: Partial<CmsLink>,\n\t\tlocale: Locale,\n\t\thomePage: HomePageRef,\n\t): string | undefined => cmsToHref(createRoutes, link, locale, homePage);\n}\n\nfunction cmsToHref(\n\tcreateRoutes: CreateRoutes,\n\t{ type, url, reference }: Partial<CmsLink>,\n\tlocale: Locale,\n\t// The page configured as the site homepage — read it with `getHomePageRef`\n\t// (server) or take it as a prop (islands). `createRoutes` owns the collapse\n\t// to `/`, so every `routes.page()` caller gets the same behaviour; passing it\n\t// here just hands it through. Required so a new call site can't silently fall\n\t// back to the wrong URL.\n\thomePage: HomePageRef,\n): string | undefined {\n\tif (type === 'custom') {\n\t\treturn url ?? undefined;\n\t}\n\n\tif (!reference?.relationTo || !reference.value) {\n\t\treturn undefined;\n\t}\n\n\tconst routes = createRoutes(locale, homePage);\n\n\tif (reference.relationTo === 'pages') {\n\t\tconst page = reference.value as Page | string;\n\n\t\t// An unpopulated reference is just an id, so there is no path to route by.\n\t\t// The homepage is still recoverable — it routes by id.\n\t\tif (typeof page === 'string') {\n\t\t\treturn homePage && page === homePage.id ? routes.root() : undefined;\n\t\t}\n\n\t\tconst rawPath = page.breadcrumbs?.at(-1)?.url ?? page.slug;\n\t\tif (!rawPath) {\n\t\t\treturn undefined;\n\t\t}\n\t\tconst path = rawPath.replace(/^\\//, '');\n\t\treturn routes.find('page', path);\n\t}\n\n\tif (reference.relationTo === 'categories') {\n\t\treturn routes.find('category', (reference.value as Category).slug);\n\t}\n\n\tif (reference.relationTo === 'posts') {\n\t\treturn routes.find('post', (reference.value as Post).slug);\n\t}\n\n\tif (reference.relationTo === 'authors') {\n\t\treturn routes.find('author', (reference.value as Author).slug);\n\t}\n\n\tif (reference.relationTo === 'tags') {\n\t\treturn routes.find('tag', (reference.value as Tag).slug);\n\t}\n}\n","import type { Config } from './payload-types';\n\n/** Every locale the CMS schema knows. A site builds a subset of them. */\nexport type Locale = Config['locale'];\n\nexport interface LocaleOptions {\n\t/** The locales this site builds, default first or not. */\n\tlocales: readonly Locale[];\n\t/** The unprefixed locale. */\n\tdefaultLocale: Locale;\n\t/** URL prefix per locale: `''` for the default, `'/en'` for English. */\n\tlocalePrefix: Readonly<Record<Locale, string>>;\n}\n\nexport type Locales = ReturnType<typeof defineLocales>;\n\n/**\n * The locale helpers every URL in a site is built with. Pure, so a hydrated\n * island can use them as well as the server.\n */\nexport function defineLocales({\n\tlocales,\n\tdefaultLocale,\n\tlocalePrefix,\n}: LocaleOptions) {\n\tfunction isLocale(value: unknown): value is Locale {\n\t\treturn (\n\t\t\ttypeof value === 'string' &&\n\t\t\t(locales as readonly string[]).includes(value)\n\t\t);\n\t}\n\n\tfunction resolveLocale(value: unknown): Locale {\n\t\treturn isLocale(value) ? value : defaultLocale;\n\t}\n\n\tfunction prefixPath(path: string, locale: Locale): string {\n\t\tconst prefix = localePrefix[locale];\n\t\tif (!prefix) {\n\t\t\treturn path;\n\t\t}\n\t\t// The site is built with `trailingSlash: 'always'`, so the prefix alone is a\n\t\t// 404: `/en` is not a route, `/en/` is. Layout falls back to this for any\n\t\t// locale a page does not name an alternate for, which made the homepage's\n\t\t// own language switcher — and the `hreflang` beside it — a dead link.\n\t\tif (path === '/') {\n\t\t\treturn `${prefix}/`;\n\t\t}\n\t\treturn `${prefix}${path}`;\n\t}\n\n\tfunction stripLocalePrefix(path: string): {\n\t\tlocale: Locale;\n\t\tpath: string;\n\t} {\n\t\tfor (const locale of locales) {\n\t\t\tconst prefix = localePrefix[locale];\n\t\t\tif (!prefix) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (path === prefix) {\n\t\t\t\treturn { locale, path: '/' };\n\t\t\t}\n\t\t\tif (path.startsWith(`${prefix}/`)) {\n\t\t\t\treturn { locale, path: path.slice(prefix.length) };\n\t\t\t}\n\t\t}\n\t\treturn { locale: defaultLocale, path };\n\t}\n\n\treturn {\n\t\tlocales,\n\t\tdefaultLocale,\n\t\tlocalePrefix,\n\t\tisLocale,\n\t\tresolveLocale,\n\t\tprefixPath,\n\t\tstripLocalePrefix,\n\t};\n}\n","import type { Locale, Locales } from './locales';\nimport type { Page } from './payload-types';\n\nfunction trimSlashes(str: string) {\n\treturn str.replace(/^\\/+|\\/+$/g, '');\n}\n\ntype BasePath = `/${string}/` | '/';\nfunction page(base: BasePath, dynamic: string) {\n\t// If dynamic is null we must have bad data, we only restrict things with types\n\tif (!dynamic) {\n\t\treturn '/404'; // give a 500 error or somehting here in future\n\t}\n\n\treturn base + trimSlashes(dynamic);\n}\n\n/**\n * The page configured as the site homepage. It is served at `/` (or `/en/`) and\n * is deliberately not published at its own slug, so every page URL this module\n * builds for it has to collapse to the site root. Read it with `getHomePageRef`\n * on the server, or take it as a prop inside a hydrated island.\n */\nexport type HomePageRef = { id: string; path: string } | null;\n\ntype Type =\n\t| 'blog'\n\t| 'page'\n\t| 'category'\n\t| 'author'\n\t| 'post'\n\t| 'tag'\n\t| 'team-member';\n\nexport type CreateRoutes = (\n\tlocale: Locale,\n\thomePage?: HomePageRef,\n) => ReturnType<typeof buildRoutes>;\n\n/**\n * The site's route builder, bound to its locale configuration. Pure, so it is\n * as usable inside a hydrated island as on the server.\n */\nexport function defineRoutes({ prefixPath }: Pick<Locales, 'prefixPath'>) {\n\treturn ((locale: Locale, homePage: HomePageRef = null) =>\n\t\tbuildRoutes(prefixPath, locale, homePage)) satisfies CreateRoutes;\n}\n\nfunction buildRoutes(\n\tprefixPath: Locales['prefixPath'],\n\tlocale: Locale,\n\thomePage: HomePageRef,\n) {\n\tconst p = (path: string) => {\n\t\tconst prefixed = prefixPath(path, locale);\n\t\treturn prefixed.endsWith('/') ? prefixed : `${prefixed}/`;\n\t};\n\n\tconst rootRoutes = {\n\t\tindex: () => p('/'),\n\t\tsearch: () => p(locale === 'en' ? '/search' : '/buscar'),\n\t} as const;\n\n\tconst teamBase = locale === 'en' ? '/team' : '/equipo';\n\tconst teamRoutes = {\n\t\tindex: () => p(teamBase),\n\t\tmember: (slug: string) => p(page(`${teamBase}/`, slug)),\n\t\t// File endpoint — no trailing slash before the extension.\n\t\tvcard: (slug: string) =>\n\t\t\t`${prefixPath(page(`${teamBase}/`, slug), locale)}.vcf`,\n\t} as const;\n\n\tconst blogRoutes = {\n\t\tindex: () => p('/blog'),\n\t\tmore: () => p('/blog/2'),\n\t\tpage: (n: number) => p(`/blog/${n}`) as string,\n\t\tcategory: (path: string) => p(page('/blog/category/', path)),\n\t\tauthor: (slug: string) => p(page('/blog/author/', slug)),\n\t\tpost: (slug: string) => p(page('/blog/', slug)),\n\t\ttag: (slug: string) => p(page('/blog/tag/', slug)),\n\t} as const;\n\n\ttype RootRoutes = typeof rootRoutes;\n\tconst root = <T extends keyof RootRoutes = 'index'>(\n\t\tname: T = 'index' as T,\n\t\t...params: Parameters<RootRoutes[T]>\n\t): ReturnType<RootRoutes[T]> => (rootRoutes[name] as any)(...params);\n\n\ttype BlogRoutes = typeof blogRoutes;\n\tconst blog = <T extends keyof BlogRoutes = 'index'>(\n\t\tname: T = 'index' as T,\n\t\t...params: Parameters<BlogRoutes[T]>\n\t): ReturnType<BlogRoutes[T]> => (blogRoutes[name] as any)(...params);\n\n\ttype TeamRoutes = typeof teamRoutes;\n\tconst team = <T extends keyof TeamRoutes = 'index'>(\n\t\tname: T = 'index' as T,\n\t\t...params: Parameters<TeamRoutes[T]>\n\t): ReturnType<TeamRoutes[T]> => (teamRoutes[name] as any)(...params);\n\n\t// Callers hand us either a full document (match by id) or a bare path (match\n\t// by path) — the homepage has to collapse to the root either way.\n\tconst isHomePage = (slugOrPage: string | Page) => {\n\t\tif (!homePage) {\n\t\t\treturn false;\n\t\t}\n\n\t\treturn typeof slugOrPage === 'string'\n\t\t\t? trimSlashes(slugOrPage) === homePage.path\n\t\t\t: slugOrPage.id === homePage.id;\n\t};\n\n\tconst pageRoute = (slugOrPage: string | Page) => {\n\t\tif (isHomePage(slugOrPage)) {\n\t\t\treturn rootRoutes.index();\n\t\t}\n\n\t\tif (typeof slugOrPage !== 'string') {\n\t\t\tconst url = slugOrPage.breadcrumbs?.at(-1)?.url;\n\t\t\tif (url) {\n\t\t\t\treturn p(url);\n\t\t\t}\n\n\t\t\treturn p(page('/', slugOrPage.slug));\n\t\t}\n\n\t\treturn p(page('/', slugOrPage));\n\t};\n\n\treturn {\n\t\tlocale,\n\t\troot,\n\t\tblog,\n\t\tteam,\n\t\tpage: pageRoute,\n\t\tsearch: rootRoutes.search,\n\t\tfind(routeType: Type | string, slugOrPath: string | undefined) {\n\t\t\tconst type = routeType as Type;\n\n\t\t\tif (type === 'blog') {\n\t\t\t\treturn blog();\n\t\t\t}\n\n\t\t\tif (!slugOrPath) {\n\t\t\t\treturn undefined;\n\t\t\t}\n\n\t\t\tif (type === 'page') {\n\t\t\t\treturn pageRoute(slugOrPath);\n\t\t\t}\n\n\t\t\tif (type === 'team-member') {\n\t\t\t\treturn team('member', slugOrPath);\n\t\t\t}\n\n\t\t\tif (['category', 'author', 'post', 'tag'].includes(type)) {\n\t\t\t\treturn blog(type, slugOrPath);\n\t\t\t}\n\t\t},\n\t\tfromCMSCollection(\n\t\t\tcollectionSlug: string,\n\t\t\tdoc: { slug: string } & Partial<Page>,\n\t\t) {\n\t\t\tswitch (collectionSlug) {\n\t\t\t\tcase 'posts':\n\t\t\t\t\treturn blog('post', doc.slug);\n\t\t\t\tcase 'pages':\n\t\t\t\t\treturn pageRoute(doc as Page);\n\t\t\t\tcase 'categories':\n\t\t\t\t\treturn blog('category', doc.slug);\n\t\t\t\tcase 'authors':\n\t\t\t\t\treturn blog('author', doc.slug);\n\t\t\t\tcase 'tags':\n\t\t\t\t\treturn blog('tag', doc.slug);\n\t\t\t\tcase 'team-members':\n\t\t\t\t\treturn team('member', doc.slug);\n\t\t\t}\n\n\t\t\treturn undefined;\n\t\t},\n\t} as const;\n}\n\nexport type Routes = ReturnType<typeof buildRoutes>;\n"],"mappings":"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","names":["page"]}
@@ -0,0 +1,51 @@
1
+ // src/form-proxy/upload-policy.ts
2
+ var MAX_UPLOAD_BYTES = 10 * 1024 * 1024;
3
+ var MAX_REQUEST_BYTES = MAX_UPLOAD_BYTES + 64 * 1024;
4
+ var FILE_TYPE_MIME_MAP = {
5
+ images: [
6
+ "image/jpeg",
7
+ "image/png",
8
+ "image/gif",
9
+ "image/webp",
10
+ "image/svg+xml"
11
+ ],
12
+ documents: [
13
+ "application/pdf",
14
+ "application/msword",
15
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
16
+ "application/vnd.ms-excel",
17
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
18
+ "application/x-cfb",
19
+ "text/plain"
20
+ ],
21
+ videos: ["video/mp4", "video/webm", "video/ogg", "video/quicktime"],
22
+ audio: ["audio/mpeg", "audio/ogg", "audio/wav", "audio/webm"]
23
+ };
24
+ var ALLOWED_UPLOAD_MIME_TYPES = [
25
+ ...new Set(Object.values(FILE_TYPE_MIME_MAP).flat())
26
+ ];
27
+ function acceptAttribute(types) {
28
+ if (!types || types.length === 0) {
29
+ return "";
30
+ }
31
+ return types.flatMap((type) => FILE_TYPE_MIME_MAP[type] ?? []).join(",");
32
+ }
33
+ function uploadRefusal(file) {
34
+ if (file.size > MAX_UPLOAD_BYTES) {
35
+ return "size";
36
+ }
37
+ if (file.type && !ALLOWED_UPLOAD_MIME_TYPES.includes(file.type)) {
38
+ return "type";
39
+ }
40
+ return null;
41
+ }
42
+
43
+ export {
44
+ MAX_UPLOAD_BYTES,
45
+ MAX_REQUEST_BYTES,
46
+ FILE_TYPE_MIME_MAP,
47
+ ALLOWED_UPLOAD_MIME_TYPES,
48
+ acceptAttribute,
49
+ uploadRefusal
50
+ };
51
+ //# sourceMappingURL=chunk-MNZ7DIGC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/form-proxy/upload-policy.ts"],"sourcesContent":["// What the CMS's `form-uploads` collection accepts, mirrored here so the\n// browser, the proxy route and the CMS agree about the same file.\n//\n// The CMS is the authority: `src/blocks/Form/uploadPolicy.ts` there declares\n// the size limit and the MIME map, and since postedin/cms#278 it enforces both\n// — a file over the limit is answered 413, a type outside the list 400. It also\n// *sniffs the bytes*, which nothing on this side can do, so a file whose\n// contents do not match its name passes every check here and is still refused.\n// Everything below is therefore an early, friendlier \"no\", never a guarantee of\n// a \"yes\".\n//\n// A leaf module with no imports: the React form islands read it in the browser\n// and the proxy handler reads it on the server.\n\n/** 10 MB, matching `MAX_UPLOAD_FILE_SIZE` in the CMS. */\nexport const MAX_UPLOAD_BYTES = 10 * 1024 * 1024;\n\n/**\n * Room for the multipart envelope around a max-size file.\n *\n * Here rather than beside the handler that reads it so that every limit the\n * upload path is held to is in one file, which is also the file the browser\n * imports — importing it from the handler would pull the handler into the\n * client bundle.\n */\nexport const MAX_REQUEST_BYTES = MAX_UPLOAD_BYTES + 64 * 1024;\n\n/**\n * The MIME types behind each choice in a file field's `allowedFileTypes`, and\n * — flattened — the whole of what the CMS accepts.\n *\n * `application/x-cfb` is what a legacy `.doc` or `.xls` sniffs as; the CMS\n * carries it for that reason and it is kept here so the two lists can be\n * compared line by line.\n */\nexport const FILE_TYPE_MIME_MAP: Record<string, string[]> = {\n\timages: [\n\t\t'image/jpeg',\n\t\t'image/png',\n\t\t'image/gif',\n\t\t'image/webp',\n\t\t'image/svg+xml',\n\t],\n\tdocuments: [\n\t\t'application/pdf',\n\t\t'application/msword',\n\t\t'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n\t\t'application/vnd.ms-excel',\n\t\t'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n\t\t'application/x-cfb',\n\t\t'text/plain',\n\t],\n\tvideos: ['video/mp4', 'video/webm', 'video/ogg', 'video/quicktime'],\n\taudio: ['audio/mpeg', 'audio/ogg', 'audio/wav', 'audio/webm'],\n};\n\nexport const ALLOWED_UPLOAD_MIME_TYPES: string[] = [\n\t...new Set(Object.values(FILE_TYPE_MIME_MAP).flat()),\n];\n\n/** The `accept` attribute for a file field's declared `allowedFileTypes`. */\nexport function acceptAttribute(types?: string[] | null): string {\n\tif (!types || types.length === 0) {\n\t\treturn '';\n\t}\n\treturn types.flatMap((type) => FILE_TYPE_MIME_MAP[type] ?? []).join(',');\n}\n\n/** Why the CMS would refuse this file, as far as the browser can tell. */\nexport type UploadRefusal = 'size' | 'type';\n\n/**\n * The refusal a file would earn before it is sent, or `null` to send it.\n *\n * A file the browser reports no type for is sent: an empty `type` is what\n * Chrome gives an extension it does not recognise, and the CMS sniffs the bytes\n * anyway, so guessing here would refuse files the CMS would have taken. The\n * types a field does not ask for are left to the `accept` attribute and to the\n * CMS; this only holds a file to what the collection accepts at all.\n */\nexport function uploadRefusal(file: {\n\tsize: number;\n\ttype: string;\n}): UploadRefusal | null {\n\tif (file.size > MAX_UPLOAD_BYTES) {\n\t\treturn 'size';\n\t}\n\tif (file.type && !ALLOWED_UPLOAD_MIME_TYPES.includes(file.type)) {\n\t\treturn 'type';\n\t}\n\treturn null;\n}\n"],"mappings":";AAeO,IAAM,mBAAmB,KAAK,OAAO;AAUrC,IAAM,oBAAoB,mBAAmB,KAAK;AAUlD,IAAM,qBAA+C;AAAA,EAC3D,QAAQ;AAAA,IACP;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AAAA,EACA,WAAW;AAAA,IACV;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACD;AAAA,EACA,QAAQ,CAAC,aAAa,cAAc,aAAa,iBAAiB;AAAA,EAClE,OAAO,CAAC,cAAc,aAAa,aAAa,YAAY;AAC7D;AAEO,IAAM,4BAAsC;AAAA,EAClD,GAAG,IAAI,IAAI,OAAO,OAAO,kBAAkB,EAAE,KAAK,CAAC;AACpD;AAGO,SAAS,gBAAgB,OAAiC;AAChE,MAAI,CAAC,SAAS,MAAM,WAAW,GAAG;AACjC,WAAO;AAAA,EACR;AACA,SAAO,MAAM,QAAQ,CAAC,SAAS,mBAAmB,IAAI,KAAK,CAAC,CAAC,EAAE,KAAK,GAAG;AACxE;AAcO,SAAS,cAAc,MAGL;AACxB,MAAI,KAAK,OAAO,kBAAkB;AACjC,WAAO;AAAA,EACR;AACA,MAAI,KAAK,QAAQ,CAAC,0BAA0B,SAAS,KAAK,IAAI,GAAG;AAChE,WAAO;AAAA,EACR;AACA,SAAO;AACR;","names":[]}
@@ -0,0 +1,40 @@
1
+ /** 10 MB, matching `MAX_UPLOAD_FILE_SIZE` in the CMS. */
2
+ declare const MAX_UPLOAD_BYTES: number;
3
+ /**
4
+ * Room for the multipart envelope around a max-size file.
5
+ *
6
+ * Here rather than beside the handler that reads it so that every limit the
7
+ * upload path is held to is in one file, which is also the file the browser
8
+ * imports — importing it from the handler would pull the handler into the
9
+ * client bundle.
10
+ */
11
+ declare const MAX_REQUEST_BYTES: number;
12
+ /**
13
+ * The MIME types behind each choice in a file field's `allowedFileTypes`, and
14
+ * — flattened — the whole of what the CMS accepts.
15
+ *
16
+ * `application/x-cfb` is what a legacy `.doc` or `.xls` sniffs as; the CMS
17
+ * carries it for that reason and it is kept here so the two lists can be
18
+ * compared line by line.
19
+ */
20
+ declare const FILE_TYPE_MIME_MAP: Record<string, string[]>;
21
+ declare const ALLOWED_UPLOAD_MIME_TYPES: string[];
22
+ /** The `accept` attribute for a file field's declared `allowedFileTypes`. */
23
+ declare function acceptAttribute(types?: string[] | null): string;
24
+ /** Why the CMS would refuse this file, as far as the browser can tell. */
25
+ type UploadRefusal = 'size' | 'type';
26
+ /**
27
+ * The refusal a file would earn before it is sent, or `null` to send it.
28
+ *
29
+ * A file the browser reports no type for is sent: an empty `type` is what
30
+ * Chrome gives an extension it does not recognise, and the CMS sniffs the bytes
31
+ * anyway, so guessing here would refuse files the CMS would have taken. The
32
+ * types a field does not ask for are left to the `accept` attribute and to the
33
+ * CMS; this only holds a file to what the collection accepts at all.
34
+ */
35
+ declare function uploadRefusal(file: {
36
+ size: number;
37
+ type: string;
38
+ }): UploadRefusal | null;
39
+
40
+ export { ALLOWED_UPLOAD_MIME_TYPES, FILE_TYPE_MIME_MAP, MAX_REQUEST_BYTES, MAX_UPLOAD_BYTES, type UploadRefusal, acceptAttribute, uploadRefusal };
@@ -0,0 +1,17 @@
1
+ import {
2
+ ALLOWED_UPLOAD_MIME_TYPES,
3
+ FILE_TYPE_MIME_MAP,
4
+ MAX_REQUEST_BYTES,
5
+ MAX_UPLOAD_BYTES,
6
+ acceptAttribute,
7
+ uploadRefusal
8
+ } from "../chunk-MNZ7DIGC.js";
9
+ export {
10
+ ALLOWED_UPLOAD_MIME_TYPES,
11
+ FILE_TYPE_MIME_MAP,
12
+ MAX_REQUEST_BYTES,
13
+ MAX_UPLOAD_BYTES,
14
+ acceptAttribute,
15
+ uploadRefusal
16
+ };
17
+ //# sourceMappingURL=upload-policy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}