@effing/effie 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.
- package/LICENSE +11 -0
- package/README.md +226 -0
- package/dist/index.d.ts +10472 -0
- package/dist/index.js +469 -0
- package/dist/index.js.map +1 -0
- package/package.json +43 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
// src/types.ts
|
|
2
|
+
function effieWebUrl(url) {
|
|
3
|
+
if (url.startsWith("http") || url.startsWith("data")) {
|
|
4
|
+
return url;
|
|
5
|
+
}
|
|
6
|
+
throw new Error(`Invalid web URL: ${url}`);
|
|
7
|
+
}
|
|
8
|
+
function effieFileUrl(url) {
|
|
9
|
+
if (url.startsWith("file")) {
|
|
10
|
+
return url;
|
|
11
|
+
}
|
|
12
|
+
throw new Error(`Invalid file URL: ${url}`);
|
|
13
|
+
}
|
|
14
|
+
function effieData(data) {
|
|
15
|
+
return data;
|
|
16
|
+
}
|
|
17
|
+
function effieBackground(background) {
|
|
18
|
+
return background;
|
|
19
|
+
}
|
|
20
|
+
function effieSegment(segment) {
|
|
21
|
+
return segment;
|
|
22
|
+
}
|
|
23
|
+
function effieLayer(layer) {
|
|
24
|
+
return layer;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// src/partition.ts
|
|
28
|
+
function effieDataForSegment(effieData2, segmentIndex) {
|
|
29
|
+
if (segmentIndex < 0 || segmentIndex >= effieData2.segments.length) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`Invalid segment index: ${segmentIndex}. Must be between 0 and ${effieData2.segments.length - 1}`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const segment = effieData2.segments[segmentIndex];
|
|
35
|
+
const usedSourceRefs = /* @__PURE__ */ new Set();
|
|
36
|
+
if (effieData2.background.type !== "color") {
|
|
37
|
+
const bgSource = effieData2.background.source;
|
|
38
|
+
if (typeof bgSource === "string" && bgSource.startsWith("#")) {
|
|
39
|
+
usedSourceRefs.add(bgSource.slice(1));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (segment.background && segment.background.type !== "color") {
|
|
43
|
+
const segBgSource = segment.background.source;
|
|
44
|
+
if (typeof segBgSource === "string" && segBgSource.startsWith("#")) {
|
|
45
|
+
usedSourceRefs.add(segBgSource.slice(1));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
for (const layer of segment.layers) {
|
|
49
|
+
if (typeof layer.source === "string" && layer.source.startsWith("#")) {
|
|
50
|
+
usedSourceRefs.add(layer.source.slice(1));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (segment.audio) {
|
|
54
|
+
const audioSource = segment.audio.source;
|
|
55
|
+
if (typeof audioSource === "string" && audioSource.startsWith("#")) {
|
|
56
|
+
usedSourceRefs.add(audioSource.slice(1));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const minimalSources = {};
|
|
60
|
+
if (effieData2.sources) {
|
|
61
|
+
for (const ref of usedSourceRefs) {
|
|
62
|
+
if (ref in effieData2.sources) {
|
|
63
|
+
minimalSources[ref] = effieData2.sources[ref];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
const background = effieData2.background.type === "video" ? {
|
|
68
|
+
...effieData2.background,
|
|
69
|
+
seek: (effieData2.background.seek ?? 0) + effieData2.segments.slice(0, segmentIndex).reduce((sum, seg) => sum + seg.duration, 0)
|
|
70
|
+
} : effieData2.background;
|
|
71
|
+
return {
|
|
72
|
+
width: effieData2.width,
|
|
73
|
+
height: effieData2.height,
|
|
74
|
+
fps: effieData2.fps,
|
|
75
|
+
cover: effieData2.cover,
|
|
76
|
+
background,
|
|
77
|
+
segments: [segment],
|
|
78
|
+
// Only include the target segment (with its background if any)
|
|
79
|
+
...Object.keys(minimalSources).length > 0 ? { sources: minimalSources } : {}
|
|
80
|
+
// Note: global audio is excluded - it's handled in the join phase
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function effieDataForJoin(effieData2, segmentSourceUrls) {
|
|
84
|
+
if (segmentSourceUrls.length !== effieData2.segments.length) {
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Expected ${effieData2.segments.length} segment sources, got ${segmentSourceUrls.length}`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
const sources = {};
|
|
90
|
+
for (let i = 0; i < segmentSourceUrls.length; i++) {
|
|
91
|
+
sources[`__segment_${i}`] = segmentSourceUrls[i];
|
|
92
|
+
}
|
|
93
|
+
if (effieData2.audio) {
|
|
94
|
+
const audioSource = effieData2.audio.source;
|
|
95
|
+
if (typeof audioSource === "string" && audioSource.startsWith("#")) {
|
|
96
|
+
const ref = audioSource.slice(1);
|
|
97
|
+
if (effieData2.sources && ref in effieData2.sources) {
|
|
98
|
+
sources[ref] = effieData2.sources[ref];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
width: effieData2.width,
|
|
104
|
+
height: effieData2.height,
|
|
105
|
+
fps: effieData2.fps,
|
|
106
|
+
cover: effieData2.cover,
|
|
107
|
+
background: { type: "color", color: "black" },
|
|
108
|
+
// Not used - each segment has its own background
|
|
109
|
+
segments: effieData2.segments.map((seg, i) => ({
|
|
110
|
+
duration: seg.duration,
|
|
111
|
+
layers: [],
|
|
112
|
+
// Layers not needed - video is pre-rendered
|
|
113
|
+
transition: seg.transition,
|
|
114
|
+
background: { type: "video", source: `#__segment_${i}` },
|
|
115
|
+
audio: { source: `#__segment_${i}` }
|
|
116
|
+
// FFmpeg extracts audio from mp4
|
|
117
|
+
})),
|
|
118
|
+
audio: effieData2.audio,
|
|
119
|
+
// Global audio is mixed during render
|
|
120
|
+
sources
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// src/extract.ts
|
|
125
|
+
function extractEffieSources(effieData2, options = {}) {
|
|
126
|
+
const { includeDataUrls = false } = options;
|
|
127
|
+
const sources = /* @__PURE__ */ new Set();
|
|
128
|
+
const addSource = (src) => {
|
|
129
|
+
if (src.startsWith("#")) {
|
|
130
|
+
const name = src.slice(1);
|
|
131
|
+
if (effieData2.sources?.[name]) {
|
|
132
|
+
src = effieData2.sources[name];
|
|
133
|
+
} else {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!includeDataUrls && src.startsWith("data:")) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
sources.add(src);
|
|
141
|
+
};
|
|
142
|
+
addSource(effieData2.cover);
|
|
143
|
+
if (effieData2.background.type !== "color") {
|
|
144
|
+
addSource(effieData2.background.source);
|
|
145
|
+
}
|
|
146
|
+
if (effieData2.audio) {
|
|
147
|
+
addSource(effieData2.audio.source);
|
|
148
|
+
}
|
|
149
|
+
for (const segment of effieData2.segments) {
|
|
150
|
+
if (segment.background && segment.background.type !== "color") {
|
|
151
|
+
addSource(segment.background.source);
|
|
152
|
+
}
|
|
153
|
+
if (segment.audio) {
|
|
154
|
+
addSource(segment.audio.source);
|
|
155
|
+
}
|
|
156
|
+
for (const layer of segment.layers) {
|
|
157
|
+
addSource(layer.source);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
return Array.from(sources);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/response.ts
|
|
164
|
+
function effieResponse(data, options = {}) {
|
|
165
|
+
const { headers: extraHeaders, cacheControl = "public, max-age=3600" } = options;
|
|
166
|
+
const headers = new Headers(extraHeaders);
|
|
167
|
+
if (cacheControl) {
|
|
168
|
+
headers.set("Cache-Control", cacheControl);
|
|
169
|
+
}
|
|
170
|
+
return Response.json(data, { status: 200, headers });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/schema.ts
|
|
174
|
+
import { z } from "zod";
|
|
175
|
+
var effieHttpUrlSchema = z.custom(
|
|
176
|
+
(val) => typeof val === "string" && (val.startsWith("http://") || val.startsWith("https://")),
|
|
177
|
+
{ message: "Must be an HTTP or HTTPS URL" }
|
|
178
|
+
);
|
|
179
|
+
var effieDataUrlSchema = z.custom(
|
|
180
|
+
(val) => typeof val === "string" && val.startsWith("data:"),
|
|
181
|
+
{ message: "Must be a data URL" }
|
|
182
|
+
);
|
|
183
|
+
var effieWebUrlSchema = z.custom(
|
|
184
|
+
(val) => typeof val === "string" && (val.startsWith("http://") || val.startsWith("https://") || val.startsWith("data:")),
|
|
185
|
+
{ message: "Must be an HTTP, HTTPS, or data URL" }
|
|
186
|
+
);
|
|
187
|
+
var effieFileUrlSchema = z.custom(
|
|
188
|
+
(val) => typeof val === "string" && val.startsWith("file:"),
|
|
189
|
+
{ message: "Must be a file URL" }
|
|
190
|
+
);
|
|
191
|
+
var sourceRefSchema = z.custom(
|
|
192
|
+
(val) => typeof val === "string" && val.startsWith("#"),
|
|
193
|
+
{ message: "Source reference must start with #" }
|
|
194
|
+
);
|
|
195
|
+
var effieTransitionSchema = z.union([
|
|
196
|
+
// Fade with easing
|
|
197
|
+
z.strictObject({
|
|
198
|
+
type: z.literal("fade"),
|
|
199
|
+
duration: z.number(),
|
|
200
|
+
easing: z.enum(["linear", "ease-in", "ease-out"]).optional()
|
|
201
|
+
}),
|
|
202
|
+
// Fade through color
|
|
203
|
+
z.strictObject({
|
|
204
|
+
type: z.literal("fade"),
|
|
205
|
+
duration: z.number(),
|
|
206
|
+
through: z.enum(["black", "white", "grays"])
|
|
207
|
+
}),
|
|
208
|
+
// Barn door wipes
|
|
209
|
+
z.strictObject({
|
|
210
|
+
type: z.literal("barn"),
|
|
211
|
+
duration: z.number(),
|
|
212
|
+
orientation: z.enum(["horizontal", "vertical"]).optional(),
|
|
213
|
+
mode: z.enum(["open", "close"]).optional()
|
|
214
|
+
}),
|
|
215
|
+
// Circle wipes
|
|
216
|
+
z.strictObject({
|
|
217
|
+
type: z.literal("circle"),
|
|
218
|
+
duration: z.number(),
|
|
219
|
+
mode: z.enum(["open", "close", "crop"]).optional()
|
|
220
|
+
}),
|
|
221
|
+
// Directional transitions (wipe, slide, smooth, slice)
|
|
222
|
+
z.strictObject({
|
|
223
|
+
type: z.enum(["wipe", "slide", "smooth", "slice"]),
|
|
224
|
+
duration: z.number(),
|
|
225
|
+
direction: z.enum(["left", "right", "up", "down"]).optional()
|
|
226
|
+
}),
|
|
227
|
+
// Zoom
|
|
228
|
+
z.strictObject({
|
|
229
|
+
type: z.literal("zoom"),
|
|
230
|
+
duration: z.number()
|
|
231
|
+
}),
|
|
232
|
+
// Standalone transitions
|
|
233
|
+
z.strictObject({
|
|
234
|
+
type: z.enum(["dissolve", "pixelize", "radial"]),
|
|
235
|
+
duration: z.number()
|
|
236
|
+
})
|
|
237
|
+
]);
|
|
238
|
+
var effieEffectSchema = z.union([
|
|
239
|
+
z.strictObject({
|
|
240
|
+
type: z.literal("fade-in"),
|
|
241
|
+
duration: z.number(),
|
|
242
|
+
start: z.number()
|
|
243
|
+
}),
|
|
244
|
+
z.strictObject({
|
|
245
|
+
type: z.literal("fade-out"),
|
|
246
|
+
duration: z.number(),
|
|
247
|
+
start: z.number()
|
|
248
|
+
}),
|
|
249
|
+
z.strictObject({
|
|
250
|
+
type: z.literal("saturate-in"),
|
|
251
|
+
duration: z.number(),
|
|
252
|
+
start: z.number()
|
|
253
|
+
}),
|
|
254
|
+
z.strictObject({
|
|
255
|
+
type: z.literal("saturate-out"),
|
|
256
|
+
duration: z.number(),
|
|
257
|
+
start: z.number()
|
|
258
|
+
}),
|
|
259
|
+
z.strictObject({
|
|
260
|
+
type: z.literal("scroll"),
|
|
261
|
+
duration: z.number(),
|
|
262
|
+
direction: z.enum(["left", "right", "up", "down"]),
|
|
263
|
+
distance: z.number()
|
|
264
|
+
})
|
|
265
|
+
]);
|
|
266
|
+
var effieMotionSchema = z.union([
|
|
267
|
+
z.strictObject({
|
|
268
|
+
type: z.literal("bounce"),
|
|
269
|
+
start: z.number().optional(),
|
|
270
|
+
duration: z.number().optional(),
|
|
271
|
+
amplitude: z.number().optional()
|
|
272
|
+
}),
|
|
273
|
+
z.strictObject({
|
|
274
|
+
type: z.literal("shake"),
|
|
275
|
+
start: z.number().optional(),
|
|
276
|
+
duration: z.number().optional(),
|
|
277
|
+
intensity: z.number().optional(),
|
|
278
|
+
frequency: z.number().optional()
|
|
279
|
+
}),
|
|
280
|
+
z.strictObject({
|
|
281
|
+
type: z.literal("slide"),
|
|
282
|
+
start: z.number().optional(),
|
|
283
|
+
duration: z.number().optional(),
|
|
284
|
+
direction: z.enum(["left", "right", "up", "down"]),
|
|
285
|
+
distance: z.number().optional(),
|
|
286
|
+
reverse: z.boolean().optional(),
|
|
287
|
+
easing: z.enum(["linear", "ease-in", "ease-out", "ease-in-out"]).optional()
|
|
288
|
+
})
|
|
289
|
+
]);
|
|
290
|
+
function createEffieSourcesSchema(urlSchema) {
|
|
291
|
+
return z.record(z.string(), urlSchema);
|
|
292
|
+
}
|
|
293
|
+
function createEffieSourceSchema(urlSchema) {
|
|
294
|
+
return z.union([urlSchema, sourceRefSchema]);
|
|
295
|
+
}
|
|
296
|
+
function createEffieBackgroundSchema(urlSchema) {
|
|
297
|
+
const sourceSchema = createEffieSourceSchema(urlSchema);
|
|
298
|
+
return z.union([
|
|
299
|
+
z.strictObject({
|
|
300
|
+
type: z.literal("image"),
|
|
301
|
+
source: sourceSchema
|
|
302
|
+
}),
|
|
303
|
+
z.strictObject({
|
|
304
|
+
type: z.literal("video"),
|
|
305
|
+
source: sourceSchema,
|
|
306
|
+
seek: z.number().optional()
|
|
307
|
+
}),
|
|
308
|
+
z.strictObject({
|
|
309
|
+
type: z.literal("color"),
|
|
310
|
+
color: z.string()
|
|
311
|
+
})
|
|
312
|
+
]);
|
|
313
|
+
}
|
|
314
|
+
function createEffieAudioSchema(urlSchema) {
|
|
315
|
+
const sourceSchema = createEffieSourceSchema(urlSchema);
|
|
316
|
+
return z.strictObject({
|
|
317
|
+
source: sourceSchema,
|
|
318
|
+
volume: z.number().min(0).max(1).optional(),
|
|
319
|
+
fadeIn: z.number().optional(),
|
|
320
|
+
fadeOut: z.number().optional(),
|
|
321
|
+
seek: z.number().optional()
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function createEffieLayerSchema(urlSchema) {
|
|
325
|
+
const sourceSchema = createEffieSourceSchema(urlSchema);
|
|
326
|
+
return z.strictObject({
|
|
327
|
+
type: z.enum(["image", "animation"]),
|
|
328
|
+
source: sourceSchema,
|
|
329
|
+
delay: z.number().optional(),
|
|
330
|
+
from: z.number().optional(),
|
|
331
|
+
until: z.number().optional(),
|
|
332
|
+
effects: z.array(effieEffectSchema).optional(),
|
|
333
|
+
motion: effieMotionSchema.optional()
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
function createEffieSegmentSchema(urlSchema) {
|
|
337
|
+
const layerSchema = createEffieLayerSchema(urlSchema);
|
|
338
|
+
const backgroundSchema = createEffieBackgroundSchema(urlSchema);
|
|
339
|
+
const audioSchema = createEffieAudioSchema(urlSchema);
|
|
340
|
+
return z.strictObject({
|
|
341
|
+
duration: z.number(),
|
|
342
|
+
layers: z.array(layerSchema),
|
|
343
|
+
background: backgroundSchema.optional(),
|
|
344
|
+
audio: audioSchema.optional(),
|
|
345
|
+
transition: effieTransitionSchema.optional()
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
function collectSourceRefs(data) {
|
|
349
|
+
const refs = [];
|
|
350
|
+
const addIfRef = (source) => {
|
|
351
|
+
if (source.startsWith("#")) {
|
|
352
|
+
refs.push(source.slice(1));
|
|
353
|
+
}
|
|
354
|
+
};
|
|
355
|
+
if ("source" in data.background && data.background.source) {
|
|
356
|
+
addIfRef(data.background.source);
|
|
357
|
+
}
|
|
358
|
+
if (data.audio) {
|
|
359
|
+
addIfRef(data.audio.source);
|
|
360
|
+
}
|
|
361
|
+
for (const segment of data.segments) {
|
|
362
|
+
if (segment.background && "source" in segment.background && segment.background.source) {
|
|
363
|
+
addIfRef(segment.background.source);
|
|
364
|
+
}
|
|
365
|
+
if (segment.audio) {
|
|
366
|
+
addIfRef(segment.audio.source);
|
|
367
|
+
}
|
|
368
|
+
for (const layer of segment.layers) {
|
|
369
|
+
addIfRef(layer.source);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return refs;
|
|
373
|
+
}
|
|
374
|
+
function createEffieDataSchema(urlSchema) {
|
|
375
|
+
const sourcesSchema = createEffieSourcesSchema(urlSchema);
|
|
376
|
+
const segmentSchema = createEffieSegmentSchema(urlSchema);
|
|
377
|
+
const backgroundSchema = createEffieBackgroundSchema(urlSchema);
|
|
378
|
+
const audioSchema = createEffieAudioSchema(urlSchema);
|
|
379
|
+
return z.strictObject({
|
|
380
|
+
width: z.number(),
|
|
381
|
+
height: z.number(),
|
|
382
|
+
fps: z.number(),
|
|
383
|
+
cover: effieWebUrlSchema,
|
|
384
|
+
// cover must always be a web URL
|
|
385
|
+
sources: sourcesSchema.optional(),
|
|
386
|
+
background: backgroundSchema,
|
|
387
|
+
audio: audioSchema.optional(),
|
|
388
|
+
segments: z.array(segmentSchema)
|
|
389
|
+
}).superRefine((data, ctx) => {
|
|
390
|
+
const definedSources = new Set(Object.keys(data.sources ?? {}));
|
|
391
|
+
const referencedSources = collectSourceRefs(data);
|
|
392
|
+
for (const ref of referencedSources) {
|
|
393
|
+
if (!definedSources.has(ref)) {
|
|
394
|
+
ctx.addIssue({
|
|
395
|
+
code: z.ZodIssueCode.custom,
|
|
396
|
+
message: `Source reference "#${ref}" not found in sources`
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
for (let i = 1; i < data.segments.length; i++) {
|
|
401
|
+
const segment = data.segments[i];
|
|
402
|
+
const prevSegment = data.segments[i - 1];
|
|
403
|
+
if (segment.transition) {
|
|
404
|
+
const transitionDuration = segment.transition.duration;
|
|
405
|
+
if (segment.duration < transitionDuration) {
|
|
406
|
+
ctx.addIssue({
|
|
407
|
+
code: z.ZodIssueCode.custom,
|
|
408
|
+
message: `Segment ${i} duration (${segment.duration}s) must be at least as long as its transition duration (${transitionDuration}s)`,
|
|
409
|
+
path: ["segments", i, "duration"]
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
if (prevSegment.duration < transitionDuration) {
|
|
413
|
+
ctx.addIssue({
|
|
414
|
+
code: z.ZodIssueCode.custom,
|
|
415
|
+
message: `Segment ${i - 1} duration (${prevSegment.duration}s) must be at least as long as the following transition duration (${transitionDuration}s)`,
|
|
416
|
+
path: ["segments", i - 1, "duration"]
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
var effieSourcesSchema = createEffieSourcesSchema(effieWebUrlSchema);
|
|
424
|
+
var effieSourceSchema = createEffieSourceSchema(effieWebUrlSchema);
|
|
425
|
+
var effieBackgroundSchema = createEffieBackgroundSchema(effieWebUrlSchema);
|
|
426
|
+
var effieAudioSchema = createEffieAudioSchema(effieWebUrlSchema);
|
|
427
|
+
var effieLayerSchema = createEffieLayerSchema(effieWebUrlSchema);
|
|
428
|
+
var effieSegmentSchema = createEffieSegmentSchema(effieWebUrlSchema);
|
|
429
|
+
var effieDataSchema = createEffieDataSchema(effieWebUrlSchema);
|
|
430
|
+
var webOrFileUrlSchema = z.custom(
|
|
431
|
+
(val) => typeof val === "string" && (val.startsWith("http://") || val.startsWith("https://") || val.startsWith("data:") || val.startsWith("file:")),
|
|
432
|
+
{ message: "Must be an HTTP, HTTPS, data, or file URL" }
|
|
433
|
+
);
|
|
434
|
+
var effieDataWithFilesSchema = createEffieDataSchema(webOrFileUrlSchema);
|
|
435
|
+
export {
|
|
436
|
+
createEffieAudioSchema,
|
|
437
|
+
createEffieBackgroundSchema,
|
|
438
|
+
createEffieDataSchema,
|
|
439
|
+
createEffieLayerSchema,
|
|
440
|
+
createEffieSegmentSchema,
|
|
441
|
+
createEffieSourceSchema,
|
|
442
|
+
createEffieSourcesSchema,
|
|
443
|
+
effieAudioSchema,
|
|
444
|
+
effieBackground,
|
|
445
|
+
effieBackgroundSchema,
|
|
446
|
+
effieData,
|
|
447
|
+
effieDataForJoin,
|
|
448
|
+
effieDataForSegment,
|
|
449
|
+
effieDataSchema,
|
|
450
|
+
effieDataUrlSchema,
|
|
451
|
+
effieDataWithFilesSchema,
|
|
452
|
+
effieEffectSchema,
|
|
453
|
+
effieFileUrl,
|
|
454
|
+
effieFileUrlSchema,
|
|
455
|
+
effieHttpUrlSchema,
|
|
456
|
+
effieLayer,
|
|
457
|
+
effieLayerSchema,
|
|
458
|
+
effieMotionSchema,
|
|
459
|
+
effieResponse,
|
|
460
|
+
effieSegment,
|
|
461
|
+
effieSegmentSchema,
|
|
462
|
+
effieSourceSchema,
|
|
463
|
+
effieSourcesSchema,
|
|
464
|
+
effieTransitionSchema,
|
|
465
|
+
effieWebUrl,
|
|
466
|
+
effieWebUrlSchema,
|
|
467
|
+
extractEffieSources
|
|
468
|
+
};
|
|
469
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts","../src/partition.ts","../src/extract.ts","../src/response.ts","../src/schema.ts"],"sourcesContent":["export type EffieWebUrl = EffieHttpUrl | EffieDataUrl; // [!] does not include file URLs\nexport type EffieHttpUrl = `http${string}`; // HTTP URL starts with http: or https:\nexport type EffieDataUrl = `data${string}`; // data URL starts with data:\n\nexport type EffieFileUrl = `file${string}`; // file URL starts with file:\n\nexport type EffieSources<U extends string = EffieWebUrl> = {\n [key: string]: U; // key: source name, value: URL\n};\n\nexport type EffieSource<\n S extends EffieSources<U>,\n U extends string = EffieWebUrl,\n> = U | `#${Extract<keyof S, string>}`; // source name\n\nexport type EffieData<\n S extends EffieSources<U>,\n U extends string = EffieWebUrl,\n> = {\n width: number; // frame width\n height: number; // frame height\n fps: number; // frames per second\n cover: EffieWebUrl; // cover image has to be a direct URL\n sources?: S; // common sources, to avoid duplication\n background: EffieBackground<S, U>; // all-encompassing background\n audio?: EffieAudio<S, U>; // general soundtrack audio\n segments: EffieSegment<S, U>[]; // consecutive video segments\n};\n\nexport type EffieBackground<\n S extends EffieSources<U>,\n U extends string = EffieWebUrl,\n> =\n | {\n type: \"image\";\n source: EffieSource<S, U>;\n }\n | {\n type: \"video\";\n source: EffieSource<S, U>;\n seek?: number; // seek to this position in seconds\n }\n | {\n type: \"color\";\n color: string; // color name or [0x|#]RRGGBB[AA]\n };\n\nexport type EffieAudio<\n S extends EffieSources<U>,\n U extends string = EffieWebUrl,\n> = {\n source: EffieSource<S, U>;\n volume?: number; // volume in range [0, 1]\n fadeIn?: number; // fade-in duration in seconds\n fadeOut?: number; // fade-out duration in seconds\n seek?: number; // seek to this position in seconds\n};\n\nexport type EffieSegment<\n S extends EffieSources<U>,\n U extends string = EffieWebUrl,\n> = {\n duration: number;\n layers: EffieLayer<S, U>[];\n background?: EffieBackground<S, U>; // overrides global background for this segment\n audio?: EffieAudio<S, U>;\n transition?: EffieTransition; // ignored on the first segment\n};\n\nexport type EffieLayer<\n S extends EffieSources<U>,\n U extends string = EffieWebUrl,\n> = {\n type:\n | \"image\" // png or jpeg\n | \"animation\"; // annie\n source: EffieSource<S, U>;\n delay?: number; // delay (before start) time in seconds\n from?: number; // clip from this time in seconds\n until?: number; // clip until this time in seconds\n effects?: EffieEffect[];\n motion?: EffieMotion;\n};\n\nexport type EffieTransition = {\n duration: number;\n} & (\n | { type: \"fade\"; easing?: \"linear\" | \"ease-in\" | \"ease-out\" }\n // Fade through color (always linear)\n | { type: \"fade\"; through: \"black\" | \"white\" | \"grays\" }\n // Barn door wipes (default: horizontal, open)\n | {\n type: \"barn\";\n orientation?: \"horizontal\" | \"vertical\";\n mode?: \"open\" | \"close\";\n }\n // Circle wipes (default: open)\n | { type: \"circle\"; mode?: \"open\" | \"close\" | \"crop\" }\n // Directional transitions (default: left)\n | {\n type: \"wipe\" | \"slide\" | \"smooth\" | \"slice\";\n direction?: \"left\" | \"right\" | \"up\" | \"down\";\n }\n // Zoom\n | { type: \"zoom\" }\n // Standalone transitions\n | { type: \"dissolve\" | \"pixelize\" | \"radial\" }\n);\n\nexport type EffieEffect = {\n duration: number;\n} & (\n | { type: \"fade-in\"; start: number }\n | { type: \"fade-out\"; start: number }\n | { type: \"saturate-in\"; start: number }\n | { type: \"saturate-out\"; start: number }\n | {\n type: \"scroll\";\n direction: \"left\" | \"right\" | \"up\" | \"down\";\n distance: number;\n }\n);\n\nexport type EffieMotion = {\n start?: number;\n duration?: number;\n} & (\n | { type: \"bounce\"; amplitude?: number }\n | { type: \"shake\"; intensity?: number; frequency?: number }\n | {\n type: \"slide\";\n direction: \"left\" | \"right\" | \"up\" | \"down\";\n distance?: number;\n reverse?: boolean;\n easing?: \"linear\" | \"ease-in\" | \"ease-out\" | \"ease-in-out\";\n }\n);\n\nexport function effieWebUrl(url: string) {\n if (url.startsWith(\"http\") || url.startsWith(\"data\")) {\n return url as EffieWebUrl;\n }\n throw new Error(`Invalid web URL: ${url}`);\n}\n\nexport function effieFileUrl(url: string) {\n if (url.startsWith(\"file\")) {\n return url as EffieFileUrl;\n }\n throw new Error(`Invalid file URL: ${url}`);\n}\n\nexport function effieData<S extends EffieSources>(data: EffieData<S>) {\n return data; // just for typing convenience\n}\n\nexport function effieBackground<S extends EffieSources>(\n background: EffieBackground<S>,\n) {\n return background; // just for typing convenience\n}\n\nexport function effieSegment<S extends EffieSources>(segment: EffieSegment<S>) {\n return segment; // just for typing convenience\n}\n\nexport function effieLayer<S extends EffieSources>(layer: EffieLayer<S>) {\n return layer; // just for typing convenience\n}\n","import type { EffieData, EffieSources, EffieWebUrl } from \"./types\";\n\n/**\n * Returns a minimal EffieData containing only what's needed to render a single segment.\n * Can be used for distributed rendering where each segment is rendered independently.\n */\nexport function effieDataForSegment(\n effieData: EffieData<EffieSources>,\n segmentIndex: number,\n): EffieData<EffieSources> {\n if (segmentIndex < 0 || segmentIndex >= effieData.segments.length) {\n throw new Error(\n `Invalid segment index: ${segmentIndex}. Must be between 0 and ${\n effieData.segments.length - 1\n }`,\n );\n }\n\n const segment = effieData.segments[segmentIndex];\n\n // Collect all source references used by this segment\n const usedSourceRefs = new Set<string>();\n\n // Check global background source\n if (effieData.background.type !== \"color\") {\n const bgSource = effieData.background.source;\n if (typeof bgSource === \"string\" && bgSource.startsWith(\"#\")) {\n usedSourceRefs.add(bgSource.slice(1));\n }\n }\n\n // Check segment background source\n if (segment.background && segment.background.type !== \"color\") {\n const segBgSource = segment.background.source;\n if (typeof segBgSource === \"string\" && segBgSource.startsWith(\"#\")) {\n usedSourceRefs.add(segBgSource.slice(1));\n }\n }\n\n // Check layer sources\n for (const layer of segment.layers) {\n if (typeof layer.source === \"string\" && layer.source.startsWith(\"#\")) {\n usedSourceRefs.add(layer.source.slice(1));\n }\n }\n\n // Check segment audio source\n if (segment.audio) {\n const audioSource = segment.audio.source;\n if (typeof audioSource === \"string\" && audioSource.startsWith(\"#\")) {\n usedSourceRefs.add(audioSource.slice(1));\n }\n }\n\n // Build minimal sources object with only referenced sources\n const minimalSources: EffieSources = {};\n if (effieData.sources) {\n for (const ref of usedSourceRefs) {\n if (ref in effieData.sources) {\n minimalSources[ref] = effieData.sources[ref];\n }\n }\n }\n\n // Build background with accumulated seek time for video backgrounds\n const background =\n effieData.background.type === \"video\"\n ? {\n ...effieData.background,\n seek:\n (effieData.background.seek ?? 0) +\n effieData.segments\n .slice(0, segmentIndex)\n .reduce((sum, seg) => sum + seg.duration, 0),\n }\n : effieData.background;\n\n // Create minimal effie data with just this segment\n return {\n width: effieData.width,\n height: effieData.height,\n fps: effieData.fps,\n cover: effieData.cover,\n background,\n segments: [segment], // Only include the target segment (with its background if any)\n ...(Object.keys(minimalSources).length > 0\n ? { sources: minimalSources }\n : {}),\n // Note: global audio is excluded - it's handled in the join phase\n };\n}\n\n/**\n * Returns EffieData for joining pre-rendered segments into a final video.\n * Each segment uses its corresponding pre-rendered video as both background and audio source.\n *\n * @param effieData - The original EffieData with segment metadata (durations, transitions)\n * @param segmentSourceUrls - URLs to pre-rendered segment videos (one per segment)\n */\nexport function effieDataForJoin<\n U extends string = EffieWebUrl,\n V extends string = EffieWebUrl,\n>(\n effieData: EffieData<EffieSources<U>, U>,\n segmentSourceUrls: V[],\n): EffieData<EffieSources<U | V>, U | V> {\n if (segmentSourceUrls.length !== effieData.segments.length) {\n throw new Error(\n `Expected ${effieData.segments.length} segment sources, got ${segmentSourceUrls.length}`,\n );\n }\n\n // Build sources object with segment references and global audio refs\n const sources: EffieSources<U | V> = {};\n\n // Add segment sources as named references (enables caching for background+audio reuse)\n // Using __segment_ prefix to avoid collisions with user-defined sources\n for (let i = 0; i < segmentSourceUrls.length; i++) {\n sources[`__segment_${i}`] = segmentSourceUrls[i];\n }\n\n // Include any source refs used by global audio\n if (effieData.audio) {\n const audioSource = effieData.audio.source;\n if (typeof audioSource === \"string\" && audioSource.startsWith(\"#\")) {\n const ref = audioSource.slice(1);\n if (effieData.sources && ref in effieData.sources) {\n sources[ref] = effieData.sources[ref];\n }\n }\n }\n\n return {\n width: effieData.width,\n height: effieData.height,\n fps: effieData.fps,\n cover: effieData.cover,\n background: { type: \"color\", color: \"black\" }, // Not used - each segment has its own background\n segments: effieData.segments.map((seg, i) => ({\n duration: seg.duration,\n layers: [], // Layers not needed - video is pre-rendered\n transition: seg.transition,\n background: { type: \"video\" as const, source: `#__segment_${i}` },\n audio: { source: `#__segment_${i}` }, // FFmpeg extracts audio from mp4\n })),\n audio: effieData.audio, // Global audio is mixed during render\n sources,\n };\n}\n","import type { EffieData, EffieSources, EffieWebUrl } from \"./types\";\n\n/**\n * Options for extracting sources from EffieData\n */\nexport type ExtractSourcesOptions = {\n /** Include data URLs in the result (default: false) */\n includeDataUrls?: boolean;\n};\n\n/**\n * Extract all source URLs from an EffieData composition.\n * Resolves #references and deduplicates results.\n *\n * @param effieData - The Effie composition\n * @param options - Extraction options\n * @returns Array of unique source URLs\n *\n * @example\n * ```ts\n * const sources = extractEffieSources(effieData);\n * // [\"https://cdn.example.com/bg.jpg\", \"https://api.example.com/annie/hero\", ...]\n * ```\n */\nexport function extractEffieSources<U extends string = EffieWebUrl>(\n effieData: EffieData<EffieSources<U>, U>,\n options: ExtractSourcesOptions = {},\n): string[] {\n const { includeDataUrls = false } = options;\n const sources = new Set<string>();\n\n const addSource = (src: string) => {\n // Resolve #references\n if (src.startsWith(\"#\")) {\n const name = src.slice(1);\n if (effieData.sources?.[name]) {\n src = effieData.sources[name];\n } else {\n return; // Reference not found, skip\n }\n }\n\n // Filter data URLs unless explicitly asked to include\n if (!includeDataUrls && src.startsWith(\"data:\")) {\n return;\n }\n\n sources.add(src);\n };\n\n // Cover image\n addSource(effieData.cover);\n\n // Global background\n if (effieData.background.type !== \"color\") {\n addSource(effieData.background.source);\n }\n\n // Global audio\n if (effieData.audio) {\n addSource(effieData.audio.source);\n }\n\n // Segments\n for (const segment of effieData.segments) {\n // Segment background\n if (segment.background && segment.background.type !== \"color\") {\n addSource(segment.background.source);\n }\n // Segment audio\n if (segment.audio) {\n addSource(segment.audio.source);\n }\n // Layers\n for (const layer of segment.layers) {\n addSource(layer.source);\n }\n }\n\n return Array.from(sources);\n}\n","import type { EffieData, EffieSources } from \"./types\";\n\n/**\n * Options for effie Response generation\n */\nexport type EffieResponseOptions = {\n /** Additional headers to include in the response */\n headers?: HeadersInit;\n /** Cache-Control header value (default: \"public, max-age=3600\") */\n cacheControl?: string;\n};\n\n/**\n * Create an HTTP Response containing effie JSON data\n *\n * This is the most convenient way to serve an effie composition from a web server.\n * It handles JSON serialization, content-type, and caching headers automatically.\n *\n * @param data The effie data to serialize\n * @param options Configuration options\n * @returns Response with JSON body\n *\n * @example\n * ```ts\n * // In a route handler:\n * export async function loader({ params }: LoaderFunctionArgs) {\n * const effie = await renderEffie(effieId, props, { width, height });\n * return effieResponse(effie);\n * }\n * ```\n */\nexport function effieResponse<S extends EffieSources>(\n data: EffieData<S>,\n options: EffieResponseOptions = {},\n): Response {\n const { headers: extraHeaders, cacheControl = \"public, max-age=3600\" } =\n options;\n\n const headers = new Headers(extraHeaders);\n if (cacheControl) {\n headers.set(\"Cache-Control\", cacheControl);\n }\n\n return Response.json(data, { status: 200, headers });\n}\n","import { z } from \"zod\";\nimport type {\n EffieHttpUrl,\n EffieDataUrl,\n EffieWebUrl,\n EffieFileUrl,\n EffieSources,\n EffieData,\n EffieBackground,\n EffieAudio,\n EffieSegment,\n EffieLayer,\n EffieTransition,\n EffieEffect,\n EffieMotion,\n} from \"./types\";\n\n// URL schemas using z.custom to enforce exact type matching:\n\nexport const effieHttpUrlSchema = z.custom<EffieHttpUrl>(\n (val): val is EffieHttpUrl =>\n typeof val === \"string\" &&\n (val.startsWith(\"http://\") || val.startsWith(\"https://\")),\n { message: \"Must be an HTTP or HTTPS URL\" },\n);\n\nexport const effieDataUrlSchema = z.custom<EffieDataUrl>(\n (val): val is EffieDataUrl =>\n typeof val === \"string\" && val.startsWith(\"data:\"),\n { message: \"Must be a data URL\" },\n);\n\nexport const effieWebUrlSchema = z.custom<EffieWebUrl>(\n (val): val is EffieWebUrl =>\n typeof val === \"string\" &&\n (val.startsWith(\"http://\") ||\n val.startsWith(\"https://\") ||\n val.startsWith(\"data:\")),\n { message: \"Must be an HTTP, HTTPS, or data URL\" },\n) satisfies z.ZodType<EffieWebUrl>;\n\nexport const effieFileUrlSchema = z.custom<EffieFileUrl>(\n (val): val is EffieFileUrl =>\n typeof val === \"string\" && val.startsWith(\"file:\"),\n { message: \"Must be a file URL\" },\n) satisfies z.ZodType<EffieFileUrl>;\n\n// Source reference schema (matches #sourceName pattern)\nconst sourceRefSchema = z.custom<`#${string}`>(\n (val): val is `#${string}` => typeof val === \"string\" && val.startsWith(\"#\"),\n { message: \"Source reference must start with #\" },\n);\n\n// Transition schema\nexport const effieTransitionSchema = z.union([\n // Fade with easing\n z.strictObject({\n type: z.literal(\"fade\"),\n duration: z.number(),\n easing: z.enum([\"linear\", \"ease-in\", \"ease-out\"]).optional(),\n }),\n // Fade through color\n z.strictObject({\n type: z.literal(\"fade\"),\n duration: z.number(),\n through: z.enum([\"black\", \"white\", \"grays\"]),\n }),\n // Barn door wipes\n z.strictObject({\n type: z.literal(\"barn\"),\n duration: z.number(),\n orientation: z.enum([\"horizontal\", \"vertical\"]).optional(),\n mode: z.enum([\"open\", \"close\"]).optional(),\n }),\n // Circle wipes\n z.strictObject({\n type: z.literal(\"circle\"),\n duration: z.number(),\n mode: z.enum([\"open\", \"close\", \"crop\"]).optional(),\n }),\n // Directional transitions (wipe, slide, smooth, slice)\n z.strictObject({\n type: z.enum([\"wipe\", \"slide\", \"smooth\", \"slice\"]),\n duration: z.number(),\n direction: z.enum([\"left\", \"right\", \"up\", \"down\"]).optional(),\n }),\n // Zoom\n z.strictObject({\n type: z.literal(\"zoom\"),\n duration: z.number(),\n }),\n // Standalone transitions\n z.strictObject({\n type: z.enum([\"dissolve\", \"pixelize\", \"radial\"]),\n duration: z.number(),\n }),\n]) satisfies z.ZodType<EffieTransition>;\n\n// Effect schema\nexport const effieEffectSchema = z.union([\n z.strictObject({\n type: z.literal(\"fade-in\"),\n duration: z.number(),\n start: z.number(),\n }),\n z.strictObject({\n type: z.literal(\"fade-out\"),\n duration: z.number(),\n start: z.number(),\n }),\n z.strictObject({\n type: z.literal(\"saturate-in\"),\n duration: z.number(),\n start: z.number(),\n }),\n z.strictObject({\n type: z.literal(\"saturate-out\"),\n duration: z.number(),\n start: z.number(),\n }),\n z.strictObject({\n type: z.literal(\"scroll\"),\n duration: z.number(),\n direction: z.enum([\"left\", \"right\", \"up\", \"down\"]),\n distance: z.number(),\n }),\n]) satisfies z.ZodType<EffieEffect>;\n\n// Motion schema\nexport const effieMotionSchema = z.union([\n z.strictObject({\n type: z.literal(\"bounce\"),\n start: z.number().optional(),\n duration: z.number().optional(),\n amplitude: z.number().optional(),\n }),\n z.strictObject({\n type: z.literal(\"shake\"),\n start: z.number().optional(),\n duration: z.number().optional(),\n intensity: z.number().optional(),\n frequency: z.number().optional(),\n }),\n z.strictObject({\n type: z.literal(\"slide\"),\n start: z.number().optional(),\n duration: z.number().optional(),\n direction: z.enum([\"left\", \"right\", \"up\", \"down\"]),\n distance: z.number().optional(),\n reverse: z.boolean().optional(),\n easing: z.enum([\"linear\", \"ease-in\", \"ease-out\", \"ease-in-out\"]).optional(),\n }),\n]) satisfies z.ZodType<EffieMotion>;\n\n// Schema factories for generic types.\n// Note: These return inferred Zod types. Type checking happens on the concrete\n// exported schemas below via explicit type annotations (which use `satisfies`\n// semantics - assignment to a typed variable fails if types don't match).\n\nexport function createEffieSourcesSchema<U extends string>(\n urlSchema: z.ZodType<U>,\n) {\n return z.record(z.string(), urlSchema);\n}\n\nexport function createEffieSourceSchema<U extends string>(\n urlSchema: z.ZodType<U>,\n) {\n return z.union([urlSchema, sourceRefSchema]);\n}\n\nexport function createEffieBackgroundSchema<U extends string>(\n urlSchema: z.ZodType<U>,\n) {\n const sourceSchema = createEffieSourceSchema(urlSchema);\n\n return z.union([\n z.strictObject({\n type: z.literal(\"image\"),\n source: sourceSchema,\n }),\n z.strictObject({\n type: z.literal(\"video\"),\n source: sourceSchema,\n seek: z.number().optional(),\n }),\n z.strictObject({\n type: z.literal(\"color\"),\n color: z.string(),\n }),\n ]);\n}\n\nexport function createEffieAudioSchema<U extends string>(\n urlSchema: z.ZodType<U>,\n) {\n const sourceSchema = createEffieSourceSchema(urlSchema);\n\n return z.strictObject({\n source: sourceSchema,\n volume: z.number().min(0).max(1).optional(),\n fadeIn: z.number().optional(),\n fadeOut: z.number().optional(),\n seek: z.number().optional(),\n });\n}\n\nexport function createEffieLayerSchema<U extends string>(\n urlSchema: z.ZodType<U>,\n) {\n const sourceSchema = createEffieSourceSchema(urlSchema);\n\n return z.strictObject({\n type: z.enum([\"image\", \"animation\"]),\n source: sourceSchema,\n delay: z.number().optional(),\n from: z.number().optional(),\n until: z.number().optional(),\n effects: z.array(effieEffectSchema).optional(),\n motion: effieMotionSchema.optional(),\n });\n}\n\nexport function createEffieSegmentSchema<U extends string>(\n urlSchema: z.ZodType<U>,\n) {\n const layerSchema = createEffieLayerSchema(urlSchema);\n const backgroundSchema = createEffieBackgroundSchema(urlSchema);\n const audioSchema = createEffieAudioSchema(urlSchema);\n\n return z.strictObject({\n duration: z.number(),\n layers: z.array(layerSchema),\n background: backgroundSchema.optional(),\n audio: audioSchema.optional(),\n transition: effieTransitionSchema.optional(),\n });\n}\n\n// Type for parsed data shape (matches Zod output, used for source ref collection)\ntype ParsedEffieData = {\n background: { source?: string } | { type: \"color\" };\n audio?: { source: string };\n segments: Array<{\n background?: { source?: string } | { type: \"color\" };\n audio?: { source: string };\n layers: Array<{ source: string }>;\n }>;\n};\n\n// Helper to collect all source references from parsed data\nfunction collectSourceRefs(data: ParsedEffieData): string[] {\n const refs: string[] = [];\n\n const addIfRef = (source: string) => {\n if (source.startsWith(\"#\")) {\n refs.push(source.slice(1));\n }\n };\n\n // Top-level background and audio\n if (\"source\" in data.background && data.background.source) {\n addIfRef(data.background.source);\n }\n if (data.audio) {\n addIfRef(data.audio.source);\n }\n\n // Segments\n for (const segment of data.segments) {\n if (\n segment.background &&\n \"source\" in segment.background &&\n segment.background.source\n ) {\n addIfRef(segment.background.source);\n }\n if (segment.audio) {\n addIfRef(segment.audio.source);\n }\n for (const layer of segment.layers) {\n addIfRef(layer.source);\n }\n }\n\n return refs;\n}\n\nexport function createEffieDataSchema<U extends string>(\n urlSchema: z.ZodType<U>,\n) {\n const sourcesSchema = createEffieSourcesSchema(urlSchema);\n const segmentSchema = createEffieSegmentSchema(urlSchema);\n const backgroundSchema = createEffieBackgroundSchema(urlSchema);\n const audioSchema = createEffieAudioSchema(urlSchema);\n\n return z\n .strictObject({\n width: z.number(),\n height: z.number(),\n fps: z.number(),\n cover: effieWebUrlSchema, // cover must always be a web URL\n sources: sourcesSchema.optional(),\n background: backgroundSchema,\n audio: audioSchema.optional(),\n segments: z.array(segmentSchema),\n })\n .superRefine((data, ctx) => {\n // Validate source references exist\n const definedSources = new Set(Object.keys(data.sources ?? {}));\n // Type assertion: Zod has validated the structure, but its inferred types\n // add spurious `| undefined` to required fields\n const referencedSources = collectSourceRefs(data as ParsedEffieData);\n\n for (const ref of referencedSources) {\n if (!definedSources.has(ref)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Source reference \"#${ref}\" not found in sources`,\n });\n }\n }\n\n // Validate transition durations: both current and previous segment must be\n // at least as long as the transition duration\n for (let i = 1; i < data.segments.length; i++) {\n const segment = data.segments[i];\n const prevSegment = data.segments[i - 1];\n\n if (segment.transition) {\n const transitionDuration = segment.transition.duration;\n\n if (segment.duration < transitionDuration) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Segment ${i} duration (${segment.duration}s) must be at least as long as its transition duration (${transitionDuration}s)`,\n path: [\"segments\", i, \"duration\"],\n });\n }\n\n if (prevSegment.duration < transitionDuration) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Segment ${i - 1} duration (${prevSegment.duration}s) must be at least as long as the following transition duration (${transitionDuration}s)`,\n path: [\"segments\", i - 1, \"duration\"],\n });\n }\n }\n }\n });\n}\n\n// Default schemas for web URLs (most common use case):\n\nexport const effieSourcesSchema: z.ZodType<EffieSources<EffieWebUrl>> =\n createEffieSourcesSchema(effieWebUrlSchema);\n\nexport const effieSourceSchema = createEffieSourceSchema(effieWebUrlSchema);\n\nexport const effieBackgroundSchema: z.ZodType<\n EffieBackground<EffieSources<EffieWebUrl>, EffieWebUrl>\n> = createEffieBackgroundSchema(effieWebUrlSchema);\n\nexport const effieAudioSchema: z.ZodType<\n EffieAudio<EffieSources<EffieWebUrl>, EffieWebUrl>\n> = createEffieAudioSchema(effieWebUrlSchema);\n\nexport const effieLayerSchema: z.ZodType<\n EffieLayer<EffieSources<EffieWebUrl>, EffieWebUrl>\n> = createEffieLayerSchema(effieWebUrlSchema);\n\nexport const effieSegmentSchema: z.ZodType<\n EffieSegment<EffieSources<EffieWebUrl>, EffieWebUrl>\n> = createEffieSegmentSchema(effieWebUrlSchema);\n\nexport const effieDataSchema: z.ZodType<\n EffieData<EffieSources<EffieWebUrl>, EffieWebUrl>\n> = createEffieDataSchema(effieWebUrlSchema);\n\n// Schema for data that allows file URLs (for trusted operations)\ntype WebOrFileUrl = EffieWebUrl | EffieFileUrl;\nconst webOrFileUrlSchema = z.custom<WebOrFileUrl>(\n (val): val is WebOrFileUrl =>\n typeof val === \"string\" &&\n (val.startsWith(\"http://\") ||\n val.startsWith(\"https://\") ||\n val.startsWith(\"data:\") ||\n val.startsWith(\"file:\")),\n { message: \"Must be an HTTP, HTTPS, data, or file URL\" },\n);\n\nexport const effieDataWithFilesSchema: z.ZodType<\n EffieData<EffieSources<WebOrFileUrl>, WebOrFileUrl>\n> = createEffieDataSchema(webOrFileUrlSchema);\n"],"mappings":"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","names":["effieData","effieData"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@effing/effie",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Effie video composition format and utilities",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"devDependencies": {
|
|
16
|
+
"tsup": "^8.0.0",
|
|
17
|
+
"typescript": "^5.9.3",
|
|
18
|
+
"vitest": "^3.2.4",
|
|
19
|
+
"zod": "^3.25.76"
|
|
20
|
+
},
|
|
21
|
+
"peerDependencies": {
|
|
22
|
+
"zod": "^3.0.0"
|
|
23
|
+
},
|
|
24
|
+
"peerDependenciesMeta": {
|
|
25
|
+
"zod": {
|
|
26
|
+
"optional": true
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"video",
|
|
31
|
+
"composition",
|
|
32
|
+
"ffmpeg"
|
|
33
|
+
],
|
|
34
|
+
"license": "O'Saasy",
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsup",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"test": "vitest run"
|
|
42
|
+
}
|
|
43
|
+
}
|