chore: format Cloudflare Worker code
This commit is contained in:
parent
63aa2b1e09
commit
52aa9d92fd
@ -1,22 +1,22 @@
|
|||||||
{
|
{
|
||||||
"name": "ai",
|
"name": "ai",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"deploy": "wrangler deploy",
|
"deploy": "wrangler deploy",
|
||||||
"dev": "wrangler dev",
|
"dev": "wrangler dev",
|
||||||
"start": "wrangler dev",
|
"start": "wrangler dev",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"cf-typegen": "wrangler types"
|
"cf-typegen": "wrangler types"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/vitest-pool-workers": "^0.1.0",
|
"@cloudflare/vitest-pool-workers": "^0.1.0",
|
||||||
"@cloudflare/workers-types": "^4.20240512.0",
|
"@cloudflare/workers-types": "^4.20240512.0",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"vitest": "1.3.0",
|
"vitest": "1.3.0",
|
||||||
"wrangler": "^3.0.0"
|
"wrangler": "^3.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@anthropic-ai/sdk": "^0.27.2"
|
"@anthropic-ai/sdk": "^0.27.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,57 +1,61 @@
|
|||||||
import { Anthropic } from "@anthropic-ai/sdk";
|
import { Anthropic } from "@anthropic-ai/sdk"
|
||||||
import { MessageParam } from "@anthropic-ai/sdk/src/resources/messages.js";
|
import { MessageParam } from "@anthropic-ai/sdk/src/resources/messages.js"
|
||||||
|
|
||||||
export interface Env {
|
export interface Env {
|
||||||
ANTHROPIC_API_KEY: string;
|
ANTHROPIC_API_KEY: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
async fetch(request: Request, env: Env): Promise<Response> {
|
async fetch(request: Request, env: Env): Promise<Response> {
|
||||||
// Handle CORS preflight requests
|
// Handle CORS preflight requests
|
||||||
if (request.method === "OPTIONS") {
|
if (request.method === "OPTIONS") {
|
||||||
return new Response(null, {
|
return new Response(null, {
|
||||||
headers: {
|
headers: {
|
||||||
"Access-Control-Allow-Origin": "*",
|
"Access-Control-Allow-Origin": "*",
|
||||||
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
|
||||||
"Access-Control-Allow-Headers": "Content-Type",
|
"Access-Control-Allow-Headers": "Content-Type",
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
if (request.method !== "GET" && request.method !== "POST") {
|
if (request.method !== "GET" && request.method !== "POST") {
|
||||||
return new Response("Method Not Allowed", { status: 405 });
|
return new Response("Method Not Allowed", { status: 405 })
|
||||||
}
|
}
|
||||||
|
|
||||||
let body;
|
let body
|
||||||
let isEditCodeWidget = false;
|
let isEditCodeWidget = false
|
||||||
if (request.method === "POST") {
|
if (request.method === "POST") {
|
||||||
body = await request.json() as { messages: unknown; context: unknown; activeFileContent: string };
|
body = (await request.json()) as {
|
||||||
} else {
|
messages: unknown
|
||||||
const url = new URL(request.url);
|
context: unknown
|
||||||
const fileName = url.searchParams.get("fileName") || "";
|
activeFileContent: string
|
||||||
const code = url.searchParams.get("code") || "";
|
}
|
||||||
const line = url.searchParams.get("line") || "";
|
} else {
|
||||||
const instructions = url.searchParams.get("instructions") || "";
|
const url = new URL(request.url)
|
||||||
|
const fileName = url.searchParams.get("fileName") || ""
|
||||||
|
const code = url.searchParams.get("code") || ""
|
||||||
|
const line = url.searchParams.get("line") || ""
|
||||||
|
const instructions = url.searchParams.get("instructions") || ""
|
||||||
|
|
||||||
body = {
|
body = {
|
||||||
messages: [{ role: "human", content: instructions }],
|
messages: [{ role: "human", content: instructions }],
|
||||||
context: `File: ${fileName}\nLine: ${line}\nCode:\n${code}`,
|
context: `File: ${fileName}\nLine: ${line}\nCode:\n${code}`,
|
||||||
activeFileContent: code,
|
activeFileContent: code,
|
||||||
};
|
}
|
||||||
isEditCodeWidget = true;
|
isEditCodeWidget = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const messages = body.messages;
|
const messages = body.messages
|
||||||
const context = body.context;
|
const context = body.context
|
||||||
const activeFileContent = body.activeFileContent;
|
const activeFileContent = body.activeFileContent
|
||||||
|
|
||||||
if (!Array.isArray(messages) || messages.length === 0) {
|
if (!Array.isArray(messages) || messages.length === 0) {
|
||||||
return new Response("Invalid or empty messages", { status: 400 });
|
return new Response("Invalid or empty messages", { status: 400 })
|
||||||
}
|
}
|
||||||
|
|
||||||
let systemMessage;
|
let systemMessage
|
||||||
if (isEditCodeWidget) {
|
if (isEditCodeWidget) {
|
||||||
systemMessage = `You are an AI code editor. Your task is to modify the given code based on the user's instructions. Only output the modified code, without any explanations or markdown formatting. The code should be a direct replacement for the existing code.
|
systemMessage = `You are an AI code editor. Your task is to modify the given code based on the user's instructions. Only output the modified code, without any explanations or markdown formatting. The code should be a direct replacement for the existing code.
|
||||||
|
|
||||||
Context:
|
Context:
|
||||||
${context}
|
${context}
|
||||||
@ -61,9 +65,9 @@ ${activeFileContent}
|
|||||||
|
|
||||||
Instructions: ${messages[0].content}
|
Instructions: ${messages[0].content}
|
||||||
|
|
||||||
Respond only with the modified code that can directly replace the existing code.`;
|
Respond only with the modified code that can directly replace the existing code.`
|
||||||
} else {
|
} else {
|
||||||
systemMessage = `You are an intelligent programming assistant. Please respond to the following request concisely. If your response includes code, please format it using triple backticks (\`\`\`) with the appropriate language identifier. For example:
|
systemMessage = `You are an intelligent programming assistant. Please respond to the following request concisely. If your response includes code, please format it using triple backticks (\`\`\`) with the appropriate language identifier. For example:
|
||||||
|
|
||||||
\`\`\`python
|
\`\`\`python
|
||||||
print("Hello, World!")
|
print("Hello, World!")
|
||||||
@ -71,51 +75,54 @@ print("Hello, World!")
|
|||||||
|
|
||||||
Provide a clear and concise explanation along with any code snippets. Keep your response brief and to the point.
|
Provide a clear and concise explanation along with any code snippets. Keep your response brief and to the point.
|
||||||
|
|
||||||
${context ? `Context:\n${context}\n` : ''}
|
${context ? `Context:\n${context}\n` : ""}
|
||||||
${activeFileContent ? `Active File Content:\n${activeFileContent}\n` : ''}`;
|
${activeFileContent ? `Active File Content:\n${activeFileContent}\n` : ""}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const anthropicMessages = messages.map(msg => ({
|
const anthropicMessages = messages.map((msg) => ({
|
||||||
role: msg.role === 'human' ? 'user' : 'assistant',
|
role: msg.role === "human" ? "user" : "assistant",
|
||||||
content: msg.content
|
content: msg.content,
|
||||||
})) as MessageParam[];
|
})) as MessageParam[]
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const anthropic = new Anthropic({ apiKey: env.ANTHROPIC_API_KEY });
|
const anthropic = new Anthropic({ apiKey: env.ANTHROPIC_API_KEY })
|
||||||
|
|
||||||
const stream = await anthropic.messages.create({
|
const stream = await anthropic.messages.create({
|
||||||
model: "claude-3-5-sonnet-20240620",
|
model: "claude-3-5-sonnet-20240620",
|
||||||
max_tokens: 1024,
|
max_tokens: 1024,
|
||||||
system: systemMessage,
|
system: systemMessage,
|
||||||
messages: anthropicMessages,
|
messages: anthropicMessages,
|
||||||
stream: true,
|
stream: true,
|
||||||
});
|
})
|
||||||
|
|
||||||
const encoder = new TextEncoder();
|
const encoder = new TextEncoder()
|
||||||
|
|
||||||
const streamResponse = new ReadableStream({
|
const streamResponse = new ReadableStream({
|
||||||
async start(controller) {
|
async start(controller) {
|
||||||
for await (const chunk of stream) {
|
for await (const chunk of stream) {
|
||||||
if (chunk.type === 'content_block_delta' && chunk.delta.type === 'text_delta') {
|
if (
|
||||||
const bytes = encoder.encode(chunk.delta.text);
|
chunk.type === "content_block_delta" &&
|
||||||
controller.enqueue(bytes);
|
chunk.delta.type === "text_delta"
|
||||||
}
|
) {
|
||||||
}
|
const bytes = encoder.encode(chunk.delta.text)
|
||||||
controller.close();
|
controller.enqueue(bytes)
|
||||||
},
|
}
|
||||||
});
|
}
|
||||||
|
controller.close()
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
return new Response(streamResponse, {
|
return new Response(streamResponse, {
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "text/plain; charset=utf-8",
|
"Content-Type": "text/plain; charset=utf-8",
|
||||||
"Access-Control-Allow-Origin": "*",
|
"Access-Control-Allow-Origin": "*",
|
||||||
"Cache-Control": "no-cache",
|
"Cache-Control": "no-cache",
|
||||||
"Connection": "keep-alive",
|
Connection: "keep-alive",
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error:", error);
|
console.error("Error:", error)
|
||||||
return new Response("Internal Server Error", { status: 500 });
|
return new Response("Internal Server Error", { status: 500 })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
@ -1,25 +1,30 @@
|
|||||||
// test/index.spec.ts
|
// test/index.spec.ts
|
||||||
import { env, createExecutionContext, waitOnExecutionContext, SELF } from 'cloudflare:test';
|
import {
|
||||||
import { describe, it, expect } from 'vitest';
|
createExecutionContext,
|
||||||
import worker from '../src/index';
|
env,
|
||||||
|
SELF,
|
||||||
|
waitOnExecutionContext,
|
||||||
|
} from "cloudflare:test"
|
||||||
|
import { describe, expect, it } from "vitest"
|
||||||
|
import worker from "../src/index"
|
||||||
|
|
||||||
// For now, you'll need to do something like this to get a correctly-typed
|
// For now, you'll need to do something like this to get a correctly-typed
|
||||||
// `Request` to pass to `worker.fetch()`.
|
// `Request` to pass to `worker.fetch()`.
|
||||||
const IncomingRequest = Request<unknown, IncomingRequestCfProperties>;
|
const IncomingRequest = Request<unknown, IncomingRequestCfProperties>
|
||||||
|
|
||||||
describe('Hello World worker', () => {
|
describe("Hello World worker", () => {
|
||||||
it('responds with Hello World! (unit style)', async () => {
|
it("responds with Hello World! (unit style)", async () => {
|
||||||
const request = new IncomingRequest('http://example.com');
|
const request = new IncomingRequest("http://example.com")
|
||||||
// Create an empty context to pass to `worker.fetch()`.
|
// Create an empty context to pass to `worker.fetch()`.
|
||||||
const ctx = createExecutionContext();
|
const ctx = createExecutionContext()
|
||||||
const response = await worker.fetch(request, env, ctx);
|
const response = await worker.fetch(request, env, ctx)
|
||||||
// Wait for all `Promise`s passed to `ctx.waitUntil()` to settle before running test assertions
|
// Wait for all `Promise`s passed to `ctx.waitUntil()` to settle before running test assertions
|
||||||
await waitOnExecutionContext(ctx);
|
await waitOnExecutionContext(ctx)
|
||||||
expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`);
|
expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`)
|
||||||
});
|
})
|
||||||
|
|
||||||
it('responds with Hello World! (integration style)', async () => {
|
it("responds with Hello World! (integration style)", async () => {
|
||||||
const response = await SELF.fetch('https://example.com');
|
const response = await SELF.fetch("https://example.com")
|
||||||
expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`);
|
expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"extends": "../tsconfig.json",
|
"extends": "../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"types": [
|
"types": [
|
||||||
"@cloudflare/workers-types/experimental",
|
"@cloudflare/workers-types/experimental",
|
||||||
"@cloudflare/vitest-pool-workers"
|
"@cloudflare/vitest-pool-workers"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": ["./**/*.ts", "../src/env.d.ts"],
|
"include": ["./**/*.ts", "../src/env.d.ts"],
|
||||||
"exclude": []
|
"exclude": []
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,9 @@
|
|||||||
|
|
||||||
/* Language and Environment */
|
/* Language and Environment */
|
||||||
"target": "es2021" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
"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. */,
|
"lib": [
|
||||||
|
"es2021"
|
||||||
|
] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
|
||||||
"jsx": "react" /* Specify what JSX code is generated. */,
|
"jsx": "react" /* Specify what JSX code is generated. */,
|
||||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
|
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config"
|
||||||
|
|
||||||
export default defineWorkersConfig({
|
export default defineWorkersConfig({
|
||||||
test: {
|
test: {
|
||||||
poolOptions: {
|
poolOptions: {
|
||||||
workers: {
|
workers: {
|
||||||
wrangler: { configPath: "./wrangler.toml" },
|
wrangler: { configPath: "./wrangler.toml" },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
3
backend/ai/worker-configuration.d.ts
vendored
3
backend/ai/worker-configuration.d.ts
vendored
@ -1,4 +1,3 @@
|
|||||||
// Generated by Wrangler
|
// Generated by Wrangler
|
||||||
// After adding bindings to `wrangler.toml`, regenerate this interface via `npm run cf-typegen`
|
// After adding bindings to `wrangler.toml`, regenerate this interface via `npm run cf-typegen`
|
||||||
interface Env {
|
interface Env {}
|
||||||
}
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import type { Config } from "drizzle-kit";
|
import type { Config } from "drizzle-kit"
|
||||||
|
|
||||||
export default process.env.LOCAL_DB_PATH
|
export default process.env.LOCAL_DB_PATH
|
||||||
? ({
|
? ({
|
||||||
@ -16,4 +16,4 @@ export default process.env.LOCAL_DB_PATH
|
|||||||
wranglerConfigPath: "wrangler.toml",
|
wranglerConfigPath: "wrangler.toml",
|
||||||
dbName: "d1-sandbox",
|
dbName: "d1-sandbox",
|
||||||
},
|
},
|
||||||
} satisfies Config);
|
} satisfies Config)
|
||||||
|
@ -1,32 +1,32 @@
|
|||||||
{
|
{
|
||||||
"name": "database",
|
"name": "database",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"deploy": "wrangler deploy",
|
"deploy": "wrangler deploy",
|
||||||
"dev": "wrangler dev",
|
"dev": "wrangler dev",
|
||||||
"start": "wrangler dev",
|
"start": "wrangler dev",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"generate": "drizzle-kit generate:sqlite --schema=src/schema.ts",
|
"generate": "drizzle-kit generate:sqlite --schema=src/schema.ts",
|
||||||
"up": "drizzle-kit up:sqlite --schema=src/schema.ts",
|
"up": "drizzle-kit up:sqlite --schema=src/schema.ts",
|
||||||
"db:studio": "cross-env LOCAL_DB_PATH=$(find .wrangler/state/v3/d1/miniflare-D1DatabaseObject -type f -name '*.sqlite' -print -quit) drizzle-kit studio"
|
"db:studio": "cross-env LOCAL_DB_PATH=$(find .wrangler/state/v3/d1/miniflare-D1DatabaseObject -type f -name '*.sqlite' -print -quit) drizzle-kit studio"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/vitest-pool-workers": "^0.1.0",
|
"@cloudflare/vitest-pool-workers": "^0.1.0",
|
||||||
"@cloudflare/workers-types": "^4.20240405.0",
|
"@cloudflare/workers-types": "^4.20240405.0",
|
||||||
"@types/itty-router-extras": "^0.4.3",
|
"@types/itty-router-extras": "^0.4.3",
|
||||||
"drizzle-kit": "^0.20.17",
|
"drizzle-kit": "^0.20.17",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"vitest": "1.3.0",
|
"vitest": "1.3.0",
|
||||||
"wrangler": "^3.0.0"
|
"wrangler": "^3.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@paralleldrive/cuid2": "^2.2.2",
|
"@paralleldrive/cuid2": "^2.2.2",
|
||||||
"better-sqlite3": "^9.5.0",
|
"better-sqlite3": "^9.5.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
"drizzle-orm": "^0.30.8",
|
"drizzle-orm": "^0.30.8",
|
||||||
"itty-router": "^5.0.16",
|
"itty-router": "^5.0.16",
|
||||||
"itty-router-extras": "^0.4.6",
|
"itty-router-extras": "^0.4.6",
|
||||||
"zod": "^3.22.4"
|
"zod": "^3.22.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,11 +1,11 @@
|
|||||||
// import type { DrizzleD1Database } from "drizzle-orm/d1";
|
// import type { DrizzleD1Database } from "drizzle-orm/d1";
|
||||||
import { drizzle } from "drizzle-orm/d1"
|
import { drizzle } from "drizzle-orm/d1"
|
||||||
import { json } from "itty-router-extras"
|
import { json } from "itty-router-extras"
|
||||||
import { ZodError, z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { user, sandbox, usersToSandboxes } from "./schema"
|
|
||||||
import * as schema from "./schema"
|
|
||||||
import { and, eq, sql } from "drizzle-orm"
|
import { and, eq, sql } from "drizzle-orm"
|
||||||
|
import * as schema from "./schema"
|
||||||
|
import { sandbox, user, usersToSandboxes } from "./schema"
|
||||||
|
|
||||||
export interface Env {
|
export interface Env {
|
||||||
DB: D1Database
|
DB: D1Database
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
import { createId } from "@paralleldrive/cuid2"
|
||||||
import { createId } from "@paralleldrive/cuid2";
|
import { relations } from "drizzle-orm"
|
||||||
import { relations, sql } from "drizzle-orm";
|
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"
|
||||||
|
|
||||||
export const user = sqliteTable("user", {
|
export const user = sqliteTable("user", {
|
||||||
id: text("id")
|
id: text("id")
|
||||||
@ -11,14 +11,14 @@ export const user = sqliteTable("user", {
|
|||||||
email: text("email").notNull(),
|
email: text("email").notNull(),
|
||||||
image: text("image"),
|
image: text("image"),
|
||||||
generations: integer("generations").default(0),
|
generations: integer("generations").default(0),
|
||||||
});
|
})
|
||||||
|
|
||||||
export type User = typeof user.$inferSelect;
|
export type User = typeof user.$inferSelect
|
||||||
|
|
||||||
export const userRelations = relations(user, ({ many }) => ({
|
export const userRelations = relations(user, ({ many }) => ({
|
||||||
sandbox: many(sandbox),
|
sandbox: many(sandbox),
|
||||||
usersToSandboxes: many(usersToSandboxes),
|
usersToSandboxes: many(usersToSandboxes),
|
||||||
}));
|
}))
|
||||||
|
|
||||||
export const sandbox = sqliteTable("sandbox", {
|
export const sandbox = sqliteTable("sandbox", {
|
||||||
id: text("id")
|
id: text("id")
|
||||||
@ -32,9 +32,9 @@ export const sandbox = sqliteTable("sandbox", {
|
|||||||
userId: text("user_id")
|
userId: text("user_id")
|
||||||
.notNull()
|
.notNull()
|
||||||
.references(() => user.id),
|
.references(() => user.id),
|
||||||
});
|
})
|
||||||
|
|
||||||
export type Sandbox = typeof sandbox.$inferSelect;
|
export type Sandbox = typeof sandbox.$inferSelect
|
||||||
|
|
||||||
export const sandboxRelations = relations(sandbox, ({ one, many }) => ({
|
export const sandboxRelations = relations(sandbox, ({ one, many }) => ({
|
||||||
author: one(user, {
|
author: one(user, {
|
||||||
@ -42,7 +42,7 @@ export const sandboxRelations = relations(sandbox, ({ one, many }) => ({
|
|||||||
references: [user.id],
|
references: [user.id],
|
||||||
}),
|
}),
|
||||||
usersToSandboxes: many(usersToSandboxes),
|
usersToSandboxes: many(usersToSandboxes),
|
||||||
}));
|
}))
|
||||||
|
|
||||||
export const usersToSandboxes = sqliteTable("users_to_sandboxes", {
|
export const usersToSandboxes = sqliteTable("users_to_sandboxes", {
|
||||||
userId: text("userId")
|
userId: text("userId")
|
||||||
@ -52,15 +52,18 @@ export const usersToSandboxes = sqliteTable("users_to_sandboxes", {
|
|||||||
.notNull()
|
.notNull()
|
||||||
.references(() => sandbox.id),
|
.references(() => sandbox.id),
|
||||||
sharedOn: integer("sharedOn", { mode: "timestamp_ms" }),
|
sharedOn: integer("sharedOn", { mode: "timestamp_ms" }),
|
||||||
});
|
})
|
||||||
|
|
||||||
export const usersToSandboxesRelations = relations(usersToSandboxes, ({ one }) => ({
|
export const usersToSandboxesRelations = relations(
|
||||||
group: one(sandbox, {
|
usersToSandboxes,
|
||||||
fields: [usersToSandboxes.sandboxId],
|
({ one }) => ({
|
||||||
references: [sandbox.id],
|
group: one(sandbox, {
|
||||||
}),
|
fields: [usersToSandboxes.sandboxId],
|
||||||
user: one(user, {
|
references: [sandbox.id],
|
||||||
fields: [usersToSandboxes.userId],
|
}),
|
||||||
references: [user.id],
|
user: one(user, {
|
||||||
}),
|
fields: [usersToSandboxes.userId],
|
||||||
}));
|
references: [user.id],
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
)
|
||||||
|
@ -1,25 +1,30 @@
|
|||||||
// test/index.spec.ts
|
// test/index.spec.ts
|
||||||
import { env, createExecutionContext, waitOnExecutionContext, SELF } from "cloudflare:test";
|
import {
|
||||||
import { describe, it, expect } from "vitest";
|
createExecutionContext,
|
||||||
import worker from "../src/index";
|
env,
|
||||||
|
SELF,
|
||||||
|
waitOnExecutionContext,
|
||||||
|
} from "cloudflare:test"
|
||||||
|
import { describe, expect, it } from "vitest"
|
||||||
|
import worker from "../src/index"
|
||||||
|
|
||||||
// For now, you'll need to do something like this to get a correctly-typed
|
// For now, you'll need to do something like this to get a correctly-typed
|
||||||
// `Request` to pass to `worker.fetch()`.
|
// `Request` to pass to `worker.fetch()`.
|
||||||
const IncomingRequest = Request<unknown, IncomingRequestCfProperties>;
|
const IncomingRequest = Request<unknown, IncomingRequestCfProperties>
|
||||||
|
|
||||||
describe("Hello World worker", () => {
|
describe("Hello World worker", () => {
|
||||||
it("responds with Hello World! (unit style)", async () => {
|
it("responds with Hello World! (unit style)", async () => {
|
||||||
const request = new IncomingRequest("http://example.com");
|
const request = new IncomingRequest("http://example.com")
|
||||||
// Create an empty context to pass to `worker.fetch()`.
|
// Create an empty context to pass to `worker.fetch()`.
|
||||||
const ctx = createExecutionContext();
|
const ctx = createExecutionContext()
|
||||||
const response = await worker.fetch(request, env, ctx);
|
const response = await worker.fetch(request, env, ctx)
|
||||||
// Wait for all `Promise`s passed to `ctx.waitUntil()` to settle before running test assertions
|
// Wait for all `Promise`s passed to `ctx.waitUntil()` to settle before running test assertions
|
||||||
await waitOnExecutionContext(ctx);
|
await waitOnExecutionContext(ctx)
|
||||||
expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`);
|
expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`)
|
||||||
});
|
})
|
||||||
|
|
||||||
it("responds with Hello World! (integration style)", async () => {
|
it("responds with Hello World! (integration style)", async () => {
|
||||||
const response = await SELF.fetch("https://example.com");
|
const response = await SELF.fetch("https://example.com")
|
||||||
expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`);
|
expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"extends": "../tsconfig.json",
|
"extends": "../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"types": [
|
"types": [
|
||||||
"@cloudflare/workers-types/experimental",
|
"@cloudflare/workers-types/experimental",
|
||||||
"@cloudflare/vitest-pool-workers"
|
"@cloudflare/vitest-pool-workers"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": ["./**/*.ts", "../src/env.d.ts"],
|
"include": ["./**/*.ts", "../src/env.d.ts"],
|
||||||
"exclude": []
|
"exclude": []
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,9 @@
|
|||||||
|
|
||||||
/* Language and Environment */
|
/* Language and Environment */
|
||||||
"target": "es2021" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
"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. */,
|
"lib": [
|
||||||
|
"es2021"
|
||||||
|
] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
|
||||||
"jsx": "react" /* Specify what JSX code is generated. */,
|
"jsx": "react" /* Specify what JSX code is generated. */,
|
||||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
|
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config"
|
||||||
|
|
||||||
export default defineWorkersConfig({
|
export default defineWorkersConfig({
|
||||||
test: {
|
test: {
|
||||||
poolOptions: {
|
poolOptions: {
|
||||||
workers: {
|
workers: {
|
||||||
wrangler: { configPath: "./wrangler.toml" },
|
wrangler: { configPath: "./wrangler.toml" },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
@ -1,23 +1,23 @@
|
|||||||
{
|
{
|
||||||
"name": "storage",
|
"name": "storage",
|
||||||
"version": "0.0.0",
|
"version": "0.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"deploy": "wrangler deploy",
|
"deploy": "wrangler deploy",
|
||||||
"dev": "wrangler dev --remote",
|
"dev": "wrangler dev --remote",
|
||||||
"start": "wrangler dev",
|
"start": "wrangler dev",
|
||||||
"test": "vitest",
|
"test": "vitest",
|
||||||
"cf-typegen": "wrangler types"
|
"cf-typegen": "wrangler types"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/vitest-pool-workers": "^0.1.0",
|
"@cloudflare/vitest-pool-workers": "^0.1.0",
|
||||||
"@cloudflare/workers-types": "^4.20240419.0",
|
"@cloudflare/workers-types": "^4.20240419.0",
|
||||||
"typescript": "^5.0.4",
|
"typescript": "^5.0.4",
|
||||||
"vitest": "1.3.0",
|
"vitest": "1.3.0",
|
||||||
"wrangler": "^3.0.0"
|
"wrangler": "^3.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"p-limit": "^6.1.0",
|
"p-limit": "^6.1.0",
|
||||||
"zod": "^3.23.4"
|
"zod": "^3.23.4"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,5 +1,5 @@
|
|||||||
|
import pLimit from "p-limit"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
import pLimit from 'p-limit';
|
|
||||||
|
|
||||||
export interface Env {
|
export interface Env {
|
||||||
R2: R2Bucket
|
R2: R2Bucket
|
||||||
@ -144,20 +144,24 @@ export default {
|
|||||||
const body = await request.json()
|
const body = await request.json()
|
||||||
const { sandboxId, type } = initSchema.parse(body)
|
const { sandboxId, type } = initSchema.parse(body)
|
||||||
|
|
||||||
console.log(`Copying template: ${type}`);
|
console.log(`Copying template: ${type}`)
|
||||||
|
|
||||||
// List all objects under the directory
|
// List all objects under the directory
|
||||||
const { objects } = await env.Templates.list({ prefix: type });
|
const { objects } = await env.Templates.list({ prefix: type })
|
||||||
|
|
||||||
// Copy each object to the new directory with a 5 concurrency limit
|
// Copy each object to the new directory with a 5 concurrency limit
|
||||||
const limit = pLimit(5);
|
const limit = pLimit(5)
|
||||||
await Promise.all(objects.map(({ key }) =>
|
await Promise.all(
|
||||||
limit(async () => {
|
objects.map(({ key }) =>
|
||||||
const destinationKey = key.replace(type, `projects/${sandboxId}`);
|
limit(async () => {
|
||||||
const fileBody = await env.Templates.get(key).then(res => res?.body ?? "");
|
const destinationKey = key.replace(type, `projects/${sandboxId}`)
|
||||||
await env.R2.put(destinationKey, fileBody);
|
const fileBody = await env.Templates.get(key).then(
|
||||||
})
|
(res) => res?.body ?? ""
|
||||||
));
|
)
|
||||||
|
await env.R2.put(destinationKey, fileBody)
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
return success
|
return success
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,25 +1,30 @@
|
|||||||
// test/index.spec.ts
|
// test/index.spec.ts
|
||||||
import { env, createExecutionContext, waitOnExecutionContext, SELF } from 'cloudflare:test';
|
import {
|
||||||
import { describe, it, expect } from 'vitest';
|
createExecutionContext,
|
||||||
import worker from '../src/index';
|
env,
|
||||||
|
SELF,
|
||||||
|
waitOnExecutionContext,
|
||||||
|
} from "cloudflare:test"
|
||||||
|
import { describe, expect, it } from "vitest"
|
||||||
|
import worker from "../src/index"
|
||||||
|
|
||||||
// For now, you'll need to do something like this to get a correctly-typed
|
// For now, you'll need to do something like this to get a correctly-typed
|
||||||
// `Request` to pass to `worker.fetch()`.
|
// `Request` to pass to `worker.fetch()`.
|
||||||
const IncomingRequest = Request<unknown, IncomingRequestCfProperties>;
|
const IncomingRequest = Request<unknown, IncomingRequestCfProperties>
|
||||||
|
|
||||||
describe('Hello World worker', () => {
|
describe("Hello World worker", () => {
|
||||||
it('responds with Hello World! (unit style)', async () => {
|
it("responds with Hello World! (unit style)", async () => {
|
||||||
const request = new IncomingRequest('http://example.com');
|
const request = new IncomingRequest("http://example.com")
|
||||||
// Create an empty context to pass to `worker.fetch()`.
|
// Create an empty context to pass to `worker.fetch()`.
|
||||||
const ctx = createExecutionContext();
|
const ctx = createExecutionContext()
|
||||||
const response = await worker.fetch(request, env, ctx);
|
const response = await worker.fetch(request, env, ctx)
|
||||||
// Wait for all `Promise`s passed to `ctx.waitUntil()` to settle before running test assertions
|
// Wait for all `Promise`s passed to `ctx.waitUntil()` to settle before running test assertions
|
||||||
await waitOnExecutionContext(ctx);
|
await waitOnExecutionContext(ctx)
|
||||||
expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`);
|
expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`)
|
||||||
});
|
})
|
||||||
|
|
||||||
it('responds with Hello World! (integration style)', async () => {
|
it("responds with Hello World! (integration style)", async () => {
|
||||||
const response = await SELF.fetch('https://example.com');
|
const response = await SELF.fetch("https://example.com")
|
||||||
expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`);
|
expect(await response.text()).toMatchInlineSnapshot(`"Hello World!"`)
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
"extends": "../tsconfig.json",
|
"extends": "../tsconfig.json",
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"types": [
|
"types": [
|
||||||
"@cloudflare/workers-types/experimental",
|
"@cloudflare/workers-types/experimental",
|
||||||
"@cloudflare/vitest-pool-workers"
|
"@cloudflare/vitest-pool-workers"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": ["./**/*.ts", "../src/env.d.ts"],
|
"include": ["./**/*.ts", "../src/env.d.ts"],
|
||||||
"exclude": []
|
"exclude": []
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,9 @@
|
|||||||
|
|
||||||
/* Language and Environment */
|
/* Language and Environment */
|
||||||
"target": "es2021" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
|
"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. */,
|
"lib": [
|
||||||
|
"es2021"
|
||||||
|
] /* Specify a set of bundled library declaration files that describe the target runtime environment. */,
|
||||||
"jsx": "react" /* Specify what JSX code is generated. */,
|
"jsx": "react" /* Specify what JSX code is generated. */,
|
||||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config";
|
import { defineWorkersConfig } from "@cloudflare/vitest-pool-workers/config"
|
||||||
|
|
||||||
export default defineWorkersConfig({
|
export default defineWorkersConfig({
|
||||||
test: {
|
test: {
|
||||||
poolOptions: {
|
poolOptions: {
|
||||||
workers: {
|
workers: {
|
||||||
wrangler: { configPath: "./wrangler.toml" },
|
wrangler: { configPath: "./wrangler.toml" },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
})
|
||||||
|
3
backend/storage/worker-configuration.d.ts
vendored
3
backend/storage/worker-configuration.d.ts
vendored
@ -1,4 +1,3 @@
|
|||||||
// Generated by Wrangler
|
// Generated by Wrangler
|
||||||
// After adding bindings to `wrangler.toml`, regenerate this interface via `npm run cf-typegen`
|
// After adding bindings to `wrangler.toml`, regenerate this interface via `npm run cf-typegen`
|
||||||
interface Env {
|
interface Env {}
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user