alepha 0.13.8 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api/audits/index.d.ts +2 -1
- package/dist/api/audits/index.d.ts.map +1 -0
- package/dist/api/files/index.d.ts +2 -1
- package/dist/api/files/index.d.ts.map +1 -0
- package/dist/api/jobs/index.d.ts +158 -157
- package/dist/api/jobs/index.d.ts.map +1 -0
- package/dist/api/notifications/index.d.ts.map +1 -0
- package/dist/api/parameters/index.d.ts +4 -4
- package/dist/api/parameters/index.d.ts.map +1 -0
- package/dist/api/users/index.d.ts +132 -131
- package/dist/api/users/index.d.ts.map +1 -0
- package/dist/api/verifications/index.d.ts.map +1 -0
- package/dist/batch/index.d.ts.map +1 -0
- package/dist/bucket/index.d.ts.map +1 -0
- package/dist/cache/core/index.d.ts.map +1 -0
- package/dist/cache/redis/index.d.ts.map +1 -0
- package/dist/cli/index.d.ts +44 -32
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +380 -109
- package/dist/cli/index.js.map +1 -1
- package/dist/command/index.d.ts +11 -1
- package/dist/command/index.d.ts.map +1 -0
- package/dist/command/index.js +45 -6
- package/dist/command/index.js.map +1 -1
- package/dist/core/index.browser.js +1334 -1318
- package/dist/core/index.browser.js.map +1 -1
- package/dist/core/index.d.ts +75 -71
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +1337 -1321
- package/dist/core/index.js.map +1 -1
- package/dist/core/index.native.js +1337 -1321
- package/dist/core/index.native.js.map +1 -1
- package/dist/datetime/index.d.ts.map +1 -0
- package/dist/email/index.d.ts.map +1 -0
- package/dist/fake/index.d.ts.map +1 -0
- package/dist/file/index.d.ts.map +1 -0
- package/dist/lock/core/index.d.ts.map +1 -0
- package/dist/lock/redis/index.d.ts.map +1 -0
- package/dist/logger/index.d.ts +1 -0
- package/dist/logger/index.d.ts.map +1 -0
- package/dist/mcp/index.d.ts +820 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +978 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/orm/index.d.ts +180 -107
- package/dist/orm/index.d.ts.map +1 -0
- package/dist/orm/index.js +260 -174
- package/dist/orm/index.js.map +1 -1
- package/dist/queue/core/index.d.ts +4 -4
- package/dist/queue/core/index.d.ts.map +1 -0
- package/dist/queue/redis/index.d.ts.map +1 -0
- package/dist/redis/index.d.ts.map +1 -0
- package/dist/retry/index.d.ts.map +1 -0
- package/dist/router/index.d.ts.map +1 -0
- package/dist/scheduler/index.d.ts.map +1 -0
- package/dist/security/index.d.ts.map +1 -0
- package/dist/server/auth/index.d.ts +155 -155
- package/dist/server/auth/index.d.ts.map +1 -0
- package/dist/server/cache/index.d.ts.map +1 -0
- package/dist/server/compress/index.d.ts.map +1 -0
- package/dist/server/cookies/index.d.ts.map +1 -0
- package/dist/server/core/index.d.ts.map +1 -0
- package/dist/server/cors/index.d.ts.map +1 -0
- package/dist/server/health/index.d.ts.map +1 -0
- package/dist/server/helmet/index.d.ts.map +1 -0
- package/dist/server/links/index.d.ts +33 -33
- package/dist/server/links/index.d.ts.map +1 -0
- package/dist/server/metrics/index.d.ts.map +1 -0
- package/dist/server/multipart/index.d.ts.map +1 -0
- package/dist/server/proxy/index.d.ts.map +1 -0
- package/dist/server/rate-limit/index.d.ts.map +1 -0
- package/dist/server/security/index.d.ts +9 -9
- package/dist/server/security/index.d.ts.map +1 -0
- package/dist/server/static/index.d.ts.map +1 -0
- package/dist/server/swagger/index.d.ts.map +1 -0
- package/dist/sms/index.d.ts.map +1 -0
- package/dist/thread/index.d.ts.map +1 -0
- package/dist/topic/core/index.d.ts.map +1 -0
- package/dist/topic/redis/index.d.ts.map +1 -0
- package/dist/vite/index.d.ts +10 -2
- package/dist/vite/index.d.ts.map +1 -0
- package/dist/vite/index.js +36 -14
- package/dist/vite/index.js.map +1 -1
- package/dist/websocket/index.d.ts.map +1 -0
- package/package.json +9 -4
- package/src/cli/apps/AlephaCli.ts +2 -0
- package/src/cli/apps/AlephaPackageBuilderCli.ts +12 -8
- package/src/cli/assets/mainTs.ts +9 -10
- package/src/cli/commands/ChangelogCommands.ts +389 -0
- package/src/cli/commands/DrizzleCommands.ts +204 -4
- package/src/cli/commands/ViteCommands.ts +26 -16
- package/src/cli/services/AlephaCliUtils.ts +23 -150
- package/src/command/providers/CliProvider.ts +76 -5
- package/src/core/providers/SchemaValidator.ts +23 -1
- package/src/mcp/errors/McpError.ts +72 -0
- package/src/mcp/helpers/jsonrpc.ts +163 -0
- package/src/mcp/index.ts +132 -0
- package/src/mcp/interfaces/McpTypes.ts +248 -0
- package/src/mcp/primitives/$prompt.ts +188 -0
- package/src/mcp/primitives/$resource.ts +171 -0
- package/src/mcp/primitives/$tool.ts +285 -0
- package/src/mcp/providers/McpServerProvider.ts +382 -0
- package/src/mcp/transports/SseMcpTransport.ts +172 -0
- package/src/mcp/transports/StdioMcpTransport.ts +126 -0
- package/src/orm/index.ts +12 -0
- package/src/orm/providers/drivers/CloudflareD1Provider.ts +164 -0
- package/src/orm/providers/drivers/NodeSqliteProvider.ts +3 -1
- package/src/vite/plugins/viteAlephaBuild.ts +8 -2
- package/src/vite/plugins/viteAlephaDev.ts +6 -2
- package/src/vite/tasks/buildServer.ts +1 -1
- package/src/vite/tasks/generateCloudflare.ts +43 -15
- package/src/vite/tasks/runAlepha.ts +1 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { access, readFile, unlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
2
3
|
import { join } from "node:path";
|
|
3
4
|
import { $env, $inject, OPTIONS, t } from "alepha";
|
|
4
5
|
import { $command } from "alepha/command";
|
|
@@ -13,8 +14,8 @@ import {
|
|
|
13
14
|
generateSitemap,
|
|
14
15
|
generateVercel,
|
|
15
16
|
prerenderPages,
|
|
16
|
-
type ViteAlephaBuildOptions,
|
|
17
17
|
} from "alepha/vite";
|
|
18
|
+
import type * as Vite from "vite";
|
|
18
19
|
import { AlephaCliUtils } from "../services/AlephaCliUtils.ts";
|
|
19
20
|
|
|
20
21
|
export class ViteCommands {
|
|
@@ -130,6 +131,7 @@ export class ViteCommands {
|
|
|
130
131
|
handler: async ({ flags, args, run, root }) => {
|
|
131
132
|
// Tell viteAlephaBuild plugin to skip - CLI handles all tasks
|
|
132
133
|
process.env.ALEPHA_BUILD_MODE = "cli";
|
|
134
|
+
process.env.NODE_ENV = "production";
|
|
133
135
|
|
|
134
136
|
if (await this.utils.hasExpo(root)) {
|
|
135
137
|
// will coming soon
|
|
@@ -156,11 +158,14 @@ export class ViteCommands {
|
|
|
156
158
|
alias: "clean dist",
|
|
157
159
|
});
|
|
158
160
|
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
161
|
+
const vite: typeof Vite = createRequire(import.meta.url)("vite");
|
|
162
|
+
const config = await vite.resolveConfig({}, "build", "production");
|
|
163
|
+
const alephaPlugin: any = config.plugins.find(
|
|
164
|
+
(it) => it.name === "alepha:build",
|
|
165
|
+
);
|
|
166
|
+
const viteAlephaBuildOptions = alephaPlugin?.[OPTIONS] || {};
|
|
167
|
+
|
|
168
|
+
await this.utils.loadEnvFile(root, [".env", ".env.production"]);
|
|
164
169
|
|
|
165
170
|
const stats = flags.stats ?? viteAlephaBuildOptions.stats ?? false;
|
|
166
171
|
const hasServer = viteAlephaBuildOptions.serverEntry !== false;
|
|
@@ -173,6 +178,12 @@ export class ViteCommands {
|
|
|
173
178
|
// No index.html
|
|
174
179
|
}
|
|
175
180
|
|
|
181
|
+
// Extract client options
|
|
182
|
+
const clientOptions =
|
|
183
|
+
typeof viteAlephaBuildOptions.client === "object"
|
|
184
|
+
? viteAlephaBuildOptions.client
|
|
185
|
+
: {};
|
|
186
|
+
|
|
176
187
|
// Build client
|
|
177
188
|
if (hasClient) {
|
|
178
189
|
await run({
|
|
@@ -182,6 +193,7 @@ export class ViteCommands {
|
|
|
182
193
|
silent: true,
|
|
183
194
|
dist: `${distDir}/${clientDir}`,
|
|
184
195
|
stats,
|
|
196
|
+
precompress: clientOptions.precompress,
|
|
185
197
|
}),
|
|
186
198
|
});
|
|
187
199
|
}
|
|
@@ -224,11 +236,7 @@ export class ViteCommands {
|
|
|
224
236
|
|
|
225
237
|
if (hasClient) {
|
|
226
238
|
// Generate sitemap
|
|
227
|
-
const sitemapBaseUrl =
|
|
228
|
-
flags.sitemap ??
|
|
229
|
-
(typeof viteAlephaBuildOptions.client === "object"
|
|
230
|
-
? viteAlephaBuildOptions.client.sitemap?.hostname
|
|
231
|
-
: undefined);
|
|
239
|
+
const sitemapBaseUrl = flags.sitemap ?? clientOptions.sitemap?.hostname;
|
|
232
240
|
|
|
233
241
|
if (sitemapBaseUrl) {
|
|
234
242
|
await run({
|
|
@@ -246,11 +254,7 @@ export class ViteCommands {
|
|
|
246
254
|
}
|
|
247
255
|
|
|
248
256
|
// Pre-render static pages
|
|
249
|
-
const shouldPrerender =
|
|
250
|
-
flags.prerender ??
|
|
251
|
-
(typeof viteAlephaBuildOptions.client === "object"
|
|
252
|
-
? viteAlephaBuildOptions.client.prerender
|
|
253
|
-
: false);
|
|
257
|
+
const shouldPrerender = flags.prerender ?? clientOptions.prerender;
|
|
254
258
|
|
|
255
259
|
if (shouldPrerender) {
|
|
256
260
|
await run({
|
|
@@ -259,6 +263,7 @@ export class ViteCommands {
|
|
|
259
263
|
await prerenderPages({
|
|
260
264
|
dist: `${distDir}/${clientDir}`,
|
|
261
265
|
entry: `${distDir}/index.js`,
|
|
266
|
+
compress: clientOptions.precompress,
|
|
262
267
|
});
|
|
263
268
|
},
|
|
264
269
|
});
|
|
@@ -283,11 +288,16 @@ export class ViteCommands {
|
|
|
283
288
|
}
|
|
284
289
|
|
|
285
290
|
if (flags.cloudflare || viteAlephaBuildOptions.cloudflare) {
|
|
291
|
+
const config =
|
|
292
|
+
typeof viteAlephaBuildOptions.cloudflare === "boolean"
|
|
293
|
+
? {}
|
|
294
|
+
: viteAlephaBuildOptions.cloudflare;
|
|
286
295
|
await run({
|
|
287
296
|
name: "add Cloudflare config",
|
|
288
297
|
handler: () =>
|
|
289
298
|
generateCloudflare({
|
|
290
299
|
distDir,
|
|
300
|
+
config,
|
|
291
301
|
}),
|
|
292
302
|
});
|
|
293
303
|
}
|
|
@@ -5,7 +5,6 @@ import { $inject, Alepha, AlephaError } from "alepha";
|
|
|
5
5
|
import type { RunnerMethod } from "alepha/command";
|
|
6
6
|
import { FileSystemProvider } from "alepha/file";
|
|
7
7
|
import { $logger } from "alepha/logger";
|
|
8
|
-
import type { DrizzleKitProvider, RepositoryProvider } from "alepha/orm";
|
|
9
8
|
import { boot } from "alepha/vite";
|
|
10
9
|
import { tsImport } from "tsx/esm/api";
|
|
11
10
|
import { appRouterTs } from "../assets/appRouterTs.ts";
|
|
@@ -495,159 +494,33 @@ ${models.map((it: string) => `export const ${it} = models["${it}"];`).join("\n")
|
|
|
495
494
|
}
|
|
496
495
|
|
|
497
496
|
/**
|
|
498
|
-
*
|
|
497
|
+
* Load environment variables from a .env file.
|
|
499
498
|
*
|
|
500
|
-
*
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
* @param options - Configuration options including kit, provider info, and paths
|
|
504
|
-
* @returns Path to the generated drizzle.config.js file
|
|
499
|
+
* Reads the .env file in the specified root directory and sets
|
|
500
|
+
* the environment variables in process.env.
|
|
505
501
|
*/
|
|
506
|
-
public async
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
);
|
|
527
|
-
|
|
528
|
-
const config: Record<string, any> = {
|
|
529
|
-
schema: entitiesJsPath,
|
|
530
|
-
out: `./migrations/${options.providerName}`,
|
|
531
|
-
dialect: options.dialect,
|
|
532
|
-
dbCredentials: {
|
|
533
|
-
url: options.providerUrl,
|
|
534
|
-
},
|
|
535
|
-
};
|
|
536
|
-
|
|
537
|
-
if (options.dialect === "sqlite") {
|
|
538
|
-
let url = options.providerUrl;
|
|
539
|
-
url = url.replace("sqlite://", "").replace("file://", "");
|
|
540
|
-
url = join(options.rootDir, url);
|
|
541
|
-
|
|
542
|
-
config.dbCredentials = {
|
|
543
|
-
url,
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
|
|
547
|
-
if (options.providerName === "pglite") {
|
|
548
|
-
config.driver = "pglite";
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
const drizzleConfigJs = `export default ${JSON.stringify(config, null, 2)}`;
|
|
552
|
-
|
|
553
|
-
return await this.writeConfigFile(
|
|
554
|
-
"drizzle.config.js",
|
|
555
|
-
drizzleConfigJs,
|
|
556
|
-
options.rootDir,
|
|
557
|
-
);
|
|
558
|
-
}
|
|
559
|
-
|
|
560
|
-
public async loadEnvFile(root: string): Promise<void> {
|
|
561
|
-
const envPath = join(root, ".env");
|
|
562
|
-
try {
|
|
563
|
-
const envContent = await readFile(envPath, "utf8");
|
|
564
|
-
const lines = envContent.split("\n");
|
|
565
|
-
for (const line of lines) {
|
|
566
|
-
const [key, ...rest] = line.split("=");
|
|
567
|
-
if (key) {
|
|
568
|
-
const value = rest.join("=");
|
|
569
|
-
process.env[key.trim()] = value.trim();
|
|
502
|
+
public async loadEnvFile(
|
|
503
|
+
root: string,
|
|
504
|
+
files: string[] = [".env"],
|
|
505
|
+
): Promise<void> {
|
|
506
|
+
for (const it of files) {
|
|
507
|
+
for (const file of [it, `${it}.local`]) {
|
|
508
|
+
const envPath = join(root, file);
|
|
509
|
+
try {
|
|
510
|
+
const envContent = await readFile(envPath, "utf8");
|
|
511
|
+
const lines = envContent.split("\n");
|
|
512
|
+
for (const line of lines) {
|
|
513
|
+
const [key, ...rest] = line.split("=");
|
|
514
|
+
if (key) {
|
|
515
|
+
const value = rest.join("=");
|
|
516
|
+
process.env[key.trim()] = value.trim();
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
this.log.debug(`Loaded environment variables from ${envPath}`);
|
|
520
|
+
} catch {
|
|
521
|
+
this.log.debug(`No ${file} file found at ${envPath}, skipping load.`);
|
|
570
522
|
}
|
|
571
523
|
}
|
|
572
|
-
this.log.debug(`Loaded environment variables from ${envPath}`);
|
|
573
|
-
} catch {
|
|
574
|
-
this.log.debug(`No .env file found at ${envPath}, skipping load.`);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
/**
|
|
579
|
-
* Run a drizzle-kit command for all database providers in an Alepha instance.
|
|
580
|
-
*
|
|
581
|
-
* Iterates through all repository providers, prepares Drizzle config for each,
|
|
582
|
-
* and executes the specified drizzle-kit command.
|
|
583
|
-
*
|
|
584
|
-
* @param options - Configuration including command to run, flags, and logging
|
|
585
|
-
*/
|
|
586
|
-
public async runDrizzleKitCommand(options: {
|
|
587
|
-
root: string;
|
|
588
|
-
args?: string;
|
|
589
|
-
command: string;
|
|
590
|
-
commandFlags?: string;
|
|
591
|
-
provider?: string;
|
|
592
|
-
logMessage: (providerName: string, dialect: string) => string;
|
|
593
|
-
}): Promise<void> {
|
|
594
|
-
const rootDir = options.root;
|
|
595
|
-
|
|
596
|
-
await this.loadEnvFile(rootDir);
|
|
597
|
-
|
|
598
|
-
this.log.debug(`Using project root: ${rootDir}`);
|
|
599
|
-
|
|
600
|
-
const { alepha, entry } = await this.loadAlephaFromServerEntryFile(
|
|
601
|
-
rootDir,
|
|
602
|
-
options.args,
|
|
603
|
-
);
|
|
604
|
-
|
|
605
|
-
const drizzleKitProvider =
|
|
606
|
-
alepha.inject<DrizzleKitProvider>("DrizzleKitProvider");
|
|
607
|
-
const repositoryProvider =
|
|
608
|
-
alepha.inject<RepositoryProvider>("RepositoryProvider");
|
|
609
|
-
const accepted = new Set<string>([]);
|
|
610
|
-
|
|
611
|
-
for (const primitive of repositoryProvider.getRepositories()) {
|
|
612
|
-
const provider = primitive.provider;
|
|
613
|
-
const providerName = provider.name;
|
|
614
|
-
const dialect = provider.dialect;
|
|
615
|
-
|
|
616
|
-
if (accepted.has(providerName)) {
|
|
617
|
-
continue;
|
|
618
|
-
}
|
|
619
|
-
accepted.add(providerName);
|
|
620
|
-
|
|
621
|
-
// Skip if provider filter is set and doesn't match
|
|
622
|
-
if (options.provider && options.provider !== providerName) {
|
|
623
|
-
this.log.debug(
|
|
624
|
-
`Skipping provider '${providerName}' (filter: ${options.provider})`,
|
|
625
|
-
);
|
|
626
|
-
continue;
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
this.log.info("");
|
|
630
|
-
this.log.info(options.logMessage(providerName, dialect));
|
|
631
|
-
|
|
632
|
-
const drizzleConfigJsPath = await this.prepareDrizzleConfig({
|
|
633
|
-
kit: drizzleKitProvider,
|
|
634
|
-
provider,
|
|
635
|
-
providerName,
|
|
636
|
-
providerUrl: provider.url,
|
|
637
|
-
dialect,
|
|
638
|
-
entry,
|
|
639
|
-
rootDir,
|
|
640
|
-
});
|
|
641
|
-
|
|
642
|
-
const flags = options.commandFlags ? ` ${options.commandFlags}` : "";
|
|
643
|
-
await this.exec(
|
|
644
|
-
`drizzle-kit ${options.command} --config=${drizzleConfigJsPath}${flags}`,
|
|
645
|
-
{
|
|
646
|
-
env: {
|
|
647
|
-
NODE_OPTIONS: "--import tsx",
|
|
648
|
-
},
|
|
649
|
-
},
|
|
650
|
-
);
|
|
651
524
|
}
|
|
652
525
|
}
|
|
653
526
|
|
|
@@ -146,6 +146,7 @@ export class CliProvider {
|
|
|
146
146
|
argv,
|
|
147
147
|
command.options.args,
|
|
148
148
|
command.name === "",
|
|
149
|
+
command.flags,
|
|
149
150
|
);
|
|
150
151
|
|
|
151
152
|
await this.alepha.context.run(async () => {
|
|
@@ -297,9 +298,12 @@ export class CliProvider {
|
|
|
297
298
|
): Record<string, any> {
|
|
298
299
|
const result: Record<string, any> = {};
|
|
299
300
|
|
|
300
|
-
for (
|
|
301
|
+
for (let i = 0; i < argv.length; i++) {
|
|
302
|
+
const arg = argv[i];
|
|
303
|
+
if (!arg.startsWith("-")) continue;
|
|
304
|
+
|
|
301
305
|
const [rawKey, ...valueParts] = arg.replace(/^-{1,2}/, "").split("=");
|
|
302
|
-
|
|
306
|
+
let value = valueParts.join("=");
|
|
303
307
|
|
|
304
308
|
const def = flagDefs.find((d) => d.aliases.includes(rawKey));
|
|
305
309
|
if (!def) continue;
|
|
@@ -307,6 +311,7 @@ export class CliProvider {
|
|
|
307
311
|
if (t.schema.isBoolean(def.schema)) {
|
|
308
312
|
result[def.key] = true;
|
|
309
313
|
} else if (value) {
|
|
314
|
+
// Value provided via --flag=value syntax
|
|
310
315
|
try {
|
|
311
316
|
if (t.schema.isObject(def.schema) || t.schema.isArray(def.schema)) {
|
|
312
317
|
result[def.key] = JSON.parse(value);
|
|
@@ -317,24 +322,90 @@ export class CliProvider {
|
|
|
317
322
|
throw new CommandError(`Invalid JSON value for flag --${rawKey}`);
|
|
318
323
|
}
|
|
319
324
|
} else {
|
|
320
|
-
|
|
325
|
+
// Check for space-separated value: --flag value
|
|
326
|
+
const nextArg = argv[i + 1];
|
|
327
|
+
if (nextArg && !nextArg.startsWith("-")) {
|
|
328
|
+
value = nextArg;
|
|
329
|
+
try {
|
|
330
|
+
if (t.schema.isObject(def.schema) || t.schema.isArray(def.schema)) {
|
|
331
|
+
result[def.key] = JSON.parse(value);
|
|
332
|
+
} else {
|
|
333
|
+
result[def.key] = value;
|
|
334
|
+
}
|
|
335
|
+
} catch {
|
|
336
|
+
throw new CommandError(`Invalid JSON value for flag --${rawKey}`);
|
|
337
|
+
}
|
|
338
|
+
} else {
|
|
339
|
+
throw new CommandError(`Flag --${rawKey} requires a value.`);
|
|
340
|
+
}
|
|
321
341
|
}
|
|
322
342
|
}
|
|
323
343
|
|
|
324
344
|
return result;
|
|
325
345
|
}
|
|
326
346
|
|
|
347
|
+
/**
|
|
348
|
+
* Get indices of argv elements that are consumed by flags (including space-separated values).
|
|
349
|
+
*/
|
|
350
|
+
protected getFlagConsumedIndices(
|
|
351
|
+
argv: string[],
|
|
352
|
+
flagDefs: { key: string; aliases: string[]; schema: TSchema }[],
|
|
353
|
+
): Set<number> {
|
|
354
|
+
const consumed = new Set<number>();
|
|
355
|
+
|
|
356
|
+
for (let i = 0; i < argv.length; i++) {
|
|
357
|
+
const arg = argv[i];
|
|
358
|
+
if (!arg.startsWith("-")) continue;
|
|
359
|
+
|
|
360
|
+
consumed.add(i);
|
|
361
|
+
|
|
362
|
+
const [rawKey, ...valueParts] = arg.replace(/^-{1,2}/, "").split("=");
|
|
363
|
+
const hasEqualValue = valueParts.length > 0;
|
|
364
|
+
|
|
365
|
+
const def = flagDefs.find((d) => d.aliases.includes(rawKey));
|
|
366
|
+
if (!def) continue;
|
|
367
|
+
|
|
368
|
+
// If not a boolean flag and no = value, the next arg is consumed as the value
|
|
369
|
+
if (!t.schema.isBoolean(def.schema) && !hasEqualValue) {
|
|
370
|
+
const nextArg = argv[i + 1];
|
|
371
|
+
if (nextArg && !nextArg.startsWith("-")) {
|
|
372
|
+
consumed.add(i + 1);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return consumed;
|
|
378
|
+
}
|
|
379
|
+
|
|
327
380
|
protected parseCommandArgs(
|
|
328
381
|
argv: string[],
|
|
329
382
|
schema?: TSchema,
|
|
330
383
|
isRootCommand = false,
|
|
384
|
+
flagSchema?: TObject,
|
|
331
385
|
): any {
|
|
332
386
|
if (!schema) {
|
|
333
387
|
return undefined;
|
|
334
388
|
}
|
|
335
389
|
|
|
336
|
-
//
|
|
337
|
-
const
|
|
390
|
+
// Get indices consumed by flags (including space-separated values)
|
|
391
|
+
const flagDefs = flagSchema
|
|
392
|
+
? Object.entries(flagSchema.properties).map(([key, value]) => ({
|
|
393
|
+
key,
|
|
394
|
+
aliases: [
|
|
395
|
+
key,
|
|
396
|
+
...((value as any).aliases ??
|
|
397
|
+
((value as any).alias ? [(value as any).alias] : undefined) ??
|
|
398
|
+
[]),
|
|
399
|
+
],
|
|
400
|
+
schema: value as TSchema,
|
|
401
|
+
}))
|
|
402
|
+
: [];
|
|
403
|
+
const consumedIndices = this.getFlagConsumedIndices(argv, flagDefs);
|
|
404
|
+
|
|
405
|
+
// Extract positional arguments (non-flag arguments that aren't consumed as flag values)
|
|
406
|
+
const positionalArgs = argv.filter(
|
|
407
|
+
(arg, idx) => !arg.startsWith("-") && !consumedIndices.has(idx),
|
|
408
|
+
);
|
|
338
409
|
// For root commands, there's no command name to remove; otherwise slice off the command name
|
|
339
410
|
const argsOnly = isRootCommand ? positionalArgs : positionalArgs.slice(1);
|
|
340
411
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { TSchema } from "typebox";
|
|
2
2
|
import { Compile, type Validator } from "typebox/compile";
|
|
3
3
|
import { TypeBoxError } from "../errors/TypeBoxError.ts";
|
|
4
|
-
import
|
|
4
|
+
import { $hook } from "../primitives/$hook.ts";
|
|
5
|
+
import { type Static, t, Value } from "./TypeProvider.ts";
|
|
5
6
|
|
|
6
7
|
export class SchemaValidator {
|
|
7
8
|
protected cache = new Map<TSchema, Validator>();
|
|
9
|
+
protected useEval: boolean = true;
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Validate the value against the provided schema.
|
|
@@ -23,6 +25,10 @@ export class SchemaValidator {
|
|
|
23
25
|
});
|
|
24
26
|
|
|
25
27
|
try {
|
|
28
|
+
//
|
|
29
|
+
if (!this.useEval) {
|
|
30
|
+
return Value.Parse(schema, newValue);
|
|
31
|
+
}
|
|
26
32
|
return this.getValidator(schema).Parse(newValue) as Static<T>;
|
|
27
33
|
} catch (error: any) {
|
|
28
34
|
if (error.cause?.errors?.[0]) {
|
|
@@ -130,6 +136,22 @@ export class SchemaValidator {
|
|
|
130
136
|
}
|
|
131
137
|
return false;
|
|
132
138
|
};
|
|
139
|
+
|
|
140
|
+
protected onConfigure = $hook({
|
|
141
|
+
on: "configure",
|
|
142
|
+
handler: () => {
|
|
143
|
+
this.useEval = this.canEval();
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
protected canEval(): boolean {
|
|
148
|
+
try {
|
|
149
|
+
Compile(t.object({ test: t.string() })).Parse({ test: "value" });
|
|
150
|
+
return true;
|
|
151
|
+
} catch {
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
133
155
|
}
|
|
134
156
|
|
|
135
157
|
export interface ValidateOptions {
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { JsonRpcErrorCodes } from "../helpers/jsonrpc.ts";
|
|
2
|
+
|
|
3
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
export class McpError extends Error {
|
|
6
|
+
name = "McpError";
|
|
7
|
+
code: number;
|
|
8
|
+
|
|
9
|
+
constructor(
|
|
10
|
+
message: string,
|
|
11
|
+
code: number = JsonRpcErrorCodes.INTERNAL_ERROR,
|
|
12
|
+
) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.code = code;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
19
|
+
|
|
20
|
+
export class McpMethodNotFoundError extends McpError {
|
|
21
|
+
name = "McpMethodNotFoundError";
|
|
22
|
+
|
|
23
|
+
constructor(method: string) {
|
|
24
|
+
super(`Method not found: ${method}`, JsonRpcErrorCodes.METHOD_NOT_FOUND);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
export class McpToolNotFoundError extends McpError {
|
|
31
|
+
name = "McpToolNotFoundError";
|
|
32
|
+
tool: string;
|
|
33
|
+
|
|
34
|
+
constructor(tool: string) {
|
|
35
|
+
super(`Tool not found: ${tool}`, JsonRpcErrorCodes.METHOD_NOT_FOUND);
|
|
36
|
+
this.tool = tool;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
export class McpResourceNotFoundError extends McpError {
|
|
43
|
+
name = "McpResourceNotFoundError";
|
|
44
|
+
uri: string;
|
|
45
|
+
|
|
46
|
+
constructor(uri: string) {
|
|
47
|
+
super(`Resource not found: ${uri}`, JsonRpcErrorCodes.METHOD_NOT_FOUND);
|
|
48
|
+
this.uri = uri;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
export class McpPromptNotFoundError extends McpError {
|
|
55
|
+
name = "McpPromptNotFoundError";
|
|
56
|
+
prompt: string;
|
|
57
|
+
|
|
58
|
+
constructor(prompt: string) {
|
|
59
|
+
super(`Prompt not found: ${prompt}`, JsonRpcErrorCodes.METHOD_NOT_FOUND);
|
|
60
|
+
this.prompt = prompt;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ---------------------------------------------------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
export class McpInvalidParamsError extends McpError {
|
|
67
|
+
name = "McpInvalidParamsError";
|
|
68
|
+
|
|
69
|
+
constructor(message: string) {
|
|
70
|
+
super(message, JsonRpcErrorCodes.INVALID_PARAMS);
|
|
71
|
+
}
|
|
72
|
+
}
|