opinionated-machine 10.5.0 → 11.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +154 -0
- package/README.md +513 -52
- package/dist/lib/DIContext.d.ts +13 -1
- package/dist/lib/DIContext.js +28 -8
- package/dist/lib/DIContext.js.map +1 -1
- package/dist/lib/api-contracts/apiRouteBuilder.d.ts +47 -5
- package/dist/lib/api-contracts/apiRouteBuilder.js +57 -8
- package/dist/lib/api-contracts/apiRouteBuilder.js.map +1 -1
- package/dist/lib/api-contracts/apiSseConnectionRegistry.d.ts +182 -0
- package/dist/lib/api-contracts/apiSseConnectionRegistry.js +332 -0
- package/dist/lib/api-contracts/apiSseConnectionRegistry.js.map +1 -0
- package/dist/lib/api-contracts/index.d.ts +1 -0
- package/dist/lib/api-contracts/index.js +1 -0
- package/dist/lib/api-contracts/index.js.map +1 -1
- package/dist/lib/gateway/gatewayMetadata.d.ts +1 -0
- package/dist/lib/gateway/gatewayMetadata.js +11 -0
- package/dist/lib/gateway/gatewayMetadata.js.map +1 -1
- package/dist/lib/gateway/index.d.ts +1 -0
- package/dist/lib/gateway/index.js +1 -0
- package/dist/lib/gateway/index.js.map +1 -1
- package/dist/lib/gateway/manifest/buildManifest.d.ts +18 -0
- package/dist/lib/gateway/manifest/buildManifest.js +31 -0
- package/dist/lib/gateway/manifest/buildManifest.js.map +1 -1
- package/dist/lib/gateway/manifest/manifestSchema.d.ts +18 -0
- package/dist/lib/gateway/manifest/manifestSchema.js +27 -0
- package/dist/lib/gateway/manifest/manifestSchema.js.map +1 -1
- package/dist/lib/gateway/routeStreaming.d.ts +79 -0
- package/dist/lib/gateway/routeStreaming.js +70 -0
- package/dist/lib/gateway/routeStreaming.js.map +1 -0
- package/dist/lib/resolverFunctions.d.ts +7 -0
- package/dist/lib/resolverFunctions.js +13 -0
- package/dist/lib/resolverFunctions.js.map +1 -1
- package/dist/lib/routes/fastifyRouteBuilder.js +9 -2
- package/dist/lib/routes/fastifyRouteBuilder.js.map +1 -1
- package/dist/lib/sse/AbstractSSEController.d.ts +10 -0
- package/dist/lib/sse/AbstractSSEController.js +9 -0
- package/dist/lib/sse/AbstractSSEController.js.map +1 -1
- package/dist/lib/sse/eventIds.d.ts +138 -0
- package/dist/lib/sse/eventIds.js +155 -0
- package/dist/lib/sse/eventIds.js.map +1 -0
- package/dist/lib/sse/index.d.ts +3 -2
- package/dist/lib/sse/index.js +6 -2
- package/dist/lib/sse/index.js.map +1 -1
- package/dist/lib/sse/rooms/SSERoomEventPublisher.d.ts +121 -0
- package/dist/lib/sse/rooms/SSERoomEventPublisher.js +119 -0
- package/dist/lib/sse/rooms/SSERoomEventPublisher.js.map +1 -0
- package/dist/lib/sse/rooms/index.d.ts +1 -0
- package/dist/lib/sse/rooms/index.js +1 -0
- package/dist/lib/sse/rooms/index.js.map +1 -1
- package/dist/lib/testing/apiSseEventValidation.d.ts +1 -1
- package/dist/lib/testing/apiSseInjectHelpers.js +9 -10
- package/dist/lib/testing/apiSseInjectHelpers.js.map +1 -1
- package/dist/lib/testing/sseHttpClient.d.ts +10 -2
- package/dist/lib/testing/sseHttpClient.js +13 -6
- package/dist/lib/testing/sseHttpClient.js.map +1 -1
- package/dist/lib/testing/sseInjectClient.d.ts +1 -1
- package/dist/lib/testing/sseInjectClient.js +1 -1
- package/dist/lib/testing/sseInjectClient.js.map +1 -1
- package/dist/lib/testing/sseTestTypes.d.ts +1 -1
- package/package.json +13 -13
- package/dist/lib/sse/sseParser.d.ts +0 -167
- package/dist/lib/sse/sseParser.js +0 -225
- package/dist/lib/sse/sseParser.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { createSSEStreamParser } from '@opinionated-machine/sse-parser';
|
|
1
2
|
import { stringify } from 'fast-querystring';
|
|
2
|
-
import { parseSSEBuffer } from "../sse/sseParser.js";
|
|
3
3
|
import { resolveApiSseSchemas, validateApiSseEvent } from "./apiSseEventValidation.js";
|
|
4
4
|
/**
|
|
5
5
|
* Whether `fetch()` can send this value as-is. Everything else is JSON-stringified,
|
|
@@ -123,11 +123,19 @@ export class SSEHttpClient {
|
|
|
123
123
|
* subsequent body read with an `AbortError`.
|
|
124
124
|
*/
|
|
125
125
|
response;
|
|
126
|
+
/**
|
|
127
|
+
* Optional hook invoked with every decoded raw text chunk as it arrives —
|
|
128
|
+
* including comment frames (e.g. `: heartbeat`) that the SSE parser drops.
|
|
129
|
+
* Useful for asserting heartbeat delivery or tracking byte-level liveness.
|
|
130
|
+
* Only fires while the stream is being consumed via events()/collectEvents().
|
|
131
|
+
*/
|
|
132
|
+
onRawChunk;
|
|
126
133
|
abortController;
|
|
127
134
|
responseBody;
|
|
128
135
|
streamReader;
|
|
129
136
|
decoder = new TextDecoder();
|
|
130
|
-
buffer
|
|
137
|
+
/** Owns the partial-frame buffer and the Last-Event-ID cursor across chunks. */
|
|
138
|
+
parser = createSSEStreamParser();
|
|
131
139
|
closed = false;
|
|
132
140
|
constructor(response, abortController) {
|
|
133
141
|
this.response = response;
|
|
@@ -257,10 +265,9 @@ export class SSEHttpClient {
|
|
|
257
265
|
this.closed = true;
|
|
258
266
|
break;
|
|
259
267
|
}
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
this.
|
|
263
|
-
for (const event of parseResult.events) {
|
|
268
|
+
const chunk = this.decoder.decode(readResult.value, { stream: true });
|
|
269
|
+
this.onRawChunk?.(chunk);
|
|
270
|
+
for (const event of this.parser.push(chunk)) {
|
|
264
271
|
if (signal?.aborted) {
|
|
265
272
|
return;
|
|
266
273
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sseHttpClient.js","sourceRoot":"","sources":["../../../lib/testing/sseHttpClient.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"sseHttpClient.js","sourceRoot":"","sources":["../../../lib/testing/sseHttpClient.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAuB,MAAM,iCAAiC,CAAA;AAC5F,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAG5C,OAAO,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAsGtF;;;GAGG;AACH,SAAS,WAAW,CAAC,IAAa;IAChC,OAAO,CACL,OAAO,IAAI,KAAK,QAAQ;QACxB,IAAI,YAAY,eAAe;QAC/B,IAAI,YAAY,QAAQ;QACxB,IAAI,YAAY,IAAI;QACpB,IAAI,YAAY,WAAW;QAC3B,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC;QACxB,IAAI,YAAY,cAAc,CAC/B,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,SAAS,uBAAuB,CAAC,IAAe;IAC9C,OAAO,IAAI,YAAY,eAAe,IAAI,IAAI,YAAY,QAAQ,IAAI,IAAI,YAAY,IAAI,CAAA;AAC5F,CAAC;AAED,SAAS,oBAAoB,CAAC,OAA+B;IAC3D,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,cAAc,CAAC,CAAA;AACvF,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CACvB,IAAa,EACb,MAA+B,EAC/B,OAA+B;IAE/B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO,SAAS,CAAA;IAClB,CAAC;IAED,IAAI,MAAM,KAAK,KAAK,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CACb,wGAAwG,CACzG,CAAA;IACH,CAAC;IAED,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,IAAI,CAAC,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC;YACrE,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAA;QAC9C,CAAC;QACD,OAAO,IAAI,CAAA;IACb,CAAC;IAED,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;IAC3C,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CACb,2CAA2C,OAAO,IAAI,8GAA8G,CACrK,CAAA;IACH,CAAC;IACD,IAAI,CAAC,oBAAoB,CAAC,OAAO,CAAC,EAAE,CAAC;QACnC,OAAO,CAAC,cAAc,CAAC,GAAG,kBAAkB,CAAA;IAC9C,CAAC;IACD,OAAO,cAAc,CAAA;AACvB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,MAAM,OAAO,aAAa;IACxB;;;;;;;;;;;OAWG;IACM,QAAQ,CAAU;IAC3B;;;;;OAKG;IACH,UAAU,CAA0B;IACnB,eAAe,CAAiB;IAChC,YAAY,CAAmC;IACxD,YAAY,CAAqD;IACxD,OAAO,GAAG,IAAI,WAAW,EAAE,CAAA;IAC5C,gFAAgF;IAC/D,MAAM,GAAG,qBAAqB,EAAE,CAAA;IACzC,MAAM,GAAG,KAAK,CAAA;IAEtB,YAAoB,QAAkB,EAAE,eAAgC;QACtE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,eAAe,GAAG,eAAe,CAAA;QACtC,IAAI,CAAC,YAAY,GAAG,QAAQ,CAAC,IAAI,CAAA;IACnC,CAAC;IAED;;;OAGG;IACH,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,MAAM,CAAA;IACpB,CAAC;IAED;;;;;OAKG;IACH,IAAY,MAAM;QAChB,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;gBACvB,MAAM,IAAI,KAAK,CACb,8CAA8C,IAAI,CAAC,QAAQ,CAAC,MAAM,6DAA6D,CAChI,CAAA;YACH,CAAC;YACD,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,SAAS,EAAE,CAAA;QACnD,CAAC;QACD,OAAO,IAAI,CAAC,YAAY,CAAA;IAC1B,CAAC;IAoED,MAAM,CAAC,KAAK,CAAC,OAAO,CAClB,OAAe,EACf,IAAY,EACZ,OAGwD;QAExD,+BAA+B;QAC/B,IAAI,aAAa,GAAG,IAAI,CAAA;QACxB,IAAI,OAAO,EAAE,KAAK,EAAE,CAAC;YACnB,MAAM,WAAW,GAAG,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;YAC5C,IAAI,WAAW,EAAE,CAAC;gBAChB,aAAa,GAAG,GAAG,IAAI,IAAI,WAAW,EAAE,CAAA;YAC1C,CAAC;QACH,CAAC;QAED,yFAAyF;QACzF,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,CAAC,CAAC,WAAW,EAA6B,CAAA;QAClF,MAAM,OAAO,GAA2B;YACtC,MAAM,EAAE,mBAAmB;YAC3B,GAAG,OAAO,EAAE,OAAO;SACpB,CAAA;QAED,MAAM,IAAI,GAAG,gBAAgB,CAAC,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,CAAA;QAE7D,sDAAsD;QACtD,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAA;QAC7C,MAAM,WAAW,GAAgB;YAC/B,MAAM;YACN,OAAO;YACP,IAAI;YACJ,MAAM,EAAE,eAAe,CAAC,MAAM;SAC/B,CAAA;QACD,IAAI,IAAI,YAAY,cAAc,EAAE,CAAC;YACnC,uFAAuF;YACvF,CAAC;YAAC,WAAiD,CAAC,MAAM,GAAG,MAAM,CAAA;QACrE,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,OAAO,GAAG,aAAa,EAAE,EAAE,WAAW,CAAC,CAAA;QAEvE,MAAM,MAAM,GAAG,IAAI,aAAa,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAA;QAE3D,2EAA2E;QAC3E,IAAI,OAAO,IAAI,uBAAuB,IAAI,OAAO,IAAI,OAAO,CAAC,qBAAqB,EAAE,CAAC;YACnF,MAAM,YAAY,GAAG,OAAO,CAAC,qBAAqB,CAAA;YAClD,MAAM,WAAW,GAAG;gBAClB,OAAO,EAAE,YAAY,CAAC,OAAO,IAAI,IAAI;gBACrC,SAAS,EAAE,CAAC,IAAqB,EAAE,EAAE,CACnC,IAAI,CAAC,OAAO,CAAC,GAAG,KAAK,aAAa,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,MAAM;aACrF,CAAA;YACD,IAAI,CAAC;gBACH,0EAA0E;gBAC1E,wDAAwD;gBACxD,MAAM,gBAAgB,GACpB,KAAK,IAAI,YAAY;oBACnB,CAAC,CAAC,MAAM,YAAY,CAAC,GAAG,CAAC,iBAAiB,CAAC,WAAW,CAAC;oBACvD,CAAC,CAAC,MAAM,YAAY,CAAC,UAAU,CAAC,aAAa,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAA;gBAChF,OAAO,EAAE,MAAM,EAAE,gBAAgB,EAAE,CAAA;YACrC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,yEAAyE;gBACzE,0EAA0E;gBAC1E,yEAAyE;gBACzE,gCAAgC;gBAChC,MAAM,CAAC,KAAK,EAAE,CAAA;gBACd,MAAM,KAAK,CAAA;YACb,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA8BG;IACH,KAAK,CAAC,CAAC,MAAM,CAAC,MAAoB;QAChC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YACpB,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;gBACpB,OAAM;YACR,CAAC;YAED,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAA;YACnD,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;gBAC7B,OAAM;YACR,CAAC;YAED,IAAI,UAAU,CAAC,IAAI,EAAE,CAAC;gBACpB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;gBAClB,MAAK;YACP,CAAC;YAED,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAA;YACrE,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAA;YAExB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC5C,IAAI,MAAM,EAAE,OAAO,EAAE,CAAC;oBACpB,OAAM;gBACR,CAAC;gBACD,MAAM,KAAK,CAAA;YACb,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,aAAa,CAAC,MAAoB;QAC9C,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;QAEtC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,WAAW,CAAA;QACpB,CAAC;QAED,IAAI,WAAW,GAAG,KAAK,CAAA;QAEvB,MAAM,YAAY,GAAG,IAAI,OAAO,CAAY,CAAC,OAAO,EAAE,EAAE;YACtD,MAAM,OAAO,GAAG,GAAG,EAAE;gBACnB,IAAI,CAAC,WAAW,EAAE,CAAC;oBACjB,OAAO,CAAC,SAAS,CAAC,CAAA;gBACpB,CAAC;YACH,CAAC,CAAA;YACD,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;gBACnB,OAAO,EAAE,CAAA;YACX,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAA;YAC3D,CAAC;QACH,CAAC,CAAC,CAAA;QAEF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC,CAAA;QAC9D,WAAW,GAAG,IAAI,CAAA;QAElB,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,qDAAqD;YACrD,WAAW,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAA;QAC7B,CAAC;QAED,OAAO,MAAM,CAAA;IACf,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,KAAK,CAAC,aAAa,CACjB,gBAA+D,EAC/D,OAAO,GAAG,IAAI;QAEd,MAAM,SAAS,GAAqB,EAAE,CAAA;QACtC,MAAM,OAAO,GAAG,OAAO,gBAAgB,KAAK,QAAQ,CAAA;QACpD,MAAM,eAAe,GAAG,IAAI,eAAe,EAAE,CAAA;QAC7C,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,CAAC,eAAe,CAAC,MAAM,CAAC,CAAA;QACpD,IAAI,QAAQ,GAAG,KAAK,CAAA;QAEpB,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,EAAE;YAChC,QAAQ,GAAG,IAAI,CAAA;YACf,eAAe,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,kCAAkC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAA;QACzF,CAAC,EAAE,OAAO,CAAC,CAAA;QAEX,IAAI,CAAC;YACH,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,QAAQ,EAAE,CAAC;gBACnC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;gBAErB,IAAI,OAAO,IAAI,SAAS,CAAC,MAAM,IAAI,gBAAgB,EAAE,CAAC;oBACpD,MAAK;gBACP,CAAC;gBACD,IAAI,CAAC,OAAO,IAAI,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;oBACxC,MAAK;gBACP,CAAC;YACH,CAAC;YAED,2EAA2E;YAC3E,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,eAAe,CAAC,MAAM,CAAC,MAAM,CAAA;YACrC,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,iDAAiD;YACjD,IAAI,QAAQ,IAAI,eAAe,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC/C,MAAM,eAAe,CAAC,MAAM,CAAC,MAAM,CAAA;YACrC,CAAC;YACD,MAAM,GAAG,CAAA;QACX,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,SAAS,CAAC,CAAA;YACvB,eAAe,CAAC,KAAK,EAAE,CAAA,CAAC,0CAA0C;QACpE,CAAC;QAED,OAAO,SAAS,CAAA;IAClB,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,KAAK,CAAC,CAAC,SAAS,CACd,QAAkB,EAClB,MAAoB;QAEpB,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAA;QACvE,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9C,MAAM,mBAAmB,CAAW,iBAAiB,EAAE,KAAK,EAAE,aAAa,CAAC,CAAA;QAC9E,CAAC;IACH,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,KAAK,CAAC,gBAAgB,CACpB,QAAkB,EAClB,gBAAsE,EACtE,OAAgB;QAEhB,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAA;QAC9E,yFAAyF;QACzF,uFAAuF;QACvF,4DAA4D;QAC5D,MAAM,SAAS,GAAG,IAAI,GAAG,EAAyC,CAAA;QAClE,MAAM,QAAQ,GAAG,CAAC,KAAqB,EAAyB,EAAE;YAChE,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;YACnC,IAAI,MAAM,EAAE,CAAC;gBACX,OAAO,MAAM,CAAA;YACf,CAAC;YACD,MAAM,MAAM,GAAG,mBAAmB,CAAW,iBAAiB,EAAE,KAAK,EAAE,oBAAoB,CAAC,CAAA;YAC5F,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;YAC5B,OAAO,MAAM,CAAA;QACf,CAAC,CAAA;QAED,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,aAAa,CACxC,OAAO,gBAAgB,KAAK,QAAQ;YAClC,CAAC,CAAC,gBAAgB;YAClB,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAChD,OAAO,CACR,CAAA;QACD,OAAO,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;IAChC,CAAC;IAED;;;;;;;;;OASG;IACH,KAAK;QACH,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA;QAClB,8EAA8E;QAC9E,2EAA2E;QAC3E,8DAA8D;QAC9D,IAAI,CAAC,YAAY,EAAE,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;YACrC,6CAA6C;QAC/C,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,CAAA;IAC9B,CAAC;CACF"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ParsedSSEEvent } from '
|
|
1
|
+
import { type ParsedSSEEvent } from '@opinionated-machine/sse-parser';
|
|
2
2
|
import type { AnyFastifyInstance } from './AnyFastifyInstance.ts';
|
|
3
3
|
import type { SSEConnectOptions, SSETestConnection } from './sseTestTypes.ts';
|
|
4
4
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sseInjectClient.js","sourceRoot":"","sources":["../../../lib/testing/sseInjectClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,cAAc,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"sseInjectClient.js","sourceRoot":"","sources":["../../../lib/testing/sseInjectClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AnySSEContractDefinition, HttpStatusCode } from '@lokalise/api-contracts';
|
|
2
|
+
import type { ParsedSSEEvent } from '@opinionated-machine/sse-parser';
|
|
2
3
|
import type { InjectOptions } from 'fastify';
|
|
3
4
|
import type { z } from 'zod';
|
|
4
|
-
import type { ParsedSSEEvent } from '../sse/sseParser.ts';
|
|
5
5
|
/**
|
|
6
6
|
* HTTP methods Fastify's `inject()` accepts, in either case.
|
|
7
7
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opinionated-machine",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "11.2.0",
|
|
4
4
|
"description": "Very opinionated DI framework for fastify, built on top of awilix ",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,13 +18,15 @@
|
|
|
18
18
|
],
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|
|
21
|
-
"url": "git+https://github.com/kibertoad/opinionated-machine.git"
|
|
21
|
+
"url": "git+https://github.com/kibertoad/opinionated-machine.git",
|
|
22
|
+
"directory": "packages/opinionated-machine"
|
|
22
23
|
},
|
|
23
24
|
"dependencies": {
|
|
24
25
|
"@fastify/sse": "^0.6.0",
|
|
25
26
|
"fast-querystring": "^1.1.2",
|
|
26
27
|
"fastify-plugin": "^6.0.0",
|
|
27
|
-
"ts-deepmerge": "^8.0.0"
|
|
28
|
+
"ts-deepmerge": "^8.0.0",
|
|
29
|
+
"@opinionated-machine/sse-parser": "0.1.0"
|
|
28
30
|
},
|
|
29
31
|
"peerDependencies": {
|
|
30
32
|
"@lokalise/api-contracts": ">=8.0.0",
|
|
@@ -38,22 +40,22 @@
|
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
40
42
|
"@biomejs/biome": "2.4.4",
|
|
41
|
-
"@changesets/cli": "^3.0.0",
|
|
42
43
|
"@lokalise/api-contracts": "^8.0.0",
|
|
43
44
|
"@lokalise/biome-config": "^3.1.1",
|
|
44
45
|
"@lokalise/fastify-api-contracts": "^7.0.0",
|
|
45
46
|
"@lokalise/node-core": "^14.7.4",
|
|
46
47
|
"@lokalise/tsconfig": "^3.1.0",
|
|
47
48
|
"@types/node": "^22.19.7",
|
|
48
|
-
"@vitest/coverage-v8": "^4.
|
|
49
|
+
"@vitest/coverage-v8": "^4.1.11",
|
|
49
50
|
"awilix": "^13.0.0",
|
|
50
|
-
"awilix-manager": "^7.0.
|
|
51
|
-
"fastify": "^5.
|
|
51
|
+
"awilix-manager": "^7.0.1",
|
|
52
|
+
"fastify": "^5.12.4",
|
|
52
53
|
"fastify-type-provider-zod": "^7.0.0",
|
|
53
54
|
"rimraf": "^6.1.2",
|
|
54
55
|
"typescript": "^5.9.3",
|
|
55
|
-
"vitest": "^4.
|
|
56
|
-
"zod": "^4.
|
|
56
|
+
"vitest": "^4.1.11",
|
|
57
|
+
"zod": "^4.6.2",
|
|
58
|
+
"@opinionated-machine/sse-fallback": "0.1.1"
|
|
57
59
|
},
|
|
58
60
|
"private": false,
|
|
59
61
|
"publishConfig": {
|
|
@@ -67,7 +69,7 @@
|
|
|
67
69
|
"di",
|
|
68
70
|
"fastify"
|
|
69
71
|
],
|
|
70
|
-
"homepage": "https://github.com/kibertoad/opinionated-machine",
|
|
72
|
+
"homepage": "https://github.com/kibertoad/opinionated-machine/tree/main/packages/opinionated-machine",
|
|
71
73
|
"files": [
|
|
72
74
|
"README.md",
|
|
73
75
|
"CHANGELOG.md",
|
|
@@ -79,8 +81,6 @@
|
|
|
79
81
|
"lint": "biome check . && tsc",
|
|
80
82
|
"lint:fix": "biome check --write .",
|
|
81
83
|
"test": "vitest --typecheck",
|
|
82
|
-
"test:ci": "pnpm run test --coverage"
|
|
83
|
-
"changeset": "changeset",
|
|
84
|
-
"ci:publish": "changeset publish"
|
|
84
|
+
"test:ci": "pnpm run test --coverage"
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SSE (Server-Sent Events) parsing utilities.
|
|
3
|
-
*
|
|
4
|
-
* This module provides utilities for parsing SSE event streams according
|
|
5
|
-
* to the W3C Server-Sent Events specification.
|
|
6
|
-
*
|
|
7
|
-
* @see https://html.spec.whatwg.org/multipage/server-sent-events.html
|
|
8
|
-
*
|
|
9
|
-
* @module sseParser
|
|
10
|
-
*/
|
|
11
|
-
export type ParsedSSEEvent = {
|
|
12
|
-
/**
|
|
13
|
-
* Event ID for client reconnection via Last-Event-ID header.
|
|
14
|
-
* When the client reconnects, it can send this ID to resume from where it left off.
|
|
15
|
-
*/
|
|
16
|
-
id?: string;
|
|
17
|
-
/**
|
|
18
|
-
* Event type name that maps to EventSource event listeners.
|
|
19
|
-
* Defaults to 'message' when not specified.
|
|
20
|
-
*/
|
|
21
|
-
event?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Event data payload as a string.
|
|
24
|
-
* For multi-line data, lines are joined with newlines.
|
|
25
|
-
* Typically contains JSON that should be parsed by the consumer.
|
|
26
|
-
*/
|
|
27
|
-
data: string;
|
|
28
|
-
/**
|
|
29
|
-
* Reconnection delay hint in milliseconds.
|
|
30
|
-
* Suggests how long the client should wait before reconnecting.
|
|
31
|
-
*/
|
|
32
|
-
retry?: number;
|
|
33
|
-
};
|
|
34
|
-
/**
|
|
35
|
-
* Parse SSE events from a complete text response.
|
|
36
|
-
*
|
|
37
|
-
* This function parses a complete SSE response body into individual events.
|
|
38
|
-
* SSE events are separated by blank lines, and each event can have multiple fields.
|
|
39
|
-
*
|
|
40
|
-
* **SSE Format:**
|
|
41
|
-
* ```
|
|
42
|
-
* id: event-id
|
|
43
|
-
* event: event-name
|
|
44
|
-
* data: line1
|
|
45
|
-
* data: line2
|
|
46
|
-
* retry: 3000
|
|
47
|
-
*
|
|
48
|
-
* ```
|
|
49
|
-
*
|
|
50
|
-
* **Field Rules:**
|
|
51
|
-
* - `id:` - Event ID for Last-Event-ID reconnection
|
|
52
|
-
* - `event:` - Event type (defaults to 'message')
|
|
53
|
-
* - `data:` - Event payload (multiple data lines are joined with newlines)
|
|
54
|
-
* - `retry:` - Reconnection delay in milliseconds
|
|
55
|
-
* - Lines starting with `:` are comments and ignored
|
|
56
|
-
*
|
|
57
|
-
* @param text - Raw SSE text to parse
|
|
58
|
-
* @returns Array of parsed events
|
|
59
|
-
*
|
|
60
|
-
* @example
|
|
61
|
-
* ```typescript
|
|
62
|
-
* // Parse a simple SSE response
|
|
63
|
-
* const text = `event: message
|
|
64
|
-
* data: {"text":"hello"}
|
|
65
|
-
*
|
|
66
|
-
* event: done
|
|
67
|
-
* data: {"status":"complete"}
|
|
68
|
-
*
|
|
69
|
-
* `
|
|
70
|
-
* const events = parseSSEEvents(text)
|
|
71
|
-
* // events = [
|
|
72
|
-
* // { event: 'message', data: '{"text":"hello"}' },
|
|
73
|
-
* // { event: 'done', data: '{"status":"complete"}' }
|
|
74
|
-
* // ]
|
|
75
|
-
* ```
|
|
76
|
-
*
|
|
77
|
-
* @example
|
|
78
|
-
* ```typescript
|
|
79
|
-
* // Parse events with IDs (for reconnection)
|
|
80
|
-
* const text = `id: 1
|
|
81
|
-
* event: update
|
|
82
|
-
* data: {"value":42}
|
|
83
|
-
*
|
|
84
|
-
* id: 2
|
|
85
|
-
* event: update
|
|
86
|
-
* data: {"value":43}
|
|
87
|
-
*
|
|
88
|
-
* `
|
|
89
|
-
* const events = parseSSEEvents(text)
|
|
90
|
-
* // Store last ID for reconnection: events[events.length - 1].id
|
|
91
|
-
* ```
|
|
92
|
-
*
|
|
93
|
-
* @example
|
|
94
|
-
* ```typescript
|
|
95
|
-
* // Multi-line data
|
|
96
|
-
* const text = `event: log
|
|
97
|
-
* data: Line 1
|
|
98
|
-
* data: Line 2
|
|
99
|
-
* data: Line 3
|
|
100
|
-
*
|
|
101
|
-
* `
|
|
102
|
-
* const events = parseSSEEvents(text)
|
|
103
|
-
* // events[0].data === "Line 1\nLine 2\nLine 3"
|
|
104
|
-
* ```
|
|
105
|
-
*/
|
|
106
|
-
export declare function parseSSEEvents(text: string): ParsedSSEEvent[];
|
|
107
|
-
/**
|
|
108
|
-
* Result of incremental SSE buffer parsing.
|
|
109
|
-
*/
|
|
110
|
-
export type ParseSSEBufferResult = {
|
|
111
|
-
/** Complete events parsed from the buffer */
|
|
112
|
-
events: ParsedSSEEvent[];
|
|
113
|
-
/** Remaining incomplete data to prepend to next chunk */
|
|
114
|
-
remaining: string;
|
|
115
|
-
};
|
|
116
|
-
/**
|
|
117
|
-
* Parse SSE events incrementally from a buffer.
|
|
118
|
-
*
|
|
119
|
-
* This function is designed for streaming scenarios where data arrives
|
|
120
|
-
* in chunks. It parses complete events and returns any incomplete data
|
|
121
|
-
* that should be prepended to the next chunk.
|
|
122
|
-
*
|
|
123
|
-
* **Usage Pattern:**
|
|
124
|
-
* 1. Append new chunk to buffer
|
|
125
|
-
* 2. Call parseSSEBuffer(buffer)
|
|
126
|
-
* 3. Process the events
|
|
127
|
-
* 4. Set buffer = remaining for next iteration
|
|
128
|
-
*
|
|
129
|
-
* @param buffer - Current buffer containing SSE data
|
|
130
|
-
* @returns Object with parsed events and remaining incomplete buffer
|
|
131
|
-
*
|
|
132
|
-
* @example
|
|
133
|
-
* ```typescript
|
|
134
|
-
* // Streaming SSE parsing with fetch
|
|
135
|
-
* const response = await fetch(url)
|
|
136
|
-
* const reader = response.body.getReader()
|
|
137
|
-
* const decoder = new TextDecoder()
|
|
138
|
-
* let buffer = ''
|
|
139
|
-
*
|
|
140
|
-
* while (true) {
|
|
141
|
-
* const { done, value } = await reader.read()
|
|
142
|
-
* if (done) break
|
|
143
|
-
*
|
|
144
|
-
* buffer += decoder.decode(value, { stream: true })
|
|
145
|
-
* const { events, remaining } = parseSSEBuffer(buffer)
|
|
146
|
-
* buffer = remaining
|
|
147
|
-
*
|
|
148
|
-
* for (const event of events) {
|
|
149
|
-
* console.log('Received:', event.event, JSON.parse(event.data))
|
|
150
|
-
* }
|
|
151
|
-
* }
|
|
152
|
-
* ```
|
|
153
|
-
*
|
|
154
|
-
* @example
|
|
155
|
-
* ```typescript
|
|
156
|
-
* // Node.js readable stream
|
|
157
|
-
* let buffer = ''
|
|
158
|
-
* stream.on('data', (chunk: Buffer) => {
|
|
159
|
-
* buffer += chunk.toString()
|
|
160
|
-
* const { events, remaining } = parseSSEBuffer(buffer)
|
|
161
|
-
* buffer = remaining
|
|
162
|
-
*
|
|
163
|
-
* events.forEach(event => emit('sse-event', event))
|
|
164
|
-
* })
|
|
165
|
-
* ```
|
|
166
|
-
*/
|
|
167
|
-
export declare function parseSSEBuffer(buffer: string): ParseSSEBufferResult;
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* SSE (Server-Sent Events) parsing utilities.
|
|
3
|
-
*
|
|
4
|
-
* This module provides utilities for parsing SSE event streams according
|
|
5
|
-
* to the W3C Server-Sent Events specification.
|
|
6
|
-
*
|
|
7
|
-
* @see https://html.spec.whatwg.org/multipage/server-sent-events.html
|
|
8
|
-
*
|
|
9
|
-
* @module sseParser
|
|
10
|
-
*/
|
|
11
|
-
/**
|
|
12
|
-
* A parsed SSE event.
|
|
13
|
-
*
|
|
14
|
-
* SSE events consist of optional id, event type, data, and retry fields.
|
|
15
|
-
* The data field is always present and contains the event payload as a string.
|
|
16
|
-
*
|
|
17
|
-
* @example
|
|
18
|
-
* ```typescript
|
|
19
|
-
* const event: ParsedSSEEvent = {
|
|
20
|
-
* id: 'msg-123',
|
|
21
|
-
* event: 'message',
|
|
22
|
-
* data: '{"text":"Hello, world!"}',
|
|
23
|
-
* retry: 3000,
|
|
24
|
-
* }
|
|
25
|
-
*
|
|
26
|
-
* // Parse the JSON data
|
|
27
|
-
* const payload = JSON.parse(event.data)
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
/**
|
|
31
|
-
* Parse a single SSE line and update the event state.
|
|
32
|
-
* Returns true if a complete event was found (empty line with data).
|
|
33
|
-
*/
|
|
34
|
-
function parseSSELine(line, currentEvent, dataLines) {
|
|
35
|
-
if (line.startsWith('id:')) {
|
|
36
|
-
currentEvent.id = line.slice(3).trim();
|
|
37
|
-
}
|
|
38
|
-
else if (line.startsWith('event:')) {
|
|
39
|
-
currentEvent.event = line.slice(6).trim();
|
|
40
|
-
}
|
|
41
|
-
else if (line.startsWith('data:')) {
|
|
42
|
-
dataLines.push(line.slice(5).trim());
|
|
43
|
-
}
|
|
44
|
-
else if (line.startsWith('retry:')) {
|
|
45
|
-
currentEvent.retry = Number.parseInt(line.slice(6).trim(), 10);
|
|
46
|
-
}
|
|
47
|
-
else if (line === '' && dataLines.length > 0) {
|
|
48
|
-
return true; // Event complete
|
|
49
|
-
}
|
|
50
|
-
// Comment lines (starting with :) are implicitly ignored
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Parse SSE events from a complete text response.
|
|
55
|
-
*
|
|
56
|
-
* This function parses a complete SSE response body into individual events.
|
|
57
|
-
* SSE events are separated by blank lines, and each event can have multiple fields.
|
|
58
|
-
*
|
|
59
|
-
* **SSE Format:**
|
|
60
|
-
* ```
|
|
61
|
-
* id: event-id
|
|
62
|
-
* event: event-name
|
|
63
|
-
* data: line1
|
|
64
|
-
* data: line2
|
|
65
|
-
* retry: 3000
|
|
66
|
-
*
|
|
67
|
-
* ```
|
|
68
|
-
*
|
|
69
|
-
* **Field Rules:**
|
|
70
|
-
* - `id:` - Event ID for Last-Event-ID reconnection
|
|
71
|
-
* - `event:` - Event type (defaults to 'message')
|
|
72
|
-
* - `data:` - Event payload (multiple data lines are joined with newlines)
|
|
73
|
-
* - `retry:` - Reconnection delay in milliseconds
|
|
74
|
-
* - Lines starting with `:` are comments and ignored
|
|
75
|
-
*
|
|
76
|
-
* @param text - Raw SSE text to parse
|
|
77
|
-
* @returns Array of parsed events
|
|
78
|
-
*
|
|
79
|
-
* @example
|
|
80
|
-
* ```typescript
|
|
81
|
-
* // Parse a simple SSE response
|
|
82
|
-
* const text = `event: message
|
|
83
|
-
* data: {"text":"hello"}
|
|
84
|
-
*
|
|
85
|
-
* event: done
|
|
86
|
-
* data: {"status":"complete"}
|
|
87
|
-
*
|
|
88
|
-
* `
|
|
89
|
-
* const events = parseSSEEvents(text)
|
|
90
|
-
* // events = [
|
|
91
|
-
* // { event: 'message', data: '{"text":"hello"}' },
|
|
92
|
-
* // { event: 'done', data: '{"status":"complete"}' }
|
|
93
|
-
* // ]
|
|
94
|
-
* ```
|
|
95
|
-
*
|
|
96
|
-
* @example
|
|
97
|
-
* ```typescript
|
|
98
|
-
* // Parse events with IDs (for reconnection)
|
|
99
|
-
* const text = `id: 1
|
|
100
|
-
* event: update
|
|
101
|
-
* data: {"value":42}
|
|
102
|
-
*
|
|
103
|
-
* id: 2
|
|
104
|
-
* event: update
|
|
105
|
-
* data: {"value":43}
|
|
106
|
-
*
|
|
107
|
-
* `
|
|
108
|
-
* const events = parseSSEEvents(text)
|
|
109
|
-
* // Store last ID for reconnection: events[events.length - 1].id
|
|
110
|
-
* ```
|
|
111
|
-
*
|
|
112
|
-
* @example
|
|
113
|
-
* ```typescript
|
|
114
|
-
* // Multi-line data
|
|
115
|
-
* const text = `event: log
|
|
116
|
-
* data: Line 1
|
|
117
|
-
* data: Line 2
|
|
118
|
-
* data: Line 3
|
|
119
|
-
*
|
|
120
|
-
* `
|
|
121
|
-
* const events = parseSSEEvents(text)
|
|
122
|
-
* // events[0].data === "Line 1\nLine 2\nLine 3"
|
|
123
|
-
* ```
|
|
124
|
-
*/
|
|
125
|
-
export function parseSSEEvents(text) {
|
|
126
|
-
const events = [];
|
|
127
|
-
const lines = text.split('\n');
|
|
128
|
-
let currentEvent = {};
|
|
129
|
-
let dataLines = [];
|
|
130
|
-
for (const line of lines) {
|
|
131
|
-
if (parseSSELine(line, currentEvent, dataLines)) {
|
|
132
|
-
events.push({
|
|
133
|
-
...currentEvent,
|
|
134
|
-
data: dataLines.join('\n'),
|
|
135
|
-
});
|
|
136
|
-
currentEvent = {};
|
|
137
|
-
dataLines = [];
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
// Handle case where stream doesn't end with double newline
|
|
141
|
-
if (dataLines.length > 0) {
|
|
142
|
-
events.push({
|
|
143
|
-
...currentEvent,
|
|
144
|
-
data: dataLines.join('\n'),
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
return events;
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Parse SSE events incrementally from a buffer.
|
|
151
|
-
*
|
|
152
|
-
* This function is designed for streaming scenarios where data arrives
|
|
153
|
-
* in chunks. It parses complete events and returns any incomplete data
|
|
154
|
-
* that should be prepended to the next chunk.
|
|
155
|
-
*
|
|
156
|
-
* **Usage Pattern:**
|
|
157
|
-
* 1. Append new chunk to buffer
|
|
158
|
-
* 2. Call parseSSEBuffer(buffer)
|
|
159
|
-
* 3. Process the events
|
|
160
|
-
* 4. Set buffer = remaining for next iteration
|
|
161
|
-
*
|
|
162
|
-
* @param buffer - Current buffer containing SSE data
|
|
163
|
-
* @returns Object with parsed events and remaining incomplete buffer
|
|
164
|
-
*
|
|
165
|
-
* @example
|
|
166
|
-
* ```typescript
|
|
167
|
-
* // Streaming SSE parsing with fetch
|
|
168
|
-
* const response = await fetch(url)
|
|
169
|
-
* const reader = response.body.getReader()
|
|
170
|
-
* const decoder = new TextDecoder()
|
|
171
|
-
* let buffer = ''
|
|
172
|
-
*
|
|
173
|
-
* while (true) {
|
|
174
|
-
* const { done, value } = await reader.read()
|
|
175
|
-
* if (done) break
|
|
176
|
-
*
|
|
177
|
-
* buffer += decoder.decode(value, { stream: true })
|
|
178
|
-
* const { events, remaining } = parseSSEBuffer(buffer)
|
|
179
|
-
* buffer = remaining
|
|
180
|
-
*
|
|
181
|
-
* for (const event of events) {
|
|
182
|
-
* console.log('Received:', event.event, JSON.parse(event.data))
|
|
183
|
-
* }
|
|
184
|
-
* }
|
|
185
|
-
* ```
|
|
186
|
-
*
|
|
187
|
-
* @example
|
|
188
|
-
* ```typescript
|
|
189
|
-
* // Node.js readable stream
|
|
190
|
-
* let buffer = ''
|
|
191
|
-
* stream.on('data', (chunk: Buffer) => {
|
|
192
|
-
* buffer += chunk.toString()
|
|
193
|
-
* const { events, remaining } = parseSSEBuffer(buffer)
|
|
194
|
-
* buffer = remaining
|
|
195
|
-
*
|
|
196
|
-
* events.forEach(event => emit('sse-event', event))
|
|
197
|
-
* })
|
|
198
|
-
* ```
|
|
199
|
-
*/
|
|
200
|
-
export function parseSSEBuffer(buffer) {
|
|
201
|
-
const events = [];
|
|
202
|
-
const lines = buffer.split('\n');
|
|
203
|
-
let currentEvent = {};
|
|
204
|
-
let dataLines = [];
|
|
205
|
-
let lastCompleteEventEnd = 0;
|
|
206
|
-
let currentPosition = 0;
|
|
207
|
-
for (const line of lines) {
|
|
208
|
-
currentPosition += line.length + 1; // +1 for the \n
|
|
209
|
-
if (parseSSELine(line, currentEvent, dataLines)) {
|
|
210
|
-
events.push({
|
|
211
|
-
...currentEvent,
|
|
212
|
-
data: dataLines.join('\n'),
|
|
213
|
-
});
|
|
214
|
-
currentEvent = {};
|
|
215
|
-
dataLines = [];
|
|
216
|
-
lastCompleteEventEnd = currentPosition;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
// Return remaining incomplete data
|
|
220
|
-
// Preserve any unconsumed content after the last complete event,
|
|
221
|
-
// including incomplete events with only id:/event:/retry: lines (not just data: lines)
|
|
222
|
-
const remaining = lastCompleteEventEnd < buffer.length ? buffer.slice(lastCompleteEventEnd) : '';
|
|
223
|
-
return { events, remaining };
|
|
224
|
-
}
|
|
225
|
-
//# sourceMappingURL=sseParser.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"sseParser.js","sourceRoot":"","sources":["../../../lib/sse/sseParser.ts"],"names":[],"mappings":"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"}
|