Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
packages/language/src/generated/**
**/schema.ts
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zenstack-v3",
"version": "3.0.0-alpha.9",
"version": "3.0.0-alpha.10",
"description": "ZenStack",
"packageManager": "[email protected]",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"publisher": "zenstack",
"displayName": "ZenStack CLI",
"description": "FullStack database toolkit with built-in access control and automatic API generation.",
"version": "3.0.0-alpha.9",
"version": "3.0.0-alpha.10",
"type": "module",
"author": {
"name": "ZenStack Team"
Expand Down
32 changes: 32 additions & 0 deletions packages/cli/src/actions/action-utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { findUp } from '@zenstackhq/common-helpers';
import { loadDocument } from '@zenstackhq/language';
import { PrismaSchemaGenerator } from '@zenstackhq/sdk';
import colors from 'colors';
Expand All @@ -13,6 +14,14 @@ export function getSchemaFile(file?: string) {
return file;
}

const pkgJsonConfig = getPkgJsonConfig(process.cwd());
if (pkgJsonConfig.schema) {
if (!fs.existsSync(pkgJsonConfig.schema)) {
throw new CliError(`Schema file not found: ${pkgJsonConfig.schema}`);
}
return pkgJsonConfig.schema;
}

if (fs.existsSync('./zenstack/schema.zmodel')) {
return './zenstack/schema.zmodel';
} else if (fs.existsSync('./schema.zmodel')) {
Expand Down Expand Up @@ -51,3 +60,26 @@ export async function generateTempPrismaSchema(zmodelPath: string) {
fs.writeFileSync(prismaSchemaFile, prismaSchema);
return prismaSchemaFile;
}

export function getPkgJsonConfig(startPath: string) {
const result: { schema: string | undefined; output: string | undefined } = { schema: undefined, output: undefined };
const pkgJsonFile = findUp(['package.json'], startPath, false);

if (!pkgJsonFile) {
return result;
}

let pkgJson: any = undefined;
try {
pkgJson = JSON.parse(fs.readFileSync(pkgJsonFile, 'utf8'));
} catch {
return result;
}

if (pkgJson.zenstack && typeof pkgJson.zenstack === 'object') {
result.schema = pkgJson.zenstack.schema && path.resolve(path.dirname(pkgJsonFile), pkgJson.zenstack.schema);
result.output = pkgJson.zenstack.output && path.resolve(path.dirname(pkgJsonFile), pkgJson.zenstack.output);
}

return result;
}
15 changes: 9 additions & 6 deletions packages/cli/src/actions/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@ async function runPush(options: Options) {

try {
// run prisma db push
const cmd = `prisma db push --schema "${prismaSchemaFile}"${
options.acceptDataLoss ? ' --accept-data-loss' : ''
}${options.forceReset ? ' --force-reset' : ''} --skip-generate`;
const cmd = [
'prisma db push',
` --schema "${prismaSchemaFile}"`,
options.acceptDataLoss && ' --accept-data-loss',
options.forceReset && ' --force-reset',
' --skip-generate',
].join('');

try {
await execPackage(cmd, {
stdio: 'inherit',
});
await execPackage(cmd);
} catch (err) {
handleSubProcessError(err);
}
Expand Down
16 changes: 14 additions & 2 deletions packages/cli/src/actions/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { PrismaSchemaGenerator, TsSchemaGenerator, type CliGenerator } from '@ze
import colors from 'colors';
import fs from 'node:fs';
import path from 'node:path';
import { getSchemaFile, loadSchemaDocument } from './action-utils';
import { getPkgJsonConfig, getSchemaFile, loadSchemaDocument } from './action-utils';

type Options = {
schema?: string;
Expand All @@ -20,7 +20,7 @@ export async function run(options: Options) {
const schemaFile = getSchemaFile(options.schema);

const model = await loadSchemaDocument(schemaFile);
const outputPath = options.output ?? path.dirname(schemaFile);
const outputPath = getOutputPath(options, schemaFile);

// generate TS schema
const tsSchemaFile = path.join(outputPath, 'schema.ts');
Expand Down Expand Up @@ -55,6 +55,18 @@ const client = new ZenStackClient(schema, {
}
}

function getOutputPath(options: Options, schemaFile: string) {
if (options.output) {
return options.output;
}
const pkgJsonConfig = getPkgJsonConfig(process.cwd());
if (pkgJsonConfig.output) {
return pkgJsonConfig.output;
} else {
return path.dirname(schemaFile);
}
}

async function runPlugins(model: Model, outputPath: string, tsSchemaFile: string) {
const plugins = model.declarations.filter(isPlugin);
for (const plugin of plugins) {
Expand Down
34 changes: 22 additions & 12 deletions packages/cli/src/actions/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,32 +53,42 @@ export async function run(command: string, options: CommonOptions) {

async function runDev(prismaSchemaFile: string, options: DevOptions) {
try {
await execPackage(
`prisma migrate dev --schema "${prismaSchemaFile}" --skip-generate${options.name ? ` --name ${options.name}` : ''}${options.createOnly ? ' --create-only' : ''}`,
{
stdio: 'inherit',
},
);
const cmd = [
'prisma migrate dev',
` --schema "${prismaSchemaFile}"`,
' --skip-generate',
options.name && ` --name ${options.name}`,
options.createOnly && ' --create-only',
].join('');

await execPackage(cmd);
} catch (err) {
handleSubProcessError(err);
}
}

async function runReset(prismaSchemaFile: string, options: ResetOptions) {
try {
await execPackage(`prisma migrate reset --schema "${prismaSchemaFile}"${options.force ? ' --force' : ''}`, {
stdio: 'inherit',
});
const cmd = [
'prisma migrate reset',
` --schema "${prismaSchemaFile}"`,
options.force && ' --force',
].join('');

await execPackage(cmd);
} catch (err) {
handleSubProcessError(err);
}
}

async function runDeploy(prismaSchemaFile: string, _options: DeployOptions) {
try {
await execPackage(`prisma migrate deploy --schema "${prismaSchemaFile}"`, {
stdio: 'inherit',
});
const cmd = [
'prisma migrate deploy',
` --schema "${prismaSchemaFile}"`,
].join('');

await execPackage(cmd);
} catch (err) {
handleSubProcessError(err);
}
Expand Down
15 changes: 15 additions & 0 deletions packages/cli/test/generate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,19 @@ describe('CLI generate command test', () => {
runCli('generate --save-prisma-schema "../prisma/schema.prisma"', workDir);
expect(fs.existsSync(path.join(workDir, 'prisma/schema.prisma'))).toBe(true);
});

it('should respect package.json config', () => {
const workDir = createProject(model);
fs.mkdirSync(path.join(workDir, 'foo'));
fs.renameSync(path.join(workDir, 'zenstack/schema.zmodel'), path.join(workDir, 'foo/schema.zmodel'));
fs.rmdirSync(path.join(workDir, 'zenstack'));
const pkgJson = JSON.parse(fs.readFileSync(path.join(workDir, 'package.json'), 'utf8'));
pkgJson.zenstack = {
schema: './foo/schema.zmodel',
output: './bar',
};
fs.writeFileSync(path.join(workDir, 'package.json'), JSON.stringify(pkgJson, null, 2));
runCli('generate', workDir);
expect(fs.existsSync(path.join(workDir, 'bar/schema.ts'))).toBe(true);
});
});
2 changes: 1 addition & 1 deletion packages/common-helpers/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/common-helpers",
"version": "3.0.0-alpha.9",
"version": "3.0.0-alpha.10",
"description": "ZenStack Common Helpers",
"type": "module",
"scripts": {
Expand Down
34 changes: 34 additions & 0 deletions packages/common-helpers/src/find-up.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import fs from 'fs';
import path from 'path';

/**
* A type named FindUp that takes a type parameter e which extends boolean.
*/
export type FindUpResult<Multiple extends boolean> = Multiple extends true ? string[] | undefined : string | undefined;

/**
* Find and return file paths by searching parent directories based on the given names list and current working directory (cwd) path.
* Optionally return a single path or multiple paths.
* If multiple allowed, return all paths found.
* If no paths are found, return undefined.
*
* @param names An array of strings representing names to search for within the directory
* @param cwd A string representing the current working directory
* @param multiple A boolean flag indicating whether to search for multiple levels. Useful for finding node_modules directories...
* @param An array of strings representing the accumulated results used in multiple results
* @returns Path(s) to a specific file or folder within the directory or parent directories
*/
export function findUp<Multiple extends boolean = false>(
names: string[],
cwd: string = process.cwd(),
multiple: Multiple = false as Multiple,
result: string[] = [],
): FindUpResult<Multiple> {
if (!names.some((name) => !!name)) return undefined;
const target = names.find((name) => fs.existsSync(path.join(cwd, name)));
if (multiple === false && target) return path.join(cwd, target) as FindUpResult<Multiple>;
if (target) result.push(path.join(cwd, target));
const up = path.resolve(cwd, '..');
if (up === cwd) return (multiple && result.length > 0 ? result : undefined) as FindUpResult<Multiple>; // it'll fail anyway
return findUp(names, up, multiple, result);
}
1 change: 1 addition & 0 deletions packages/common-helpers/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './find-up';
export * from './is-plain-object';
export * from './lower-case-first';
export * from './param-case';
Expand Down
16 changes: 10 additions & 6 deletions packages/common-helpers/src/param-case.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,19 @@ const DEFAULT_STRIP_REGEXP = /[^A-Z0-9]+/gi;

export function paramCase(input: string) {
const result = input
.replace(DEFAULT_SPLIT_REGEXP_1, "$1\0$2")
.replace(DEFAULT_SPLIT_REGEXP_2, "$1\0$2")
.replace(DEFAULT_STRIP_REGEXP, "\0");
.replace(DEFAULT_SPLIT_REGEXP_1, '$1\0$2')
.replace(DEFAULT_SPLIT_REGEXP_2, '$1\0$2')
.replace(DEFAULT_STRIP_REGEXP, '\0');

let start = 0;
let end = result.length;

while (result.charAt(start) === "\0") start++;
while (result.charAt(end - 1) === "\0") end--;
while (result.charAt(start) === '\0') start++;
while (result.charAt(end - 1) === '\0') end--;

return result.slice(start, end).split("\0").map((str) => str.toLowerCase()).join("-");
return result
.slice(start, end)
.split('\0')
.map((str) => str.toLowerCase())
.join('-');
}
2 changes: 1 addition & 1 deletion packages/create-zenstack/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-zenstack",
"version": "3.0.0-alpha.9",
"version": "3.0.0-alpha.10",
"description": "Create a new ZenStack project",
"type": "module",
"scripts": {
Expand Down
2 changes: 1 addition & 1 deletion packages/eslint-config/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/eslint-config",
"version": "3.0.0-alpha.9",
"version": "3.0.0-alpha.10",
"type": "module",
"private": true,
"license": "MIT"
Expand Down
2 changes: 1 addition & 1 deletion packages/ide/vscode/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "zenstack",
"publisher": "zenstack",
"version": "3.0.0-alpha.9",
"version": "3.0.0-alpha.10",
"displayName": "ZenStack Language Tools",
"description": "VSCode extension for ZenStack ZModel language",
"private": true,
Expand Down
2 changes: 1 addition & 1 deletion packages/language/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@zenstackhq/language",
"description": "ZenStack ZModel language specification",
"version": "3.0.0-alpha.9",
"version": "3.0.0-alpha.10",
"license": "MIT",
"author": "ZenStack Team",
"files": [
Expand Down
6 changes: 3 additions & 3 deletions packages/language/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -331,9 +331,9 @@ export function getObjectLiteral<T>(expr: Expression | ConfigExpr | undefined):
return result as T;
}

export function getLiteralArray<
T extends string | number | boolean | any = any,
>(expr: Expression | ConfigExpr | undefined): T[] | undefined {
export function getLiteralArray<T extends string | number | boolean | any = any>(
expr: Expression | ConfigExpr | undefined,
): T[] | undefined {
const arr = getArray(expr);
if (!arr) {
return undefined;
Expand Down
18 changes: 14 additions & 4 deletions packages/runtime/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/runtime",
"version": "3.0.0-alpha.9",
"version": "3.0.0-alpha.10",
"description": "ZenStack Runtime",
"type": "module",
"scripts": {
Expand Down Expand Up @@ -39,6 +39,16 @@
"default": "./dist/schema.cjs"
}
},
"./helpers": {
"import": {
"types": "./dist/helpers.d.ts",
"default": "./dist/helpers.js"
},
"require": {
"types": "./dist/helpers.d.cts",
"default": "./dist/helpers.cjs"
}
},
"./plugins/policy": {
"import": {
"types": "./dist/plugins/policy.d.ts",
Expand All @@ -63,12 +73,12 @@
"nanoid": "^5.0.9",
"ts-pattern": "catalog:",
"ulid": "^3.0.0",
"uuid": "^11.0.5"
"uuid": "^11.0.5",
"zod": "catalog:"
},
"peerDependencies": {
"better-sqlite3": "^11.8.1",
"pg": "^8.13.1",
"zod": "catalog:"
"pg": "^8.13.1"
},
"peerDependenciesMeta": {
"better-sqlite3": {
Expand Down
8 changes: 8 additions & 0 deletions packages/runtime/src/client/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,14 @@ export type ClientContract<Schema extends SchemaDef> = {
[Key in GetModels<Schema> as Uncapitalize<Key>]: ModelOperations<Schema, Key>;
} & Procedures<Schema>;

/**
* The contract for a client in a transaction.
*/
export type TransactionClientContract<Schema extends SchemaDef> = Omit<
ClientContract<Schema>,
TransactionUnsupportedMethods
>;

type _TypeMap = {
String: string;
Int: number;
Expand Down
4 changes: 2 additions & 2 deletions packages/runtime/src/client/crud/operations/aggregate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ import { BaseOperationHandler } from './base';
export class AggregateOperationHandler<Schema extends SchemaDef> extends BaseOperationHandler<Schema> {
async handle(_operation: 'aggregate', args: unknown | undefined) {
// normalize args to strip `undefined` fields
const normalizeArgs = this.normalizeArgs(args);
const normalizedArgs = this.normalizeArgs(args);

// parse args
const parsedArgs = this.inputValidator.validateAggregateArgs(this.model, normalizeArgs);
const parsedArgs = this.inputValidator.validateAggregateArgs(this.model, normalizedArgs);

let query = this.kysely.selectFrom((eb) => {
// nested query for filtering and pagination
Expand Down
Loading