@cortexkit/subc-client 0.4.1 → 0.8.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/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +17 -5
- package/dist/auth.js.map +1 -1
- package/dist/client.d.ts +57 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +168 -14
- package/dist/client.js.map +1 -1
- package/dist/connection-file.d.ts.map +1 -1
- package/dist/connection-file.js +11 -1
- package/dist/connection-file.js.map +1 -1
- package/dist/envelope.d.ts.map +1 -1
- package/dist/envelope.js +36 -1
- package/dist/envelope.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/provider.d.ts +8 -25
- package/dist/provider.d.ts.map +1 -1
- package/dist/provider.js +123 -53
- package/dist/provider.js.map +1 -1
- package/dist/socket.d.ts +12 -0
- package/dist/socket.d.ts.map +1 -1
- package/dist/socket.js +33 -3
- package/dist/socket.js.map +1 -1
- package/package.json +1 -1
- package/src/auth.ts +21 -9
- package/src/client.ts +210 -20
- package/src/connection-file.ts +15 -1
- package/src/envelope.ts +54 -1
- package/src/index.ts +1 -5
- package/src/provider.ts +153 -86
- package/src/socket.ts +41 -3
package/dist/socket.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"socket.js","sourceRoot":"","sources":["../src/socket.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,gFAAgF;AAChF,6EAA6E;AAC7E,6EAA6E;AAC7E,uCAAuC;AAEvC,OAAO,GAAG,MAAM,UAAU,CAAC;AAE3B,OAAO,EACL,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,kBAAkB,EAClB,gBAAgB,GAEjB,MAAM,eAAe,CAAC;AAEvB,MAAM,OAAO,iBAAkB,SAAQ,KAAK;CAAG;AAC/C,MAAM,OAAO,kBAAmB,SAAQ,KAAK;CAAG;AAEhD,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAGvC;IAFX,YACE,OAAe,EACN,KAAa;QAEtB,KAAK,CAAC,OAAO,CAAC,CAAC;QAFN,UAAK,GAAL,KAAK,CAAQ;IAGxB,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAGpC;IAFX,YACE,OAAe,EACN,KAAa;QAEtB,KAAK,CAAC,OAAO,CAAC,CAAC;QAFN,UAAK,GAAL,KAAK,CAAQ;IAGxB,CAAC;CACF;
|
|
1
|
+
{"version":3,"file":"socket.js","sourceRoot":"","sources":["../src/socket.ts"],"names":[],"mappings":"AAAA,yEAAyE;AACzE,gFAAgF;AAChF,6EAA6E;AAC7E,6EAA6E;AAC7E,uCAAuC;AAEvC,OAAO,GAAG,MAAM,UAAU,CAAC;AAE3B,OAAO,EACL,YAAY,EACZ,WAAW,EACX,iBAAiB,EACjB,UAAU,EACV,kBAAkB,EAClB,gBAAgB,GAEjB,MAAM,eAAe,CAAC;AAEvB,MAAM,OAAO,iBAAkB,SAAQ,KAAK;CAAG;AAC/C,MAAM,OAAO,kBAAmB,SAAQ,KAAK;CAAG;AAEhD,MAAM,OAAO,yBAA0B,SAAQ,KAAK;IAGvC;IAFX,YACE,OAAe,EACN,KAAa;QAEtB,KAAK,CAAC,OAAO,CAAC,CAAC;QAFN,UAAK,GAAL,KAAK,CAAQ;IAGxB,CAAC;CACF;AAED,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAGpC;IAFX,YACE,OAAe,EACN,KAAa;QAEtB,KAAK,CAAC,OAAO,CAAC,CAAC;QAFN,UAAK,GAAL,KAAK,CAAQ;IAGxB,CAAC;CACF;AASD,MAAM,UAAU,aAAa,CAAC,KAAiB;IAC7C,yEAAyE;IACzE,8EAA8E;IAC9E,+EAA+E;IAC/E,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,cAAc,GAAG,MAAM,CAAC,2BAA2B,CAAC,CAAC;AAC3D,MAAM,sBAAsB,GAAG,MAAM,CAAC,kCAAkC,CAAC,CAAC;AAE1E,6EAA6E;AAC7E,MAAM,UAAU,aAAa,CAAC,MAAkB,EAAE,KAAiB,EAAE,UAAkB;IACrF,MAAM,QAAQ,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;IACxC,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AAC/F,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,oBAAoB,CAAC,MAAkB,EAAE,KAAiB,EAAE,UAAkB;IAC5F,MAAM,QAAQ,GAAG,MAAM,CAAC,sBAAsB,CAAC,CAAC;IAChD,OAAO,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,EAAE,UAAU,CAAC,CAAC;AACtG,CAAC;AASD,MAAM,OAAO,UAAU;IACJ,IAAI,CAAa;IAC1B,MAAM,GAAa,EAAE,CAAC;IACtB,QAAQ,GAAG,CAAC,CAAC;IACb,MAAM,GAAkB,IAAI,CAAC;IAC7B,SAAS,GAAiB,IAAI,CAAC;IAEvC;;gDAE4C;IAC5C,aAAa;QACX,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED,YAAoB,IAAgB;QAClC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE;YAChC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACxB,IAAI,CAAC,QAAQ,IAAI,KAAK,CAAC,MAAM,CAAC;YAC9B,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,CAAC,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,CAAC,GAAU,EAAE,EAAE;YAC1B,IAAI,CAAC,IAAI,CAAC,SAAS;gBAAE,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;YAC1C,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,CAAC,CAAC;QACF,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACrF,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,4BAA4B,CAAC,CAAC,CAAC,CAAC;QAChF,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC;IAChF,CAAC;IAED;;;;OAIG;IACH,SAAS;QACP,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC;IACrC,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,IAAY,EAAE,IAAY,EAAE,UAAkB;QAC3D,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACrC,MAAM,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YACzC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YACtB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBAC5B,IAAI,CAAC,OAAO,EAAE,CAAC;gBACf,MAAM,CAAC,IAAI,kBAAkB,CAAC,2BAA2B,IAAI,IAAI,IAAI,EAAE,CAAC,CAAC,CAAC;YAC5E,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;YACzC,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE;gBACxB,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,OAAO,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;YAChC,CAAC,CAAC,CAAC;YACH,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACzB,YAAY,CAAC,KAAK,CAAC,CAAC;gBACpB,MAAM,CAAC,GAAG,CAAC,CAAC;YACd,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,KAAK,CAAC,SAAS,CACb,gBAAwB,EACxB,YAAgD,EAChD,QAAqB;QAErB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;QACzE,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAE,CAAC;QAC3B,IAAI,OAAO,KAAK,gBAAgB;YAAE,MAAM,IAAI,WAAW,CAAC,gCAAgC,OAAO,EAAE,EAAE,qBAAqB,CAAC,CAAC;QAE1H,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,UAAU,GAAG,iBAAiB,EAAE,gBAAgB,CAAC,CAAC;QACzF,MAAM,WAAW,GAAG,IAAI,UAAU,CAAC,UAAU,CAAC,CAAC;QAC/C,WAAW,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACxB,WAAW,CAAC,GAAG,CAAC,SAAS,EAAE,iBAAiB,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;QACzC,IAAI,MAAM,CAAC,GAAG,GAAG,kBAAkB,EAAE,CAAC;YACpC,MAAM,IAAI,WAAW,CAAC,cAAc,MAAM,CAAC,GAAG,gBAAgB,kBAAkB,EAAE,EAAE,sBAAsB,CAAC,CAAC;QAC9G,CAAC;QACD,QAAQ,EAAE,EAAE,CAAC;QACb,MAAM,cAAc,GAClB,OAAO,YAAY,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,CAAC,aAAa,CAAC;QAC5F,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;QACrG,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IAC1B,CAAC;IAED,iFAAiF;IACjF,SAAS,CAAC,CAAS,EAAE,UAAkB;QACrC,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO,OAAO,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC,CAAC;QAC5E,CAAC;QACD,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;QACvD,OAAO,IAAI,OAAO,CAAa,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACjD,wEAAwE;YACxE,0EAA0E;YAC1E,IAAI,KAAK,GAAyC,IAAI,CAAC;YACvD,IAAI,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;gBAChC,MAAM,SAAS,GAAG,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC1C,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;oBACnB,MAAM,CAAC,IAAI,kBAAkB,CAAC,yBAAyB,CAAC,QAAQ,CAAC,CAAC,CAAC;oBACnE,OAAO;gBACT,CAAC;gBACD,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;oBACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;oBACnB,MAAM,CAAC,IAAI,kBAAkB,CAAC,yBAAyB,CAAC,QAAQ,CAAC,CAAC,CAAC;gBACrE,CAAC,EAAE,SAAS,CAAC,CAAC;YAChB,CAAC;YACD,IAAI,CAAC,MAAM,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;YAClD,IAAI,CAAC,QAAQ,EAAE,CAAC;QAClB,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,KAAK,CAAC,KAAiB,EAAE,UAAkB;QAC/C,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC;IACzD,CAAC;IAED,YAAY,CAAC,KAAiB,EAAE,UAAkB;QAChD,OAAO,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC;IACjE,CAAC;IAED,KAAK,CAAC,CAAC,cAAc,CAAC,CAAC,KAAiB,EAAE,UAAkB;QAC1D,MAAM,IAAI,CAAC,WAAW,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC;IAC3D,CAAC;IAED,CAAC,sBAAsB,CAAC,CAAC,KAAiB,EAAE,UAAkB;QAC5D,OAAO,IAAI,CAAC,kBAAkB,CAAC,aAAa,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC,CAAC;IACnE,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,MAAc,EAAE,UAAkB;QAC1D,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC,SAAS,CAAC;QAC9D,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,GAAG,YAAY,yBAAyB,IAAI,GAAG,YAAY,sBAAsB,EAAE,CAAC;gBACtF,MAAM,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC;YACzB,CAAC;YACD,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC;IAEO,kBAAkB,CAAC,MAAc,EAAE,UAAkB;QAC3D,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,OAAO;gBACL,MAAM,EAAE,KAAK;gBACb,SAAS,EAAE,OAAO,CAAC,MAAM,CACvB,IAAI,yBAAyB,CAAC,qDAAqD,EAAE,IAAI,CAAC,SAAS,CAAC,CACrG;aACF,CAAC;QACJ,CAAC;QAED,IAAI,MAAM,GAAG,KAAK,CAAC;QACnB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,IAAI,KAAK,GAAyC,IAAI,CAAC;QACvD,MAAM,SAAS,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;YACtD,MAAM,MAAM,GAAG,CAAC,GAAe,EAAQ,EAAE;gBACvC,IAAI,OAAO;oBAAE,OAAO;gBACpB,OAAO,GAAG,IAAI,CAAC;gBACf,IAAI,KAAK;oBAAE,YAAY,CAAC,KAAK,CAAC,CAAC;gBAC/B,GAAG,EAAE,CAAC;YACR,CAAC,CAAC;YAEF,MAAM,SAAS,GAAG,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC1C,IAAI,SAAS,IAAI,CAAC,EAAE,CAAC;gBACnB,MAAM,CAAC,GAAG,EAAE,CACV,MAAM,CACJ,IAAI,yBAAyB,CAC3B,gDAAgD,EAChD,IAAI,kBAAkB,CAAC,2BAA2B,CAAC,CACpD,CACF,CACF,CAAC;gBACF,OAAO;YACT,CAAC;YAED,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;gBACtB,MAAM,OAAO,GAAG,IAAI,kBAAkB,CAAC,2BAA2B,CAAC,CAAC;gBACpE,MAAM,CAAC,GAAG,EAAE,CACV,MAAM,CACJ,MAAM;oBACJ,CAAC,CAAC,IAAI,sBAAsB,CAAC,sDAAsD,EAAE,OAAO,CAAC;oBAC7F,CAAC,CAAC,IAAI,yBAAyB,CAAC,gDAAgD,EAAE,OAAO,CAAC,CAC7F,CACF,CAAC;YACJ,CAAC,EAAE,SAAS,CAAC,CAAC;YAEd,IAAI,CAAC;gBACH,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE;oBAC9B,MAAM,CAAC,GAAG,EAAE;wBACV,IAAI,GAAG,EAAE,CAAC;4BACR,MAAM,CACJ,IAAI,sBAAsB,CACxB,0EAA0E,EAC1E,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CACpD,CACF,CAAC;wBACJ,CAAC;6BAAM,CAAC;4BACN,OAAO,EAAE,CAAC;wBACZ,CAAC;oBACH,CAAC,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC;gBACH,MAAM,GAAG,IAAI,CAAC;YAChB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,GAAG,EAAE,CACV,MAAM,CACJ,IAAI,yBAAyB,CAC3B,sDAAsD,EACtD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CACpD,CACF,CACF,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;IAC/B,CAAC;IAED,KAAK;QACH,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;IACtB,CAAC;IAEO,QAAQ;QACd,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC;QACtB,IAAI,CAAC,CAAC;YAAE,OAAO;QACf,IAAI,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;YAC5B,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,CAAC,KAAK;gBAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;YACf,OAAO;QACT,CAAC;QACD,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;YACnB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,CAAC,KAAK;gBAAE,YAAY,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;YACnC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAEO,IAAI,CAAC,CAAS;QACpB,MAAM,GAAG,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,GAAG,GAAG,CAAC,CAAC;QACZ,OAAO,GAAG,GAAG,CAAC,EAAE,CAAC;YACf,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,CAAC,GAAG,GAAG,CAAC;YACrB,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE,CAAC;gBACxB,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;gBACpB,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC;gBACnB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACtB,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;gBAC7B,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACrC,GAAG,IAAI,IAAI,CAAC;YACd,CAAC;QACH,CAAC;QACD,IAAI,CAAC,QAAQ,IAAI,CAAC,CAAC;QACnB,OAAO,GAAG,CAAC;IACb,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/subc-client",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "TypeScript client for the subc daemon. Wire-compatible (byte-for-byte) with the Rust subc-transport handshake and subc-protocol envelope.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
package/src/auth.ts
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import { createHmac, randomBytes, timingSafeEqual } from "node:crypto";
|
|
17
17
|
|
|
18
18
|
import type { ConnectionInfo } from "./connection-file.js";
|
|
19
|
-
import { SubcSocket } from "./socket.js";
|
|
19
|
+
import { SubcSocket, writeBorrowed } from "./socket.js";
|
|
20
20
|
|
|
21
21
|
export const NONCE_LEN = 32;
|
|
22
22
|
export const PROOF_LEN = 32;
|
|
@@ -59,8 +59,8 @@ async function writeMessage(
|
|
|
59
59
|
}
|
|
60
60
|
const lenPrefix = new Uint8Array(4);
|
|
61
61
|
new DataView(lenPrefix.buffer).setUint32(0, json.length, true);
|
|
62
|
-
await sock
|
|
63
|
-
await sock
|
|
62
|
+
await writeBorrowed(sock, lenPrefix, deadlineMs);
|
|
63
|
+
await writeBorrowed(sock, json, deadlineMs);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
async function readMessage<T>(sock: SubcSocket, deadlineMs: number): Promise<T> {
|
|
@@ -78,10 +78,22 @@ async function readMessage<T>(sock: SubcSocket, deadlineMs: number): Promise<T>
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
interface ServerProofMessage {
|
|
81
|
-
daemon_id:
|
|
82
|
-
server_nonce:
|
|
81
|
+
daemon_id: unknown;
|
|
82
|
+
server_nonce: unknown;
|
|
83
83
|
daemon_ver: string;
|
|
84
|
-
server_proof:
|
|
84
|
+
server_proof: unknown;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function authBytes(value: unknown, field: string): Uint8Array {
|
|
88
|
+
if (!Array.isArray(value)) {
|
|
89
|
+
throw new AuthError(`auth field '${field}' must be a byte array`);
|
|
90
|
+
}
|
|
91
|
+
for (const byte of value) {
|
|
92
|
+
if (typeof byte !== "number" || !Number.isInteger(byte) || byte < 0 || byte > 255) {
|
|
93
|
+
throw new AuthError(`auth field '${field}' has invalid byte ${String(byte)}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return Uint8Array.from(value as number[]);
|
|
85
97
|
}
|
|
86
98
|
|
|
87
99
|
/**
|
|
@@ -103,9 +115,9 @@ export async function authenticateClient(
|
|
|
103
115
|
);
|
|
104
116
|
|
|
105
117
|
const proof = await readMessage<ServerProofMessage>(sock, deadlineMs);
|
|
106
|
-
const serverNonce =
|
|
107
|
-
const daemonId =
|
|
108
|
-
const serverProof =
|
|
118
|
+
const serverNonce = authBytes(proof.server_nonce, "server_nonce");
|
|
119
|
+
const daemonId = authBytes(proof.daemon_id, "daemon_id");
|
|
120
|
+
const serverProof = authBytes(proof.server_proof, "server_proof");
|
|
109
121
|
|
|
110
122
|
const expected = computeProof(conn.key, SERVER_PROOF_DOMAIN, clientNonce, serverNonce, daemonId);
|
|
111
123
|
if (!constantTimeEq(expected, serverProof)) {
|
package/src/client.ts
CHANGED
|
@@ -37,6 +37,8 @@ import {
|
|
|
37
37
|
SocketWriteNotQueuedError,
|
|
38
38
|
SocketWriteQueuedError,
|
|
39
39
|
SubcSocket,
|
|
40
|
+
writeBorrowed,
|
|
41
|
+
writeTrackedBorrowed,
|
|
40
42
|
} from "./socket.js";
|
|
41
43
|
|
|
42
44
|
const debug = debuglog("subc-client");
|
|
@@ -54,6 +56,7 @@ const DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
|
|
|
54
56
|
// NOT a deadline extension: an absent reply still settles right after the check
|
|
55
57
|
// phase. Capped so it can never approach BODY_READ_TIMEOUT_MS.
|
|
56
58
|
const TIMEOUT_ARBITRATION_GRACE_MS = 50;
|
|
59
|
+
const LIVENESS_PROBE_WINDOW_MS = 2000;
|
|
57
60
|
// Internal marker set as the `code` on the SubcError a request-deadline timeout
|
|
58
61
|
// rejects with, so the managed classifier can tell a deadline (reply may simply
|
|
59
62
|
// not have been read in time) from an actual connection drop. Never surfaced to
|
|
@@ -210,13 +213,14 @@ export class SubcCallError extends Error {
|
|
|
210
213
|
/**
|
|
211
214
|
* A live subscription to a provider's event stream, riding a single held-open
|
|
212
215
|
* request. `onEvent` fires for each StreamData frame; `closed` resolves when the
|
|
213
|
-
* provider ends the stream (StreamEnd)
|
|
214
|
-
* GOODBYE. `unsubscribe` cancels the held-open request
|
|
216
|
+
* provider ends the stream (StreamEnd) or the consumer unsubscribes, and rejects on
|
|
217
|
+
* an Error terminal or a route GOODBYE. `unsubscribe` cancels the held-open request
|
|
218
|
+
* so the provider unwinds.
|
|
215
219
|
*/
|
|
216
220
|
export interface Subscription {
|
|
217
221
|
/** Cancel the subscription: sends Cancel on the held-open request; idempotent. */
|
|
218
222
|
unsubscribe(): void;
|
|
219
|
-
/** Resolves on StreamEnd; rejects on an Error terminal or route close. */
|
|
223
|
+
/** Resolves on StreamEnd or local unsubscribe; rejects on an Error terminal or route close. */
|
|
220
224
|
readonly closed: Promise<void>;
|
|
221
225
|
}
|
|
222
226
|
|
|
@@ -257,6 +261,15 @@ export interface ConnectOptions {
|
|
|
257
261
|
reconnectBackoff?: ReconnectBackoff;
|
|
258
262
|
/** Injectable sleep for timer-free reconnect tests. */
|
|
259
263
|
sleep?: (ms: number) => Promise<void>;
|
|
264
|
+
/**
|
|
265
|
+
* Window the post-deadline liveness probe waits for ANY inbound frame after
|
|
266
|
+
* sending its channel-0 Ping before convicting the socket as half-open.
|
|
267
|
+
* Genuine event-loop starvation self-protects at any setting: a loop starved
|
|
268
|
+
* enough to delay the probe's check also delays the read loop, and when it
|
|
269
|
+
* wakes the buffered Pong dispatches first. Exposed for deterministic tests.
|
|
270
|
+
*/
|
|
271
|
+
livenessProbeWindowMs?: number;
|
|
272
|
+
|
|
260
273
|
/**
|
|
261
274
|
* Hard cap on the timeout-arbitration grace window (see
|
|
262
275
|
* TIMEOUT_ARBITRATION_GRACE_MS). A reply whose bytes are actively arriving when
|
|
@@ -265,6 +278,26 @@ export interface ConnectOptions {
|
|
|
265
278
|
* exposed mainly so tests can prove the arbitration deterministically.
|
|
266
279
|
*/
|
|
267
280
|
timeoutArbitrationGraceMs?: number;
|
|
281
|
+
/**
|
|
282
|
+
* Observer for daemon-originated channel-0 control pushes (`route.closing`,
|
|
283
|
+
* `route.closed`, and any op added later). Purely advisory: the daemon's
|
|
284
|
+
* load-bearing route-death signal remains the GOODBYE frame, and nothing in
|
|
285
|
+
* the client's own route lifecycle consumes these. The contract is the wire
|
|
286
|
+
* contract verbatim -- unrecognized ops still arrive here (callers ignore
|
|
287
|
+
* what they don't know, per the MUST-ignore clause), a push that fails to
|
|
288
|
+
* parse as JSON is dropped without surfacing, and an observer throw is
|
|
289
|
+
* swallowed like every other caller callback so it cannot fail the read loop
|
|
290
|
+
* or unrelated requests.
|
|
291
|
+
*/
|
|
292
|
+
onControlPush?: (push: ControlPush) => void;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** A parsed daemon-originated channel-0 control push. */
|
|
296
|
+
export interface ControlPush {
|
|
297
|
+
/** The push discriminator, e.g. "route.closing" | "route.closed". */
|
|
298
|
+
op: string;
|
|
299
|
+
/** The full parsed body, `op` included, for op-specific fields. */
|
|
300
|
+
body: Record<string, unknown>;
|
|
268
301
|
}
|
|
269
302
|
|
|
270
303
|
interface NormalizedConnectOptions {
|
|
@@ -275,6 +308,8 @@ interface NormalizedConnectOptions {
|
|
|
275
308
|
reconnectBackoff: ReconnectBackoff;
|
|
276
309
|
sleep: (ms: number) => Promise<void>;
|
|
277
310
|
timeoutArbitrationGraceMs: number;
|
|
311
|
+
livenessProbeWindowMs: number;
|
|
312
|
+
onControlPush?: (push: ControlPush) => void;
|
|
278
313
|
}
|
|
279
314
|
|
|
280
315
|
interface OpenedConnection {
|
|
@@ -423,11 +458,20 @@ export class SubcClient {
|
|
|
423
458
|
// double-execute anything. The cached bind is dead (module restarted and
|
|
424
459
|
// its route-gone GOODBYE raced or was missed); evict it so the retry
|
|
425
460
|
// re-opens the route instead of resending into the same dead channel.
|
|
426
|
-
|
|
461
|
+
// stale_route_epoch is the same class with a sharper cause (subconscious
|
|
462
|
+
// issue #39): the channel is known but its epoch was released while this
|
|
463
|
+
// request was in flight. The daemon's contract for the code is
|
|
464
|
+
// NOT-FORWARDED — dropped before delivery — so the retry is safe by
|
|
465
|
+
// construction, and the remedy is identical: evict, re-open, resend once.
|
|
466
|
+
const deadBindCode = err.code === "unknown_channel" || err.code === "stale_route_epoch";
|
|
467
|
+
if (deadBindCode && !retriedUnknownChannel && !this.closeStarted) {
|
|
427
468
|
retriedUnknownChannel = true;
|
|
428
469
|
this.evictRouteHandle(routeHandle);
|
|
429
470
|
continue;
|
|
430
471
|
}
|
|
472
|
+
if (deadBindCode && retriedUnknownChannel) {
|
|
473
|
+
this.evictRouteHandle(routeHandle);
|
|
474
|
+
}
|
|
431
475
|
if (err.kind === "not_sent") {
|
|
432
476
|
try {
|
|
433
477
|
await this.reconnectAfterDrop(err);
|
|
@@ -442,6 +486,14 @@ export class SubcClient {
|
|
|
442
486
|
// late under load), so tearing it down would abandon a healthy connection
|
|
443
487
|
// and its other in-flight routes for nothing.
|
|
444
488
|
this.scheduleReconnectAfterDrop(err);
|
|
489
|
+
} else if (err.kind === "outcome_unknown" && err.code === DEADLINE_NO_DROP_CODE) {
|
|
490
|
+
// Keeping the socket is only correct when the socket is actually
|
|
491
|
+
// alive. A HALF-OPEN socket (peer gone with no FIN/RST — the
|
|
492
|
+
// post-sleep/wake shape) produces the identical observable, and
|
|
493
|
+
// keeping it pins every future call to a corpse: this exact keep
|
|
494
|
+
// turned one host sleep into a whole session's tool outage. The
|
|
495
|
+
// probe supplies the discriminator neither timer can.
|
|
496
|
+
this.probeLivenessAfterDeadline();
|
|
445
497
|
}
|
|
446
498
|
throw err;
|
|
447
499
|
}
|
|
@@ -462,19 +514,23 @@ export class SubcClient {
|
|
|
462
514
|
const corr = this.allocateCorr();
|
|
463
515
|
const key = pendingKey(handle, corr);
|
|
464
516
|
|
|
517
|
+
let subscriptionPending: Pending | null = null;
|
|
518
|
+
let resolveClosed: (() => void) | null = null;
|
|
465
519
|
const closed = new Promise<void>((resolve, reject) => {
|
|
466
520
|
if (this.closedErr) {
|
|
467
521
|
reject(this.closedErr);
|
|
468
522
|
return;
|
|
469
523
|
}
|
|
470
|
-
|
|
524
|
+
resolveClosed = resolve;
|
|
525
|
+
subscriptionPending = {
|
|
471
526
|
handle,
|
|
472
527
|
resolve: () => resolve(),
|
|
473
528
|
reject,
|
|
474
529
|
onProgress: onEvent,
|
|
475
530
|
timer: null,
|
|
476
531
|
subscription: true,
|
|
477
|
-
}
|
|
532
|
+
};
|
|
533
|
+
this.pending.set(key, subscriptionPending);
|
|
478
534
|
const frame = buildFrame(
|
|
479
535
|
FrameType.Request,
|
|
480
536
|
buildFlags(false, priority, false, admission),
|
|
@@ -483,7 +539,7 @@ export class SubcClient {
|
|
|
483
539
|
corr,
|
|
484
540
|
bytes,
|
|
485
541
|
);
|
|
486
|
-
this.sock
|
|
542
|
+
writeBorrowed(this.sock, encodeFrame(frame), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch((err) => {
|
|
487
543
|
const pending = this.pending.get(key);
|
|
488
544
|
if (pending) this.rejectPending(key, pending, err instanceof Error ? err : new SubcError(String(err)));
|
|
489
545
|
});
|
|
@@ -493,7 +549,8 @@ export class SubcClient {
|
|
|
493
549
|
const unsubscribe = (): void => {
|
|
494
550
|
if (cancelled) return;
|
|
495
551
|
cancelled = true;
|
|
496
|
-
this.
|
|
552
|
+
if (subscriptionPending && resolveClosed) this.settle(key, subscriptionPending, resolveClosed);
|
|
553
|
+
if (this.isLiveHandle(handle)) this.cancel(handle, corr, priority);
|
|
497
554
|
};
|
|
498
555
|
return { unsubscribe, closed };
|
|
499
556
|
}
|
|
@@ -509,7 +566,7 @@ export class SubcClient {
|
|
|
509
566
|
corr,
|
|
510
567
|
EMPTY_BODY,
|
|
511
568
|
);
|
|
512
|
-
this.sock
|
|
569
|
+
writeBorrowed(this.sock, encodeFrame(cancel), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS).catch(() => undefined);
|
|
513
570
|
}
|
|
514
571
|
|
|
515
572
|
/** Poll status or liveness for exactly the supplied route generation. */
|
|
@@ -604,7 +661,7 @@ export class SubcClient {
|
|
|
604
661
|
0n,
|
|
605
662
|
EMPTY_BODY,
|
|
606
663
|
);
|
|
607
|
-
const write = this.sock
|
|
664
|
+
const write = writeTrackedBorrowed(this.sock, encodeFrame(goodbye), Date.now() + DEFAULT_REQUEST_TIMEOUT_MS);
|
|
608
665
|
if (!write.queued && closeOnQueueFailure) this.closeConnectionAfterCleanupFailure();
|
|
609
666
|
write.completed.catch(() => {
|
|
610
667
|
if (closeOnQueueFailure && !write.queued) this.closeConnectionAfterCleanupFailure();
|
|
@@ -676,7 +733,7 @@ export class SubcClient {
|
|
|
676
733
|
};
|
|
677
734
|
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, channel, corr, ms), ms);
|
|
678
735
|
this.pending.set(key, pending);
|
|
679
|
-
this.sock
|
|
736
|
+
writeBorrowed(this.sock, encodeFrame(frame), Date.now() + ms).catch((error) => {
|
|
680
737
|
const current = this.pending.get(key);
|
|
681
738
|
if (current) this.rejectPending(key, current, error instanceof Error ? error : new SubcError(String(error)));
|
|
682
739
|
});
|
|
@@ -794,7 +851,7 @@ export class SubcClient {
|
|
|
794
851
|
};
|
|
795
852
|
pending.timer = setTimeout(() => this.arbitrateTimeout(key, pending, handle.channel, corr, ms), ms);
|
|
796
853
|
this.pending.set(key, pending);
|
|
797
|
-
const write = this.sock
|
|
854
|
+
const write = writeTrackedBorrowed(this.sock, encodeFrame(frame), Date.now() + ms);
|
|
798
855
|
handedToSocket = write.queued;
|
|
799
856
|
write.completed.catch((error) => {
|
|
800
857
|
const current = this.pending.get(key);
|
|
@@ -891,6 +948,73 @@ export class SubcClient {
|
|
|
891
948
|
}
|
|
892
949
|
}
|
|
893
950
|
|
|
951
|
+
// Stamped by dispatch() on every inbound frame; read by the liveness probe.
|
|
952
|
+
private lastInboundAtMs = 0;
|
|
953
|
+
// Single-flight guard: overlapping deadline-no-drop settles share one probe.
|
|
954
|
+
private livenessProbe: Promise<void> | null = null;
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* Discriminate event-loop starvation from a half-open socket after a
|
|
958
|
+
* deadline-no-drop settle. The arbitration deliberately keeps the socket on
|
|
959
|
+
* that verdict (a late-read reply under load must not cost a healthy
|
|
960
|
+
* connection), but a half-open socket — peer vanished with no FIN/RST, as
|
|
961
|
+
* after host sleep/wake — yields the SAME verdict forever, so the keep
|
|
962
|
+
* becomes a permanent pin to a dead transport. The discriminator is
|
|
963
|
+
* socket-level liveness evidence: send a channel-0 Ping (the daemon always
|
|
964
|
+
* answers — subc-core control.rs) and require ANY inbound frame within the
|
|
965
|
+
* window; the Pong suffices, and a busy socket's other traffic proves the
|
|
966
|
+
* link just as well. Silence convicts: the socket is failed with a named
|
|
967
|
+
* cause and closed, so the ordinary drop path (pending rejection now,
|
|
968
|
+
* reconnect on the next call) takes over.
|
|
969
|
+
*
|
|
970
|
+
* Starvation cannot be falsely convicted by a short window: a loop starved
|
|
971
|
+
* enough to delay the read loop delays this probe's own timer identically,
|
|
972
|
+
* and when both wake the buffered Pong dispatches before the check runs.
|
|
973
|
+
*
|
|
974
|
+
* Scope: hooked from the managed-call path only. Control-plane requests
|
|
975
|
+
* (route.open, catalog.list) are short-lived and their failures already
|
|
976
|
+
* escalate through reconnect classification; the managed path is where a
|
|
977
|
+
* plugin lives for hours and where the pin was observed in production.
|
|
978
|
+
*/
|
|
979
|
+
private probeLivenessAfterDeadline(): void {
|
|
980
|
+
if (this.livenessProbe || this.closeStarted || this.closedErr) return;
|
|
981
|
+
const sock = this.sock;
|
|
982
|
+
const generation = this.generation;
|
|
983
|
+
let corr: bigint;
|
|
984
|
+
try {
|
|
985
|
+
corr = this.allocateCorr();
|
|
986
|
+
} catch {
|
|
987
|
+
return; // allocator exhausted: the next reconnect resets it
|
|
988
|
+
}
|
|
989
|
+
const t0 = Date.now();
|
|
990
|
+
const ping = buildFrame(
|
|
991
|
+
FrameType.Ping,
|
|
992
|
+
buildFlags(false, Priority.Interactive, false, AdmissionClass.Normal),
|
|
993
|
+
0,
|
|
994
|
+
0,
|
|
995
|
+
corr,
|
|
996
|
+
new Uint8Array(),
|
|
997
|
+
);
|
|
998
|
+
const probe = (async (): Promise<void> => {
|
|
999
|
+
// A failed WRITE is not exonerating: swallow it and let the window
|
|
1000
|
+
// check run — a socket that cannot carry a Ping will not produce an
|
|
1001
|
+
// inbound frame either, and conviction is the right verdict.
|
|
1002
|
+
await writeBorrowed(sock, encodeFrame(ping), t0 + this.opts.livenessProbeWindowMs).catch(() => {});
|
|
1003
|
+
await this.opts.sleep(this.opts.livenessProbeWindowMs);
|
|
1004
|
+
if (this.sock !== sock || this.generation !== generation || this.closeStarted) return;
|
|
1005
|
+
if (this.lastInboundAtMs >= t0) return; // link proven — keep the socket
|
|
1006
|
+
this.fail(
|
|
1007
|
+
new SocketClosedError(
|
|
1008
|
+
`liveness probe convicted a half-open socket: no inbound frame for ${this.opts.livenessProbeWindowMs}ms after a channel-0 Ping (deadline-no-drop settles preceded this); closing so the next call reconnects`,
|
|
1009
|
+
),
|
|
1010
|
+
);
|
|
1011
|
+
sock.close();
|
|
1012
|
+
})().finally(() => {
|
|
1013
|
+
this.livenessProbe = null;
|
|
1014
|
+
});
|
|
1015
|
+
this.livenessProbe = probe;
|
|
1016
|
+
}
|
|
1017
|
+
|
|
894
1018
|
private async ensureConnectedForManaged(): Promise<void> {
|
|
895
1019
|
if (this.closeStarted) throw new SubcError("client closed");
|
|
896
1020
|
if (this.reconnecting) await this.reconnecting;
|
|
@@ -1023,6 +1147,37 @@ export class SubcClient {
|
|
|
1023
1147
|
}
|
|
1024
1148
|
|
|
1025
1149
|
private dispatch(frame: Frame): void {
|
|
1150
|
+
// Every dispatched frame proves the socket delivers inbound bytes; the
|
|
1151
|
+
// post-deadline liveness probe reads this stamp. Stamped after readLoop's
|
|
1152
|
+
// generation gate, so a stale socket's late frames never vouch for the
|
|
1153
|
+
// live one.
|
|
1154
|
+
this.lastInboundAtMs = Date.now();
|
|
1155
|
+
if (frame.header.channel === 0 && frame.header.ty === FrameType.Push) {
|
|
1156
|
+
// Daemon-originated control push (route.closing / route.closed / future
|
|
1157
|
+
// ops). Never matches a pending (corr is daemon-chosen), never an error
|
|
1158
|
+
// path: unparseable bodies and absent observers both drop silently per
|
|
1159
|
+
// the wire contract's MUST-ignore clause, and an observer throw is
|
|
1160
|
+
// swallowed for the same reason as onProgress below -- a caller callback
|
|
1161
|
+
// must not fail the read loop.
|
|
1162
|
+
const observer = this.opts.onControlPush;
|
|
1163
|
+
if (observer) {
|
|
1164
|
+
let parsed: ControlPush | null = null;
|
|
1165
|
+
try {
|
|
1166
|
+
const body = this.parseJson(frame) as Record<string, unknown>;
|
|
1167
|
+
if (body && typeof body.op === "string") parsed = { op: body.op, body };
|
|
1168
|
+
} catch {
|
|
1169
|
+
// Unparseable control push: ignored by contract.
|
|
1170
|
+
}
|
|
1171
|
+
if (parsed) {
|
|
1172
|
+
try {
|
|
1173
|
+
observer(parsed);
|
|
1174
|
+
} catch {
|
|
1175
|
+
// Observer's own throw, on its own stack; the stream must survive.
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
return;
|
|
1180
|
+
}
|
|
1026
1181
|
let handle: RouteHandle | null = null;
|
|
1027
1182
|
if (frame.header.channel !== 0) {
|
|
1028
1183
|
handle = this.liveRoutes.get(frame.header.channel) ?? null;
|
|
@@ -1036,11 +1191,32 @@ export class SubcClient {
|
|
|
1036
1191
|
const pending = this.pending.get(key);
|
|
1037
1192
|
if (pending) {
|
|
1038
1193
|
if (pending.acceptFrame && !pending.acceptFrame(frame)) return;
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1194
|
+
switch (frame.header.ty) {
|
|
1195
|
+
case FrameType.Push:
|
|
1196
|
+
case FrameType.StreamData:
|
|
1197
|
+
// A THROW FROM CALLER CODE MUST NOT REACH THE READ LOOP.
|
|
1198
|
+
//
|
|
1199
|
+
// `onProgress` (and `onEvent`, which is the same slot) is supplied by
|
|
1200
|
+
// the caller and runs INSIDE readLoop's frame dispatch. An escaping
|
|
1201
|
+
// throw unwinds into readLoop's catch, which treats it as a socket
|
|
1202
|
+
// failure: it calls fail(), rejecting EVERY in-flight request on this
|
|
1203
|
+
// connection -- other routes included -- with the caller's own error as
|
|
1204
|
+
// the reported cause, and stops reading. One consumer's bad event
|
|
1205
|
+
// handler therefore drops the whole connection, and the surfaced error
|
|
1206
|
+
// names the handler rather than the transport, so the failure reads as
|
|
1207
|
+
// a subc fault.
|
|
1208
|
+
//
|
|
1209
|
+
// Swallowing is deliberate and is NOT hiding a fault from the caller:
|
|
1210
|
+
// the throw happened in their own callback, on their own stack, where
|
|
1211
|
+
// they are the party able to catch it. What the client owes them is
|
|
1212
|
+
// that the stream and every sibling route keep working.
|
|
1213
|
+
try {
|
|
1214
|
+
pending.onProgress?.(frame.body);
|
|
1215
|
+
} catch {
|
|
1216
|
+
// Swallow the caller's own handler error so one bad callback cannot
|
|
1217
|
+
// abort the read loop or fail unrelated requests. See above.
|
|
1218
|
+
}
|
|
1219
|
+
return;
|
|
1044
1220
|
case FrameType.Response:
|
|
1045
1221
|
case FrameType.StreamEnd:
|
|
1046
1222
|
this.settle(key, pending, () => pending.resolve(frame));
|
|
@@ -1061,7 +1237,17 @@ export class SubcClient {
|
|
|
1061
1237
|
}
|
|
1062
1238
|
|
|
1063
1239
|
if (frame.header.ty === FrameType.Goodbye && handle) {
|
|
1064
|
-
|
|
1240
|
+
// Carries `route_closed` like the other two route-close sites (closeRoute,
|
|
1241
|
+
// late route.open). It was the only one without a code, so a consumer
|
|
1242
|
+
// branching on `route_closed` to recognise a gone route saw this one as an
|
|
1243
|
+
// uncoded generic failure and fell through to whatever its default was.
|
|
1244
|
+
//
|
|
1245
|
+
// THE CODE MUST NOT MAKE THIS RETRYABLE. A GOODBYE arriving mid-request
|
|
1246
|
+
// means the request was already forwarded and the module may have run it,
|
|
1247
|
+
// so it stays kind=outcome_unknown -- the same class as a mid-flight socket
|
|
1248
|
+
// drop, and never the not_sent/unknown_channel class that call() retries.
|
|
1249
|
+
// The code says WHICH route ended, not that it is safe to send again.
|
|
1250
|
+
this.failHandle(handle, new SubcError("route closed by subc (GOODBYE)", "route_closed"));
|
|
1065
1251
|
if (this.liveRoutes.get(handle.channel) === handle) this.liveRoutes.delete(handle.channel);
|
|
1066
1252
|
this.evictRouteHandle(handle);
|
|
1067
1253
|
return;
|
|
@@ -1199,11 +1385,12 @@ export class SubcClient {
|
|
|
1199
1385
|
}
|
|
1200
1386
|
|
|
1201
1387
|
private encode(value: unknown): Uint8Array {
|
|
1202
|
-
return
|
|
1388
|
+
return Buffer.from(JSON.stringify(value), "utf8");
|
|
1203
1389
|
}
|
|
1204
1390
|
|
|
1205
1391
|
private parseJson(frame: Frame): unknown {
|
|
1206
|
-
|
|
1392
|
+
const b = frame.body;
|
|
1393
|
+
return JSON.parse(Buffer.from(b.buffer, b.byteOffset, b.byteLength).toString("utf8"));
|
|
1207
1394
|
}
|
|
1208
1395
|
}
|
|
1209
1396
|
|
|
@@ -1243,6 +1430,7 @@ export function isRetryableRouteOpenCode(code: string | undefined): boolean {
|
|
|
1243
1430
|
return (
|
|
1244
1431
|
code === "unknown_module" ||
|
|
1245
1432
|
code === "module_reloading" ||
|
|
1433
|
+
code === "module_warming" ||
|
|
1246
1434
|
code === "target_unavailable" ||
|
|
1247
1435
|
code === "module_timeout"
|
|
1248
1436
|
);
|
|
@@ -1266,6 +1454,8 @@ function normalizeConnectOptions(opts: ConnectOptions): NormalizedConnectOptions
|
|
|
1266
1454
|
reconnectBackoff: opts.reconnectBackoff ?? DEFAULT_RECONNECT_BACKOFF,
|
|
1267
1455
|
sleep: opts.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms))),
|
|
1268
1456
|
timeoutArbitrationGraceMs: opts.timeoutArbitrationGraceMs ?? TIMEOUT_ARBITRATION_GRACE_MS,
|
|
1457
|
+
livenessProbeWindowMs: opts.livenessProbeWindowMs ?? LIVENESS_PROBE_WINDOW_MS,
|
|
1458
|
+
onControlPush: opts.onControlPush,
|
|
1269
1459
|
};
|
|
1270
1460
|
}
|
|
1271
1461
|
|
package/src/connection-file.ts
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
import { promises as fs } from "node:fs";
|
|
8
8
|
|
|
9
|
+
import { PROTOCOL_VERSION } from "./envelope.js";
|
|
10
|
+
|
|
9
11
|
export const SCHEMA_VERSION = 1;
|
|
10
12
|
export const MIN_KEY_LEN = 32;
|
|
11
13
|
export const DAEMON_ID_LEN = 16;
|
|
@@ -29,9 +31,14 @@ export interface ConnectionInfo {
|
|
|
29
31
|
export class ConnectionFileError extends Error {}
|
|
30
32
|
|
|
31
33
|
function toBytes(value: unknown, field: string): Uint8Array {
|
|
32
|
-
if (!Array.isArray(value)
|
|
34
|
+
if (!Array.isArray(value)) {
|
|
33
35
|
throw new ConnectionFileError(`connection file field '${field}' must be a JSON array of bytes`);
|
|
34
36
|
}
|
|
37
|
+
for (const byte of value) {
|
|
38
|
+
if (typeof byte !== "number" || !Number.isInteger(byte) || byte < 0 || byte > 255) {
|
|
39
|
+
throw new ConnectionFileError(`connection file field '${field}' has invalid byte ${String(byte)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
35
42
|
return Uint8Array.from(value as number[]);
|
|
36
43
|
}
|
|
37
44
|
|
|
@@ -84,6 +91,13 @@ export async function readConnectionFile(path: string): Promise<ConnectionInfo>
|
|
|
84
91
|
throw new ConnectionFileError(`connection file JSON read failed for ${path}: ${String(err)}`);
|
|
85
92
|
}
|
|
86
93
|
|
|
94
|
+
const wireVersion = parsed.wire_version;
|
|
95
|
+
if (wireVersion !== undefined && wireVersion !== PROTOCOL_VERSION) {
|
|
96
|
+
throw new ConnectionFileError(
|
|
97
|
+
`connection file wire_version ${String(wireVersion)} but this client speaks ${PROTOCOL_VERSION}; the client library must be upgraded`,
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
87
101
|
const endpointsRaw = parsed.endpoints;
|
|
88
102
|
if (!Array.isArray(endpointsRaw)) {
|
|
89
103
|
throw new ConnectionFileError("connection file 'endpoints' must be an array");
|
package/src/envelope.ts
CHANGED
|
@@ -122,6 +122,59 @@ export class DecodeError extends Error {
|
|
|
122
122
|
}
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
+
function validateHeaderFields(header: EnvelopeHeader): void {
|
|
126
|
+
// Apply wire-width coercions in serialization order before semantic validation.
|
|
127
|
+
// This preserves encodeHeader's modulo behavior and its early conversion errors.
|
|
128
|
+
const len = header.len >>> 0;
|
|
129
|
+
const ver = (header.ver >>> 0) & 0xff;
|
|
130
|
+
const typeByte = (header.ty >>> 0) & 0xff;
|
|
131
|
+
const flags = (header.flags >>> 0) & 0xff;
|
|
132
|
+
const channel = (header.channel >>> 0) & 0xffff;
|
|
133
|
+
const epoch = header.epoch >>> 0;
|
|
134
|
+
void BigInt.asUintN(64, header.corr);
|
|
135
|
+
|
|
136
|
+
if (ver !== PROTOCOL_VERSION) throw new DecodeError(`unsupported envelope version ${ver}`, "unsupported_version");
|
|
137
|
+
if (typeByte > FRAME_TYPE_MAX) throw new DecodeError(`unknown frame type byte ${typeByte}`, "unknown_frame_type");
|
|
138
|
+
const ty = typeByte as FrameType;
|
|
139
|
+
if ((flags & FLAG_RESERVED_MASK) !== 0) {
|
|
140
|
+
throw new DecodeError(
|
|
141
|
+
`reserved flag bits set in flags 0b${flags.toString(2).padStart(8, "0")}`,
|
|
142
|
+
"reserved_flag_bits",
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
if (((flags & FLAG_PRIORITY_MASK) >> FLAG_PRIORITY_SHIFT) === 0b11) {
|
|
146
|
+
throw new DecodeError(
|
|
147
|
+
`reserved priority bits set in flags 0b${flags.toString(2).padStart(8, "0")}`,
|
|
148
|
+
"reserved_priority_bits",
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
const admission = (flags & FLAG_ADMISSION_MASK) >> FLAG_ADMISSION_SHIFT;
|
|
152
|
+
if (admission === 0b11) {
|
|
153
|
+
throw new DecodeError(
|
|
154
|
+
`reserved admission class set in flags 0b${flags.toString(2).padStart(8, "0")}`,
|
|
155
|
+
"reserved_admission_class",
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
if (admission === AdmissionClass.Sheddable && ty !== FrameType.Push && ty !== FrameType.StreamData) {
|
|
159
|
+
throw new DecodeError(
|
|
160
|
+
`SHEDDABLE admission class is illegal on ${FrameType[ty]} in flags 0b${flags.toString(2).padStart(8, "0")}`,
|
|
161
|
+
"sheddable_illegal_frame_type",
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
if (channel === 0 && epoch !== 0) {
|
|
165
|
+
throw new DecodeError(
|
|
166
|
+
`control channel carried nonzero epoch ${epoch}`,
|
|
167
|
+
"nonzero_epoch_on_control_channel",
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
if (isPureHeader(ty) && len !== 0) {
|
|
171
|
+
throw new DecodeError(
|
|
172
|
+
`pure-header frame ${FrameType[ty]} declared non-zero body length ${len}`,
|
|
173
|
+
"pure_header_frame_with_body",
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
125
178
|
/** Decode and validate a header from the front of `bytes`. */
|
|
126
179
|
export function decodeHeader(bytes: Uint8Array): EnvelopeHeader {
|
|
127
180
|
if (bytes.length < FROZEN_PREFIX_LEN) {
|
|
@@ -210,7 +263,7 @@ export function buildFrameWithVersion(
|
|
|
210
263
|
throw new DecodeError(`frame body ${body.length} exceeds max ${MAX_FRAME_BODY_LEN}`, "frame_body_too_large");
|
|
211
264
|
}
|
|
212
265
|
const header = { len: body.length, ver, ty, flags, channel, epoch, corr };
|
|
213
|
-
|
|
266
|
+
validateHeaderFields(header);
|
|
214
267
|
return { header, body };
|
|
215
268
|
}
|
|
216
269
|
|
package/src/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ export {
|
|
|
16
16
|
type RequestOptions,
|
|
17
17
|
type ManagedCallOptions,
|
|
18
18
|
type ConnectOptions,
|
|
19
|
+
type ControlPush,
|
|
19
20
|
type ReconnectBackoff,
|
|
20
21
|
type SubcCallErrorKind,
|
|
21
22
|
type SubscribeOptions,
|
|
@@ -70,19 +71,16 @@ export {
|
|
|
70
71
|
type BindDecision,
|
|
71
72
|
type Principal,
|
|
72
73
|
type BindingsInput,
|
|
73
|
-
type CircuitBreakerInput,
|
|
74
74
|
type Concurrency,
|
|
75
75
|
type ConsumerRoleInput,
|
|
76
76
|
type ExecutionMode,
|
|
77
77
|
type IdentityBindingInput,
|
|
78
78
|
type IdentityScope,
|
|
79
79
|
type InternalTransport,
|
|
80
|
-
type LeaseScope,
|
|
81
80
|
type ManagementOperationInput,
|
|
82
81
|
type ManagementOperationKind,
|
|
83
82
|
type ManagementSurfaceManifestOptions,
|
|
84
83
|
type ManifestInput,
|
|
85
|
-
type ModelPolicyInput,
|
|
86
84
|
type ModuleHelloAckBody,
|
|
87
85
|
type ObservabilityKind,
|
|
88
86
|
type ObservabilitySurfaceInput,
|
|
@@ -95,12 +93,10 @@ export {
|
|
|
95
93
|
type ProviderHealthHandler,
|
|
96
94
|
type ProviderRoleInput,
|
|
97
95
|
type RouteBindRequest,
|
|
98
|
-
type ScheduledTaskInput,
|
|
99
96
|
type StorageBindingInput,
|
|
100
97
|
type StorageKind,
|
|
101
98
|
type StorageScope,
|
|
102
99
|
type SubcProviderConnectOptions,
|
|
103
|
-
type TaskEligibilityInput,
|
|
104
100
|
type ToolInput,
|
|
105
101
|
type TrustTier,
|
|
106
102
|
type VaultGrantInput,
|