Skip to content

Commit 70a81c6

Browse files
authored
merge dev to main (v2.11.0) (#1943)
2 parents 689d013 + 4605278 commit 70a81c6

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+2046
-165
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "zenstack-monorepo",
3-
"version": "2.10.2",
3+
"version": "2.11.0",
44
"description": "",
55
"scripts": {
66
"build": "pnpm -r --filter=\"!./packages/ide/*\" build",

packages/ide/jetbrains/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ plugins {
99
}
1010

1111
group = "dev.zenstack"
12-
version = "2.10.2"
12+
version = "2.11.0"
1313

1414
repositories {
1515
mavenCentral()

packages/ide/jetbrains/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "jetbrains",
3-
"version": "2.10.2",
3+
"version": "2.11.0",
44
"displayName": "ZenStack JetBrains IDE Plugin",
55
"description": "ZenStack JetBrains IDE plugin",
66
"homepage": "https://zenstack.dev",

packages/language/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@zenstackhq/language",
3-
"version": "2.10.2",
3+
"version": "2.11.0",
44
"displayName": "ZenStack modeling language compiler",
55
"description": "ZenStack modeling language compiler",
66
"homepage": "https://zenstack.dev",

packages/misc/redwood/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@zenstackhq/redwood",
33
"displayName": "ZenStack RedwoodJS Integration",
4-
"version": "2.10.2",
4+
"version": "2.11.0",
55
"description": "CLI and runtime for integrating ZenStack with RedwoodJS projects.",
66
"repository": {
77
"type": "git",

packages/plugins/openapi/package.json

Lines changed: 1 addition & 1 deletion
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": "2.10.2",
4+
"version": "2.11.0",
55
"description": "ZenStack plugin and runtime supporting OpenAPI",
66
"main": "index.js",
77
"repository": {

packages/plugins/openapi/src/rest-generator.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -906,16 +906,24 @@ export class RESTfulOpenAPIGenerator extends OpenAPIGeneratorBase {
906906
},
907907
};
908908

909+
let idFieldSchema: OAPI.SchemaObject = { type: 'string' };
910+
if (idFields.length === 1) {
911+
// FIXME: JSON:API actually requires id field to be a string,
912+
// but currently the RESTAPIHandler returns the original data
913+
// type as declared in the ZModel schema.
914+
idFieldSchema = this.fieldTypeToOpenAPISchema(idFields[0].type);
915+
}
916+
909917
if (mode === 'create') {
910918
// 'id' is required if there's no default value
911919
const idFields = model.fields.filter((f) => isIdField(f));
912920
if (idFields.length === 1 && !hasAttribute(idFields[0], '@default')) {
913-
properties = { id: { type: 'string' }, ...properties };
921+
properties = { id: idFieldSchema, ...properties };
914922
toplevelRequired.unshift('id');
915923
}
916924
} else {
917925
// 'id' always required for read and update
918-
properties = { id: { type: 'string' }, ...properties };
926+
properties = { id: idFieldSchema, ...properties };
919927
toplevelRequired.unshift('id');
920928
}
921929

packages/plugins/openapi/tests/openapi-restful.test.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ model Bar {
8484

8585
const { name: output } = tmp.fileSync({ postfix: '.yaml' });
8686

87-
const options = buildOptions(model, modelFile, output, '3.1.0');
87+
const options = buildOptions(model, modelFile, output, specVersion);
8888
await generate(model, options, dmmf);
8989

9090
console.log(`OpenAPI specification generated for ${specVersion}: ${output}`);
@@ -324,7 +324,7 @@ model Foo {
324324

325325
const { name: output } = tmp.fileSync({ postfix: '.yaml' });
326326

327-
const options = buildOptions(model, modelFile, output, '3.1.0');
327+
const options = buildOptions(model, modelFile, output, specVersion);
328328
await generate(model, options, dmmf);
329329

330330
console.log(`OpenAPI specification generated for ${specVersion}: ${output}`);
@@ -340,6 +340,28 @@ model Foo {
340340
}
341341
});
342342

343+
it('int field as id', async () => {
344+
const { model, dmmf, modelFile } = await loadZModelAndDmmf(`
345+
plugin openapi {
346+
provider = '${normalizePath(path.resolve(__dirname, '../dist'))}'
347+
}
348+
349+
model Foo {
350+
id Int @id @default(autoincrement())
351+
}
352+
`);
353+
354+
const { name: output } = tmp.fileSync({ postfix: '.yaml' });
355+
356+
const options = buildOptions(model, modelFile, output, '3.0.0');
357+
await generate(model, options, dmmf);
358+
console.log(`OpenAPI specification generated: ${output}`);
359+
await OpenAPIParser.validate(output);
360+
361+
const parsed = YAML.parse(fs.readFileSync(output, 'utf-8'));
362+
expect(parsed.components.schemas.Foo.properties.id.type).toBe('integer');
363+
});
364+
343365
it('exposes individual fields from a compound id as attributes', async () => {
344366
const { model, dmmf, modelFile } = await loadZModelAndDmmf(`
345367
plugin openapi {

packages/plugins/swr/package.json

Lines changed: 1 addition & 1 deletion
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": "2.10.2",
4+
"version": "2.11.0",
55
"description": "ZenStack plugin for generating SWR hooks",
66
"main": "index.js",
77
"repository": {

packages/plugins/tanstack-query/package.json

Lines changed: 1 addition & 1 deletion
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": "2.10.2",
4+
"version": "2.11.0",
55
"description": "ZenStack plugin for generating tanstack-query hooks",
66
"main": "index.js",
77
"exports": {

packages/plugins/trpc/package.json

Lines changed: 1 addition & 1 deletion
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": "2.10.2",
4+
"version": "2.11.0",
55
"description": "ZenStack plugin for tRPC",
66
"main": "index.js",
77
"repository": {

packages/runtime/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@zenstackhq/runtime",
33
"displayName": "ZenStack Runtime Library",
4-
"version": "2.10.2",
4+
"version": "2.11.0",
55
"description": "Runtime of ZenStack for both client-side and server-side environments.",
66
"repository": {
77
"type": "git",
@@ -80,6 +80,10 @@
8080
"types": "./zod-utils.d.ts",
8181
"default": "./zod-utils.js"
8282
},
83+
"./encryption": {
84+
"types": "./encryption/index.d.ts",
85+
"default": "./encryption/index.js"
86+
},
8387
"./package.json": {
8488
"default": "./package.json"
8589
}

packages/runtime/src/constants.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,15 @@ export const PRISMA_MINIMUM_VERSION = '5.0.0';
6767
* Prefix for auxiliary relation field generated for delegated models
6868
*/
6969
export const DELEGATE_AUX_RELATION_PREFIX = 'delegate_aux';
70+
71+
/**
72+
* Prisma actions that can have a write payload
73+
*/
74+
export const ACTIONS_WITH_WRITE_PAYLOAD = [
75+
'create',
76+
'createMany',
77+
'createManyAndReturn',
78+
'update',
79+
'updateMany',
80+
'upsert',
81+
];

packages/runtime/src/cross/nested-write-visitor.ts

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import type { FieldInfo, ModelMeta } from './model-meta';
55
import { resolveField } from './model-meta';
66
import { MaybePromise, PrismaWriteActionType, PrismaWriteActions } from './types';
7-
import { getModelFields } from './utils';
7+
import { enumerate, getModelFields } from './utils';
88

99
type NestingPathItem = { field?: FieldInfo; model: string; where: any; unique: boolean };
1010

@@ -310,31 +310,33 @@ export class NestedWriteVisitor {
310310
payload: any,
311311
nestingPath: NestingPathItem[]
312312
) {
313-
for (const field of getModelFields(payload)) {
314-
const fieldInfo = resolveField(this.modelMeta, model, field);
315-
if (!fieldInfo) {
316-
continue;
317-
}
313+
for (const item of enumerate(payload)) {
314+
for (const field of getModelFields(item)) {
315+
const fieldInfo = resolveField(this.modelMeta, model, field);
316+
if (!fieldInfo) {
317+
continue;
318+
}
318319

319-
if (fieldInfo.isDataModel) {
320-
if (payload[field]) {
321-
// recurse into nested payloads
322-
for (const [subAction, subData] of Object.entries<any>(payload[field])) {
323-
if (this.isPrismaWriteAction(subAction) && subData) {
324-
await this.doVisit(fieldInfo.type, subAction, subData, payload[field], fieldInfo, [
325-
...nestingPath,
326-
]);
320+
if (fieldInfo.isDataModel) {
321+
if (item[field]) {
322+
// recurse into nested payloads
323+
for (const [subAction, subData] of Object.entries<any>(item[field])) {
324+
if (this.isPrismaWriteAction(subAction) && subData) {
325+
await this.doVisit(fieldInfo.type, subAction, subData, item[field], fieldInfo, [
326+
...nestingPath,
327+
]);
328+
}
327329
}
328330
}
329-
}
330-
} else {
331-
// visit plain field
332-
if (this.callback.field) {
333-
await this.callback.field(fieldInfo, action, payload[field], {
334-
parent: payload,
335-
nestingPath,
336-
field: fieldInfo,
337-
});
331+
} else {
332+
// visit plain field
333+
if (this.callback.field) {
334+
await this.callback.field(fieldInfo, action, item[field], {
335+
parent: item,
336+
nestingPath,
337+
field: fieldInfo,
338+
});
339+
}
338340
}
339341
}
340342
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import { _decrypt, _encrypt, ENCRYPTION_KEY_BYTES, getKeyDigest, loadKey } from './utils';
2+
3+
/**
4+
* Default encrypter
5+
*/
6+
export class Encrypter {
7+
private key: CryptoKey | undefined;
8+
private keyDigest: string | undefined;
9+
10+
constructor(private readonly encryptionKey: Uint8Array) {
11+
if (encryptionKey.length !== ENCRYPTION_KEY_BYTES) {
12+
throw new Error(`Encryption key must be ${ENCRYPTION_KEY_BYTES} bytes`);
13+
}
14+
}
15+
16+
/**
17+
* Encrypts the given data
18+
*/
19+
async encrypt(data: string): Promise<string> {
20+
if (!this.key) {
21+
this.key = await loadKey(this.encryptionKey, ['encrypt']);
22+
}
23+
24+
if (!this.keyDigest) {
25+
this.keyDigest = await getKeyDigest(this.encryptionKey);
26+
}
27+
28+
return _encrypt(data, this.key, this.keyDigest);
29+
}
30+
}
31+
32+
/**
33+
* Default decrypter
34+
*/
35+
export class Decrypter {
36+
private keys: Array<{ key: CryptoKey; digest: string }> = [];
37+
38+
constructor(private readonly decryptionKeys: Uint8Array[]) {
39+
if (decryptionKeys.length === 0) {
40+
throw new Error('At least one decryption key must be provided');
41+
}
42+
43+
for (const key of decryptionKeys) {
44+
if (key.length !== ENCRYPTION_KEY_BYTES) {
45+
throw new Error(`Decryption key must be ${ENCRYPTION_KEY_BYTES} bytes`);
46+
}
47+
}
48+
}
49+
50+
/**
51+
* Decrypts the given data
52+
*/
53+
async decrypt(data: string): Promise<string> {
54+
if (this.keys.length === 0) {
55+
this.keys = await Promise.all(
56+
this.decryptionKeys.map(async (key) => ({
57+
key: await loadKey(key, ['decrypt']),
58+
digest: await getKeyDigest(key),
59+
}))
60+
);
61+
}
62+
63+
return _decrypt(data, async (digest) =>
64+
this.keys.filter((entry) => entry.digest === digest).map((entry) => entry.key)
65+
);
66+
}
67+
}

0 commit comments

Comments
 (0)