zenstack-migrate-from-v2 — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited zenstack-migrate-from-v2 (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
ZenStack V3 is a major rewrite: the Prisma ORM engine is replaced with ZenStack's own engine built on Kysely, while the ZModel schema stays largely compatible and the query API stays PrismaClient-compatible. Supported databases: PostgreSQL, MySQL, SQLite.
Because V2 was Prisma-based, the migration has two layers: the generic Prisma→ZenStack changes, then the V2-specific deltas below. For general setup/CLI see zenstack-project-setup.
V2 ran on Prisma, so start with the `zenstack-migrate-from-prisma` skill (swap deps, move the schema to zenstack/schema.zmodel, replace the client with ZenStackClient, update generate/migrate scripts). Then apply the V2-specific steps below.
npm uninstall zenstack @zenstackhq/runtime
npm install @zenstackhq/schema @zenstackhq/orm
npm install --save-dev @zenstackhq/cli| V2 | V3 |
|---|---|
zenstack (CLI) | @zenstackhq/cli |
@zenstackhq/runtime | @zenstackhq/orm |
| — | @zenstackhq/schema (new) |
The CLI command moves from zenstack <cmd> to zen <cmd> (e.g. zen generate).
In V2 access control was built into the runtime (enhance(prisma)). In V3 it's an opt-in plugin.
npm install @zenstackhq/plugin-policy plugin policy {
provider = '@zenstackhq/plugin-policy'
} import { ZenStackClient } from '@zenstackhq/orm';
import { PolicyPlugin } from '@zenstackhq/plugin-policy';
export const db = new ZenStackClient(schema, { dialect });
export const authDb = db.$use(new PolicyPlugin()); // was: enhance(prisma, { user })
// per request:
const userDb = authDb.$setAuth(user); // was: passing { user } to enhance()See zenstack-access-control for the full policy/runtime model.
future() → post-update + before()V2 expressed post-update conditions with future() inside an update rule. V3 uses a dedicated post-update operation, where bare field references mean the new values and before() reads the old ones.
// V2
@@deny('update', future().ownerId != ownerId)
// V3
@@deny('post-update', ownerId != before().ownerId)V2's abstract model + extends becomes a type applied with with (see zenstack-schema-modeling).
// V2
abstract model Timestamped {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post extends Timestamped { title String }
// V3
type Timestamped {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post with Timestamped { title String }(Note: extends still exists in V3, but for polymorphism via @@delegate, which is a different feature — don't use it as a plain mixin replacement.)
V3 requires you to pass an explicit apiHandler (RPCApiHandler or RestApiHandler), and the client-supplying callback is renamed getPrisma → getClient:
// V2
ZenStackMiddleware({ getPrisma: (req) => enhance(prisma, { user: getUser(req) }) });
// V3
ZenStackMiddleware({
apiHandler: new RPCApiHandler({ schema }),
getClient: (req) => authDb.$setAuth(getUser(req)),
});See zenstack-crud-server for all frameworks and both API styles.
Flat hook names are replaced by hooks grouped under a client that mirrors the ORM:
// V2
import { useFindManyUser } from '~/hooks';
const { data } = useFindManyUser({ where: { ... } });
// V3
import { useClientQueries } from '@zenstackhq/tanstack-query/react';
import { schema } from '~/zenstack/schema';
const client = useClientQueries(schema);
const { data } = client.user.useFindMany({ where: { ... } });SWR support was dropped in V3. See zenstack-crud-server for the full TanStack Query setup.
apiHandler.generateSpec() (see zenstack-crud-server).
Run zen generate, typecheck, and exercise your test suite / app. Confirm access control behaves as expected now that it's an explicit $use(new PolicyPlugin()) + $setAuth() flow rather than V2's implicit enhance().
Full ZenStack documentation for this topic is bundled under references/:
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.