mirror of
https://github.com/sveltejs/ai-tools.git
synced 2026-07-04 19:45:32 +08:00
Compare commits
1 Commits
function-c
...
wip-docs-w
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
199d57a8e3 |
@@ -1,3 +1,4 @@
|
||||
DATABASE_URL=file:test.db
|
||||
DATABASE_TOKEN=needs_to_be_set_but_it_can_be_anything
|
||||
VOYAGE_API_KEY=your_actual_api_key_here
|
||||
VOYAGE_API_KEY=your_actual_api_key_here
|
||||
GITHUB_WEBHOOK_SECRET=some_secret
|
||||
@@ -65,6 +65,7 @@
|
||||
"@sveltejs/mcp-schema": "workspace:^",
|
||||
"@sveltejs/mcp-server": "workspace:^",
|
||||
"@tmcp/transport-http": "^0.6.3",
|
||||
"tmcp": "^1.14.0"
|
||||
"tmcp": "^1.14.0",
|
||||
"valibot": "^1.1.0"
|
||||
}
|
||||
}
|
||||
|
||||
32
apps/mcp-remote/src/lib/schemas/index.ts
Normal file
32
apps/mcp-remote/src/lib/schemas/index.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import * as v from 'valibot';
|
||||
|
||||
// not the full schema but it contains the information we need
|
||||
export const github_webhook_schema = v.object({
|
||||
action: v.union([v.literal('closed')]),
|
||||
pull_request: v.object({
|
||||
patch_url: v.string(),
|
||||
merged: v.boolean(),
|
||||
user: v.object({
|
||||
login: v.string(),
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
export const github_content_schema = v.object({
|
||||
name: v.string(),
|
||||
path: v.string(),
|
||||
sha: v.string(),
|
||||
size: v.number(),
|
||||
url: v.string(),
|
||||
html_url: v.string(),
|
||||
git_url: v.string(),
|
||||
download_url: v.nullable(v.string()),
|
||||
type: v.literal('file'),
|
||||
content: v.string(),
|
||||
encoding: v.literal('base64'),
|
||||
_links: v.object({
|
||||
self: v.string(),
|
||||
git: v.string(),
|
||||
html: v.string(),
|
||||
}),
|
||||
});
|
||||
54
apps/mcp-remote/src/routes/webhooks/docs/+server.ts
Normal file
54
apps/mcp-remote/src/routes/webhooks/docs/+server.ts
Normal file
@@ -0,0 +1,54 @@
|
||||
import { github_content_schema, github_webhook_schema } from '$lib/schemas/index.js';
|
||||
import * as v from 'valibot';
|
||||
|
||||
export async function POST({ request, fetch }) {
|
||||
const body = await request.json();
|
||||
// TODO add secret validation
|
||||
const validated_pull_request = v.safeParse(github_webhook_schema, body);
|
||||
if (!validated_pull_request.success) {
|
||||
return new Response('Invalid payload', { status: 400 });
|
||||
}
|
||||
|
||||
const { pull_request } = validated_pull_request.output;
|
||||
|
||||
if (!pull_request.merged) {
|
||||
return new Response(null, { status: 204 });
|
||||
}
|
||||
|
||||
const patch = await fetch(pull_request.patch_url);
|
||||
if (!patch.ok) {
|
||||
return new Response('Failed to fetch patch', { status: 500 });
|
||||
}
|
||||
|
||||
const patch_text = await patch.text();
|
||||
const files = [
|
||||
...patch_text.matchAll(
|
||||
/^diff --git\sa\/(?<file>.+?)\sb\/\1\n(?:(?<action>deleted|new)\sfile mode)?/gm,
|
||||
),
|
||||
].map((res) => ({ file: res.groups!.file, action: res.groups?.action ?? 'modified' }));
|
||||
|
||||
for (const file of files) {
|
||||
if (file.action === 'deleted') {
|
||||
// delete path from db
|
||||
continue;
|
||||
}
|
||||
const new_file_content = await fetch(
|
||||
`https://api.github.com/repos/sveltejs/svelte.dev/contents/${file.file}`,
|
||||
);
|
||||
if (!new_file_content.ok) {
|
||||
// push file in queue and try again later?
|
||||
continue;
|
||||
}
|
||||
const new_file_json = await new_file_content.json();
|
||||
const validated_content = v.safeParse(github_content_schema, new_file_json);
|
||||
if (!validated_content.success) {
|
||||
// push file in queue and try again later?
|
||||
continue;
|
||||
}
|
||||
const content = Buffer.from(validated_content.output.content, 'base64').toString('utf-8');
|
||||
// save content and distilled content in the db
|
||||
console.log({ content, file: file.file });
|
||||
}
|
||||
|
||||
return new Response(null, { status: 204 });
|
||||
}
|
||||
@@ -117,36 +117,6 @@ describe('add_autofixers_issues', () => {
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
it('should add a suggestion when calling a function inside an effect', () => {
|
||||
const content = run_autofixers_on_code(`
|
||||
<script>
|
||||
import { fetch_data } from './data.js';
|
||||
$effect(() => {
|
||||
fetch_data();
|
||||
});
|
||||
</script>`);
|
||||
|
||||
expect(content.suggestions.length).toBeGreaterThanOrEqual(1);
|
||||
expect(content.suggestions).toContain(
|
||||
`You are calling the function \`fetch_data\` inside an $effect. Please check if the function is reassigning a stateful variable because that's considered malpractice and check if it could use \`$derived\` instead. Ignore this suggestion if you are sure this function is not assigning any stateful variable or if you can't check if it does.`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should add a suggestion when calling a function inside an effect (with non identifier callee)', () => {
|
||||
const content = run_autofixers_on_code(`
|
||||
<script>
|
||||
import { fetch_data } from './data.js';
|
||||
$effect(() => {
|
||||
fetch_data.fetch();
|
||||
});
|
||||
</script>`);
|
||||
|
||||
expect(content.suggestions.length).toBeGreaterThanOrEqual(1);
|
||||
expect(content.suggestions).toContain(
|
||||
`You are calling a function inside an $effect. Please check if the function is reassigning a stateful variable because that's considered malpractice and check if it could use \`$derived\` instead. Ignore this suggestion if you are sure this function is not assigning any stateful variable or if you can't check if it does.`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
with_possible_inits('($init)', ({ init }) => {
|
||||
|
||||
@@ -1,10 +1,4 @@
|
||||
import type {
|
||||
AssignmentExpression,
|
||||
CallExpression,
|
||||
Identifier,
|
||||
Node,
|
||||
UpdateExpression,
|
||||
} from 'estree';
|
||||
import type { AssignmentExpression, Identifier, Node, UpdateExpression } from 'estree';
|
||||
import type { Autofixer, AutofixerState } from './index.js';
|
||||
import { left_most_id } from '../ast/utils.js';
|
||||
import type { AST } from 'svelte-eslint-parser';
|
||||
@@ -33,9 +27,9 @@ function run_if_in_effect(
|
||||
}
|
||||
}
|
||||
|
||||
function assign_or_update_visitor(
|
||||
function visitor(
|
||||
node: UpdateExpression | AssignmentExpression,
|
||||
{ state, path, next }: Context<Node | AST.SvelteNode, AutofixerState>,
|
||||
{ state, path }: Context<Node | AST.SvelteNode, AutofixerState>,
|
||||
) {
|
||||
run_if_in_effect(path, state, () => {
|
||||
function check_if_stateful_id(id: Identifier) {
|
||||
@@ -64,25 +58,9 @@ function assign_or_update_visitor(
|
||||
}
|
||||
}
|
||||
});
|
||||
next();
|
||||
}
|
||||
|
||||
function call_expression_visitor(
|
||||
node: CallExpression,
|
||||
{ state, path, next }: Context<Node | AST.SvelteNode, AutofixerState>,
|
||||
) {
|
||||
run_if_in_effect(path, state, () => {
|
||||
const function_name =
|
||||
node.callee.type === 'Identifier' ? `the function \`${node.callee.name}\`` : 'a function';
|
||||
state.output.suggestions.push(
|
||||
`You are calling ${function_name} inside an $effect. Please check if the function is reassigning a stateful variable because that's considered malpractice and check if it could use \`$derived\` instead. Ignore this suggestion if you are sure this function is not assigning any stateful variable or if you can't check if it does.`,
|
||||
);
|
||||
});
|
||||
next();
|
||||
}
|
||||
|
||||
export const assign_in_effect: Autofixer = {
|
||||
UpdateExpression: assign_or_update_visitor,
|
||||
AssignmentExpression: assign_or_update_visitor,
|
||||
CallExpression: call_expression_visitor,
|
||||
UpdateExpression: visitor,
|
||||
AssignmentExpression: visitor,
|
||||
};
|
||||
|
||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -62,6 +62,9 @@ importers:
|
||||
tmcp:
|
||||
specifier: ^1.14.0
|
||||
version: 1.14.0(typescript@5.9.2)
|
||||
valibot:
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0(typescript@5.9.2)
|
||||
devDependencies:
|
||||
'@eslint/compat':
|
||||
specifier: ^1.3.2
|
||||
|
||||
Reference in New Issue
Block a user