@rapidmx/mapi-plugin 1.0.0-beta.10

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 (204) hide show
  1. package/LICENSE +382 -0
  2. package/README.md +60 -0
  3. package/dist/lib/BaseMapiEmsmdbRoute.js +489 -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 +475 -0
  8. package/dist/lib/MapiSessionManager.js.map +1 -0
  9. package/dist/lib/RegexPatternUtils.js +30 -0
  10. package/dist/lib/RegexPatternUtils.js.map +1 -0
  11. package/dist/lib/RopDispatcher.js +145 -0
  12. package/dist/lib/RopDispatcher.js.map +1 -0
  13. package/dist/lib/codec/AppointmentRecurrence.js +315 -0
  14. package/dist/lib/codec/AppointmentRecurrence.js.map +1 -0
  15. package/dist/lib/codec/BufferCursor.js +242 -0
  16. package/dist/lib/codec/BufferCursor.js.map +1 -0
  17. package/dist/lib/codec/GlobalObjectId.js +98 -0
  18. package/dist/lib/codec/GlobalObjectId.js.map +1 -0
  19. package/dist/lib/codec/MapiGuid.js +41 -0
  20. package/dist/lib/codec/MapiGuid.js.map +1 -0
  21. package/dist/lib/codec/MapiTimeZone.js +126 -0
  22. package/dist/lib/codec/MapiTimeZone.js.map +1 -0
  23. package/dist/lib/codec/PropertyValue.js +240 -0
  24. package/dist/lib/codec/PropertyValue.js.map +1 -0
  25. package/dist/lib/codec/RopBuffer.js +38 -0
  26. package/dist/lib/codec/RopBuffer.js.map +1 -0
  27. package/dist/lib/codec/TypedString.js +54 -0
  28. package/dist/lib/codec/TypedString.js.map +1 -0
  29. package/dist/lib/index.js +99 -0
  30. package/dist/lib/index.js.map +1 -0
  31. package/dist/lib/mongo/MapiEmsmdbRouteMongo.js +85 -0
  32. package/dist/lib/mongo/MapiEmsmdbRouteMongo.js.map +1 -0
  33. package/dist/lib/mongo/MapiNspiRouteMongo.js +33 -0
  34. package/dist/lib/mongo/MapiNspiRouteMongo.js.map +1 -0
  35. package/dist/lib/mongo/index.js +7 -0
  36. package/dist/lib/mongo/index.js.map +1 -0
  37. package/dist/lib/mongo.js +6 -0
  38. package/dist/lib/mongo.js.map +1 -0
  39. package/dist/lib/nspi/NspiBindHandler.js +54 -0
  40. package/dist/lib/nspi/NspiBindHandler.js.map +1 -0
  41. package/dist/lib/nspi/NspiCodec.js +106 -0
  42. package/dist/lib/nspi/NspiCodec.js.map +1 -0
  43. package/dist/lib/nspi/NspiGetMatchesHandler.js +142 -0
  44. package/dist/lib/nspi/NspiGetMatchesHandler.js.map +1 -0
  45. package/dist/lib/rop/AddressList.js +114 -0
  46. package/dist/lib/rop/AddressList.js.map +1 -0
  47. package/dist/lib/rop/CalendarEventTarget.js +30 -0
  48. package/dist/lib/rop/CalendarEventTarget.js.map +1 -0
  49. package/dist/lib/rop/CalendarNamedProperties.js +57 -0
  50. package/dist/lib/rop/CalendarNamedProperties.js.map +1 -0
  51. package/dist/lib/rop/ContactTarget.js +19 -0
  52. package/dist/lib/rop/ContactTarget.js.map +1 -0
  53. package/dist/lib/rop/ContentsTable.js +60 -0
  54. package/dist/lib/rop/ContentsTable.js.map +1 -0
  55. package/dist/lib/rop/ExecuteBudget.js +82 -0
  56. package/dist/lib/rop/ExecuteBudget.js.map +1 -0
  57. package/dist/lib/rop/FastTransferStream.js +195 -0
  58. package/dist/lib/rop/FastTransferStream.js.map +1 -0
  59. package/dist/lib/rop/FolderTarget.js +118 -0
  60. package/dist/lib/rop/FolderTarget.js.map +1 -0
  61. package/dist/lib/rop/HandleDataCache.js +412 -0
  62. package/dist/lib/rop/HandleDataCache.js.map +1 -0
  63. package/dist/lib/rop/MeetingMessageClassHandler.js +241 -0
  64. package/dist/lib/rop/MeetingMessageClassHandler.js.map +1 -0
  65. package/dist/lib/rop/MessageBodyStream.js +68 -0
  66. package/dist/lib/rop/MessageBodyStream.js.map +1 -0
  67. package/dist/lib/rop/MessageTarget.js +57 -0
  68. package/dist/lib/rop/MessageTarget.js.map +1 -0
  69. package/dist/lib/rop/NamedPropertyRegistry.js +69 -0
  70. package/dist/lib/rop/NamedPropertyRegistry.js.map +1 -0
  71. package/dist/lib/rop/PropertyResolvers.js +353 -0
  72. package/dist/lib/rop/PropertyResolvers.js.map +1 -0
  73. package/dist/lib/rop/RepoPaging.js +54 -0
  74. package/dist/lib/rop/RepoPaging.js.map +1 -0
  75. package/dist/lib/rop/RestapiRules.js +52 -0
  76. package/dist/lib/rop/RestapiRules.js.map +1 -0
  77. package/dist/lib/rop/RopCreateMessageHandler.js +55 -0
  78. package/dist/lib/rop/RopCreateMessageHandler.js.map +1 -0
  79. package/dist/lib/rop/RopDeleteFolderHandler.js +174 -0
  80. package/dist/lib/rop/RopDeleteFolderHandler.js.map +1 -0
  81. package/dist/lib/rop/RopDeleteMessagesHandler.js +110 -0
  82. package/dist/lib/rop/RopDeleteMessagesHandler.js.map +1 -0
  83. package/dist/lib/rop/RopFastTransferSourceCopyPropertiesHandler.js +61 -0
  84. package/dist/lib/rop/RopFastTransferSourceCopyPropertiesHandler.js.map +1 -0
  85. package/dist/lib/rop/RopFastTransferSourceCopyToHandler.js +60 -0
  86. package/dist/lib/rop/RopFastTransferSourceCopyToHandler.js.map +1 -0
  87. package/dist/lib/rop/RopFastTransferSourceGetBufferHandler.js +76 -0
  88. package/dist/lib/rop/RopFastTransferSourceGetBufferHandler.js.map +1 -0
  89. package/dist/lib/rop/RopGetContentsTableHandler.js +55 -0
  90. package/dist/lib/rop/RopGetContentsTableHandler.js.map +1 -0
  91. package/dist/lib/rop/RopGetHierarchyTableHandler.js +42 -0
  92. package/dist/lib/rop/RopGetHierarchyTableHandler.js.map +1 -0
  93. package/dist/lib/rop/RopGetPropertiesSpecificHandler.js +56 -0
  94. package/dist/lib/rop/RopGetPropertiesSpecificHandler.js.map +1 -0
  95. package/dist/lib/rop/RopGetPropertyIdsFromNamesHandler.js +92 -0
  96. package/dist/lib/rop/RopGetPropertyIdsFromNamesHandler.js.map +1 -0
  97. package/dist/lib/rop/RopHandler.js +6 -0
  98. package/dist/lib/rop/RopHandler.js.map +1 -0
  99. package/dist/lib/rop/RopLogonHandler.js +135 -0
  100. package/dist/lib/rop/RopLogonHandler.js.map +1 -0
  101. package/dist/lib/rop/RopOpenFolderHandler.js +48 -0
  102. package/dist/lib/rop/RopOpenFolderHandler.js.map +1 -0
  103. package/dist/lib/rop/RopOpenMessageHandler.js +83 -0
  104. package/dist/lib/rop/RopOpenMessageHandler.js.map +1 -0
  105. package/dist/lib/rop/RopOpenStreamHandler.js +84 -0
  106. package/dist/lib/rop/RopOpenStreamHandler.js.map +1 -0
  107. package/dist/lib/rop/RopQueryRowsHandler.js +120 -0
  108. package/dist/lib/rop/RopQueryRowsHandler.js.map +1 -0
  109. package/dist/lib/rop/RopReadStreamHandler.js +73 -0
  110. package/dist/lib/rop/RopReadStreamHandler.js.map +1 -0
  111. package/dist/lib/rop/RopReleaseHandler.js +23 -0
  112. package/dist/lib/rop/RopReleaseHandler.js.map +1 -0
  113. package/dist/lib/rop/RopSaveChangesMessageHandler.js +269 -0
  114. package/dist/lib/rop/RopSaveChangesMessageHandler.js.map +1 -0
  115. package/dist/lib/rop/RopSetColumnsHandler.js +50 -0
  116. package/dist/lib/rop/RopSetColumnsHandler.js.map +1 -0
  117. package/dist/lib/rop/RopSetPropertiesHandler.js +173 -0
  118. package/dist/lib/rop/RopSetPropertiesHandler.js.map +1 -0
  119. package/dist/lib/rop/RopSubmitMessageHandler.js +342 -0
  120. package/dist/lib/rop/RopSubmitMessageHandler.js.map +1 -0
  121. package/dist/lib/rop/RopWriteStreamHandler.js +72 -0
  122. package/dist/lib/rop/RopWriteStreamHandler.js.map +1 -0
  123. package/dist/lib/rop/TaskNamedProperties.js +29 -0
  124. package/dist/lib/rop/TaskNamedProperties.js.map +1 -0
  125. package/dist/lib/rop/TaskTarget.js +15 -0
  126. package/dist/lib/rop/TaskTarget.js.map +1 -0
  127. package/dist/lib/rop/TransportSend.js +16 -0
  128. package/dist/lib/rop/TransportSend.js.map +1 -0
  129. package/dist/lib/sql/MapiEmsmdbRouteSQL.js +85 -0
  130. package/dist/lib/sql/MapiEmsmdbRouteSQL.js.map +1 -0
  131. package/dist/lib/sql/MapiNspiRouteSQL.js +33 -0
  132. package/dist/lib/sql/MapiNspiRouteSQL.js.map +1 -0
  133. package/dist/lib/sql/index.js +7 -0
  134. package/dist/lib/sql/index.js.map +1 -0
  135. package/dist/lib/sql.js +6 -0
  136. package/dist/lib/sql.js.map +1 -0
  137. package/dist/types/BaseMapiEmsmdbRoute.d.ts +139 -0
  138. package/dist/types/BaseMapiNspiRoute.d.ts +38 -0
  139. package/dist/types/MapiSessionManager.d.ts +285 -0
  140. package/dist/types/RegexPatternUtils.d.ts +14 -0
  141. package/dist/types/RopDispatcher.d.ts +53 -0
  142. package/dist/types/codec/AppointmentRecurrence.d.ts +14 -0
  143. package/dist/types/codec/BufferCursor.d.ts +92 -0
  144. package/dist/types/codec/GlobalObjectId.d.ts +15 -0
  145. package/dist/types/codec/MapiGuid.d.ts +6 -0
  146. package/dist/types/codec/MapiTimeZone.d.ts +7 -0
  147. package/dist/types/codec/PropertyValue.d.ts +87 -0
  148. package/dist/types/codec/RopBuffer.d.ts +32 -0
  149. package/dist/types/codec/TypedString.d.ts +17 -0
  150. package/dist/types/index.d.ts +94 -0
  151. package/dist/types/mongo/MapiEmsmdbRouteMongo.d.ts +20 -0
  152. package/dist/types/mongo/MapiNspiRouteMongo.d.ts +13 -0
  153. package/dist/types/mongo/index.d.ts +2 -0
  154. package/dist/types/mongo.d.ts +1 -0
  155. package/dist/types/nspi/NspiBindHandler.d.ts +22 -0
  156. package/dist/types/nspi/NspiCodec.d.ts +59 -0
  157. package/dist/types/nspi/NspiGetMatchesHandler.d.ts +26 -0
  158. package/dist/types/rop/AddressList.d.ts +48 -0
  159. package/dist/types/rop/CalendarEventTarget.d.ts +33 -0
  160. package/dist/types/rop/CalendarNamedProperties.d.ts +37 -0
  161. package/dist/types/rop/ContactTarget.d.ts +29 -0
  162. package/dist/types/rop/ContentsTable.d.ts +16 -0
  163. package/dist/types/rop/ExecuteBudget.d.ts +48 -0
  164. package/dist/types/rop/FastTransferStream.d.ts +57 -0
  165. package/dist/types/rop/FolderTarget.d.ts +64 -0
  166. package/dist/types/rop/HandleDataCache.d.ts +145 -0
  167. package/dist/types/rop/MeetingMessageClassHandler.d.ts +30 -0
  168. package/dist/types/rop/MessageBodyStream.d.ts +35 -0
  169. package/dist/types/rop/MessageTarget.d.ts +45 -0
  170. package/dist/types/rop/NamedPropertyRegistry.d.ts +50 -0
  171. package/dist/types/rop/PropertyResolvers.d.ts +86 -0
  172. package/dist/types/rop/RepoPaging.d.ts +30 -0
  173. package/dist/types/rop/RestapiRules.d.ts +32 -0
  174. package/dist/types/rop/RopCreateMessageHandler.d.ts +24 -0
  175. package/dist/types/rop/RopDeleteFolderHandler.d.ts +53 -0
  176. package/dist/types/rop/RopDeleteMessagesHandler.d.ts +43 -0
  177. package/dist/types/rop/RopFastTransferSourceCopyPropertiesHandler.d.ts +25 -0
  178. package/dist/types/rop/RopFastTransferSourceCopyToHandler.d.ts +25 -0
  179. package/dist/types/rop/RopFastTransferSourceGetBufferHandler.d.ts +22 -0
  180. package/dist/types/rop/RopGetContentsTableHandler.d.ts +25 -0
  181. package/dist/types/rop/RopGetHierarchyTableHandler.d.ts +18 -0
  182. package/dist/types/rop/RopGetPropertiesSpecificHandler.d.ts +26 -0
  183. package/dist/types/rop/RopGetPropertyIdsFromNamesHandler.d.ts +23 -0
  184. package/dist/types/rop/RopHandler.d.ts +106 -0
  185. package/dist/types/rop/RopLogonHandler.d.ts +31 -0
  186. package/dist/types/rop/RopOpenFolderHandler.d.ts +20 -0
  187. package/dist/types/rop/RopOpenMessageHandler.d.ts +28 -0
  188. package/dist/types/rop/RopOpenStreamHandler.d.ts +28 -0
  189. package/dist/types/rop/RopQueryRowsHandler.d.ts +43 -0
  190. package/dist/types/rop/RopReadStreamHandler.d.ts +20 -0
  191. package/dist/types/rop/RopReleaseHandler.d.ts +15 -0
  192. package/dist/types/rop/RopSaveChangesMessageHandler.d.ts +59 -0
  193. package/dist/types/rop/RopSetColumnsHandler.d.ts +21 -0
  194. package/dist/types/rop/RopSetPropertiesHandler.d.ts +38 -0
  195. package/dist/types/rop/RopSubmitMessageHandler.d.ts +80 -0
  196. package/dist/types/rop/RopWriteStreamHandler.d.ts +31 -0
  197. package/dist/types/rop/TaskNamedProperties.d.ts +24 -0
  198. package/dist/types/rop/TaskTarget.d.ts +24 -0
  199. package/dist/types/rop/TransportSend.d.ts +10 -0
  200. package/dist/types/sql/MapiEmsmdbRouteSQL.d.ts +20 -0
  201. package/dist/types/sql/MapiNspiRouteSQL.d.ts +13 -0
  202. package/dist/types/sql/index.d.ts +2 -0
  203. package/dist/types/sql.d.ts +1 -0
  204. package/package.json +104 -0
@@ -0,0 +1,412 @@
1
+ ///////////////////////////////////////////////////////////////////////////////
2
+ // Copyright (C) 2026 Jean-Philippe Steinmetz. All rights reserved.
3
+ // SPDX-License-Identifier: MPL-2.0
4
+ ///////////////////////////////////////////////////////////////////////////////
5
+ /** How long an entry lives without being read - the same idle lifetime as a session. */
6
+ export const HANDLE_DATA_TTL_MS = 15 * 60 * 1000;
7
+ /** The most bytes this process keeps across all entries before evicting the least recently used ones. */
8
+ export const HANDLE_DATA_MAX_BYTES = 256 * 1024 * 1024;
9
+ /**
10
+ * A process-local, byte-bounded LRU with TTL for large per-handle data: an opened stream's decoded message body
11
+ * and a FastTransfer handle's built stream. Keeping these out of the session JSON means an `Execute` doesn't
12
+ * re-serialize megabytes to Redis, and a `RopReadStream` doesn't re-fetch and re-parse the whole MIME message
13
+ * for every chunk.
14
+ *
15
+ * It is only a cache. Every caller can rebuild an entry from the handle's own (small) session state, so a miss
16
+ * (another replica served the previous request, eviction, TTL) costs one rebuild, never wrong data. Keys carry
17
+ * the handle's generation (see `assignHandle`), so a reused handle index never picks up a previous handle's data.
18
+ */
19
+ export class HandleDataCache {
20
+ constructor(maxBytes = HANDLE_DATA_MAX_BYTES, ttlMs = HANDLE_DATA_TTL_MS) {
21
+ this.maxBytes = maxBytes;
22
+ this.ttlMs = ttlMs;
23
+ this.entries = new Map();
24
+ this.totalBytes = 0;
25
+ }
26
+ get(key) {
27
+ const entry = this.entries.get(key);
28
+ if (!entry) {
29
+ return undefined;
30
+ }
31
+ this.entries.delete(key);
32
+ if (entry.expiresAt <= Date.now()) {
33
+ this.totalBytes -= entry.value.length;
34
+ return undefined;
35
+ }
36
+ // Re-inserting moves the entry to the end of the Map's iteration order (most recently used).
37
+ entry.expiresAt = Date.now() + this.ttlMs;
38
+ this.entries.set(key, entry);
39
+ return entry.value;
40
+ }
41
+ /** Stores `value` under `key`. A value larger than the whole budget isn't cached at all. */
42
+ set(key, value) {
43
+ this.delete(key);
44
+ if (value.length > this.maxBytes) {
45
+ return;
46
+ }
47
+ this.entries.set(key, { value, expiresAt: Date.now() + this.ttlMs });
48
+ this.totalBytes += value.length;
49
+ for (const [oldestKey, oldest] of this.entries) {
50
+ if (this.totalBytes <= this.maxBytes) {
51
+ break;
52
+ }
53
+ this.entries.delete(oldestKey);
54
+ this.totalBytes -= oldest.value.length;
55
+ }
56
+ }
57
+ /** The size of the live entry at `key`, without refreshing it or changing its recency. */
58
+ sizeOf(key) {
59
+ const entry = this.entries.get(key);
60
+ return entry && entry.expiresAt > Date.now() ? entry.value.length : undefined;
61
+ }
62
+ delete(key) {
63
+ const entry = this.entries.get(key);
64
+ if (entry) {
65
+ this.entries.delete(key);
66
+ this.totalBytes -= entry.value.length;
67
+ }
68
+ }
69
+ /** The bytes currently held, for tests and diagnostics. */
70
+ get size() {
71
+ return this.totalBytes;
72
+ }
73
+ }
74
+ /** The process-wide instance every handler shares. */
75
+ export const handleDataCache = new HandleDataCache();
76
+ /** The cache key for the handle at `handleIndex`, as of its current generation. A generation is a random nonce
77
+ * (see `assignHandle`), so a key is never reused, not even by a request whose session changes were never saved. */
78
+ export function handleDataKey(sessionUid, handleIndex, generation) {
79
+ return `${sessionUid}:${handleIndex}:${generation ?? ""}`;
80
+ }
81
+ /** The `HandleDataStore` key of the write stream at `handleIndex`. */
82
+ export function writeStreamKey(sessionUid, handleIndex, generation) {
83
+ return `${handleDataKey(sessionUid, handleIndex, generation)}:write`;
84
+ }
85
+ /** The most bytes of FastTransfer streams and write-stream chunks one session keeps stored at once. */
86
+ export const MAX_HANDLE_DATA_BYTES_PER_SESSION = 64 * 1024 * 1024;
87
+ /** The most bytes of FastTransfer streams and write-stream chunks one user keeps stored at once, across all of their
88
+ * sessions. */
89
+ export const MAX_HANDLE_DATA_BYTES_PER_USER = 128 * 1024 * 1024;
90
+ /** How long an owner index outlives its last write: a session's longest lifetime. */
91
+ export const HANDLE_DATA_INDEX_TTL_SECONDS = 24 * 60 * 60;
92
+ /** The most chunks one write stream keeps. Each `RopWriteStream` stores one chunk, so this bounds the hash a stream
93
+ * builds up (and the work `readChunks` does) however small the writes are. */
94
+ export const MAX_CHUNKS_PER_STREAM = 8192;
95
+ /** How often, at most, an owner index that is over its quota is recounted from its members. Between recounts a write is
96
+ * checked against the index's running total alone, so a client hammering a full quota can't make every write walk
97
+ * every member. */
98
+ export const OWNER_RECOUNT_INTERVAL_MS = 1000;
99
+ /** The index of everything a session stores, used for its quota and to delete it all when the session ends. */
100
+ export function sessionHandleDataIndex(sessionUid) {
101
+ return `session.${sessionUid}`;
102
+ }
103
+ /** The quota owners a write by `sessionUid` (belonging to `userUid`) counts against. */
104
+ export function handleDataOwners(sessionUid, userUid) {
105
+ return [
106
+ { index: sessionHandleDataIndex(sessionUid), maxBytes: MAX_HANDLE_DATA_BYTES_PER_SESSION },
107
+ { index: `user.${userUid}`, maxBytes: MAX_HANDLE_DATA_BYTES_PER_USER },
108
+ ];
109
+ }
110
+ /** Walks `chunkAt(offset)` from offset 0 until `size` bytes are assembled. */
111
+ async function assembleChunks(size, chunkAt) {
112
+ const parts = [];
113
+ let offset = 0;
114
+ while (offset < size) {
115
+ const chunk = await chunkAt(offset);
116
+ if (!chunk || chunk.length === 0) {
117
+ return undefined;
118
+ }
119
+ parts.push(chunk);
120
+ offset += chunk.length;
121
+ }
122
+ return offset === size ? Buffer.concat(parts) : undefined;
123
+ }
124
+ /** A `HandleDataStore` in this process only, on a `HandleDataCache` (so it is byte-bounded and can evict). */
125
+ export class MemoryHandleDataStore {
126
+ constructor(cache = handleDataCache) {
127
+ this.cache = cache;
128
+ this.indexes = new Map();
129
+ this.streams = new Map();
130
+ }
131
+ async get(key) {
132
+ return this.cache.get(key);
133
+ }
134
+ async set(key, value, _ttlSeconds, owners = []) {
135
+ if (!this.admit(key, value.length, owners)) {
136
+ return false;
137
+ }
138
+ this.cache.set(key, value);
139
+ return true;
140
+ }
141
+ async putChunk(key, offset, data, _ttlSeconds, owners = []) {
142
+ const stream = this.streams.get(key) ?? { offsets: new Set(), size: 0 };
143
+ if (!stream.offsets.has(offset) && stream.offsets.size >= MAX_CHUNKS_PER_STREAM) {
144
+ return false;
145
+ }
146
+ const size = Math.max(stream.size, offset + data.length);
147
+ if (!this.admit(key, size, owners)) {
148
+ return false;
149
+ }
150
+ this.cache.set(`${key}#${offset}`, data);
151
+ stream.offsets.add(offset);
152
+ stream.size = size;
153
+ this.streams.set(key, stream);
154
+ return true;
155
+ }
156
+ readChunks(key, size) {
157
+ return assembleChunks(size, (offset) => this.cache.get(`${key}#${offset}`));
158
+ }
159
+ async delete(key, owners = []) {
160
+ this.cache.delete(key);
161
+ for (const offset of this.streams.get(key)?.offsets ?? []) {
162
+ this.cache.delete(`${key}#${offset}`);
163
+ }
164
+ this.streams.delete(key);
165
+ for (const owner of owners) {
166
+ const index = this.indexes.get(owner.index);
167
+ const bytes = index?.members.get(key);
168
+ if (index && bytes !== undefined) {
169
+ index.members.delete(key);
170
+ index.total -= bytes;
171
+ }
172
+ }
173
+ }
174
+ async deleteOwnedBy(index) {
175
+ for (const key of this.indexes.get(index)?.members.keys() ?? []) {
176
+ await this.delete(key);
177
+ }
178
+ this.indexes.delete(index);
179
+ }
180
+ /** `true` while `key` still holds a value or its stream's first chunk (a stream missing that can't be read). */
181
+ exists(key) {
182
+ if (this.cache.sizeOf(key) !== undefined) {
183
+ return true;
184
+ }
185
+ const first = this.streams.get(key)?.offsets.values().next().value;
186
+ return first !== undefined && this.cache.sizeOf(`${key}#${first}`) !== undefined;
187
+ }
188
+ /** Records `key` at `bytes` in every owner index, unless that would take one past its quota. */
189
+ admit(key, bytes, owners) {
190
+ const indexes = [];
191
+ for (const owner of owners) {
192
+ const index = this.indexes.get(owner.index) ?? { members: new Map(), total: 0, recountAfter: 0 };
193
+ this.indexes.set(owner.index, index);
194
+ if (!this.fits(index, key, bytes, owner.maxBytes)) {
195
+ return false;
196
+ }
197
+ indexes.push(index);
198
+ }
199
+ for (const index of indexes) {
200
+ index.total += bytes - (index.members.get(key) ?? 0);
201
+ index.members.set(key, bytes);
202
+ }
203
+ return true;
204
+ }
205
+ /** `true` when `key` at `bytes` keeps `index` within `maxBytes`, recounting an index that looks full (rate limited). */
206
+ fits(index, key, bytes, maxBytes) {
207
+ if (index.total - (index.members.get(key) ?? 0) + bytes <= maxBytes) {
208
+ return true;
209
+ }
210
+ const now = Date.now();
211
+ if (now < index.recountAfter) {
212
+ return false;
213
+ }
214
+ index.recountAfter = now + OWNER_RECOUNT_INTERVAL_MS;
215
+ index.total = 0;
216
+ for (const [member, size] of index.members) {
217
+ if (this.exists(member)) {
218
+ index.total += size;
219
+ }
220
+ else {
221
+ index.members.delete(member);
222
+ this.streams.delete(member);
223
+ }
224
+ }
225
+ return index.total - (index.members.get(key) ?? 0) + bytes <= maxBytes;
226
+ }
227
+ }
228
+ /**
229
+ * The owner index helpers every quota script shares. An index is a sorted set (member -> raw bytes) with its running
230
+ * total in `<index>.total` (always the sum of the set's scores) and a recount rate limit in `<index>.recount`.
231
+ * - `recount` rebuilds the total from the members whose data key still exists, dropping the rest.
232
+ * - `admits` checks the running total, recounting (at most once per `recountMs`) only when that total would refuse. An
233
+ * index without a total yet (written before totals existed, or expired) is recounted first.
234
+ * - `record` sets a member's bytes, adjusting the total by the difference.
235
+ * The data keys `recount` looks at are built from the members, so they aren't declared in KEYS (fine on one Redis node,
236
+ * not on a cluster).
237
+ */
238
+ const OWNER_INDEX_LUA = `
239
+ local function recount(index, prefix, indexTtl)
240
+ local total = 0
241
+ local members = redis.call('ZRANGE', index, 0, -1, 'WITHSCORES')
242
+ for j = 1, #members, 2 do
243
+ if redis.call('EXISTS', prefix .. members[j]) == 0 then
244
+ redis.call('ZREM', index, members[j])
245
+ else
246
+ total = total + tonumber(members[j + 1])
247
+ end
248
+ end
249
+ redis.call('SET', index .. '.total', tostring(total), 'EX', indexTtl)
250
+ return total
251
+ end
252
+ local function admits(index, member, bytes, maxBytes, prefix, recountMs, indexTtl)
253
+ local stored = redis.call('GET', index .. '.total')
254
+ local fresh = not stored
255
+ local total = stored and tonumber(stored) or recount(index, prefix, indexTtl)
256
+ local old = tonumber(redis.call('ZSCORE', index, member) or 0)
257
+ if total - old + bytes <= maxBytes then return true end
258
+ if fresh or not redis.call('SET', index .. '.recount', '1', 'PX', recountMs, 'NX') then return false end
259
+ total = recount(index, prefix, indexTtl)
260
+ old = tonumber(redis.call('ZSCORE', index, member) or 0)
261
+ return total - old + bytes <= maxBytes
262
+ end
263
+ local function record(index, member, bytes, indexTtl)
264
+ local old = tonumber(redis.call('ZSCORE', index, member) or 0)
265
+ redis.call('ZADD', index, tostring(bytes), member)
266
+ redis.call('INCRBY', index .. '.total', tostring(bytes - old))
267
+ redis.call('EXPIRE', index, indexTtl)
268
+ redis.call('EXPIRE', index .. '.total', indexTtl)
269
+ end
270
+ `;
271
+ /** Stores a value within its owners' quotas. KEYS: data key, then each owner index. ARGV: data key prefix, member name,
272
+ * index ttl, recount interval ms, base64 value, ttl, raw bytes, then each owner's max bytes. Returns 1 stored, 0 refused. */
273
+ export const SET_WITH_QUOTA_SCRIPT = `${OWNER_INDEX_LUA}
274
+ local bytes = tonumber(ARGV[7])
275
+ for i = 2, #KEYS do
276
+ if not admits(KEYS[i], ARGV[2], bytes, tonumber(ARGV[i + 6]), ARGV[1], ARGV[4], ARGV[3]) then return 0 end
277
+ end
278
+ redis.call('SETEX', KEYS[1], ARGV[6], ARGV[5])
279
+ for i = 2, #KEYS do record(KEYS[i], ARGV[2], bytes, ARGV[3]) end
280
+ return 1
281
+ `;
282
+ /** Stores one write-stream chunk within its owners' quotas, in O(1): the hash keeps its size (the end of its furthest
283
+ * chunk) in the `size` field, and that size is what the owners are charged, so a chunk rewritten at the same offset
284
+ * isn't charged twice. KEYS: hash key, then each owner index. ARGV: data key prefix, member name, index ttl, recount
285
+ * interval ms, unpadded base64url chunk, ttl, raw chunk bytes, hash field (offset), max chunks, then each owner's max
286
+ * bytes. Returns 1 stored, 0 refused (quota, or a new chunk past the max chunks). */
287
+ export const PUT_CHUNK_WITH_QUOTA_SCRIPT = `${OWNER_INDEX_LUA}
288
+ local field = ARGV[8]
289
+ if redis.call('HEXISTS', KEYS[1], field) == 0 and redis.call('HLEN', KEYS[1]) > tonumber(ARGV[9]) then return 0 end
290
+ local size = math.max(tonumber(redis.call('HGET', KEYS[1], 'size') or 0), tonumber(field) + tonumber(ARGV[7]))
291
+ for i = 2, #KEYS do
292
+ if not admits(KEYS[i], ARGV[2], size, tonumber(ARGV[i + 8]), ARGV[1], ARGV[4], ARGV[3]) then return 0 end
293
+ end
294
+ redis.call('HSET', KEYS[1], field, ARGV[5], 'size', tostring(size))
295
+ redis.call('EXPIRE', KEYS[1], ARGV[6])
296
+ for i = 2, #KEYS do record(KEYS[i], ARGV[2], size, ARGV[3]) end
297
+ return 1
298
+ `;
299
+ /** Deletes one data key and takes it off its owners' totals. KEYS: data key, then each owner index. ARGV: member name. */
300
+ export const DELETE_WITH_OWNERS_SCRIPT = `
301
+ redis.call('DEL', KEYS[1])
302
+ for i = 2, #KEYS do
303
+ local old = redis.call('ZSCORE', KEYS[i], ARGV[1])
304
+ if old then
305
+ redis.call('ZREM', KEYS[i], ARGV[1])
306
+ if redis.call('EXISTS', KEYS[i] .. '.total') == 1 then redis.call('INCRBY', KEYS[i] .. '.total', tostring(-tonumber(old))) end
307
+ end
308
+ end
309
+ return 1
310
+ `;
311
+ /** Deletes every key an owner index names, then the index with its total and recount marker. KEYS: index. ARGV: data key
312
+ * prefix. Returns the members (unprefixed keys) it deleted, so the caller can drop its local copies too. */
313
+ export const DELETE_OWNED_SCRIPT = `
314
+ local members = redis.call('ZRANGE', KEYS[1], 0, -1)
315
+ for _, member in ipairs(members) do
316
+ redis.call('DEL', ARGV[1] .. member)
317
+ end
318
+ redis.call('DEL', KEYS[1], KEYS[1] .. '.total', KEYS[1] .. '.recount')
319
+ return members
320
+ `;
321
+ /** The prefix of every handle data key in Redis. */
322
+ export const HANDLE_DATA_PREFIX = "mapi.handle.";
323
+ /**
324
+ * A `HandleDataStore` on a node-redis client, with this process's `HandleDataCache` in front for reads. Values are
325
+ * stored base64-encoded (a plain string works with any client configuration); a write stream is a hash of
326
+ * `offset -> chunk` plus its `size`. Owner indexes are sorted sets (`mapi.handle-owner.<index>`) of key -> raw bytes, with
327
+ * a running total beside each (see `OWNER_INDEX_LUA`).
328
+ */
329
+ export class RedisHandleDataStore {
330
+ constructor(client, local = handleDataCache) {
331
+ this.client = client;
332
+ this.local = local;
333
+ }
334
+ async get(key) {
335
+ const cached = this.local.get(key);
336
+ if (cached) {
337
+ return cached;
338
+ }
339
+ const stored = await this.client.get(redisKey(key));
340
+ if (stored == null) {
341
+ return undefined;
342
+ }
343
+ const value = Buffer.from(stored, "base64");
344
+ this.local.set(key, value);
345
+ return value;
346
+ }
347
+ async set(key, value, ttlSeconds, owners = []) {
348
+ const result = await this.client.eval(SET_WITH_QUOTA_SCRIPT, {
349
+ keys: [redisKey(key), ...owners.map((owner) => ownerKey(owner.index))],
350
+ arguments: [
351
+ ...ownerScriptArguments(key),
352
+ value.toString("base64"),
353
+ String(ttlSeconds),
354
+ String(value.length),
355
+ ...owners.map((owner) => String(owner.maxBytes)),
356
+ ],
357
+ });
358
+ if (Number(result) !== 1) {
359
+ return false;
360
+ }
361
+ this.local.set(key, value);
362
+ return true;
363
+ }
364
+ async putChunk(key, offset, data, ttlSeconds, owners = []) {
365
+ const result = await this.client.eval(PUT_CHUNK_WITH_QUOTA_SCRIPT, {
366
+ keys: [redisKey(key), ...owners.map((owner) => ownerKey(owner.index))],
367
+ arguments: [
368
+ ...ownerScriptArguments(key),
369
+ // Unpadded base64url: the smallest encoding Node's "base64" decoding reads back.
370
+ data.toString("base64url"),
371
+ String(ttlSeconds),
372
+ String(data.length),
373
+ String(offset),
374
+ String(MAX_CHUNKS_PER_STREAM),
375
+ ...owners.map((owner) => String(owner.maxBytes)),
376
+ ],
377
+ });
378
+ return Number(result) === 1;
379
+ }
380
+ async readChunks(key, size) {
381
+ const chunks = (await this.client.hGetAll(redisKey(key))) ?? {};
382
+ return assembleChunks(size, (offset) => (chunks[String(offset)] !== undefined ? Buffer.from(chunks[String(offset)], "base64") : undefined));
383
+ }
384
+ async delete(key, owners = []) {
385
+ this.local.delete(key);
386
+ if (owners.length === 0) {
387
+ await this.client.del(redisKey(key));
388
+ return;
389
+ }
390
+ await this.client.eval(DELETE_WITH_OWNERS_SCRIPT, { keys: [redisKey(key), ...owners.map((owner) => ownerKey(owner.index))], arguments: [key] });
391
+ }
392
+ async deleteOwnedBy(index) {
393
+ const deleted = (await this.client.eval(DELETE_OWNED_SCRIPT, { keys: [ownerKey(index)], arguments: [HANDLE_DATA_PREFIX] })) ?? [];
394
+ for (const key of deleted) {
395
+ this.local.delete(String(key));
396
+ }
397
+ }
398
+ }
399
+ function redisKey(key) {
400
+ return `${HANDLE_DATA_PREFIX}${key}`;
401
+ }
402
+ /** The ARGV every quota script starts with: data key prefix, member name, index ttl, recount interval ms. */
403
+ function ownerScriptArguments(key) {
404
+ return [HANDLE_DATA_PREFIX, key, String(HANDLE_DATA_INDEX_TTL_SECONDS), String(OWNER_RECOUNT_INTERVAL_MS)];
405
+ }
406
+ /** The Redis key of an owner index. */
407
+ export function ownerKey(index) {
408
+ return `mapi.handle-owner.${index}`;
409
+ }
410
+ /** The store used when a `RopContext` doesn't carry one (unit tests, or a route built without a session manager). */
411
+ export const defaultHandleDataStore = new MemoryHandleDataStore();
412
+ //# sourceMappingURL=HandleDataCache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HandleDataCache.js","sourceRoot":"","sources":["../../../src/rop/HandleDataCache.ts"],"names":[],"mappings":"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"}