@effing/effie 0.1.2 → 0.2.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/dist/index.d.ts CHANGED
@@ -132,6 +132,17 @@ declare function effieDataForSegment(effieData: EffieData<EffieSources>, segment
132
132
  */
133
133
  declare function effieDataForJoin<U extends string = EffieWebUrl, V extends string = EffieWebUrl>(effieData: EffieData<EffieSources<U>, U>, segmentSourceUrls: V[]): EffieData<EffieSources<U | V>, U | V>;
134
134
 
135
+ /**
136
+ * Source type for extraction
137
+ */
138
+ type EffieSourceType = "image" | "video" | "audio" | "animation";
139
+ /**
140
+ * A source with its type information
141
+ */
142
+ type EffieSourceWithType = {
143
+ url: string;
144
+ type: EffieSourceType;
145
+ };
135
146
  /**
136
147
  * Options for extracting sources from EffieData
137
148
  */
@@ -139,6 +150,21 @@ type ExtractSourcesOptions = {
139
150
  /** Include data URLs in the result (default: false) */
140
151
  includeDataUrls?: boolean;
141
152
  };
153
+ /**
154
+ * Extract all source URLs from an EffieData composition with their types.
155
+ * Resolves #references and deduplicates results.
156
+ *
157
+ * @param effieData - The Effie composition
158
+ * @param options - Extraction options
159
+ * @returns Array of unique source URLs with their types
160
+ *
161
+ * @example
162
+ * ```ts
163
+ * const sources = extractEffieSourcesWithTypes(effieData);
164
+ * // [{ url: "https://cdn.example.com/bg.jpg", type: "image" }, ...]
165
+ * ```
166
+ */
167
+ declare function extractEffieSourcesWithTypes<U extends string = EffieWebUrl>(effieData: EffieData<EffieSources<U>, U>, options?: ExtractSourcesOptions): EffieSourceWithType[];
142
168
  /**
143
169
  * Extract all source URLs from an EffieData composition.
144
170
  * Resolves #references and deduplicates results.
@@ -10469,4 +10495,4 @@ declare const effieDataSchema: z.ZodType<EffieData<EffieSources<EffieWebUrl>, Ef
10469
10495
  type WebOrFileUrl = EffieWebUrl | EffieFileUrl;
10470
10496
  declare const effieDataWithFilesSchema: z.ZodType<EffieData<EffieSources<WebOrFileUrl>, WebOrFileUrl>>;
10471
10497
 
10472
- export { type EffieAudio, type EffieBackground, type EffieData, type EffieDataUrl, type EffieEffect, type EffieFileUrl, type EffieHttpUrl, type EffieLayer, type EffieMotion, type EffieResponseOptions, type EffieSegment, type EffieSource, type EffieSources, type EffieTransition, type EffieWebUrl, type ExtractSourcesOptions, createEffieAudioSchema, createEffieBackgroundSchema, createEffieDataSchema, createEffieLayerSchema, createEffieSegmentSchema, createEffieSourceSchema, createEffieSourcesSchema, effieAudioSchema, effieBackground, effieBackgroundSchema, effieData, effieDataForJoin, effieDataForSegment, effieDataSchema, effieDataUrlSchema, effieDataWithFilesSchema, effieEffectSchema, effieFileUrl, effieFileUrlSchema, effieHttpUrlSchema, effieLayer, effieLayerSchema, effieMotionSchema, effieResponse, effieSegment, effieSegmentSchema, effieSourceSchema, effieSourcesSchema, effieTransitionSchema, effieWebUrl, effieWebUrlSchema, extractEffieSources };
10498
+ export { type EffieAudio, type EffieBackground, type EffieData, type EffieDataUrl, type EffieEffect, type EffieFileUrl, type EffieHttpUrl, type EffieLayer, type EffieMotion, type EffieResponseOptions, type EffieSegment, type EffieSource, type EffieSourceType, type EffieSourceWithType, type EffieSources, type EffieTransition, type EffieWebUrl, type ExtractSourcesOptions, createEffieAudioSchema, createEffieBackgroundSchema, createEffieDataSchema, createEffieLayerSchema, createEffieSegmentSchema, createEffieSourceSchema, createEffieSourcesSchema, effieAudioSchema, effieBackground, effieBackgroundSchema, effieData, effieDataForJoin, effieDataForSegment, effieDataSchema, effieDataUrlSchema, effieDataWithFilesSchema, effieEffectSchema, effieFileUrl, effieFileUrlSchema, effieHttpUrlSchema, effieLayer, effieLayerSchema, effieMotionSchema, effieResponse, effieSegment, effieSegmentSchema, effieSourceSchema, effieSourcesSchema, effieTransitionSchema, effieWebUrl, effieWebUrlSchema, extractEffieSources, extractEffieSourcesWithTypes };
package/dist/index.js CHANGED
@@ -122,10 +122,10 @@ function effieDataForJoin(effieData2, segmentSourceUrls) {
122
122
  }
123
123
 
124
124
  // src/extract.ts
125
- function extractEffieSources(effieData2, options = {}) {
125
+ function extractEffieSourcesWithTypes(effieData2, options = {}) {
126
126
  const { includeDataUrls = false } = options;
127
- const sources = /* @__PURE__ */ new Set();
128
- const addSource = (src) => {
127
+ const sourceMap = /* @__PURE__ */ new Map();
128
+ const addSource = (src, type) => {
129
129
  if (src.startsWith("#")) {
130
130
  const name = src.slice(1);
131
131
  if (effieData2.sources?.[name]) {
@@ -137,27 +137,38 @@ function extractEffieSources(effieData2, options = {}) {
137
137
  if (!includeDataUrls && src.startsWith("data:")) {
138
138
  return;
139
139
  }
140
- sources.add(src);
140
+ if (!sourceMap.has(src)) {
141
+ sourceMap.set(src, type);
142
+ }
141
143
  };
142
- addSource(effieData2.cover);
143
- if (effieData2.background.type !== "color") {
144
- addSource(effieData2.background.source);
144
+ addSource(effieData2.cover, "image");
145
+ if (effieData2.background.type === "image") {
146
+ addSource(effieData2.background.source, "image");
147
+ } else if (effieData2.background.type === "video") {
148
+ addSource(effieData2.background.source, "video");
145
149
  }
146
150
  if (effieData2.audio) {
147
- addSource(effieData2.audio.source);
151
+ addSource(effieData2.audio.source, "audio");
148
152
  }
149
153
  for (const segment of effieData2.segments) {
150
- if (segment.background && segment.background.type !== "color") {
151
- addSource(segment.background.source);
154
+ if (segment.background) {
155
+ if (segment.background.type === "image") {
156
+ addSource(segment.background.source, "image");
157
+ } else if (segment.background.type === "video") {
158
+ addSource(segment.background.source, "video");
159
+ }
152
160
  }
153
161
  if (segment.audio) {
154
- addSource(segment.audio.source);
162
+ addSource(segment.audio.source, "audio");
155
163
  }
156
164
  for (const layer of segment.layers) {
157
- addSource(layer.source);
165
+ addSource(layer.source, layer.type);
158
166
  }
159
167
  }
160
- return Array.from(sources);
168
+ return Array.from(sourceMap.entries()).map(([url, type]) => ({ url, type }));
169
+ }
170
+ function extractEffieSources(effieData2, options = {}) {
171
+ return extractEffieSourcesWithTypes(effieData2, options).map((s) => s.url);
161
172
  }
162
173
 
163
174
  // src/response.ts
@@ -464,6 +475,7 @@ export {
464
475
  effieTransitionSchema,
465
476
  effieWebUrl,
466
477
  effieWebUrlSchema,
467
- extractEffieSources
478
+ extractEffieSources,
479
+ extractEffieSourcesWithTypes
468
480
  };
469
481
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +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"]}
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 * Source type for extraction\n */\nexport type EffieSourceType = \"image\" | \"video\" | \"audio\" | \"animation\";\n\n/**\n * A source with its type information\n */\nexport type EffieSourceWithType = {\n url: string;\n type: EffieSourceType;\n};\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 with their types.\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 with their types\n *\n * @example\n * ```ts\n * const sources = extractEffieSourcesWithTypes(effieData);\n * // [{ url: \"https://cdn.example.com/bg.jpg\", type: \"image\" }, ...]\n * ```\n */\nexport function extractEffieSourcesWithTypes<U extends string = EffieWebUrl>(\n effieData: EffieData<EffieSources<U>, U>,\n options: ExtractSourcesOptions = {},\n): EffieSourceWithType[] {\n const { includeDataUrls = false } = options;\n // Map from URL to type (first type wins for deduplication)\n const sourceMap = new Map<string, EffieSourceType>();\n\n const addSource = (src: string, type: EffieSourceType) => {\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 // Only add if not already present (first type wins)\n if (!sourceMap.has(src)) {\n sourceMap.set(src, type);\n }\n };\n\n // Cover image\n addSource(effieData.cover, \"image\");\n\n // Global background\n if (effieData.background.type === \"image\") {\n addSource(effieData.background.source, \"image\");\n } else if (effieData.background.type === \"video\") {\n addSource(effieData.background.source, \"video\");\n }\n\n // Global audio\n if (effieData.audio) {\n addSource(effieData.audio.source, \"audio\");\n }\n\n // Segments\n for (const segment of effieData.segments) {\n // Segment background\n if (segment.background) {\n if (segment.background.type === \"image\") {\n addSource(segment.background.source, \"image\");\n } else if (segment.background.type === \"video\") {\n addSource(segment.background.source, \"video\");\n }\n }\n // Segment audio\n if (segment.audio) {\n addSource(segment.audio.source, \"audio\");\n }\n // Layers\n for (const layer of segment.layers) {\n addSource(layer.source, layer.type);\n }\n }\n\n return Array.from(sourceMap.entries()).map(([url, type]) => ({ url, type }));\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 return extractEffieSourcesWithTypes(effieData, options).map((s) => s.url);\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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effing/effie",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "Effie video composition format and utilities",
5
5
  "type": "module",
6
6
  "exports": {