@idevconn/create-icore 0.5.2 → 0.6.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/cli.js +36 -27
- package/dist/index.cjs +36 -27
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +36 -27
- package/package.json +1 -1
- package/templates/apps/api/.env.example +20 -0
- package/templates/apps/api/tsconfig.json +6 -1
- package/templates/apps/microservices/auth/.env.example +5 -0
- package/templates/apps/microservices/auth/tsconfig.json +6 -1
- package/templates/apps/microservices/jobs/tsconfig.json +6 -1
- package/templates/apps/microservices/notes/.env.example +5 -0
- package/templates/apps/microservices/notes/tsconfig.json +6 -1
- package/templates/apps/microservices/notes-e2e/src/support/global.d.ts +6 -0
- package/templates/apps/microservices/payment/.env.example +5 -0
- package/templates/apps/microservices/payment/tsconfig.json +6 -1
- package/templates/apps/microservices/upload/.env.example +5 -0
- package/templates/apps/microservices/upload/tsconfig.json +6 -1
- package/templates/apps/templates/client-antd/src/components/AccessDeniedPage.tsx +1 -1
- package/templates/apps/templates/client-antd/src/components/layout/LayoutHeader.tsx +1 -1
- package/templates/apps/templates/client-antd/src/components/layout/LayoutSider.tsx +3 -3
- package/templates/apps/templates/client-antd/src/routes/_dashboard/dashboard.tsx +2 -2
- package/templates/apps/templates/client-antd/src/routes/_dashboard/notes.tsx +2 -2
- package/templates/apps/templates/client-antd/src/routes/_dashboard/profile.tsx +2 -2
- package/templates/apps/templates/client-antd/src/routes/auth.callback.tsx +1 -1
- package/templates/apps/templates/client-antd/src/routes/auth.oauth.callback.tsx +1 -1
- package/templates/apps/templates/client-antd/src/routes/login.tsx +1 -1
- package/templates/apps/templates/client-antd/tsconfig.json +6 -1
- package/templates/apps/templates/client-antd-e2e/src/icore.spec.ts +2 -2
- package/templates/apps/templates/client-mui/src/components/AccessDeniedPage.tsx +1 -1
- package/templates/apps/templates/client-mui/src/components/layout/LayoutHeader.tsx +1 -1
- package/templates/apps/templates/client-mui/src/components/layout/LayoutSider.tsx +3 -15
- package/templates/apps/templates/client-mui/src/routes/_dashboard/dashboard.tsx +2 -6
- package/templates/apps/templates/client-mui/src/routes/_dashboard/notes.tsx +2 -2
- package/templates/apps/templates/client-mui/src/routes/_dashboard/profile.tsx +3 -3
- package/templates/apps/templates/client-mui/src/routes/auth.callback.tsx +1 -1
- package/templates/apps/templates/client-mui/src/routes/auth.oauth.callback.tsx +1 -1
- package/templates/apps/templates/client-mui/src/routes/login.tsx +3 -3
- package/templates/apps/templates/client-mui/tsconfig.json +6 -1
- package/templates/apps/templates/client-mui-e2e/src/icore.spec.ts +2 -2
- package/templates/apps/templates/client-shadcn/src/components/AccessDeniedPage.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/components/layout/LayoutSider.tsx +3 -3
- package/templates/apps/templates/client-shadcn/src/components/notes/DeleteNoteConfirm.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/components/notes/NoteDialog.tsx +3 -3
- package/templates/apps/templates/client-shadcn/src/components/notes/NotesTable.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/components/ui/button.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/components/ui/card.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/components/ui/input.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/components/ui/label.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/routes/_dashboard/dashboard.tsx +3 -9
- package/templates/apps/templates/client-shadcn/src/routes/_dashboard/notes.tsx +6 -6
- package/templates/apps/templates/client-shadcn/src/routes/_dashboard/profile.tsx +7 -7
- package/templates/apps/templates/client-shadcn/src/routes/auth.callback.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/routes/auth.oauth.callback.tsx +1 -1
- package/templates/apps/templates/client-shadcn/src/routes/login.tsx +19 -12
- package/templates/apps/templates/client-shadcn/tsconfig.json +6 -1
- package/templates/libs/auth-strategies/firebase/src/lib/__tests__/firebase-auth.contract.unit.test.ts +6 -3
- package/templates/libs/auth-strategies/supabase/src/lib/__tests__/supabase-auth.contract.unit.test.ts +5 -2
- package/templates/libs/db-strategies/firestore/src/lib/__tests__/firestore-db.contract.unit.test.ts +1 -2
- package/templates/libs/db-strategies/supabase/src/lib/__tests__/supabase-db.contract.unit.test.ts +1 -2
- package/templates/libs/shared/src/__tests__/cross-boundary.unit.test.ts +2 -1
- package/templates/libs/shared/src/__tests__/transport.unit.test.ts +47 -8
- package/templates/libs/shared/src/abilities/subjects.ts +12 -1
- package/templates/libs/shared/src/strategies/__tests__/fake-auth.contract.unit.test.ts +2 -2
- package/templates/libs/shared/src/strategies/__tests__/fake-db.contract.unit.test.ts +2 -2
- package/templates/libs/shared/src/strategies/__tests__/fake-storage.contract.unit.test.ts +2 -2
- package/templates/libs/shared/src/transport.ts +41 -0
- package/templates/libs/storage-strategies/cloudinary/src/lib/__tests__/cloudinary-storage.contract.unit.test.ts +1 -2
- package/templates/libs/storage-strategies/firebase/src/lib/__tests__/firebase-storage.contract.unit.test.ts +1 -2
- package/templates/libs/storage-strategies/supabase/src/lib/__tests__/supabase-storage.contract.unit.test.ts +1 -2
- package/templates/libs/vite-plugins/src/index.d.mts +5 -7
- package/templates/libs/vite-plugins/src/index.mjs +1 -1
- package/templates/libs/vite-plugins/tsconfig.json +2 -1
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { runAuthContract } from '@icore/shared/testing';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
FirebaseAuthStrategy,
|
|
5
|
+
createMockIdentityToolkit,
|
|
6
|
+
type MockHandle,
|
|
7
|
+
createMockAdminAuth,
|
|
8
|
+
} from '@icore/auth-firebase';
|
|
6
9
|
|
|
7
10
|
const toolkits = new WeakMap<FirebaseAuthStrategy, MockHandle>();
|
|
8
11
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { runAuthContract } from '@icore/shared/testing';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
SupabaseAuthStrategy,
|
|
4
|
+
createMockSupabaseClient,
|
|
5
|
+
type MockSupabaseClient,
|
|
6
|
+
} from '@icore/auth-supabase';
|
|
4
7
|
|
|
5
8
|
const mocks = new WeakMap<SupabaseAuthStrategy, MockSupabaseClient>();
|
|
6
9
|
|
package/templates/libs/db-strategies/firestore/src/lib/__tests__/firestore-db.contract.unit.test.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { runDBContract } from '@icore/shared/testing';
|
|
2
|
-
import { FirestoreDBStrategy } from '
|
|
3
|
-
import { createMockFirestore } from '../testing/mock-firestore';
|
|
2
|
+
import { FirestoreDBStrategy, createMockFirestore } from '@icore/db-firestore';
|
|
4
3
|
|
|
5
4
|
runDBContract('FirestoreDBStrategy', () => {
|
|
6
5
|
const db = createMockFirestore();
|
package/templates/libs/db-strategies/supabase/src/lib/__tests__/supabase-db.contract.unit.test.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { runDBContract } from '@icore/shared/testing';
|
|
2
|
-
import { SupabaseDBStrategy } from '
|
|
3
|
-
import { createMockSupabaseDB } from '../testing/mock-supabase-postgres';
|
|
2
|
+
import { SupabaseDBStrategy, createMockSupabaseDB } from '@icore/db-supabase';
|
|
4
3
|
|
|
5
4
|
runDBContract('SupabaseDBStrategy', () => {
|
|
6
5
|
const client = createMockSupabaseDB();
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* needing a full build.
|
|
13
13
|
*/
|
|
14
14
|
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
15
|
+
import type { Dirent } from 'node:fs';
|
|
15
16
|
import { join, resolve } from 'node:path';
|
|
16
17
|
import { describe, expect, it } from 'vitest';
|
|
17
18
|
|
|
@@ -21,7 +22,7 @@ const ROOT = resolve(__dirname, '../../../../..');
|
|
|
21
22
|
|
|
22
23
|
async function walkTs(dir: string): Promise<string[]> {
|
|
23
24
|
const files: string[] = [];
|
|
24
|
-
let entries:
|
|
25
|
+
let entries: Dirent<string>[];
|
|
25
26
|
try {
|
|
26
27
|
entries = await readdir(dir, { withFileTypes: true });
|
|
27
28
|
} catch {
|
|
@@ -19,8 +19,8 @@ describe('buildTransport', () => {
|
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
it('defaults to TCP when ${PREFIX}_TRANSPORT is unset', () => {
|
|
22
|
-
process.env
|
|
23
|
-
process.env
|
|
22
|
+
process.env['AUTH_HOST'] = '127.0.0.1';
|
|
23
|
+
process.env['AUTH_PORT'] = '4001';
|
|
24
24
|
const opts = buildTransport('AUTH');
|
|
25
25
|
expect(opts.transport).toBe(Transport.TCP);
|
|
26
26
|
const tcp = opts.options as { host: string; port: number };
|
|
@@ -29,8 +29,8 @@ describe('buildTransport', () => {
|
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
it('selects Redis when ${PREFIX}_TRANSPORT=redis', () => {
|
|
32
|
-
process.env
|
|
33
|
-
process.env
|
|
32
|
+
process.env['AUTH_TRANSPORT'] = 'redis';
|
|
33
|
+
process.env['AUTH_REDIS_URL'] = 'redis://localhost:6379';
|
|
34
34
|
const opts = buildTransport('AUTH');
|
|
35
35
|
expect(opts.transport).toBe(Transport.REDIS);
|
|
36
36
|
const redis = opts.options as { url: string; retryAttempts: number; retryDelay: number };
|
|
@@ -42,8 +42,8 @@ describe('buildTransport', () => {
|
|
|
42
42
|
});
|
|
43
43
|
|
|
44
44
|
it('selects NATS when ${PREFIX}_TRANSPORT=nats', () => {
|
|
45
|
-
process.env
|
|
46
|
-
process.env
|
|
45
|
+
process.env['AUTH_TRANSPORT'] = 'nats';
|
|
46
|
+
process.env['AUTH_NATS_URL'] = 'nats://localhost:4222,nats://localhost:4223';
|
|
47
47
|
const opts = buildTransport('AUTH');
|
|
48
48
|
expect(opts.transport).toBe(Transport.NATS);
|
|
49
49
|
const nats = opts.options as {
|
|
@@ -58,13 +58,52 @@ describe('buildTransport', () => {
|
|
|
58
58
|
expect(nats.maxReconnectAttempts).toBe(-1);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
it('selects MQTT when ${PREFIX}_TRANSPORT=mqtt', () => {
|
|
62
|
+
process.env.AUTH_TRANSPORT = 'mqtt';
|
|
63
|
+
process.env.AUTH_MQTT_URL = 'mqtt://localhost:1883';
|
|
64
|
+
const opts = buildTransport('AUTH');
|
|
65
|
+
expect(opts.transport).toBe(Transport.MQTT);
|
|
66
|
+
expect((opts.options as { url: string }).url).toBe('mqtt://localhost:1883');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('selects RMQ when ${PREFIX}_TRANSPORT=rmq', () => {
|
|
70
|
+
process.env.AUTH_TRANSPORT = 'rmq';
|
|
71
|
+
process.env.AUTH_RMQ_URL = 'amqp://localhost:5672';
|
|
72
|
+
process.env.AUTH_RMQ_QUEUE = 'auth_queue';
|
|
73
|
+
const opts = buildTransport('AUTH');
|
|
74
|
+
expect(opts.transport).toBe(Transport.RMQ);
|
|
75
|
+
const o = opts.options as { urls: string[]; queue: string };
|
|
76
|
+
expect(o.urls).toEqual(['amqp://localhost:5672']);
|
|
77
|
+
expect(o.queue).toBe('auth_queue');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('selects Kafka when ${PREFIX}_TRANSPORT=kafka (brokers + derived ids)', () => {
|
|
81
|
+
process.env.AUTH_TRANSPORT = 'kafka';
|
|
82
|
+
process.env.AUTH_KAFKA_BROKERS = 'localhost:9092,localhost:9093';
|
|
83
|
+
const opts = buildTransport('AUTH');
|
|
84
|
+
expect(opts.transport).toBe(Transport.KAFKA);
|
|
85
|
+
const o = opts.options as {
|
|
86
|
+
client: { brokers: string[]; clientId: string };
|
|
87
|
+
consumer: { groupId: string };
|
|
88
|
+
};
|
|
89
|
+
expect(o.client.brokers).toEqual(['localhost:9092', 'localhost:9093']);
|
|
90
|
+
expect(o.client.clientId).toBe('auth-client'); // derived from prefix
|
|
91
|
+
expect(o.consumer.groupId).toBe('auth-consumer');
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('throws when a broker var is missing (rmq needs queue)', () => {
|
|
95
|
+
process.env.AUTH_TRANSPORT = 'rmq';
|
|
96
|
+
process.env.AUTH_RMQ_URL = 'amqp://localhost:5672';
|
|
97
|
+
expect(() => buildTransport('AUTH')).toThrow(/AUTH_RMQ_QUEUE/);
|
|
98
|
+
});
|
|
99
|
+
|
|
61
100
|
it('throws on unknown transport', () => {
|
|
62
|
-
process.env
|
|
101
|
+
process.env['AUTH_TRANSPORT'] = 'sqs';
|
|
63
102
|
expect(() => buildTransport('AUTH')).toThrow(/sqs/);
|
|
64
103
|
});
|
|
65
104
|
|
|
66
105
|
it('throws when a required env var is missing', () => {
|
|
67
|
-
process.env
|
|
106
|
+
process.env['AUTH_TRANSPORT'] = 'redis';
|
|
68
107
|
expect(() => buildTransport('AUTH')).toThrow(/AUTH_REDIS_URL/);
|
|
69
108
|
});
|
|
70
109
|
});
|
|
@@ -1,2 +1,13 @@
|
|
|
1
|
+
import type { InferSubjects } from '@casl/ability';
|
|
2
|
+
|
|
1
3
|
export type AbilityAction = 'manage' | 'create' | 'read' | 'update' | 'delete';
|
|
2
|
-
|
|
4
|
+
|
|
5
|
+
// NoteSubject is the shaped object passed to subject('Note', { ... }) in tests
|
|
6
|
+
// and on the frontend <Can>. Included in AbilitySubject so ability.can() accepts
|
|
7
|
+
// tagged instances, not just the string name.
|
|
8
|
+
export interface NoteSubject {
|
|
9
|
+
id: string;
|
|
10
|
+
ownerId: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type AbilitySubject = InferSubjects<NoteSubject> | 'all' | 'User' | 'Profile' | 'Note';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { FakeAuthStrategy } from '
|
|
2
|
-
import { runAuthContract } from '
|
|
1
|
+
import { FakeAuthStrategy } from '@icore/shared';
|
|
2
|
+
import { runAuthContract } from '@icore/shared/testing';
|
|
3
3
|
|
|
4
4
|
runAuthContract('FakeAuthStrategy', () => new FakeAuthStrategy(), {
|
|
5
5
|
getMagicLinkToken: (strategy, email) =>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FakeDBStrategy } from '
|
|
2
|
-
import { runDBContract } from '
|
|
1
|
+
import { FakeDBStrategy } from '@icore/shared';
|
|
2
|
+
import { runDBContract } from '@icore/shared/testing';
|
|
3
3
|
|
|
4
4
|
runDBContract('FakeDBStrategy', () => new FakeDBStrategy());
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FakeStorageStrategy } from '
|
|
2
|
-
import { runStorageContract } from '
|
|
1
|
+
import { FakeStorageStrategy } from '@icore/shared';
|
|
2
|
+
import { runStorageContract } from '@icore/shared/testing';
|
|
3
3
|
|
|
4
4
|
runStorageContract('FakeStorageStrategy', () => new FakeStorageStrategy());
|
|
@@ -10,6 +10,12 @@ function transportKeys(prefix: string, kind: string): string[] {
|
|
|
10
10
|
return [`${prefix}_REDIS_URL`];
|
|
11
11
|
case 'nats':
|
|
12
12
|
return [`${prefix}_NATS_URL`];
|
|
13
|
+
case 'mqtt':
|
|
14
|
+
return [`${prefix}_MQTT_URL`];
|
|
15
|
+
case 'rmq':
|
|
16
|
+
return [`${prefix}_RMQ_URL`, `${prefix}_RMQ_QUEUE`];
|
|
17
|
+
case 'kafka':
|
|
18
|
+
return [`${prefix}_KAFKA_BROKERS`];
|
|
13
19
|
default:
|
|
14
20
|
return [];
|
|
15
21
|
}
|
|
@@ -98,6 +104,41 @@ export function buildTransport(prefix: string): ClientOptions {
|
|
|
98
104
|
reconnectTimeWait: 2000,
|
|
99
105
|
},
|
|
100
106
|
} as unknown as ClientOptions;
|
|
107
|
+
case 'mqtt':
|
|
108
|
+
// mqtt.js auto-reconnects (reconnectPeriod default) so a dropped broker
|
|
109
|
+
// re-attaches; a broker that's down on boot is handled by the
|
|
110
|
+
// bootstrapMicroservice() retry, same as nats.
|
|
111
|
+
return {
|
|
112
|
+
transport: Transport.MQTT,
|
|
113
|
+
options: {
|
|
114
|
+
url: required(`${prefix}_MQTT_URL`),
|
|
115
|
+
},
|
|
116
|
+
} as unknown as ClientOptions;
|
|
117
|
+
case 'rmq':
|
|
118
|
+
// amqp-connection-manager reconnects in the background; the initial
|
|
119
|
+
// connect failure is caught by bootstrapMicroservice() and retried.
|
|
120
|
+
return {
|
|
121
|
+
transport: Transport.RMQ,
|
|
122
|
+
options: {
|
|
123
|
+
urls: required(`${prefix}_RMQ_URL`).split(','),
|
|
124
|
+
queue: required(`${prefix}_RMQ_QUEUE`),
|
|
125
|
+
queueOptions: { durable: false },
|
|
126
|
+
},
|
|
127
|
+
} as unknown as ClientOptions;
|
|
128
|
+
case 'kafka':
|
|
129
|
+
// kafkajs retries broker connections internally; clientId defaults from
|
|
130
|
+
// the prefix, and the consumer needs a groupId.
|
|
131
|
+
return {
|
|
132
|
+
transport: Transport.KAFKA,
|
|
133
|
+
options: {
|
|
134
|
+
client: {
|
|
135
|
+
clientId:
|
|
136
|
+
process.env[`${prefix}_KAFKA_CLIENT_ID`]?.trim() || `${prefix.toLowerCase()}-client`,
|
|
137
|
+
brokers: required(`${prefix}_KAFKA_BROKERS`).split(','),
|
|
138
|
+
},
|
|
139
|
+
consumer: { groupId: `${prefix.toLowerCase()}-consumer` },
|
|
140
|
+
},
|
|
141
|
+
} as unknown as ClientOptions;
|
|
101
142
|
default:
|
|
102
143
|
throw new Error(`Unknown transport: ${kind}`);
|
|
103
144
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { runStorageContract } from '@icore/shared/testing';
|
|
2
|
-
import { CloudinaryStorageStrategy } from '
|
|
3
|
-
import { createMockCloudinary } from '../testing/mock-cloudinary.js';
|
|
2
|
+
import { CloudinaryStorageStrategy, createMockCloudinary } from '@icore/storage-cloudinary';
|
|
4
3
|
|
|
5
4
|
runStorageContract('CloudinaryStorageStrategy', () => {
|
|
6
5
|
const api = createMockCloudinary();
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { runStorageContract } from '@icore/shared/testing';
|
|
2
|
-
import { FirebaseStorageStrategy } from '
|
|
3
|
-
import { createMockFirebaseBucket } from '../testing/mock-firebase-storage.js';
|
|
2
|
+
import { FirebaseStorageStrategy, createMockFirebaseBucket } from '@icore/storage-firebase';
|
|
4
3
|
|
|
5
4
|
runStorageContract('FirebaseStorageStrategy', () => {
|
|
6
5
|
const bucket = createMockFirebaseBucket('icore-uploads');
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { runStorageContract } from '@icore/shared/testing';
|
|
2
|
-
import { SupabaseStorageStrategy } from '
|
|
3
|
-
import { createMockSupabaseStorageClient } from '../testing/mock-supabase-storage';
|
|
2
|
+
import { SupabaseStorageStrategy, createMockSupabaseStorageClient } from '@icore/storage-supabase';
|
|
4
3
|
|
|
5
4
|
runStorageContract('SupabaseStorageStrategy', () => {
|
|
6
5
|
const client = createMockSupabaseStorageClient('icore-uploads');
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Plugin } from 'vite';
|
|
2
|
-
import type {
|
|
1
|
+
import type { Plugin, UserConfig } from 'vite';
|
|
2
|
+
import type { TestUserConfig } from 'vitest/config';
|
|
3
3
|
|
|
4
4
|
export declare function noServerModulesPlugin(): Plugin;
|
|
5
5
|
|
|
@@ -18,10 +18,8 @@ export declare function commonManualChunks(
|
|
|
18
18
|
export declare function commonTestConfig(
|
|
19
19
|
name: string,
|
|
20
20
|
coverageDir: string,
|
|
21
|
-
): NonNullable<
|
|
21
|
+
): NonNullable<TestUserConfig['test']>;
|
|
22
22
|
|
|
23
|
-
export declare function commonServer(
|
|
24
|
-
port: number,
|
|
25
|
-
): NonNullable<import('vite').UserConfig['server']>;
|
|
23
|
+
export declare function commonServer(port: number): NonNullable<UserConfig['server']>;
|
|
26
24
|
|
|
27
|
-
export declare function apiInfoPlugin(opts?: { proxyTarget?: string }):
|
|
25
|
+
export declare function apiInfoPlugin(opts?: { proxyTarget?: string }): Plugin;
|
|
@@ -88,7 +88,7 @@ export function commonManualChunks(uiChunkFn) {
|
|
|
88
88
|
*
|
|
89
89
|
* @param {string} name - project name (e.g. 'client-shadcn')
|
|
90
90
|
* @param {string} coverageDir - relative path to coverage output dir
|
|
91
|
-
* @returns {import('vitest/config').
|
|
91
|
+
* @returns {import('vitest/config').TestUserConfig['test']}
|
|
92
92
|
*/
|
|
93
93
|
export function commonTestConfig(name, coverageDir) {
|
|
94
94
|
return {
|