Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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: 1 addition & 1 deletion .github/workflows/build-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ jobs:

strategy:
matrix:
node-version: [20.x]
node-version: [22.x]
provider: [sqlite, postgresql]

steps:
Expand Down
12 changes: 10 additions & 2 deletions .github/workflows/bump-version.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@ name: Bump Version

on:
workflow_dispatch:
inputs:
version_type:
description: 'Version type to bump'
required: true
type: choice
options:
- patch
- minor

permissions:
contents: write
Expand All @@ -26,15 +34,15 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
node-version: 22.x
cache: 'pnpm'

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Bump version
id: bump
run: npx tsx scripts/bump-version.ts
run: npx tsx scripts/bump-version.ts ${{ inputs.version_type }}

- name: Create PR
uses: peter-evans/create-pull-request@v7
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20.x
node-version: 22.x
cache: 'pnpm'
registry-url: 'https://registry.npmjs.org'

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/update-samples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
if: steps.check-package.outputs.exists == 'true'
uses: actions/setup-node@v4
with:
node-version: 20.x
node-version: 22.x
cache: 'npm'

- name: Update @zenstackhq packages to latest
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ dist
.pnpm-store
*.vsix
.DS_Store
coverage
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Before you start working on anything major, please make sure to open an issue or

## Prerequisites

- Node.js: v20 or above
- Node.js: v22 or above
- PNPM: as specified in [package.json](./package.json)

Test cases are run against both SQLite and Postgres. You should have a postgres server (16 or above) running (either natively or via Docker). The default connection is:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
</a>
<h1>ZenStack: Modern Data Layer for TypeScript Apps</h1>
<a href="https://www.npmjs.com/package/@zenstackhq/cli?activeTab=versions">
<img src="https://img.shields.io/npm/v/%40zenstackhq%2Fcli/next">
<img src="https://img.shields.io/npm/v/%40zenstackhq%2Fcli/latest">
</a>
<img src="https://github.com/zenstackhq/zenstack-v3/actions/workflows/build-test.yml/badge.svg">
<a href="https://twitter.com/zenstackhq">
Expand Down
13 changes: 10 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "zenstack-v3",
"version": "3.0.0",
"version": "3.1.0",
"description": "ZenStack",
"packageManager": "pnpm@10.23.0",
"type": "module",
Expand All @@ -12,10 +12,12 @@
"test:all": "pnpm run test:sqlite && pnpm run test:pg",
"test:pg": "TEST_DB_PROVIDER=postgresql turbo run test",
"test:sqlite": "TEST_DB_PROVIDER=sqlite turbo run test",
"test:coverage": "vitest run --coverage",
"format": "prettier --write \"**/*.{ts,tsx,md}\"",
"pr": "gh pr create --fill-first --base dev",
"merge-main": "gh pr create --title \"merge dev to main\" --body \"\" --base main --head dev",
"bump-version": "gh workflow run .github/workflows/bump-version.yml --ref dev",
"bump-patch": "gh workflow run .github/workflows/bump-version.yml --ref dev -f version_type=patch",
"bump-minor": "gh workflow run .github/workflows/bump-version.yml --ref dev -f version_type=minor",
"publish-all": "pnpm --filter \"./packages/**\" -r publish --access public",
"publish-preview": "pnpm --filter \"./packages/**\" -r publish --force --registry https://preview.registry.zenstack.dev/",
"unpublish-preview": "pnpm --filter \"./packages/**\" -r --shell-mode exec -- npm unpublish -f --registry https://preview.registry.zenstack.dev/ \"\\$PNPM_PACKAGE_NAME\""
Expand All @@ -26,8 +28,10 @@
"devDependencies": {
"@eslint/js": "^9.29.0",
"@types/node": "catalog:",
"@vitest/coverage-v8": "^4.0.16",
"eslint": "~9.29.0",
"glob": "^11.1.0",
"npm-run-all": "^4.1.5",
"prettier": "^3.5.3",
"prisma": "catalog:",
"tsup": "^8.5.0",
Expand All @@ -40,7 +44,10 @@
},
"pnpm": {
"onlyBuiltDependencies": [
"better-sqlite3"
"@parcel/watcher",
"better-sqlite3",
"esbuild",
"vue-demi"
]
}
}
2 changes: 1 addition & 1 deletion packages/auth-adapters/better-auth/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@zenstackhq/better-auth",
"version": "3.0.0",
"version": "3.1.0",
"description": "ZenStack Better Auth Adapter. This adapter is modified from better-auth's Prisma adapter.",
"type": "module",
"scripts": {
Expand Down
5 changes: 4 additions & 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",
"version": "3.1.0",
"type": "module",
"author": {
"name": "ZenStack Team"
Expand All @@ -28,6 +28,9 @@
"test": "vitest run",
"pack": "pnpm pack"
},
"exports": {
"./package.json": "./package.json"
},
"dependencies": {
"@zenstackhq/common-helpers": "workspace:*",
"@zenstackhq/language": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/src/actions/generate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const client = new ZenStackClient(schema, {
});
\`\`\`

Check documentation: https://zenstack.dev/docs/3.x`);
Check documentation: https://zenstack.dev/docs/`);
}
}

Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/actions/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import { STARTER_ZMODEL } from './templates';
*/
export async function run(projectPath: string) {
const packages = [
{ name: '@zenstackhq/cli@next', dev: true },
{ name: '@zenstackhq/orm@next', dev: false },
{ name: '@zenstackhq/cli@latest', dev: true },
{ name: '@zenstackhq/orm@latest', dev: false },
];
let pm = await detect();
if (!pm) {
Expand Down
4 changes: 4 additions & 0 deletions packages/clients/client-helpers/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import config from '@zenstackhq/eslint-config/base.js';

/** @type {import("eslint").Linter.Config} */
export default config;
40 changes: 40 additions & 0 deletions packages/clients/client-helpers/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@zenstackhq/client-helpers",
"version": "3.1.0",
"description": "Helpers for implementing clients that consume ZenStack's CRUD service",
"type": "module",
"scripts": {
"build": "tsc --noEmit && tsup-node && pnpm test:typecheck",
"watch": "tsup-node --watch",
"lint": "eslint src --ext ts",
"test": "vitest run",
"test:typecheck": "tsc --noEmit --project tsconfig.test.json",
"pack": "pnpm pack"
},
"author": "ZenStack Team",
"license": "MIT",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./fetch": {
"types": "./dist/fetch.d.ts",
"default": "./dist/fetch.js"
}
},
"dependencies": {
"@zenstackhq/common-helpers": "workspace:*",
"@zenstackhq/schema": "workspace:*",
"decimal.js": "catalog:",
"superjson": "^2.2.3"
},
"devDependencies": {
"@zenstackhq/eslint-config": "workspace:*",
"@zenstackhq/language": "workspace:*",
"@zenstackhq/orm": "workspace:*",
"@zenstackhq/sdk": "workspace:*",
"@zenstackhq/typescript-config": "workspace:*",
"@zenstackhq/vitest-config": "workspace:*"
}
}
4 changes: 4 additions & 0 deletions packages/clients/client-helpers/src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/**
* The default query endpoint.
*/
export const DEFAULT_QUERY_ENDPOINT = '/api/model';
107 changes: 107 additions & 0 deletions packages/clients/client-helpers/src/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import { lowerCaseFirst } from '@zenstackhq/common-helpers';
import Decimal from 'decimal.js';
import SuperJSON from 'superjson';
import type { QueryError } from './types';

/**
* Function signature for `fetch`.
*/
export type FetchFn = (url: string, options?: RequestInit) => Promise<Response>;

/**
* A fetcher function that uses fetch API to make HTTP requests and automatically unmarshals
* the response using superjson.
*/
export async function fetcher<R>(url: string, options?: RequestInit, customFetch?: FetchFn): Promise<R> {
const _fetch = customFetch ?? fetch;
const res = await _fetch(url, options);
if (!res.ok) {
const errData = unmarshal(await res.text());
if (errData.error?.rejectedByPolicy && errData.error?.rejectReason === 'cannot-read-back') {
// policy doesn't allow mutation result to be read back, just return undefined
return undefined as any;
}
const error: QueryError = new Error('An error occurred while fetching the data.');
error.info = errData.error;
error.status = res.status;
throw error;
}

const textResult = await res.text();
try {
return unmarshal(textResult).data as R;
} catch (err) {
console.error(`Unable to deserialize data:`, textResult);
throw err;
}
}

/**
* Makes a URL for the given endpoint, model, operation, and args that matches the RPC-style server API.
*/
export function makeUrl(endpoint: string, model: string, operation: string, args?: unknown) {
const baseUrl = `${endpoint}/${lowerCaseFirst(model)}/${operation}`;
if (!args) {
return baseUrl;
}

const { data, meta } = serialize(args);
let result = `${baseUrl}?q=${encodeURIComponent(JSON.stringify(data))}`;
if (meta) {
result += `&meta=${encodeURIComponent(JSON.stringify({ serialization: meta }))}`;
}
return result;
}

SuperJSON.registerCustom<Decimal, string>(
{
isApplicable: (v): v is Decimal =>
v instanceof Decimal ||
// interop with decimal.js
v?.toStringTag === '[object Decimal]',
serialize: (v) => v.toJSON(),
deserialize: (v) => new Decimal(v),
},
'Decimal',
);

/**
* Serialize the given value with superjson
*/
export function serialize(value: unknown): { data: unknown; meta: unknown } {
const { json, meta } = SuperJSON.serialize(value);
return { data: json, meta };
}

/**
* Deserialize the given value with superjson using the given metadata
*/
export function deserialize(value: unknown, meta: any): unknown {
return SuperJSON.deserialize({ json: value as any, meta });
}

/**
* Marshal the given value to a string using superjson
*/
export function marshal(value: unknown) {
const { data, meta } = serialize(value);
if (meta) {
return JSON.stringify({ ...(data as any), meta: { serialization: meta } });
} else {
return JSON.stringify(data);
}
}

/**
* Unmarshal the given string value using superjson, assuming the value is a JSON stringified
* object containing the serialized data and serialization metadata.
*/
export function unmarshal(value: string) {
const parsed = JSON.parse(value);
if (typeof parsed === 'object' && parsed?.data && parsed?.meta?.serialization) {
const deserializedData = deserialize(parsed.data, parsed.meta.serialization);
return { ...parsed, data: deserializedData };
} else {
return parsed;
}
}
9 changes: 9 additions & 0 deletions packages/clients/client-helpers/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export * from './constants';
export * from './invalidation';
export * from './logging';
export * from './mutator';
export * from './nested-read-visitor';
export * from './nested-write-visitor';
export * from './optimistic';
export * from './query-analysis';
export * from './types';
Loading