nextjs-cms 0.10.0 → 0.10.2
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/dist/api/actions/pages.d.ts +3 -3
- package/dist/api/trpc/root.d.ts +3 -3
- package/dist/api/trpc/routers/navigation.d.ts +3 -3
- package/dist/api/trpc/server.d.ts +9 -9
- package/dist/core/config/config-loader.d.ts +2 -2
- package/dist/core/fields/date-range.d.ts +4 -4
- package/dist/core/fields/inline-image.d.ts +45 -0
- package/dist/core/fields/inline-image.d.ts.map +1 -0
- package/dist/core/fields/inline-image.js +30 -0
- package/dist/core/fields/photo.d.ts +6 -6
- package/dist/core/fields/richText.d.ts +8 -8
- package/dist/core/fields/richText.d.ts.map +1 -1
- package/dist/core/fields/richText.js +4 -35
- package/dist/core/fields/select.d.ts +1 -1
- package/dist/core/helpers/index.d.ts +2 -0
- package/dist/core/helpers/index.d.ts.map +1 -1
- package/dist/core/helpers/index.js +1 -0
- package/dist/core/helpers/inline-image.d.ts +46 -0
- package/dist/core/helpers/inline-image.d.ts.map +1 -0
- package/dist/core/helpers/inline-image.js +30 -0
- package/dist/core/sections/category.d.ts +4 -4
- package/dist/core/sections/hasItems.d.ts +10 -10
- package/dist/core/sections/section.d.ts +3 -3
- package/dist/core/sections/simple.d.ts +4 -4
- package/dist/core/security/dom.d.ts +4 -0
- package/dist/core/security/dom.d.ts.map +1 -1
- package/dist/core/security/dom.js +5 -1
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -1
- package/dist/utils/utils.d.ts +13 -0
- package/dist/utils/utils.d.ts.map +1 -1
- package/dist/utils/utils.js +15 -0
- package/dist/validators/richText.d.ts.map +1 -1
- package/dist/validators/richText.js +35 -1
- package/package.json +2 -2
|
@@ -70,13 +70,13 @@ declare const optionsSchema: z.ZodObject<{
|
|
|
70
70
|
maxDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
71
71
|
defaultStartValue: z.ZodOptional<z.ZodDate>;
|
|
72
72
|
defaultEndValue: z.ZodOptional<z.ZodDate>;
|
|
73
|
+
order: z.ZodOptional<z.ZodNumber>;
|
|
74
|
+
localized: z.ZodOptional<z.ZodBoolean>;
|
|
73
75
|
label: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
74
76
|
required: z.ZodOptional<z.ZodBoolean>;
|
|
75
77
|
defaultValue: z.ZodOptional<z.ZodAny>;
|
|
76
|
-
order: z.ZodOptional<z.ZodNumber>;
|
|
77
78
|
conditionalRules: z.ZodOptional<z.ZodUnion<readonly [z.ZodTuple<[z.ZodCustom<import("../types/index.js").ConditionalRule, import("../types/index.js").ConditionalRule>], null>, z.ZodCustom<import("../types/index.js").ConditionalRuleGroup, import("../types/index.js").ConditionalRuleGroup>]>>;
|
|
78
79
|
adminGenerated: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodLiteral<false>, z.ZodLiteral<"readonly">]>>;
|
|
79
|
-
localized: z.ZodOptional<z.ZodBoolean>;
|
|
80
80
|
}, z.core.$strict>;
|
|
81
81
|
declare const dateRangeFieldConfigSchema: z.ZodObject<{
|
|
82
82
|
/**
|
|
@@ -97,13 +97,13 @@ declare const dateRangeFieldConfigSchema: z.ZodObject<{
|
|
|
97
97
|
maxDate: z.ZodOptional<z.ZodUnion<readonly [z.ZodDate, z.ZodLiteral<"now">]>>;
|
|
98
98
|
defaultStartValue: z.ZodOptional<z.ZodDate>;
|
|
99
99
|
defaultEndValue: z.ZodOptional<z.ZodDate>;
|
|
100
|
+
order: z.ZodOptional<z.ZodNumber>;
|
|
101
|
+
localized: z.ZodOptional<z.ZodBoolean>;
|
|
100
102
|
label: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
|
|
101
103
|
required: z.ZodOptional<z.ZodBoolean>;
|
|
102
104
|
defaultValue: z.ZodOptional<z.ZodAny>;
|
|
103
|
-
order: z.ZodOptional<z.ZodNumber>;
|
|
104
105
|
conditionalRules: z.ZodOptional<z.ZodUnion<readonly [z.ZodTuple<[z.ZodCustom<import("../types/index.js").ConditionalRule, import("../types/index.js").ConditionalRule>], null>, z.ZodCustom<import("../types/index.js").ConditionalRuleGroup, import("../types/index.js").ConditionalRuleGroup>]>>;
|
|
105
106
|
adminGenerated: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<true>, z.ZodLiteral<false>, z.ZodLiteral<"readonly">]>>;
|
|
106
|
-
localized: z.ZodOptional<z.ZodBoolean>;
|
|
107
107
|
}, z.core.$strict>;
|
|
108
108
|
export type DateRangeFieldConfig = z.infer<typeof dateRangeFieldConfigSchema>;
|
|
109
109
|
/**
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type sharp from 'sharp';
|
|
2
|
+
import type { Background } from '../helpers/background.js';
|
|
3
|
+
/**
|
|
4
|
+
* Inline-image sizing shared by the rich-text field, the editor upload route, and the client-side
|
|
5
|
+
* validator.
|
|
6
|
+
*
|
|
7
|
+
* Deliberately a leaf module with type-only imports: the validator runs in the browser, and
|
|
8
|
+
* reaching these through `core/fields/index.js` would pull the whole field barrel — and with it
|
|
9
|
+
* the database client, sharp, and fs — into the client bundle.
|
|
10
|
+
*/
|
|
11
|
+
export type InlineImageSize = {
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
fit: 'inside';
|
|
15
|
+
} | {
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
fit: 'cover';
|
|
19
|
+
} | {
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
fit: 'contain';
|
|
23
|
+
background?: Background;
|
|
24
|
+
};
|
|
25
|
+
export type InlineImageMaxFileSize = {
|
|
26
|
+
size: number;
|
|
27
|
+
unit: 'kb' | 'mb';
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Bounds every inline image is fitted into unless the field overrides `size`.
|
|
31
|
+
* 1200x675 is 16:9 at a width that covers typical article body content, scaled down to fit so a
|
|
32
|
+
* body image is never cropped or padded unless the field asks for it.
|
|
33
|
+
*/
|
|
34
|
+
export declare const DEFAULT_INLINE_IMAGE_SIZE: InlineImageSize;
|
|
35
|
+
/** Largest accepted inline image unless the field overrides `maxFileSize`. */
|
|
36
|
+
export declare const DEFAULT_MAX_FILE_SIZE: InlineImageMaxFileSize;
|
|
37
|
+
/** Same shape `extractInlineImages` looks for, so every side agrees on what an inline image is. */
|
|
38
|
+
export declare const INLINE_IMAGE_REGEX: RegExp;
|
|
39
|
+
/**
|
|
40
|
+
* Sharp resize options for an inline image, so every place that writes one — the field on save and
|
|
41
|
+
* the editor upload route — sizes it identically. `background` is the CMS-config fallback, used
|
|
42
|
+
* only when the field asks for `contain` without naming its own.
|
|
43
|
+
*/
|
|
44
|
+
export declare function inlineImageResizeOptions(size: InlineImageSize, background: Background): sharp.ResizeOptions;
|
|
45
|
+
//# sourceMappingURL=inline-image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-image.d.ts","sourceRoot":"","sources":["../../../src/core/fields/inline-image.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAE1D;;;;;;;GAOG;AAEH,MAAM,MAAM,eAAe,GACrB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,QAAQ,CAAA;CAAE,GAChD;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,GAC/C;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,SAAS,CAAC;IAAC,UAAU,CAAC,EAAE,UAAU,CAAA;CAAE,CAAA;AAEhF,MAAM,MAAM,sBAAsB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAA;CAAE,CAAA;AAExE;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,eAIvC,CAAA;AAED,8EAA8E;AAC9E,eAAO,MAAM,qBAAqB,EAAE,sBAAgD,CAAA;AAEpF,mGAAmG;AACnG,eAAO,MAAM,kBAAkB,QAAmD,CAAA;AAElF;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,GAAG,KAAK,CAAC,aAAa,CAU3G"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounds every inline image is fitted into unless the field overrides `size`.
|
|
3
|
+
* 1200x675 is 16:9 at a width that covers typical article body content, scaled down to fit so a
|
|
4
|
+
* body image is never cropped or padded unless the field asks for it.
|
|
5
|
+
*/
|
|
6
|
+
export const DEFAULT_INLINE_IMAGE_SIZE = {
|
|
7
|
+
width: 1200,
|
|
8
|
+
height: 675,
|
|
9
|
+
fit: 'inside',
|
|
10
|
+
};
|
|
11
|
+
/** Largest accepted inline image unless the field overrides `maxFileSize`. */
|
|
12
|
+
export const DEFAULT_MAX_FILE_SIZE = { size: 1, unit: 'mb' };
|
|
13
|
+
/** Same shape `extractInlineImages` looks for, so every side agrees on what an inline image is. */
|
|
14
|
+
export const INLINE_IMAGE_REGEX = /<img.*?src="(data:image\/.*?;base64,.*?)".*?>/g;
|
|
15
|
+
/**
|
|
16
|
+
* Sharp resize options for an inline image, so every place that writes one — the field on save and
|
|
17
|
+
* the editor upload route — sizes it identically. `background` is the CMS-config fallback, used
|
|
18
|
+
* only when the field asks for `contain` without naming its own.
|
|
19
|
+
*/
|
|
20
|
+
export function inlineImageResizeOptions(size, background) {
|
|
21
|
+
return {
|
|
22
|
+
width: size.width,
|
|
23
|
+
height: size.height,
|
|
24
|
+
fit: size.fit,
|
|
25
|
+
// `inside` only ever scales down — enlarging a smaller image to fill the box would cost
|
|
26
|
+
// quality for nothing. `cover` and `contain` produce the exact box, so they may enlarge.
|
|
27
|
+
withoutEnlargement: size.fit === 'inside',
|
|
28
|
+
...(size.fit === 'contain' ? { background: size.background ?? background } : {}),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -140,10 +140,10 @@ declare const configSchema: z.ZodObject<{
|
|
|
140
140
|
* @hint 'jpg' is an alias for 'jpeg'
|
|
141
141
|
*/
|
|
142
142
|
fileType: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
143
|
-
webp: "webp";
|
|
144
|
-
jpg: "jpg";
|
|
145
143
|
jpeg: "jpeg";
|
|
144
|
+
jpg: "jpg";
|
|
146
145
|
png: "png";
|
|
146
|
+
webp: "webp";
|
|
147
147
|
}>>>;
|
|
148
148
|
/**
|
|
149
149
|
* Remove the extension from the file name
|
|
@@ -353,10 +353,10 @@ declare const optionsSchema: z.ZodObject<{
|
|
|
353
353
|
* @hint 'jpg' is an alias for 'jpeg'
|
|
354
354
|
*/
|
|
355
355
|
fileType: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
356
|
-
webp: "webp";
|
|
357
|
-
jpg: "jpg";
|
|
358
356
|
jpeg: "jpeg";
|
|
357
|
+
jpg: "jpg";
|
|
359
358
|
png: "png";
|
|
359
|
+
webp: "webp";
|
|
360
360
|
}>>>;
|
|
361
361
|
/**
|
|
362
362
|
* Remove the extension from the file name
|
|
@@ -467,10 +467,10 @@ declare const photoFieldConfigSchema: z.ZodObject<{
|
|
|
467
467
|
* @hint 'jpg' is an alias for 'jpeg'
|
|
468
468
|
*/
|
|
469
469
|
fileType: z.ZodOptional<z.ZodArray<z.ZodEnum<{
|
|
470
|
-
webp: "webp";
|
|
471
|
-
jpg: "jpg";
|
|
472
470
|
jpeg: "jpeg";
|
|
471
|
+
jpg: "jpg";
|
|
473
472
|
png: "png";
|
|
473
|
+
webp: "webp";
|
|
474
474
|
}>>>;
|
|
475
475
|
/**
|
|
476
476
|
* Remove the extension from the file name
|
|
@@ -61,10 +61,10 @@ declare const allowImageUploadsSchema: z.ZodObject<{
|
|
|
61
61
|
*/
|
|
62
62
|
omitExtension: z.ZodOptional<z.ZodBoolean>;
|
|
63
63
|
format: z.ZodOptional<z.ZodEnum<{
|
|
64
|
-
webp: "webp";
|
|
65
|
-
jpg: "jpg";
|
|
66
64
|
jpeg: "jpeg";
|
|
65
|
+
jpg: "jpg";
|
|
67
66
|
png: "png";
|
|
67
|
+
webp: "webp";
|
|
68
68
|
}>>;
|
|
69
69
|
}, z.core.$strict>;
|
|
70
70
|
declare const configSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
@@ -133,10 +133,10 @@ declare const configSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
133
133
|
*/
|
|
134
134
|
omitExtension: z.ZodOptional<z.ZodBoolean>;
|
|
135
135
|
format: z.ZodOptional<z.ZodEnum<{
|
|
136
|
-
webp: "webp";
|
|
137
|
-
jpg: "jpg";
|
|
138
136
|
jpeg: "jpeg";
|
|
137
|
+
jpg: "jpg";
|
|
139
138
|
png: "png";
|
|
139
|
+
webp: "webp";
|
|
140
140
|
}>>;
|
|
141
141
|
}, z.core.$strict>>;
|
|
142
142
|
}, z.core.$strict>, z.ZodObject<{
|
|
@@ -261,10 +261,10 @@ declare const optionsSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
261
261
|
*/
|
|
262
262
|
omitExtension: z.ZodOptional<z.ZodBoolean>;
|
|
263
263
|
format: z.ZodOptional<z.ZodEnum<{
|
|
264
|
-
webp: "webp";
|
|
265
|
-
jpg: "jpg";
|
|
266
264
|
jpeg: "jpeg";
|
|
265
|
+
jpg: "jpg";
|
|
267
266
|
png: "png";
|
|
267
|
+
webp: "webp";
|
|
268
268
|
}>>;
|
|
269
269
|
}, z.core.$strict>>;
|
|
270
270
|
name: z.ZodString;
|
|
@@ -357,10 +357,10 @@ declare const richTextFieldConfigSchema: z.ZodIntersection<z.ZodDiscriminatedUni
|
|
|
357
357
|
*/
|
|
358
358
|
omitExtension: z.ZodOptional<z.ZodBoolean>;
|
|
359
359
|
format: z.ZodOptional<z.ZodEnum<{
|
|
360
|
-
webp: "webp";
|
|
361
|
-
jpg: "jpg";
|
|
362
360
|
jpeg: "jpeg";
|
|
361
|
+
jpg: "jpg";
|
|
363
362
|
png: "png";
|
|
363
|
+
webp: "webp";
|
|
364
364
|
}>>;
|
|
365
365
|
}, z.core.$strict>>;
|
|
366
366
|
name: z.ZodString;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"richText.d.ts","sourceRoot":"","sources":["../../../src/core/fields/richText.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,CAAC,MAAM,KAAK,CAAA;AAExB,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAMpE,OAAO,EAAoB,UAAU,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"richText.d.ts","sourceRoot":"","sources":["../../../src/core/fields/richText.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,9 +7,10 @@ import * as z from 'zod';
|
|
|
7
7
|
import { db } from '../../db/client.js';
|
|
8
8
|
import { EditorPhotosTable } from '../../db/schema.js';
|
|
9
9
|
import getString from '../../translations/index.js';
|
|
10
|
-
import { humanReadableFileSize } from '../../utils/index.js';
|
|
10
|
+
import { base64ByteLength, humanReadableFileSize, maxFileSizeToBytes } from '../../utils/index.js';
|
|
11
11
|
import { getCMSConfig } from '../config/index.js';
|
|
12
12
|
import { backgroundSchema, entityKind } from '../helpers/index.js';
|
|
13
|
+
import { DEFAULT_INLINE_IMAGE_SIZE, DEFAULT_MAX_FILE_SIZE, inlineImageResizeOptions } from '../helpers/inline-image.js';
|
|
13
14
|
import { sanitizeRichText } from '../security/dom.js';
|
|
14
15
|
import { baseFieldConfigSchema, Field } from './field.js';
|
|
15
16
|
const positiveInt = z.number().int().positive();
|
|
@@ -42,27 +43,6 @@ const inlineImageSizeSchema = z.discriminatedUnion('fit', [
|
|
|
42
43
|
background: backgroundSchema.optional().describe('Background color for padding'),
|
|
43
44
|
}),
|
|
44
45
|
]);
|
|
45
|
-
/**
|
|
46
|
-
* Bounds every inline image is fitted into unless the field overrides `size`.
|
|
47
|
-
* 1200x675 is 16:9 at a width that covers typical article body content, scaled down to fit so a
|
|
48
|
-
* body image is never cropped or padded unless the field asks for it.
|
|
49
|
-
*/
|
|
50
|
-
const DEFAULT_INLINE_IMAGE_SIZE = {
|
|
51
|
-
width: 1200,
|
|
52
|
-
height: 675,
|
|
53
|
-
fit: 'inside',
|
|
54
|
-
};
|
|
55
|
-
/** Largest accepted inline image unless the field overrides `maxFileSize`. */
|
|
56
|
-
const DEFAULT_MAX_FILE_SIZE = { size: 1, unit: 'mb' };
|
|
57
|
-
const BYTES_PER_UNIT = { kb: 1024, mb: 1024 * 1024 };
|
|
58
|
-
/**
|
|
59
|
-
* Decoded byte length of a base64 payload, without allocating the buffer — inline images are
|
|
60
|
-
* rejected before anything is decoded or written.
|
|
61
|
-
*/
|
|
62
|
-
function base64ByteLength(base64) {
|
|
63
|
-
const padding = base64.endsWith('==') ? 2 : base64.endsWith('=') ? 1 : 0;
|
|
64
|
-
return Math.floor((base64.length * 3) / 4) - padding;
|
|
65
|
-
}
|
|
66
46
|
/**
|
|
67
47
|
* Matches the `{name}` placeholder (whitespace tolerated: `{ name }`).
|
|
68
48
|
*
|
|
@@ -239,7 +219,7 @@ export class RichTextField extends Field {
|
|
|
239
219
|
const regex = /<img.*?src="(data:image\/.*?;base64,.*?)".*?>/g;
|
|
240
220
|
const matches = this.value.matchAll(regex);
|
|
241
221
|
const maxFileSize = this.allowImageUploads.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
|
|
242
|
-
const maxBytes = maxFileSize
|
|
222
|
+
const maxBytes = maxFileSizeToBytes(maxFileSize);
|
|
243
223
|
for (const match of matches) {
|
|
244
224
|
const base64String = match[1];
|
|
245
225
|
const extension = base64String.split(';')[0].split('/')[1];
|
|
@@ -294,18 +274,7 @@ export class RichTextField extends Field {
|
|
|
294
274
|
const image = sharp(buffer);
|
|
295
275
|
const size = this.allowImageUploads.size ?? DEFAULT_INLINE_IMAGE_SIZE;
|
|
296
276
|
await image
|
|
297
|
-
.resize(
|
|
298
|
-
width: size.width,
|
|
299
|
-
height: size.height,
|
|
300
|
-
fit: size.fit,
|
|
301
|
-
// `inside` only ever scales down — enlarging a smaller image to fill the box
|
|
302
|
-
// would cost quality for nothing. `cover` and `contain` produce the exact box,
|
|
303
|
-
// so they are allowed to enlarge.
|
|
304
|
-
withoutEnlargement: size.fit === 'inside',
|
|
305
|
-
...(size.fit === 'contain'
|
|
306
|
-
? { background: size.background ?? cmsConfig.media.images.background }
|
|
307
|
-
: {}),
|
|
308
|
-
})
|
|
277
|
+
.resize(inlineImageResizeOptions(size, cmsConfig.media.images.background))
|
|
309
278
|
.toFormat(this.allowImageUploads.format ?? 'webp')
|
|
310
279
|
.toFile(path.join(uploadsFolder, '.photos', sectionName, photo.name));
|
|
311
280
|
/**
|
|
@@ -379,8 +379,8 @@ declare const selectFieldConfigSchema: z.ZodIntersection<z.ZodUnion<readonly [z.
|
|
|
379
379
|
}, z.core.$strict>]>, z.ZodObject<{
|
|
380
380
|
type: z.ZodLiteral<"select">;
|
|
381
381
|
optionsType: z.ZodEnum<{
|
|
382
|
-
db: "db";
|
|
383
382
|
section: "section";
|
|
383
|
+
db: "db";
|
|
384
384
|
static: "static";
|
|
385
385
|
}>;
|
|
386
386
|
build: z.ZodFunction<z.core.$ZodFunctionArgs, z.ZodCustom<SelectField, SelectField>>;
|
|
@@ -4,6 +4,8 @@ export type { LZEntity } from './entity.js';
|
|
|
4
4
|
export { hasOwnEntityKind } from './entity.js';
|
|
5
5
|
export { evaluateConditionalRule, evaluateConditionalRuleGroup, normalizeConditionalRules, } from './conditional-rules.js';
|
|
6
6
|
export { backgroundSchema } from './background.js';
|
|
7
|
+
export { inlineImageResizeOptions, DEFAULT_INLINE_IMAGE_SIZE, DEFAULT_MAX_FILE_SIZE, INLINE_IMAGE_REGEX, } from './inline-image.js';
|
|
8
|
+
export type { InlineImageSize, InlineImageMaxFileSize } from './inline-image.js';
|
|
7
9
|
export type { Background } from './background.js';
|
|
8
10
|
export { watermarkPositionEnum, watermarkObjectSchema, watermarkConfigSchema, WATERMARK_DEFAULTS, resolveWatermark, } from './watermark.js';
|
|
9
11
|
export type { WatermarkPosition, WatermarkObject, WatermarkConfig, ResolvedWatermark } from './watermark.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/helpers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAI9C,OAAO,EACH,uBAAuB,EACvB,4BAA4B,EAC5B,yBAAyB,GAC5B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EACH,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,GACnB,MAAM,gBAAgB,CAAA;AACvB,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/helpers/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAA;AAChC,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAI9C,OAAO,EACH,uBAAuB,EACvB,4BAA4B,EAC5B,yBAAyB,GAC5B,MAAM,wBAAwB,CAAA;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAA;AAClD,OAAO,EACH,wBAAwB,EACxB,yBAAyB,EACzB,qBAAqB,EACrB,kBAAkB,GACrB,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAA;AAChF,YAAY,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AACjD,OAAO,EACH,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EAClB,gBAAgB,GACnB,MAAM,gBAAgB,CAAA;AACvB,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA"}
|
|
@@ -6,4 +6,5 @@ export { hasOwnEntityKind } from './entity.js';
|
|
|
6
6
|
// duplicate export when `core/index` merges both barrels.
|
|
7
7
|
export { evaluateConditionalRule, evaluateConditionalRuleGroup, normalizeConditionalRules, } from './conditional-rules.js';
|
|
8
8
|
export { backgroundSchema } from './background.js';
|
|
9
|
+
export { inlineImageResizeOptions, DEFAULT_INLINE_IMAGE_SIZE, DEFAULT_MAX_FILE_SIZE, INLINE_IMAGE_REGEX, } from './inline-image.js';
|
|
9
10
|
export { watermarkPositionEnum, watermarkObjectSchema, watermarkConfigSchema, WATERMARK_DEFAULTS, resolveWatermark, } from './watermark.js';
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type sharp from 'sharp';
|
|
2
|
+
import type { Background } from './background.js';
|
|
3
|
+
/**
|
|
4
|
+
* Inline-image sizing shared by the rich-text field, the editor upload route, and the client-side
|
|
5
|
+
* validator.
|
|
6
|
+
*
|
|
7
|
+
* Deliberately a leaf module with type-only imports. The validators are environment-neutral —
|
|
8
|
+
* the admin form calls them in the browser, and a consumer may call them server-side — so reaching
|
|
9
|
+
* these through a barrel that pulls the database client, sharp, or fs would break the client build.
|
|
10
|
+
* Import this file directly rather than via `core/helpers/index.js` for the same reason.
|
|
11
|
+
*/
|
|
12
|
+
export type InlineImageSize = {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
fit: 'inside';
|
|
16
|
+
} | {
|
|
17
|
+
width: number;
|
|
18
|
+
height: number;
|
|
19
|
+
fit: 'cover';
|
|
20
|
+
} | {
|
|
21
|
+
width: number;
|
|
22
|
+
height: number;
|
|
23
|
+
fit: 'contain';
|
|
24
|
+
background?: Background;
|
|
25
|
+
};
|
|
26
|
+
export type InlineImageMaxFileSize = {
|
|
27
|
+
size: number;
|
|
28
|
+
unit: 'kb' | 'mb';
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Bounds every inline image is fitted into unless the field overrides `size`.
|
|
32
|
+
* 1200x675 is 16:9 at a width that covers typical article body content, scaled down to fit so a
|
|
33
|
+
* body image is never cropped or padded unless the field asks for it.
|
|
34
|
+
*/
|
|
35
|
+
export declare const DEFAULT_INLINE_IMAGE_SIZE: InlineImageSize;
|
|
36
|
+
/** Largest accepted inline image unless the field overrides `maxFileSize`. */
|
|
37
|
+
export declare const DEFAULT_MAX_FILE_SIZE: InlineImageMaxFileSize;
|
|
38
|
+
/** Same shape `extractInlineImages` looks for, so every side agrees on what an inline image is. */
|
|
39
|
+
export declare const INLINE_IMAGE_REGEX: RegExp;
|
|
40
|
+
/**
|
|
41
|
+
* Sharp resize options for an inline image, so every place that writes one — the field on save and
|
|
42
|
+
* the editor upload route — sizes it identically. `background` is the CMS-config fallback, used
|
|
43
|
+
* only when the field asks for `contain` without naming its own.
|
|
44
|
+
*/
|
|
45
|
+
export declare function inlineImageResizeOptions(size: InlineImageSize, background: Background): sharp.ResizeOptions;
|
|
46
|
+
//# sourceMappingURL=inline-image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"inline-image.d.ts","sourceRoot":"","sources":["../../../src/core/helpers/inline-image.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAE9B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD;;;;;;;;GAQG;AAEH,MAAM,MAAM,eAAe,GACrB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,QAAQ,CAAA;CAAE,GAChD;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,OAAO,CAAA;CAAE,GAC/C;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,SAAS,CAAC;IAAC,UAAU,CAAC,EAAE,UAAU,CAAA;CAAE,CAAA;AAEhF,MAAM,MAAM,sBAAsB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,GAAG,IAAI,CAAA;CAAE,CAAA;AAExE;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,eAIvC,CAAA;AAED,8EAA8E;AAC9E,eAAO,MAAM,qBAAqB,EAAE,sBAAgD,CAAA;AAEpF,mGAAmG;AACnG,eAAO,MAAM,kBAAkB,QAAmD,CAAA;AAElF;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,GAAG,KAAK,CAAC,aAAa,CAU3G"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bounds every inline image is fitted into unless the field overrides `size`.
|
|
3
|
+
* 1200x675 is 16:9 at a width that covers typical article body content, scaled down to fit so a
|
|
4
|
+
* body image is never cropped or padded unless the field asks for it.
|
|
5
|
+
*/
|
|
6
|
+
export const DEFAULT_INLINE_IMAGE_SIZE = {
|
|
7
|
+
width: 1200,
|
|
8
|
+
height: 675,
|
|
9
|
+
fit: 'inside',
|
|
10
|
+
};
|
|
11
|
+
/** Largest accepted inline image unless the field overrides `maxFileSize`. */
|
|
12
|
+
export const DEFAULT_MAX_FILE_SIZE = { size: 1, unit: 'mb' };
|
|
13
|
+
/** Same shape `extractInlineImages` looks for, so every side agrees on what an inline image is. */
|
|
14
|
+
export const INLINE_IMAGE_REGEX = /<img.*?src="(data:image\/.*?;base64,.*?)".*?>/g;
|
|
15
|
+
/**
|
|
16
|
+
* Sharp resize options for an inline image, so every place that writes one — the field on save and
|
|
17
|
+
* the editor upload route — sizes it identically. `background` is the CMS-config fallback, used
|
|
18
|
+
* only when the field asks for `contain` without naming its own.
|
|
19
|
+
*/
|
|
20
|
+
export function inlineImageResizeOptions(size, background) {
|
|
21
|
+
return {
|
|
22
|
+
width: size.width,
|
|
23
|
+
height: size.height,
|
|
24
|
+
fit: size.fit,
|
|
25
|
+
// `inside` only ever scales down — enlarging a smaller image to fill the box would cost
|
|
26
|
+
// quality for nothing. `cover` and `contain` produce the exact box, so they may enlarge.
|
|
27
|
+
withoutEnlargement: size.fit === 'inside',
|
|
28
|
+
...(size.fit === 'contain' ? { background: size.background ?? background } : {}),
|
|
29
|
+
};
|
|
30
|
+
}
|