@web-ts-toolkit/message-service 0.52.0 → 0.53.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.
Files changed (6) hide show
  1. package/README.md +833 -11
  2. package/index.d.mts +270 -943
  3. package/index.d.ts +270 -943
  4. package/index.js +174 -43
  5. package/index.mjs +172 -43
  6. package/package.json +3 -3
package/index.js CHANGED
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
33
  ActionConflictError: () => ActionConflictError,
34
+ ActionInputValidationError: () => ActionInputValidationError,
34
35
  ActionNotAllowedError: () => ActionNotAllowedError,
35
36
  ActionNotFoundError: () => ActionNotFoundError,
36
37
  ActionNotificationPendingError: () => ActionNotificationPendingError,
@@ -60,6 +61,7 @@ __export(index_exports, {
60
61
  MessageTransactionRequiredError: () => MessageTransactionRequiredError,
61
62
  NoopEmailProvider: () => NoopEmailProvider,
62
63
  NoopPaymentProvider: () => NoopPaymentProvider,
64
+ PaymentProviderRequiredError: () => PaymentProviderRequiredError,
63
65
  PaymentSessionCompensationAggregateError: () => PaymentSessionCompensationAggregateError,
64
66
  PaymentSessionCompensationError: () => PaymentSessionCompensationError,
65
67
  TemplateNotFoundError: () => TemplateNotFoundError,
@@ -290,25 +292,32 @@ function includesAction(templateCd, actionCd, registry) {
290
292
 
291
293
  // src/schemas/methods.ts
292
294
  var import_mongoose2 = require("mongoose");
295
+ function isValidMessageUserRoles(roles) {
296
+ if (roles === void 0) return true;
297
+ if (!Array.isArray(roles)) return false;
298
+ for (const role of roles) {
299
+ if (typeof role !== "string") return false;
300
+ }
301
+ return true;
302
+ }
293
303
  function isSender(user) {
294
- const fromUser = normalizeStoredUserId(this.fromUser);
295
- const userId = normalizeMessageUserId(user);
296
- return fromUser !== null && userId !== null && fromUser === userId;
304
+ if (!isValidMessageUserRoles(user?.roles)) return false;
305
+ return matchesStoredUserId(this.fromUser, user);
297
306
  }
298
307
  function isReceiver(user) {
299
- const toUser = normalizeStoredUserId(this.toUser);
300
- const userId = normalizeMessageUserId(user);
301
- return toUser !== null && userId !== null && toUser === userId || this.toRoles.some((r) => user.roles?.includes(r));
308
+ const roles = user?.roles;
309
+ if (!isValidMessageUserRoles(roles)) return false;
310
+ return matchesStoredUserId(this.toUser, user) || this.toRoles.some((r) => roles?.includes(r));
302
311
  }
303
- function normalizeMessageUserId(user) {
304
- return normalizeId(user?._id);
305
- }
306
- function normalizeStoredUserId(value) {
307
- if (value instanceof import_mongoose2.Types.ObjectId) return value.toHexString();
308
- if (value !== null && typeof value === "object") {
309
- return normalizeId(value._id);
310
- }
311
- return normalizeId(value);
312
+ function matchesStoredUserId(value, user) {
313
+ const stored = value instanceof import_mongoose2.Types.ObjectId || value === null || typeof value !== "object" ? value : value._id;
314
+ const userId = normalizeId(user?._id);
315
+ if (userId === null) return false;
316
+ if (stored instanceof import_mongoose2.Types.ObjectId && /^[a-f\d]{24}$/i.test(userId)) {
317
+ return stored.toHexString() === userId.toLowerCase();
318
+ }
319
+ const storedId = normalizeId(stored);
320
+ return storedId !== null && storedId === userId;
312
321
  }
313
322
  function normalizeId(value) {
314
323
  if (typeof value === "string") {
@@ -318,6 +327,11 @@ function normalizeId(value) {
318
327
  return value instanceof import_mongoose2.Types.ObjectId ? value.toHexString() : null;
319
328
  }
320
329
 
330
+ // src/user-id.ts
331
+ function normalizeUserIdForStorage(id) {
332
+ return typeof id === "string" ? id.trim() : id;
333
+ }
334
+
321
335
  // src/errors.ts
322
336
  var TRANSACTION_REQUIRED_MESSAGE = "message-service requires MongoDB replica set or sharded-cluster transactions for idempotent batch creation";
323
337
  var MessageNotFoundError = class extends Error {
@@ -370,6 +384,16 @@ var ActionNotAllowedError = class extends Error {
370
384
  markRuntimeError(this, this.name);
371
385
  }
372
386
  };
387
+ var ActionInputValidationError = class extends Error {
388
+ static [Symbol.hasInstance](value) {
389
+ return isRuntimeError(value, "ActionInputValidationError");
390
+ }
391
+ constructor(message = "invalid action input") {
392
+ super(message);
393
+ this.name = "ActionInputValidationError";
394
+ markRuntimeError(this, this.name);
395
+ }
396
+ };
373
397
  var InvalidMessageUserError = class extends Error {
374
398
  static [Symbol.hasInstance](value) {
375
399
  return isRuntimeError(value, "InvalidMessageUserError");
@@ -525,6 +549,16 @@ var MessageModelResolutionError = class extends Error {
525
549
  }
526
550
  }
527
551
  };
552
+ var PaymentProviderRequiredError = class extends Error {
553
+ static [Symbol.hasInstance](value) {
554
+ return isRuntimeError(value, "PaymentProviderRequiredError");
555
+ }
556
+ constructor() {
557
+ super("paymentProvider is required for messages with a non-empty paymentCd");
558
+ this.name = "PaymentProviderRequiredError";
559
+ markRuntimeError(this, this.name);
560
+ }
561
+ };
528
562
  var PaymentSessionCompensationError = class extends Error {
529
563
  static [Symbol.hasInstance](value) {
530
564
  return isRuntimeError(value, "PaymentSessionCompensationError") || isRuntimeError(value, "PaymentSessionCompensationAggregateError");
@@ -735,6 +769,9 @@ function requireMessageUserId(user) {
735
769
  if (!isValidMessageUserId(id)) {
736
770
  throw new InvalidMessageUserError();
737
771
  }
772
+ if (!isValidMessageUserRoles(user.roles)) {
773
+ throw new InvalidMessageUserError("user.roles must be an array of strings when provided");
774
+ }
738
775
  return typeof id === "string" ? id.trim() : String(id);
739
776
  }
740
777
  var MessageService = class {
@@ -774,7 +811,11 @@ var MessageService = class {
774
811
  );
775
812
  this.clientRequestDelay = options.clientRequestDelay ?? this.delay;
776
813
  this.clientRequestNow = options.clientRequestNow ?? (() => Date.now());
777
- this.actionLeaseMs = ACTION_LEASE_MS;
814
+ this.actionLeaseMs = this.validateDurationOption(
815
+ "actionLeaseMs",
816
+ options.actionLeaseMs === void 0 ? ACTION_LEASE_MS : options.actionLeaseMs,
817
+ 1
818
+ );
778
819
  }
779
820
  // -------------------------------------------------------------------------
780
821
  // Message lookup
@@ -820,10 +861,25 @@ var MessageService = class {
820
861
  *
821
862
  * User-facing entry point: `user` (and `payerUser` when provided) must
822
863
  * carry a valid principal id (non-empty string or ObjectId; numbers,
823
- * arrays, and plain objects are rejected with `InvalidMessageUserError`)
864
+ * arrays, and plain objects are rejected with `InvalidMessageUserError`),
865
+ * and optional `roles` must be undefined or an array of strings,
824
866
  * before any template, provider, or model effect runs. Both the
825
867
  * idempotent (`clientRequestId`) and direct branches enforce this.
826
868
  *
869
+ * Each `PrepareResult.documents` selects that item's attachments; `[]` clears
870
+ * them. Omission/undefined retains the original caller `payload.documents`
871
+ * fallback, independently of the prepared business payload. Selected arrays
872
+ * are copied per item without mutating caller/preparation arrays.
873
+ *
874
+ * Each fresh item's `item.paymentCd || template.paymentCd || ''` requests a
875
+ * session when nonempty, independently of recipients. An empty item override
876
+ * inherits the template code. Absent/null providers throw
877
+ * `PaymentProviderRequiredError`; configured-provider rejections propagate and
878
+ * null/empty session returns throw `Error('payment session creation failed')`.
879
+ * The payer remains `(payerUser || user)._id`, not the prepared sender/recipient.
880
+ * Direct batches retain committed prefixes; idempotent batches commit atomically
881
+ * and compensate all known uncommitted sessions on failure.
882
+ *
827
883
  * Idempotent replay contract (MSGF-06): a completed same-scope replay
828
884
  * returns the current active/archive records merged from both collections
829
885
  * (`Array<IMessage | IMessageArchive>`), sorted by exact
@@ -991,15 +1047,17 @@ var MessageService = class {
991
1047
  * or matches one of the recipient's roles.
992
1048
  *
993
1049
  * User-facing operation: validates `user` via the shared principal
994
- * contract. Returns `IMessage[]` ordered by `{ createdAt: -1, _id: -1 }`.
1050
+ * contract before model resolution. Returns `IMessage[]` ordered by
1051
+ * `{ createdAt: -1, _id: -1 }`.
995
1052
  */
996
1053
  async listMessages(params) {
997
1054
  const { user, limit: rawLimit, skip: rawSkip, populate } = params;
998
1055
  const limit = this.normalizeListLimit(rawLimit);
999
1056
  const skip = this.normalizeListSkip(rawSkip);
1057
+ const filter = this.buildVisibilityFilter(user);
1000
1058
  const Message = this.resolveModel("active");
1001
1059
  const query = this.applyPopulate(
1002
- Message.find(this.buildVisibilityFilter(user)).sort({ createdAt: -1, _id: -1 }).skip(skip).limit(limit),
1060
+ Message.find(filter).sort({ createdAt: -1, _id: -1 }).skip(skip).limit(limit),
1003
1061
  populate
1004
1062
  );
1005
1063
  return query;
@@ -1008,11 +1066,13 @@ var MessageService = class {
1008
1066
  * Count active (non-archived) messages visible to a user.
1009
1067
  * Useful for badge indicators ("3 new messages").
1010
1068
  *
1011
- * User-facing operation: validates `user` via the shared principal contract.
1069
+ * User-facing operation: validates `user` via the shared principal contract
1070
+ * before model resolution.
1012
1071
  */
1013
1072
  async countMessages(user) {
1073
+ const filter = this.buildVisibilityFilter(user);
1014
1074
  const Message = this.resolveModel("active");
1015
- return Message.countDocuments(this.buildVisibilityFilter(user));
1075
+ return Message.countDocuments(filter);
1016
1076
  }
1017
1077
  // -------------------------------------------------------------------------
1018
1078
  // Get actions for a message
@@ -1039,8 +1099,7 @@ var MessageService = class {
1039
1099
  * render empty), distinct from creation-time `templateData`.
1040
1100
  */
1041
1101
  async getActions(messageId, usertype, options) {
1042
- const user = requireMessageUserId(options?.user);
1043
- const authorizedUser = { ...options.user, _id: user };
1102
+ const authorizedUser = this.buildAuthorizedUser(options?.user);
1044
1103
  const message = await this.findMessage(messageId);
1045
1104
  if (!message) return null;
1046
1105
  if (!options.isAdmin) {
@@ -1074,18 +1133,33 @@ var MessageService = class {
1074
1133
  * Execute an action with a durable claim, stable handler attempt key,
1075
1134
  * transactional archive, and post-commit sender notification status.
1076
1135
  *
1136
+ * `actionLeaseMs` controls when a same-action retry may take over. Expiry alone
1137
+ * does not cancel the handler or reject a still-current owner; takeover fences
1138
+ * stale completion/failure writes with `ActionConflictError`. There is no
1139
+ * heartbeat or renewal; external effects must deduplicate by `actionAttemptId`.
1140
+ *
1077
1141
  * User-facing operation: `data.user` is validated before any template or
1078
- * model effect. Authorization and handler selection bind to the persisted
1142
+ * model effect. Handler users contain only the documented `MessageUser`
1143
+ * fields, read explicitly (including getters), with a copied roles array;
1144
+ * the caller's user and original `req` are not modified.
1145
+ * Authorization and handler selection bind to the persisted
1079
1146
  * claim, not the caller-supplied `data.message` copy. Archived messages
1080
1147
  * are re-read by id on their owning connection and apply a stored
1081
1148
  * sender/receiver relationship gate before disclosing current attempt IDs
1082
1149
  * or notification state. A missing archive throws `MessageNotFoundError`.
1150
+ * `data.input` is explicit submitted data, never inferred from `req`. The
1151
+ * current claimed action's optional synchronous pure `parseInput` supplies
1152
+ * `ctx.input` after authorization. Invalid input throws
1153
+ * `ActionInputValidationError`; unexpected parser failures preserve their
1154
+ * cause as server errors. Both use fenced pre-handler release: fresh claims
1155
+ * return to active, retries retain their stable attempt/action as retryable.
1156
+ * Hosts own input bounds, runtime validation, and durable command/result
1157
+ * binding at first host reservation (not at package claim).
1083
1158
  * Returns the handler's value (`unknown`; templates
1084
1159
  * document their own shape).
1085
1160
  */
1086
1161
  async handleAction(templateCd, actionCd, data) {
1087
- const userId = this.requireUserId(data.user);
1088
- const authorizedUser = { ...data.user, _id: userId };
1162
+ const authorizedUser = this.buildAuthorizedUser(data.user);
1089
1163
  if (this.isArchivedMessage(data.message)) {
1090
1164
  const archived = await this.resolveModel("archive", data.message).findById(
1091
1165
  data.message._id
@@ -1105,25 +1179,28 @@ var MessageService = class {
1105
1179
  }
1106
1180
  const claim = await this.claimAction(data.message, actionCd, authorizedUser);
1107
1181
  let authorizedAction;
1182
+ let input;
1108
1183
  try {
1109
1184
  authorizedAction = this.authorizeClaimedAction(templateCd, actionCd, claim.message, authorizedUser, {
1110
1185
  permissions: data.permissions
1111
1186
  });
1112
- } catch (authError) {
1187
+ input = this.parseActionInput(authorizedAction, data.input);
1188
+ } catch (preHandlerError) {
1113
1189
  try {
1114
- await this.releaseDeniedActionClaim(claim, authError);
1190
+ await this.releaseDeniedActionClaim(claim, preHandlerError);
1115
1191
  } catch (releaseError) {
1116
1192
  if (!(releaseError instanceof ActionConflictError)) {
1117
1193
  throw releaseError;
1118
1194
  }
1119
1195
  }
1120
- throw authError;
1196
+ throw preHandlerError;
1121
1197
  }
1122
1198
  const ctx = this.buildActionContext({
1123
1199
  ...data,
1124
1200
  user: authorizedUser,
1125
1201
  message: claim.message,
1126
- actionAttemptId: claim.actionAttemptId
1202
+ actionAttemptId: claim.actionAttemptId,
1203
+ input
1127
1204
  });
1128
1205
  let result;
1129
1206
  try {
@@ -1244,13 +1321,14 @@ var MessageService = class {
1244
1321
  }
1245
1322
  }
1246
1323
  async buildMessageDocument(template, m, ctx, clientRequestScope, clientRequestItemIndex) {
1247
- const toUser = m.toUser ?? null;
1248
- const toRoles = (m.toRoles && m.toRoles.length > 0 ? m.toRoles : this.adminRoles).slice();
1324
+ const toUser = m.toUser == null ? null : normalizeUserIdForStorage(m.toUser);
1325
+ const toRoles = (m.toRoles && m.toRoles.length > 0 ? m.toRoles : toUser === null ? this.adminRoles : []).slice();
1249
1326
  const type = m.type || template.type || "notification";
1250
1327
  const paymentCd = m.paymentCd || template.paymentCd || "";
1251
1328
  let paymentSession = null;
1252
1329
  try {
1253
- if (paymentCd && this.paymentProvider && (toUser || toRoles.length > 0)) {
1330
+ if (paymentCd) {
1331
+ if (!this.paymentProvider) throw new PaymentProviderRequiredError();
1254
1332
  paymentSession = await this.paymentProvider.createSession(
1255
1333
  (ctx.payerUser || ctx.user)._id,
1256
1334
  paymentCd,
@@ -1262,12 +1340,12 @@ var MessageService = class {
1262
1340
  return {
1263
1341
  type,
1264
1342
  templateCd: template.templateCd,
1265
- fromUser: m.fromUser || ctx.user._id,
1343
+ fromUser: normalizeUserIdForStorage(m.fromUser || ctx.user._id),
1266
1344
  toUser,
1267
1345
  toRoles,
1268
1346
  senderContent: interpolated.senderContent,
1269
1347
  receiverContent: interpolated.receiverContent,
1270
- documents: ctx.payload.documents || [],
1348
+ documents: (m.documents !== void 0 ? m.documents : ctx.payload.documents || []).slice(),
1271
1349
  paymentSession,
1272
1350
  paymentCd,
1273
1351
  payload: m.payload || ctx.payload,
@@ -1377,12 +1455,36 @@ var MessageService = class {
1377
1455
  message: data.message,
1378
1456
  user: data.user,
1379
1457
  actionAttemptId: data.actionAttemptId,
1458
+ input: data.input,
1380
1459
  getModel: this.getTemplateModelResolver(data.message),
1381
1460
  expireSession: this.expirePaymentSession,
1382
1461
  refundPayment: this.refundPaymentSession,
1383
1462
  req: data.req
1384
1463
  };
1385
1464
  }
1465
+ parseActionInput(action, input) {
1466
+ if (!action.parseInput) return input;
1467
+ try {
1468
+ const parsed = action.parseInput(input);
1469
+ if (parsed !== null && (typeof parsed === "object" || typeof parsed === "function")) {
1470
+ const then = parsed.then;
1471
+ if (typeof then === "function") {
1472
+ void Promise.resolve().then(() => {
1473
+ const discarded = Reflect.apply(then, parsed, [() => {
1474
+ }, () => {
1475
+ }]);
1476
+ return discarded === parsed ? void 0 : discarded;
1477
+ }).catch(() => {
1478
+ });
1479
+ throw new Error("action.parseInput must be synchronous");
1480
+ }
1481
+ }
1482
+ return parsed;
1483
+ } catch (error) {
1484
+ if (error instanceof ActionInputValidationError) throw error;
1485
+ throw new Error("action.parseInput failed", { cause: error });
1486
+ }
1487
+ }
1386
1488
  async claimAction(message, actionCd, user) {
1387
1489
  const Message = this.resolveModel("active", message);
1388
1490
  const messageId = message._id;
@@ -1709,6 +1811,11 @@ var MessageService = class {
1709
1811
  requireUserId(user) {
1710
1812
  return requireMessageUserId(user);
1711
1813
  }
1814
+ buildAuthorizedUser(user) {
1815
+ const _id = this.requireUserId(user);
1816
+ const { id, roles, displayName } = user;
1817
+ return { _id, id, roles: roles?.slice(), displayName };
1818
+ }
1712
1819
  async findByClientRequestScope(scope) {
1713
1820
  const Message = this.resolveModel("active");
1714
1821
  const docs = await Message.find(scope).sort({ clientRequestItemIndex: 1, _id: 1 }).limit(Number.MAX_SAFE_INTEGER);
@@ -2002,7 +2109,7 @@ function createArchiveMethod(ctx) {
2002
2109
  const archiveDoc = {
2003
2110
  ...source,
2004
2111
  actionCd,
2005
- archivedBy,
2112
+ archivedBy: normalizeUserIdForStorage(archivedBy),
2006
2113
  archivedAt: /* @__PURE__ */ new Date(),
2007
2114
  actionNotificationState: "none",
2008
2115
  actionNotificationError: null,
@@ -2071,22 +2178,31 @@ function createPostSaveEmailHook(ctx) {
2071
2178
  if (!title || ctx.emailNotificationExclusions.includes(normalizeEmailTitle(title))) {
2072
2179
  return;
2073
2180
  }
2074
- let user;
2181
+ let email;
2075
2182
  try {
2076
- const User = resolveDocumentModel(this, ctx.userModelName, "user");
2077
- user = await User.findById(this.toUser).select("email").lean();
2183
+ if (ctx.emailRecipientResolver) {
2184
+ email = await ctx.emailRecipientResolver({
2185
+ recipientId: this.toUser,
2186
+ connection: this.constructor.db,
2187
+ message: this
2188
+ });
2189
+ } else {
2190
+ const User = resolveDocumentModel(this, ctx.userModelName, "user");
2191
+ const user = await User.findById(this.toUser).select("email").lean();
2192
+ email = user?.email;
2193
+ }
2078
2194
  } catch (error) {
2079
2195
  await reportEmailFailure(ctx, "recipientLookup", error, this, title);
2080
2196
  return;
2081
2197
  }
2082
- if (!user?.email) {
2198
+ if (!email) {
2083
2199
  return;
2084
2200
  }
2085
2201
  const long = this.receiverContent.long || "";
2086
2202
  const short = this.receiverContent.short || "";
2087
2203
  const body = long.length > short.length ? long : short;
2088
2204
  try {
2089
- await ctx.emailNotifier(user.email, title, body);
2205
+ await ctx.emailNotifier(email, title, body);
2090
2206
  } catch (error) {
2091
2207
  await reportEmailFailure(ctx, "notifier", error, this, title);
2092
2208
  }
@@ -2116,6 +2232,7 @@ function resolveConfig(config) {
2116
2232
  return {
2117
2233
  onTransactionCleanupFailure: config?.onTransactionCleanupFailure,
2118
2234
  emailNotifier: config?.emailNotifier ?? null,
2235
+ emailRecipientResolver: config?.emailRecipientResolver,
2119
2236
  onEmailDeliveryFailure: config?.onEmailDeliveryFailure,
2120
2237
  emailNotificationExclusions: (config?.emailNotificationExclusions ?? []).map(normalizeEmailTitle),
2121
2238
  userModelName: config?.userModelName ?? "User",
@@ -2125,7 +2242,7 @@ function resolveConfig(config) {
2125
2242
  }
2126
2243
  function buildMessageSchema(config) {
2127
2244
  const resolved = resolveConfig(config);
2128
- if (resolved.emailNotifier) {
2245
+ if (resolved.emailNotifier && !resolved.emailRecipientResolver) {
2129
2246
  assertModelRegistered(resolved.connection, resolved.userModelName, "user");
2130
2247
  }
2131
2248
  const schema = new import_mongoose4.default.Schema(BaseMessageFields, {
@@ -2176,6 +2293,7 @@ function buildMessageSchema(config) {
2176
2293
  "save",
2177
2294
  createPostSaveEmailHook({
2178
2295
  emailNotifier: resolved.emailNotifier,
2296
+ emailRecipientResolver: resolved.emailRecipientResolver,
2179
2297
  onEmailDeliveryFailure: resolved.onEmailDeliveryFailure,
2180
2298
  emailNotificationExclusions: resolved.emailNotificationExclusions,
2181
2299
  userModelName: resolved.userModelName
@@ -2402,6 +2520,9 @@ function isMongooseCastError(error) {
2402
2520
  return !!error && typeof error === "object" && error.name === "CastError";
2403
2521
  }
2404
2522
  function mapServiceError(error) {
2523
+ if (error instanceof ActionInputValidationError) {
2524
+ throw new import_express_json_router.default.clientErrors.BadRequestError("invalid action input");
2525
+ }
2405
2526
  if (error instanceof MessageNotFoundError) {
2406
2527
  throw new import_express_json_router.default.clientErrors.NotFoundError("message not found");
2407
2528
  }
@@ -2453,6 +2574,7 @@ var SERVICE_CONSTRUCTION_KEYS = [
2453
2574
  "registry",
2454
2575
  "defaultListLimit",
2455
2576
  "maxListLimit",
2577
+ "actionLeaseMs",
2456
2578
  "clientRequestLeaseMs",
2457
2579
  "clientRequestWaitMs",
2458
2580
  "clientRequestPollMs",
@@ -2491,6 +2613,7 @@ function createMessageRoutes(options) {
2491
2613
  registry: construction.registry,
2492
2614
  defaultListLimit: construction.defaultListLimit,
2493
2615
  maxListLimit: construction.maxListLimit,
2616
+ actionLeaseMs: construction.actionLeaseMs,
2494
2617
  clientRequestLeaseMs: construction.clientRequestLeaseMs,
2495
2618
  clientRequestWaitMs: construction.clientRequestWaitMs,
2496
2619
  clientRequestPollMs: construction.clientRequestPollMs,
@@ -2561,7 +2684,13 @@ function createMessageRoutes(options) {
2561
2684
  }
2562
2685
  const permissions = getPermissions(req);
2563
2686
  try {
2564
- return await service.handleAction(message.templateCd, actionCd, { message, user, permissions, req });
2687
+ return await service.handleAction(message.templateCd, actionCd, {
2688
+ message,
2689
+ user,
2690
+ permissions,
2691
+ input: req.body,
2692
+ req
2693
+ });
2565
2694
  } catch (error) {
2566
2695
  if (error instanceof ActionNotificationPendingError) {
2567
2696
  return new import_express_json_router.default.success.Accepted({ message: error.message, actionAttemptId: error.actionAttemptId });
@@ -2585,6 +2714,7 @@ function defaultGetIdentity(req) {
2585
2714
  // Annotate the CommonJS export names for ESM import in node:
2586
2715
  0 && (module.exports = {
2587
2716
  ActionConflictError,
2717
+ ActionInputValidationError,
2588
2718
  ActionNotAllowedError,
2589
2719
  ActionNotFoundError,
2590
2720
  ActionNotificationPendingError,
@@ -2614,6 +2744,7 @@ function defaultGetIdentity(req) {
2614
2744
  MessageTransactionRequiredError,
2615
2745
  NoopEmailProvider,
2616
2746
  NoopPaymentProvider,
2747
+ PaymentProviderRequiredError,
2617
2748
  PaymentSessionCompensationAggregateError,
2618
2749
  PaymentSessionCompensationError,
2619
2750
  TemplateNotFoundError,