@rapidmx/mapi-plugin 1.0.0-beta.3

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 (180) hide show
  1. package/LICENSE +382 -0
  2. package/README.md +60 -0
  3. package/dist/lib/BaseMapiEmsmdbRoute.js +297 -0
  4. package/dist/lib/BaseMapiEmsmdbRoute.js.map +1 -0
  5. package/dist/lib/BaseMapiNspiRoute.js +114 -0
  6. package/dist/lib/BaseMapiNspiRoute.js.map +1 -0
  7. package/dist/lib/MapiSessionManager.js +119 -0
  8. package/dist/lib/MapiSessionManager.js.map +1 -0
  9. package/dist/lib/RopDispatcher.js +34 -0
  10. package/dist/lib/RopDispatcher.js.map +1 -0
  11. package/dist/lib/codec/AppointmentRecurrence.js +314 -0
  12. package/dist/lib/codec/AppointmentRecurrence.js.map +1 -0
  13. package/dist/lib/codec/BufferCursor.js +213 -0
  14. package/dist/lib/codec/BufferCursor.js.map +1 -0
  15. package/dist/lib/codec/GlobalObjectId.js +92 -0
  16. package/dist/lib/codec/GlobalObjectId.js.map +1 -0
  17. package/dist/lib/codec/MapiGuid.js +41 -0
  18. package/dist/lib/codec/MapiGuid.js.map +1 -0
  19. package/dist/lib/codec/MapiTimeZone.js +93 -0
  20. package/dist/lib/codec/MapiTimeZone.js.map +1 -0
  21. package/dist/lib/codec/PropertyValue.js +214 -0
  22. package/dist/lib/codec/PropertyValue.js.map +1 -0
  23. package/dist/lib/codec/RopBuffer.js +26 -0
  24. package/dist/lib/codec/RopBuffer.js.map +1 -0
  25. package/dist/lib/codec/TypedString.js +54 -0
  26. package/dist/lib/codec/TypedString.js.map +1 -0
  27. package/dist/lib/index.js +99 -0
  28. package/dist/lib/index.js.map +1 -0
  29. package/dist/lib/mongo/MapiEmsmdbRouteMongo.js +84 -0
  30. package/dist/lib/mongo/MapiEmsmdbRouteMongo.js.map +1 -0
  31. package/dist/lib/mongo/MapiNspiRouteMongo.js +33 -0
  32. package/dist/lib/mongo/MapiNspiRouteMongo.js.map +1 -0
  33. package/dist/lib/mongo/index.js +7 -0
  34. package/dist/lib/mongo/index.js.map +1 -0
  35. package/dist/lib/mongo.js +6 -0
  36. package/dist/lib/mongo.js.map +1 -0
  37. package/dist/lib/nspi/NspiBindHandler.js +54 -0
  38. package/dist/lib/nspi/NspiBindHandler.js.map +1 -0
  39. package/dist/lib/nspi/NspiCodec.js +106 -0
  40. package/dist/lib/nspi/NspiCodec.js.map +1 -0
  41. package/dist/lib/nspi/NspiGetMatchesHandler.js +142 -0
  42. package/dist/lib/nspi/NspiGetMatchesHandler.js.map +1 -0
  43. package/dist/lib/rop/CalendarEventTarget.js +27 -0
  44. package/dist/lib/rop/CalendarEventTarget.js.map +1 -0
  45. package/dist/lib/rop/CalendarNamedProperties.js +57 -0
  46. package/dist/lib/rop/CalendarNamedProperties.js.map +1 -0
  47. package/dist/lib/rop/ContactTarget.js +23 -0
  48. package/dist/lib/rop/ContactTarget.js.map +1 -0
  49. package/dist/lib/rop/FastTransferStream.js +98 -0
  50. package/dist/lib/rop/FastTransferStream.js.map +1 -0
  51. package/dist/lib/rop/FolderTarget.js +111 -0
  52. package/dist/lib/rop/FolderTarget.js.map +1 -0
  53. package/dist/lib/rop/MeetingMessageClassHandler.js +95 -0
  54. package/dist/lib/rop/MeetingMessageClassHandler.js.map +1 -0
  55. package/dist/lib/rop/MessageBodyStream.js +42 -0
  56. package/dist/lib/rop/MessageBodyStream.js.map +1 -0
  57. package/dist/lib/rop/MessageTarget.js +44 -0
  58. package/dist/lib/rop/MessageTarget.js.map +1 -0
  59. package/dist/lib/rop/NamedPropertyRegistry.js +66 -0
  60. package/dist/lib/rop/NamedPropertyRegistry.js.map +1 -0
  61. package/dist/lib/rop/PropertyResolvers.js +313 -0
  62. package/dist/lib/rop/PropertyResolvers.js.map +1 -0
  63. package/dist/lib/rop/RopCreateMessageHandler.js +53 -0
  64. package/dist/lib/rop/RopCreateMessageHandler.js.map +1 -0
  65. package/dist/lib/rop/RopDeleteFolderHandler.js +133 -0
  66. package/dist/lib/rop/RopDeleteFolderHandler.js.map +1 -0
  67. package/dist/lib/rop/RopDeleteMessagesHandler.js +70 -0
  68. package/dist/lib/rop/RopDeleteMessagesHandler.js.map +1 -0
  69. package/dist/lib/rop/RopFastTransferSourceCopyPropertiesHandler.js +61 -0
  70. package/dist/lib/rop/RopFastTransferSourceCopyPropertiesHandler.js.map +1 -0
  71. package/dist/lib/rop/RopFastTransferSourceCopyToHandler.js +61 -0
  72. package/dist/lib/rop/RopFastTransferSourceCopyToHandler.js.map +1 -0
  73. package/dist/lib/rop/RopFastTransferSourceGetBufferHandler.js +75 -0
  74. package/dist/lib/rop/RopFastTransferSourceGetBufferHandler.js.map +1 -0
  75. package/dist/lib/rop/RopGetContentsTableHandler.js +76 -0
  76. package/dist/lib/rop/RopGetContentsTableHandler.js.map +1 -0
  77. package/dist/lib/rop/RopGetHierarchyTableHandler.js +40 -0
  78. package/dist/lib/rop/RopGetHierarchyTableHandler.js.map +1 -0
  79. package/dist/lib/rop/RopGetPropertiesSpecificHandler.js +55 -0
  80. package/dist/lib/rop/RopGetPropertiesSpecificHandler.js.map +1 -0
  81. package/dist/lib/rop/RopGetPropertyIdsFromNamesHandler.js +92 -0
  82. package/dist/lib/rop/RopGetPropertyIdsFromNamesHandler.js.map +1 -0
  83. package/dist/lib/rop/RopHandler.js +2 -0
  84. package/dist/lib/rop/RopHandler.js.map +1 -0
  85. package/dist/lib/rop/RopLogonHandler.js +132 -0
  86. package/dist/lib/rop/RopLogonHandler.js.map +1 -0
  87. package/dist/lib/rop/RopOpenFolderHandler.js +46 -0
  88. package/dist/lib/rop/RopOpenFolderHandler.js.map +1 -0
  89. package/dist/lib/rop/RopOpenMessageHandler.js +81 -0
  90. package/dist/lib/rop/RopOpenMessageHandler.js.map +1 -0
  91. package/dist/lib/rop/RopOpenStreamHandler.js +81 -0
  92. package/dist/lib/rop/RopOpenStreamHandler.js.map +1 -0
  93. package/dist/lib/rop/RopQueryRowsHandler.js +91 -0
  94. package/dist/lib/rop/RopQueryRowsHandler.js.map +1 -0
  95. package/dist/lib/rop/RopReadStreamHandler.js +55 -0
  96. package/dist/lib/rop/RopReadStreamHandler.js.map +1 -0
  97. package/dist/lib/rop/RopReleaseHandler.js +20 -0
  98. package/dist/lib/rop/RopReleaseHandler.js.map +1 -0
  99. package/dist/lib/rop/RopSaveChangesMessageHandler.js +210 -0
  100. package/dist/lib/rop/RopSaveChangesMessageHandler.js.map +1 -0
  101. package/dist/lib/rop/RopSetColumnsHandler.js +52 -0
  102. package/dist/lib/rop/RopSetColumnsHandler.js.map +1 -0
  103. package/dist/lib/rop/RopSetPropertiesHandler.js +162 -0
  104. package/dist/lib/rop/RopSetPropertiesHandler.js.map +1 -0
  105. package/dist/lib/rop/RopSubmitMessageHandler.js +314 -0
  106. package/dist/lib/rop/RopSubmitMessageHandler.js.map +1 -0
  107. package/dist/lib/rop/RopWriteStreamHandler.js +45 -0
  108. package/dist/lib/rop/RopWriteStreamHandler.js.map +1 -0
  109. package/dist/lib/rop/TaskNamedProperties.js +29 -0
  110. package/dist/lib/rop/TaskNamedProperties.js.map +1 -0
  111. package/dist/lib/rop/TaskTarget.js +19 -0
  112. package/dist/lib/rop/TaskTarget.js.map +1 -0
  113. package/dist/lib/sql/MapiEmsmdbRouteSQL.js +84 -0
  114. package/dist/lib/sql/MapiEmsmdbRouteSQL.js.map +1 -0
  115. package/dist/lib/sql/MapiNspiRouteSQL.js +33 -0
  116. package/dist/lib/sql/MapiNspiRouteSQL.js.map +1 -0
  117. package/dist/lib/sql/index.js +7 -0
  118. package/dist/lib/sql/index.js.map +1 -0
  119. package/dist/lib/sql.js +6 -0
  120. package/dist/lib/sql.js.map +1 -0
  121. package/dist/types/BaseMapiEmsmdbRoute.d.ts +82 -0
  122. package/dist/types/BaseMapiNspiRoute.d.ts +38 -0
  123. package/dist/types/MapiSessionManager.d.ts +130 -0
  124. package/dist/types/RopDispatcher.d.ts +17 -0
  125. package/dist/types/codec/AppointmentRecurrence.d.ts +14 -0
  126. package/dist/types/codec/BufferCursor.d.ts +78 -0
  127. package/dist/types/codec/GlobalObjectId.d.ts +11 -0
  128. package/dist/types/codec/MapiGuid.d.ts +6 -0
  129. package/dist/types/codec/MapiTimeZone.d.ts +7 -0
  130. package/dist/types/codec/PropertyValue.d.ts +77 -0
  131. package/dist/types/codec/RopBuffer.d.ts +25 -0
  132. package/dist/types/codec/TypedString.d.ts +17 -0
  133. package/dist/types/index.d.ts +94 -0
  134. package/dist/types/mongo/MapiEmsmdbRouteMongo.d.ts +19 -0
  135. package/dist/types/mongo/MapiNspiRouteMongo.d.ts +13 -0
  136. package/dist/types/mongo/index.d.ts +2 -0
  137. package/dist/types/mongo.d.ts +1 -0
  138. package/dist/types/nspi/NspiBindHandler.d.ts +22 -0
  139. package/dist/types/nspi/NspiCodec.d.ts +59 -0
  140. package/dist/types/nspi/NspiGetMatchesHandler.d.ts +24 -0
  141. package/dist/types/rop/CalendarEventTarget.d.ts +29 -0
  142. package/dist/types/rop/CalendarNamedProperties.d.ts +37 -0
  143. package/dist/types/rop/ContactTarget.d.ts +29 -0
  144. package/dist/types/rop/FastTransferStream.d.ts +18 -0
  145. package/dist/types/rop/FolderTarget.d.ts +63 -0
  146. package/dist/types/rop/MeetingMessageClassHandler.d.ts +36 -0
  147. package/dist/types/rop/MessageBodyStream.d.ts +26 -0
  148. package/dist/types/rop/MessageTarget.d.ts +38 -0
  149. package/dist/types/rop/NamedPropertyRegistry.d.ts +46 -0
  150. package/dist/types/rop/PropertyResolvers.d.ts +60 -0
  151. package/dist/types/rop/RopCreateMessageHandler.d.ts +23 -0
  152. package/dist/types/rop/RopDeleteFolderHandler.d.ts +38 -0
  153. package/dist/types/rop/RopDeleteMessagesHandler.d.ts +31 -0
  154. package/dist/types/rop/RopFastTransferSourceCopyPropertiesHandler.d.ts +24 -0
  155. package/dist/types/rop/RopFastTransferSourceCopyToHandler.d.ts +24 -0
  156. package/dist/types/rop/RopFastTransferSourceGetBufferHandler.d.ts +23 -0
  157. package/dist/types/rop/RopGetContentsTableHandler.d.ts +31 -0
  158. package/dist/types/rop/RopGetHierarchyTableHandler.d.ts +17 -0
  159. package/dist/types/rop/RopGetPropertiesSpecificHandler.d.ts +23 -0
  160. package/dist/types/rop/RopGetPropertyIdsFromNamesHandler.d.ts +23 -0
  161. package/dist/types/rop/RopHandler.d.ts +67 -0
  162. package/dist/types/rop/RopLogonHandler.d.ts +31 -0
  163. package/dist/types/rop/RopOpenFolderHandler.d.ts +19 -0
  164. package/dist/types/rop/RopOpenMessageHandler.d.ts +27 -0
  165. package/dist/types/rop/RopOpenStreamHandler.d.ts +27 -0
  166. package/dist/types/rop/RopQueryRowsHandler.d.ts +36 -0
  167. package/dist/types/rop/RopReadStreamHandler.d.ts +18 -0
  168. package/dist/types/rop/RopReleaseHandler.d.ts +14 -0
  169. package/dist/types/rop/RopSaveChangesMessageHandler.d.ts +44 -0
  170. package/dist/types/rop/RopSetColumnsHandler.d.ts +21 -0
  171. package/dist/types/rop/RopSetPropertiesHandler.d.ts +38 -0
  172. package/dist/types/rop/RopSubmitMessageHandler.d.ts +70 -0
  173. package/dist/types/rop/RopWriteStreamHandler.d.ts +21 -0
  174. package/dist/types/rop/TaskNamedProperties.d.ts +24 -0
  175. package/dist/types/rop/TaskTarget.d.ts +24 -0
  176. package/dist/types/sql/MapiEmsmdbRouteSQL.d.ts +19 -0
  177. package/dist/types/sql/MapiNspiRouteSQL.d.ts +13 -0
  178. package/dist/types/sql/index.d.ts +2 -0
  179. package/dist/types/sql.d.ts +1 -0
  180. package/package.json +104 -0
@@ -0,0 +1,297 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ ///////////////////////////////////////////////////////////////////////////////
14
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
15
+ // SPDX-License-Identifier: MPL-2.0
16
+ ///////////////////////////////////////////////////////////////////////////////
17
+ import { ApiError, ObjectDecorators } from "@rapidrest/core";
18
+ import { ApiErrorMessages, ApiErrors, RepoUtils, RouteDecorators, } from "@rapidrest/service-core";
19
+ import { BufferReader, BufferWriter } from "./codec/BufferCursor.js";
20
+ import { decodeRopBuffer, encodeRopBuffer } from "./codec/RopBuffer.js";
21
+ import { MapiSessionManager } from "./MapiSessionManager.js";
22
+ import { dispatchRops } from "./RopDispatcher.js";
23
+ import { ScanPipeline } from "@rapidmx/restapi/scan";
24
+ import { RecoverableRepoUtils, resolveCallerMailboxUid } from "@rapidmx/restapi";
25
+ const { Init, Inject, Logger } = ObjectDecorators;
26
+ const { Auth, Post, Request, Response, User: AuthUser } = RouteDecorators;
27
+ /** The well-known MAPI HRESULT `MAPI_E_LOGON_FAILED`, reused here to signal "no such session - reconnect"
28
+ * via `X-ResponseCode`/`ErrorCode`. Not a claim of exact `[MS-OXCRPC]` return-value-table parity for this
29
+ * specific condition - a real client only needs a non-zero code to know to re-`Connect`, not a precise one. */
30
+ const ERROR_SESSION_NOT_FOUND = 0x80040111;
31
+ function firstHeader(req, name) {
32
+ const value = req.headers[name];
33
+ return Array.isArray(value) ? value[0] : value;
34
+ }
35
+ /**
36
+ * Abstract base for the single fixed EMSMDB endpoint (`POST /mapi/emsmdb` by `[MS-OXCMAPIHTTP]` convention,
37
+ * though the concrete path is left to the consuming application to mount via `@Route(...)` - see
38
+ * `BaseEasRoute.ts` for the identical undecorated-base-class pattern this follows). Like EAS, MAPI/HTTP
39
+ * multiplexes several request types against one URL - here via the `X-RequestType` **header**
40
+ * (`Connect`/`Execute`/`Disconnect`/`NotificationWait`) rather than a query parameter - so there is exactly
41
+ * one `@Post()` method, not one per request type.
42
+ *
43
+ * **Auth**: `@Auth(["jwt"])` - unchanged from every other route in this app. Real, modern Exchange Server's
44
+ * own MAPI virtual directory genuinely supports `OAuth` as a configured `IISAuthenticationMethods` value
45
+ * alongside `NTLM`/`Negotiate`, so Bearer-token auth on this exact endpoint is real current Exchange
46
+ * behavior, not a deviation this library invents - see the architecture plan's "Auth" section.
47
+ *
48
+ * **Connect/Disconnect are complete**: `Connect` establishes a real `MapiSessionContext` (via
49
+ * `MapiSessionManager`) and sets the two spec-fixed session cookies (`MapiContext`/`MapiSequence`);
50
+ * `Disconnect` releases the session. **`Execute` dispatches real ROPs** (starting with `RopLogon`/`RopRelease`
51
+ * - build-order step 4) via `RopDispatcher`, using whichever `RopHandler`s `ropHandlerClasses` registers;
52
+ * an unrecognized `RopId` simply stops processing (see `RopDispatcher`'s own doc comment for why), the same
53
+ * "real, functional, no commands implemented yet" stance `BaseEasRoute`'s own skeleton step took for its
54
+ * empty `commandHandlerClasses` before `ProvisionCommand` landed - here scoped per-ROP instead of per-request.
55
+ *
56
+ * **Always non-chunked**: every response here uses `Content-Length` (via `res.status(200).send(buffer)`),
57
+ * never `Transfer-Encoding: chunked` - both are equally spec-valid per `[MS-OXCMAPIHTTP]`'s own "Common
58
+ * Response Format", and skipping the `PROCESSING`/`PENDING`/`DONE` keep-alive meta-tag streaming matches
59
+ * every other route in this library's own response idiom. See the architecture plan's "Protocol facts"
60
+ * section for the full reasoning and its documented limitation.
61
+ *
62
+ * `mailboxClass`/`folderClass` are supplied by the Mongo/SQL concrete subclasses, following the exact
63
+ * one-line-per-backend pattern used throughout this library's other routes/jobs. `folderRepo` is built once
64
+ * here (not per-handler, unlike EAS's per-command repo pattern) and threaded through `RopContext` to every
65
+ * `RopHandler` - see `RopHandler.ts`'s own doc comment for why.
66
+ *
67
+ * @author Jean-Philippe Steinmetz
68
+ */
69
+ export class BaseMapiEmsmdbRoute {
70
+ constructor() {
71
+ /** ROP handler classes to instantiate (one each) in `@Init`, keyed by their own `ropId`. Empty until a
72
+ * concrete `RopHandler` lands - every ROP is then simply left unprocessed (see `RopDispatcher`'s own doc
73
+ * comment), the correct, honest behavior for a transport skeleton with no ROPs implemented yet. */
74
+ this.ropHandlerClasses = [];
75
+ this.ropHandlers = new Map();
76
+ }
77
+ async init() {
78
+ this.mailboxRepo = await this._objectFactory.newInstance(RepoUtils, {
79
+ name: this.mailboxClass.name,
80
+ args: [this.mailboxClass],
81
+ });
82
+ this.folderRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
83
+ name: this.folderClass.name,
84
+ args: [this.folderClass],
85
+ });
86
+ this.messageRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
87
+ name: this.messageClass.name,
88
+ args: [this.messageClass],
89
+ });
90
+ this.calendarEventRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
91
+ name: this.calendarEventClass.name,
92
+ args: [this.calendarEventClass],
93
+ });
94
+ this.contactRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
95
+ name: this.contactClass.name,
96
+ args: [this.contactClass],
97
+ });
98
+ this.taskRepo = await this._objectFactory.newInstance(RecoverableRepoUtils, {
99
+ name: this.taskClass.name,
100
+ args: [this.taskClass],
101
+ });
102
+ // Label extends plain BaseEntity, not RecoverableBaseEntity (see restapi's own model doc comment) - no
103
+ // soft-delete support to preserve, so a plain RepoUtils is correct here, matching mailboxRepo's own
104
+ // choice for the identical reason.
105
+ this.labelRepo = await this._objectFactory.newInstance(RepoUtils, {
106
+ name: this.labelClass.name,
107
+ args: [this.labelClass],
108
+ });
109
+ this.sessionManager = await this._objectFactory.newInstance(MapiSessionManager);
110
+ for (const HandlerClass of this.ropHandlerClasses) {
111
+ const handler = await this._objectFactory.newInstance(HandlerClass);
112
+ this.ropHandlers.set(handler.ropId, handler);
113
+ }
114
+ }
115
+ async dispatch(req, res, user) {
116
+ if (!this.mailboxRepo ||
117
+ !this.folderRepo ||
118
+ !this.messageRepo ||
119
+ !this.calendarEventRepo ||
120
+ !this.contactRepo ||
121
+ !this.taskRepo ||
122
+ !this.labelRepo ||
123
+ !this.sessionManager ||
124
+ !this.blobStore ||
125
+ !this.scanPipeline ||
126
+ !this.mailTransport) {
127
+ throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
128
+ }
129
+ if (!user) {
130
+ throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, ApiErrorMessages.AUTH_PERMISSION_FAILURE);
131
+ }
132
+ const requestType = firstHeader(req, "x-requesttype");
133
+ const clientInfo = firstHeader(req, "x-clientinfo") ?? "";
134
+ const requestId = firstHeader(req, "x-requestid") ?? "";
135
+ res.setHeader("Content-Type", "application/mapi-http")
136
+ .setHeader("X-RequestType", requestType ?? "")
137
+ .setHeader("X-RequestId", requestId)
138
+ .setHeader("X-ResponseCode", "0")
139
+ .setHeader("X-ClientInfo", clientInfo)
140
+ .setHeader("X-ServerApplication", "RapidREST-Mail");
141
+ switch (requestType) {
142
+ case "Connect":
143
+ await this.handleConnect(req, res, user);
144
+ return;
145
+ case "Execute":
146
+ await this.handleExecute(req, res);
147
+ return;
148
+ case "Disconnect":
149
+ await this.handleDisconnect(req, res);
150
+ return;
151
+ case "NotificationWait":
152
+ // Real, spec-defined request type, deliberately deferred - no push-notification support in
153
+ // this pass (see the architecture plan's ROP scope table). Distinct from an unrecognized
154
+ // X-RequestType value (400 below), the same distinction BaseEasRoute draws between 501
155
+ // ("recognized but deferred") and 400 ("malformed request").
156
+ res.status(501).send();
157
+ return;
158
+ default:
159
+ throw new ApiError(ApiErrors.INVALID_REQUEST, 400, ApiErrorMessages.INVALID_REQUEST);
160
+ }
161
+ }
162
+ /**
163
+ * `Connect` establishes a new Session Context - never trusts the request body's own `UserDn` field for
164
+ * mailbox identity (the same "never a client-supplied mailbox, always resolved from the authenticated
165
+ * JWT" principle `BaseEasRoute`/`BaseSearchRoute` already apply via `resolveCallerMailboxUid`) - so the
166
+ * request body is never decoded at all here.
167
+ */
168
+ async handleConnect(req, res, user) {
169
+ const mailboxUid = await resolveCallerMailboxUid(this.mailboxRepo, user);
170
+ if (!mailboxUid) {
171
+ throw new ApiError(ApiErrors.NOT_FOUND, 404, ApiErrorMessages.NOT_FOUND);
172
+ }
173
+ const mailbox = await this.mailboxRepo.findOne(mailboxUid, { ignoreACL: true });
174
+ const session = await this.sessionManager.create(mailboxUid, user.uid);
175
+ res.appendHeader("Set-Cookie", `MapiContext=${session.uid}`);
176
+ res.appendHeader("Set-Cookie", `MapiSequence=0`);
177
+ const body = new BufferWriter();
178
+ body.writeUInt32LE(0); // StatusCode
179
+ body.writeUInt32LE(0); // ErrorCode
180
+ body.writeUInt32LE(60000); // PollsMax (ms) - pragmatic constant, not a tuned server policy value
181
+ body.writeUInt32LE(3); // RetryCount - pragmatic constant
182
+ body.writeUInt32LE(5000); // RetryDelay (ms) - pragmatic constant
183
+ // DnPrefix: this library addresses mailboxes by UID/SMTP-address, never real X.500 DNs, so this is a
184
+ // fixed, unused placeholder rather than a real DN prefix a client could build recipients from.
185
+ body.writeNullTerminatedString8("/");
186
+ body.writeNullTerminatedUtf16LE(mailbox?.displayName ?? "");
187
+ body.writeUInt32LE(0); // AuxiliaryBufferSize
188
+ res.status(200).send(body.toBuffer());
189
+ }
190
+ /**
191
+ * `Execute` decodes the outer envelope (`Flags`/`RopBufferSize`/`RopBuffer`/...) and the inner ROP buffer
192
+ * framing (`RopBuffer.ts`), dispatches every contained ROP via `RopDispatcher`, and re-encodes the
193
+ * collected responses - preserving the incoming `handleTable` unchanged (this pragmatic subset never
194
+ * allocates/frees table-wide handle slots at the framing level; individual `RopHandler`s manage their own
195
+ * entries within `session.handles` instead).
196
+ */
197
+ async handleExecute(req, res) {
198
+ const sessionId = req.cookies["MapiContext"];
199
+ const session = sessionId
200
+ ? await this.sessionManager.load(sessionId)
201
+ : undefined;
202
+ if (!session) {
203
+ res.setHeader("X-ResponseCode", String(ERROR_SESSION_NOT_FOUND));
204
+ const body = new BufferWriter();
205
+ body.writeUInt32LE(0); // StatusCode
206
+ body.writeUInt32LE(ERROR_SESSION_NOT_FOUND); // ErrorCode
207
+ body.writeUInt32LE(0); // Flags
208
+ body.writeUInt32LE(0); // RopBufferSize
209
+ body.writeUInt32LE(0); // AuxiliaryBufferSize
210
+ res.status(200).send(body.toBuffer());
211
+ return;
212
+ }
213
+ const requestReader = new BufferReader(req.rawBody ?? Buffer.alloc(0));
214
+ requestReader.readUInt32LE(); // Flags - unused by this pragmatic subset (no client ROP-response hints honored)
215
+ const ropBufferSize = requestReader.readUInt32LE();
216
+ const ropBufferBytes = requestReader.readBytes(ropBufferSize);
217
+ // MaxRopOut/AuxiliaryBufferSize/AuxiliaryBuffer intentionally left unread - no output-size capping or
218
+ // auxiliary-payload support in this pragmatic subset.
219
+ const { ropsList, handleTable } = decodeRopBuffer(ropBufferBytes);
220
+ const context = {
221
+ mailboxUid: session.mailboxUid,
222
+ userUid: session.userUid,
223
+ session,
224
+ mailboxRepo: this.mailboxRepo,
225
+ folderRepo: this.folderRepo,
226
+ messageRepo: this.messageRepo,
227
+ calendarEventRepo: this.calendarEventRepo,
228
+ contactRepo: this.contactRepo,
229
+ taskRepo: this.taskRepo,
230
+ labelRepo: this.labelRepo,
231
+ folderClass: this.folderClass,
232
+ messageClass: this.messageClass,
233
+ calendarEventClass: this.calendarEventClass,
234
+ contactClass: this.contactClass,
235
+ taskClass: this.taskClass,
236
+ labelClass: this.labelClass,
237
+ blobStore: this.blobStore,
238
+ scanPipeline: this.scanPipeline,
239
+ mailTransport: this.mailTransport,
240
+ };
241
+ const responseRopsList = await dispatchRops(ropsList, this.ropHandlers, context);
242
+ const responseRopBuffer = encodeRopBuffer({ ropsList: responseRopsList, handleTable });
243
+ await this.sessionManager.save(session);
244
+ const body = new BufferWriter();
245
+ body.writeUInt32LE(0); // StatusCode
246
+ body.writeUInt32LE(0); // ErrorCode
247
+ body.writeUInt32LE(0); // Flags
248
+ body.writeUInt32LE(responseRopBuffer.length);
249
+ body.writeBytes(responseRopBuffer);
250
+ body.writeUInt32LE(0); // AuxiliaryBufferSize
251
+ res.status(200).send(body.toBuffer());
252
+ }
253
+ async handleDisconnect(req, res) {
254
+ const sessionId = req.cookies["MapiContext"];
255
+ if (sessionId) {
256
+ await this.sessionManager.destroy(sessionId);
257
+ }
258
+ const body = new BufferWriter();
259
+ body.writeUInt32LE(0); // StatusCode
260
+ body.writeUInt32LE(0); // ErrorCode
261
+ body.writeUInt32LE(0); // AuxiliaryBufferSize
262
+ res.status(200).send(body.toBuffer());
263
+ }
264
+ }
265
+ __decorate([
266
+ Inject("BlobStore"),
267
+ __metadata("design:type", Object)
268
+ ], BaseMapiEmsmdbRoute.prototype, "blobStore", void 0);
269
+ __decorate([
270
+ Inject(ScanPipeline),
271
+ __metadata("design:type", ScanPipeline)
272
+ ], BaseMapiEmsmdbRoute.prototype, "scanPipeline", void 0);
273
+ __decorate([
274
+ Inject("MailTransport"),
275
+ __metadata("design:type", Object)
276
+ ], BaseMapiEmsmdbRoute.prototype, "mailTransport", void 0);
277
+ __decorate([
278
+ Logger,
279
+ __metadata("design:type", Object)
280
+ ], BaseMapiEmsmdbRoute.prototype, "logger", void 0);
281
+ __decorate([
282
+ Init,
283
+ __metadata("design:type", Function),
284
+ __metadata("design:paramtypes", []),
285
+ __metadata("design:returntype", Promise)
286
+ ], BaseMapiEmsmdbRoute.prototype, "init", null);
287
+ __decorate([
288
+ Auth(["jwt"]),
289
+ Post(),
290
+ __param(0, Request),
291
+ __param(1, Response),
292
+ __param(2, AuthUser),
293
+ __metadata("design:type", Function),
294
+ __metadata("design:paramtypes", [Object, Object, Object]),
295
+ __metadata("design:returntype", Promise)
296
+ ], BaseMapiEmsmdbRoute.prototype, "dispatch", null);
297
+ //# sourceMappingURL=BaseMapiEmsmdbRoute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseMapiEmsmdbRoute.js","sourceRoot":"","sources":["../../src/BaseMapiEmsmdbRoute.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,114 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
11
+ return function (target, key) { decorator(target, key, paramIndex); }
12
+ };
13
+ ///////////////////////////////////////////////////////////////////////////////
14
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
15
+ // SPDX-License-Identifier: MPL-2.0
16
+ ///////////////////////////////////////////////////////////////////////////////
17
+ import { ApiError, ObjectDecorators } from "@rapidrest/core";
18
+ import { ApiErrorMessages, ApiErrors, RepoUtils, RouteDecorators } from "@rapidrest/service-core";
19
+ import { handleNspiBind, handleNspiUnbind } from "./nspi/NspiBindHandler.js";
20
+ import { handleNspiGetMatches } from "./nspi/NspiGetMatchesHandler.js";
21
+ import { resolveCallerMailboxUid } from "@rapidmx/restapi";
22
+ const { Init } = ObjectDecorators;
23
+ const { Auth, Post, Request, Response, User: AuthUser } = RouteDecorators;
24
+ function firstHeader(req, name) {
25
+ const value = req.headers[name];
26
+ return Array.isArray(value) ? value[0] : value;
27
+ }
28
+ /**
29
+ * Abstract base for the single fixed NSPI endpoint (`POST /mapi/nspi` by `[MS-OXCMAPIHTTP]` convention, though
30
+ * the concrete path is left to the consuming application to mount via `@Route(...)`, the identical
31
+ * undecorated-base-class pattern `BaseMapiEmsmdbRoute`/`BaseEasRoute` already establish). Dispatches on the
32
+ * `X-RequestType` header, the same multiplexing mechanism EMSMDB uses - but unlike EMSMDB, there is no ROP
33
+ * buffer framing here: each `X-RequestType` value (`Bind`/`Unbind`/`GetMatches`) is its own flat, self-
34
+ * contained request/response body (`[MS-OXCMAPIHTTP]` §2.2.5), so this route dispatches directly to a small
35
+ * handler function per operation instead of decoding a multiplexed stream.
36
+ *
37
+ * **Pragmatic subset scope**: only `Bind`/`Unbind`/`GetMatches` are implemented - real NSPI's other dozen-plus
38
+ * request types (`QueryRows`, `ResolveNames`, `GetProps`, `ModProps`, ...) are genuine full address-book/
39
+ * directory-replication operations this deployment's own minimal GAL-lookup use case doesn't need; an
40
+ * unrecognized `X-RequestType` gets a `501`, the same "recognized-but-deferred vs malformed" distinction
41
+ * `BaseMapiEmsmdbRoute`'s own `NotificationWait` case already draws. See `NspiBindHandler.ts`'s own doc comment
42
+ * for why no real NSPI session state is created or tracked between calls.
43
+ *
44
+ * **Auth**: `@Auth(["jwt"])`, unchanged from every other route in this app - the same reasoning
45
+ * `BaseMapiEmsmdbRoute`'s own doc comment already gives for why Bearer-token auth on an address-book endpoint
46
+ * is real, current Exchange behavior, not a deviation this library invents.
47
+ *
48
+ * `mailboxClass`/`contactClass` are supplied by the Mongo/SQL concrete subclasses. GAL search itself (see
49
+ * `NspiGetMatchesHandler.ts`) uses `RepoUtils`'s `regex(...)` operator, which compiles identically on both
50
+ * backends - no per-backend hook needed here.
51
+ *
52
+ * @author Jean-Philippe Steinmetz
53
+ */
54
+ export class BaseMapiNspiRoute {
55
+ async init() {
56
+ this.mailboxRepo = await this._objectFactory.newInstance(RepoUtils, {
57
+ name: this.mailboxClass.name,
58
+ args: [this.mailboxClass],
59
+ });
60
+ this.contactRepo = await this._objectFactory.newInstance(RepoUtils, {
61
+ name: this.contactClass.name,
62
+ args: [this.contactClass],
63
+ });
64
+ }
65
+ async dispatch(req, res, user) {
66
+ if (!this.mailboxRepo || !this.contactRepo) {
67
+ throw new ApiError(ApiErrors.INTERNAL_ERROR, 500, ApiErrorMessages.INTERNAL_ERROR);
68
+ }
69
+ if (!user) {
70
+ throw new ApiError(ApiErrors.AUTH_PERMISSION_FAILURE, 403, ApiErrorMessages.AUTH_PERMISSION_FAILURE);
71
+ }
72
+ const requestType = firstHeader(req, "x-requesttype");
73
+ res.setHeader("Content-Type", "application/mapi-http")
74
+ .setHeader("X-RequestType", requestType ?? "")
75
+ .setHeader("X-ResponseCode", "0")
76
+ .setHeader("X-ServerApplication", "RapidREST-Mail");
77
+ switch (requestType) {
78
+ case "Bind":
79
+ await handleNspiBind(res, user, this.mailboxRepo);
80
+ return;
81
+ case "Unbind":
82
+ handleNspiUnbind(res);
83
+ return;
84
+ case "GetMatches": {
85
+ const mailboxUid = await resolveCallerMailboxUid(this.mailboxRepo, user);
86
+ if (!mailboxUid) {
87
+ throw new ApiError(ApiErrors.NOT_FOUND, 404, ApiErrorMessages.NOT_FOUND);
88
+ }
89
+ await handleNspiGetMatches(req, res, mailboxUid, this.contactRepo);
90
+ return;
91
+ }
92
+ default:
93
+ res.status(501).send();
94
+ return;
95
+ }
96
+ }
97
+ }
98
+ __decorate([
99
+ Init,
100
+ __metadata("design:type", Function),
101
+ __metadata("design:paramtypes", []),
102
+ __metadata("design:returntype", Promise)
103
+ ], BaseMapiNspiRoute.prototype, "init", null);
104
+ __decorate([
105
+ Auth(["jwt"]),
106
+ Post(),
107
+ __param(0, Request),
108
+ __param(1, Response),
109
+ __param(2, AuthUser),
110
+ __metadata("design:type", Function),
111
+ __metadata("design:paramtypes", [Object, Object, Object]),
112
+ __metadata("design:returntype", Promise)
113
+ ], BaseMapiNspiRoute.prototype, "dispatch", null);
114
+ //# sourceMappingURL=BaseMapiNspiRoute.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BaseMapiNspiRoute.js","sourceRoot":"","sources":["../../src/BaseMapiNspiRoute.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,119 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ ///////////////////////////////////////////////////////////////////////////////
11
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
12
+ // SPDX-License-Identifier: MPL-2.0
13
+ ///////////////////////////////////////////////////////////////////////////////
14
+ import { ObjectDecorators } from "@rapidrest/core";
15
+ import { RedisCache, SimpleEntity } from "@rapidrest/service-core";
16
+ const { Init } = ObjectDecorators;
17
+ /** A pragmatic idle-session lifetime. The spec leaves session lifetime to server policy, not a fixed wire
18
+ * value - a real client re-establishes (`Connect`) transparently whenever its session context has expired. */
19
+ const SESSION_TTL_SECONDS = 15 * 60;
20
+ /**
21
+ * The MAPI/HTTP `Session Context` (`[MS-OXCMAPIHTTP]` §3.1.1.1): everything a `Connect`-established session
22
+ * needs across subsequent `Execute` requests. Never persisted to a real database - purely an ephemeral,
23
+ * TTL-bound cache entry (see `MapiSessionManager` below), so this deliberately extends `SimpleEntity` (just a
24
+ * `uid`) rather than this library's own `BaseEntity`, which would add ACL/soft-delete/optimistic-locking
25
+ * machinery this object has no use for.
26
+ *
27
+ * `createdAt` is a plain ISO-8601 string, not a `Date` - `RedisCache`'s Redis-backed path round-trips values
28
+ * through `JSON.stringify`/`JSON.parse`, which silently turns a `Date` into a string on the way out without
29
+ * reviving it back to a `Date` on the way in; storing it as a string from the start keeps the shape identical
30
+ * whether a session happens to be served from the in-memory or the Redis-backed path.
31
+ */
32
+ export class MapiSessionContext extends SimpleEntity {
33
+ /** `mailboxUid`/`userUid` are always known at construction time (the only call site is
34
+ * `MapiSessionManager.create()`, which resolves both up front) - required here rather than optional with
35
+ * a same-value fallback, which would just be dead code no real caller ever takes the other branch of. */
36
+ constructor(other) {
37
+ super(other);
38
+ this.handles = {};
39
+ this.nextHandleIndex = 1;
40
+ this.createdAt = new Date().toISOString();
41
+ /** This session's FID assignments for the 13 `RopLogon` special folders, keyed by FID (decimal string),
42
+ * valued `"virtual:<name>"` or `"folder:<uid>"` - see `RopLogonHandler`'s own doc comment. Populated by
43
+ * `RopLogon`, read back by a later `RopOpenFolder`. */
44
+ this.folderIds = {};
45
+ /** The reverse of `folderIds` (target -> FID) plus a monotonic counter, maintained alongside it by
46
+ * `FolderTarget.assignOrGetFid`/`RopLogonHandler` so "does this target already have a FID" is an O(1)
47
+ * lookup instead of a linear scan of `folderIds` repeated once per table row - see `assignOrGetFid`'s own
48
+ * doc comment for why the scan this replaces was a genuine (not just theoretical) quadratic cost across a
49
+ * session's lifetime. Small and purely additive to session state (one more string-keyed map of numbers,
50
+ * the same order of magnitude as `folderIds` itself), unlike caching whole resolved rows would be. */
51
+ this.folderTargetIds = {};
52
+ this.nextFolderId = 1;
53
+ /** This session's MID assignments, keyed by MID (decimal string), valued `"message:<uid>"` - the message
54
+ * analog of `folderIds` above. Unlike `folderIds` (pre-populated by `RopLogon`), a MID only ever comes into
55
+ * existence lazily, the first time a `RopQueryRows` row exposes a message's `PidTagMid` column (see
56
+ * `MessageTarget.assignOrGetMid`), read back by a later `RopOpenMessage`. */
57
+ this.messageIds = {};
58
+ /** The reverse of `messageIds` (target -> MID) plus a monotonic counter - the message analog of
59
+ * `folderTargetIds`/`nextFolderId` above, for the identical O(1)-lookup reason. */
60
+ this.messageTargetIds = {};
61
+ this.nextMessageId = 1;
62
+ /** This session's `RopGetPropertyIdsFromNames` mapping table (`[MS-OXCPRPT]` §2.2.12), keyed by a JSON
63
+ * string encoding of the `{guid, kind, lid|name}` `PropertyName` the numeric ID was assigned to - see
64
+ * `NamedPropertyRegistry.ts`'s own doc comment for why a JSON string (rather than a delimiter-joined one)
65
+ * is the safest key shape here. A real client resolves every named property (almost every Calendar-specific
66
+ * one - `PidLidAppointmentStartWhole`, `PidLidBusyStatus`, `PidLidAppointmentRecur`, ...) through this table
67
+ * once per session before ever setting/reading it via `RopSetProperties`/`RopGetPropertiesSpecific`. */
68
+ this.namedProperties = {};
69
+ /** The reverse of `namedProperties` (numeric ID -> `PropertyName` JSON key) plus a monotonic counter -
70
+ * `NamedPropertyRegistry.ts`'s own doc comment covers why both the O(n) forward scan this replaces and the
71
+ * `Math.max(...spread)` id-allocation it used are real problems, not just theoretical ones, at real
72
+ * mailbox/session scale. */
73
+ this.namedPropertyIds = {};
74
+ this.nextNamedPropertyId = 0x8000;
75
+ this.mailboxUid = other.mailboxUid;
76
+ this.userUid = other.userUid;
77
+ }
78
+ }
79
+ /**
80
+ * Stores/loads `MapiSessionContext`s, keyed by the opaque session id that also becomes the `MapiContext`
81
+ * cookie value. Reuses `@rapidrest/service-core`'s existing generic `RedisCache<T>` (Redis-backed when a
82
+ * `cache` datastore is configured, transparent in-memory fallback otherwise) rather than either a bespoke
83
+ * Redis client (unlike `PingCommand`'s own throwaway pub/sub client - fine for ephemeral fan-out, not a good
84
+ * fit for stateful session data) or the framework's generic cookie/session middleware (built for a whole-app
85
+ * `req.session` object, not this protocol's specific handle-table shape). `RedisCache` needs no real
86
+ * persisted entity - only a class with a `.name` for its Redis key prefix - so a purely ephemeral,
87
+ * never-persisted class like `MapiSessionContext` is a perfectly valid `type` for it.
88
+ *
89
+ * @author Jean-Philippe Steinmetz
90
+ */
91
+ export class MapiSessionManager {
92
+ async init() {
93
+ this.cache = await this._objectFactory.newInstance(RedisCache, {
94
+ name: MapiSessionContext.name,
95
+ args: [MapiSessionContext],
96
+ });
97
+ }
98
+ async create(mailboxUid, userUid) {
99
+ const context = new MapiSessionContext({ mailboxUid, userUid });
100
+ await this.cache.save(context.uid, context, SESSION_TTL_SECONDS);
101
+ return context;
102
+ }
103
+ async load(sessionId) {
104
+ return this.cache.load(sessionId);
105
+ }
106
+ async save(context) {
107
+ await this.cache.save(context.uid, context, SESSION_TTL_SECONDS);
108
+ }
109
+ async destroy(sessionId) {
110
+ await this.cache.delete(sessionId);
111
+ }
112
+ }
113
+ __decorate([
114
+ Init,
115
+ __metadata("design:type", Function),
116
+ __metadata("design:paramtypes", []),
117
+ __metadata("design:returntype", Promise)
118
+ ], MapiSessionManager.prototype, "init", null);
119
+ //# sourceMappingURL=MapiSessionManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MapiSessionManager.js","sourceRoot":"","sources":["../../src/MapiSessionManager.ts"],"names":[],"mappings":";;;;;;;;;AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAiB,UAAU,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAClF,MAAM,EAAE,IAAI,EAAE,GAAG,gBAAgB,CAAC;AAElC;8GAC8G;AAC9G,MAAM,mBAAmB,GAAG,EAAE,GAAG,EAAE,CAAC;AAgDpC;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,kBAAmB,SAAQ,YAAY;IA8ChD;;6GAEyG;IACzG,YAAmB,KAAsE;QACrF,KAAK,CAAC,KAAK,CAAC,CAAC;QA/CV,YAAO,GAAqC,EAAE,CAAC;QAC/C,oBAAe,GAAG,CAAC,CAAC;QACpB,cAAS,GAAW,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACpD;;+DAEuD;QAChD,cAAS,GAA2B,EAAE,CAAC;QAE9C;;;;;8GAKsG;QAC/F,oBAAe,GAA2B,EAAE,CAAC;QAC7C,iBAAY,GAAG,CAAC,CAAC;QAExB;;;qFAG6E;QACtE,eAAU,GAA2B,EAAE,CAAC;QAE/C;2FACmF;QAC5E,qBAAgB,GAA2B,EAAE,CAAC;QAC9C,kBAAa,GAAG,CAAC,CAAC;QAEzB;;;;;gHAKwG;QACjG,oBAAe,GAA2B,EAAE,CAAC;QAEpD;;;oCAG4B;QACrB,qBAAgB,GAA2B,EAAE,CAAC;QAC9C,wBAAmB,GAAG,MAAM,CAAC;QAOhC,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;QACnC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC;IACjC,CAAC;CACJ;AAED;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,kBAAkB;IAOd,AAAN,KAAK,CAAC,IAAI;QACb,IAAI,CAAC,KAAK,GAAG,MAAM,IAAI,CAAC,cAAe,CAAC,WAAW,CAAC,UAAU,EAAE;YAC5D,IAAI,EAAE,kBAAkB,CAAC,IAAI;YAC7B,IAAI,EAAE,CAAC,kBAAkB,CAAC;SAC7B,CAAC,CAAC;IACP,CAAC;IAEM,KAAK,CAAC,MAAM,CAAC,UAAkB,EAAE,OAAe;QACnD,MAAM,OAAO,GAAG,IAAI,kBAAkB,CAAC,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC,CAAC;QAChE,MAAM,IAAI,CAAC,KAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC;QAClE,OAAO,OAAO,CAAC;IACnB,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,SAAiB;QAC/B,OAAO,IAAI,CAAC,KAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACvC,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,OAA2B;QACzC,MAAM,IAAI,CAAC,KAAM,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,mBAAmB,CAAC,CAAC;IACtE,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,SAAiB;QAClC,MAAM,IAAI,CAAC,KAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACxC,CAAC;CACJ;AAxBgB;IADZ,IAAI;;;;8CAMJ"}
@@ -0,0 +1,34 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ import { BufferReader, BufferWriter } from "./codec/BufferCursor.js";
6
+ /**
7
+ * Walks a decoded `RopBuffer`'s `ropsList` blob, dispatching each contained ROP - identified by its own
8
+ * leading `RopId` byte - to the matching registered handler in turn, collecting every response into one
9
+ * output buffer. Deliberately ROP-agnostic: it knows nothing about any specific ROP's own fields, only how to
10
+ * find the *next* one, via the handler's own advanced `reader` position once `handle()` returns - see
11
+ * `RopBuffer.ts`'s doc comment for why a fully generic split isn't possible for this wire format.
12
+ *
13
+ * An unrecognized `RopId` stops processing immediately: the bytes belonging to that ROP (and everything after
14
+ * it) can't be skipped without knowing that ROP's own layout, so there is no safe way to resync and continue.
15
+ * This is an inherent constraint of the wire format, not a design choice this library is choosing not to
16
+ * handle - a real client only ever sends ROPs the server has already told it (via its supported-command
17
+ * surface) that it understands.
18
+ *
19
+ * @author Jean-Philippe Steinmetz
20
+ */
21
+ export async function dispatchRops(ropsList, handlers, context) {
22
+ const reader = new BufferReader(ropsList);
23
+ const writer = new BufferWriter();
24
+ while (reader.hasMore()) {
25
+ const ropId = reader.readUInt8();
26
+ const handler = handlers.get(ropId);
27
+ if (!handler) {
28
+ break;
29
+ }
30
+ await handler.handle(reader, writer, context);
31
+ }
32
+ return writer.toBuffer();
33
+ }
34
+ //# sourceMappingURL=RopDispatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RopDispatcher.js","sourceRoot":"","sources":["../../src/RopDispatcher.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,mEAAmE;AACnE,mCAAmC;AACnC,+EAA+E;AAC/E,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAGrE;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAC9B,QAAgB,EAChB,QAAiC,EACjC,OAAmB;IAEnB,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,QAAQ,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,IAAI,YAAY,EAAE,CAAC;IAClC,OAAO,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC;QACjC,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACpC,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,MAAM;QACV,CAAC;QACD,MAAM,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC;AAC7B,CAAC"}