@vurb/core 3.12.7 → 3.13.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.
@@ -4,98 +4,98 @@
4
4
  */
5
5
  /** Generate `vitest.config.ts` */
6
6
  export function vitestConfig() {
7
- return `import { defineConfig } from 'vitest/config';
8
-
9
- export default defineConfig({
10
- test: {
11
- include: ['tests/**/*.test.ts'],
12
- },
13
- });
7
+ return `import { defineConfig } from 'vitest/config';
8
+
9
+ export default defineConfig({
10
+ test: {
11
+ include: ['tests/**/*.test.ts'],
12
+ },
13
+ });
14
14
  `;
15
15
  }
16
16
  /** Generate `tests/setup.ts` — Test infrastructure */
17
17
  export function testSetupTs() {
18
- return `/**
19
- * Test Setup — In-Memory MVA Emulator
20
- *
21
- * Creates a VurbTester that runs the full pipeline
22
- * (Zod → Middleware → Handler → Egress Firewall)
23
- * without any network transport.
24
- *
25
- * 2ms per test. $0.00 in tokens. Zero servers.
26
- */
27
- import { fileURLToPath } from 'node:url';
28
- import { createVurbTester } from '@vurb/testing';
29
- import { autoDiscover } from '@vurb/core';
30
- import { f } from '../src/vurb.js';
31
-
32
- const registry = f.registry();
33
- await autoDiscover(registry, fileURLToPath(new URL('../src/tools', import.meta.url)));
34
-
35
- export const tester = createVurbTester(registry, {
36
- contextFactory: () => ({
37
- role: 'ADMIN' as const,
38
- tenantId: 'test-tenant',
39
- }),
40
- });
18
+ return `/**
19
+ * Test Setup — In-Memory MVA Emulator
20
+ *
21
+ * Creates a VurbTester that runs the full pipeline
22
+ * (Zod → Middleware → Handler → Egress Firewall)
23
+ * without any network transport.
24
+ *
25
+ * 2ms per test. $0.00 in tokens. Zero servers.
26
+ */
27
+ import { fileURLToPath } from 'node:url';
28
+ import { createVurbTester } from '@vurb/testing';
29
+ import { autoDiscover } from '@vurb/core';
30
+ import { f } from '../src/vurb.js';
31
+
32
+ const registry = f.registry();
33
+ await autoDiscover(registry, fileURLToPath(new URL('../src/tools', import.meta.url)));
34
+
35
+ export const tester = createVurbTester(registry, {
36
+ contextFactory: () => ({
37
+ role: 'ADMIN' as const,
38
+ tenantId: 'test-tenant',
39
+ }),
40
+ });
41
41
  `;
42
42
  }
43
43
  /** Generate `tests/system.test.ts` — Egress Firewall + RBAC tests */
44
44
  export function systemTestTs() {
45
- return `/**
46
- * System Tools — Egress Firewall & RBAC Tests
47
- *
48
- * Proves that:
49
- * 1. The Presenter strips undeclared fields (SOC2 CC6.1)
50
- * 2. RBAC middleware blocks GUEST access (SOC2 CC6.3)
51
- * 3. System rules from .describe() are injected
52
- */
53
- import { describe, it, expect } from 'vitest';
54
- import { tester } from './setup.js';
55
-
56
- describe('System Tools', () => {
57
- describe('Egress Firewall', () => {
58
- it('should return validated health data through the Presenter', async () => {
59
- const result = await tester.callAction('system', 'health');
60
-
61
- expect(result.isError).toBe(false);
62
- expect(result.data).toHaveProperty('status');
63
- expect(result.data).toHaveProperty('uptime');
64
- expect(result.data).toHaveProperty('version');
65
- expect(result.data).toHaveProperty('timestamp');
66
- });
67
-
68
- it('should strip undeclared fields (tenant must NOT leak)', async () => {
69
- const result = await tester.callAction('system', 'health');
70
-
71
- expect(result.isError).toBe(false);
72
- // The handler returns 'tenant' but the Presenter schema
73
- // does not declare it → stripped by Egress Firewall
74
- expect(result.data).not.toHaveProperty('tenant');
75
- });
76
-
77
- it('should include JIT system rules from .describe()', async () => {
78
- const result = await tester.callAction('system', 'health');
79
-
80
- expect(result.systemRules.length).toBeGreaterThan(0);
81
- expect(result.systemRules.some(
82
- (r: string) => r.includes('uptime') || r.includes('Uptime')
83
- )).toBe(true);
84
- });
85
- });
86
-
87
- describe('Echo Tool', () => {
88
- it('should echo the message back', async () => {
89
- const result = await tester.callAction('system', 'echo', {
90
- message: 'hello vurb',
91
- });
92
-
93
- expect(result.isError).toBe(false);
94
- expect(result.data).toHaveProperty('echo', 'hello vurb');
95
- expect(result.data).toHaveProperty('receivedAt');
96
- });
97
- });
98
- });
45
+ return `/**
46
+ * System Tools — Egress Firewall & RBAC Tests
47
+ *
48
+ * Proves that:
49
+ * 1. The Presenter strips undeclared fields (SOC2 CC6.1)
50
+ * 2. RBAC middleware blocks GUEST access (SOC2 CC6.3)
51
+ * 3. System rules from .describe() are injected
52
+ */
53
+ import { describe, it, expect } from 'vitest';
54
+ import { tester } from './setup.js';
55
+
56
+ describe('System Tools', () => {
57
+ describe('Egress Firewall', () => {
58
+ it('should return validated health data through the Presenter', async () => {
59
+ const result = await tester.callAction('system', 'health');
60
+
61
+ expect(result.isError).toBe(false);
62
+ expect(result.data).toHaveProperty('status');
63
+ expect(result.data).toHaveProperty('uptime');
64
+ expect(result.data).toHaveProperty('version');
65
+ expect(result.data).toHaveProperty('timestamp');
66
+ });
67
+
68
+ it('should strip undeclared fields (tenant must NOT leak)', async () => {
69
+ const result = await tester.callAction('system', 'health');
70
+
71
+ expect(result.isError).toBe(false);
72
+ // The handler returns 'tenant' but the Presenter schema
73
+ // does not declare it → stripped by Egress Firewall
74
+ expect(result.data).not.toHaveProperty('tenant');
75
+ });
76
+
77
+ it('should include JIT system rules from .describe()', async () => {
78
+ const result = await tester.callAction('system', 'health');
79
+
80
+ expect(result.systemRules.length).toBeGreaterThan(0);
81
+ expect(result.systemRules.some(
82
+ (r: string) => r.includes('uptime') || r.includes('Uptime')
83
+ )).toBe(true);
84
+ });
85
+ });
86
+
87
+ describe('Echo Tool', () => {
88
+ it('should echo the message back', async () => {
89
+ const result = await tester.callAction('system', 'echo', {
90
+ message: 'hello vurb',
91
+ });
92
+
93
+ expect(result.isError).toBe(false);
94
+ expect(result.data).toHaveProperty('echo', 'hello vurb');
95
+ expect(result.data).toHaveProperty('receivedAt');
96
+ });
97
+ });
98
+ });
99
99
  `;
100
100
  }
101
101
  //# sourceMappingURL=testing.js.map
@@ -8,56 +8,56 @@
8
8
  */
9
9
  /** Generate `src/tools/system/health.ts` — Health check with Presenter */
10
10
  export function healthToolTs() {
11
- return `/**
12
- * System Health Tool — Full MVA Pipeline (Fluent API)
13
- *
14
- * Demonstrates:
15
- * - f.query() — read-only semantic verb (auto-sets readOnlyHint)
16
- * - .describe() — LLM-facing description
17
- * - .returns() — Presenter (Egress Firewall + system rules + UI)
18
- * - .handle(input, ctx) — fully typed handler
19
- * - export default for autoDiscover()
20
- */
21
- import { f } from '../../vurb.js';
22
- import { SystemPresenter } from '../../presenters/SystemPresenter.js';
23
-
24
- export default f.query('system.health')
25
- .describe('Real-time server health status')
26
- .returns(SystemPresenter)
27
- .handle(async (_input, ctx) => {
28
- // Return raw data — the Presenter validates, strips
29
- // undeclared fields, injects rules, and renders UI.
30
- return {
31
- status: 'healthy',
32
- uptime: process.uptime(),
33
- version: '0.1.0',
34
- timestamp: new Date().toISOString(),
35
- tenant: ctx.tenantId,
36
- };
37
- });
11
+ return `/**
12
+ * System Health Tool — Full MVA Pipeline (Fluent API)
13
+ *
14
+ * Demonstrates:
15
+ * - f.query() — read-only semantic verb (auto-sets readOnlyHint)
16
+ * - .describe() — LLM-facing description
17
+ * - .returns() — Presenter (Egress Firewall + system rules + UI)
18
+ * - .handle(input, ctx) — fully typed handler
19
+ * - export default for autoDiscover()
20
+ */
21
+ import { f } from '../../vurb.js';
22
+ import { SystemPresenter } from '../../presenters/SystemPresenter.js';
23
+
24
+ export default f.query('system.health')
25
+ .describe('Real-time server health status')
26
+ .returns(SystemPresenter)
27
+ .handle(async (_input, ctx) => {
28
+ // Return raw data — the Presenter validates, strips
29
+ // undeclared fields, injects rules, and renders UI.
30
+ return {
31
+ status: 'healthy',
32
+ uptime: process.uptime(),
33
+ version: '0.1.0',
34
+ timestamp: new Date().toISOString(),
35
+ tenant: ctx.tenantId,
36
+ };
37
+ });
38
38
  `;
39
39
  }
40
40
  /** Generate `src/tools/system/echo.ts` — Simple echo tool */
41
41
  export function echoToolTs() {
42
- return `/**
43
- * Echo Tool — Connectivity Testing (Fluent API)
44
- *
45
- * Demonstrates:
46
- * - f.query() with .withString() typed parameter
47
- * - .handle(input, ctx) — input.message is typed as string
48
- * - Implicit success() wrapping — return raw data, framework wraps it
49
- */
50
- import { f } from '../../vurb.js';
51
-
52
- export default f.query('system.echo')
53
- .describe('Echo a message back (connectivity test)')
54
- .withString('message', 'Message to echo back')
55
- .handle(async (input) => {
56
- return {
57
- echo: input['message'],
58
- receivedAt: new Date().toISOString(),
59
- };
60
- });
42
+ return `/**
43
+ * Echo Tool — Connectivity Testing (Fluent API)
44
+ *
45
+ * Demonstrates:
46
+ * - f.query() with .withString() typed parameter
47
+ * - .handle(input, ctx) — input.message is typed as string
48
+ * - Implicit success() wrapping — return raw data, framework wraps it
49
+ */
50
+ import { f } from '../../vurb.js';
51
+
52
+ export default f.query('system.echo')
53
+ .describe('Echo a message back (connectivity test)')
54
+ .withString('message', 'Message to echo back')
55
+ .handle(async (input) => {
56
+ return {
57
+ echo: input['message'],
58
+ receivedAt: new Date().toISOString(),
59
+ };
60
+ });
61
61
  `;
62
62
  }
63
63
  //# sourceMappingURL=tools.js.map
@@ -4,79 +4,79 @@
4
4
  */
5
5
  /** Generate `prisma/schema.prisma` */
6
6
  export function prismaSchema() {
7
- return `// Prisma Schema — Database-Driven MCP Server
8
- //
9
- // The @vurb/prisma-gen generator reads annotations
10
- // and auto-generates Presenters + ToolBuilders with:
11
- // - Field-level security (/// @vurb.hide)
12
- // - Tenant isolation
13
- // - OOM protection
14
-
15
- generator client {
16
- provider = "prisma-client-js"
17
- }
18
-
19
- generator vurb {
20
- provider = "@vurb/prisma-gen"
21
- }
22
-
23
- datasource db {
24
- provider = "postgresql"
25
- url = env("DATABASE_URL")
26
- }
27
-
28
- model User {
29
- id String @id @default(cuid())
30
- email String @unique
31
- name String
32
-
33
- /// @vurb.hide — Stripped by the Egress Firewall before reaching the LLM
34
- password String
35
-
36
- role String @default("USER")
37
- createdAt DateTime @default(now())
38
- updatedAt DateTime @updatedAt
39
-
40
- posts Post[]
41
- }
42
-
43
- model Post {
44
- id String @id @default(cuid())
45
- title String
46
- content String?
47
- published Boolean @default(false)
48
- createdAt DateTime @default(now())
49
-
50
- author User @relation(fields: [authorId], references: [id])
51
- authorId String
52
- }
7
+ return `// Prisma Schema — Database-Driven MCP Server
8
+ //
9
+ // The @vurb/prisma-gen generator reads annotations
10
+ // and auto-generates Presenters + ToolBuilders with:
11
+ // - Field-level security (/// @vurb.hide)
12
+ // - Tenant isolation
13
+ // - OOM protection
14
+
15
+ generator client {
16
+ provider = "prisma-client-js"
17
+ }
18
+
19
+ generator vurb {
20
+ provider = "@vurb/prisma-gen"
21
+ }
22
+
23
+ datasource db {
24
+ provider = "postgresql"
25
+ url = env("DATABASE_URL")
26
+ }
27
+
28
+ model User {
29
+ id String @id @default(cuid())
30
+ email String @unique
31
+ name String
32
+
33
+ /// @vurb.hide — Stripped by the Egress Firewall before reaching the LLM
34
+ password String
35
+
36
+ role String @default("USER")
37
+ createdAt DateTime @default(now())
38
+ updatedAt DateTime @updatedAt
39
+
40
+ posts Post[]
41
+ }
42
+
43
+ model Post {
44
+ id String @id @default(cuid())
45
+ title String
46
+ content String?
47
+ published Boolean @default(false)
48
+ createdAt DateTime @default(now())
49
+
50
+ author User @relation(fields: [authorId], references: [id])
51
+ authorId String
52
+ }
53
53
  `;
54
54
  }
55
55
  /** Generate `src/tools/db/users.ts` */
56
56
  export function dbUsersToolTs() {
57
- return `/**
58
- * Database Users Tool — Prisma-Driven CRUD (Fluent API)
59
- *
60
- * Demonstrates:
61
- * - f.query() with .withOptionalNumber() typed parameter
62
- * - .handle(input, ctx) — input.take is typed as number | undefined
63
- * - Implicit success() wrapping
64
- */
65
- import { f } from '../../vurb.js';
66
-
67
- export default f.query('db.list_users')
68
- .describe('List users from the database')
69
- .withOptionalNumber('take', 'Max results (1-50)')
70
- .handle(async (input, ctx) => {
71
- // TODO: Replace with your Prisma client
72
- // const users = await ctx.db.user.findMany({ take: input.take ?? 10 });
73
- // return users;
74
-
75
- return {
76
- hint: 'Connect your Prisma client in src/context.ts to enable database queries.',
77
- example: 'const users = await ctx.db.user.findMany({ take: 10 })',
78
- };
79
- });
57
+ return `/**
58
+ * Database Users Tool — Prisma-Driven CRUD (Fluent API)
59
+ *
60
+ * Demonstrates:
61
+ * - f.query() with .withOptionalNumber() typed parameter
62
+ * - .handle(input, ctx) — input.take is typed as number | undefined
63
+ * - Implicit success() wrapping
64
+ */
65
+ import { f } from '../../vurb.js';
66
+
67
+ export default f.query('db.list_users')
68
+ .describe('List users from the database')
69
+ .withOptionalNumber('take', 'Max results (1-50)')
70
+ .handle(async (input, ctx) => {
71
+ // TODO: Replace with your Prisma client
72
+ // const users = await ctx.db.user.findMany({ take: input.take ?? 10 });
73
+ // return users;
74
+
75
+ return {
76
+ hint: 'Connect your Prisma client in src/context.ts to enable database queries.',
77
+ example: 'const users = await ctx.db.user.findMany({ take: 10 })',
78
+ };
79
+ });
80
80
  `;
81
81
  }
82
82
  //# sourceMappingURL=database.js.map
@@ -4,73 +4,73 @@
4
4
  */
5
5
  /** Generate `src/auth.ts` — OAuth Device Flow setup */
6
6
  export function oauthSetupTs(config) {
7
- return `/**
8
- * OAuth Setup — Device Flow Authentication (RFC 8628)
9
- *
10
- * Pre-configured \`createAuthTool()\` with login, complete, status, logout actions.
11
- * The \`requireAuth()\` middleware protects any tool with one line.
12
- *
13
- * 1. Set CLIENT_ID and AUTH endpoints in .env
14
- * 2. Register the auth tool in server.ts
15
- * 3. Use \`requireAuth()\` on protected tools
16
- */
17
- import { createAuthTool, TokenManager } from '@vurb/oauth';
18
- import type { ToolRegistry } from '@vurb/core';
19
-
20
- export function registerAuth<TContext>(registry: ToolRegistry<TContext>): void {
21
- const clientId = process.env['OAUTH_CLIENT_ID'];
22
- const authEndpoint = process.env['OAUTH_AUTH_ENDPOINT'];
23
- const tokenEndpoint = process.env['OAUTH_TOKEN_ENDPOINT'];
24
-
25
- if (!clientId || !authEndpoint || !tokenEndpoint) {
26
- console.error('⚠️ OAUTH_CLIENT_ID, OAUTH_AUTH_ENDPOINT, OAUTH_TOKEN_ENDPOINT are required in .env');
27
- return;
28
- }
29
-
30
- const auth = createAuthTool({
31
- clientId,
32
- authorizationEndpoint: authEndpoint,
33
- tokenEndpoint,
34
- tokenManager: {
35
- configDir: '.${config.name}',
36
- envVar: '${config.name.toUpperCase().replace(/-/g, '_')}_TOKEN',
37
- },
38
- });
39
-
40
- registry.register(auth);
41
- console.error('🔐 OAuth Device Flow registered (auth.login → auth.complete → auth.status)');
42
- }
7
+ return `/**
8
+ * OAuth Setup — Device Flow Authentication (RFC 8628)
9
+ *
10
+ * Pre-configured \`createAuthTool()\` with login, complete, status, logout actions.
11
+ * The \`requireAuth()\` middleware protects any tool with one line.
12
+ *
13
+ * 1. Set CLIENT_ID and AUTH endpoints in .env
14
+ * 2. Register the auth tool in server.ts
15
+ * 3. Use \`requireAuth()\` on protected tools
16
+ */
17
+ import { createAuthTool, TokenManager } from '@vurb/oauth';
18
+ import type { ToolRegistry } from '@vurb/core';
19
+
20
+ export function registerAuth<TContext>(registry: ToolRegistry<TContext>): void {
21
+ const clientId = process.env['OAUTH_CLIENT_ID'];
22
+ const authEndpoint = process.env['OAUTH_AUTH_ENDPOINT'];
23
+ const tokenEndpoint = process.env['OAUTH_TOKEN_ENDPOINT'];
24
+
25
+ if (!clientId || !authEndpoint || !tokenEndpoint) {
26
+ console.error('⚠️ OAUTH_CLIENT_ID, OAUTH_AUTH_ENDPOINT, OAUTH_TOKEN_ENDPOINT are required in .env');
27
+ return;
28
+ }
29
+
30
+ const auth = createAuthTool({
31
+ clientId,
32
+ authorizationEndpoint: authEndpoint,
33
+ tokenEndpoint,
34
+ tokenManager: {
35
+ configDir: '.${config.name}',
36
+ envVar: '${config.name.toUpperCase().replace(/-/g, '_')}_TOKEN',
37
+ },
38
+ });
39
+
40
+ registry.register(auth);
41
+ console.error('🔐 OAuth Device Flow registered (auth.login → auth.complete → auth.status)');
42
+ }
43
43
  `;
44
44
  }
45
45
  /** Generate `src/middleware/auth.ts` — requireAuth middleware */
46
46
  export function oauthMiddlewareTs() {
47
- return `/**
48
- * Auth Middleware — Protect tools with requireAuth()
49
- *
50
- * @example
51
- * \`\`\`ts
52
- * import { withAuth } from '../middleware/auth.js';
53
- *
54
- * export default f.query('projects.list')
55
- * .describe('List all projects')
56
- * .use(withAuth)
57
- * .handle(async (input, ctx) => { /* authenticated */ });
58
- * \`\`\`
59
- */
60
- import { requireAuth } from '@vurb/oauth';
61
-
62
- /**
63
- * Pre-configured auth middleware.
64
- * Rejects unauthenticated requests with \`AUTH_REQUIRED\` + self-healing hints.
65
- */
66
- export const withAuth = requireAuth({
67
- extractToken: (ctx: unknown) => {
68
- const obj = ctx as Record<string, unknown>;
69
- return typeof obj['token'] === 'string' ? obj['token'] : null;
70
- },
71
- recoveryHint: 'Call auth action=login to authenticate via browser',
72
- recoveryAction: 'auth',
73
- });
47
+ return `/**
48
+ * Auth Middleware — Protect tools with requireAuth()
49
+ *
50
+ * @example
51
+ * \`\`\`ts
52
+ * import { withAuth } from '../middleware/auth.js';
53
+ *
54
+ * export default f.query('projects.list')
55
+ * .describe('List all projects')
56
+ * .use(withAuth)
57
+ * .handle(async (input, ctx) => { /* authenticated */ });
58
+ * \`\`\`
59
+ */
60
+ import { requireAuth } from '@vurb/oauth';
61
+
62
+ /**
63
+ * Pre-configured auth middleware.
64
+ * Rejects unauthenticated requests with \`AUTH_REQUIRED\` + self-healing hints.
65
+ */
66
+ export const withAuth = requireAuth({
67
+ extractToken: (ctx: unknown) => {
68
+ const obj = ctx as Record<string, unknown>;
69
+ return typeof obj['token'] === 'string' ? obj['token'] : null;
70
+ },
71
+ recoveryHint: 'Call auth action=login to authenticate via browser',
72
+ recoveryAction: 'auth',
73
+ });
74
74
  `;
75
75
  }
76
76
  //# sourceMappingURL=oauth.js.map