@zenstackhq/server 3.4.0-beta.3 → 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.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");