@replohq/sdk 0.13.0 → 0.15.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/_vendor/replo-utils/lib/json.mjs +2 -42
- package/_vendor/replo-utils/lib/misc.mjs +3 -0
- package/_vendor/schemas/contentCollection.d.ts +231 -0
- package/_vendor/schemas/contentCollection.mjs +2 -0
- package/_vendor/schemas/contentEntry.d.ts +53 -0
- package/_vendor/schemas/contentEntry.mjs +3 -0
- package/_vendor/schemas/contentFrontmatter.d.ts +18 -0
- package/_vendor/schemas/contentManifest.d.ts +44 -0
- package/_vendor/schemas/contentManifest.mjs +197 -0
- package/_vendor/schemas/money.mjs +2 -0
- package/_vendor/schemas/routing/locale.mjs +2 -0
- package/_vendor/schemas/routing/rules.mjs +2 -0
- package/cart/cart-provider.d.ts +1 -2
- package/cart/cart-provider.js +1 -12
- package/cart/cart-provider.js.map +2 -2
- package/cart/hooks/use-buy-now.js +2 -1
- package/cart/hooks/use-buy-now.js.map +2 -2
- package/chunk-AL3F36PH.mjs +1514 -0
- package/chunk-GV5FY7ZZ.mjs +89 -0
- package/chunk-I23U2PUI.mjs +151 -0
- package/chunk-IQ5GRTXO.mjs +182 -0
- package/chunk-VKO7P3ED.mjs +41 -0
- package/content.d.ts +126 -0
- package/content.js +594 -0
- package/content.js.map +7 -0
- package/lib/buildMetadata.js +3 -3
- package/lib/external-navigation.d.ts +16 -0
- package/lib/external-navigation.js +13 -0
- package/lib/external-navigation.js.map +7 -0
- package/package.json +12 -4
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/isObject.js
|
|
2
|
+
function isObject(value) {
|
|
3
|
+
var type = typeof value;
|
|
4
|
+
return value != null && (type == "object" || type == "function");
|
|
5
|
+
}
|
|
6
|
+
var isObject_default = isObject;
|
|
7
|
+
|
|
8
|
+
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/isArray.js
|
|
9
|
+
var isArray = Array.isArray;
|
|
10
|
+
var isArray_default = isArray;
|
|
11
|
+
|
|
12
|
+
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/_freeGlobal.js
|
|
13
|
+
var freeGlobal = typeof global == "object" && global && global.Object === Object && global;
|
|
14
|
+
var freeGlobal_default = freeGlobal;
|
|
15
|
+
|
|
16
|
+
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/_root.js
|
|
17
|
+
var freeSelf = typeof self == "object" && self && self.Object === Object && self;
|
|
18
|
+
var root = freeGlobal_default || freeSelf || Function("return this")();
|
|
19
|
+
var root_default = root;
|
|
20
|
+
|
|
21
|
+
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/_Symbol.js
|
|
22
|
+
var Symbol = root_default.Symbol;
|
|
23
|
+
var Symbol_default = Symbol;
|
|
24
|
+
|
|
25
|
+
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/_getRawTag.js
|
|
26
|
+
var objectProto = Object.prototype;
|
|
27
|
+
var hasOwnProperty = objectProto.hasOwnProperty;
|
|
28
|
+
var nativeObjectToString = objectProto.toString;
|
|
29
|
+
var symToStringTag = Symbol_default ? Symbol_default.toStringTag : void 0;
|
|
30
|
+
function getRawTag(value) {
|
|
31
|
+
var isOwn = hasOwnProperty.call(value, symToStringTag), tag = value[symToStringTag];
|
|
32
|
+
try {
|
|
33
|
+
value[symToStringTag] = void 0;
|
|
34
|
+
var unmasked = true;
|
|
35
|
+
} catch (e) {
|
|
36
|
+
}
|
|
37
|
+
var result = nativeObjectToString.call(value);
|
|
38
|
+
if (unmasked) {
|
|
39
|
+
if (isOwn) {
|
|
40
|
+
value[symToStringTag] = tag;
|
|
41
|
+
} else {
|
|
42
|
+
delete value[symToStringTag];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
var getRawTag_default = getRawTag;
|
|
48
|
+
|
|
49
|
+
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/_objectToString.js
|
|
50
|
+
var objectProto2 = Object.prototype;
|
|
51
|
+
var nativeObjectToString2 = objectProto2.toString;
|
|
52
|
+
function objectToString(value) {
|
|
53
|
+
return nativeObjectToString2.call(value);
|
|
54
|
+
}
|
|
55
|
+
var objectToString_default = objectToString;
|
|
56
|
+
|
|
57
|
+
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/_baseGetTag.js
|
|
58
|
+
var nullTag = "[object Null]";
|
|
59
|
+
var undefinedTag = "[object Undefined]";
|
|
60
|
+
var symToStringTag2 = Symbol_default ? Symbol_default.toStringTag : void 0;
|
|
61
|
+
function baseGetTag(value) {
|
|
62
|
+
if (value == null) {
|
|
63
|
+
return value === void 0 ? undefinedTag : nullTag;
|
|
64
|
+
}
|
|
65
|
+
return symToStringTag2 && symToStringTag2 in Object(value) ? getRawTag_default(value) : objectToString_default(value);
|
|
66
|
+
}
|
|
67
|
+
var baseGetTag_default = baseGetTag;
|
|
68
|
+
|
|
69
|
+
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/isObjectLike.js
|
|
70
|
+
function isObjectLike(value) {
|
|
71
|
+
return value != null && typeof value == "object";
|
|
72
|
+
}
|
|
73
|
+
var isObjectLike_default = isObjectLike;
|
|
74
|
+
|
|
75
|
+
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/isNumber.js
|
|
76
|
+
var numberTag = "[object Number]";
|
|
77
|
+
function isNumber(value) {
|
|
78
|
+
return typeof value == "number" || isObjectLike_default(value) && baseGetTag_default(value) == numberTag;
|
|
79
|
+
}
|
|
80
|
+
var isNumber_default = isNumber;
|
|
81
|
+
|
|
82
|
+
// ../../node_modules/.pnpm/lodash-es@4.17.21/node_modules/lodash-es/isString.js
|
|
83
|
+
var stringTag = "[object String]";
|
|
84
|
+
function isString(value) {
|
|
85
|
+
return typeof value == "string" || !isArray_default(value) && isObjectLike_default(value) && baseGetTag_default(value) == stringTag;
|
|
86
|
+
}
|
|
87
|
+
var isString_default = isString;
|
|
88
|
+
|
|
89
|
+
export { Symbol_default, baseGetTag_default, freeGlobal_default, isArray_default, isNumber_default, isObjectLike_default, isObject_default, isString_default, root_default };
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { CONTENT_SEO_VALUE_KEYS, CONTENT_RELATED_VALUE_STORAGE_KEY, CONTENT_PRIVATE_VALUE_KEYS, contentEntryStatusSchema, isContentPrivateValueKey, getContentFieldValueSchema } from './chunk-IQ5GRTXO.mjs';
|
|
2
|
+
import { stringify, parseDocument } from 'yaml';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
var frontmatterDataSchema = z.record(z.string(), z.unknown());
|
|
6
|
+
var CONTENT_FRONTMATTER_PATTERN = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/u;
|
|
7
|
+
function parseContentFrontmatterYaml(yamlText) {
|
|
8
|
+
try {
|
|
9
|
+
const document = parseDocument(yamlText, {
|
|
10
|
+
merge: false,
|
|
11
|
+
schema: "core",
|
|
12
|
+
strict: true,
|
|
13
|
+
uniqueKeys: true
|
|
14
|
+
});
|
|
15
|
+
if (document.errors.length > 0) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
const parsed = frontmatterDataSchema.safeParse(
|
|
19
|
+
document.toJS({ maxAliasCount: 20 })
|
|
20
|
+
);
|
|
21
|
+
return parsed.success ? parsed.data : null;
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function parseContentFrontmatter(raw) {
|
|
27
|
+
const match = raw.match(CONTENT_FRONTMATTER_PATTERN);
|
|
28
|
+
if (!match) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
const values = parseContentFrontmatterYaml(match[1] ?? "");
|
|
32
|
+
if (values === null) {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
values,
|
|
37
|
+
body: raw.slice(match[0].length).replace(/^\r?\n/u, "")
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
var CONTENT_MANAGED_ENTRY_VALUE_KEYS = [
|
|
41
|
+
"slug",
|
|
42
|
+
"title",
|
|
43
|
+
"status",
|
|
44
|
+
"author",
|
|
45
|
+
"authorInitials",
|
|
46
|
+
"excerpt",
|
|
47
|
+
"coverImage",
|
|
48
|
+
"coverImageAlt",
|
|
49
|
+
"tags",
|
|
50
|
+
"updatedAt",
|
|
51
|
+
"publishedAt",
|
|
52
|
+
"archivedAt",
|
|
53
|
+
...CONTENT_SEO_VALUE_KEYS,
|
|
54
|
+
CONTENT_RELATED_VALUE_STORAGE_KEY,
|
|
55
|
+
...CONTENT_PRIVATE_VALUE_KEYS
|
|
56
|
+
];
|
|
57
|
+
var contentEntryFrontmatterSchema = z.object({
|
|
58
|
+
slug: z.unknown().optional(),
|
|
59
|
+
title: z.string().nullish(),
|
|
60
|
+
status: contentEntryStatusSchema,
|
|
61
|
+
author: z.string().nullish(),
|
|
62
|
+
authorInitials: z.string().nullish(),
|
|
63
|
+
excerpt: z.string().nullish(),
|
|
64
|
+
coverImage: z.string().nullish(),
|
|
65
|
+
coverImageAlt: z.string().nullish(),
|
|
66
|
+
tags: z.array(z.string()).nullish(),
|
|
67
|
+
updatedAt: z.string().nullish(),
|
|
68
|
+
publishedAt: z.string().nullish(),
|
|
69
|
+
archivedAt: z.string().nullish(),
|
|
70
|
+
seoTitle: z.string().nullish(),
|
|
71
|
+
seoDescription: z.string().nullish(),
|
|
72
|
+
related: z.array(z.string()).nullish(),
|
|
73
|
+
scratchpad: z.unknown().optional(),
|
|
74
|
+
backlinks: z.unknown().optional()
|
|
75
|
+
}).passthrough();
|
|
76
|
+
function parseContentEntryDocument(raw) {
|
|
77
|
+
const frontmatter = parseContentFrontmatter(raw);
|
|
78
|
+
if (!frontmatter) {
|
|
79
|
+
return {
|
|
80
|
+
status: "invalid",
|
|
81
|
+
message: "Entry frontmatter is missing or contains invalid YAML."
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
const parsedValues = contentEntryFrontmatterSchema.safeParse(
|
|
85
|
+
frontmatter.values
|
|
86
|
+
);
|
|
87
|
+
if (!parsedValues.success) {
|
|
88
|
+
const issue = parsedValues.error.issues[0];
|
|
89
|
+
const field = issue?.path.join(".");
|
|
90
|
+
return {
|
|
91
|
+
status: "invalid",
|
|
92
|
+
message: `${field ? `Invalid ${field}: ` : ""}${issue?.message ?? "Invalid entry fields."}`
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
status: "ready",
|
|
97
|
+
document: {
|
|
98
|
+
values: parsedValues.data,
|
|
99
|
+
body: frontmatter.body
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function serializeContentEntryDocument({
|
|
104
|
+
values,
|
|
105
|
+
body
|
|
106
|
+
}) {
|
|
107
|
+
const yamlText = stringify(values).replace(/\n+$/u, "");
|
|
108
|
+
const normalizedBody = `${body.replace(/\s+$/u, "")}
|
|
109
|
+
`;
|
|
110
|
+
return `---
|
|
111
|
+
${yamlText}
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
${normalizedBody}`;
|
|
115
|
+
}
|
|
116
|
+
function getContentRenderableEntryValues({
|
|
117
|
+
document,
|
|
118
|
+
slug
|
|
119
|
+
}) {
|
|
120
|
+
const publicFrontmatterValues = Object.fromEntries(
|
|
121
|
+
Object.entries(document.values).filter(([fieldId]) => {
|
|
122
|
+
return !isContentPrivateValueKey(fieldId);
|
|
123
|
+
})
|
|
124
|
+
);
|
|
125
|
+
return {
|
|
126
|
+
...publicFrontmatterValues,
|
|
127
|
+
relatedContent: publicFrontmatterValues[CONTENT_RELATED_VALUE_STORAGE_KEY] ?? [],
|
|
128
|
+
body: document.body,
|
|
129
|
+
slug
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function getContentEntryFieldValidationIssues({
|
|
133
|
+
fields,
|
|
134
|
+
values,
|
|
135
|
+
requireRequiredFields
|
|
136
|
+
}) {
|
|
137
|
+
return fields.flatMap((field) => {
|
|
138
|
+
if (!field.enabled || isContentPrivateValueKey(field.id)) {
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
const value = values[field.id];
|
|
142
|
+
const isMissing = value === void 0 || value === null || value === "";
|
|
143
|
+
if (isMissing) {
|
|
144
|
+
return requireRequiredFields && field.required ? [{ fieldId: field.id, message: "Required value is missing." }] : [];
|
|
145
|
+
}
|
|
146
|
+
const parsedValue = getContentFieldValueSchema(field.type).safeParse(value);
|
|
147
|
+
return parsedValue.success ? [] : [{ fieldId: field.id, message: "Value has the wrong format." }];
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export { CONTENT_MANAGED_ENTRY_VALUE_KEYS, contentEntryFrontmatterSchema, getContentEntryFieldValidationIssues, getContentRenderableEntryValues, parseContentEntryDocument, serializeContentEntryDocument };
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// ../schemas/contentCollection.ts
|
|
4
|
+
var CONTENT_MANIFEST_FILENAME = "content-manifest.generated.json";
|
|
5
|
+
var CONTENT_DIRNAME = "content";
|
|
6
|
+
var CONTENT_COLLECTION_SCHEMA_FILENAME = "_collection.json";
|
|
7
|
+
var CONTENT_ENTRY_FILE_EXTENSION = ".mdx";
|
|
8
|
+
var CONTENT_RELATED_VALUE_STORAGE_KEY = "related";
|
|
9
|
+
var CONTENT_SLUG_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
|
|
10
|
+
var CONTENT_MAX_SLUG_LENGTH = 100;
|
|
11
|
+
var contentSlugSchema = z.string().min(1).max(CONTENT_MAX_SLUG_LENGTH).regex(CONTENT_SLUG_PATTERN);
|
|
12
|
+
function isValidContentSlug(slug) {
|
|
13
|
+
return contentSlugSchema.safeParse(slug).success;
|
|
14
|
+
}
|
|
15
|
+
var CONTENT_ENTRY_STATUSES = [
|
|
16
|
+
"draft",
|
|
17
|
+
"published",
|
|
18
|
+
"archived"
|
|
19
|
+
];
|
|
20
|
+
var contentEntryStatusSchema = z.enum(CONTENT_ENTRY_STATUSES);
|
|
21
|
+
function isContentEntryPubliclyVisible({
|
|
22
|
+
status,
|
|
23
|
+
archivedAt,
|
|
24
|
+
allowDrafts
|
|
25
|
+
}) {
|
|
26
|
+
if (archivedAt !== null && archivedAt !== void 0) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
return status === "published" || allowDrafts && status === "draft";
|
|
30
|
+
}
|
|
31
|
+
var contentCollectionFieldTypeSchema = z.enum([
|
|
32
|
+
"short-text",
|
|
33
|
+
"long-text",
|
|
34
|
+
"rich-text",
|
|
35
|
+
"image",
|
|
36
|
+
"tags",
|
|
37
|
+
"related",
|
|
38
|
+
"date",
|
|
39
|
+
"url",
|
|
40
|
+
"status",
|
|
41
|
+
"boolean"
|
|
42
|
+
]);
|
|
43
|
+
var contentCollectionColorSchema = z.enum([
|
|
44
|
+
"seashell",
|
|
45
|
+
"yellow",
|
|
46
|
+
"lightGreen",
|
|
47
|
+
"darkGreen",
|
|
48
|
+
"lightPink",
|
|
49
|
+
"magenta",
|
|
50
|
+
"purple",
|
|
51
|
+
"blue"
|
|
52
|
+
]);
|
|
53
|
+
var contentCollectionFieldSchema = z.object({
|
|
54
|
+
id: z.string().min(1).max(128),
|
|
55
|
+
label: z.string().min(1).max(200),
|
|
56
|
+
type: contentCollectionFieldTypeSchema,
|
|
57
|
+
required: z.boolean(),
|
|
58
|
+
enabled: z.boolean(),
|
|
59
|
+
helpText: z.string().optional(),
|
|
60
|
+
builtin: z.boolean()
|
|
61
|
+
});
|
|
62
|
+
var contentCollectionFileSchema = z.object({
|
|
63
|
+
name: z.string().min(1).max(200),
|
|
64
|
+
description: z.string(),
|
|
65
|
+
icon: z.string().min(1),
|
|
66
|
+
color: contentCollectionColorSchema,
|
|
67
|
+
tags: z.array(z.string()),
|
|
68
|
+
fields: z.array(contentCollectionFieldSchema).max(200),
|
|
69
|
+
archivedAt: z.iso.datetime().nullish(),
|
|
70
|
+
hasListingPage: z.boolean().optional()
|
|
71
|
+
}).passthrough();
|
|
72
|
+
function isSafeContentUrl(value, { image }) {
|
|
73
|
+
try {
|
|
74
|
+
const url = new URL(value, "https://content.local");
|
|
75
|
+
return image ? url.protocol === "http:" || url.protocol === "https:" : ["http:", "https:", "mailto:", "tel:"].includes(url.protocol);
|
|
76
|
+
} catch {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function getContentFieldValueSchema(fieldType) {
|
|
81
|
+
if (fieldType === "short-text" || fieldType === "long-text" || fieldType === "rich-text") {
|
|
82
|
+
return z.string();
|
|
83
|
+
}
|
|
84
|
+
if (fieldType === "image") {
|
|
85
|
+
return z.string().refine((value) => isSafeContentUrl(value, { image: true }));
|
|
86
|
+
}
|
|
87
|
+
if (fieldType === "tags" || fieldType === "related") {
|
|
88
|
+
return z.array(z.string());
|
|
89
|
+
}
|
|
90
|
+
if (fieldType === "date") {
|
|
91
|
+
return z.string().refine((value) => !Number.isNaN(Date.parse(value)));
|
|
92
|
+
}
|
|
93
|
+
if (fieldType === "url") {
|
|
94
|
+
return z.string().refine((value) => isSafeContentUrl(value, { image: false }));
|
|
95
|
+
}
|
|
96
|
+
if (fieldType === "status") {
|
|
97
|
+
return contentEntryStatusSchema;
|
|
98
|
+
}
|
|
99
|
+
return z.boolean();
|
|
100
|
+
}
|
|
101
|
+
var CONTENT_RESERVED_COLLECTION_SLUGS = ["api", "new"];
|
|
102
|
+
var CONTENT_RESERVED_ENTRY_SLUGS = ["new"];
|
|
103
|
+
var reservedEntrySlugSet = new Set(
|
|
104
|
+
CONTENT_RESERVED_ENTRY_SLUGS
|
|
105
|
+
);
|
|
106
|
+
function isContentReservedEntrySlug(slug) {
|
|
107
|
+
return reservedEntrySlugSet.has(slug);
|
|
108
|
+
}
|
|
109
|
+
var CONTENT_BUILTIN_FIELD_ROLES = {
|
|
110
|
+
title: "content",
|
|
111
|
+
author: "content",
|
|
112
|
+
excerpt: "content",
|
|
113
|
+
coverImage: "content",
|
|
114
|
+
body: "content",
|
|
115
|
+
tags: "content",
|
|
116
|
+
relatedContent: "content",
|
|
117
|
+
slug: "publishing",
|
|
118
|
+
status: "publishing",
|
|
119
|
+
publishedAt: "publishing"
|
|
120
|
+
};
|
|
121
|
+
var CONTENT_SEO_VALUE_KEYS = ["seoTitle", "seoDescription"];
|
|
122
|
+
var CONTENT_PRIVATE_VALUE_KEYS = ["scratchpad", "backlinks"];
|
|
123
|
+
var CONTENT_COUPLED_VALUE_PARENTS = {
|
|
124
|
+
coverImageAlt: "coverImage",
|
|
125
|
+
authorInitials: "author"
|
|
126
|
+
};
|
|
127
|
+
var privateValueKeySet = new Set(
|
|
128
|
+
CONTENT_PRIVATE_VALUE_KEYS
|
|
129
|
+
);
|
|
130
|
+
var seoValueKeySet = new Set(CONTENT_SEO_VALUE_KEYS);
|
|
131
|
+
var reservedCollectionSlugSet = new Set(
|
|
132
|
+
CONTENT_RESERVED_COLLECTION_SLUGS
|
|
133
|
+
);
|
|
134
|
+
function formatContentEntryRef({
|
|
135
|
+
collectionSlug,
|
|
136
|
+
entrySlug
|
|
137
|
+
}) {
|
|
138
|
+
return `${collectionSlug}/${entrySlug}`;
|
|
139
|
+
}
|
|
140
|
+
function parseContentEntryRef(ref) {
|
|
141
|
+
const normalized = ref.replace(/^content\//u, "").replace(/\.mdx$/u, "");
|
|
142
|
+
const segments = normalized.split("/");
|
|
143
|
+
if (segments.length !== 2) {
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
const [collectionSlug, entrySlug] = segments;
|
|
147
|
+
if (!collectionSlug || !entrySlug) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
return { collectionSlug, entrySlug };
|
|
151
|
+
}
|
|
152
|
+
function isContentPrivateValueKey(key) {
|
|
153
|
+
return privateValueKeySet.has(key);
|
|
154
|
+
}
|
|
155
|
+
function isContentSeoValueKey(key) {
|
|
156
|
+
return seoValueKeySet.has(key);
|
|
157
|
+
}
|
|
158
|
+
function isContentReservedCollectionSlug(slug) {
|
|
159
|
+
return reservedCollectionSlugSet.has(slug);
|
|
160
|
+
}
|
|
161
|
+
function getContentFieldRole(fieldId) {
|
|
162
|
+
if (isContentSeoValueKey(fieldId)) {
|
|
163
|
+
return "seo";
|
|
164
|
+
}
|
|
165
|
+
if (isContentPrivateValueKey(fieldId)) {
|
|
166
|
+
return "private";
|
|
167
|
+
}
|
|
168
|
+
const builtinRoles = CONTENT_BUILTIN_FIELD_ROLES;
|
|
169
|
+
return builtinRoles[fieldId] ?? "content";
|
|
170
|
+
}
|
|
171
|
+
function getPublishedContentFields(fields) {
|
|
172
|
+
return fields.filter((field) => {
|
|
173
|
+
return field.enabled && getContentFieldRole(field.id) !== "private";
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
function getOrderedContentFields(fields) {
|
|
177
|
+
return fields.filter((field) => {
|
|
178
|
+
return field.enabled && getContentFieldRole(field.id) === "content";
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export { CONTENT_BUILTIN_FIELD_ROLES, CONTENT_COLLECTION_SCHEMA_FILENAME, CONTENT_COUPLED_VALUE_PARENTS, CONTENT_DIRNAME, CONTENT_ENTRY_FILE_EXTENSION, CONTENT_ENTRY_STATUSES, CONTENT_MANIFEST_FILENAME, CONTENT_MAX_SLUG_LENGTH, CONTENT_PRIVATE_VALUE_KEYS, CONTENT_RELATED_VALUE_STORAGE_KEY, CONTENT_RESERVED_COLLECTION_SLUGS, CONTENT_RESERVED_ENTRY_SLUGS, CONTENT_SEO_VALUE_KEYS, CONTENT_SLUG_PATTERN, contentCollectionColorSchema, contentCollectionFieldSchema, contentCollectionFieldTypeSchema, contentCollectionFileSchema, contentEntryStatusSchema, contentSlugSchema, formatContentEntryRef, getContentFieldRole, getContentFieldValueSchema, getOrderedContentFields, getPublishedContentFields, isContentEntryPubliclyVisible, isContentPrivateValueKey, isContentReservedCollectionSlug, isContentReservedEntrySlug, isContentSeoValueKey, isSafeContentUrl, isValidContentSlug, parseContentEntryRef };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// ../replo-utils/lib/object.ts
|
|
2
|
+
function omit(obj, keysToOmit) {
|
|
3
|
+
const result = { ...obj };
|
|
4
|
+
for (const key of keysToOmit) {
|
|
5
|
+
delete result[key];
|
|
6
|
+
}
|
|
7
|
+
return result;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
// ../replo-utils/lib/json.ts
|
|
11
|
+
function parseJsonOrNull(json) {
|
|
12
|
+
try {
|
|
13
|
+
return JSON.parse(json);
|
|
14
|
+
} catch {
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function omitJsonObjectFieldsOrNull({
|
|
19
|
+
json,
|
|
20
|
+
fields
|
|
21
|
+
}) {
|
|
22
|
+
const parsedJson = parseJsonOrNull(json);
|
|
23
|
+
if (typeof parsedJson !== "object" || parsedJson === null || Array.isArray(parsedJson)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return jsonStringifyOrNull(
|
|
27
|
+
omit(Object.fromEntries(Object.entries(parsedJson)), fields)
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
function jsonStringifyOrNull(value) {
|
|
31
|
+
if (value === null || value === void 0) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
return JSON.stringify(value);
|
|
36
|
+
} catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export { jsonStringifyOrNull, omitJsonObjectFieldsOrNull, parseJsonOrNull };
|
package/content.d.ts
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import "server-only";
|
|
2
|
+
import type { Metadata } from "next";
|
|
3
|
+
import type { ReactNode } from "react";
|
|
4
|
+
import type { ContentManifest } from "./_vendor/schemas/contentManifest";
|
|
5
|
+
import { contentCollectionFieldSchema } from "./_vendor/schemas/contentCollection";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
/** Public name for the shared collection-field contract. */
|
|
8
|
+
export type ContentField = z.infer<typeof contentCollectionFieldSchema>;
|
|
9
|
+
export interface ContentEntry {
|
|
10
|
+
collection: {
|
|
11
|
+
name: string;
|
|
12
|
+
description: string;
|
|
13
|
+
slug: string;
|
|
14
|
+
};
|
|
15
|
+
fields: ContentField[];
|
|
16
|
+
slug: string;
|
|
17
|
+
body: string;
|
|
18
|
+
values: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
/** One entry's slot in a collection listing. */
|
|
21
|
+
export interface ContentCollectionEntry {
|
|
22
|
+
slug: string;
|
|
23
|
+
title: string;
|
|
24
|
+
excerpt: string | null;
|
|
25
|
+
coverImage: string | null;
|
|
26
|
+
coverImageAlt: string | null;
|
|
27
|
+
publishedAt: string | null;
|
|
28
|
+
status: "draft" | "published";
|
|
29
|
+
}
|
|
30
|
+
export interface ContentCollection {
|
|
31
|
+
slug: string;
|
|
32
|
+
name: string;
|
|
33
|
+
description: string;
|
|
34
|
+
entries: ContentCollectionEntry[];
|
|
35
|
+
}
|
|
36
|
+
/** An ordered content field paired with this entry's value for it. */
|
|
37
|
+
export interface ContentFieldWithValue {
|
|
38
|
+
field: ContentField;
|
|
39
|
+
value: unknown;
|
|
40
|
+
}
|
|
41
|
+
export interface ContentSitemapEntry {
|
|
42
|
+
url: string;
|
|
43
|
+
lastModified?: Date;
|
|
44
|
+
}
|
|
45
|
+
export interface ContentSource {
|
|
46
|
+
read(relativePath: string): Promise<string | null>;
|
|
47
|
+
listCollections(): Promise<string[]>;
|
|
48
|
+
listEntries(collection: string): Promise<string[]>;
|
|
49
|
+
}
|
|
50
|
+
interface ContentOptions {
|
|
51
|
+
contentSource?: ContentSource;
|
|
52
|
+
contentDirectory?: string;
|
|
53
|
+
contentManifest?: ContentManifest;
|
|
54
|
+
}
|
|
55
|
+
export declare function createContentSource({ contentDirectory, contentManifest, }?: {
|
|
56
|
+
contentDirectory?: string;
|
|
57
|
+
contentManifest?: ContentManifest;
|
|
58
|
+
}): ContentSource;
|
|
59
|
+
export declare function getContentEntry({ collection, slug, ...contentOptions }: {
|
|
60
|
+
collection: string;
|
|
61
|
+
slug: string;
|
|
62
|
+
} & ContentOptions): Promise<ContentEntry>;
|
|
63
|
+
/** Static params for one collection's `[slug]` detail route. */
|
|
64
|
+
export declare function getContentCollectionStaticParams({ collection, ...contentOptions }: {
|
|
65
|
+
collection: string;
|
|
66
|
+
} & ContentOptions): Promise<{
|
|
67
|
+
slug: string;
|
|
68
|
+
}[]>;
|
|
69
|
+
/**
|
|
70
|
+
* A collection and its renderable entries for a listing page. Uses the
|
|
71
|
+
* not-found contract when the collection is missing or archived.
|
|
72
|
+
*/
|
|
73
|
+
export declare function getContentCollection({ collection, ...contentOptions }: {
|
|
74
|
+
collection: string;
|
|
75
|
+
} & ContentOptions): Promise<ContentCollection>;
|
|
76
|
+
/**
|
|
77
|
+
* Per-entry head metadata (title/description/OpenGraph/Twitter) from the
|
|
78
|
+
* entry's SEO fields with content fallbacks. Returns an empty object when the
|
|
79
|
+
* entry is not renderable, so `generateMetadata` can call it unconditionally.
|
|
80
|
+
*/
|
|
81
|
+
export declare function getContentEntryMetadata({ collection, slug, ...contentOptions }: {
|
|
82
|
+
collection: string;
|
|
83
|
+
slug: string;
|
|
84
|
+
} & ContentOptions): Promise<Metadata>;
|
|
85
|
+
/**
|
|
86
|
+
* Structured data for an entry as a JSON-LD script tag. Serialization is
|
|
87
|
+
* script-safe (`<` escaped) so entry content can never break out of the tag.
|
|
88
|
+
* The type is a deliberately neutral `WebPage`; collections have no schema
|
|
89
|
+
* type of their own.
|
|
90
|
+
*/
|
|
91
|
+
export declare function ContentEntryJsonLd({ entry }: {
|
|
92
|
+
entry: ContentEntry;
|
|
93
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
94
|
+
/**
|
|
95
|
+
* Absolute sitemap entries for every collection listing and renderable entry.
|
|
96
|
+
* `origin` comes from the caller (e.g. the request's platform-validated Host)
|
|
97
|
+
* because site origins are not fixed at build time.
|
|
98
|
+
*/
|
|
99
|
+
export declare function getContentSitemapEntries({ origin, ...contentOptions }: {
|
|
100
|
+
origin: string;
|
|
101
|
+
} & ContentOptions): Promise<ContentSitemapEntry[]>;
|
|
102
|
+
/**
|
|
103
|
+
* The full sitemap XML document for a site's content (home, collection
|
|
104
|
+
* listings, entries). Kept here so the generated sitemap route stays a tiny,
|
|
105
|
+
* restylable-free shell around one call.
|
|
106
|
+
*/
|
|
107
|
+
export declare function getContentSitemapXml({ origin, ...contentOptions }: {
|
|
108
|
+
origin: string;
|
|
109
|
+
} & ContentOptions): Promise<string>;
|
|
110
|
+
/**
|
|
111
|
+
* The entry's renderable content fields in collection-settings order, paired
|
|
112
|
+
* with values. This is the published half of the shared ordering contract in
|
|
113
|
+
* `schemas/contentCollection`; empty values are omitted so templates never render
|
|
114
|
+
* bare labels.
|
|
115
|
+
*/
|
|
116
|
+
export declare function getContentFields(entry: ContentEntry): ContentFieldWithValue[];
|
|
117
|
+
export declare function ContentFieldValue({ field, value, }: {
|
|
118
|
+
field: ContentField;
|
|
119
|
+
value: unknown;
|
|
120
|
+
}): ReactNode;
|
|
121
|
+
/** A related entry resolved to a public link; unresolvable refs stay plain text. */
|
|
122
|
+
export interface ContentRelatedLink {
|
|
123
|
+
title: string;
|
|
124
|
+
href: string;
|
|
125
|
+
}
|
|
126
|
+
export {};
|