From 5f0b912c236b7794de660d030f96264a349d250f Mon Sep 17 00:00:00 2001 From: Hunain Ahmed Date: Mon, 6 Jan 2025 15:26:17 +0500 Subject: [PATCH 01/11] add next.js app router simplifier --- packages/lambda/src/client.ts | 2 + packages/lambda/src/simplifiers/index.ts | 58 ++++++++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 packages/lambda/src/simplifiers/index.ts diff --git a/packages/lambda/src/client.ts b/packages/lambda/src/client.ts index efad02e9281..97b4ab166fb 100644 --- a/packages/lambda/src/client.ts +++ b/packages/lambda/src/client.ts @@ -7,6 +7,7 @@ import {getRenderProgress} from './api/get-render-progress'; import {getSites} from './api/get-sites'; import type {PresignUrlInput} from './api/presign-url'; import {presignUrl} from './api/presign-url'; +import { appRouterWebhooks } from './simplifiers'; import type { RenderMediaOnLambdaInput, RenderMediaOnLambdaOutput, @@ -45,6 +46,7 @@ export { renderVideoOnLambda, speculateFunctionName, validateWebhookSignature, + appRouterWebhooks }; export type { DeleteRenderInput, diff --git a/packages/lambda/src/simplifiers/index.ts b/packages/lambda/src/simplifiers/index.ts new file mode 100644 index 00000000000..f99d6b604f2 --- /dev/null +++ b/packages/lambda/src/simplifiers/index.ts @@ -0,0 +1,58 @@ +import { validateWebhookSignature } from "../client"; +import type { WebhookPayload } from "../client"; + +type AppRouterWebhookArgs = { + testing?: boolean; + extraHeaders?: Record; + secret: string; + onSuccess?: (payload: WebhookPayload) => void; + onTimeout?: (payload: WebhookPayload) => void; + onError?: (payload: WebhookPayload) => void; +} + +const appRouterWebhooks = (options: AppRouterWebhookArgs): (req: Request) => Promise => { + const {testing, extraHeaders, secret, onSuccess, onTimeout, onError} = options; + return async function(req: Request): Promise { + let headers = extraHeaders || {}; + + if (testing) { + const testingheaders = { + 'Access-Control-Allow-Origin': 'https://www.remotion.dev', + 'Access-Control-Allow-Headers': + 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-Remotion-Status, X-Remotion-Signature, X-Remotion-Mode', + 'Access-Control-Allow-Methods': 'OPTIONS,POST', + }; + headers = {...headers, ...testingheaders}; + } + + if (req.method === 'OPTIONS') { + // do we have any use of the OPTIONS method other than the tester on webhooks page ? if so we can add a condition here to only return this if testing mode enabled + return new Response(null, { + headers, + }); + } + + // Parse the body properly + const body = await req.json(); + + validateWebhookSignature({ + secret, + body, + signatureHeader: req.headers.get('X-Remotion-Signature') as string, + }); + + const payload = body as WebhookPayload; + + if (payload.type === 'success' && onSuccess) { + onSuccess(payload); + } else if (payload.type === 'timeout' && onTimeout) { + onTimeout(payload); + } else if (payload.type === 'error' && onError) { + onError(payload); + } + + return new Response(JSON.stringify({success: true}), {headers}); + } +} + +export {appRouterWebhooks} \ No newline at end of file From 136b0dbe7a240c659400708873f118f60f1986ca Mon Sep 17 00:00:00 2001 From: Hunain Ahmed Date: Mon, 6 Jan 2025 18:31:44 +0500 Subject: [PATCH 02/11] add function for next.js pages router --- packages/lambda/package.json | 1 + packages/lambda/src/client.ts | 5 ++- packages/lambda/src/simplifiers/index.ts | 54 +++++++++++++++++++++++- 3 files changed, 56 insertions(+), 4 deletions(-) diff --git a/packages/lambda/package.json b/packages/lambda/package.json index cc0949951c5..e7d265feef5 100644 --- a/packages/lambda/package.json +++ b/packages/lambda/package.json @@ -35,6 +35,7 @@ "@remotion/streaming": "workspace:*", "@remotion/serverless": "workspace:*", "mime-types": "2.1.34", + "next": "^15.1.3", "remotion": "workspace:*", "zod": "3.22.3" }, diff --git a/packages/lambda/src/client.ts b/packages/lambda/src/client.ts index 97b4ab166fb..166c8d7ecf9 100644 --- a/packages/lambda/src/client.ts +++ b/packages/lambda/src/client.ts @@ -7,7 +7,7 @@ import {getRenderProgress} from './api/get-render-progress'; import {getSites} from './api/get-sites'; import type {PresignUrlInput} from './api/presign-url'; import {presignUrl} from './api/presign-url'; -import { appRouterWebhooks } from './simplifiers'; +import { appRouterWebhooks, pageRouterWebhooks } from './simplifiers'; import type { RenderMediaOnLambdaInput, RenderMediaOnLambdaOutput, @@ -46,7 +46,8 @@ export { renderVideoOnLambda, speculateFunctionName, validateWebhookSignature, - appRouterWebhooks + appRouterWebhooks, + pageRouterWebhooks, }; export type { DeleteRenderInput, diff --git a/packages/lambda/src/simplifiers/index.ts b/packages/lambda/src/simplifiers/index.ts index f99d6b604f2..ba14d0f0cc2 100644 --- a/packages/lambda/src/simplifiers/index.ts +++ b/packages/lambda/src/simplifiers/index.ts @@ -1,6 +1,6 @@ import { validateWebhookSignature } from "../client"; import type { WebhookPayload } from "../client"; - +import type {NextApiRequest, NextApiResponse} from 'next' type AppRouterWebhookArgs = { testing?: boolean; extraHeaders?: Record; @@ -55,4 +55,54 @@ const appRouterWebhooks = (options: AppRouterWebhookArgs): (req: Request) => Pro } } -export {appRouterWebhooks} \ No newline at end of file +const headersMutator = (res: NextApiResponse, headers: Record) => { + Object.entries(headers).forEach(([key, value]) => { + res.setHeader(key, value); + }) + }; + + const pageRouterWebhooks = (options: AppRouterWebhookArgs) => { + const {testing, extraHeaders, secret, onSuccess, onTimeout, onError} = options; + return async function(req: NextApiRequest, res: NextApiResponse): Promise { + // set the headers + headersMutator(res, extraHeaders || {}) + + if (testing) { + const testingheaders = { + 'Access-Control-Allow-Origin': 'https://www.remotion.dev', + 'Access-Control-Allow-Headers': + 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-Remotion-Status, X-Remotion-Signature, X-Remotion-Mode', + 'Access-Control-Allow-Methods': 'OPTIONS,POST', + }; + // add the headers for testing + headersMutator(res, testingheaders) + } + + if (req.method === 'OPTIONS') { + res.status(200).end(); + return; + } + + validateWebhookSignature({ + secret, + body: req.body, + signatureHeader: req.headers['x-remotion-signature'] as string, + }); + + // If code reaches this path, the webhook is authentic. + const payload = req.body as WebhookPayload; + if (payload.type === 'success' && onSuccess) { + onSuccess(payload) + } else if (payload.type === 'timeout' && onTimeout) { + onTimeout(payload) + + }else if (payload.type === 'error' && onError) { + onError(payload) + } + + res.status(200).json({ + success: true, + }); + }} + +export {appRouterWebhooks, pageRouterWebhooks} \ No newline at end of file From b6c96d9f081f21fdd7b41f239407301d5c8731b1 Mon Sep 17 00:00:00 2001 From: Hunain Ahmed Date: Tue, 7 Jan 2025 20:52:19 +0500 Subject: [PATCH 03/11] add documentation --- .../components/TableOfContents/lambda.tsx | 8 ++ .../docs/docs/lambda/approuterwebhooks.mdx | 71 +++++++++++ .../docs/docs/lambda/pagerouterwebhooks.mdx | 68 ++++++++++ packages/docs/docs/lambda/webhooks.mdx | 119 +++--------------- packages/docs/sidebars.js | 2 + 5 files changed, 168 insertions(+), 100 deletions(-) create mode 100644 packages/docs/docs/lambda/approuterwebhooks.mdx create mode 100644 packages/docs/docs/lambda/pagerouterwebhooks.mdx diff --git a/packages/docs/components/TableOfContents/lambda.tsx b/packages/docs/components/TableOfContents/lambda.tsx index e62c4d65ede..88e9d793248 100644 --- a/packages/docs/components/TableOfContents/lambda.tsx +++ b/packages/docs/components/TableOfContents/lambda.tsx @@ -94,6 +94,14 @@ export const TableOfContents: React.FC = () => { validateWebhookSignature()
Validate an incoming webhook request is authentic
+ + appRouterWebhooks() +
Handle incoming webhooks specifically for the Next.js app router
+
+ + pageRouterWebhooks() +
Handle incoming webhooks specifically for the Next.js pages router
+
); diff --git a/packages/docs/docs/lambda/approuterwebhooks.mdx b/packages/docs/docs/lambda/approuterwebhooks.mdx new file mode 100644 index 00000000000..162caa5a37f --- /dev/null +++ b/packages/docs/docs/lambda/approuterwebhooks.mdx @@ -0,0 +1,71 @@ +--- +image: /generated/articles-docs-lambda-approuterwebhooks.png +id: approuterwebhooks +title: appRouterWebhooks() +slug: /lambda/approuterwebhooks +crumb: "Lambda API" +--- + +Simplifies the process of setting up Remotion Lambda Client in your Next.js app which is using App Router. Refer to [pagesRouterWebhooks()](/docs/lambda/pagerouterwebhooks) for doing the same in apps using Pages Router. +## API + +The function accepts an object with six key-value pairs: + +### `secret` + +Your webhook secret, must be a `string` + +### `testing` + +Whether or not to allow requests intending to test the endpoint, useful while using Webhook endpoint tester on [Webhooks Page](/docs/lambda/webhooks). Should be a `boolean`. + +### `extraHeaders` + +Add your own custom headers to the outgoing response. Provide key-value pairs where both the key and value are strings. + + +### `onSuccess()` + +A function that is called with a `Payload` object as an argument when the incoming request indicates a successful event. + +### `onError()` + +A function that is called with a `Payload` object as an argument when the incoming request indicates an error. + +### `onTimeout()` + +A function that is called with a `Payload` object as an argument when the incoming request indicates a timeout. + + +## Example + +Setting up a webhook endpoint in a Next.js app which uses App Router. This will listen on the endpoint: `mydomain.com/api` + + +```tsx twoslash title="app/api/route.ts" +import { + appRouterWebhooks +} from '@remotion/lambda/client'; + +export const POST = appRouterWebhooks( + { + secret: 'mysecret', + testing: true, + extraHeaders: { + "region" : 'south-asia' + }, + onSuccess: () => (console.log("Rendering Completed Successfully")), + onError: () => (console.log("Something went wrong while rendering")), + onTimeout: () => (console.log("Timeout occured while rendering")) + } +) + +export const OPTIONS = POST; +``` + +See [Webhooks](/docs/lambda/webhooks) for an Express example. + +## See also + +- [Webhooks](/docs/lambda/webhooks) +- [Source code for this function](https://github.com/remotion-dev/remotion/blob/main/packages/lambda/src/simplifiers/index.ts) diff --git a/packages/docs/docs/lambda/pagerouterwebhooks.mdx b/packages/docs/docs/lambda/pagerouterwebhooks.mdx new file mode 100644 index 00000000000..5f006f3427b --- /dev/null +++ b/packages/docs/docs/lambda/pagerouterwebhooks.mdx @@ -0,0 +1,68 @@ +--- +image: /generated/articles-docs-lambda-pagerouterwebhooks.png +id: pagerouterwebhooks +title: pageRouterWebhooks() +slug: /lambda/pagerouterwebhooks +crumb: "Lambda API" +--- + +Simplifies the process of setting up Remotion Lambda Client in your Next.js app which is using Pages Router. Similar to [appRouterWebhooks()](/docs/lambda/approuterwebhooks). +## API + +The function accepts an object with six key-value pairs: + +### `secret` + +Your webhook secret, must be a `string` + +### `testing` + +Whether or not to allow requests intending to test the endpoint, useful while using Webhook endpoint tester on [Webhooks Page](/docs/lambda/webhooks). Should be a `boolean`. + +### `extraHeaders` + +Add your own custom headers to the outgoing response. Provide key-value pairs where both the key and value are strings. + + +### `onSuccess()` + +A function that is called with a `Payload` object as an argument when the incoming request indicates a successful event. + +### `onError()` + +A function that is called with a `Payload` object as an argument when the incoming request indicates an error. + +### `onTimeout()` + +A function that is called with a `Payload` object as an argument when the incoming request indicates a timeout. + +## Example + +Setting up a webhook endpoint in a Next.js app which uses Pages Router. This will listen on the endpoint: `mydomain.com/api/webhook` + +```tsx twoslash title="pages/api/webhook.ts" +import { + pageRouterWebhooks +} from '@remotion/lambda/client'; + +const handler = pageRouterWebhooks( + { + secret: 'mysecret', + testing: true, + extraHeaders: { + "region" : 'south-asia' + }, + onSuccess: () => (console.log("Rendering Completed Successfully")), + onError: () => (console.log("Something went wrong while rendering")), + onTimeout: () => (console.log("Timeout occured while rendering")) + } +) +export default handler +``` + +See [Webhooks](/docs/lambda/webhooks) for an Express example. + +## See also + +- [Webhooks](/docs/lambda/webhooks) +- [Source code for this function](https://github.com/remotion-dev/remotion/blob/main/packages/lambda/src/simplifiers/index.ts) diff --git a/packages/docs/docs/lambda/webhooks.mdx b/packages/docs/docs/lambda/webhooks.mdx index e000deaf5b8..8c77725b11e 100644 --- a/packages/docs/docs/lambda/webhooks.mdx +++ b/packages/docs/docs/lambda/webhooks.mdx @@ -205,122 +205,41 @@ router.post("/my-remotion-webhook-endpoint", jsonParser, (req, res) => { ## Example webhook endpoint (Next.JS App Router) -Similary, here is an example endpoint in Next.JS for the App Router. - -Since this endpoint is going to be executed in an AWS Lambda function on its own, you want to import the Remotion functions from [`@remotion/lambda/client`](/docs/lambda/light-client). +Similary, here is an example endpoint in Next.JS for the App Router using [appRouterWebhooks()](/docs/lambda/approuterwebhooks) ```tsx twoslash title="app/api/webhook.ts" import { - validateWebhookSignature, - WebhookPayload, + appRouterWebhooks } from '@remotion/lambda/client'; - -// Enable testing through the tool below -// You may disable it in production -const ENABLE_TESTING = true; - -export const POST = async (req: Request, res: Response) => { - let headers = {}; - - if (ENABLE_TESTING) { - const testingheaders = { - 'Access-Control-Allow-Origin': 'https://www.remotion.dev', - 'Access-Control-Allow-Headers': - 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-Remotion-Status, X-Remotion-Signature, X-Remotion-Mode', - 'Access-Control-Allow-Methods': 'OPTIONS,POST', - }; - headers = {...headers, ...testingheaders}; - } - - if (req.method === 'OPTIONS') { - return new Response(null, { - headers, - }); + +export const POST = appRouterWebhooks( + { + secret: 'mysecret', + testing: true, + onTimeout: ()=>(console.log("timeout")) } - - // Parse the body properly - const body = await req.json(); - - validateWebhookSignature({ - secret: process.env.WEBHOOK_SECRET as string, - body: body, - signatureHeader: req.headers.get('X-Remotion-Signature') as string, - }); - - const payload = body as WebhookPayload; - - if (payload.type === 'success') { - //... - } else if (payload.type === 'timeout') { - //... - } - - return new Response(JSON.stringify({success: true})); -}; - +) + export const OPTIONS = POST; ``` ## Example webhook endpoint (Next.JS Pages Router) -The same endpoint as above, but using the Pages Router. +The same endpoint as above, but using the Pages Router with the help of [pageRouterWebhooks()](/docs/lambda/pagerouterwebhooks) ```tsx twoslash title="pages/api/webhook.ts" -type NextApiRequest = { - body: object; - headers: Record; - method: string; -}; -type NextApiResponse = { - status: (code: number) => {json: (body: object) => void; end: () => void}; - setHeader: (key: string, value: string) => void; - end: () => void; -}; -// ---cut--- import { - validateWebhookSignature, - WebhookPayload, + pageRouterWebhooks } from '@remotion/lambda/client'; -// Enable testing through the tool below -// You may disable it in production -const ENABLE_TESTING = true; - -export default async function handler( - req: NextApiRequest, - res: NextApiResponse, -) { - if (ENABLE_TESTING) { - res.setHeader('Access-Control-Allow-Origin', 'https://www.remotion.dev'); - res.setHeader('Access-Control-Allow-Methods', 'OPTIONS,POST'); - res.setHeader( - 'Access-Control-Allow-Headers', - 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-Remotion-Status, X-Remotion-Signature, X-Remotion-Mode', - ); +const handler = pageRouterWebhooks( + { + secret: 'mysecret', + testing: true, + onError: () => (console.log('Something went wrong')) } - if (req.method === 'OPTIONS') { - res.status(200).end(); - return; - } - - validateWebhookSignature({ - secret: process.env.WEBHOOK_SECRET as string, - body: req.body, - signatureHeader: req.headers['x-remotion-signature'] as string, - }); - - // If code reaches this path, the webhook is authentic. - const payload = req.body as WebhookPayload; - if (payload.type === 'success') { - // ... - } else if (payload.type === 'timeout') { - // ... - } - - res.status(200).json({ - success: true, - }); -} +) +export default handler ``` ## Test your webhook endpoint diff --git a/packages/docs/sidebars.js b/packages/docs/sidebars.js index 2571bbd4422..73b77483e53 100644 --- a/packages/docs/sidebars.js +++ b/packages/docs/sidebars.js @@ -250,6 +250,8 @@ module.exports = { 'lambda/simulatepermissions', 'lambda/speculatefunctionname', 'lambda/validatewebhooksignature', + 'lambda/approuterwebhooks', + 'lambda/pagerouterwebhooks' ], }, { From 3e59c9d428a2fa18f791c5bb65c6ce80da53ca32 Mon Sep 17 00:00:00 2001 From: Hunain Ahmed Date: Wed, 8 Jan 2025 17:03:54 +0500 Subject: [PATCH 04/11] updated lockfile --- pnpm-lock.yaml | 177 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 177 insertions(+) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 59ede27667e..26b1dfdcacb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1360,6 +1360,9 @@ importers: mime-types: specifier: 2.1.34 version: 2.1.34 + next: + specifier: ^15.1.3 + version: 15.1.3(react-dom@19.0.0)(react@19.0.0) remotion: specifier: workspace:* version: link:../core @@ -2962,6 +2965,59 @@ importers: specifier: workspace:* version: link:../core + packages/tester: + dependencies: + next: + specifier: 15.1.3 + version: 15.1.3(react-dom@19.0.0)(react@19.0.0) + react: + specifier: ^19.0.0 + version: 19.0.0 + react-dom: + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) + devDependencies: + '@types/node': + specifier: ^20 + version: 20.12.14 + '@types/react': + specifier: ^19 + version: 19.0.0 + '@types/react-dom': + specifier: ^19 + version: 19.0.0 + typescript: + specifier: ^5 + version: 5.5.4 + + packages/tester2: + dependencies: + '@remotion/lambda': + specifier: workspace:* + version: link:../lambda + next: + specifier: 15.1.3 + version: 15.1.3(react-dom@19.0.0)(react@19.0.0) + react: + specifier: ^19.0.0 + version: 19.0.0 + react-dom: + specifier: ^19.0.0 + version: 19.0.0(react@19.0.0) + devDependencies: + '@types/node': + specifier: ^20 + version: 20.12.14 + '@types/react': + specifier: ^19 + version: 19.0.0 + '@types/react-dom': + specifier: ^19 + version: 19.0.0 + typescript: + specifier: ^5 + version: 5.5.4 + packages/three: dependencies: remotion: @@ -11045,6 +11101,10 @@ packages: resolution: {integrity: sha512-Hm3jIGsoUl6RLB1vzY+dZeqb+/kWPZ+h34yiWxW0dV87l8Im/eMOwpOA+a0L78U0HM04syEjXuRlCozqpwuojQ==} dev: false + /@next/env@15.1.3: + resolution: {integrity: sha512-Q1tXwQCGWyA3ehMph3VO+E6xFPHDKdHFYosadt0F78EObYxPio0S09H9UGYznDe6Wc8eLKLG89GqcFJJDiK5xw==} + dev: false + /@next/eslint-plugin-next@15.0.4: resolution: {integrity: sha512-rbsF17XGzHtR7SDWzWpavSfum3/UdnF8bAaisnKwP//si3KWPTedVUsflAdjyK1zW3rweBjbALfKcavFneLGvg==} dependencies: @@ -11069,6 +11129,15 @@ packages: dev: false optional: true + /@next/swc-darwin-arm64@15.1.3: + resolution: {integrity: sha512-aZtmIh8jU89DZahXQt1La0f2EMPt/i7W+rG1sLtYJERsP7GRnNFghsciFpQcKHcGh4dUiyTB5C1X3Dde/Gw8gg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + /@next/swc-darwin-x64@14.2.15: resolution: {integrity: sha512-5TGyjFcf8ampZP3e+FyCax5zFVHi+Oe7sZyaKOngsqyaNEpOgkKB3sqmymkZfowy3ufGA/tUgDPPxpQx931lHg==} engines: {node: '>= 10'} @@ -11087,6 +11156,15 @@ packages: dev: false optional: true + /@next/swc-darwin-x64@15.1.3: + resolution: {integrity: sha512-aw8901rjkVBK5mbq5oV32IqkJg+CQa6aULNlN8zyCWSsePzEG3kpDkAFkkTOh3eJ0p95KbkLyWBzslQKamXsLA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: false + optional: true + /@next/swc-linux-arm64-gnu@14.2.15: resolution: {integrity: sha512-3Bwv4oc08ONiQ3FiOLKT72Q+ndEMyLNsc/D3qnLMbtUYTQAmkx9E/JRu0DBpHxNddBmNT5hxz1mYBphJ3mfrrw==} engines: {node: '>= 10'} @@ -11105,6 +11183,15 @@ packages: dev: false optional: true + /@next/swc-linux-arm64-gnu@15.1.3: + resolution: {integrity: sha512-YbdaYjyHa4fPK4GR4k2XgXV0p8vbU1SZh7vv6El4bl9N+ZSiMfbmqCuCuNU1Z4ebJMumafaz6UCC2zaJCsdzjw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@next/swc-linux-arm64-musl@14.2.15: resolution: {integrity: sha512-k5xf/tg1FBv/M4CMd8S+JL3uV9BnnRmoe7F+GWC3DxkTCD9aewFRH1s5rJ1zkzDa+Do4zyN8qD0N8c84Hu96FQ==} engines: {node: '>= 10'} @@ -11123,6 +11210,15 @@ packages: dev: false optional: true + /@next/swc-linux-arm64-musl@15.1.3: + resolution: {integrity: sha512-qgH/aRj2xcr4BouwKG3XdqNu33SDadqbkqB6KaZZkozar857upxKakbRllpqZgWl/NDeSCBYPmUAZPBHZpbA0w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@next/swc-linux-x64-gnu@14.2.15: resolution: {integrity: sha512-kE6q38hbrRbKEkkVn62reLXhThLRh6/TvgSP56GkFNhU22TbIrQDEMrO7j0IcQHcew2wfykq8lZyHFabz0oBrA==} engines: {node: '>= 10'} @@ -11141,6 +11237,15 @@ packages: dev: false optional: true + /@next/swc-linux-x64-gnu@15.1.3: + resolution: {integrity: sha512-uzafnTFwZCPN499fNVnS2xFME8WLC9y7PLRs/yqz5lz1X/ySoxfaK2Hbz74zYUdEg+iDZPd8KlsWaw9HKkLEVw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@next/swc-linux-x64-musl@14.2.15: resolution: {integrity: sha512-PZ5YE9ouy/IdO7QVJeIcyLn/Rc4ml9M2G4y3kCM9MNf1YKvFY4heg3pVa/jQbMro+tP6yc4G2o9LjAz1zxD7tQ==} engines: {node: '>= 10'} @@ -11159,6 +11264,15 @@ packages: dev: false optional: true + /@next/swc-linux-x64-musl@15.1.3: + resolution: {integrity: sha512-el6GUFi4SiDYnMTTlJJFMU+GHvw0UIFnffP1qhurrN1qJV3BqaSRUjkDUgVV44T6zpw1Lc6u+yn0puDKHs+Sbw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: false + optional: true + /@next/swc-win32-arm64-msvc@14.2.15: resolution: {integrity: sha512-2raR16703kBvYEQD9HNLyb0/394yfqzmIeyp2nDzcPV4yPjqNUG3ohX6jX00WryXz6s1FXpVhsCo3i+g4RUX+g==} engines: {node: '>= 10'} @@ -11177,6 +11291,15 @@ packages: dev: false optional: true + /@next/swc-win32-arm64-msvc@15.1.3: + resolution: {integrity: sha512-6RxKjvnvVMM89giYGI1qye9ODsBQpHSHVo8vqA8xGhmRPZHDQUE4jcDbhBwK0GnFMqBnu+XMg3nYukNkmLOLWw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@next/swc-win32-ia32-msvc@14.2.15: resolution: {integrity: sha512-fyTE8cklgkyR1p03kJa5zXEaZ9El+kDNM5A+66+8evQS5e/6v0Gk28LqA0Jet8gKSOyP+OTm/tJHzMlGdQerdQ==} engines: {node: '>= 10'} @@ -11204,6 +11327,15 @@ packages: dev: false optional: true + /@next/swc-win32-x64-msvc@15.1.3: + resolution: {integrity: sha512-VId/f5blObG7IodwC5Grf+aYP0O8Saz1/aeU3YcWqNdIUAmFQY3VEPKPaIzfv32F/clvanOb2K2BR5DtDs6XyQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: false + optional: true + /@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1: resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} dependencies: @@ -24321,6 +24453,51 @@ packages: - babel-plugin-macros dev: false + /next@15.1.3(react-dom@19.0.0)(react@19.0.0): + resolution: {integrity: sha512-5igmb8N8AEhWDYzogcJvtcRDU6n4cMGtBklxKD4biYv4LXN8+awc/bbQ2IM2NQHdVPgJ6XumYXfo3hBtErg1DA==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.41.2 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + dependencies: + '@next/env': 15.1.3 + '@swc/counter': 0.1.3 + '@swc/helpers': 0.5.15 + busboy: 1.6.0 + caniuse-lite: 1.0.30001690 + postcss: 8.4.31 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + styled-jsx: 5.1.6(@babel/core@7.23.2)(react@19.0.0) + optionalDependencies: + '@next/swc-darwin-arm64': 15.1.3 + '@next/swc-darwin-x64': 15.1.3 + '@next/swc-linux-arm64-gnu': 15.1.3 + '@next/swc-linux-arm64-musl': 15.1.3 + '@next/swc-linux-x64-gnu': 15.1.3 + '@next/swc-linux-x64-musl': 15.1.3 + '@next/swc-win32-arm64-msvc': 15.1.3 + '@next/swc-win32-x64-msvc': 15.1.3 + sharp: 0.33.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + dev: false + /nlcst-to-string@4.0.0: resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} dependencies: From 1422d41a95a9fc95a2dcf2b1ff6b64e43f042128 Mon Sep 17 00:00:00 2001 From: Hunain Ahmed Date: Wed, 8 Jan 2025 17:14:37 +0500 Subject: [PATCH 05/11] fix format --- packages/lambda/src/client.ts | 6 +- packages/lambda/src/simplifiers/index.ts | 224 ++++++++++++----------- 2 files changed, 120 insertions(+), 110 deletions(-) diff --git a/packages/lambda/src/client.ts b/packages/lambda/src/client.ts index 6523cf79f42..93c7874132b 100644 --- a/packages/lambda/src/client.ts +++ b/packages/lambda/src/client.ts @@ -7,7 +7,6 @@ import {getRenderProgress} from './api/get-render-progress'; import {getSites} from './api/get-sites'; import type {PresignUrlInput} from './api/presign-url'; import {presignUrl} from './api/presign-url'; -import { appRouterWebhooks, pageRouterWebhooks } from './simplifiers'; import type { RenderMediaOnLambdaInput, RenderMediaOnLambdaOutput, @@ -25,6 +24,7 @@ import type {SpeculateFunctionNameInput} from './api/speculate-function-name'; import {speculateFunctionName} from './api/speculate-function-name'; import {validateWebhookSignature} from './api/validate-webhook-signature'; import type {RenderProgress} from './shared/constants'; +import {appRouterWebhooks, pageRouterWebhooks} from './simplifiers'; export type {WebhookPayload} from '@remotion/serverless'; export {CustomCredentials, DeleteAfter} from '@remotion/serverless/client'; @@ -35,19 +35,19 @@ export { } from './api/get-aws-client'; export type {AwsRegion} from './regions'; export { + appRouterWebhooks, deleteRender, getCompositionsOnLambda, getFunctions, getRenderProgress, getSites, + pageRouterWebhooks, presignUrl, renderMediaOnLambda, renderStillOnLambda, renderVideoOnLambda, speculateFunctionName, validateWebhookSignature, - appRouterWebhooks, - pageRouterWebhooks, }; export type { DeleteRenderInput, diff --git a/packages/lambda/src/simplifiers/index.ts b/packages/lambda/src/simplifiers/index.ts index ba14d0f0cc2..bdd8e5974a3 100644 --- a/packages/lambda/src/simplifiers/index.ts +++ b/packages/lambda/src/simplifiers/index.ts @@ -1,108 +1,118 @@ -import { validateWebhookSignature } from "../client"; -import type { WebhookPayload } from "../client"; -import type {NextApiRequest, NextApiResponse} from 'next' +import type {NextApiRequest, NextApiResponse} from 'next'; +import type {WebhookPayload} from '../client'; +import {validateWebhookSignature} from '../client'; type AppRouterWebhookArgs = { - testing?: boolean; - extraHeaders?: Record; - secret: string; - onSuccess?: (payload: WebhookPayload) => void; - onTimeout?: (payload: WebhookPayload) => void; - onError?: (payload: WebhookPayload) => void; -} - -const appRouterWebhooks = (options: AppRouterWebhookArgs): (req: Request) => Promise => { - const {testing, extraHeaders, secret, onSuccess, onTimeout, onError} = options; - return async function(req: Request): Promise { - let headers = extraHeaders || {}; - - if (testing) { - const testingheaders = { - 'Access-Control-Allow-Origin': 'https://www.remotion.dev', - 'Access-Control-Allow-Headers': - 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-Remotion-Status, X-Remotion-Signature, X-Remotion-Mode', - 'Access-Control-Allow-Methods': 'OPTIONS,POST', - }; - headers = {...headers, ...testingheaders}; - } - - if (req.method === 'OPTIONS') { - // do we have any use of the OPTIONS method other than the tester on webhooks page ? if so we can add a condition here to only return this if testing mode enabled - return new Response(null, { - headers, - }); - } - - // Parse the body properly - const body = await req.json(); - - validateWebhookSignature({ - secret, - body, - signatureHeader: req.headers.get('X-Remotion-Signature') as string, - }); - - const payload = body as WebhookPayload; - - if (payload.type === 'success' && onSuccess) { - onSuccess(payload); - } else if (payload.type === 'timeout' && onTimeout) { - onTimeout(payload); - } else if (payload.type === 'error' && onError) { - onError(payload); - } - - return new Response(JSON.stringify({success: true}), {headers}); - } -} - -const headersMutator = (res: NextApiResponse, headers: Record) => { - Object.entries(headers).forEach(([key, value]) => { - res.setHeader(key, value); - }) - }; - - const pageRouterWebhooks = (options: AppRouterWebhookArgs) => { - const {testing, extraHeaders, secret, onSuccess, onTimeout, onError} = options; - return async function(req: NextApiRequest, res: NextApiResponse): Promise { - // set the headers - headersMutator(res, extraHeaders || {}) - - if (testing) { - const testingheaders = { - 'Access-Control-Allow-Origin': 'https://www.remotion.dev', - 'Access-Control-Allow-Headers': - 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-Remotion-Status, X-Remotion-Signature, X-Remotion-Mode', - 'Access-Control-Allow-Methods': 'OPTIONS,POST', - }; - // add the headers for testing - headersMutator(res, testingheaders) - } - - if (req.method === 'OPTIONS') { - res.status(200).end(); - return; - } - - validateWebhookSignature({ - secret, - body: req.body, - signatureHeader: req.headers['x-remotion-signature'] as string, - }); - - // If code reaches this path, the webhook is authentic. - const payload = req.body as WebhookPayload; - if (payload.type === 'success' && onSuccess) { - onSuccess(payload) - } else if (payload.type === 'timeout' && onTimeout) { - onTimeout(payload) - - }else if (payload.type === 'error' && onError) { - onError(payload) - } - - res.status(200).json({ - success: true, - }); - }} - -export {appRouterWebhooks, pageRouterWebhooks} \ No newline at end of file + testing?: boolean; + extraHeaders?: Record; + secret: string; + onSuccess?: (payload: WebhookPayload) => void; + onTimeout?: (payload: WebhookPayload) => void; + onError?: (payload: WebhookPayload) => void; +}; + +const appRouterWebhooks = ( + options: AppRouterWebhookArgs, +): ((req: Request) => Promise) => { + const {testing, extraHeaders, secret, onSuccess, onTimeout, onError} = + options; + return async function (req: Request): Promise { + let headers = extraHeaders || {}; + + if (testing) { + const testingheaders = { + 'Access-Control-Allow-Origin': 'https://www.remotion.dev', + 'Access-Control-Allow-Headers': + 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-Remotion-Status, X-Remotion-Signature, X-Remotion-Mode', + 'Access-Control-Allow-Methods': 'OPTIONS,POST', + }; + headers = {...headers, ...testingheaders}; + } + + if (req.method === 'OPTIONS') { + // do we have any use of the OPTIONS method other than the tester on webhooks page ? if so we can add a condition here to only return this if testing mode enabled + return new Response(null, { + headers, + }); + } + + // Parse the body properly + const body = await req.json(); + + validateWebhookSignature({ + secret, + body, + signatureHeader: req.headers.get('X-Remotion-Signature') as string, + }); + + const payload = body as WebhookPayload; + + if (payload.type === 'success' && onSuccess) { + onSuccess(payload); + } else if (payload.type === 'timeout' && onTimeout) { + onTimeout(payload); + } else if (payload.type === 'error' && onError) { + onError(payload); + } + + return new Response(JSON.stringify({success: true}), {headers}); + }; +}; + +const headersMutator = ( + res: NextApiResponse, + headers: Record, +) => { + Object.entries(headers).forEach(([key, value]) => { + res.setHeader(key, value); + }); +}; + +const pageRouterWebhooks = (options: AppRouterWebhookArgs) => { + const {testing, extraHeaders, secret, onSuccess, onTimeout, onError} = + options; + return async function ( + req: NextApiRequest, + res: NextApiResponse, + ): Promise { + // set the headers + headersMutator(res, extraHeaders || {}); + + if (testing) { + const testingheaders = { + 'Access-Control-Allow-Origin': 'https://www.remotion.dev', + 'Access-Control-Allow-Headers': + 'X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, X-Remotion-Status, X-Remotion-Signature, X-Remotion-Mode', + 'Access-Control-Allow-Methods': 'OPTIONS,POST', + }; + // add the headers for testing + headersMutator(res, testingheaders); + } + + if (req.method === 'OPTIONS') { + res.status(200).end(); + return; + } + + validateWebhookSignature({ + secret, + body: req.body, + signatureHeader: req.headers['x-remotion-signature'] as string, + }); + + // If code reaches this path, the webhook is authentic. + const payload = req.body as WebhookPayload; + if (payload.type === 'success' && onSuccess) { + onSuccess(payload); + } else if (payload.type === 'timeout' && onTimeout) { + onTimeout(payload); + } else if (payload.type === 'error' && onError) { + onError(payload); + } + + res.status(200).json({ + success: true, + }); + }; +}; + +export {appRouterWebhooks, pageRouterWebhooks}; From 141be51a62ca2c6beec6e8de08e4a02c9eba026c Mon Sep 17 00:00:00 2001 From: Jonny Burger Date: Thu, 9 Jan 2025 11:52:09 +0100 Subject: [PATCH 06/11] restructure --- .../docs/docs/lambda/approuterwebhooks.mdx | 36 +- .../docs/docs/lambda/pagerouterwebhooks.mdx | 34 +- packages/docs/src/data/articles.ts | 7132 +++++++++-------- ...articles-docs-lambda-approuterwebhooks.png | Bin 0 -> 47759 bytes ...rticles-docs-lambda-pagerouterwebhooks.png | Bin 0 -> 48538 bytes packages/lambda/package.json | 2 +- .../{simplifiers => webhook-presets}/index.ts | 0 pnpm-lock.yaml | 337 +- 8 files changed, 3668 insertions(+), 3873 deletions(-) create mode 100644 packages/docs/static/generated/articles-docs-lambda-approuterwebhooks.png create mode 100644 packages/docs/static/generated/articles-docs-lambda-pagerouterwebhooks.png rename packages/lambda/src/{simplifiers => webhook-presets}/index.ts (100%) diff --git a/packages/docs/docs/lambda/approuterwebhooks.mdx b/packages/docs/docs/lambda/approuterwebhooks.mdx index 162caa5a37f..6e72fe271de 100644 --- a/packages/docs/docs/lambda/approuterwebhooks.mdx +++ b/packages/docs/docs/lambda/approuterwebhooks.mdx @@ -3,10 +3,11 @@ image: /generated/articles-docs-lambda-approuterwebhooks.png id: approuterwebhooks title: appRouterWebhooks() slug: /lambda/approuterwebhooks -crumb: "Lambda API" +crumb: 'Lambda API' --- Simplifies the process of setting up Remotion Lambda Client in your Next.js app which is using App Router. Refer to [pagesRouterWebhooks()](/docs/lambda/pagerouterwebhooks) for doing the same in apps using Pages Router. + ## API The function accepts an object with six key-value pairs: @@ -23,7 +24,6 @@ Whether or not to allow requests intending to test the endpoint, useful while us Add your own custom headers to the outgoing response. Provide key-value pairs where both the key and value are strings. - ### `onSuccess()` A function that is called with a `Payload` object as an argument when the incoming request indicates a successful event. @@ -36,30 +36,24 @@ A function that is called with a `Payload` object as an argument when the incomi A function that is called with a `Payload` object as an argument when the incoming request indicates a timeout. - ## Example Setting up a webhook endpoint in a Next.js app which uses App Router. This will listen on the endpoint: `mydomain.com/api` - ```tsx twoslash title="app/api/route.ts" -import { - appRouterWebhooks -} from '@remotion/lambda/client'; - -export const POST = appRouterWebhooks( - { - secret: 'mysecret', - testing: true, - extraHeaders: { - "region" : 'south-asia' - }, - onSuccess: () => (console.log("Rendering Completed Successfully")), - onError: () => (console.log("Something went wrong while rendering")), - onTimeout: () => (console.log("Timeout occured while rendering")) - } -) - +import {appRouterWebhooks} from '@remotion/lambda/client'; + +export const POST = appRouterWebhooks({ + secret: 'mysecret', + testing: true, + extraHeaders: { + region: 'south-asia', + }, + onSuccess: () => console.log('Rendering Completed Successfully'), + onError: () => console.log('Something went wrong while rendering'), + onTimeout: () => console.log('Timeout occured while rendering'), +}); + export const OPTIONS = POST; ``` diff --git a/packages/docs/docs/lambda/pagerouterwebhooks.mdx b/packages/docs/docs/lambda/pagerouterwebhooks.mdx index 5f006f3427b..292e76528d4 100644 --- a/packages/docs/docs/lambda/pagerouterwebhooks.mdx +++ b/packages/docs/docs/lambda/pagerouterwebhooks.mdx @@ -3,10 +3,11 @@ image: /generated/articles-docs-lambda-pagerouterwebhooks.png id: pagerouterwebhooks title: pageRouterWebhooks() slug: /lambda/pagerouterwebhooks -crumb: "Lambda API" +crumb: 'Lambda API' --- Simplifies the process of setting up Remotion Lambda Client in your Next.js app which is using Pages Router. Similar to [appRouterWebhooks()](/docs/lambda/approuterwebhooks). + ## API The function accepts an object with six key-value pairs: @@ -23,7 +24,6 @@ Whether or not to allow requests intending to test the endpoint, useful while us Add your own custom headers to the outgoing response. Provide key-value pairs where both the key and value are strings. - ### `onSuccess()` A function that is called with a `Payload` object as an argument when the incoming request indicates a successful event. @@ -41,23 +41,19 @@ A function that is called with a `Payload` object as an argument when the incomi Setting up a webhook endpoint in a Next.js app which uses Pages Router. This will listen on the endpoint: `mydomain.com/api/webhook` ```tsx twoslash title="pages/api/webhook.ts" -import { - pageRouterWebhooks -} from '@remotion/lambda/client'; - -const handler = pageRouterWebhooks( - { - secret: 'mysecret', - testing: true, - extraHeaders: { - "region" : 'south-asia' - }, - onSuccess: () => (console.log("Rendering Completed Successfully")), - onError: () => (console.log("Something went wrong while rendering")), - onTimeout: () => (console.log("Timeout occured while rendering")) - } -) -export default handler +import {pageRouterWebhooks} from '@remotion/lambda/client'; + +const handler = pageRouterWebhooks({ + secret: 'mysecret', + testing: true, + extraHeaders: { + region: 'south-asia', + }, + onSuccess: () => console.log('Rendering Completed Successfully'), + onError: () => console.log('Something went wrong while rendering'), + onTimeout: () => console.log('Timeout occured while rendering'), +}); +export default handler; ``` See [Webhooks](/docs/lambda/webhooks) for an Express example. diff --git a/packages/docs/src/data/articles.ts b/packages/docs/src/data/articles.ts index f775435d477..2de7ecf05a9 100644 --- a/packages/docs/src/data/articles.ts +++ b/packages/docs/src/data/articles.ts @@ -1,129 +1,102 @@ export const articles = [ { - id: '2-0-migration', - title: 'v2.0 Migration', - relativePath: 'docs/2-0-migration.mdx', - compId: 'articles-docs-2-0-migration', - crumb: 'Version Upgrade', + id: 'figma', + title: 'Import from Figma', + relativePath: 'docs/figma.mdx', + compId: 'articles-docs-figma', + crumb: 'The best of both', noAi: false, - slug: '2-0-migration', + slug: 'figma', }, { - id: '3-0-migration', - title: 'v3.0 Migration', - relativePath: 'docs/3-0-migration.mdx', - compId: 'articles-docs-3-0-migration', - crumb: 'Version Upgrade', + id: 'env-variables', + title: 'Environment variables', + relativePath: 'docs/env-variables.mdx', + compId: 'articles-docs-env-variables', + crumb: 'How To', noAi: false, - slug: '3-0-migration', + slug: 'env-variables', }, { - id: '4-0-alpha', - title: 'v4.0 Alpha', - relativePath: 'docs/4-0-alpha.mdx', - compId: 'articles-docs-4-0-alpha', - crumb: 'Version Upgrade', + id: 'skia', + title: '@remotion/skia', + relativePath: 'docs/skia/skia.mdx', + compId: 'articles-docs-skia-skia', + crumb: null, noAi: false, - slug: '4-0-alpha', + slug: 'skia/skia', }, { - id: '4-0-migration', - title: 'v4.0 Migration', - relativePath: 'docs/4-0-migration.mdx', - compId: 'articles-docs-4-0-migration', - crumb: 'Version Upgrade', + id: 'skia/enable-skia', + title: 'enableSkia()', + relativePath: 'docs/skia/enable-skia.mdx', + compId: 'articles-docs-skia-enable-skia', + crumb: '@remotion/skia', noAi: false, - slug: '4-0-migration', + slug: 'skia/enable-skia', }, { - id: '5-0-migration', - title: 'v5.0 Migration', - relativePath: 'docs/5-0-migration.mdx', - compId: 'articles-docs-5-0-migration', - crumb: 'Version Upgrade', + id: 'skia/skia-canvas', + title: '', + relativePath: 'docs/skia/skia-canvas.mdx', + compId: 'articles-docs-skia-skia-canvas', + crumb: '@remotion/skia', noAi: false, - slug: '5-0-migration', + slug: 'skia/skia-canvas', }, { - id: 'absolute-fill', - title: '', - relativePath: 'docs/absolute-fill.mdx', - compId: 'articles-docs-absolute-fill', + id: 'spring', + title: 'spring()', + relativePath: 'docs/spring.mdx', + compId: 'articles-docs-spring', crumb: 'API', noAi: false, - slug: 'absolute-fill', - }, - { - id: 'acknowledgements', - title: 'Acknowledgements', - relativePath: 'docs/acknowledgements.mdx', - compId: 'articles-docs-acknowledgements', - crumb: 'Credits', - noAi: false, - slug: 'acknowledgements', - }, - { - id: 'after-effects', - title: 'Import from After Effects', - relativePath: 'docs/after-effects.mdx', - compId: 'articles-docs-after-effects', - crumb: 'Integrations', - noAi: false, - slug: 'after-effects', - }, - { - id: 'angular', - title: 'Using Remotion in Angular', - relativePath: 'docs/angular.mdx', - compId: 'articles-docs-angular', - crumb: 'Integrations', - noAi: false, - slug: 'angular', + slug: 'spring', }, { - id: 'animated-emoji', - title: '', - relativePath: 'docs/animated-emoji/animated-emoji.mdx', - compId: 'articles-docs-animated-emoji-animated-emoji', - crumb: '@remotion/animated-emoji', + id: 'audio-visualization', + title: 'Audio visualization', + relativePath: 'docs/audio-visualization.mdx', + compId: 'articles-docs-audio-visualization', + crumb: 'Techniques', noAi: false, - slug: 'animated-emoji/animated-emoji', + slug: 'audio-visualization', }, { - id: 'get-available-emoji', - title: 'getAvailableEmoji()', - relativePath: 'docs/animated-emoji/get-available-emoji.mdx', - compId: 'articles-docs-animated-emoji-get-available-emoji', - crumb: '@remotion/animated-emoji', + id: 'react-native', + title: 'React Native', + relativePath: 'docs/react-native.mdx', + compId: 'articles-docs-react-native', + crumb: null, noAi: false, - slug: 'animated-emoji/get-available-emoji', + slug: 'react-native', }, { - id: 'animated-emoji/index', - title: '@remotion/animated-emoji', - relativePath: 'docs/animated-emoji/index.mdx', - compId: 'articles-docs-animated-emoji-index', - crumb: null, + id: 'use-current-scale', + title: 'useCurrentScale()', + relativePath: 'docs/use-current-scale.mdx', + compId: 'articles-docs-use-current-scale', + crumb: 'API', noAi: false, - slug: 'animated-emoji/index', + slug: 'use-current-scale', }, { - id: 'animating-properties', - title: 'Animating properties', - relativePath: 'docs/animating-properties.mdx', - compId: 'articles-docs-animating-properties', - crumb: 'The basics', + id: 'config', + title: 'Configuration file', + relativePath: 'docs/config.mdx', + compId: 'articles-docs-config', + crumb: 'remotion.config.ts', noAi: false, - slug: 'animating-properties', + slug: 'config', }, { - id: 'animation-math', - title: 'Animation math', - relativePath: 'docs/animation-math.mdx', - compId: 'articles-docs-animation-math', - crumb: 'Techniques', + id: 'make-transform', + title: 'makeTransform()', + relativePath: 'docs/animation-utils/make-transform.mdx', + compId: 'articles-docs-animation-utils-make-transform', + crumb: '@remotion/animation-utils', noAi: false, - slug: 'animation-math', + slug: 'animation-utils/make-transform', }, { id: 'animation-utils/index', @@ -144,166 +117,211 @@ export const articles = [ slug: 'animation-utils/interpolate-styles', }, { - id: 'make-transform', - title: 'makeTransform()', - relativePath: 'docs/animation-utils/make-transform.mdx', - compId: 'articles-docs-animation-utils-make-transform', - crumb: '@remotion/animation-utils', + id: '4-0-alpha', + title: 'v4.0 Alpha', + relativePath: 'docs/4-0-alpha.mdx', + compId: 'articles-docs-4-0-alpha', + crumb: 'Version Upgrade', noAi: false, - slug: 'animation-utils/make-transform', + slug: '4-0-alpha', }, { - id: 'api', - title: 'API overview', - relativePath: 'docs/api.mdx', - compId: 'articles-docs-api', - crumb: null, + id: 'use-video-config', + title: 'useVideoConfig()', + relativePath: 'docs/use-video-config.mdx', + compId: 'articles-docs-use-video-config', + crumb: 'API', noAi: false, - slug: 'api', + slug: 'use-video-config', }, { - id: 'artifact', - title: '', - relativePath: 'docs/artifact.mdx', - compId: 'articles-docs-artifact', - crumb: 'API', + id: 'legacy-babel', + title: 'Using legacy Babel transpilation', + relativePath: 'docs/legacy-babel-loader.mdx', + compId: 'articles-docs-legacy-babel-loader', + crumb: 'How To', noAi: false, - slug: 'artifact', + slug: 'legacy-babel', }, { - id: 'artifacts', - title: 'Emitting Artifacts', - relativePath: 'docs/artifacts.mdx', - compId: 'articles-docs-artifacts', - crumb: 'Techniques', + id: 'brownfield', + title: 'Installing Remotion in an existing project', + relativePath: 'docs/brownfield-installation.mdx', + compId: 'articles-docs-brownfield-installation', + crumb: 'Brownfield integration', noAi: false, - slug: 'artifacts', + slug: 'brownfield', }, { - id: 'get-help', - title: 'Get help', - relativePath: 'docs/ask-for-help.mdx', - compId: 'articles-docs-ask-for-help', - crumb: null, + id: 'studio/studio', + title: 'Starting the Studio', + relativePath: 'docs/studio/studio.mdx', + compId: 'articles-docs-studio-studio', + crumb: 'Timeline basics', noAi: false, - slug: 'get-help', + slug: 'studio/studio', }, { - id: 'audio-visualization', - title: 'Audio visualization', - relativePath: 'docs/audio-visualization.mdx', - compId: 'articles-docs-audio-visualization', - crumb: 'Techniques', + id: 'studio/quick-switcher', + title: 'Quick Switcher', + relativePath: 'docs/studio/quick-switcher.mdx', + compId: 'articles-docs-studio-quick-switcher', + crumb: 'Remotion Studio', noAi: false, - slug: 'audio-visualization', + slug: 'studio/quick-switcher', }, { - id: 'audio', - title: '