@zenstackhq/cli 3.1.0 → 3.2.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/index.cjs +88 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +89 -6
- package/dist/index.js.map +1 -1
- package/package.json +17 -9
- package/.turbo/turbo-build.log +0 -22
- package/eslint.config.js +0 -4
- package/scripts/post-build.ts +0 -20
- package/src/actions/action-utils.ts +0 -146
- package/src/actions/check.ts +0 -22
- package/src/actions/db.ts +0 -51
- package/src/actions/format.ts +0 -27
- package/src/actions/generate.ts +0 -226
- package/src/actions/index.ts +0 -10
- package/src/actions/info.ts +0 -71
- package/src/actions/init.ts +0 -61
- package/src/actions/migrate.ts +0 -149
- package/src/actions/seed.ts +0 -38
- package/src/actions/templates.ts +0 -58
- package/src/cli-error.ts +0 -4
- package/src/constants.ts +0 -5
- package/src/index.ts +0 -233
- package/src/plugins/index.ts +0 -2
- package/src/plugins/prisma.ts +0 -21
- package/src/plugins/typescript.ts +0 -40
- package/src/telemetry.ts +0 -139
- package/src/utils/exec-utils.ts +0 -61
- package/src/utils/is-ci.ts +0 -5
- package/src/utils/is-container.ts +0 -23
- package/src/utils/is-docker.ts +0 -31
- package/src/utils/is-wsl.ts +0 -18
- package/src/utils/machine-id-utils.ts +0 -76
- package/src/utils/version-utils.ts +0 -50
- package/test/check.test.ts +0 -101
- package/test/db.test.ts +0 -61
- package/test/format.test.ts +0 -33
- package/test/generate.test.ts +0 -76
- package/test/init.test.ts +0 -14
- package/test/migrate.test.ts +0 -72
- package/test/plugins/custom-plugin.test.ts +0 -50
- package/test/plugins/prisma-plugin.test.ts +0 -81
- package/test/ts-schema-gen.test.ts +0 -445
- package/test/utils.ts +0 -23
- package/tsconfig.json +0 -4
- package/tsup.config.ts +0 -13
- package/vitest.config.ts +0 -4
package/src/telemetry.ts
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import { init, type Mixpanel } from 'mixpanel';
|
|
2
|
-
import { randomUUID } from 'node:crypto';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import * as os from 'os';
|
|
5
|
-
import { TELEMETRY_TRACKING_TOKEN } from './constants';
|
|
6
|
-
import { isInCi } from './utils/is-ci';
|
|
7
|
-
import { isInContainer } from './utils/is-container';
|
|
8
|
-
import isDocker from './utils/is-docker';
|
|
9
|
-
import { isWsl } from './utils/is-wsl';
|
|
10
|
-
import { getMachineId } from './utils/machine-id-utils';
|
|
11
|
-
import { getVersion } from './utils/version-utils';
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Telemetry events
|
|
15
|
-
*/
|
|
16
|
-
export type TelemetryEvents =
|
|
17
|
-
| 'cli:start'
|
|
18
|
-
| 'cli:complete'
|
|
19
|
-
| 'cli:error'
|
|
20
|
-
| 'cli:command:start'
|
|
21
|
-
| 'cli:command:complete'
|
|
22
|
-
| 'cli:command:error'
|
|
23
|
-
| 'cli:plugin:start'
|
|
24
|
-
| 'cli:plugin:complete'
|
|
25
|
-
| 'cli:plugin:error';
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Utility class for sending telemetry
|
|
29
|
-
*/
|
|
30
|
-
export class Telemetry {
|
|
31
|
-
private readonly mixpanel: Mixpanel | undefined;
|
|
32
|
-
private readonly hostId = getMachineId();
|
|
33
|
-
private readonly sessionid = randomUUID();
|
|
34
|
-
private readonly _os_type = os.type();
|
|
35
|
-
private readonly _os_release = os.release();
|
|
36
|
-
private readonly _os_arch = os.arch();
|
|
37
|
-
private readonly _os_version = os.version();
|
|
38
|
-
private readonly _os_platform = os.platform();
|
|
39
|
-
private readonly version = getVersion();
|
|
40
|
-
private readonly prismaVersion = this.getPrismaVersion();
|
|
41
|
-
private readonly isDocker = isDocker();
|
|
42
|
-
private readonly isWsl = isWsl();
|
|
43
|
-
private readonly isContainer = isInContainer();
|
|
44
|
-
private readonly isCi = isInCi;
|
|
45
|
-
|
|
46
|
-
constructor() {
|
|
47
|
-
if (process.env['DO_NOT_TRACK'] !== '1' && TELEMETRY_TRACKING_TOKEN) {
|
|
48
|
-
this.mixpanel = init(TELEMETRY_TRACKING_TOKEN, {
|
|
49
|
-
geolocate: true,
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
get isTracking() {
|
|
55
|
-
return !!this.mixpanel;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
track(event: TelemetryEvents, properties: Record<string, unknown> = {}) {
|
|
59
|
-
if (this.mixpanel) {
|
|
60
|
-
const payload = {
|
|
61
|
-
distinct_id: this.hostId,
|
|
62
|
-
session: this.sessionid,
|
|
63
|
-
time: new Date(),
|
|
64
|
-
$os: this._os_type,
|
|
65
|
-
osType: this._os_type,
|
|
66
|
-
osRelease: this._os_release,
|
|
67
|
-
osPlatform: this._os_platform,
|
|
68
|
-
osArch: this._os_arch,
|
|
69
|
-
osVersion: this._os_version,
|
|
70
|
-
nodeVersion: process.version,
|
|
71
|
-
version: this.version,
|
|
72
|
-
prismaVersion: this.prismaVersion,
|
|
73
|
-
isDocker: this.isDocker,
|
|
74
|
-
isWsl: this.isWsl,
|
|
75
|
-
isContainer: this.isContainer,
|
|
76
|
-
isCi: this.isCi,
|
|
77
|
-
...properties,
|
|
78
|
-
};
|
|
79
|
-
this.mixpanel.track(event, payload);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
trackError(err: Error) {
|
|
84
|
-
this.track('cli:error', {
|
|
85
|
-
message: err.message,
|
|
86
|
-
stack: err.stack,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async trackSpan<T>(
|
|
91
|
-
startEvent: TelemetryEvents,
|
|
92
|
-
completeEvent: TelemetryEvents,
|
|
93
|
-
errorEvent: TelemetryEvents,
|
|
94
|
-
properties: Record<string, unknown>,
|
|
95
|
-
action: () => Promise<T> | T,
|
|
96
|
-
) {
|
|
97
|
-
this.track(startEvent, properties);
|
|
98
|
-
const start = Date.now();
|
|
99
|
-
let success = true;
|
|
100
|
-
try {
|
|
101
|
-
return await action();
|
|
102
|
-
} catch (err: any) {
|
|
103
|
-
this.track(errorEvent, {
|
|
104
|
-
message: err.message,
|
|
105
|
-
stack: err.stack,
|
|
106
|
-
...properties,
|
|
107
|
-
});
|
|
108
|
-
success = false;
|
|
109
|
-
throw err;
|
|
110
|
-
} finally {
|
|
111
|
-
this.track(completeEvent, {
|
|
112
|
-
duration: Date.now() - start,
|
|
113
|
-
success,
|
|
114
|
-
...properties,
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
async trackCommand(command: string, action: () => Promise<void> | void) {
|
|
120
|
-
await this.trackSpan('cli:command:start', 'cli:command:complete', 'cli:command:error', { command }, action);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
async trackCli(action: () => Promise<void> | void) {
|
|
124
|
-
await this.trackSpan('cli:start', 'cli:complete', 'cli:error', {}, action);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
getPrismaVersion() {
|
|
128
|
-
try {
|
|
129
|
-
const packageJsonPath = import.meta.resolve('prisma/package.json');
|
|
130
|
-
const packageJsonUrl = new URL(packageJsonPath);
|
|
131
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonUrl, 'utf8'));
|
|
132
|
-
return packageJson.version;
|
|
133
|
-
} catch {
|
|
134
|
-
return undefined;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
export const telemetry = new Telemetry();
|
package/src/utils/exec-utils.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { execSync as _exec, type ExecSyncOptions } from 'child_process';
|
|
2
|
-
import { fileURLToPath } from 'url';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Utility for executing command synchronously and prints outputs on current console
|
|
6
|
-
*/
|
|
7
|
-
export function execSync(cmd: string, options?: Omit<ExecSyncOptions, 'env'> & { env?: Record<string, string> }): void {
|
|
8
|
-
const { env, ...restOptions } = options ?? {};
|
|
9
|
-
const mergedEnv = env ? { ...process.env, ...env } : undefined;
|
|
10
|
-
_exec(cmd, {
|
|
11
|
-
encoding: 'utf-8',
|
|
12
|
-
stdio: options?.stdio ?? 'inherit',
|
|
13
|
-
env: mergedEnv,
|
|
14
|
-
...restOptions,
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Utility for running package commands through npx/bunx
|
|
20
|
-
*/
|
|
21
|
-
export function execPackage(
|
|
22
|
-
cmd: string,
|
|
23
|
-
options?: Omit<ExecSyncOptions, 'env'> & { env?: Record<string, string> },
|
|
24
|
-
): void {
|
|
25
|
-
const packageManager = process?.versions?.['bun'] ? 'bunx' : 'npx';
|
|
26
|
-
execSync(`${packageManager} ${cmd}`, options);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Utility for running prisma commands
|
|
31
|
-
*/
|
|
32
|
-
export function execPrisma(args: string, options?: Omit<ExecSyncOptions, 'env'> & { env?: Record<string, string> }) {
|
|
33
|
-
let prismaPath: string | undefined;
|
|
34
|
-
try {
|
|
35
|
-
if (typeof import.meta.resolve === 'function') {
|
|
36
|
-
// esm
|
|
37
|
-
prismaPath = fileURLToPath(import.meta.resolve('prisma/build/index.js'));
|
|
38
|
-
} else {
|
|
39
|
-
// cjs
|
|
40
|
-
prismaPath = require.resolve('prisma/build/index.js');
|
|
41
|
-
}
|
|
42
|
-
} catch {
|
|
43
|
-
// ignore and fallback
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const _options = {
|
|
47
|
-
...options,
|
|
48
|
-
env: {
|
|
49
|
-
...options?.env,
|
|
50
|
-
PRISMA_HIDE_UPDATE_MESSAGE: '1',
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
if (!prismaPath) {
|
|
55
|
-
// fallback to npx/bunx execute
|
|
56
|
-
execPackage(`prisma ${args}`, _options);
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
execSync(`node ${prismaPath} ${args}`, _options);
|
|
61
|
-
}
|
package/src/utils/is-ci.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import isDocker from './is-docker';
|
|
3
|
-
|
|
4
|
-
let cachedResult: boolean | undefined;
|
|
5
|
-
|
|
6
|
-
// Podman detection
|
|
7
|
-
const hasContainerEnv = () => {
|
|
8
|
-
try {
|
|
9
|
-
fs.statSync('/run/.containerenv');
|
|
10
|
-
return true;
|
|
11
|
-
} catch {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export function isInContainer() {
|
|
17
|
-
// TODO: Use `??=` when targeting Node.js 16.
|
|
18
|
-
if (cachedResult === undefined) {
|
|
19
|
-
cachedResult = hasContainerEnv() || isDocker();
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return cachedResult;
|
|
23
|
-
}
|
package/src/utils/is-docker.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// Copied over from https://github.com/sindresorhus/is-docker for CJS compatibility
|
|
2
|
-
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
|
|
5
|
-
let isDockerCached: boolean | undefined;
|
|
6
|
-
|
|
7
|
-
function hasDockerEnv() {
|
|
8
|
-
try {
|
|
9
|
-
fs.statSync('/.dockerenv');
|
|
10
|
-
return true;
|
|
11
|
-
} catch {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function hasDockerCGroup() {
|
|
17
|
-
try {
|
|
18
|
-
return fs.readFileSync('/proc/self/cgroup', 'utf8').includes('docker');
|
|
19
|
-
} catch {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export default function isDocker() {
|
|
25
|
-
// TODO: Use `??=` when targeting Node.js 16.
|
|
26
|
-
if (isDockerCached === undefined) {
|
|
27
|
-
isDockerCached = hasDockerEnv() || hasDockerCGroup();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
return isDockerCached;
|
|
31
|
-
}
|
package/src/utils/is-wsl.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import process from 'node:process';
|
|
2
|
-
import os from 'node:os';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
export const isWsl = () => {
|
|
5
|
-
if (process.platform !== 'linux') {
|
|
6
|
-
return false;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
if (os.release().toLowerCase().includes('microsoft')) {
|
|
10
|
-
return true;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
try {
|
|
14
|
-
return fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft');
|
|
15
|
-
} catch {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
};
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
// modified from https://github.com/automation-stack/node-machine-id
|
|
2
|
-
|
|
3
|
-
import { execSync } from 'child_process';
|
|
4
|
-
import { createHash, randomUUID } from 'node:crypto';
|
|
5
|
-
|
|
6
|
-
const { platform } = process;
|
|
7
|
-
const win32RegBinPath = {
|
|
8
|
-
native: '%windir%\\System32',
|
|
9
|
-
mixed: '%windir%\\sysnative\\cmd.exe /c %windir%\\System32',
|
|
10
|
-
};
|
|
11
|
-
const guid = {
|
|
12
|
-
darwin: 'ioreg -rd1 -c IOPlatformExpertDevice',
|
|
13
|
-
win32:
|
|
14
|
-
`${win32RegBinPath[isWindowsProcessMixedOrNativeArchitecture()]}\\REG.exe ` +
|
|
15
|
-
'QUERY HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Cryptography ' +
|
|
16
|
-
'/v MachineGuid',
|
|
17
|
-
linux: '( cat /var/lib/dbus/machine-id /etc/machine-id 2> /dev/null || hostname 2> /dev/null) | head -n 1 || :',
|
|
18
|
-
freebsd: 'kenv -q smbios.system.uuid || sysctl -n kern.hostuuid',
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
function isWindowsProcessMixedOrNativeArchitecture() {
|
|
22
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
23
|
-
if (process.arch === 'ia32' && process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432')) {
|
|
24
|
-
return 'mixed';
|
|
25
|
-
}
|
|
26
|
-
return 'native';
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function hash(guid: string): string {
|
|
30
|
-
return createHash('sha256').update(guid).digest('hex');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function expose(result: string): string | undefined {
|
|
34
|
-
switch (platform) {
|
|
35
|
-
case 'darwin':
|
|
36
|
-
return result
|
|
37
|
-
.split('IOPlatformUUID')[1]
|
|
38
|
-
?.split('\n')[0]
|
|
39
|
-
?.replace(/=|\s+|"/gi, '')
|
|
40
|
-
.toLowerCase();
|
|
41
|
-
case 'win32':
|
|
42
|
-
return result
|
|
43
|
-
.toString()
|
|
44
|
-
.split('REG_SZ')[1]
|
|
45
|
-
?.replace(/\r+|\n+|\s+/gi, '')
|
|
46
|
-
.toLowerCase();
|
|
47
|
-
case 'linux':
|
|
48
|
-
return result
|
|
49
|
-
.toString()
|
|
50
|
-
.replace(/\r+|\n+|\s+/gi, '')
|
|
51
|
-
.toLowerCase();
|
|
52
|
-
case 'freebsd':
|
|
53
|
-
return result
|
|
54
|
-
.toString()
|
|
55
|
-
.replace(/\r+|\n+|\s+/gi, '')
|
|
56
|
-
.toLowerCase();
|
|
57
|
-
default:
|
|
58
|
-
throw new Error(`Unsupported platform: ${process.platform}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function getMachineId() {
|
|
63
|
-
if (!(platform in guid)) {
|
|
64
|
-
return randomUUID();
|
|
65
|
-
}
|
|
66
|
-
try {
|
|
67
|
-
const value = execSync(guid[platform as keyof typeof guid]);
|
|
68
|
-
const id = expose(value.toString());
|
|
69
|
-
if (!id) {
|
|
70
|
-
return randomUUID();
|
|
71
|
-
}
|
|
72
|
-
return hash(id);
|
|
73
|
-
} catch {
|
|
74
|
-
return randomUUID();
|
|
75
|
-
}
|
|
76
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import colors from 'colors';
|
|
2
|
-
import fs from 'node:fs';
|
|
3
|
-
import path from 'node:path';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import semver from 'semver';
|
|
6
|
-
|
|
7
|
-
const CHECK_VERSION_TIMEOUT = 2000;
|
|
8
|
-
const VERSION_CHECK_TAG = 'next';
|
|
9
|
-
|
|
10
|
-
export function getVersion() {
|
|
11
|
-
try {
|
|
12
|
-
// isomorphic __dirname
|
|
13
|
-
const _dirname = typeof __dirname !== 'undefined' ? __dirname : path.dirname(fileURLToPath(import.meta.url));
|
|
14
|
-
return JSON.parse(fs.readFileSync(path.join(_dirname, '../package.json'), 'utf8')).version;
|
|
15
|
-
} catch {
|
|
16
|
-
return undefined;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export async function checkNewVersion() {
|
|
21
|
-
const currVersion = getVersion();
|
|
22
|
-
let latestVersion: string;
|
|
23
|
-
try {
|
|
24
|
-
latestVersion = await getLatestVersion();
|
|
25
|
-
} catch {
|
|
26
|
-
// noop
|
|
27
|
-
return;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (latestVersion && currVersion && semver.gt(latestVersion, currVersion)) {
|
|
31
|
-
console.log(`A newer version ${colors.cyan(latestVersion)} is available.`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export async function getLatestVersion() {
|
|
36
|
-
const fetchResult = await fetch(`https://registry.npmjs.org/@zenstackhq/cli/${VERSION_CHECK_TAG}`, {
|
|
37
|
-
headers: { accept: 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*' },
|
|
38
|
-
signal: AbortSignal.timeout(CHECK_VERSION_TIMEOUT),
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
if (fetchResult.ok) {
|
|
42
|
-
const data: any = await fetchResult.json();
|
|
43
|
-
const latestVersion = data?.version;
|
|
44
|
-
if (typeof latestVersion === 'string' && semver.valid(latestVersion)) {
|
|
45
|
-
return latestVersion;
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
throw new Error('invalid npm registry response');
|
|
50
|
-
}
|
package/test/check.test.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { describe, expect, it } from 'vitest';
|
|
4
|
-
import { createProject, runCli } from './utils';
|
|
5
|
-
|
|
6
|
-
const validModel = `
|
|
7
|
-
model User {
|
|
8
|
-
id String @id @default(cuid())
|
|
9
|
-
email String @unique
|
|
10
|
-
name String?
|
|
11
|
-
posts Post[]
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
model Post {
|
|
15
|
-
id String @id @default(cuid())
|
|
16
|
-
title String
|
|
17
|
-
content String?
|
|
18
|
-
author User @relation(fields: [authorId], references: [id])
|
|
19
|
-
authorId String
|
|
20
|
-
}
|
|
21
|
-
`;
|
|
22
|
-
|
|
23
|
-
const invalidModel = `
|
|
24
|
-
model User {
|
|
25
|
-
id String @id @default(cuid())
|
|
26
|
-
email String @unique
|
|
27
|
-
posts Post[]
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
model Post {
|
|
31
|
-
id String @id @default(cuid())
|
|
32
|
-
title String
|
|
33
|
-
author User @relation(fields: [authorId], references: [id])
|
|
34
|
-
// Missing authorId field - should cause validation error
|
|
35
|
-
}
|
|
36
|
-
`;
|
|
37
|
-
|
|
38
|
-
describe('CLI validate command test', () => {
|
|
39
|
-
it('should validate a valid schema successfully', () => {
|
|
40
|
-
const workDir = createProject(validModel);
|
|
41
|
-
|
|
42
|
-
// Should not throw an error
|
|
43
|
-
expect(() => runCli('check', workDir)).not.toThrow();
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should fail validation for invalid schema', () => {
|
|
47
|
-
const workDir = createProject(invalidModel);
|
|
48
|
-
|
|
49
|
-
// Should throw an error due to validation failure
|
|
50
|
-
expect(() => runCli('check', workDir)).toThrow();
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
it('should respect custom schema location', () => {
|
|
54
|
-
const workDir = createProject(validModel);
|
|
55
|
-
fs.renameSync(path.join(workDir, 'zenstack/schema.zmodel'), path.join(workDir, 'zenstack/custom.zmodel'));
|
|
56
|
-
|
|
57
|
-
// Should not throw an error when using custom schema path
|
|
58
|
-
expect(() => runCli('check --schema ./zenstack/custom.zmodel', workDir)).not.toThrow();
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('should fail when schema file does not exist', () => {
|
|
62
|
-
const workDir = createProject(validModel);
|
|
63
|
-
|
|
64
|
-
// Should throw an error when schema file doesn't exist
|
|
65
|
-
expect(() => runCli('check --schema ./nonexistent.zmodel', workDir)).toThrow();
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should respect package.json config', () => {
|
|
69
|
-
const workDir = createProject(validModel);
|
|
70
|
-
fs.mkdirSync(path.join(workDir, 'foo'));
|
|
71
|
-
fs.renameSync(path.join(workDir, 'zenstack/schema.zmodel'), path.join(workDir, 'foo/schema.zmodel'));
|
|
72
|
-
fs.rmdirSync(path.join(workDir, 'zenstack'));
|
|
73
|
-
|
|
74
|
-
const pkgJson = JSON.parse(fs.readFileSync(path.join(workDir, 'package.json'), 'utf8'));
|
|
75
|
-
pkgJson.zenstack = {
|
|
76
|
-
schema: './foo/schema.zmodel',
|
|
77
|
-
};
|
|
78
|
-
fs.writeFileSync(path.join(workDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
|
|
79
|
-
|
|
80
|
-
// Should not throw an error when using package.json config
|
|
81
|
-
expect(() => runCli('check', workDir)).not.toThrow();
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
it('should validate schema with syntax errors', () => {
|
|
85
|
-
const modelWithSyntaxError = `
|
|
86
|
-
datasource db {
|
|
87
|
-
provider = "sqlite"
|
|
88
|
-
url = "file:./dev.db"
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
model User {
|
|
92
|
-
id String @id @default(cuid())
|
|
93
|
-
email String @unique
|
|
94
|
-
// Missing closing brace - syntax error
|
|
95
|
-
`;
|
|
96
|
-
const workDir = createProject(modelWithSyntaxError, false);
|
|
97
|
-
|
|
98
|
-
// Should throw an error due to syntax error
|
|
99
|
-
expect(() => runCli('check', workDir)).toThrow();
|
|
100
|
-
});
|
|
101
|
-
});
|
package/test/db.test.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { describe, expect, it } from 'vitest';
|
|
4
|
-
import { createProject, runCli } from './utils';
|
|
5
|
-
|
|
6
|
-
const model = `
|
|
7
|
-
model User {
|
|
8
|
-
id String @id @default(cuid())
|
|
9
|
-
}
|
|
10
|
-
`;
|
|
11
|
-
|
|
12
|
-
describe('CLI db commands test', () => {
|
|
13
|
-
it('should generate a database with db push', () => {
|
|
14
|
-
const workDir = createProject(model);
|
|
15
|
-
runCli('db push', workDir);
|
|
16
|
-
expect(fs.existsSync(path.join(workDir, 'zenstack/dev.db'))).toBe(true);
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
it('should seed the database with db seed with seed script', () => {
|
|
20
|
-
const workDir = createProject(model);
|
|
21
|
-
const pkgJson = JSON.parse(fs.readFileSync(path.join(workDir, 'package.json'), 'utf8'));
|
|
22
|
-
pkgJson.zenstack = {
|
|
23
|
-
seed: 'node seed.js',
|
|
24
|
-
};
|
|
25
|
-
fs.writeFileSync(path.join(workDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
|
|
26
|
-
fs.writeFileSync(
|
|
27
|
-
path.join(workDir, 'seed.js'),
|
|
28
|
-
`
|
|
29
|
-
import fs from 'node:fs';
|
|
30
|
-
fs.writeFileSync('seed.txt', 'success');
|
|
31
|
-
`,
|
|
32
|
-
);
|
|
33
|
-
|
|
34
|
-
runCli('db seed', workDir);
|
|
35
|
-
expect(fs.readFileSync(path.join(workDir, 'seed.txt'), 'utf8')).toBe('success');
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
it('should seed the database after migrate reset', () => {
|
|
39
|
-
const workDir = createProject(model);
|
|
40
|
-
const pkgJson = JSON.parse(fs.readFileSync(path.join(workDir, 'package.json'), 'utf8'));
|
|
41
|
-
pkgJson.zenstack = {
|
|
42
|
-
seed: 'node seed.js',
|
|
43
|
-
};
|
|
44
|
-
fs.writeFileSync(path.join(workDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
|
|
45
|
-
fs.writeFileSync(
|
|
46
|
-
path.join(workDir, 'seed.js'),
|
|
47
|
-
`
|
|
48
|
-
import fs from 'node:fs';
|
|
49
|
-
fs.writeFileSync('seed.txt', 'success');
|
|
50
|
-
`,
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
runCli('migrate reset --force', workDir);
|
|
54
|
-
expect(fs.readFileSync(path.join(workDir, 'seed.txt'), 'utf8')).toBe('success');
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it('should skip seeding the database without seed script', () => {
|
|
58
|
-
const workDir = createProject(model);
|
|
59
|
-
runCli('db seed', workDir);
|
|
60
|
-
});
|
|
61
|
-
});
|
package/test/format.test.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
import { createProject, runCli } from './utils';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
|
|
5
|
-
const model = `
|
|
6
|
-
model User {
|
|
7
|
-
id String @id @default(cuid())
|
|
8
|
-
email String @unique
|
|
9
|
-
}
|
|
10
|
-
`;
|
|
11
|
-
|
|
12
|
-
describe('CLI format command test', () => {
|
|
13
|
-
it('should format a valid schema successfully', () => {
|
|
14
|
-
const workDir = createProject(model);
|
|
15
|
-
expect(() => runCli('format', workDir)).not.toThrow();
|
|
16
|
-
const updatedContent = fs.readFileSync(`${workDir}/zenstack/schema.zmodel`, 'utf-8');
|
|
17
|
-
expect(
|
|
18
|
-
updatedContent.includes(`model User {
|
|
19
|
-
id String @id @default(cuid())
|
|
20
|
-
email String @unique
|
|
21
|
-
}`),
|
|
22
|
-
).toBeTruthy();
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('should silently ignore invalid schema', () => {
|
|
26
|
-
const invalidModel = `
|
|
27
|
-
model User {
|
|
28
|
-
id String @id @default(cuid())
|
|
29
|
-
`;
|
|
30
|
-
const workDir = createProject(invalidModel);
|
|
31
|
-
expect(() => runCli('format', workDir)).not.toThrow();
|
|
32
|
-
});
|
|
33
|
-
});
|
package/test/generate.test.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { describe, expect, it } from 'vitest';
|
|
4
|
-
import { createProject, runCli } from './utils';
|
|
5
|
-
|
|
6
|
-
const model = `
|
|
7
|
-
model User {
|
|
8
|
-
id String @id @default(cuid())
|
|
9
|
-
}
|
|
10
|
-
`;
|
|
11
|
-
|
|
12
|
-
describe('CLI generate command test', () => {
|
|
13
|
-
it('should generate a TypeScript schema', () => {
|
|
14
|
-
const workDir = createProject(model);
|
|
15
|
-
runCli('generate', workDir);
|
|
16
|
-
expect(fs.existsSync(path.join(workDir, 'zenstack/schema.ts'))).toBe(true);
|
|
17
|
-
expect(fs.existsSync(path.join(workDir, 'zenstack/schema.prisma'))).toBe(false);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it('should respect custom output directory', () => {
|
|
21
|
-
const workDir = createProject(model);
|
|
22
|
-
runCli('generate --output ./zen', workDir);
|
|
23
|
-
expect(fs.existsSync(path.join(workDir, 'zen/schema.ts'))).toBe(true);
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('should respect custom schema location', () => {
|
|
27
|
-
const workDir = createProject(model);
|
|
28
|
-
fs.renameSync(path.join(workDir, 'zenstack/schema.zmodel'), path.join(workDir, 'zenstack/foo.zmodel'));
|
|
29
|
-
runCli('generate --schema ./zenstack/foo.zmodel', workDir);
|
|
30
|
-
expect(fs.existsSync(path.join(workDir, 'zenstack/schema.ts'))).toBe(true);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('should respect package.json config', () => {
|
|
34
|
-
const workDir = createProject(model);
|
|
35
|
-
fs.mkdirSync(path.join(workDir, 'foo'));
|
|
36
|
-
fs.renameSync(path.join(workDir, 'zenstack/schema.zmodel'), path.join(workDir, 'foo/schema.zmodel'));
|
|
37
|
-
fs.rmdirSync(path.join(workDir, 'zenstack'));
|
|
38
|
-
const pkgJson = JSON.parse(fs.readFileSync(path.join(workDir, 'package.json'), 'utf8'));
|
|
39
|
-
pkgJson.zenstack = {
|
|
40
|
-
schema: './foo/schema.zmodel',
|
|
41
|
-
output: './bar',
|
|
42
|
-
};
|
|
43
|
-
fs.writeFileSync(path.join(workDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
|
|
44
|
-
runCli('generate', workDir);
|
|
45
|
-
expect(fs.existsSync(path.join(workDir, 'bar/schema.ts'))).toBe(true);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it('should respect package.json schema dir config', () => {
|
|
49
|
-
const workDir = createProject(model);
|
|
50
|
-
fs.mkdirSync(path.join(workDir, 'foo'));
|
|
51
|
-
fs.renameSync(path.join(workDir, 'zenstack/schema.zmodel'), path.join(workDir, 'foo/schema.zmodel'));
|
|
52
|
-
fs.rmdirSync(path.join(workDir, 'zenstack'));
|
|
53
|
-
const pkgJson = JSON.parse(fs.readFileSync(path.join(workDir, 'package.json'), 'utf8'));
|
|
54
|
-
pkgJson.zenstack = {
|
|
55
|
-
schema: './foo',
|
|
56
|
-
output: './bar',
|
|
57
|
-
};
|
|
58
|
-
fs.writeFileSync(path.join(workDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
|
|
59
|
-
runCli('generate', workDir);
|
|
60
|
-
expect(fs.existsSync(path.join(workDir, 'bar/schema.ts'))).toBe(true);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it('should respect lite option', () => {
|
|
64
|
-
const workDir = createProject(model);
|
|
65
|
-
runCli('generate --lite', workDir);
|
|
66
|
-
expect(fs.existsSync(path.join(workDir, 'zenstack/schema.ts'))).toBe(true);
|
|
67
|
-
expect(fs.existsSync(path.join(workDir, 'zenstack/schema-lite.ts'))).toBe(true);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should respect liteOnly option', () => {
|
|
71
|
-
const workDir = createProject(model);
|
|
72
|
-
runCli('generate --lite-only', workDir);
|
|
73
|
-
expect(fs.existsSync(path.join(workDir, 'zenstack/schema.ts'))).toBe(false);
|
|
74
|
-
expect(fs.existsSync(path.join(workDir, 'zenstack/schema-lite.ts'))).toBe(true);
|
|
75
|
-
});
|
|
76
|
-
});
|