@zenstackhq/server 3.4.0-beta.4 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.cjs +81 -0
- package/dist/api.cjs.map +1 -1
- package/dist/api.d.cts +1 -0
- package/dist/api.d.ts +1 -0
- package/dist/api.js +82 -1
- package/dist/api.js.map +1 -1
- package/dist/fastify.d.cts +1 -1
- package/dist/fastify.d.ts +1 -1
- package/package.json +13 -13
package/dist/api.d.cts
CHANGED
|
@@ -147,6 +147,7 @@ declare class RPCApiHandler<Schema extends SchemaDef = SchemaDef> implements Api
|
|
|
147
147
|
get schema(): Schema;
|
|
148
148
|
get log(): LogConfig | undefined;
|
|
149
149
|
handleRequest({ client, method, path, query, requestBody }: RequestContext<Schema>): Promise<Response>;
|
|
150
|
+
private handleTransaction;
|
|
150
151
|
private handleProcedureRequest;
|
|
151
152
|
private isValidModel;
|
|
152
153
|
private makeBadInputErrorResponse;
|
package/dist/api.d.ts
CHANGED
|
@@ -147,6 +147,7 @@ declare class RPCApiHandler<Schema extends SchemaDef = SchemaDef> implements Api
|
|
|
147
147
|
get schema(): Schema;
|
|
148
148
|
get log(): LogConfig | undefined;
|
|
149
149
|
handleRequest({ client, method, path, query, requestBody }: RequestContext<Schema>): Promise<Response>;
|
|
150
|
+
private handleTransaction;
|
|
150
151
|
private handleProcedureRequest;
|
|
151
152
|
private isValidModel;
|
|
152
153
|
private makeBadInputErrorResponse;
|
package/dist/api.js
CHANGED
|
@@ -2032,11 +2032,13 @@ ${err.stack}` : "Unknown error");
|
|
|
2032
2032
|
|
|
2033
2033
|
// src/api/rpc/index.ts
|
|
2034
2034
|
import { lowerCaseFirst as lowerCaseFirst2, safeJSONStringify } from "@zenstackhq/common-helpers";
|
|
2035
|
-
import { ORMError as ORMError3, ORMErrorReason as ORMErrorReason2 } from "@zenstackhq/orm";
|
|
2035
|
+
import { CoreCrudOperations, ORMError as ORMError3, ORMErrorReason as ORMErrorReason2 } from "@zenstackhq/orm";
|
|
2036
2036
|
import SuperJSON4 from "superjson";
|
|
2037
2037
|
import { match as match3 } from "ts-pattern";
|
|
2038
2038
|
import z3 from "zod";
|
|
2039
2039
|
import { fromError as fromError2 } from "zod-validation-error/v4";
|
|
2040
|
+
var TRANSACTION_ROUTE_PREFIX = "$transaction";
|
|
2041
|
+
var VALID_OPS = new Set(CoreCrudOperations);
|
|
2040
2042
|
registerCustomSerializers();
|
|
2041
2043
|
var RPCApiHandler = class {
|
|
2042
2044
|
static {
|
|
@@ -2079,6 +2081,14 @@ var RPCApiHandler = class {
|
|
|
2079
2081
|
requestBody
|
|
2080
2082
|
});
|
|
2081
2083
|
}
|
|
2084
|
+
if (model === TRANSACTION_ROUTE_PREFIX) {
|
|
2085
|
+
return this.handleTransaction({
|
|
2086
|
+
client,
|
|
2087
|
+
method: method.toUpperCase(),
|
|
2088
|
+
type: op,
|
|
2089
|
+
requestBody
|
|
2090
|
+
});
|
|
2091
|
+
}
|
|
2082
2092
|
model = lowerCaseFirst2(model);
|
|
2083
2093
|
method = method.toUpperCase();
|
|
2084
2094
|
let args;
|
|
@@ -2177,6 +2187,77 @@ var RPCApiHandler = class {
|
|
|
2177
2187
|
}
|
|
2178
2188
|
}
|
|
2179
2189
|
}
|
|
2190
|
+
async handleTransaction({ client, method, type, requestBody }) {
|
|
2191
|
+
if (method !== "POST") {
|
|
2192
|
+
return this.makeBadInputErrorResponse("invalid request method, only POST is supported");
|
|
2193
|
+
}
|
|
2194
|
+
if (type !== "sequential") {
|
|
2195
|
+
return this.makeBadInputErrorResponse(`unsupported transaction type: ${type}`);
|
|
2196
|
+
}
|
|
2197
|
+
if (!requestBody || !Array.isArray(requestBody) || requestBody.length === 0) {
|
|
2198
|
+
return this.makeBadInputErrorResponse("request body must be a non-empty array of operations");
|
|
2199
|
+
}
|
|
2200
|
+
const processedOps = [];
|
|
2201
|
+
for (let i = 0; i < requestBody.length; i++) {
|
|
2202
|
+
const item = requestBody[i];
|
|
2203
|
+
if (!item || typeof item !== "object") {
|
|
2204
|
+
return this.makeBadInputErrorResponse(`operation at index ${i} must be an object`);
|
|
2205
|
+
}
|
|
2206
|
+
const { model: itemModel, op: itemOp, args: itemArgs } = item;
|
|
2207
|
+
if (!itemModel || typeof itemModel !== "string") {
|
|
2208
|
+
return this.makeBadInputErrorResponse(`operation at index ${i} is missing a valid "model" field`);
|
|
2209
|
+
}
|
|
2210
|
+
if (!itemOp || typeof itemOp !== "string") {
|
|
2211
|
+
return this.makeBadInputErrorResponse(`operation at index ${i} is missing a valid "op" field`);
|
|
2212
|
+
}
|
|
2213
|
+
if (!VALID_OPS.has(itemOp)) {
|
|
2214
|
+
return this.makeBadInputErrorResponse(`operation at index ${i} has invalid op: ${itemOp}`);
|
|
2215
|
+
}
|
|
2216
|
+
if (!this.isValidModel(client, lowerCaseFirst2(itemModel))) {
|
|
2217
|
+
return this.makeBadInputErrorResponse(`operation at index ${i} has unknown model: ${itemModel}`);
|
|
2218
|
+
}
|
|
2219
|
+
if (itemArgs !== void 0 && itemArgs !== null && (typeof itemArgs !== "object" || Array.isArray(itemArgs))) {
|
|
2220
|
+
return this.makeBadInputErrorResponse(`operation at index ${i} has invalid "args" field`);
|
|
2221
|
+
}
|
|
2222
|
+
const { result: processedArgs, error: argsError } = await this.processRequestPayload(itemArgs ?? {});
|
|
2223
|
+
if (argsError) {
|
|
2224
|
+
return this.makeBadInputErrorResponse(`operation at index ${i}: ${argsError}`);
|
|
2225
|
+
}
|
|
2226
|
+
processedOps.push({
|
|
2227
|
+
model: lowerCaseFirst2(itemModel),
|
|
2228
|
+
op: itemOp,
|
|
2229
|
+
args: processedArgs
|
|
2230
|
+
});
|
|
2231
|
+
}
|
|
2232
|
+
try {
|
|
2233
|
+
const promises = processedOps.map(({ model, op, args }) => {
|
|
2234
|
+
return client[model][op](args);
|
|
2235
|
+
});
|
|
2236
|
+
log(this.options.log, "debug", () => `handling "$transaction" request with ${promises.length} operations`);
|
|
2237
|
+
const clientResult = await client.$transaction(promises);
|
|
2238
|
+
const { json, meta } = SuperJSON4.serialize(clientResult);
|
|
2239
|
+
const responseBody = {
|
|
2240
|
+
data: json
|
|
2241
|
+
};
|
|
2242
|
+
if (meta) {
|
|
2243
|
+
responseBody.meta = {
|
|
2244
|
+
serialization: meta
|
|
2245
|
+
};
|
|
2246
|
+
}
|
|
2247
|
+
const response = {
|
|
2248
|
+
status: 200,
|
|
2249
|
+
body: responseBody
|
|
2250
|
+
};
|
|
2251
|
+
log(this.options.log, "debug", () => `sending response for "$transaction" request: ${safeJSONStringify(response)}`);
|
|
2252
|
+
return response;
|
|
2253
|
+
} catch (err) {
|
|
2254
|
+
log(this.options.log, "error", `error occurred when handling "$transaction" request`, err);
|
|
2255
|
+
if (err instanceof ORMError3) {
|
|
2256
|
+
return this.makeORMErrorResponse(err);
|
|
2257
|
+
}
|
|
2258
|
+
return this.makeGenericErrorResponse(err);
|
|
2259
|
+
}
|
|
2260
|
+
}
|
|
2180
2261
|
async handleProcedureRequest({ client, method, proc, query, requestBody }) {
|
|
2181
2262
|
if (!proc) {
|
|
2182
2263
|
return this.makeBadInputErrorResponse("missing procedure name");
|