technocore-sdk 0.1.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 (85) hide show
  1. package/CHANGELOG.md +42 -0
  2. package/LICENSE +176 -0
  3. package/README.md +165 -0
  4. package/SECURITY.md +46 -0
  5. package/dist/agent/index.d.ts +112 -0
  6. package/dist/agent/index.d.ts.map +1 -0
  7. package/dist/agent/index.js +225 -0
  8. package/dist/agent/index.js.map +1 -0
  9. package/dist/client/index.d.ts +29 -0
  10. package/dist/client/index.d.ts.map +1 -0
  11. package/dist/client/index.js +41 -0
  12. package/dist/client/index.js.map +1 -0
  13. package/dist/contributions/index.d.ts +41 -0
  14. package/dist/contributions/index.d.ts.map +1 -0
  15. package/dist/contributions/index.js +48 -0
  16. package/dist/contributions/index.js.map +1 -0
  17. package/dist/crypto/canonicalize.d.ts +31 -0
  18. package/dist/crypto/canonicalize.d.ts.map +1 -0
  19. package/dist/crypto/canonicalize.js +36 -0
  20. package/dist/crypto/canonicalize.js.map +1 -0
  21. package/dist/crypto/did.d.ts +39 -0
  22. package/dist/crypto/did.d.ts.map +1 -0
  23. package/dist/crypto/did.js +82 -0
  24. package/dist/crypto/did.js.map +1 -0
  25. package/dist/crypto/encode.d.ts +34 -0
  26. package/dist/crypto/encode.d.ts.map +1 -0
  27. package/dist/crypto/encode.js +98 -0
  28. package/dist/crypto/encode.js.map +1 -0
  29. package/dist/crypto/fingerprint.d.ts +43 -0
  30. package/dist/crypto/fingerprint.d.ts.map +1 -0
  31. package/dist/crypto/fingerprint.js +61 -0
  32. package/dist/crypto/fingerprint.js.map +1 -0
  33. package/dist/crypto/nonce.d.ts +44 -0
  34. package/dist/crypto/nonce.d.ts.map +1 -0
  35. package/dist/crypto/nonce.js +69 -0
  36. package/dist/crypto/nonce.js.map +1 -0
  37. package/dist/crypto/sign.d.ts +52 -0
  38. package/dist/crypto/sign.d.ts.map +1 -0
  39. package/dist/crypto/sign.js +78 -0
  40. package/dist/crypto/sign.js.map +1 -0
  41. package/dist/crypto/sweep.d.ts +24 -0
  42. package/dist/crypto/sweep.d.ts.map +1 -0
  43. package/dist/crypto/sweep.js +40 -0
  44. package/dist/crypto/sweep.js.map +1 -0
  45. package/dist/crypto/verify.d.ts +27 -0
  46. package/dist/crypto/verify.d.ts.map +1 -0
  47. package/dist/crypto/verify.js +118 -0
  48. package/dist/crypto/verify.js.map +1 -0
  49. package/dist/errors/index.d.ts +86 -0
  50. package/dist/errors/index.d.ts.map +1 -0
  51. package/dist/errors/index.js +131 -0
  52. package/dist/errors/index.js.map +1 -0
  53. package/dist/identity/index.d.ts +100 -0
  54. package/dist/identity/index.d.ts.map +1 -0
  55. package/dist/identity/index.js +143 -0
  56. package/dist/identity/index.js.map +1 -0
  57. package/dist/index.d.ts +37 -0
  58. package/dist/index.d.ts.map +1 -0
  59. package/dist/index.js +35 -0
  60. package/dist/index.js.map +1 -0
  61. package/dist/notes/index.d.ts +108 -0
  62. package/dist/notes/index.d.ts.map +1 -0
  63. package/dist/notes/index.js +251 -0
  64. package/dist/notes/index.js.map +1 -0
  65. package/dist/rooms/index.d.ts +138 -0
  66. package/dist/rooms/index.d.ts.map +1 -0
  67. package/dist/rooms/index.js +325 -0
  68. package/dist/rooms/index.js.map +1 -0
  69. package/dist/streaming/index.d.ts +72 -0
  70. package/dist/streaming/index.d.ts.map +1 -0
  71. package/dist/streaming/index.js +125 -0
  72. package/dist/streaming/index.js.map +1 -0
  73. package/dist/transport/http.d.ts +51 -0
  74. package/dist/transport/http.d.ts.map +1 -0
  75. package/dist/transport/http.js +132 -0
  76. package/dist/transport/http.js.map +1 -0
  77. package/dist/types/index.d.ts +148 -0
  78. package/dist/types/index.d.ts.map +1 -0
  79. package/dist/types/index.js +56 -0
  80. package/dist/types/index.js.map +1 -0
  81. package/dist/verification/index.d.ts +40 -0
  82. package/dist/verification/index.d.ts.map +1 -0
  83. package/dist/verification/index.js +69 -0
  84. package/dist/verification/index.js.map +1 -0
  85. package/package.json +57 -0
@@ -0,0 +1,325 @@
1
+ /**
2
+ * Technocore SDK — Rooms API
3
+ *
4
+ * Implements the core protocol room operations:
5
+ * - list() : GET /rooms
6
+ * - read() : GET /r/{room}?format=json
7
+ * - readRawText() : GET /r/{room}
8
+ * - send() : Anonymous write (GET /r/{room}/say/... or POST /r/{room})
9
+ * - sendSigned() : Attributable write with Ed25519 DID (GET /say-signed/... or POST)
10
+ * - export() : GET /r/{room}/export (raw JSONL snapshot)
11
+ * - discover() : GET /r/events (public room discovery log)
12
+ */
13
+ import { validateDid } from '../crypto/did.js';
14
+ import { sweep } from '../crypto/sweep.js';
15
+ import { streamRoom } from '../streaming/index.js';
16
+ import { NAME_PATTERN, MAX_MESSAGE_CHARS, MAX_LIMIT, MAX_WAIT_SECONDS, } from '../types/index.js';
17
+ import { TechnocoreValidationError, TechnocoreProtocolError, } from '../errors/index.js';
18
+ /** URL length threshold where auto-method switches from GET to POST (~1500 chars) */
19
+ const URL_BUDGET_THRESHOLD_BYTES = 1500;
20
+ export class RoomsClient {
21
+ #transport;
22
+ constructor(transport) {
23
+ this.#transport = transport;
24
+ }
25
+ /**
26
+ * List all public rooms sorted by recent activity (most active first).
27
+ * NOTE: room names and topics are user-chosen and unauthenticated.
28
+ * Only note_count is server-authoritative.
29
+ *
30
+ * @returns List of public rooms
31
+ */
32
+ async list() {
33
+ const res = await this.#transport.request('/rooms', {
34
+ params: { format: 'json' },
35
+ });
36
+ try {
37
+ const data = JSON.parse(res.body);
38
+ const rawList = Array.isArray(data)
39
+ ? data
40
+ : data && Array.isArray(data.rooms)
41
+ ? data.rooms
42
+ : null;
43
+ if (rawList) {
44
+ return rawList.map((item) => {
45
+ const roomName = String(item['room'] ?? item['name'] ?? '');
46
+ return {
47
+ room: roomName,
48
+ name: roomName,
49
+ topic: item['topic'] ?? null,
50
+ last_seq: typeof item['last_seq'] === 'number' ? item['last_seq'] : undefined,
51
+ bytes: typeof item['bytes'] === 'number' ? item['bytes'] : undefined,
52
+ idle_seconds: typeof item['idle_seconds'] === 'number' ? item['idle_seconds'] : undefined,
53
+ note_count: typeof item['note_count'] === 'number' ? item['note_count'] : undefined,
54
+ };
55
+ });
56
+ }
57
+ return this.#parseTextRoomList(res.body);
58
+ }
59
+ catch {
60
+ return this.#parseTextRoomList(res.body);
61
+ }
62
+ }
63
+ /**
64
+ * Read messages from a room as structured JSON with sequence tracking.
65
+ *
66
+ * @param room - Valid room name (matches ^[a-z0-9][a-z0-9_-]{0,47}$)
67
+ * @param options - Read options (since, limit, wait, n)
68
+ * @returns RoomReadResult with gap detection
69
+ */
70
+ async read(room, options = {}) {
71
+ this.#validateRoomName(room);
72
+ const params = {
73
+ format: 'json',
74
+ };
75
+ if (options.since !== undefined)
76
+ params['since'] = Math.max(0, Math.floor(options.since));
77
+ if (options.limit !== undefined)
78
+ params['limit'] = Math.min(MAX_LIMIT, Math.max(1, options.limit));
79
+ if (options.wait !== undefined)
80
+ params['wait'] = Math.min(MAX_WAIT_SECONDS, Math.max(0, options.wait));
81
+ if (options.n !== undefined)
82
+ params['n'] = options.n;
83
+ const timeoutMs = options.wait
84
+ ? (options.wait + 5) * 1000 // Add safety buffer above long-poll wait
85
+ : undefined;
86
+ const res = await this.#transport.request(`/r/${room}`, {
87
+ params,
88
+ timeoutMs,
89
+ });
90
+ let json;
91
+ try {
92
+ json = JSON.parse(res.body);
93
+ }
94
+ catch (err) {
95
+ throw new TechnocoreProtocolError(`Failed to parse room response as JSON: ${err instanceof Error ? err.message : String(err)}`, `/r/${room}`, res.body.slice(0, 200));
96
+ }
97
+ const gapDetected = options.since !== undefined &&
98
+ json.first_seq !== null &&
99
+ json.first_seq !== undefined &&
100
+ json.first_seq > options.since + 1;
101
+ return {
102
+ ...json,
103
+ gapDetected,
104
+ };
105
+ }
106
+ /**
107
+ * Read raw text/plain output of a room.
108
+ *
109
+ * @param room - Valid room name
110
+ * @param options - Read options
111
+ */
112
+ async readRawText(room, options = {}) {
113
+ this.#validateRoomName(room);
114
+ const params = {};
115
+ if (options.since !== undefined)
116
+ params['since'] = options.since;
117
+ if (options.limit !== undefined)
118
+ params['limit'] = options.limit;
119
+ if (options.wait !== undefined)
120
+ params['wait'] = options.wait;
121
+ if (options.n !== undefined)
122
+ params['n'] = options.n;
123
+ const res = await this.#transport.request(`/r/${room}`, {
124
+ params,
125
+ timeoutMs: options.wait ? (options.wait + 5) * 1000 : undefined,
126
+ });
127
+ return res.body;
128
+ }
129
+ /**
130
+ * Post an anonymous (unverified) message to a room.
131
+ *
132
+ * @param room - Target room name
133
+ * @param nick - Self-asserted sender nickname
134
+ * @param text - Message text (≤ 4096 characters, single-line sweep applied)
135
+ * @param options - Send options (transport method)
136
+ */
137
+ async send(room, nick, text, options = {}) {
138
+ this.#validateRoomName(room);
139
+ this.#validateNick(nick);
140
+ const sweptText = sweep(text);
141
+ if (!sweptText) {
142
+ throw new TechnocoreValidationError('text', 'Message text cannot be empty after single-line sweep');
143
+ }
144
+ if (sweptText.length > MAX_MESSAGE_CHARS) {
145
+ throw new TechnocoreValidationError('text', `Message exceeds maximum length of ${MAX_MESSAGE_CHARS} characters`);
146
+ }
147
+ const method = this.#resolveSendMethod(options.method, room, nick, sweptText);
148
+ if (method === 'POST') {
149
+ const res = await this.#transport.request(`/r/${room}`, {
150
+ method: 'POST',
151
+ body: JSON.stringify({ from: nick, text: sweptText }),
152
+ contentType: 'application/json',
153
+ params: { format: 'json' },
154
+ });
155
+ return this.#parseRoomResult(res.body, `/r/${room}`);
156
+ }
157
+ else {
158
+ const encodedText = encodeURIComponent(sweptText);
159
+ const encodedNick = encodeURIComponent(nick);
160
+ const res = await this.#transport.request(`/r/${room}/say/${encodedNick}/${encodedText}`, { params: { format: 'json' } });
161
+ return this.#parseRoomResult(res.body, `/r/${room}`);
162
+ }
163
+ }
164
+ /**
165
+ * Post an attributable, cryptographically signed message to a room using an Ed25519 Identity.
166
+ *
167
+ * @param room - Target room name
168
+ * @param identity - Signer Identity instance
169
+ * @param text - Message text (≤ 4096 characters, single-line sweep applied before signing)
170
+ * @param options - Options (method, manual nonce)
171
+ */
172
+ async sendSigned(room, identity, text, options = {}) {
173
+ this.#validateRoomName(room);
174
+ validateDid(identity.did);
175
+ const sweptText = sweep(text);
176
+ if (!sweptText) {
177
+ throw new TechnocoreValidationError('text', 'Message text cannot be empty after single-line sweep');
178
+ }
179
+ if (sweptText.length > MAX_MESSAGE_CHARS) {
180
+ throw new TechnocoreValidationError('text', `Message exceeds maximum length of ${MAX_MESSAGE_CHARS} characters`);
181
+ }
182
+ // Sign payload
183
+ const signResult = options.nonce !== undefined
184
+ ? await identity.signMessageWithNonce(room, String(options.nonce), sweptText)
185
+ : await identity.signMessage(room, sweptText);
186
+ const method = this.#resolveSignedSendMethod(options.method, room, identity.did, signResult.sig, signResult.nonce, signResult.sweptText);
187
+ if (method === 'POST') {
188
+ const res = await this.#transport.request(`/r/${room}`, {
189
+ method: 'POST',
190
+ body: JSON.stringify({
191
+ did: identity.did,
192
+ sig: signResult.sig,
193
+ nonce: signResult.nonce,
194
+ text: signResult.sweptText,
195
+ }),
196
+ contentType: 'application/json',
197
+ params: { format: 'json' },
198
+ });
199
+ return this.#parseRoomResult(res.body, `/r/${room}`);
200
+ }
201
+ else {
202
+ const encodedText = encodeURIComponent(signResult.sweptText);
203
+ const res = await this.#transport.request(`/r/${room}/say-signed/${identity.did}/${signResult.sig}/${signResult.nonce}/${encodedText}`, { params: { format: 'json' } });
204
+ return this.#parseRoomResult(res.body, `/r/${room}`);
205
+ }
206
+ }
207
+ /**
208
+ * Export the room's stored ring file as raw JSONL snapshots (byte-exact as written).
209
+ *
210
+ * @param room - Target room name
211
+ */
212
+ async export(room) {
213
+ this.#validateRoomName(room);
214
+ const res = await this.#transport.request(`/r/${room}/export`);
215
+ const generationHeader = res.headers.get('x-room-generation');
216
+ const generation = generationHeader ? parseInt(generationHeader, 10) : 0;
217
+ const rawJsonl = res.body;
218
+ const messages = [];
219
+ if (rawJsonl.trim()) {
220
+ const lines = rawJsonl.split('\n');
221
+ for (const line of lines) {
222
+ const trimmed = line.trim();
223
+ if (trimmed) {
224
+ try {
225
+ messages.push(JSON.parse(trimmed));
226
+ }
227
+ catch {
228
+ // Skip unparseable lines in snapshot
229
+ }
230
+ }
231
+ }
232
+ }
233
+ return {
234
+ room,
235
+ generation: isNaN(generation) ? 0 : generation,
236
+ rawJsonl,
237
+ messages,
238
+ };
239
+ }
240
+ /**
241
+ * Discover newly created public rooms by reading the server-written /r/events log.
242
+ *
243
+ * @param options - Read options (since, limit, wait)
244
+ */
245
+ async discover(options = {}) {
246
+ return this.read('events', options);
247
+ }
248
+ /**
249
+ * Stream live messages from a room using long-polling with automatic cursor advancement.
250
+ *
251
+ * @example
252
+ * ```typescript
253
+ * for await (const { message } of client.rooms.stream('lobby', { wait: 5 })) {
254
+ * console.log(message.text);
255
+ * }
256
+ * ```
257
+ */
258
+ stream(room, options = {}) {
259
+ return streamRoom(this, room, options);
260
+ }
261
+ // ─── Private Helpers ────────────────────────────────────────────────────────
262
+ #validateRoomName(room) {
263
+ if (!NAME_PATTERN.test(room)) {
264
+ throw new TechnocoreValidationError('room', `Invalid room name '${room}'. Must match ^[a-z0-9][a-z0-9_-]{0,47}$`, room);
265
+ }
266
+ }
267
+ #validateNick(nick) {
268
+ if (!NAME_PATTERN.test(nick)) {
269
+ throw new TechnocoreValidationError('nick', `Invalid nickname '${nick}'. Must match ^[a-z0-9][a-z0-9_-]{0,47}$`, nick);
270
+ }
271
+ }
272
+ #resolveSendMethod(userMethod, room, nick, text) {
273
+ if (userMethod === 'POST')
274
+ return 'POST';
275
+ if (userMethod === 'GET')
276
+ return 'GET';
277
+ // 'auto' mode: check URL length budget
278
+ const testUrl = `/r/${room}/say/${encodeURIComponent(nick)}/${encodeURIComponent(text)}`;
279
+ if (testUrl.length > URL_BUDGET_THRESHOLD_BYTES) {
280
+ return 'POST';
281
+ }
282
+ return 'GET';
283
+ }
284
+ #resolveSignedSendMethod(userMethod, room, did, sig, nonce, text) {
285
+ if (userMethod === 'POST')
286
+ return 'POST';
287
+ if (userMethod === 'GET')
288
+ return 'GET';
289
+ // 'auto' mode: check URL length budget
290
+ const testUrl = `/r/${room}/say-signed/${did}/${sig}/${nonce}/${encodeURIComponent(text)}`;
291
+ if (testUrl.length > URL_BUDGET_THRESHOLD_BYTES) {
292
+ return 'POST';
293
+ }
294
+ return 'GET';
295
+ }
296
+ #parseRoomResult(body, endpoint) {
297
+ try {
298
+ const json = JSON.parse(body);
299
+ return {
300
+ ...json,
301
+ gapDetected: false,
302
+ };
303
+ }
304
+ catch (err) {
305
+ throw new TechnocoreProtocolError(`Failed to parse room response as JSON: ${err instanceof Error ? err.message : String(err)}`, endpoint, body.slice(0, 200));
306
+ }
307
+ }
308
+ #parseTextRoomList(text) {
309
+ const rooms = [];
310
+ const lines = text.split('\n');
311
+ for (const line of lines) {
312
+ const match = line.match(/^([a-z0-9][a-z0-9_-]{0,47})\s+(.*)$/);
313
+ if (match?.[1]) {
314
+ rooms.push({
315
+ room: match[1],
316
+ name: match[1],
317
+ topic: match[2]?.trim() || null,
318
+ note_count: 0,
319
+ });
320
+ }
321
+ }
322
+ return rooms;
323
+ }
324
+ }
325
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/rooms/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAEnD,OAAO,EACL,YAAY,EACZ,iBAAiB,EACjB,SAAS,EACT,gBAAgB,GACjB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,oBAAoB,CAAC;AAiE5B,qFAAqF;AACrF,MAAM,0BAA0B,GAAG,IAAI,CAAC;AAExC,MAAM,OAAO,WAAW;IACb,UAAU,CAAgB;IAEnC,YAAY,SAAwB;QAClC,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;IAC9B,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,IAAI;QACR,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE;YAClD,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;SAC3B,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAClC,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;gBACjC,CAAC,CAAC,IAAI;gBACN,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC;oBACjC,CAAC,CAAC,IAAI,CAAC,KAAK;oBACZ,CAAC,CAAC,IAAI,CAAC;YAEX,IAAI,OAAO,EAAE,CAAC;gBACZ,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,IAA6B,EAAE,EAAE;oBACnD,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;oBAC5D,OAAO;wBACL,IAAI,EAAE,QAAQ;wBACd,IAAI,EAAE,QAAQ;wBACd,KAAK,EAAG,IAAI,CAAC,OAAO,CAAY,IAAI,IAAI;wBACxC,QAAQ,EAAE,OAAO,IAAI,CAAC,UAAU,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS;wBAC7E,KAAK,EAAE,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;wBACpE,YAAY,EAAE,OAAO,IAAI,CAAC,cAAc,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,SAAS;wBACzF,UAAU,EAAE,OAAO,IAAI,CAAC,YAAY,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,SAAS;qBACpF,CAAC;gBACJ,CAAC,CAAC,CAAC;YACL,CAAC;YACD,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3C,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,IAAI,CAAC,IAAY,EAAE,UAA2B,EAAE;QACpD,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAE7B,MAAM,MAAM,GAAgD;YAC1D,MAAM,EAAE,MAAM;SACf,CAAC;QAEF,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QAC1F,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;QACnG,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;YAAE,MAAM,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACvG,IAAI,OAAO,CAAC,CAAC,KAAK,SAAS;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;QAErD,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI;YAC5B,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,yCAAyC;YACrE,CAAC,CAAC,SAAS,CAAC;QAEd,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE;YACtD,MAAM;YACN,SAAS;SACV,CAAC,CAAC;QAEH,IAAI,IAAsB,CAAC;QAC3B,IAAI,CAAC;YACH,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,uBAAuB,CAC/B,0CAA0C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAC5F,MAAM,IAAI,EAAE,EACZ,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CACvB,CAAC;QACJ,CAAC;QAED,MAAM,WAAW,GACf,OAAO,CAAC,KAAK,KAAK,SAAS;YAC3B,IAAI,CAAC,SAAS,KAAK,IAAI;YACvB,IAAI,CAAC,SAAS,KAAK,SAAS;YAC5B,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,KAAK,GAAG,CAAC,CAAC;QAErC,OAAO;YACL,GAAG,IAAI;YACP,WAAW;SACZ,CAAC;IACJ,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,WAAW,CAAC,IAAY,EAAE,UAA2B,EAAE;QAC3D,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAE7B,MAAM,MAAM,GAAgD,EAAE,CAAC;QAC/D,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC;QACjE,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS;YAAE,MAAM,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC;QACjE,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS;YAAE,MAAM,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;QAC9D,IAAI,OAAO,CAAC,CAAC,KAAK,SAAS;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;QAErD,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE;YACtD,MAAM;YACN,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,SAAS;SAChE,CAAC,CAAC;QAEH,OAAO,GAAG,CAAC,IAAI,CAAC;IAClB,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,IAAI,CACR,IAAY,EACZ,IAAY,EACZ,IAAY,EACZ,UAAuB,EAAE;QAEzB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;QAEzB,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,yBAAyB,CACjC,MAAM,EACN,sDAAsD,CACvD,CAAC;QACJ,CAAC;QACD,IAAI,SAAS,CAAC,MAAM,GAAG,iBAAiB,EAAE,CAAC;YACzC,MAAM,IAAI,yBAAyB,CACjC,MAAM,EACN,qCAAqC,iBAAiB,aAAa,CACpE,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QAE9E,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE;gBACtD,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC;gBACrD,WAAW,EAAE,kBAAkB;gBAC/B,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;aAC3B,CAAC,CAAC;YACH,OAAO,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;aAAM,CAAC;YACN,MAAM,WAAW,GAAG,kBAAkB,CAAC,SAAS,CAAC,CAAC;YAClD,MAAM,WAAW,GAAG,kBAAkB,CAAC,IAAI,CAAC,CAAC;YAC7C,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CACvC,MAAM,IAAI,QAAQ,WAAW,IAAI,WAAW,EAAE,EAC9C,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAC/B,CAAC;YACF,OAAO,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,UAAU,CACd,IAAY,EACZ,QAAkB,EAClB,IAAY,EACZ,UAA6B,EAAE;QAE/B,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAC7B,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;QAE1B,MAAM,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;QAC9B,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,yBAAyB,CACjC,MAAM,EACN,sDAAsD,CACvD,CAAC;QACJ,CAAC;QACD,IAAI,SAAS,CAAC,MAAM,GAAG,iBAAiB,EAAE,CAAC;YACzC,MAAM,IAAI,yBAAyB,CACjC,MAAM,EACN,qCAAqC,iBAAiB,aAAa,CACpE,CAAC;QACJ,CAAC;QAED,eAAe;QACf,MAAM,UAAU,GACd,OAAO,CAAC,KAAK,KAAK,SAAS;YACzB,CAAC,CAAC,MAAM,QAAQ,CAAC,oBAAoB,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;YAC7E,CAAC,CAAC,MAAM,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QAElD,MAAM,MAAM,GAAG,IAAI,CAAC,wBAAwB,CAC1C,OAAO,CAAC,MAAM,EACd,IAAI,EACJ,QAAQ,CAAC,GAAG,EACZ,UAAU,CAAC,GAAG,EACd,UAAU,CAAC,KAAK,EAChB,UAAU,CAAC,SAAS,CACrB,CAAC;QAEF,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;YACtB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE;gBACtD,MAAM,EAAE,MAAM;gBACd,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;oBACnB,GAAG,EAAE,QAAQ,CAAC,GAAG;oBACjB,GAAG,EAAE,UAAU,CAAC,GAAG;oBACnB,KAAK,EAAE,UAAU,CAAC,KAAK;oBACvB,IAAI,EAAE,UAAU,CAAC,SAAS;iBAC3B,CAAC;gBACF,WAAW,EAAE,kBAAkB;gBAC/B,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;aAC3B,CAAC,CAAC;YACH,OAAO,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;aAAM,CAAC;YACN,MAAM,WAAW,GAAG,kBAAkB,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;YAC7D,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CACvC,MAAM,IAAI,eAAe,QAAQ,CAAC,GAAG,IAAI,UAAU,CAAC,GAAG,IAAI,UAAU,CAAC,KAAK,IAAI,WAAW,EAAE,EAC5F,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAC/B,CAAC;YACF,OAAO,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,MAAM,CAAC,IAAY;QACvB,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;QAE7B,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,IAAI,SAAS,CAAC,CAAC;QAC/D,MAAM,gBAAgB,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;QAC9D,MAAM,UAAU,GAAG,gBAAgB,CAAC,CAAC,CAAC,QAAQ,CAAC,gBAAgB,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAEzE,MAAM,QAAQ,GAAG,GAAG,CAAC,IAAI,CAAC;QAC1B,MAAM,QAAQ,GAAwB,EAAE,CAAC;QAEzC,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YACnC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;gBACzB,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;gBAC5B,IAAI,OAAO,EAAE,CAAC;oBACZ,IAAI,CAAC;wBACH,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAsB,CAAC,CAAC;oBAC1D,CAAC;oBAAC,MAAM,CAAC;wBACP,qCAAqC;oBACvC,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO;YACL,IAAI;YACJ,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,UAAU;YAC9C,QAAQ;YACR,QAAQ;SACT,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,QAAQ,CAAC,UAA2B,EAAE;QAC1C,OAAO,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IACtC,CAAC;IAED;;;;;;;;;OASG;IACH,MAAM,CAAC,IAAY,EAAE,UAAyB,EAAE;QAC9C,OAAO,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,+EAA+E;IAE/E,iBAAiB,CAAC,IAAY;QAC5B,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,yBAAyB,CACjC,MAAM,EACN,sBAAsB,IAAI,0CAA0C,EACpE,IAAI,CACL,CAAC;QACJ,CAAC;IACH,CAAC;IAED,aAAa,CAAC,IAAY;QACxB,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,yBAAyB,CACjC,MAAM,EACN,qBAAqB,IAAI,0CAA0C,EACnE,IAAI,CACL,CAAC;QACJ,CAAC;IACH,CAAC;IAED,kBAAkB,CAChB,UAA+C,EAC/C,IAAY,EACZ,IAAY,EACZ,IAAY;QAEZ,IAAI,UAAU,KAAK,MAAM;YAAE,OAAO,MAAM,CAAC;QACzC,IAAI,UAAU,KAAK,KAAK;YAAE,OAAO,KAAK,CAAC;QAEvC,uCAAuC;QACvC,MAAM,OAAO,GAAG,MAAM,IAAI,QAAQ,kBAAkB,CAAC,IAAI,CAAC,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;QACzF,IAAI,OAAO,CAAC,MAAM,GAAG,0BAA0B,EAAE,CAAC;YAChD,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,wBAAwB,CACtB,UAA+C,EAC/C,IAAY,EACZ,GAAW,EACX,GAAW,EACX,KAAa,EACb,IAAY;QAEZ,IAAI,UAAU,KAAK,MAAM;YAAE,OAAO,MAAM,CAAC;QACzC,IAAI,UAAU,KAAK,KAAK;YAAE,OAAO,KAAK,CAAC;QAEvC,uCAAuC;QACvC,MAAM,OAAO,GAAG,MAAM,IAAI,eAAe,GAAG,IAAI,GAAG,IAAI,KAAK,IAAI,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC;QAC3F,IAAI,OAAO,CAAC,MAAM,GAAG,0BAA0B,EAAE,CAAC;YAChD,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,gBAAgB,CAAC,IAAY,EAAE,QAAgB;QAC7C,IAAI,CAAC;YACH,MAAM,IAAI,GAAqB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAChD,OAAO;gBACL,GAAG,IAAI;gBACP,WAAW,EAAE,KAAK;aACnB,CAAC;QACJ,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,uBAAuB,CAC/B,0CAA0C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAC5F,QAAQ,EACR,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CACnB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,kBAAkB,CAAC,IAAY;QAC7B,MAAM,KAAK,GAAe,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;YAChE,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACf,KAAK,CAAC,IAAI,CAAC;oBACT,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;oBACd,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;oBACd,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,IAAI;oBAC/B,UAAU,EAAE,CAAC;iBACd,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;CACF"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Technocore SDK — Streaming & Reliability
3
+ *
4
+ * Implements a resilient AsyncIterable long-polling stream over room messages.
5
+ *
6
+ * Features:
7
+ * - Protocol-native long-polling (?since=<seq>&wait=10)
8
+ * - Automatic cursor advancement
9
+ * - Sequence gap detection & notifications
10
+ * - Exponential backoff with jitter on network / 5xx / 429 rate limit errors
11
+ * - Respects server 429 retry-after headers/body
12
+ * - Clean termination via AbortSignal
13
+ * - Configurable error threshold to prevent infinite broken loops
14
+ */
15
+ import { RoomsClient } from '../rooms/index.js';
16
+ import type { TechnocoreMessage } from '../types/index.js';
17
+ export interface StreamOptions {
18
+ /**
19
+ * Sequence cursor to start reading from.
20
+ * If omitted, the stream begins by fetching the latest messages in the room.
21
+ */
22
+ since?: number;
23
+ /**
24
+ * Long-poll hold time in seconds per request (0..10, default: 10).
25
+ */
26
+ wait?: number;
27
+ /**
28
+ * Maximum consecutive errors before stream aborts (default: 10).
29
+ */
30
+ maxConsecutiveErrors?: number;
31
+ /**
32
+ * Initial backoff delay in ms on failure (default: 1000).
33
+ */
34
+ backoffInitialMs?: number;
35
+ /**
36
+ * Maximum backoff delay in ms (default: 30000).
37
+ */
38
+ backoffMaxMs?: number;
39
+ /**
40
+ * AbortSignal for external stream cancellation.
41
+ */
42
+ signal?: AbortSignal;
43
+ /**
44
+ * Callback fired when messages were dropped by the room ring retention boundary.
45
+ */
46
+ onGap?: (gap: {
47
+ lastSeenSeq: number;
48
+ firstReceivedSeq: number;
49
+ missedEstimate: number;
50
+ }) => void;
51
+ /**
52
+ * Callback fired on non-fatal retryable errors.
53
+ */
54
+ onError?: (error: Error, consecutiveFailures: number, retryDelayMs: number) => void;
55
+ }
56
+ export interface StreamItem {
57
+ readonly message: TechnocoreMessage;
58
+ readonly room: string;
59
+ readonly gapDetected: boolean;
60
+ }
61
+ /**
62
+ * Creates an AsyncIterable stream that yields messages as they arrive in a room.
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * for await (const item of streamRoom(roomsClient, 'lobby', { since: 1000 })) {
67
+ * console.log(`[${item.message.seq}] ${item.message.from}: ${item.message.text}`);
68
+ * }
69
+ * ```
70
+ */
71
+ export declare function streamRoom(roomsClient: RoomsClient, room: string, options?: StreamOptions): AsyncGenerator<StreamItem, void, unknown>;
72
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/streaming/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAE3D,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;OAEG;IACH,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE;QAAE,WAAW,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACjG;;OAEG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,mBAAmB,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACrF;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED;;;;;;;;;GASG;AACH,wBAAuB,UAAU,CAC/B,WAAW,EAAE,WAAW,EACxB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,aAAkB,GAC1B,cAAc,CAAC,UAAU,EAAE,IAAI,EAAE,OAAO,CAAC,CAkG3C"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Technocore SDK — Streaming & Reliability
3
+ *
4
+ * Implements a resilient AsyncIterable long-polling stream over room messages.
5
+ *
6
+ * Features:
7
+ * - Protocol-native long-polling (?since=<seq>&wait=10)
8
+ * - Automatic cursor advancement
9
+ * - Sequence gap detection & notifications
10
+ * - Exponential backoff with jitter on network / 5xx / 429 rate limit errors
11
+ * - Respects server 429 retry-after headers/body
12
+ * - Clean termination via AbortSignal
13
+ * - Configurable error threshold to prevent infinite broken loops
14
+ */
15
+ import { TechnocoreRateLimitError } from '../errors/index.js';
16
+ import { MAX_WAIT_SECONDS } from '../types/index.js';
17
+ /**
18
+ * Creates an AsyncIterable stream that yields messages as they arrive in a room.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * for await (const item of streamRoom(roomsClient, 'lobby', { since: 1000 })) {
23
+ * console.log(`[${item.message.seq}] ${item.message.from}: ${item.message.text}`);
24
+ * }
25
+ * ```
26
+ */
27
+ export async function* streamRoom(roomsClient, room, options = {}) {
28
+ const waitSec = Math.min(MAX_WAIT_SECONDS, Math.max(0, options.wait ?? MAX_WAIT_SECONDS));
29
+ const maxErrors = options.maxConsecutiveErrors ?? 10;
30
+ const initialBackoff = options.backoffInitialMs ?? 1000;
31
+ const maxBackoff = options.backoffMaxMs ?? 30_000;
32
+ const signal = options.signal;
33
+ let currentSince = options.since;
34
+ let consecutiveErrors = 0;
35
+ let currentBackoff = initialBackoff;
36
+ // If no initial since cursor is given, do a quick read to establish the baseline cursor
37
+ if (currentSince === undefined) {
38
+ if (signal?.aborted)
39
+ return;
40
+ try {
41
+ const initial = await roomsClient.read(room, { limit: 1 });
42
+ currentSince = initial.last_seq;
43
+ }
44
+ catch (err) {
45
+ if (signal?.aborted)
46
+ return;
47
+ // If room doesn't exist yet, start from cursor 0
48
+ currentSince = 0;
49
+ }
50
+ }
51
+ while (!signal?.aborted) {
52
+ try {
53
+ const response = await roomsClient.read(room, {
54
+ since: currentSince,
55
+ wait: waitSec,
56
+ });
57
+ // Reset error state on successful request
58
+ consecutiveErrors = 0;
59
+ currentBackoff = initialBackoff;
60
+ if (response.gapDetected && response.first_seq !== null && currentSince !== undefined) {
61
+ const gap = {
62
+ lastSeenSeq: currentSince,
63
+ firstReceivedSeq: response.first_seq,
64
+ missedEstimate: Math.max(0, response.first_seq - (currentSince + 1)),
65
+ };
66
+ options.onGap?.(gap);
67
+ }
68
+ // Yield all new messages
69
+ for (const message of response.messages) {
70
+ if (signal?.aborted)
71
+ return;
72
+ yield {
73
+ message,
74
+ room,
75
+ gapDetected: response.gapDetected,
76
+ };
77
+ // Advance cursor
78
+ if (message.seq > (currentSince ?? 0)) {
79
+ currentSince = message.seq;
80
+ }
81
+ }
82
+ // Update cursor even if no messages were returned
83
+ if (response.last_seq > (currentSince ?? 0)) {
84
+ currentSince = response.last_seq;
85
+ }
86
+ // If server returned wait_held: false (no long-poll slots), pause slightly before retrying
87
+ if (response.wait_held === false && response.messages.length === 0) {
88
+ await sleep(Math.min(waitSec * 1000, 3000), signal);
89
+ }
90
+ }
91
+ catch (error) {
92
+ if (signal?.aborted)
93
+ return;
94
+ consecutiveErrors++;
95
+ const err = error instanceof Error ? error : new Error(String(error));
96
+ if (consecutiveErrors >= maxErrors) {
97
+ throw new Error(`Technocore streaming aborted after ${consecutiveErrors} consecutive errors on room '${room}': ${err.message}`, { cause: err });
98
+ }
99
+ // Calculate retry delay with jitter
100
+ let retryDelayMs = currentBackoff + Math.floor(Math.random() * 500);
101
+ // If rate-limited, respect server's Retry-After delay
102
+ if (error instanceof TechnocoreRateLimitError) {
103
+ retryDelayMs = Math.max(retryDelayMs, error.retryAfterSeconds * 1000);
104
+ }
105
+ options.onError?.(err, consecutiveErrors, retryDelayMs);
106
+ await sleep(retryDelayMs, signal);
107
+ // Increase exponential backoff
108
+ currentBackoff = Math.min(currentBackoff * 1.5, maxBackoff);
109
+ }
110
+ }
111
+ }
112
+ function sleep(ms, signal) {
113
+ return new Promise((resolve) => {
114
+ if (signal?.aborted) {
115
+ resolve();
116
+ return;
117
+ }
118
+ const timeoutId = setTimeout(resolve, ms);
119
+ signal?.addEventListener('abort', () => {
120
+ clearTimeout(timeoutId);
121
+ resolve();
122
+ }, { once: true });
123
+ });
124
+ }
125
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/streaming/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Technocore SDK — HTTP Transport
3
+ *
4
+ * Thin wrapper around fetch with:
5
+ * - configurable base URL
6
+ * - timeout / AbortSignal support
7
+ * - structured error mapping (429 → TechnocoreRateLimitError, etc.)
8
+ * - never retries writes automatically
9
+ */
10
+ export interface TransportConfig {
11
+ /** Base URL for all requests. */
12
+ baseUrl?: string;
13
+ /** Custom fetch implementation. */
14
+ fetch?: typeof globalThis.fetch;
15
+ /** Default timeout in milliseconds. */
16
+ timeoutMs?: number;
17
+ }
18
+ export interface RequestOptions {
19
+ /** HTTP method. Default: GET. */
20
+ method?: 'GET' | 'POST';
21
+ /** Request body (for POST). */
22
+ body?: string;
23
+ /** Content-Type header (for POST). */
24
+ contentType?: string;
25
+ /** Request timeout in ms (overrides default). */
26
+ timeoutMs?: number;
27
+ /** AbortSignal for external cancellation. */
28
+ signal?: AbortSignal;
29
+ /** Query parameters. */
30
+ params?: Record<string, string | number | undefined>;
31
+ }
32
+ export interface TransportResponse {
33
+ readonly status: number;
34
+ readonly body: string;
35
+ readonly contentType: string;
36
+ readonly headers: Headers;
37
+ }
38
+ /**
39
+ * HTTP transport layer for Technocore protocol.
40
+ */
41
+ export declare class HttpTransport {
42
+ #private;
43
+ readonly baseUrl: string;
44
+ constructor(config?: TransportConfig);
45
+ /**
46
+ * Make an HTTP request to the Technocore server.
47
+ * Throws typed errors for protocol-specific failure modes.
48
+ */
49
+ request(path: string, options?: RequestOptions): Promise<TransportResponse>;
50
+ }
51
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/transport/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH,MAAM,WAAW,eAAe;IAC9B,iCAAiC;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mCAAmC;IACnC,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,uCAAuC;IACvC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,iCAAiC;IACjC,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iDAAiD;IACjD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,wBAAwB;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC,CAAC;CACtD;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;GAEG;AACH,qBAAa,aAAa;;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBAIb,MAAM,GAAE,eAAoB;IAMxC;;;OAGG;IACG,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,iBAAiB,CAAC;CA4HtF"}