Skip to content

Commit 9312f40

Browse files
authored
patch release: 1.2.2 (#827)
2 parents 2c345e1 + b53e355 commit 9312f40

File tree

19 files changed

+309
-53
lines changed

19 files changed

+309
-53
lines changed

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "zenstack-monorepo",
3-
"version": "1.2.1",
3+
"version": "1.2.2",
44
"description": "",
55
"scripts": {
66
"build": "pnpm -r build",

Diff for: packages/language/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@zenstackhq/language",
3-
"version": "1.2.1",
3+
"version": "1.2.2",
44
"displayName": "ZenStack modeling language compiler",
55
"description": "ZenStack modeling language compiler",
66
"homepage": "https://zenstack.dev",

Diff for: packages/plugins/openapi/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@zenstackhq/openapi",
33
"displayName": "ZenStack Plugin and Runtime for OpenAPI",
4-
"version": "1.2.1",
4+
"version": "1.2.2",
55
"description": "ZenStack plugin and runtime supporting OpenAPI",
66
"main": "index.js",
77
"repository": {

Diff for: packages/plugins/swr/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@zenstackhq/swr",
33
"displayName": "ZenStack plugin for generating SWR hooks",
4-
"version": "1.2.1",
4+
"version": "1.2.2",
55
"description": "ZenStack plugin for generating SWR hooks",
66
"main": "index.js",
77
"repository": {

Diff for: packages/plugins/tanstack-query/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@zenstackhq/tanstack-query",
33
"displayName": "ZenStack plugin for generating tanstack-query hooks",
4-
"version": "1.2.1",
4+
"version": "1.2.2",
55
"description": "ZenStack plugin for generating tanstack-query hooks",
66
"main": "index.js",
77
"exports": {

Diff for: packages/plugins/trpc/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@zenstackhq/trpc",
33
"displayName": "ZenStack plugin for tRPC",
4-
"version": "1.2.1",
4+
"version": "1.2.2",
55
"description": "ZenStack plugin for tRPC",
66
"main": "index.js",
77
"repository": {

Diff for: packages/runtime/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@zenstackhq/runtime",
33
"displayName": "ZenStack Runtime Library",
4-
"version": "1.2.1",
4+
"version": "1.2.2",
55
"description": "Runtime of ZenStack for both client-side and server-side environments.",
66
"repository": {
77
"type": "git",

Diff for: packages/schema/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"publisher": "zenstack",
44
"displayName": "ZenStack Language Tools",
55
"description": "A toolkit for building secure CRUD apps with Next.js + Typescript",
6-
"version": "1.2.1",
6+
"version": "1.2.2",
77
"author": {
88
"name": "ZenStack Team"
99
},

Diff for: packages/schema/src/plugins/access-policy/expression-writer.ts

+54-32
Original file line numberDiff line numberDiff line change
@@ -223,15 +223,28 @@ export class ExpressionWriter {
223223
}
224224

225225
private writeCollectionPredicate(expr: BinaryExpr, operator: string) {
226-
this.block(() => {
227-
this.writeFieldCondition(
228-
expr.left,
229-
() => {
230-
this.write(expr.right);
231-
},
232-
operator === '?' ? 'some' : operator === '!' ? 'every' : 'none'
233-
);
234-
});
226+
// check if the operand should be compiled to a relation query
227+
// or a plain expression
228+
const compileToRelationQuery =
229+
(this.isPostGuard && this.isFutureMemberAccess(expr.left)) ||
230+
(!this.isPostGuard && !this.isFutureMemberAccess(expr.left));
231+
232+
if (compileToRelationQuery) {
233+
this.block(() => {
234+
this.writeFieldCondition(
235+
expr.left,
236+
() => {
237+
// inner scope of collection expression is always compiled as non-post-guard
238+
const innerWriter = new ExpressionWriter(this.writer, false);
239+
innerWriter.write(expr.right);
240+
},
241+
operator === '?' ? 'some' : operator === '!' ? 'every' : 'none'
242+
);
243+
});
244+
} else {
245+
const plain = this.plainExprBuilder.transform(expr);
246+
this.writer.write(`${plain} ? ${TRUE} : ${FALSE}`);
247+
}
235248
}
236249

237250
private isFieldAccess(expr: Expression): boolean {
@@ -275,6 +288,19 @@ export class ExpressionWriter {
275288
}
276289
}
277290

291+
private writeIdFieldsCheck(model: DataModel, value: Expression) {
292+
const idFields = this.requireIdFields(model);
293+
idFields.forEach((idField, idx) => {
294+
// eg: id: user.id
295+
this.writer.write(`${idField.name}:`);
296+
this.plain(value);
297+
this.writer.write(`.${idField.name}`);
298+
if (idx !== idFields.length - 1) {
299+
this.writer.write(',');
300+
}
301+
});
302+
}
303+
278304
private writeComparison(expr: BinaryExpr, operator: ComparisonOperator) {
279305
const leftIsFieldAccess = this.isFieldAccess(expr.left);
280306
const rightIsFieldAccess = this.isFieldAccess(expr.right);
@@ -298,7 +324,7 @@ export class ExpressionWriter {
298324
operator = this.negateOperator(operator);
299325
}
300326

301-
if (isMemberAccessExpr(fieldAccess) && isFutureExpr(fieldAccess.operand)) {
327+
if (this.isFutureMemberAccess(fieldAccess)) {
302328
// future().field should be treated as the "field" directly, so we
303329
// strip 'future().' and synthesize a reference expr
304330
fieldAccess = {
@@ -338,8 +364,6 @@ export class ExpressionWriter {
338364
// right now this branch only serves comparison with `auth`, like
339365
// @@allow('all', owner == auth())
340366

341-
const idFields = this.requireIdFields(dataModel);
342-
343367
if (operator !== '==' && operator !== '!=') {
344368
throw new PluginError(name, 'Only == and != operators are allowed');
345369
}
@@ -354,25 +378,13 @@ export class ExpressionWriter {
354378
}
355379

356380
this.block(() => {
357-
idFields.forEach((idField, idx) => {
358-
const writeIdsCheck = () => {
359-
// id: user.id
360-
this.writer.write(`${idField.name}:`);
361-
this.plain(operand);
362-
this.writer.write(`.${idField.name}`);
363-
if (idx !== idFields.length - 1) {
364-
this.writer.write(',');
365-
}
366-
};
367-
368-
if (isThisExpr(fieldAccess) && operator === '!=') {
369-
// wrap a not
370-
this.writer.writeLine('NOT:');
371-
this.block(() => writeIdsCheck());
372-
} else {
373-
writeIdsCheck();
374-
}
375-
});
381+
if (isThisExpr(fieldAccess) && operator === '!=') {
382+
// negate
383+
this.writer.writeLine('isNot:');
384+
this.block(() => this.writeIdFieldsCheck(dataModel, operand));
385+
} else {
386+
this.writeIdFieldsCheck(dataModel, operand);
387+
}
376388
});
377389
} else {
378390
if (this.equivalentRefs(fieldAccess, operand)) {
@@ -386,7 +398,13 @@ export class ExpressionWriter {
386398
// we should generate a field reference (comparing fields in the same model)
387399
this.writeFieldReference(operand);
388400
} else {
389-
this.plain(operand);
401+
if (dataModel && this.isModelTyped(operand)) {
402+
// the comparison is between model types, generate id fields comparison block
403+
this.block(() => this.writeIdFieldsCheck(dataModel, operand));
404+
} else {
405+
// scalar value, just generate the plain expression
406+
this.plain(operand);
407+
}
390408
}
391409
});
392410
}
@@ -400,6 +418,10 @@ export class ExpressionWriter {
400418
);
401419
}
402420

421+
private isFutureMemberAccess(expr: Expression): expr is MemberAccessExpr {
422+
return isMemberAccessExpr(expr) && isFutureExpr(expr.operand);
423+
}
424+
403425
private requireIdFields(dataModel: DataModel) {
404426
const idFields = getIdFields(dataModel);
405427
if (!idFields || idFields.length === 0) {

Diff for: packages/schema/src/plugins/access-policy/policy-guard-generator.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,16 @@ export default class PolicyGenerator {
207207
}
208208

209209
private processUpdatePolicies(expressions: Expression[], postUpdate: boolean) {
210-
return expressions
211-
.map((expr) => this.visitPolicyExpression(expr, postUpdate))
212-
.filter((e): e is Expression => !!e);
210+
const hasFutureReference = expressions.some((expr) => this.hasFutureReference(expr));
211+
if (postUpdate) {
212+
// when compiling post-update rules, if any rule contains `future()` reference,
213+
// we include all as post-update rules
214+
return hasFutureReference ? expressions : [];
215+
} else {
216+
// when compiling pre-update rules, if any rule contains `future()` reference,
217+
// we completely skip pre-update check and defer them to post-update
218+
return hasFutureReference ? [] : expressions;
219+
}
213220
}
214221

215222
private visitPolicyExpression(expr: Expression, postUpdate: boolean): Expression | undefined {
@@ -543,6 +550,9 @@ export default class PolicyGenerator {
543550
} else {
544551
return [];
545552
}
553+
} else if (isInvocationExpr(expr)) {
554+
// recurse into function arguments
555+
return expr.args.flatMap((arg) => collectReferencePaths(arg.value));
546556
} else {
547557
// recurse
548558
const children = streamContents(expr)

Diff for: packages/schema/src/utils/typescript-expression-transformer.ts

+17-5
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
DataModel,
66
Expression,
77
InvocationExpr,
8+
isDataModel,
89
isEnumField,
910
isThisExpr,
1011
LiteralExpr,
@@ -29,6 +30,7 @@ export class TypeScriptExpressionTransformerError extends Error {
2930
type Options = {
3031
isPostGuard?: boolean;
3132
fieldReferenceContext?: string;
33+
thisExprContext?: string;
3234
context: ExpressionContext;
3335
};
3436

@@ -99,7 +101,7 @@ export class TypeScriptExpressionTransformer {
99101

100102
private this(_expr: ThisExpr) {
101103
// "this" is mapped to the input argument
102-
return 'input';
104+
return this.options.thisExprContext ?? 'input';
103105
}
104106

105107
private memberAccess(expr: MemberAccessExpr, normalizeUndefined: boolean) {
@@ -302,11 +304,19 @@ export class TypeScriptExpressionTransformer {
302304
return `(${expr.operator} ${this.transform(expr.operand, normalizeUndefined)})`;
303305
}
304306

307+
private isModelType(expr: Expression) {
308+
return isDataModel(expr.$resolvedType?.decl);
309+
}
310+
305311
private binary(expr: BinaryExpr, normalizeUndefined: boolean): string {
306-
const _default = `(${this.transform(expr.left, normalizeUndefined)} ${expr.operator} ${this.transform(
307-
expr.right,
308-
normalizeUndefined
309-
)})`;
312+
let left = this.transform(expr.left, normalizeUndefined);
313+
let right = this.transform(expr.right, normalizeUndefined);
314+
if (this.isModelType(expr.left) && this.isModelType(expr.right)) {
315+
// comparison between model type values, map to id comparison
316+
left = `(${left}?.id ?? null)`;
317+
right = `(${right}?.id ?? null)`;
318+
}
319+
const _default = `(${left} ${expr.operator} ${right})`;
310320

311321
return match(expr.operator)
312322
.with(
@@ -346,7 +356,9 @@ export class TypeScriptExpressionTransformer {
346356
const operand = this.transform(expr.left, normalizeUndefined);
347357
const innerTransformer = new TypeScriptExpressionTransformer({
348358
...this.options,
359+
isPostGuard: false,
349360
fieldReferenceContext: '_item',
361+
thisExprContext: '_item',
350362
});
351363
const predicate = innerTransformer.transform(expr.right, normalizeUndefined);
352364

Diff for: packages/sdk/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@zenstackhq/sdk",
3-
"version": "1.2.1",
3+
"version": "1.2.2",
44
"description": "ZenStack plugin development SDK",
55
"main": "index.js",
66
"scripts": {

Diff for: packages/server/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@zenstackhq/server",
3-
"version": "1.2.1",
3+
"version": "1.2.2",
44
"displayName": "ZenStack Server-side Adapters",
55
"description": "ZenStack server-side adapters",
66
"homepage": "https://zenstack.dev",

Diff for: packages/testtools/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@zenstackhq/testtools",
3-
"version": "1.2.1",
3+
"version": "1.2.2",
44
"description": "ZenStack Test Tools",
55
"main": "index.js",
66
"private": true,

Diff for: packages/testtools/src/schema.ts

+11
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,19 @@ generator js {
8484
previewFeatures = ['clientExtensions']
8585
}
8686
87+
plugin meta {
88+
provider = '@core/model-meta'
89+
preserveTsFiles = true
90+
}
91+
92+
plugin policy {
93+
provider = '@core/access-policy'
94+
preserveTsFiles = true
95+
}
96+
8797
plugin zod {
8898
provider = '@core/zod'
99+
preserveTsFiles = true
89100
modelOnly = ${!options.fullZod}
90101
}
91102
`;

0 commit comments

Comments
 (0)