@editframe/vite-plugin 0.40.1-beta.0 → 0.40.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js.map +1 -1
- package/dist/middleware.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { Client, createURLToken } from \"@editframe/api\";\nimport {\n cacheImage,\n findOrCreateCaptions,\n generateTrack,\n generateScrubTrack,\n generateTrackFragmentIndex,\n md5FilePath,\n} from \"@editframe/assets\";\nimport debug from \"debug\";\nimport type { Plugin } from \"vite\";\n\nimport { createJitTranscodeMiddleware } from \"./jitTranscodeMiddleware.js\";\nimport {\n createAssetsApiMiddleware,\n createLocalFilesApiMiddleware,\n handleClearCache,\n} from \"./middleware.js\";\nimport { forbidRelativePaths } from \"./forbidRelativePaths.js\";\n\ninterface VitePluginEditframeOptions {\n root: string;\n cacheRoot: string;\n}\n\nconst getEditframeClient = () => {\n const token = process.env.EF_TOKEN;\n const efHost = process.env.EF_HOST;\n if (!token) {\n throw new Error(\"EF_TOKEN environment variable must be set\");\n }\n return new Client(token, efHost);\n};\n\nexport const vitePluginEditframe = (options: VitePluginEditframeOptions) => {\n return {\n name: \"vite-plugin-editframe\",\n\n configureServer(server) {\n server.middlewares.use(\n createJitTranscodeMiddleware(\n { ...options, handleRemoteUrls: true },\n { generateTrack, generateScrubTrack, generateTrackFragmentIndex },\n ),\n );\n\n server.middlewares.use(\n createAssetsApiMiddleware(options, {
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import { Client, createURLToken } from \"@editframe/api\";\nimport {\n cacheImage,\n findOrCreateCaptions,\n generateTrack,\n generateScrubTrack,\n generateTrackFragmentIndex,\n md5FilePath,\n} from \"@editframe/assets\";\nimport debug from \"debug\";\nimport type { Plugin } from \"vite\";\n\nimport { createJitTranscodeMiddleware } from \"./jitTranscodeMiddleware.js\";\nimport {\n createAssetsApiMiddleware,\n createLocalFilesApiMiddleware,\n handleClearCache,\n} from \"./middleware.js\";\nimport { forbidRelativePaths } from \"./forbidRelativePaths.js\";\n\ninterface VitePluginEditframeOptions {\n root: string;\n cacheRoot: string;\n}\n\nconst getEditframeClient = () => {\n const token = process.env.EF_TOKEN;\n const efHost = process.env.EF_HOST;\n if (!token) {\n throw new Error(\"EF_TOKEN environment variable must be set\");\n }\n return new Client(token, efHost);\n};\n\nexport const vitePluginEditframe = (options: VitePluginEditframeOptions) => {\n return {\n name: \"vite-plugin-editframe\",\n\n configureServer(server) {\n server.middlewares.use(\n createJitTranscodeMiddleware(\n { ...options, handleRemoteUrls: true },\n { generateTrack, generateScrubTrack, generateTrackFragmentIndex },\n ),\n );\n\n server.middlewares.use(\n createAssetsApiMiddleware(options, {\n cacheImage,\n findOrCreateCaptions,\n }),\n );\n\n server.middlewares.use(\n createLocalFilesApiMiddleware(options, {\n generateTrack,\n generateScrubTrack,\n generateTrackFragmentIndex,\n md5FilePath,\n }),\n );\n\n server.middlewares.use(async (req, res, next) => {\n const log = debug(\"ef:vite-plugin\");\n if (req.url?.startsWith(\"/@ef\")) {\n forbidRelativePaths(req);\n } else {\n return next();\n }\n\n log(`Handling ${req.url} at ${new Date().toISOString()}`);\n\n const cacheRoot = options.cacheRoot.replace(\"dist/\", \"src/\");\n const efPrefix = req.url.split(\"/\")[1];\n\n switch (efPrefix) {\n case \"@ef-clear-cache\": {\n await handleClearCache(req, res, cacheRoot);\n break;\n }\n case \"@ef-sign-url\": {\n if (req.method !== \"POST\") {\n res.writeHead(405, { Allow: \"POST\" });\n res.end();\n break;\n }\n\n log(\"Signing URL token\");\n\n let body = \"\";\n req.on(\"data\", (chunk) => {\n body += chunk.toString();\n });\n\n req.on(\"end\", async () => {\n try {\n const payload = JSON.parse(body);\n log(\"Token signing request payload:\", payload);\n\n const { url, params } = payload;\n if (!url) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"URL is required\" }));\n return;\n }\n\n const client = getEditframeClient();\n\n let fullUrl = url;\n if (params) {\n const urlObj = new URL(url);\n Object.entries(params).forEach(([key, value]) => {\n urlObj.searchParams.set(key, String(value));\n });\n fullUrl = urlObj.toString();\n }\n\n log(\"Creating token for full URL:\", fullUrl);\n const token = await createURLToken(client, fullUrl);\n\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ token }));\n } catch (error) {\n log(`Error signing URL token: ${error}`);\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Failed to sign URL token\" }));\n }\n });\n\n break;\n }\n default:\n log(`Unknown asset type ${efPrefix}`);\n break;\n }\n });\n },\n } satisfies Plugin;\n};\n"],"mappings":";;;;;;;;AAyBA,MAAM,2BAA2B;CAC/B,MAAM,QAAQ,QAAQ,IAAI;CAC1B,MAAM,SAAS,QAAQ,IAAI;AAC3B,KAAI,CAAC,MACH,OAAM,IAAI,MAAM,4CAA4C;AAE9D,QAAO,IAAI,OAAO,OAAO,OAAO;;AAGlC,MAAa,uBAAuB,YAAwC;AAC1E,QAAO;EACL,MAAM;EAEN,gBAAgB,QAAQ;AACtB,UAAO,YAAY,IACjB,6BACE;IAAE,GAAG;IAAS,kBAAkB;IAAM,EACtC;IAAE;IAAe;IAAoB;IAA4B,CAClE,CACF;AAED,UAAO,YAAY,IACjB,0BAA0B,SAAS;IACjC;IACA;IACD,CAAC,CACH;AAED,UAAO,YAAY,IACjB,8BAA8B,SAAS;IACrC;IACA;IACA;IACA;IACD,CAAC,CACH;AAED,UAAO,YAAY,IAAI,OAAO,KAAK,KAAK,SAAS;IAC/C,MAAM,MAAM,MAAM,iBAAiB;AACnC,QAAI,IAAI,KAAK,WAAW,OAAO,CAC7B,qBAAoB,IAAI;QAExB,QAAO,MAAM;AAGf,QAAI,YAAY,IAAI,IAAI,uBAAM,IAAI,MAAM,EAAC,aAAa,GAAG;IAEzD,MAAM,YAAY,QAAQ,UAAU,QAAQ,SAAS,OAAO;IAC5D,MAAM,WAAW,IAAI,IAAI,MAAM,IAAI,CAAC;AAEpC,YAAQ,UAAR;KACE,KAAK;AACH,YAAM,iBAAiB,KAAK,KAAK,UAAU;AAC3C;KAEF,KAAK,gBAAgB;AACnB,UAAI,IAAI,WAAW,QAAQ;AACzB,WAAI,UAAU,KAAK,EAAE,OAAO,QAAQ,CAAC;AACrC,WAAI,KAAK;AACT;;AAGF,UAAI,oBAAoB;MAExB,IAAI,OAAO;AACX,UAAI,GAAG,SAAS,UAAU;AACxB,eAAQ,MAAM,UAAU;QACxB;AAEF,UAAI,GAAG,OAAO,YAAY;AACxB,WAAI;QACF,MAAM,UAAU,KAAK,MAAM,KAAK;AAChC,YAAI,kCAAkC,QAAQ;QAE9C,MAAM,EAAE,KAAK,WAAW;AACxB,YAAI,CAAC,KAAK;AACR,aAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,aAAI,IAAI,KAAK,UAAU,EAAE,OAAO,mBAAmB,CAAC,CAAC;AACrD;;QAGF,MAAM,SAAS,oBAAoB;QAEnC,IAAI,UAAU;AACd,YAAI,QAAQ;SACV,MAAM,SAAS,IAAI,IAAI,IAAI;AAC3B,gBAAO,QAAQ,OAAO,CAAC,SAAS,CAAC,KAAK,WAAW;AAC/C,iBAAO,aAAa,IAAI,KAAK,OAAO,MAAM,CAAC;WAC3C;AACF,mBAAU,OAAO,UAAU;;AAG7B,YAAI,gCAAgC,QAAQ;QAC5C,MAAM,QAAQ,MAAM,eAAe,QAAQ,QAAQ;AAEnD,YAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,YAAI,IAAI,KAAK,UAAU,EAAE,OAAO,CAAC,CAAC;gBAC3B,OAAO;AACd,YAAI,4BAA4B,QAAQ;AACxC,YAAI,UAAU,KAAK,EAAE,gBAAgB,oBAAoB,CAAC;AAC1D,YAAI,IAAI,KAAK,UAAU,EAAE,OAAO,4BAA4B,CAAC,CAAC;;QAEhE;AAEF;;KAEF;AACE,UAAI,sBAAsB,WAAW;AACrC;;KAEJ;;EAEL"}
|
package/dist/middleware.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.js","names":["error: any"],"sources":["../src/middleware.ts"],"sourcesContent":["import { rm } from \"node:fs/promises\";\nimport path, { join } from \"node:path\";\nimport type { ServerResponse } from \"node:http\";\nimport type { IncomingMessage, NextFunction } from \"connect\";\nimport debug from \"debug\";\n\nimport { forbidRelativePaths } from \"./forbidRelativePaths.js\";\nimport { sendTaskResult } from \"./sendTaskResult.js\";\n\ntype Middleware = (\n req: IncomingMessage,\n res: ServerResponse,\n next: NextFunction,\n) => void;\n\ninterface PluginOptions {\n root: string;\n cacheRoot: string;\n}\n\ninterface AssetsDeps {\n cacheImage: (cacheRoot: string, src: string) => Promise<any>;\n findOrCreateCaptions: (cacheRoot: string, src: string) => Promise<any>;\n}\n\ninterface FilesDeps {\n generateTrack: (\n cacheRoot: string,\n src: string,\n trackUrl: string,\n ) => Promise<any>;\n generateScrubTrack: (cacheRoot: string, src: string) => Promise<any>;\n generateTrackFragmentIndex: (\n cacheRoot: string,\n src: string,\n ) => Promise<any>;\n md5FilePath: (src: string) => Promise<string>;\n}\n\nexport function createAssetsApiMiddleware(\n options: PluginOptions,\n deps: AssetsDeps,\n): Middleware {\n const { cacheImage, findOrCreateCaptions } = deps;\n return async (req, res, next) => {\n const log = debug(\"ef:vite-plugin:assets\");\n const reqUrl = req.url || \"\";\n\n if (!reqUrl.startsWith(\"/api/v1/assets/\")) {\n return next();\n }\n\n forbidRelativePaths(req);\n\n const url = new URL(reqUrl, `http://${req.headers.host}`);\n const urlPath = url.pathname;\n const src = url.searchParams.get(\"src\");\n\n if (!src) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"src parameter is required\" }));\n return;\n }\n\n const isRemote = src.startsWith(\"http://\") || src.startsWith(\"https://\");\n const absolutePath = isRemote\n ? src\n : path.join(options.root, src).replace(\"dist/\", \"src/\");\n\n log(`Handling assets API: ${urlPath} src=${src}`);\n\n try {\n if (urlPath === \"/api/v1/assets/image\") {\n if (isRemote) {\n const response = await fetch(src);\n if (!response.ok) {\n res.writeHead(response.status);\n res.end();\n return;\n }\n const contentType =\n response.headers.get(\"content-type\") ?? \"application/octet-stream\";\n const buffer = await response.arrayBuffer();\n res.writeHead(200, { \"Content-Type\": contentType });\n res.end(Buffer.from(buffer));\n } else {\n const taskResult = await cacheImage(options.cacheRoot, absolutePath);\n sendTaskResult(req, res, taskResult);\n }\n return;\n }\n\n if (urlPath === \"/api/v1/assets/captions\") {\n const taskResult = await findOrCreateCaptions(\n options.cacheRoot,\n absolutePath,\n );\n sendTaskResult(req, res, taskResult);\n return;\n }\n\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Unknown assets endpoint\" }));\n } catch (error) {\n log(`Error handling assets request: ${error}`);\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"File not found\");\n } else {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: (error as Error).message }));\n }\n }\n };\n}\n\nexport function createLocalFilesApiMiddleware(\n options: PluginOptions,\n deps: FilesDeps,\n): Middleware {\n const { generateTrack, generateScrubTrack, generateTrackFragmentIndex, md5FilePath } =\n deps;\n return async (req, res, next) => {\n const log = debug(\"ef:vite-plugin:files\");\n const reqUrl = req.url || \"\";\n\n const url = new URL(reqUrl, `http://${req.headers.host}`);\n const urlPath = url.pathname;\n const src = url.searchParams.get(\"src\");\n\n if (\n !src ||\n (urlPath !== \"/api/v1/files/index\" &&\n urlPath !== \"/api/v1/files/md5\" &&\n urlPath !== \"/api/v1/files/track\")\n ) {\n return next();\n }\n\n forbidRelativePaths(req);\n\n const absolutePath = src.startsWith(\"http\")\n ? src\n : path.join(options.root, src).replace(\"dist/\", \"src/\");\n\n log(`Handling local file API: ${urlPath} for ${absolutePath}`);\n\n try {\n if (urlPath === \"/api/v1/files/index\") {\n log(`Serving track fragment index for ${absolutePath}`);\n const taskResult = await generateTrackFragmentIndex(\n options.cacheRoot,\n absolutePath,\n );\n sendTaskResult(req, res, taskResult);\n return;\n }\n\n if (urlPath === \"/api/v1/files/md5\") {\n log(`Getting MD5 for ${absolutePath}`);\n try {\n const md5 = await md5FilePath(absolutePath);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ md5 }));\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"File not found\");\n } else {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: (error as Error).message }));\n }\n }\n return;\n }\n\n if (urlPath === \"/api/v1/files/track\") {\n const trackIdStr = url.searchParams.get(\"trackId\");\n const segmentIdStr = url.searchParams.get(\"segmentId\");\n\n if (!trackIdStr) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"trackId parameter is required\" }));\n return;\n }\n\n const trackId = parseInt(trackIdStr, 10);\n\n if (trackId === -1) {\n log(`Serving scrub track for ${absolutePath}`);\n const taskResult = await generateScrubTrack(\n options.cacheRoot,\n absolutePath,\n );\n sendTaskResult(req, res, taskResult);\n return;\n }\n\n log(\n `Serving track ${trackId} segment ${segmentIdStr || \"all\"} for ${absolutePath}`,\n );\n const trackUrl = `/@ef-track/${src}?trackId=${trackId}${segmentIdStr ? `&segmentId=${segmentIdStr}` : \"\"}`;\n const taskResult = await generateTrack(\n options.cacheRoot,\n absolutePath,\n trackUrl,\n );\n sendTaskResult(req, res, taskResult);\n return;\n }\n } catch (error) {\n log(`Error handling local file request: ${error}`);\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"File not found\");\n } else {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: (error as Error).message }));\n }\n }\n };\n}\n\nexport async function handleClearCache(\n req: IncomingMessage,\n res: ServerResponse,\n cacheRoot: string,\n): Promise<void> {\n const log = debug(\"ef:vite-plugin\");\n if (req.method !== \"DELETE\") {\n res.writeHead(405, { Allow: \"DELETE\" });\n res.end();\n return;\n }\n log(`Clearing cache for ${cacheRoot}`);\n const cachePath = join(cacheRoot, \".cache\");\n const maxRetries = 3;\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n await rm(cachePath, { recursive: true, force: true });\n break;\n } catch (error: any) {\n if (error.code === \"ENOENT\") {\n break;\n }\n if (error.code === \"ENOTEMPTY\" && attempt < maxRetries - 1) {\n await new Promise((resolve) =>\n setTimeout(resolve, 100 * (attempt + 1)),\n );\n continue;\n }\n log(\n `Warning: Cache clear attempt ${attempt + 1} failed: ${error.message}`,\n );\n if (attempt === maxRetries - 1) {\n log(`Cache clear failed after ${maxRetries} attempts, continuing anyway`);\n }\n }\n }\n res.writeHead(200, { \"Content-Type\": \"text/plain\" });\n res.end(\"Cache cleared\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"middleware.js","names":["error: any"],"sources":["../src/middleware.ts"],"sourcesContent":["import { rm } from \"node:fs/promises\";\nimport path, { join } from \"node:path\";\nimport type { ServerResponse } from \"node:http\";\nimport type { IncomingMessage, NextFunction } from \"connect\";\nimport debug from \"debug\";\n\nimport { forbidRelativePaths } from \"./forbidRelativePaths.js\";\nimport { sendTaskResult } from \"./sendTaskResult.js\";\n\ntype Middleware = (\n req: IncomingMessage,\n res: ServerResponse,\n next: NextFunction,\n) => void;\n\ninterface PluginOptions {\n root: string;\n cacheRoot: string;\n}\n\ninterface AssetsDeps {\n cacheImage: (cacheRoot: string, src: string) => Promise<any>;\n findOrCreateCaptions: (cacheRoot: string, src: string) => Promise<any>;\n}\n\ninterface FilesDeps {\n generateTrack: (\n cacheRoot: string,\n src: string,\n trackUrl: string,\n ) => Promise<any>;\n generateScrubTrack: (cacheRoot: string, src: string) => Promise<any>;\n generateTrackFragmentIndex: (cacheRoot: string, src: string) => Promise<any>;\n md5FilePath: (src: string) => Promise<string>;\n}\n\nexport function createAssetsApiMiddleware(\n options: PluginOptions,\n deps: AssetsDeps,\n): Middleware {\n const { cacheImage, findOrCreateCaptions } = deps;\n return async (req, res, next) => {\n const log = debug(\"ef:vite-plugin:assets\");\n const reqUrl = req.url || \"\";\n\n if (!reqUrl.startsWith(\"/api/v1/assets/\")) {\n return next();\n }\n\n forbidRelativePaths(req);\n\n const url = new URL(reqUrl, `http://${req.headers.host}`);\n const urlPath = url.pathname;\n const src = url.searchParams.get(\"src\");\n\n if (!src) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"src parameter is required\" }));\n return;\n }\n\n const isRemote = src.startsWith(\"http://\") || src.startsWith(\"https://\");\n const absolutePath = isRemote\n ? src\n : path.join(options.root, src).replace(\"dist/\", \"src/\");\n\n log(`Handling assets API: ${urlPath} src=${src}`);\n\n try {\n if (urlPath === \"/api/v1/assets/image\") {\n if (isRemote) {\n const response = await fetch(src);\n if (!response.ok) {\n res.writeHead(response.status);\n res.end();\n return;\n }\n const contentType =\n response.headers.get(\"content-type\") ?? \"application/octet-stream\";\n const buffer = await response.arrayBuffer();\n res.writeHead(200, { \"Content-Type\": contentType });\n res.end(Buffer.from(buffer));\n } else {\n const taskResult = await cacheImage(options.cacheRoot, absolutePath);\n sendTaskResult(req, res, taskResult);\n }\n return;\n }\n\n if (urlPath === \"/api/v1/assets/captions\") {\n const taskResult = await findOrCreateCaptions(\n options.cacheRoot,\n absolutePath,\n );\n sendTaskResult(req, res, taskResult);\n return;\n }\n\n res.writeHead(404, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"Unknown assets endpoint\" }));\n } catch (error) {\n log(`Error handling assets request: ${error}`);\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"File not found\");\n } else {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: (error as Error).message }));\n }\n }\n };\n}\n\nexport function createLocalFilesApiMiddleware(\n options: PluginOptions,\n deps: FilesDeps,\n): Middleware {\n const {\n generateTrack,\n generateScrubTrack,\n generateTrackFragmentIndex,\n md5FilePath,\n } = deps;\n return async (req, res, next) => {\n const log = debug(\"ef:vite-plugin:files\");\n const reqUrl = req.url || \"\";\n\n const url = new URL(reqUrl, `http://${req.headers.host}`);\n const urlPath = url.pathname;\n const src = url.searchParams.get(\"src\");\n\n if (\n !src ||\n (urlPath !== \"/api/v1/files/index\" &&\n urlPath !== \"/api/v1/files/md5\" &&\n urlPath !== \"/api/v1/files/track\")\n ) {\n return next();\n }\n\n forbidRelativePaths(req);\n\n const absolutePath = src.startsWith(\"http\")\n ? src\n : path.join(options.root, src).replace(\"dist/\", \"src/\");\n\n log(`Handling local file API: ${urlPath} for ${absolutePath}`);\n\n try {\n if (urlPath === \"/api/v1/files/index\") {\n log(`Serving track fragment index for ${absolutePath}`);\n const taskResult = await generateTrackFragmentIndex(\n options.cacheRoot,\n absolutePath,\n );\n sendTaskResult(req, res, taskResult);\n return;\n }\n\n if (urlPath === \"/api/v1/files/md5\") {\n log(`Getting MD5 for ${absolutePath}`);\n try {\n const md5 = await md5FilePath(absolutePath);\n res.writeHead(200, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ md5 }));\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"File not found\");\n } else {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: (error as Error).message }));\n }\n }\n return;\n }\n\n if (urlPath === \"/api/v1/files/track\") {\n const trackIdStr = url.searchParams.get(\"trackId\");\n const segmentIdStr = url.searchParams.get(\"segmentId\");\n\n if (!trackIdStr) {\n res.writeHead(400, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: \"trackId parameter is required\" }));\n return;\n }\n\n const trackId = parseInt(trackIdStr, 10);\n\n if (trackId === -1) {\n log(`Serving scrub track for ${absolutePath}`);\n const taskResult = await generateScrubTrack(\n options.cacheRoot,\n absolutePath,\n );\n sendTaskResult(req, res, taskResult);\n return;\n }\n\n log(\n `Serving track ${trackId} segment ${segmentIdStr || \"all\"} for ${absolutePath}`,\n );\n const trackUrl = `/@ef-track/${src}?trackId=${trackId}${segmentIdStr ? `&segmentId=${segmentIdStr}` : \"\"}`;\n const taskResult = await generateTrack(\n options.cacheRoot,\n absolutePath,\n trackUrl,\n );\n sendTaskResult(req, res, taskResult);\n return;\n }\n } catch (error) {\n log(`Error handling local file request: ${error}`);\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") {\n res.writeHead(404, { \"Content-Type\": \"text/plain\" });\n res.end(\"File not found\");\n } else {\n res.writeHead(500, { \"Content-Type\": \"application/json\" });\n res.end(JSON.stringify({ error: (error as Error).message }));\n }\n }\n };\n}\n\nexport async function handleClearCache(\n req: IncomingMessage,\n res: ServerResponse,\n cacheRoot: string,\n): Promise<void> {\n const log = debug(\"ef:vite-plugin\");\n if (req.method !== \"DELETE\") {\n res.writeHead(405, { Allow: \"DELETE\" });\n res.end();\n return;\n }\n log(`Clearing cache for ${cacheRoot}`);\n const cachePath = join(cacheRoot, \".cache\");\n const maxRetries = 3;\n for (let attempt = 0; attempt < maxRetries; attempt++) {\n try {\n await rm(cachePath, { recursive: true, force: true });\n break;\n } catch (error: any) {\n if (error.code === \"ENOENT\") {\n break;\n }\n if (error.code === \"ENOTEMPTY\" && attempt < maxRetries - 1) {\n await new Promise((resolve) =>\n setTimeout(resolve, 100 * (attempt + 1)),\n );\n continue;\n }\n log(\n `Warning: Cache clear attempt ${attempt + 1} failed: ${error.message}`,\n );\n if (attempt === maxRetries - 1) {\n log(\n `Cache clear failed after ${maxRetries} attempts, continuing anyway`,\n );\n }\n }\n }\n res.writeHead(200, { \"Content-Type\": \"text/plain\" });\n res.end(\"Cache cleared\");\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@editframe/vite-plugin",
|
|
3
|
-
"version": "0.40.
|
|
3
|
+
"version": "0.40.2",
|
|
4
4
|
"description": "Editframe vite plugin",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
"author": "",
|
|
21
21
|
"license": "UNLICENSED",
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@editframe/api": "0.40.
|
|
24
|
-
"@editframe/assets": "0.40.
|
|
23
|
+
"@editframe/api": "0.40.2",
|
|
24
|
+
"@editframe/assets": "0.40.2",
|
|
25
25
|
"connect": "^3.7.0",
|
|
26
26
|
"debug": "^4.3.5",
|
|
27
27
|
"mime": "^4.0.3",
|
|
@@ -48,4 +48,4 @@
|
|
|
48
48
|
},
|
|
49
49
|
"main": "./dist/index.js",
|
|
50
50
|
"module": "./dist/index.js"
|
|
51
|
-
}
|
|
51
|
+
}
|