Add support for the built-in Trigger.dev tunnel for local development
This commit is contained in:
@@ -14,6 +14,8 @@
|
||||
"ignore": [
|
||||
"webapp",
|
||||
"emails",
|
||||
"proxy",
|
||||
"yalt",
|
||||
"@trigger.dev/database"
|
||||
],
|
||||
"___experimentalUnsafeOptions_WILL_CHANGE_IN_PATCH": {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
---
|
||||
"@trigger.dev/yalt": patch
|
||||
"@trigger.dev/cli": patch
|
||||
---
|
||||
|
||||
Add support for the built-in Trigger.dev tunnel (Yalt)
|
||||
@@ -64,6 +64,9 @@ const EnvironmentSchema = z.object({
|
||||
|
||||
DEFAULT_ORG_EXECUTION_CONCURRENCY_LIMIT: z.coerce.number().int().default(10),
|
||||
DEFAULT_DEV_ENV_EXECUTION_ATTEMPTS: z.coerce.number().int().positive().default(1),
|
||||
|
||||
TUNNEL_HOST: z.string().optional(),
|
||||
TUNNEL_SECRET_KEY: z.string().optional(),
|
||||
});
|
||||
|
||||
export type Environment = z.infer<typeof EnvironmentSchema>;
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import type { LoaderFunctionArgs } from "@remix-run/server-runtime";
|
||||
import { json } from "@remix-run/server-runtime";
|
||||
import { env } from "~/env.server";
|
||||
import { authenticateApiRequest } from "~/services/apiAuth.server";
|
||||
import { YaltApiClient } from "@trigger.dev/yalt";
|
||||
import { logger } from "~/services/logger.server";
|
||||
import { prisma } from "~/db.server";
|
||||
|
||||
// This is for HEAD requests to check if the API supports tunneling
|
||||
export async function loader({ request }: LoaderFunctionArgs) {
|
||||
// Next authenticate the request
|
||||
const authenticationResult = await authenticateApiRequest(request);
|
||||
|
||||
if (!authenticationResult) {
|
||||
return json({ error: "Invalid or Missing API key" }, { status: 401 });
|
||||
}
|
||||
|
||||
if (!env.TUNNEL_HOST || !env.TUNNEL_SECRET_KEY) {
|
||||
return json({ error: "Tunneling is not supported" }, { status: 501 });
|
||||
}
|
||||
|
||||
return json({ ok: true });
|
||||
}
|
||||
|
||||
export async function action({ request }: LoaderFunctionArgs) {
|
||||
// Next authenticate the request
|
||||
const authenticationResult = await authenticateApiRequest(request);
|
||||
|
||||
if (!authenticationResult) {
|
||||
return json({ error: "Invalid or Missing API key" }, { status: 401 });
|
||||
}
|
||||
|
||||
if (authenticationResult.environment.type !== "DEVELOPMENT") {
|
||||
return json({ error: "Tunneling is only supported in development" }, { status: 501 });
|
||||
}
|
||||
|
||||
if (!env.TUNNEL_HOST || !env.TUNNEL_SECRET_KEY) {
|
||||
return json({ error: "Tunneling is not supported" }, { status: 501 });
|
||||
}
|
||||
|
||||
const yaltClient = new YaltApiClient(env.TUNNEL_HOST, env.TUNNEL_SECRET_KEY);
|
||||
|
||||
let tunnelId = authenticationResult.environment.tunnelId;
|
||||
|
||||
if (!tunnelId) {
|
||||
try {
|
||||
tunnelId = await yaltClient.createTunnel();
|
||||
|
||||
await prisma.runtimeEnvironment.update({
|
||||
where: {
|
||||
id: authenticationResult.environment.id,
|
||||
},
|
||||
data: {
|
||||
tunnelId,
|
||||
},
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error("Failed to create tunnel", { error });
|
||||
|
||||
return json({ error: "Failed to create tunnel" }, { status: 500 });
|
||||
}
|
||||
}
|
||||
|
||||
if (!tunnelId) {
|
||||
return json({ error: "Failed to create tunnel" }, { status: 500 });
|
||||
}
|
||||
|
||||
return json({ url: yaltClient.connectUrl(tunnelId) });
|
||||
}
|
||||
@@ -72,6 +72,7 @@
|
||||
"@trigger.dev/core-backend": "workspace:*",
|
||||
"@trigger.dev/database": "workspace:*",
|
||||
"@trigger.dev/sdk": "workspace:*",
|
||||
"@trigger.dev/yalt": "workspace:*",
|
||||
"@types/pg": "8.6.6",
|
||||
"@uiw/react-codemirror": "^4.19.5",
|
||||
"@whatwg-node/fetch": "^0.9.14",
|
||||
|
||||
@@ -15,6 +15,7 @@ module.exports = {
|
||||
"@trigger.dev/core-backend",
|
||||
"@trigger.dev/sdk",
|
||||
"@trigger.dev/billing",
|
||||
"@trigger.dev/yalt",
|
||||
"emails",
|
||||
"highlight.run",
|
||||
"random-words",
|
||||
@@ -24,6 +25,7 @@ module.exports = {
|
||||
"../../packages/core/src/**/*",
|
||||
"../../packages/core-backend/src/**/*",
|
||||
"../../packages/trigger-sdk/src/**/*",
|
||||
"../../packages/yalt/src/**/*",
|
||||
"../../packages/emails/src/**/*",
|
||||
];
|
||||
},
|
||||
|
||||
@@ -27,6 +27,8 @@
|
||||
"@trigger.dev/core-backend/*": ["../../packages/core-backend/src/*"],
|
||||
"@trigger.dev/database": ["../../packages/database/src/index"],
|
||||
"@trigger.dev/database/*": ["../../packages/database/src/*"],
|
||||
"@trigger.dev/yalt": ["../../packages/yalt/src/index"],
|
||||
"@trigger.dev/yalt/*": ["../../packages/yalt/src/*"],
|
||||
"emails": ["../../packages/emails/src/index"],
|
||||
"emails/*": ["../../packages/emails/src/*"]
|
||||
},
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
SECRET_KEY=
|
||||
@@ -0,0 +1,13 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
tab_width = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
||||
@@ -0,0 +1,172 @@
|
||||
# Logs
|
||||
|
||||
logs
|
||||
_.log
|
||||
npm-debug.log_
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
|
||||
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||
|
||||
# Runtime data
|
||||
|
||||
pids
|
||||
_.pid
|
||||
_.seed
|
||||
\*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
|
||||
coverage
|
||||
\*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
|
||||
.lock-wscript
|
||||
|
||||
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||
|
||||
build/Release
|
||||
|
||||
# Dependency directories
|
||||
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Snowpack dependency directory (https://snowpack.dev/)
|
||||
|
||||
web_modules/
|
||||
|
||||
# TypeScript cache
|
||||
|
||||
\*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
|
||||
.eslintcache
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
.stylelintcache
|
||||
|
||||
# Microbundle cache
|
||||
|
||||
.rpt2_cache/
|
||||
.rts2_cache_cjs/
|
||||
.rts2_cache_es/
|
||||
.rts2_cache_umd/
|
||||
|
||||
# Optional REPL history
|
||||
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
|
||||
\*.tgz
|
||||
|
||||
# Yarn Integrity file
|
||||
|
||||
.yarn-integrity
|
||||
|
||||
# dotenv environment variable files
|
||||
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# parcel-bundler cache (https://parceljs.org/)
|
||||
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
# Next.js build output
|
||||
|
||||
.next
|
||||
out
|
||||
|
||||
# Nuxt.js build / generate output
|
||||
|
||||
.nuxt
|
||||
dist
|
||||
|
||||
# Gatsby files
|
||||
|
||||
.cache/
|
||||
|
||||
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||
|
||||
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||
|
||||
# public
|
||||
|
||||
# vuepress build output
|
||||
|
||||
.vuepress/dist
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
|
||||
.temp
|
||||
.cache
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
|
||||
.docusaurus
|
||||
|
||||
# Serverless directories
|
||||
|
||||
.serverless/
|
||||
|
||||
# FuseBox cache
|
||||
|
||||
.fusebox/
|
||||
|
||||
# DynamoDB Local files
|
||||
|
||||
.dynamodb/
|
||||
|
||||
# TernJS port file
|
||||
|
||||
.tern-port
|
||||
|
||||
# Stores VSCode versions used for testing VSCode extensions
|
||||
|
||||
.vscode-test
|
||||
|
||||
# yarn v2
|
||||
|
||||
.yarn/cache
|
||||
.yarn/unplugged
|
||||
.yarn/build-state.yml
|
||||
.yarn/install-state.gz
|
||||
.pnp.\*
|
||||
|
||||
# wrangler project
|
||||
|
||||
.dev.vars
|
||||
.wrangler/
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"printWidth": 140,
|
||||
"singleQuote": true,
|
||||
"semi": true,
|
||||
"useTabs": true
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
## Trigger.dev Yalt Server
|
||||
|
||||
Yalt (Yet Another Local Tunnel) is the Trigger.dev tunneling service that powers the local development of Trigger.dev users.
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "yalt",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"deploy": "wrangler deploy",
|
||||
"dev": "wrangler dev --port 8787",
|
||||
"start": "wrangler dev"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cloudflare/workers-types": "~4.20231121.0",
|
||||
"wrangler": "^3.20.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/yalt": "workspace:*"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,258 @@
|
||||
import { ClientMessages, createRequestMessage } from '@trigger.dev/yalt';
|
||||
|
||||
export interface Env {
|
||||
// environemnt variables
|
||||
SECRET_KEY: string;
|
||||
WORKER_HOST: string;
|
||||
|
||||
// bindings
|
||||
connections: DurableObjectNamespace;
|
||||
tunnelIds: KVNamespace;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
const route = routeUrl(url, env);
|
||||
|
||||
switch (route.type) {
|
||||
case 'management': {
|
||||
return handleManagementRequest(request, env, ctx);
|
||||
}
|
||||
case 'tunnel': {
|
||||
console.log(`Handling tunnel request for ${route.name}`);
|
||||
|
||||
const id = await env.tunnelIds.get(route.name);
|
||||
|
||||
if (!id) {
|
||||
return new Response('Not Found', { status: 404 });
|
||||
}
|
||||
|
||||
const tunnel = env.connections.get(env.connections.idFromString(id));
|
||||
return tunnel.fetch(request);
|
||||
}
|
||||
case 'not_found': {
|
||||
return new Response('Not Found', { status: 404 });
|
||||
}
|
||||
}
|
||||
|
||||
return new Response('Not Found', { status: 404 });
|
||||
},
|
||||
};
|
||||
|
||||
type RouteDecision =
|
||||
| {
|
||||
type: 'management';
|
||||
}
|
||||
| {
|
||||
type: 'tunnel';
|
||||
name: string;
|
||||
}
|
||||
| { type: 'not_found' };
|
||||
|
||||
function routeUrl(url: URL, env: Env): RouteDecision {
|
||||
if (url.host.startsWith('localhost:')) {
|
||||
const searchParams = new URLSearchParams(url.search);
|
||||
const name = searchParams.get('t');
|
||||
|
||||
if (name) {
|
||||
return { type: 'tunnel', name };
|
||||
}
|
||||
|
||||
return { type: 'management' };
|
||||
}
|
||||
|
||||
if (!url.host.includes(env.WORKER_HOST)) {
|
||||
return { type: 'not_found' };
|
||||
}
|
||||
|
||||
const parts = url.host.split('.');
|
||||
|
||||
if (parts.length === 2) {
|
||||
return { type: 'management' };
|
||||
}
|
||||
|
||||
const tunnelName = parts[0];
|
||||
|
||||
if (tunnelName === 'admin') {
|
||||
return { type: 'management' };
|
||||
}
|
||||
|
||||
if (parts.length === 3) {
|
||||
return { type: 'tunnel', name: parts[0] };
|
||||
}
|
||||
|
||||
return { type: 'not_found' };
|
||||
}
|
||||
|
||||
async function handleManagementRequest(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
||||
const authHeader = request.headers.get('authorization');
|
||||
if (!authHeader) {
|
||||
return new Response('Authorization header is required', { status: 401 });
|
||||
}
|
||||
|
||||
const authHeaderParts = authHeader.split(' ');
|
||||
if (authHeaderParts.length !== 2) {
|
||||
return new Response('Authorization header is invalid', { status: 401 });
|
||||
}
|
||||
|
||||
const [authType, authKey] = authHeaderParts;
|
||||
|
||||
if (authType !== 'Bearer') {
|
||||
return new Response('Authorization header is invalid', { status: 401 });
|
||||
}
|
||||
|
||||
if (authKey !== env.SECRET_KEY) {
|
||||
return new Response('Authorization header is invalid', { status: 401 });
|
||||
}
|
||||
|
||||
// Okay now we can actually handle the request
|
||||
// We need to look at the path and see what we are doing
|
||||
// POST /api/tunnels -> create a new tunnel
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (url.pathname === '/api/tunnels' && request.method === 'POST') {
|
||||
return handleCreateTunnel(request, env, ctx);
|
||||
}
|
||||
|
||||
return new Response('Not Found', { status: 404 });
|
||||
}
|
||||
|
||||
async function handleCreateTunnel(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
||||
const tunnelId = env.connections.newUniqueId();
|
||||
const tunnelName = crypto.randomUUID();
|
||||
|
||||
await env.tunnelIds.put(tunnelName, tunnelId.toString());
|
||||
|
||||
return new Response(JSON.stringify({ id: tunnelName }), { status: 201 });
|
||||
}
|
||||
|
||||
type Resolver<T> = {
|
||||
resolve: (value: T) => void;
|
||||
reject: (error: Error) => void;
|
||||
};
|
||||
|
||||
export class YaltConnection implements DurableObject {
|
||||
private socket?: WebSocket;
|
||||
private responseResolvers: Record<string, Resolver<Response>> = {};
|
||||
|
||||
constructor(
|
||||
private state: DurableObjectState,
|
||||
private env: Env,
|
||||
) {}
|
||||
|
||||
async fetch(request: Request<unknown, CfProperties<unknown>>): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
|
||||
switch (url.pathname) {
|
||||
case '/connect': {
|
||||
console.log(`Handling connect request`);
|
||||
|
||||
// This is a request from the client to connect to the tunnel
|
||||
if (request.headers.get('Upgrade') !== 'websocket') {
|
||||
return new Response('expected websocket', { status: 400 });
|
||||
}
|
||||
|
||||
const pair = new WebSocketPair();
|
||||
const [clientSocket, serverSocket] = Object.values(pair);
|
||||
|
||||
await this.handleSocket(serverSocket);
|
||||
|
||||
console.log(`Successfully connected to tunnel`);
|
||||
|
||||
return new Response(null, { status: 101, webSocket: clientSocket });
|
||||
}
|
||||
case '/api/trigger': {
|
||||
return this.handleTunnelRequest(request);
|
||||
}
|
||||
default: {
|
||||
return new Response('Not Found', { status: 404 });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async handleSocket(socket: WebSocket) {
|
||||
if (this.socket) {
|
||||
this.socket.close(1000, 'replaced');
|
||||
}
|
||||
|
||||
this.socket = socket;
|
||||
|
||||
this.socket.addEventListener('message', (event) => {
|
||||
// Here we need to listen for "response" messages from the client and send them to the server via stored promises on this object
|
||||
const data = JSON.parse(typeof event.data === 'string' ? event.data : new TextDecoder('utf-8').decode(event.data));
|
||||
|
||||
const message = ClientMessages.safeParse(data);
|
||||
|
||||
if (!message.success) {
|
||||
console.error(message.error);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (message.data.type) {
|
||||
case 'response': {
|
||||
const { id, ...response } = message.data;
|
||||
|
||||
const resolver = this.responseResolvers[id];
|
||||
|
||||
if (!resolver) {
|
||||
console.error(`No resolver found for ${id}`);
|
||||
return;
|
||||
}
|
||||
|
||||
delete this.responseResolvers[id];
|
||||
|
||||
resolver.resolve(
|
||||
new Response(response.body, {
|
||||
status: response.status,
|
||||
headers: response.headers,
|
||||
}),
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.accept();
|
||||
}
|
||||
|
||||
private async handleTunnelRequest(request: Request): Promise<Response> {
|
||||
if (!this.socket) {
|
||||
return createErrorResponse();
|
||||
}
|
||||
|
||||
if (this.socket.readyState !== WebSocket.READY_STATE_OPEN) {
|
||||
return createErrorResponse();
|
||||
}
|
||||
|
||||
const id = crypto.randomUUID();
|
||||
|
||||
const promise = new Promise<Response>((resolve, reject) => {
|
||||
this.responseResolvers[id] = { resolve, reject };
|
||||
});
|
||||
|
||||
try {
|
||||
const message = await createRequestMessage(id, request);
|
||||
this.socket.send(JSON.stringify(message));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
delete this.responseResolvers[id];
|
||||
|
||||
return createErrorResponse();
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
}
|
||||
|
||||
const createErrorResponse = () =>
|
||||
new Response(
|
||||
JSON.stringify({
|
||||
message: 'Could not connect to your dev server. Make sure you are running the `npx @trigger.dev/cli@latest dev` command',
|
||||
}),
|
||||
{
|
||||
status: 400,
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,107 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Projects */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./", /* Specify the folder for .tsbuildinfo incremental compilation files. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
|
||||
/* Language and Environment */
|
||||
"target": "es2021" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
||||
"lib": ["es2021"] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
|
||||
"jsx": "react" /* Specify what JSX code is generated. */,
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h' */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using `jsx: react-jsx*`.` */
|
||||
// "reactNamespace": "", /* Specify the object invoked for `createElement`. This only applies when targeting `react` JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
|
||||
/* Modules */
|
||||
"module": "es2022" /* Specify what module code is generated. */,
|
||||
// "rootDir": "./", /* Specify the root folder within your source files. */
|
||||
"moduleResolution": "node" /* Specify how TypeScript looks up a file from a given module specifier. */,
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
// "typeRoots": [], /* Specify multiple folders that act like `./node_modules/@types`. */
|
||||
"types": [
|
||||
"@cloudflare/workers-types/2023-07-01"
|
||||
] /* Specify type package names to be included without being referenced in a source file. */,
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
"resolveJsonModule": true /* Enable importing .json files */,
|
||||
// "noResolve": true, /* Disallow `import`s, `require`s or `<reference>`s from expanding the number of files TypeScript should add to a project. */
|
||||
|
||||
/* JavaScript Support */
|
||||
"allowJs": true /* Allow JavaScript files to be a part of your program. Use the `checkJS` option to get errors from these files. */,
|
||||
"checkJs": false /* Enable error reporting in type-checked JavaScript files. */,
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from `node_modules`. Only applicable with `allowJs`. */
|
||||
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If `declaration` is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
"noEmit": true /* Disable emitting files from a compilation. */,
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have `@internal` in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like `__extends` in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing `const enum` declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
|
||||
/* Interop Constraints */
|
||||
"isolatedModules": true /* Ensure that each file can be safely transpiled without relying on other imports. */,
|
||||
"allowSyntheticDefaultImports": true /* Allow 'import x from y' when a module doesn't have a default export. */,
|
||||
// "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables `allowSyntheticDefaultImports` for type compatibility. */,
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
|
||||
|
||||
/* Type Checking */
|
||||
"strict": true /* Enable all strict type-checking options. */,
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied `any` type.. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account `null` and `undefined`. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for `bind`, `call`, and `apply` methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when `this` is given the type `any`. */
|
||||
// "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when a local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */,
|
||||
"paths": {
|
||||
"@trigger.dev/yalt": ["../../packages/yalt/src/index"],
|
||||
"@trigger.dev/yalt/*": ["../../packages/yalt/src/*"]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
name = "yalt"
|
||||
main = "src/index.ts"
|
||||
compatibility_date = "2023-12-06"
|
||||
workers_dev = false
|
||||
|
||||
routes = [
|
||||
{ pattern = "*.yalt.dev/*", zone_name = "yalt.dev" },
|
||||
{ pattern = "yalt.dev/*", zone_name = "yalt.dev" },
|
||||
]
|
||||
|
||||
[vars]
|
||||
WORKER_HOST = "yalt.dev"
|
||||
|
||||
[[kv_namespaces]]
|
||||
binding = "tunnelIds"
|
||||
id = "e8cf1fcc9ad34fa0819694d86afa97ca"
|
||||
|
||||
[[durable_objects.bindings]]
|
||||
name = "connections"
|
||||
class_name = "YaltConnection"
|
||||
|
||||
[[migrations]]
|
||||
tag = "v1"
|
||||
new_classes = ["YaltConnection"]
|
||||
@@ -41,6 +41,7 @@
|
||||
"@types/mock-fs": "^4.13.1",
|
||||
"@types/node": "16",
|
||||
"@types/node-fetch": "^2.6.2",
|
||||
"@types/ws": "^8.5.3",
|
||||
"rimraf": "^3.0.2",
|
||||
"tsup": "^6.5.0",
|
||||
"type-fest": "^3.6.0",
|
||||
@@ -57,6 +58,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@trigger.dev/core": "workspace:*",
|
||||
"@trigger.dev/yalt": "workspace:*",
|
||||
"@types/degit": "^2.8.3",
|
||||
"boxen": "^7.1.1",
|
||||
"chalk": "^5.2.0",
|
||||
@@ -85,9 +87,10 @@
|
||||
"terminal-link": "^3.0.0",
|
||||
"tsconfck": "^2.1.2",
|
||||
"url": "^0.11.1",
|
||||
"ws": "^8.11.0",
|
||||
"zod": "3.22.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import { RequireKeys } from "../utils/requiredKeys";
|
||||
import { Throttle } from "../utils/throttle";
|
||||
import { TriggerApi } from "../utils/triggerApi";
|
||||
import { wait } from "../utils/wait";
|
||||
import { YaltTunnel } from "@trigger.dev/yalt";
|
||||
|
||||
const asyncExecFile = util.promisify(childProcess.execFile);
|
||||
|
||||
@@ -128,7 +129,8 @@ export async function devCommand(path: string, anyOptions: any) {
|
||||
telemetryClient.dev.serverRunning(path, resolvedOptions);
|
||||
|
||||
// Setup tunnel
|
||||
const endpointUrl = await resolveEndpointUrl(apiUrl, verifiedEndpoint);
|
||||
const endpointUrl = await resolveEndpointUrl(apiUrl, apiKey, verifiedEndpoint);
|
||||
|
||||
if (!endpointUrl) {
|
||||
telemetryClient.dev.failed("failed_to_create_tunnel", resolvedOptions);
|
||||
return;
|
||||
@@ -444,7 +446,7 @@ function findServerUrls(resolvedOptions: ResolvedOptions, framework?: Framework)
|
||||
return urls;
|
||||
}
|
||||
|
||||
async function resolveEndpointUrl(apiUrl: string, endpoint: ServerEndpoint) {
|
||||
async function resolveEndpointUrl(apiUrl: string, apiKey: string, endpoint: ServerEndpoint) {
|
||||
// use tunnel URL if provided
|
||||
if (endpoint.type === "tunnel") {
|
||||
return endpoint.url;
|
||||
@@ -457,18 +459,57 @@ async function resolveEndpointUrl(apiUrl: string, endpoint: ServerEndpoint) {
|
||||
return `http://${endpoint.hostname}:${endpoint.port}`;
|
||||
}
|
||||
|
||||
// Setup tunnel
|
||||
const tunnelSpinner = ora(`🚇 Creating tunnel`).start();
|
||||
const tunnelUrl = await createTunnel(endpoint.hostname, endpoint.port, tunnelSpinner);
|
||||
const triggerApi = new TriggerApi(apiKey, apiUrl);
|
||||
|
||||
if (tunnelUrl) {
|
||||
tunnelSpinner.succeed(`🚇 Created tunnel: ${tunnelUrl}`);
|
||||
const supportsTunneling = await triggerApi.supportsTunneling();
|
||||
|
||||
if (supportsTunneling) {
|
||||
const tunnelSpinner = ora(`🚇 Creating Trigger.dev tunnel`).start();
|
||||
const tunnelUrl = await createNativeTunnel(endpoint.hostname, endpoint.port, triggerApi, tunnelSpinner);
|
||||
|
||||
if (tunnelUrl) {
|
||||
tunnelSpinner.succeed(`🚇 Trigger.dev tunnel ready`);
|
||||
}
|
||||
|
||||
return tunnelUrl;
|
||||
} else {
|
||||
// Setup tunnel
|
||||
const tunnelSpinner = ora(`🚇 Creating tunnel`).start();
|
||||
const tunnelUrl = await createNgrokTunnel(endpoint.hostname, endpoint.port, tunnelSpinner);
|
||||
|
||||
if (tunnelUrl) {
|
||||
tunnelSpinner.succeed(`🚇 Created tunnel: ${tunnelUrl}`);
|
||||
}
|
||||
|
||||
return tunnelUrl;
|
||||
}
|
||||
|
||||
return tunnelUrl;
|
||||
}
|
||||
|
||||
async function createTunnel(hostname: string, port: number, spinner: Ora) {
|
||||
let yaltTunnel: YaltTunnel | null = null;
|
||||
|
||||
async function createNativeTunnel(hostname: string, port: number, triggerApi: TriggerApi, spinner: Ora) {
|
||||
try {
|
||||
const response = await triggerApi.createTunnel();
|
||||
|
||||
// import WS dynamically
|
||||
const WebSocket = await import("ws");
|
||||
|
||||
yaltTunnel = new YaltTunnel(response.url, `${hostname}:${port}`, {
|
||||
WebSocket: WebSocket.default,
|
||||
connectionTimeout: 1000,
|
||||
maxRetries: 10
|
||||
});
|
||||
|
||||
await yaltTunnel.connect();
|
||||
|
||||
return `https://${response.url}`;
|
||||
} catch (e) {
|
||||
spinner.fail(`Failed to create tunnel.\n${e}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
async function createNgrokTunnel(hostname: string, port: number, spinner: Ora) {
|
||||
try {
|
||||
return await ngrok.connect({ addr: `${hostname}:${port}` });
|
||||
} catch (error: any) {
|
||||
|
||||
@@ -24,9 +24,9 @@ export type EndpointData = {
|
||||
|
||||
export type EndpointResponse =
|
||||
| {
|
||||
ok: true;
|
||||
data: EndpointData;
|
||||
}
|
||||
ok: true;
|
||||
data: EndpointData;
|
||||
}
|
||||
| { ok: false; error: string; retryable: boolean };
|
||||
|
||||
const RETRYABLE_PATTERN = /Could not connect to endpoint/i;
|
||||
@@ -55,13 +55,53 @@ const WhoamiResponseSchema = z.object({
|
||||
userId: z.string().optional(),
|
||||
});
|
||||
|
||||
const CreateTunnelResponseSchema = z.object({
|
||||
url: z.string(),
|
||||
});
|
||||
|
||||
export type WhoamiResponse = z.infer<typeof WhoamiResponseSchema>;
|
||||
|
||||
export class TriggerApi {
|
||||
constructor(
|
||||
private apiKey: string,
|
||||
private baseUrl: string
|
||||
) {}
|
||||
) { }
|
||||
|
||||
async supportsTunneling(): Promise<boolean> {
|
||||
const response = await fetch(`${this.baseUrl}/api/v1/tunnels`, {
|
||||
method: "HEAD",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
Authorization: `Bearer ${this.apiKey}`,
|
||||
},
|
||||
});
|
||||
|
||||
return response.ok;
|
||||
}
|
||||
|
||||
async createTunnel() {
|
||||
const response = await fetch(`${this.baseUrl}/api/v1/tunnels`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Accept: "application/json",
|
||||
Authorization: `Bearer ${this.apiKey}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Could not create tunnel: ${response.status}`);
|
||||
}
|
||||
|
||||
const body = await response.json();
|
||||
|
||||
const parsedBody = CreateTunnelResponseSchema.safeParse(body);
|
||||
|
||||
if (!parsedBody.success) {
|
||||
throw new Error(`Could not create tunnel: ${parsedBody.error.message}`);
|
||||
}
|
||||
|
||||
return parsedBody.data;
|
||||
}
|
||||
|
||||
async whoami(): Promise<WhoamiResponse | undefined> {
|
||||
const response = await fetch(`${this.baseUrl}/api/v1/whoami`, {
|
||||
|
||||
@@ -46,7 +46,10 @@
|
||||
"useDefineForClassFields": true,
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"types": ["vitest/globals"]
|
||||
"types": ["vitest/globals"],
|
||||
"paths": {
|
||||
"@trigger.dev/yalt": ["../yalt/src/index"]
|
||||
}
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "RuntimeEnvironment" ADD COLUMN "tunnelId" TEXT;
|
||||
@@ -316,6 +316,8 @@ model RuntimeEnvironment {
|
||||
createdAt DateTime @default(now())
|
||||
updatedAt DateTime @updatedAt
|
||||
|
||||
tunnelId String?
|
||||
|
||||
endpoints Endpoint[]
|
||||
jobVersions JobVersion[]
|
||||
events EventRecord[]
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
## @trigger.dev/yalt
|
||||
@@ -0,0 +1,52 @@
|
||||
{
|
||||
"name": "@trigger.dev/yalt",
|
||||
"version": "2.3.5",
|
||||
"description": "yalt.dev client library",
|
||||
"license": "MIT",
|
||||
"main": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"module": "./dist/index.mjs",
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
".": {
|
||||
"import": {
|
||||
"types": "./dist/index.d.mts",
|
||||
"default": "./dist/index.mjs"
|
||||
},
|
||||
"require": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"sideEffects": false,
|
||||
"scripts": {
|
||||
"clean": "rimraf dist",
|
||||
"build": "npm run clean && npm run build:tsup",
|
||||
"build:tsup": "tsup --dts-resolve",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"zod": "3.22.3",
|
||||
"partysocket": "^0.0.17"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@trigger.dev/tsconfig": "workspace:*",
|
||||
"@types/jest": "^29.5.3",
|
||||
"@types/node": "18",
|
||||
"jest": "^29.6.2",
|
||||
"rimraf": "^3.0.2",
|
||||
"ts-jest": "^29.1.1",
|
||||
"tsup": "^8.0.1",
|
||||
"@trigger.dev/tsup": "workspace:*",
|
||||
"typescript": "^5.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
import { z } from "zod";
|
||||
import { WebSocket } from "partysocket";
|
||||
|
||||
export const RequestMesssage = z.object({
|
||||
type: z.literal("request"),
|
||||
id: z.string(),
|
||||
headers: z.record(z.string()),
|
||||
method: z.string(),
|
||||
url: z.string(),
|
||||
body: z.string(),
|
||||
});
|
||||
|
||||
export type RequestMessage = z.infer<typeof RequestMesssage>;
|
||||
|
||||
export const ResponseMessage = z.object({
|
||||
type: z.literal("response"),
|
||||
id: z.string(),
|
||||
status: z.number(),
|
||||
headers: z.record(z.string()),
|
||||
body: z.string(),
|
||||
});
|
||||
|
||||
export type ResponseMessage = z.infer<typeof ResponseMessage>;
|
||||
|
||||
export const ClientMessages = z.discriminatedUnion("type", [ResponseMessage]);
|
||||
export const ServerMessages = z.discriminatedUnion("type", [RequestMesssage]);
|
||||
|
||||
export type ClientMessage = z.infer<typeof ClientMessages>;
|
||||
export type ServerMessage = z.infer<typeof ServerMessages>;
|
||||
|
||||
export async function createRequestMessage(id: string, request: Request): Promise<RequestMessage> {
|
||||
const { headers, method, url } = request;
|
||||
|
||||
const body = await request.text();
|
||||
|
||||
return {
|
||||
type: "request",
|
||||
id,
|
||||
headers: Object.fromEntries(headers),
|
||||
method,
|
||||
url,
|
||||
body,
|
||||
};
|
||||
}
|
||||
|
||||
async function createResponseMessage(id: string, response: Response): Promise<ResponseMessage> {
|
||||
const { headers, status } = response;
|
||||
|
||||
const body = await response.text();
|
||||
|
||||
return {
|
||||
type: "response",
|
||||
id,
|
||||
headers: Object.fromEntries(headers),
|
||||
status,
|
||||
body,
|
||||
};
|
||||
}
|
||||
|
||||
export class YaltApiClient {
|
||||
constructor(
|
||||
private host: string,
|
||||
private apiKey: string
|
||||
) {}
|
||||
|
||||
async createTunnel(): Promise<string> {
|
||||
const response = await fetch(`https://admin.${this.host}/api/tunnels`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
accept: "application/json",
|
||||
authorization: `Bearer ${this.apiKey}`,
|
||||
},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`Could not create tunnel: ${response.status}`);
|
||||
}
|
||||
|
||||
const body = await response.json();
|
||||
|
||||
return body.id;
|
||||
}
|
||||
|
||||
connectUrl(id: string): string {
|
||||
return `${id}.${this.host}`;
|
||||
}
|
||||
}
|
||||
|
||||
export type YaltTunnelOptions = {
|
||||
WebSocket?: any;
|
||||
connectionTimeout?: number;
|
||||
maxRetries?: number;
|
||||
};
|
||||
export class YaltTunnel {
|
||||
socket?: WebSocket;
|
||||
|
||||
constructor(
|
||||
private url: string,
|
||||
private address: string,
|
||||
private options: YaltTunnelOptions = {}
|
||||
) {}
|
||||
|
||||
async connect() {
|
||||
this.socket = new WebSocket(`wss://${this.url}/connect`, [], this.options);
|
||||
|
||||
this.socket.addEventListener("open", () => {});
|
||||
|
||||
this.socket.addEventListener("close", (event) => {});
|
||||
|
||||
this.socket.addEventListener("message", async (event) => {
|
||||
const data = JSON.parse(
|
||||
typeof event.data === "string" ? event.data : new TextDecoder("utf-8").decode(event.data)
|
||||
);
|
||||
|
||||
const message = ServerMessages.safeParse(data);
|
||||
|
||||
if (!message.success) {
|
||||
console.error(message.error);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (message.data.type) {
|
||||
case "request": {
|
||||
await this.handleRequest(message.data);
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
console.error(`Unknown message type: ${message.data.type}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.socket.addEventListener("error", (event) => {
|
||||
console.error(event);
|
||||
});
|
||||
}
|
||||
|
||||
private async handleRequest(request: RequestMessage) {
|
||||
if (!this.socket) {
|
||||
throw new Error("Socket is not connected");
|
||||
}
|
||||
|
||||
const url = new URL(request.url);
|
||||
// Construct the original url to be the same as the request URL but with a different hostname and using http instead of https
|
||||
const originalUrl = new URL(`http://${this.address}${url.pathname}${url.search}${url.hash}`);
|
||||
|
||||
let response: Response | null = null;
|
||||
|
||||
try {
|
||||
response = await fetch(originalUrl.href, {
|
||||
method: request.method,
|
||||
headers: request.headers,
|
||||
body: request.body,
|
||||
});
|
||||
} catch (error) {
|
||||
// Return a 502 response
|
||||
response = new Response(
|
||||
JSON.stringify({
|
||||
message: `Could not connect to ${originalUrl.href}. Make sure you are running your local app server`,
|
||||
}),
|
||||
{ status: 502, headers: { "Content-Type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
await sendResponse(request.id, response, this.socket);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function sendResponse(id: string, response: Response, socket: WebSocket) {
|
||||
const message = await createResponseMessage(id, response);
|
||||
|
||||
return socket.send(JSON.stringify(message));
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "@trigger.dev/tsconfig/node18.json",
|
||||
"include": ["src/globals.d.ts", "./src/**/*.ts", "tsup.config.ts"],
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"declaration": false,
|
||||
"declarationMap": false,
|
||||
"paths": {
|
||||
"@trigger.dev/tsup/*": ["../../config-packages/tsup/src/*"],
|
||||
"@trigger.dev/tsup": ["../../config-packages/tsup/src/index"]
|
||||
},
|
||||
"lib": ["DOM", "DOM.Iterable"]
|
||||
},
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { defineConfigPackage } from "@trigger.dev/tsup";
|
||||
|
||||
export default defineConfigPackage;
|
||||
@@ -6,11 +6,6 @@ export const triggerClient = new TriggerClient({
|
||||
apiUrl: process.env.TRIGGER_API_URL!,
|
||||
});
|
||||
|
||||
const concurrencyLimit = triggerClient.defineConcurrencyLimit({
|
||||
id: `perf-test-shared`,
|
||||
limit: 5,
|
||||
});
|
||||
|
||||
triggerClient.defineJob({
|
||||
id: `perf-test-1`,
|
||||
name: `Perf Test 1`,
|
||||
@@ -18,7 +13,6 @@ triggerClient.defineJob({
|
||||
trigger: eventTrigger({
|
||||
name: "perf.test",
|
||||
}),
|
||||
concurrencyLimit,
|
||||
run: async (payload, io, ctx) => {
|
||||
await io.runTask(
|
||||
"task-1",
|
||||
@@ -65,7 +59,6 @@ triggerClient.defineJob({
|
||||
trigger: eventTrigger({
|
||||
name: "perf.test",
|
||||
}),
|
||||
concurrencyLimit: 5,
|
||||
run: async (payload, io, ctx) => {
|
||||
await io.runTask(
|
||||
"task-1",
|
||||
@@ -112,7 +105,6 @@ triggerClient.defineJob({
|
||||
trigger: eventTrigger({
|
||||
name: "perf.test",
|
||||
}),
|
||||
concurrencyLimit,
|
||||
run: async (payload, io, ctx) => {
|
||||
await io.runTask(
|
||||
"task-1",
|
||||
|
||||
Generated
+101
-13
@@ -140,6 +140,7 @@ importers:
|
||||
'@trigger.dev/database': workspace:*
|
||||
'@trigger.dev/sdk': workspace:*
|
||||
'@trigger.dev/tailwind-config': workspace:*
|
||||
'@trigger.dev/yalt': workspace:*
|
||||
'@types/bcryptjs': ^2.4.2
|
||||
'@types/compression': ^1.7.2
|
||||
'@types/eslint': ^8.4.6
|
||||
@@ -277,6 +278,7 @@ importers:
|
||||
'@trigger.dev/core-backend': link:../../packages/core-backend
|
||||
'@trigger.dev/database': link:../../packages/database
|
||||
'@trigger.dev/sdk': link:../../packages/trigger-sdk
|
||||
'@trigger.dev/yalt': link:../../packages/yalt
|
||||
'@types/pg': 8.6.6
|
||||
'@uiw/react-codemirror': 4.19.5_th22fcplkuhrqjnlojwclcaim4
|
||||
'@whatwg-node/fetch': 0.9.14
|
||||
@@ -393,6 +395,17 @@ importers:
|
||||
tsconfig-paths: 3.14.1
|
||||
typescript: 5.2.2
|
||||
|
||||
apps/yalt:
|
||||
specifiers:
|
||||
'@cloudflare/workers-types': ~4.20231121.0
|
||||
'@trigger.dev/yalt': workspace:*
|
||||
wrangler: ^3.20.0
|
||||
dependencies:
|
||||
'@trigger.dev/yalt': link:../../packages/yalt
|
||||
devDependencies:
|
||||
'@cloudflare/workers-types': 4.20231121.0
|
||||
wrangler: 3.20.0
|
||||
|
||||
config-packages/eslint-config-custom:
|
||||
specifiers:
|
||||
eslint: ^8.24.0
|
||||
@@ -787,12 +800,14 @@ importers:
|
||||
specifiers:
|
||||
'@trigger.dev/core': workspace:*
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/yalt': workspace:*
|
||||
'@types/degit': ^2.8.3
|
||||
'@types/gradient-string': ^1.1.2
|
||||
'@types/inquirer': ^9.0.3
|
||||
'@types/mock-fs': ^4.13.1
|
||||
'@types/node': '16'
|
||||
'@types/node-fetch': ^2.6.2
|
||||
'@types/ws': ^8.5.3
|
||||
boxen: ^7.1.1
|
||||
chalk: ^5.2.0
|
||||
chokidar: ^3.5.3
|
||||
@@ -825,9 +840,11 @@ importers:
|
||||
typescript: ^4.9.5
|
||||
url: ^0.11.1
|
||||
vitest: ^0.34.4
|
||||
ws: ^8.11.0
|
||||
zod: 3.22.3
|
||||
dependencies:
|
||||
'@trigger.dev/core': link:../core
|
||||
'@trigger.dev/yalt': link:../yalt
|
||||
'@types/degit': 2.8.3
|
||||
boxen: 7.1.1
|
||||
chalk: 5.2.0
|
||||
@@ -856,6 +873,7 @@ importers:
|
||||
terminal-link: 3.0.0
|
||||
tsconfck: 2.1.2_typescript@4.9.5
|
||||
url: 0.11.1
|
||||
ws: 8.12.0
|
||||
zod: 3.22.3
|
||||
devDependencies:
|
||||
'@trigger.dev/tsconfig': link:../../config-packages/tsconfig
|
||||
@@ -864,6 +882,7 @@ importers:
|
||||
'@types/mock-fs': 4.13.1
|
||||
'@types/node': 16.18.11
|
||||
'@types/node-fetch': 2.6.2
|
||||
'@types/ws': 8.5.4
|
||||
rimraf: 3.0.2
|
||||
tsup: 6.6.3_typescript@4.9.5
|
||||
type-fest: 3.6.0
|
||||
@@ -1289,6 +1308,33 @@ importers:
|
||||
typed-emitter: 2.1.0
|
||||
typescript: 5.3.2
|
||||
|
||||
packages/yalt:
|
||||
specifiers:
|
||||
'@trigger.dev/tsconfig': workspace:*
|
||||
'@trigger.dev/tsup': workspace:*
|
||||
'@types/jest': ^29.5.3
|
||||
'@types/node': '18'
|
||||
jest: ^29.6.2
|
||||
partysocket: ^0.0.17
|
||||
rimraf: ^3.0.2
|
||||
ts-jest: ^29.1.1
|
||||
tsup: ^8.0.1
|
||||
typescript: ^5.3.0
|
||||
zod: 3.22.3
|
||||
dependencies:
|
||||
partysocket: 0.0.17
|
||||
zod: 3.22.3
|
||||
devDependencies:
|
||||
'@trigger.dev/tsconfig': link:../../config-packages/tsconfig
|
||||
'@trigger.dev/tsup': link:../../config-packages/tsup
|
||||
'@types/jest': 29.5.3
|
||||
'@types/node': 18.17.1
|
||||
jest: 29.6.2_@types+node@18.17.1
|
||||
rimraf: 3.0.2
|
||||
ts-jest: 29.1.1_3tyr5rkhps22cjyuxtwz6gzoky
|
||||
tsup: 8.0.1_a5ztaafw5l4qfghy2hjjuynb34_typescript@5.3.2
|
||||
typescript: 5.3.2
|
||||
|
||||
perf:
|
||||
specifiers:
|
||||
'@trigger.dev/cli': workspace:*
|
||||
@@ -16052,7 +16098,7 @@ packages:
|
||||
/axios/0.21.4_debug@4.3.2:
|
||||
resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.2_debug@4.3.2
|
||||
follow-redirects: 1.15.2
|
||||
transitivePeerDependencies:
|
||||
- debug
|
||||
dev: false
|
||||
@@ -20880,18 +20926,6 @@ packages:
|
||||
debug:
|
||||
optional: true
|
||||
|
||||
/follow-redirects/1.15.2_debug@4.3.2:
|
||||
resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
|
||||
engines: {node: '>=4.0'}
|
||||
peerDependencies:
|
||||
debug: '*'
|
||||
peerDependenciesMeta:
|
||||
debug:
|
||||
optional: true
|
||||
dependencies:
|
||||
debug: 4.3.2
|
||||
dev: false
|
||||
|
||||
/for-each/0.3.3:
|
||||
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
|
||||
dependencies:
|
||||
@@ -25198,6 +25232,29 @@ packages:
|
||||
- utf-8-validate
|
||||
dev: true
|
||||
|
||||
/miniflare/3.20231030.4:
|
||||
resolution: {integrity: sha512-7MBz0ArLuDop1WJGZC6tFgN6c5MRyDOIlxbm3yp0TRBpvDS/KsTuWCQcCjsxN4QQ5zvL3JTkuIZbQzRRw/j6ow==}
|
||||
engines: {node: '>=16.13'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
acorn: 8.10.0
|
||||
acorn-walk: 8.2.0
|
||||
capnp-ts: 0.7.0
|
||||
exit-hook: 2.2.1
|
||||
glob-to-regexp: 0.4.1
|
||||
source-map-support: 0.5.21
|
||||
stoppable: 1.1.0
|
||||
undici: 5.25.4
|
||||
workerd: 1.20231030.0
|
||||
ws: 8.12.0
|
||||
youch: 3.3.3
|
||||
zod: 3.22.4
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
dev: true
|
||||
|
||||
/minimal-polyfills/2.2.2:
|
||||
resolution: {integrity: sha512-eEOUq/LH/DbLWihrxUP050Wi7H/N/I2dQT98Ep6SqOpmIbk4sXOI4wqalve66QoZa+6oljbZWU6I6T4dehQGmw==}
|
||||
dev: false
|
||||
@@ -26732,6 +26789,10 @@ packages:
|
||||
resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
/partysocket/0.0.17:
|
||||
resolution: {integrity: sha512-8Re9nmgP2LzQhq+FBs9+BZNTjmMwoF4geEKlpH0lxW1JKp3FmplN74306afGH9EsOjdfcXqKY2VCZtc3iAHIow==}
|
||||
dev: false
|
||||
|
||||
/pascal-case/3.1.2:
|
||||
resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==}
|
||||
dependencies:
|
||||
@@ -33070,6 +33131,33 @@ packages:
|
||||
- utf-8-validate
|
||||
dev: true
|
||||
|
||||
/wrangler/3.20.0:
|
||||
resolution: {integrity: sha512-7mg25zJByhBmrfG+CbImSid7JNd5lxGovLA167ndtE8Yrqd3TUukrGWL8o0RCQIm0FUcgl2nCzWArJDShlZVKA==}
|
||||
engines: {node: '>=16.17.0'}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@cloudflare/kv-asset-handler': 0.2.0
|
||||
'@esbuild-plugins/node-globals-polyfill': 0.2.3_esbuild@0.17.19
|
||||
'@esbuild-plugins/node-modules-polyfill': 0.2.2_esbuild@0.17.19
|
||||
blake3-wasm: 2.1.5
|
||||
chokidar: 3.5.3
|
||||
esbuild: 0.17.19
|
||||
miniflare: 3.20231030.4
|
||||
nanoid: 3.3.6
|
||||
path-to-regexp: 6.2.1
|
||||
resolve.exports: 2.0.2
|
||||
selfsigned: 2.4.1
|
||||
source-map: 0.6.1
|
||||
source-map-support: 0.5.21
|
||||
xxhash-wasm: 1.0.2
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
transitivePeerDependencies:
|
||||
- bufferutil
|
||||
- supports-color
|
||||
- utf-8-validate
|
||||
dev: true
|
||||
|
||||
/wrap-ansi/6.2.0:
|
||||
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
Reference in New Issue
Block a user