@shotstack/shotstack-canvas 1.6.5 → 1.7.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.
@@ -1,102 +1,124 @@
1
- declare const CANVAS_CONFIG: {
2
- DEFAULTS: {
3
- width: number;
4
- height: number;
5
- pixelRatio: number;
6
- fontFamily: string;
7
- fontSize: number;
8
- color: string;
9
- textAlign: "center";
10
- };
11
- LIMITS: {
12
- minWidth: number;
13
- maxWidth: number;
14
- minHeight: number;
15
- maxHeight: number;
16
- minFontSize: number;
17
- maxFontSize: number;
18
- minDuration: number;
19
- maxDuration: number;
20
- maxTextLength: number;
21
- };
22
- ANIMATION_TYPES: readonly ["typewriter", "fadeIn", "slideIn", "shift", "ascend", "movingLetters"];
23
- };
1
+ import { z } from 'zod';
2
+ import { components } from '@shotstack/schemas';
24
3
 
25
- type RichTextValidated = Required<{
26
- type: "rich-text";
27
- text: string;
28
- width?: number;
29
- height?: number;
30
- font?: {
31
- family: string;
32
- size: number;
33
- weight: string | number;
34
- color: string;
35
- opacity: number;
36
- background?: string;
37
- };
38
- style?: {
39
- letterSpacing: number;
40
- lineHeight: number;
41
- textTransform: "none" | "uppercase" | "lowercase" | "capitalize";
42
- textDecoration: "none" | "underline" | "line-through";
43
- gradient?: {
44
- type: "linear" | "radial";
45
- angle: number;
46
- stops: {
47
- offset: number;
48
- color: string;
49
- }[];
50
- };
51
- };
52
- stroke?: {
53
- width: number;
54
- color: string;
55
- opacity: number;
56
- };
57
- shadow?: {
58
- offsetX: number;
59
- offsetY: number;
60
- blur: number;
61
- color: string;
62
- opacity: number;
63
- };
64
- background?: {
65
- color?: string;
66
- opacity: number;
67
- };
68
- border?: {
69
- width: number;
70
- color: string;
71
- opacity: number;
72
- radius: number;
73
- };
74
- padding?: number | {
75
- top: number;
76
- right: number;
77
- bottom: number;
78
- left: number;
79
- };
80
- align?: {
81
- horizontal: "left" | "center" | "right";
82
- vertical: "top" | "middle" | "bottom";
83
- };
84
- animation?: {
85
- preset: typeof CANVAS_CONFIG.ANIMATION_TYPES[number];
86
- speed: number;
87
- duration?: number;
88
- style?: "character" | "word";
89
- direction?: "left" | "right" | "up" | "down";
90
- };
91
- customFonts?: {
92
- src: string;
93
- family: string;
94
- weight?: string | number;
95
- style?: string;
96
- originalFamily?: string;
97
- }[];
98
- }>;
4
+ declare const CanvasRichTextAssetSchema: z.ZodObject<{
5
+ type: z.ZodLiteral<"rich-text">;
6
+ text: z.ZodDefault<z.ZodString>;
7
+ width: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
8
+ height: z.ZodOptional<z.ZodDefault<z.ZodNumber>>;
9
+ font: z.ZodOptional<z.ZodObject<{
10
+ style: z.ZodOptional<z.ZodEnum<{
11
+ normal: "normal";
12
+ italic: "italic";
13
+ oblique: "oblique";
14
+ }>>;
15
+ family: z.ZodDefault<z.ZodString>;
16
+ size: z.ZodDefault<z.ZodNumber>;
17
+ weight: z.ZodDefault<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
18
+ color: z.ZodDefault<z.ZodString>;
19
+ opacity: z.ZodDefault<z.ZodNumber>;
20
+ background: z.ZodOptional<z.ZodString>;
21
+ }, z.core.$strip>>;
22
+ style: z.ZodOptional<z.ZodObject<{
23
+ letterSpacing: z.ZodDefault<z.ZodNumber>;
24
+ lineHeight: z.ZodDefault<z.ZodNumber>;
25
+ textTransform: z.ZodDefault<z.ZodEnum<{
26
+ none: "none";
27
+ uppercase: "uppercase";
28
+ lowercase: "lowercase";
29
+ capitalize: "capitalize";
30
+ }>>;
31
+ textDecoration: z.ZodDefault<z.ZodEnum<{
32
+ none: "none";
33
+ underline: "underline";
34
+ "line-through": "line-through";
35
+ }>>;
36
+ gradient: z.ZodOptional<z.ZodObject<{
37
+ type: z.ZodDefault<z.ZodEnum<{
38
+ linear: "linear";
39
+ radial: "radial";
40
+ }>>;
41
+ angle: z.ZodDefault<z.ZodNumber>;
42
+ stops: z.ZodArray<z.ZodObject<{
43
+ offset: z.ZodNumber;
44
+ color: z.ZodString;
45
+ }, z.core.$strip>>;
46
+ }, z.core.$strip>>;
47
+ }, z.core.$strip>>;
48
+ stroke: z.ZodOptional<z.ZodObject<{
49
+ width: z.ZodDefault<z.ZodNumber>;
50
+ color: z.ZodDefault<z.ZodString>;
51
+ opacity: z.ZodDefault<z.ZodNumber>;
52
+ }, z.core.$strip>>;
53
+ shadow: z.ZodOptional<z.ZodObject<{
54
+ offsetX: z.ZodDefault<z.ZodNumber>;
55
+ offsetY: z.ZodDefault<z.ZodNumber>;
56
+ blur: z.ZodDefault<z.ZodNumber>;
57
+ color: z.ZodDefault<z.ZodString>;
58
+ opacity: z.ZodDefault<z.ZodNumber>;
59
+ }, z.core.$strip>>;
60
+ background: z.ZodOptional<z.ZodObject<{
61
+ borderRadius: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
62
+ color: z.ZodOptional<z.ZodString>;
63
+ opacity: z.ZodDefault<z.ZodNumber>;
64
+ }, z.core.$strip>>;
65
+ border: z.ZodOptional<z.ZodObject<{
66
+ width: z.ZodDefault<z.ZodNumber>;
67
+ color: z.ZodDefault<z.ZodString>;
68
+ opacity: z.ZodDefault<z.ZodNumber>;
69
+ radius: z.ZodDefault<z.ZodNumber>;
70
+ }, z.core.$strip>>;
71
+ padding: z.ZodOptional<z.ZodUnion<readonly [z.ZodNumber, z.ZodObject<{
72
+ top: z.ZodDefault<z.ZodNumber>;
73
+ right: z.ZodDefault<z.ZodNumber>;
74
+ bottom: z.ZodDefault<z.ZodNumber>;
75
+ left: z.ZodDefault<z.ZodNumber>;
76
+ }, z.core.$strip>]>>;
77
+ align: z.ZodOptional<z.ZodObject<{
78
+ horizontal: z.ZodDefault<z.ZodEnum<{
79
+ center: "center";
80
+ right: "right";
81
+ left: "left";
82
+ }>>;
83
+ vertical: z.ZodDefault<z.ZodEnum<{
84
+ top: "top";
85
+ bottom: "bottom";
86
+ middle: "middle";
87
+ }>>;
88
+ }, z.core.$strip>>;
89
+ animation: z.ZodOptional<z.ZodObject<{
90
+ preset: z.ZodEnum<{
91
+ typewriter: "typewriter";
92
+ fadeIn: "fadeIn";
93
+ slideIn: "slideIn";
94
+ shift: "shift";
95
+ ascend: "ascend";
96
+ movingLetters: "movingLetters";
97
+ }>;
98
+ speed: z.ZodDefault<z.ZodNumber>;
99
+ duration: z.ZodOptional<z.ZodNumber>;
100
+ style: z.ZodOptional<z.ZodEnum<{
101
+ character: "character";
102
+ word: "word";
103
+ }>>;
104
+ direction: z.ZodOptional<z.ZodEnum<{
105
+ right: "right";
106
+ left: "left";
107
+ up: "up";
108
+ down: "down";
109
+ }>>;
110
+ }, z.core.$strip>>;
111
+ customFonts: z.ZodOptional<z.ZodArray<z.ZodObject<{
112
+ src: z.ZodString;
113
+ family: z.ZodString;
114
+ weight: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>>;
115
+ style: z.ZodOptional<z.ZodString>;
116
+ originalFamily: z.ZodOptional<z.ZodString>;
117
+ }, z.core.$strip>>>;
118
+ }, z.core.$strict>;
119
+ type CanvasRichTextAsset = z.infer<typeof CanvasRichTextAssetSchema>;
99
120
 
121
+ type ShotstackRichTextAsset = components["schemas"]["RichTextAsset"];
100
122
  type RGBA = {
101
123
  r: number;
102
124
  g: number;
@@ -218,7 +240,7 @@ type Renderer = {
218
240
  render(ops: DrawOp[]): Promise<void>;
219
241
  toPNG?: () => Promise<Buffer>;
220
242
  };
221
- type ValidAsset = RichTextValidated;
243
+ type ValidAsset = CanvasRichTextAsset;
222
244
  declare const isShadowFill: (op: DrawOp) => op is Extract<DrawOp, {
223
245
  op: "FillPath";
224
246
  }> & {
@@ -255,7 +277,7 @@ declare function createTextEngine(opts?: {
255
277
  wasmBaseURL?: string;
256
278
  }): Promise<{
257
279
  validate(input: unknown): {
258
- value: RichTextValidated;
280
+ value: CanvasRichTextAsset;
259
281
  };
260
282
  registerFontFromFile(path: string, desc: {
261
283
  family: string;
@@ -265,7 +287,7 @@ declare function createTextEngine(opts?: {
265
287
  family: string;
266
288
  weight?: string | number;
267
289
  }): Promise<void>;
268
- renderFrame(asset: RichTextValidated, tSeconds: number, clipDuration?: number): Promise<DrawOp[]>;
290
+ renderFrame(asset: CanvasRichTextAsset, tSeconds: number, clipDuration?: number): Promise<DrawOp[]>;
269
291
  createRenderer(p: {
270
292
  width?: number;
271
293
  height?: number;
@@ -274,8 +296,8 @@ declare function createTextEngine(opts?: {
274
296
  render(ops: DrawOp[]): Promise<void>;
275
297
  toPNG(): Promise<Buffer>;
276
298
  }>;
277
- generateVideo(asset: RichTextValidated, options: Partial<VideoGenerationOptions>): Promise<string>;
299
+ generateVideo(asset: CanvasRichTextAsset, options: Partial<VideoGenerationOptions>): Promise<string>;
278
300
  destroy(): void;
279
301
  }>;
280
302
 
281
- export { type DrawOp, type EngineInit, type Glyph, type GradientSpec, type RGBA, type Renderer, type ShapedLine, type ValidAsset, createTextEngine, isGlyphFill, isShadowFill };
303
+ export { type CanvasRichTextAsset, CanvasRichTextAssetSchema, type DrawOp, type EngineInit, type Glyph, type GradientSpec, type RGBA, type Renderer, type ShapedLine, type ShotstackRichTextAsset, type ValidAsset, createTextEngine, isGlyphFill, isShadowFill };
@@ -1,5 +1,15 @@
1
- // src/schema/asset-schema.ts
2
- import Joi from "joi";
1
+ // src/schema/zod-schema.ts
2
+ import { z } from "zod";
3
+ import {
4
+ richTextAssetSchema,
5
+ richTextFontSchema,
6
+ richTextStyleSchema,
7
+ richTextStrokeSchema,
8
+ richTextShadowSchema,
9
+ richTextBackgroundSchema,
10
+ richTextAlignmentSchema,
11
+ richTextAnimationSchema
12
+ } from "@shotstack/schemas/zod";
3
13
 
4
14
  // src/config/canvas-constants.ts
5
15
  var CANVAS_CONFIG = {
@@ -33,110 +43,127 @@ var CANVAS_CONFIG = {
33
43
  ]
34
44
  };
35
45
 
36
- // src/schema/asset-schema.ts
46
+ // src/schema/zod-schema.ts
37
47
  var HEX6 = /^#[A-Fa-f0-9]{6}$/;
38
- var gradientSchema = Joi.object({
39
- type: Joi.string().valid("linear", "radial").default("linear"),
40
- angle: Joi.number().min(0).max(360).default(0),
41
- stops: Joi.array().items(
42
- Joi.object({
43
- offset: Joi.number().min(0).max(1).required(),
44
- color: Joi.string().pattern(HEX6).required()
45
- }).unknown(false)
46
- ).min(2).required()
47
- }).unknown(false);
48
- var shadowSchema = Joi.object({
49
- offsetX: Joi.number().default(0),
50
- offsetY: Joi.number().default(0),
51
- blur: Joi.number().min(0).default(0),
52
- color: Joi.string().pattern(HEX6).default("#000000"),
53
- opacity: Joi.number().min(0).max(1).default(0.5)
54
- }).unknown(false);
55
- var strokeSchema = Joi.object({
56
- width: Joi.number().min(0).default(0),
57
- color: Joi.string().pattern(HEX6).default("#000000"),
58
- opacity: Joi.number().min(0).max(1).default(1)
59
- }).unknown(false);
60
- var fontSchema = Joi.object({
61
- family: Joi.string().default(CANVAS_CONFIG.DEFAULTS.fontFamily),
62
- size: Joi.number().min(CANVAS_CONFIG.LIMITS.minFontSize).max(CANVAS_CONFIG.LIMITS.maxFontSize).default(CANVAS_CONFIG.DEFAULTS.fontSize),
63
- weight: Joi.alternatives().try(Joi.string(), Joi.number()).default("400"),
64
- color: Joi.string().pattern(HEX6).default(CANVAS_CONFIG.DEFAULTS.color),
65
- opacity: Joi.number().min(0).max(1).default(1),
66
- background: Joi.string().pattern(HEX6).optional()
67
- }).unknown(false);
68
- var styleSchema = Joi.object({
69
- letterSpacing: Joi.number().default(0),
70
- lineHeight: Joi.number().min(0).max(10).default(1.2),
71
- textTransform: Joi.string().valid("none", "uppercase", "lowercase", "capitalize").default("none"),
72
- textDecoration: Joi.string().valid("none", "underline", "line-through").default("none"),
73
- gradient: gradientSchema.optional()
74
- }).unknown(false);
75
- var alignmentSchema = Joi.object({
76
- horizontal: Joi.string().valid("left", "center", "right").default(CANVAS_CONFIG.DEFAULTS.textAlign),
77
- vertical: Joi.string().valid("top", "middle", "bottom").default("middle")
78
- }).unknown(false);
79
- var animationSchema = Joi.object({
80
- preset: Joi.string().valid(...CANVAS_CONFIG.ANIMATION_TYPES),
81
- speed: Joi.number().min(0.1).max(10).default(1),
82
- duration: Joi.number().min(CANVAS_CONFIG.LIMITS.minDuration).max(CANVAS_CONFIG.LIMITS.maxDuration).optional(),
83
- style: Joi.string().valid("character", "word").optional().when("preset", {
84
- is: Joi.valid("typewriter", "shift", "fadeIn", "slideIn"),
85
- then: Joi.optional(),
86
- otherwise: Joi.forbidden()
87
- }),
88
- direction: Joi.string().optional().when("preset", {
89
- switch: [
90
- { is: "ascend", then: Joi.valid("up", "down") },
91
- { is: "shift", then: Joi.valid("left", "right", "up", "down") },
92
- { is: "slideIn", then: Joi.valid("left", "right", "up", "down") },
93
- { is: "movingLetters", then: Joi.valid("left", "right", "up", "down") }
94
- ],
95
- otherwise: Joi.forbidden()
48
+ var customFontSchema = z.object({
49
+ src: z.string().url(),
50
+ family: z.string(),
51
+ weight: z.union([z.string(), z.number()]).optional(),
52
+ style: z.string().optional(),
53
+ originalFamily: z.string().optional()
54
+ });
55
+ var borderSchema = z.object({
56
+ width: z.number().min(0).default(0),
57
+ color: z.string().regex(HEX6).default("#000000"),
58
+ opacity: z.number().min(0).max(1).default(1),
59
+ radius: z.number().min(0).default(0)
60
+ });
61
+ var paddingSchema = z.union([
62
+ z.number().min(0),
63
+ z.object({
64
+ top: z.number().min(0).default(0),
65
+ right: z.number().min(0).default(0),
66
+ bottom: z.number().min(0).default(0),
67
+ left: z.number().min(0).default(0)
96
68
  })
97
- }).unknown(false);
98
- var borderSchema = Joi.object({
99
- width: Joi.number().min(0).default(0),
100
- color: Joi.string().pattern(HEX6).default("#000000"),
101
- opacity: Joi.number().min(0).max(1).default(1),
102
- radius: Joi.number().min(0).default(0)
103
- }).unknown(false);
104
- var backgroundSchema = Joi.object({
105
- color: Joi.string().pattern(HEX6).optional(),
106
- opacity: Joi.number().min(0).max(1).default(1)
107
- }).unknown(false);
108
- var paddingSchema = Joi.alternatives().try(
109
- Joi.number().min(0).default(0),
110
- Joi.object({
111
- top: Joi.number().min(0).default(0),
112
- right: Joi.number().min(0).default(0),
113
- bottom: Joi.number().min(0).default(0),
114
- left: Joi.number().min(0).default(0)
115
- }).unknown(false)
116
- );
117
- var customFontSchema = Joi.object({
118
- src: Joi.string().uri().required(),
119
- family: Joi.string().required(),
120
- weight: Joi.alternatives().try(Joi.string(), Joi.number()).optional(),
121
- style: Joi.string().optional(),
122
- originalFamily: Joi.string().optional()
123
- }).unknown(false);
124
- var RichTextAssetSchema = Joi.object({
125
- type: Joi.string().valid("rich-text").required(),
126
- text: Joi.string().allow("").max(CANVAS_CONFIG.LIMITS.maxTextLength).default(""),
127
- width: Joi.number().min(CANVAS_CONFIG.LIMITS.minWidth).max(CANVAS_CONFIG.LIMITS.maxWidth).default(CANVAS_CONFIG.DEFAULTS.width).optional(),
128
- height: Joi.number().min(CANVAS_CONFIG.LIMITS.minHeight).max(CANVAS_CONFIG.LIMITS.maxHeight).default(CANVAS_CONFIG.DEFAULTS.height).optional(),
129
- font: fontSchema.optional(),
130
- style: styleSchema.optional(),
131
- stroke: strokeSchema.optional(),
132
- shadow: shadowSchema.optional(),
133
- background: backgroundSchema.optional(),
69
+ ]);
70
+ var canvasFontSchema = richTextFontSchema.extend({
71
+ family: z.string().default(CANVAS_CONFIG.DEFAULTS.fontFamily),
72
+ size: z.number().int().min(CANVAS_CONFIG.LIMITS.minFontSize).max(CANVAS_CONFIG.LIMITS.maxFontSize).default(CANVAS_CONFIG.DEFAULTS.fontSize),
73
+ weight: z.union([z.string(), z.number()]).default("400"),
74
+ color: z.string().regex(HEX6).default(CANVAS_CONFIG.DEFAULTS.color),
75
+ opacity: z.number().min(0).max(1).default(1),
76
+ background: z.string().regex(HEX6).optional()
77
+ });
78
+ var canvasGradientSchema = z.object({
79
+ type: z.enum(["linear", "radial"]).default("linear"),
80
+ angle: z.number().min(0).max(360).default(0),
81
+ stops: z.array(z.object({
82
+ offset: z.number().min(0).max(1),
83
+ color: z.string().regex(HEX6)
84
+ })).min(2)
85
+ });
86
+ var canvasStyleSchema = richTextStyleSchema.extend({
87
+ letterSpacing: z.number().default(0),
88
+ lineHeight: z.number().min(0).max(10).default(1.2),
89
+ textTransform: z.enum(["none", "uppercase", "lowercase", "capitalize"]).default("none"),
90
+ textDecoration: z.enum(["none", "underline", "line-through"]).default("none"),
91
+ gradient: canvasGradientSchema.optional()
92
+ });
93
+ var canvasStrokeSchema = richTextStrokeSchema.extend({
94
+ width: z.number().min(0).default(0),
95
+ color: z.string().regex(HEX6).default("#000000"),
96
+ opacity: z.number().min(0).max(1).default(1)
97
+ });
98
+ var canvasShadowSchema = richTextShadowSchema.extend({
99
+ offsetX: z.number().default(0),
100
+ offsetY: z.number().default(0),
101
+ blur: z.number().min(0).default(0),
102
+ color: z.string().regex(HEX6).default("#000000"),
103
+ opacity: z.number().min(0).max(1).default(0.5)
104
+ });
105
+ var canvasBackgroundSchema = richTextBackgroundSchema.extend({
106
+ color: z.string().regex(HEX6).optional(),
107
+ opacity: z.number().min(0).max(1).default(1)
108
+ });
109
+ var canvasAlignmentSchema = richTextAlignmentSchema.extend({
110
+ horizontal: z.enum(["left", "center", "right"]).default(CANVAS_CONFIG.DEFAULTS.textAlign),
111
+ vertical: z.enum(["top", "middle", "bottom"]).default("middle")
112
+ });
113
+ var canvasAnimationSchema = richTextAnimationSchema.extend({
114
+ preset: z.enum(CANVAS_CONFIG.ANIMATION_TYPES),
115
+ speed: z.number().min(0.1).max(10).default(1),
116
+ duration: z.number().min(CANVAS_CONFIG.LIMITS.minDuration).max(CANVAS_CONFIG.LIMITS.maxDuration).optional(),
117
+ style: z.enum(["character", "word"]).optional(),
118
+ direction: z.enum(["left", "right", "up", "down"]).optional()
119
+ }).superRefine((data, ctx) => {
120
+ const presetsWithStyle = ["typewriter", "shift", "fadeIn", "slideIn"];
121
+ if (data.style && !presetsWithStyle.includes(data.preset)) {
122
+ ctx.addIssue({
123
+ code: z.ZodIssueCode.custom,
124
+ message: `style is not allowed for preset "${data.preset}"`,
125
+ path: ["style"]
126
+ });
127
+ }
128
+ const directionRules = {
129
+ ascend: ["up", "down"],
130
+ shift: ["left", "right", "up", "down"],
131
+ slideIn: ["left", "right", "up", "down"],
132
+ movingLetters: ["left", "right", "up", "down"]
133
+ };
134
+ if (data.direction) {
135
+ const allowedDirections = directionRules[data.preset];
136
+ if (!allowedDirections) {
137
+ ctx.addIssue({
138
+ code: z.ZodIssueCode.custom,
139
+ message: `direction is not allowed for preset "${data.preset}"`,
140
+ path: ["direction"]
141
+ });
142
+ } else if (!allowedDirections.includes(data.direction)) {
143
+ ctx.addIssue({
144
+ code: z.ZodIssueCode.custom,
145
+ message: `direction "${data.direction}" is not valid for preset "${data.preset}"`,
146
+ path: ["direction"]
147
+ });
148
+ }
149
+ }
150
+ });
151
+ var CanvasRichTextAssetSchema = richTextAssetSchema.extend({
152
+ type: z.literal("rich-text"),
153
+ text: z.string().max(CANVAS_CONFIG.LIMITS.maxTextLength).default(""),
154
+ width: z.number().int().min(CANVAS_CONFIG.LIMITS.minWidth).max(CANVAS_CONFIG.LIMITS.maxWidth).default(CANVAS_CONFIG.DEFAULTS.width).optional(),
155
+ height: z.number().int().min(CANVAS_CONFIG.LIMITS.minHeight).max(CANVAS_CONFIG.LIMITS.maxHeight).default(CANVAS_CONFIG.DEFAULTS.height).optional(),
156
+ font: canvasFontSchema.optional(),
157
+ style: canvasStyleSchema.optional(),
158
+ stroke: canvasStrokeSchema.optional(),
159
+ shadow: canvasShadowSchema.optional(),
160
+ background: canvasBackgroundSchema.optional(),
134
161
  border: borderSchema.optional(),
135
162
  padding: paddingSchema.optional(),
136
- align: alignmentSchema.optional(),
137
- animation: animationSchema.optional(),
138
- customFonts: Joi.array().items(customFontSchema).optional()
139
- }).unknown(false);
163
+ align: canvasAlignmentSchema.optional(),
164
+ animation: canvasAnimationSchema.optional(),
165
+ customFonts: z.array(customFontSchema).optional()
166
+ }).strict();
140
167
 
141
168
  // src/wasm/hb-loader.ts
142
169
  var hbSingleton = null;
@@ -2444,19 +2471,12 @@ async function createTextEngine(opts = {}) {
2444
2471
  }
2445
2472
  return {
2446
2473
  validate(input) {
2447
- try {
2448
- const { value, error } = RichTextAssetSchema.validate(input, {
2449
- abortEarly: false,
2450
- convert: true
2451
- });
2452
- if (error) throw error;
2453
- return { value };
2454
- } catch (err) {
2455
- if (err instanceof Error) {
2456
- throw new Error(`Validation failed: ${err.message}`);
2457
- }
2458
- throw new Error(`Validation failed: ${String(err)}`);
2474
+ const result = CanvasRichTextAssetSchema.safeParse(input);
2475
+ if (!result.success) {
2476
+ const messages = result.error.issues.map((i) => i.message).join(". ");
2477
+ throw new Error(`Validation failed: ${messages}`);
2459
2478
  }
2479
+ return { value: result.data };
2460
2480
  },
2461
2481
  async registerFontFromFile(path, desc) {
2462
2482
  try {
@@ -2650,6 +2670,7 @@ async function createTextEngine(opts = {}) {
2650
2670
  };
2651
2671
  }
2652
2672
  export {
2673
+ CanvasRichTextAssetSchema,
2653
2674
  createTextEngine,
2654
2675
  isGlyphFill2 as isGlyphFill,
2655
2676
  isShadowFill2 as isShadowFill