@fougere/transport-http 0.24.0-alpha.0 → 0.26.0-alpha.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/client.js CHANGED
@@ -54,9 +54,9 @@ export function createHttpTransport(baseUrl, options = {}) {
54
54
  // receiver choosing between a proof and a claim about the same thing.
55
55
  // `caller` is dropped on every hop: it names who signed THIS call, so carrying the
56
56
  // one this process was handed would make `shop → catalog → billing` read `shop`.
57
- // It travels outside the envelope anyway, so a stale one would be unsigned too. `crossed`
57
+ // It travels outside the envelope anyway, so a stale one would be unsigned too. `fromAnotherProcess`
58
58
  // is the next receiver's to write, for the same reason.
59
- const { caller: _established, crossed: _received, ...forwarded } = invocation;
59
+ const { caller: _established, fromAnotherProcess: _received, ...forwarded } = invocation;
60
60
  const sent = options.sign
61
61
  ? { ...forwarded, state: {}, identity: await options.sign({ ...call, ...invocation }) }
62
62
  : forwarded;
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAA2E,MAAM,wBAAwB,CAAC;AAMvK,0CAA0C;AAC1C,MAAM,UAAU,SAAS,CAAC,IAAe,EAAE,UAA6B,EAAE,EAAU;IAClF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAC1F,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,eAAe,CAAC,QAAiB,EAAE,IAAe;IAChE,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,IAAI,QAAQ,IAAI,OAAO,IAAI,QAAQ,CAAC,EAAE,CAAC;QAChG,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,SAAS,CAAC,WAAW;YAC3B,OAAO,EAAE,kEAAkE;YAC3E,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,SAAS,EAAE,IAAI,CAAC,EAAE;SACnB,CAAC,CAAC;IACL,CAAC;IACD,IAAI,OAAO,IAAI,QAAQ,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAsC,CAAC;QAC9D,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACxC,MAAM,IAAI,YAAY,CAAC;gBACrB,IAAI,EAAE,SAAS,CAAC,WAAW;gBAC3B,OAAO,EAAE,uDAAuD;gBAChE,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,SAAS,EAAE,IAAI,CAAC,EAAE;aACnB,CAAC,CAAC;QACL,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;YAAE,MAAM,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACtE,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,SAAS,CAAC,cAAc;YAC9B,OAAO,EAAE,GAAG,KAAK,CAAC,OAAO,SAAS,KAAK,CAAC,IAAI,GAAG;YAC/C,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,SAAS,EAAE,IAAI,CAAC,EAAE;SACnB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC,MAAM,CAAC;AACzB,CAAC;AAeD,+EAA+E;AAC/E,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;AAEhF,MAAM,UAAU,mBAAmB,CAAC,OAAe,EAAE,OAAO,GAAyB,EAAE;IACrF,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC;IAC1D,wFAAwF;IACxF,qFAAqF;IACrF,kFAAkF;IAClF,uFAAuF;IACvF,sFAAsF;IACtF,sBAAsB;IACtB,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC;IACpC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC;IACrC,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE;QAChC,6EAA6E;QAC7E,sEAAsE;QACtE,mFAAmF;QACnF,iFAAiF;QACjF,0FAA0F;QAC1F,wDAAwD;QACxD,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,GAAG,UAAU,CAAC;QAC9E,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI;YACvB,CAAC,CAAC,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,UAAU,EAAE,CAAC,EAAE;YACvF,CAAC,CAAC,SAAS,CAAC;QACd,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,GAAG,aAAa,EAAE;YAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAE/F,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;YAClC,IAAI,GAAa,CAAC;YAClB,IAAI,CAAC;gBACH,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE;oBACpB,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;oBAC/C,IAAI;oBACJ,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;iBACvC,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;oBACnB,MAAM,IAAI,YAAY,CAAC;wBACrB,IAAI,EAAE,SAAS,CAAC,eAAe;wBAC/B,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,EAAE,oBAAoB,SAAS,IAAI;wBACpE,KAAK,EAAE,IAAI,KAAK,CAAC,GAAG,OAAO,sBAAsB,SAAS,IAAI,CAAC;wBAC/D,OAAO,EAAE,IAAI,CAAC,OAAO;wBACrB,SAAS,EAAE,IAAI,CAAC,EAAE;qBACnB,CAAC,CAAC;gBACL,CAAC;gBACD,IAAI,OAAO,GAAG,OAAO,IAAI,mBAAmB,CAAC,GAAG,CAAC;oBAAE,SAAS;gBAC5D,MAAM,IAAI,YAAY,CAAC;oBACrB,IAAI,EAAE,SAAS,CAAC,mBAAmB;oBACnC,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,EAAE,yCAAyC;oBAC5E,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,SAAS,EAAE,IAAI,CAAC,EAAE;oBAClB,KAAK,EAAE,IAAI,KAAK,CAAC,GAAG,OAAO,iBAAkB,GAAa,EAAE,OAAO,IAAI,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;iBAC9F,CAAC,CAAC;YACL,CAAC;YAED,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;gBAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACtD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,YAAY,CAAC;oBACrB,IAAI,EAAE,SAAS,CAAC,WAAW;oBAC3B,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,EAAE,gCAAgC,GAAG,CAAC,MAAM,4BAA4B;oBACzG,KAAK,EAAE,IAAI,KAAK,CAAC,GAAG,OAAO,kBAAkB,GAAG,CAAC,MAAM,EAAE,CAAC;oBAC1D,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,SAAS,EAAE,IAAI,CAAC,EAAE;iBACnB,CAAC,CAAC;YACL,CAAC;YAED,IAAI,QAAqB,CAAC;YAC1B,IAAI,CAAC;gBACH,QAAQ,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAgB,CAAC;YAC/C,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,YAAY,CAAC;oBACrB,IAAI,EAAE,SAAS,CAAC,WAAW;oBAC3B,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,EAAE,kCAAkC;oBACrE,KAAK,EAAE,IAAI,KAAK,CAAC,GAAG,OAAO,oBAAoB,CAAC;oBAChD,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,SAAS,EAAE,IAAI,CAAC,EAAE;iBACnB,CAAC,CAAC;YACL,CAAC;YAED,OAAO,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,+FAA+F;AAC/F,SAAS,QAAQ,CAAC,IAAe,EAAE,OAAe;IAChD,OAAO,IAAI,YAAY,CAAC;QACtB,IAAI,EAAE,SAAS,CAAC,iBAAiB;QACjC,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,EAAE,0BAA0B,YAAY,EAAE,0CAA0C;QACrH,KAAK,EAAE,IAAI,KAAK,CAAC,eAAe,OAAO,EAAE,CAAC;QAC1C,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,SAAS,EAAE,IAAI,CAAC,EAAE;KACnB,CAAC,CAAC;AACL,CAAC;AAED,SAAS,SAAS,CAAC,GAAY;IAC7B,OAAO,GAAG,YAAY,YAAY,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,cAAc,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC;AACnG,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAY;IACvC,MAAM,IAAI,GAAI,GAAqC,EAAE,KAAK,EAAE,IAAI,CAAC;IAEjE,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACnE,CAAC"}
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAA2E,MAAM,wBAAwB,CAAC;AAMvK,0CAA0C;AAC1C,MAAM,UAAU,SAAS,CAAC,IAAe,EAAE,UAA6B,EAAE,EAAU;IAClF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;AAC1F,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,eAAe,CAAC,QAAiB,EAAE,IAAe;IAChE,IAAI,CAAC,QAAQ,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,CAAC,QAAQ,IAAI,QAAQ,IAAI,OAAO,IAAI,QAAQ,CAAC,EAAE,CAAC;QAChG,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,SAAS,CAAC,WAAW;YAC3B,OAAO,EAAE,kEAAkE;YAC3E,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,SAAS,EAAE,IAAI,CAAC,EAAE;SACnB,CAAC,CAAC;IACL,CAAC;IACD,IAAI,OAAO,IAAI,QAAQ,EAAE,CAAC;QACxB,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAsC,CAAC;QAC9D,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACxC,MAAM,IAAI,YAAY,CAAC;gBACrB,IAAI,EAAE,SAAS,CAAC,WAAW;gBAC3B,OAAO,EAAE,uDAAuD;gBAChE,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,SAAS,EAAE,IAAI,CAAC,EAAE;aACnB,CAAC,CAAC;QACL,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;YAAE,MAAM,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACtE,MAAM,IAAI,YAAY,CAAC;YACrB,IAAI,EAAE,SAAS,CAAC,cAAc;YAC9B,OAAO,EAAE,GAAG,KAAK,CAAC,OAAO,SAAS,KAAK,CAAC,IAAI,GAAG;YAC/C,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,SAAS,EAAE,IAAI,CAAC,EAAE;SACnB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC,MAAM,CAAC;AACzB,CAAC;AAeD,+EAA+E;AAC/E,MAAM,mBAAmB,GAAG,IAAI,GAAG,CAAC,CAAC,cAAc,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;AAEhF,MAAM,UAAU,mBAAmB,CAAC,OAAe,EAAE,OAAO,GAAyB,EAAE;IACrF,MAAM,GAAG,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC;IAC1D,wFAAwF;IACxF,qFAAqF;IACrF,kFAAkF;IAClF,uFAAuF;IACvF,sFAAsF;IACtF,sBAAsB;IACtB,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC;IACpC,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,MAAM,CAAC;IAC9C,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC;IACrC,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE;QAChC,6EAA6E;QAC7E,sEAAsE;QACtE,mFAAmF;QACnF,iFAAiF;QACjF,qGAAqG;QACrG,wDAAwD;QACxD,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,kBAAkB,EAAE,SAAS,EAAE,GAAG,SAAS,EAAE,GAAG,UAAU,CAAC;QACzF,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI;YACvB,CAAC,CAAC,EAAE,GAAG,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,IAAI,EAAE,GAAG,UAAU,EAAE,CAAC,EAAE;YACvF,CAAC,CAAC,SAAS,CAAC;QACd,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAChD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,UAAU,GAAG,aAAa,EAAE;YAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QAE/F,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;YAClC,IAAI,GAAa,CAAC;YAClB,IAAI,CAAC;gBACH,GAAG,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE;oBACpB,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;oBAC/C,IAAI;oBACJ,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;iBACvC,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,IAAI,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;oBACnB,MAAM,IAAI,YAAY,CAAC;wBACrB,IAAI,EAAE,SAAS,CAAC,eAAe;wBAC/B,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,EAAE,oBAAoB,SAAS,IAAI;wBACpE,KAAK,EAAE,IAAI,KAAK,CAAC,GAAG,OAAO,sBAAsB,SAAS,IAAI,CAAC;wBAC/D,OAAO,EAAE,IAAI,CAAC,OAAO;wBACrB,SAAS,EAAE,IAAI,CAAC,EAAE;qBACnB,CAAC,CAAC;gBACL,CAAC;gBACD,IAAI,OAAO,GAAG,OAAO,IAAI,mBAAmB,CAAC,GAAG,CAAC;oBAAE,SAAS;gBAC5D,MAAM,IAAI,YAAY,CAAC;oBACrB,IAAI,EAAE,SAAS,CAAC,mBAAmB;oBACnC,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,EAAE,yCAAyC;oBAC5E,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,SAAS,EAAE,IAAI,CAAC,EAAE;oBAClB,KAAK,EAAE,IAAI,KAAK,CAAC,GAAG,OAAO,iBAAkB,GAAa,EAAE,OAAO,IAAI,GAAG,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;iBAC9F,CAAC,CAAC;YACL,CAAC;YAED,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG;gBAAE,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;YACtD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;gBACZ,MAAM,IAAI,YAAY,CAAC;oBACrB,IAAI,EAAE,SAAS,CAAC,WAAW;oBAC3B,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,EAAE,gCAAgC,GAAG,CAAC,MAAM,4BAA4B;oBACzG,KAAK,EAAE,IAAI,KAAK,CAAC,GAAG,OAAO,kBAAkB,GAAG,CAAC,MAAM,EAAE,CAAC;oBAC1D,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,SAAS,EAAE,IAAI,CAAC,EAAE;iBACnB,CAAC,CAAC;YACL,CAAC;YAED,IAAI,QAAqB,CAAC;YAC1B,IAAI,CAAC;gBACH,QAAQ,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAgB,CAAC;YAC/C,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,YAAY,CAAC;oBACrB,IAAI,EAAE,SAAS,CAAC,WAAW;oBAC3B,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,EAAE,kCAAkC;oBACrE,KAAK,EAAE,IAAI,KAAK,CAAC,GAAG,OAAO,oBAAoB,CAAC;oBAChD,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,SAAS,EAAE,IAAI,CAAC,EAAE;iBACnB,CAAC,CAAC;YACL,CAAC;YAED,OAAO,eAAe,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC;AAED,+FAA+F;AAC/F,SAAS,QAAQ,CAAC,IAAe,EAAE,OAAe;IAChD,OAAO,IAAI,YAAY,CAAC;QACtB,IAAI,EAAE,SAAS,CAAC,iBAAiB;QACjC,OAAO,EAAE,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,EAAE,0BAA0B,YAAY,EAAE,0CAA0C;QACrH,KAAK,EAAE,IAAI,KAAK,CAAC,eAAe,OAAO,EAAE,CAAC;QAC1C,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,SAAS,EAAE,IAAI,CAAC,EAAE;KACnB,CAAC,CAAC;AACL,CAAC;AAED,SAAS,SAAS,CAAC,GAAY;IAC7B,OAAO,GAAG,YAAY,YAAY,IAAI,CAAC,GAAG,CAAC,IAAI,KAAK,cAAc,IAAI,GAAG,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC;AACnG,CAAC;AAED,SAAS,mBAAmB,CAAC,GAAY;IACvC,MAAM,IAAI,GAAI,GAAqC,EAAE,KAAK,EAAE,IAAI,CAAC;IAEjE,OAAO,OAAO,IAAI,KAAK,QAAQ,IAAI,mBAAmB,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACnE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { createHttpTransport, frameCall, unframeResponse } from './client.js';
2
- export { handleRpc } from './server.js';
2
+ export { handleRpc, pageDoor, peerDoor, type PageDoorOptions, type ReceiveOptions, type RpcCall } from './server.js';
3
3
  export { receive } from './receive.js';
4
4
  export { maxFrameBytes } from './policy.js';
5
5
  export { serve, type RunningReceiver } from './serve/RunningReceiver.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,KAAK,OAAO,EAAE,MAAM,aAAa,CAAC;AACrH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,KAAK,eAAe,EAAE,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,YAAY,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  export { createHttpTransport, frameCall, unframeResponse } from './client.js';
2
- export { handleRpc } from './server.js';
2
+ export { handleRpc, pageDoor, peerDoor } from './server.js';
3
3
  export { receive } from './receive.js';
4
4
  export { maxFrameBytes } from './policy.js';
5
5
  export { serve } from './serve/RunningReceiver.js';
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAwB,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAA2D,MAAM,aAAa,CAAC;AACrH,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAwB,MAAM,4BAA4B,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC"}
package/dist/receive.d.ts CHANGED
@@ -6,7 +6,7 @@
6
6
  * baseline, measured 2026-08-22.
7
7
  */
8
8
  import { type ReceiveOptions } from './server.js';
9
- import type { Transport } from '@fougere/core/contract';
9
+ import { type Transport } from '@fougere/core/contract';
10
10
  export interface ReceiveHttpOptions extends ReceiveOptions {
11
11
  /** The path this facade answers. Default: `/_fougere/call`. */
12
12
  path?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"receive.d.ts","sourceRoot":"","sources":["../src/receive.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AA+CD,wBAAgB,OAAO,CACrB,MAAM,EAAE,SAAS,EACjB,OAAO,GAAE,kBAAuB,GAC/B,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CA6BzC"}
1
+ {"version":3,"file":"receive.d.ts","sourceRoot":"","sources":["../src/receive.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAE7D,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAE7F,MAAM,WAAW,kBAAmB,SAAQ,cAAc;IACxD,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAKD,wBAAgB,OAAO,CACrB,MAAM,EAAE,SAAS,EACjB,OAAO,GAAE,kBAAuB,GAC/B,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,CAAC,CAmCzC"}
package/dist/receive.js CHANGED
@@ -7,44 +7,8 @@
7
7
  */
8
8
  import { handleRpc } from './server.js';
9
9
  import { maxFrameBytes, CALL_PATH, parseError, tooLarge } from './policy.js';
10
+ import { ErrorCode, FougereError, readWithin } from '@fougere/core/contract';
10
11
  const json = (body, status = 200) => new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' } });
11
- /** Distinguishes "the body was too large" from any body a caller could actually send. */
12
- const TOO_LARGE = Symbol('too large');
13
- /**
14
- * The declared length is a cheap refusal; the count is the real one. A `content-length` may
15
- * be absent, and it may lie — the copy that trusted it alone had no cap at all in that case.
16
- */
17
- async function bodyWithin(request, max) {
18
- const declared = Number(request.headers.get('content-length'));
19
- if (Number.isFinite(declared) && declared > max)
20
- return TOO_LARGE;
21
- const reader = request.body?.getReader();
22
- if (!reader)
23
- return '';
24
- const chunks = [];
25
- let size = 0;
26
- for (;;) {
27
- const { done, value } = await reader.read();
28
- if (done)
29
- break;
30
- size += value.byteLength;
31
- if (size > max) {
32
- await reader.cancel();
33
- return TOO_LARGE;
34
- }
35
- chunks.push(value);
36
- }
37
- return new TextDecoder().decode(concat(chunks, size));
38
- }
39
- function concat(chunks, size) {
40
- const all = new Uint8Array(size);
41
- let at = 0;
42
- for (const chunk of chunks) {
43
- all.set(chunk, at);
44
- at += chunk.byteLength;
45
- }
46
- return all;
47
- }
48
12
  export function receive(runner, options = {}) {
49
13
  // At CONSTRUCTION and not per call, for the reason `serve` refuses at bind: a receiver
50
14
  // that starts and then rejects everything is found in production.
@@ -57,9 +21,15 @@ export function receive(runner, options = {}) {
57
21
  if (request.method !== 'POST' || new URL(request.url).pathname !== path) {
58
22
  return new Response(null, { status: 404 });
59
23
  }
60
- const raw = await bodyWithin(request, maxFrameBytes());
61
- if (raw === TOO_LARGE)
24
+ let raw;
25
+ try {
26
+ raw = await readWithin(request.body, maxFrameBytes(), request.headers.get('content-length'));
27
+ }
28
+ catch (refused) {
29
+ if (!(refused instanceof FougereError && refused.code === ErrorCode.PAYLOAD_TOO_LARGE))
30
+ throw refused;
62
31
  return json(tooLarge(), 413);
32
+ }
63
33
  let parsed;
64
34
  try {
65
35
  parsed = JSON.parse(raw);
@@ -1 +1 @@
1
- {"version":3,"file":"receive.js","sourceRoot":"","sources":["../src/receive.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,SAAS,EAAuB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAU7E,MAAM,IAAI,GAAG,CAAC,IAAa,EAAE,MAAM,GAAG,GAAG,EAAE,EAAE,CAC3C,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;AAElG,yFAAyF;AACzF,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,CAAC,CAAC;AAEtC;;;GAGG;AACH,KAAK,UAAU,UAAU,CAAC,OAAgB,EAAE,GAAW;IACrD,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAC/D,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,GAAG,GAAG;QAAE,OAAO,SAAS,CAAC;IAElE,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,SAAS,EAAE,CAAC;IACzC,IAAI,CAAC,MAAM;QAAE,OAAO,EAAE,CAAC;IAEvB,MAAM,MAAM,GAAiB,EAAE,CAAC;IAChC,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,SAAS,CAAC;QACR,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QAC5C,IAAI,IAAI;YAAE,MAAM;QAChB,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC;QACzB,IAAI,IAAI,GAAG,GAAG,EAAE,CAAC;YACf,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC;YAEtB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC;IAED,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,SAAS,MAAM,CAAC,MAA6B,EAAE,IAAY;IACzD,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IACjC,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACnB,EAAE,IAAI,KAAK,CAAC,UAAU,CAAC;IACzB,CAAC;IAED,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,MAAiB,EACjB,OAAO,GAAuB,EAAE;IAEhC,uFAAuF;IACvF,kEAAkE;IAClE,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CACb,gHAAgH;cAC9G,iGAAiG,CACpG,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC;IAEvC,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YACxE,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7C,CAAC;QAED,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC;QACvD,IAAI,GAAG,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,CAAC,CAAC;QAEpD,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QAC5B,CAAC;QAED,OAAO,IAAI,CAAC,MAAM,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"receive.js","sourceRoot":"","sources":["../src/receive.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,SAAS,EAAuB,MAAM,aAAa,CAAC;AAC7D,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAkB,MAAM,wBAAwB,CAAC;AAS7F,MAAM,IAAI,GAAG,CAAC,IAAa,EAAE,MAAM,GAAG,GAAG,EAAE,EAAE,CAC3C,IAAI,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,EAAE,CAAC,CAAC;AAElG,MAAM,UAAU,OAAO,CACrB,MAAiB,EACjB,OAAO,GAAuB,EAAE;IAEhC,uFAAuF;IACvF,kEAAkE;IAClE,IAAI,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CACb,gHAAgH;cAC9G,iGAAiG,CACpG,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,SAAS,CAAC;IAEvC,OAAO,KAAK,EAAE,OAAO,EAAE,EAAE;QACvB,IAAI,OAAO,CAAC,MAAM,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YACxE,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QAC7C,CAAC;QAED,IAAI,GAAW,CAAC;QAChB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,IAAI,EAAE,aAAa,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC;QAC/F,CAAC;QAAC,OAAO,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC,CAAC,OAAO,YAAY,YAAY,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,CAAC,iBAAiB,CAAC;gBAAE,MAAM,OAAO,CAAC;YAEtG,OAAO,IAAI,CAAC,QAAQ,EAAE,EAAE,GAAG,CAAC,CAAC;QAC/B,CAAC;QAED,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC;QAC5B,CAAC;QAED,OAAO,IAAI,CAAC,MAAM,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC;AACJ,CAAC"}
@@ -1,4 +1,4 @@
1
- import type { Transport } from '@fougere/core';
1
+ import { type Transport } from '@fougere/core';
2
2
  import type { ServeOptions } from './ServeOptions.js';
3
3
  export interface RunningReceiver {
4
4
  port: number;
@@ -1 +1 @@
1
- {"version":3,"file":"RunningReceiver.d.ts","sourceRoot":"","sources":["../../src/serve/RunningReceiver.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAI/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,qDAAqD;AACrD,eAAO,MAAM,cAAc,UAAoC,CAAC;AAEhE,wBAAgB,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,eAAe,CAAC,CAM7F"}
1
+ {"version":3,"file":"RunningReceiver.d.ts","sourceRoot":"","sources":["../../src/serve/RunningReceiver.ts"],"names":[],"mappings":"AAEA,OAAO,EAAuC,KAAK,SAAS,EAAE,MAAM,eAAe,CAAC;AAIpF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,qDAAqD;AACrD,eAAO,MAAM,cAAc,UAAoC,CAAC;AAEhE,wBAAgB,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,eAAe,CAAC,CAM7F"}
@@ -1,4 +1,6 @@
1
1
  import { createServer } from 'node:http';
2
+ import { Readable } from 'node:stream';
3
+ import { ErrorCode, FougereError, readWithin } from '@fougere/core';
2
4
  import { handleRpc } from '../server.js';
3
5
  import { maxFrameBytes, CALL_PATH, parseError, tooLarge } from '../policy.js';
4
6
  /** What a frond binds when nobody says otherwise. */
@@ -34,24 +36,27 @@ function whyNotHere(host, options) {
34
36
  function answering(runner, options) {
35
37
  return async (req, res) => {
36
38
  if (req.method !== 'POST' || req.url !== CALL_PATH) {
37
- res.writeHead(404).end();
38
- return;
39
- }
40
- const maxBodyBytes = maxFrameBytes();
41
- const chunks = [];
42
- let size = 0;
43
- for await (const value of req) {
44
- const chunk = Buffer.from(value);
45
- size += chunk.length;
46
- if (size > maxBodyBytes) {
47
- res.writeHead(413, { 'content-type': 'application/json' }).end(JSON.stringify(tooLarge()));
39
+ const answered = options.beside && await options.beside(webRequestOf(req));
40
+ if (!answered) {
41
+ res.writeHead(404).end();
48
42
  return;
49
43
  }
50
- chunks.push(chunk);
44
+ res.writeHead(answered.status, Object.fromEntries(answered.headers)).end(Buffer.from(await answered.arrayBuffer()));
45
+ return;
46
+ }
47
+ let raw;
48
+ try {
49
+ raw = await readWithin(req, maxFrameBytes(), req.headers['content-length']);
50
+ }
51
+ catch (refused) {
52
+ if (!(refused instanceof FougereError && refused.code === ErrorCode.PAYLOAD_TOO_LARGE))
53
+ throw refused;
54
+ res.writeHead(413, { 'content-type': 'application/json' }).end(JSON.stringify(tooLarge()));
55
+ return;
51
56
  }
52
57
  let response;
53
58
  try {
54
- response = await handleRpc(runner, JSON.parse(Buffer.concat(chunks).toString('utf8')), options);
59
+ response = await handleRpc(runner, JSON.parse(raw), options);
55
60
  }
56
61
  catch {
57
62
  response = parseError();
@@ -59,6 +64,21 @@ function answering(runner, options) {
59
64
  res.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify(response));
60
65
  };
61
66
  }
67
+ /** A Node request as a Web one, its body still a stream — built only for what is not a call. */
68
+ function webRequestOf(req) {
69
+ const headers = new Headers();
70
+ for (const [name, value] of Object.entries(req.headers)) {
71
+ if (value !== undefined)
72
+ headers.set(name, Array.isArray(value) ? value.join(', ') : value);
73
+ }
74
+ // A message carries a body when it says so — a length, or chunks — and a POST with neither has none.
75
+ const reads = Number(req.headers['content-length']) > 0 || req.headers['transfer-encoding'] !== undefined;
76
+ return new Request(`http://${req.headers.host ?? 'localhost'}${req.url ?? '/'}`, {
77
+ method: req.method,
78
+ headers,
79
+ ...(reads && { body: Readable.toWeb(req), duplex: 'half' }),
80
+ });
81
+ }
62
82
  /** The port a receiver ended up on — asked of the server, since 0 means "whatever is free". */
63
83
  function listening(server, host, options) {
64
84
  return new Promise((resolve, reject) => {
@@ -1 +1 @@
1
- {"version":3,"file":"RunningReceiver.js","sourceRoot":"","sources":["../../src/serve/RunningReceiver.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"RunningReceiver.js","sourceRoot":"","sources":["../../src/serve/RunningReceiver.ts"],"names":[],"mappings":"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"}
@@ -10,5 +10,10 @@ export interface ServeOptions extends ReceiveOptions {
10
10
  allowUnsigned?: boolean;
11
11
  /** Time allowed to receive a request. Default: 15 seconds. */
12
12
  requestTimeoutMs?: number;
13
+ /**
14
+ * What answers a request that is not a call — the REST door of the fronds this process hosts.
15
+ * `undefined` from it, or no `beside` at all, is a 404.
16
+ */
17
+ beside?: (request: Request) => Promise<Response | undefined>;
13
18
  }
14
19
  //# sourceMappingURL=ServeOptions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ServeOptions.d.ts","sourceRoot":"","sources":["../../src/serve/ServeOptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,YAAa,SAAQ,cAAc;IAClD,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B"}
1
+ {"version":3,"file":"ServeOptions.d.ts","sourceRoot":"","sources":["../../src/serve/ServeOptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,WAAW,YAAa,SAAQ,cAAc;IAClD,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,4EAA4E;IAC5E,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,8DAA8D;IAC9D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B;;;OAGG;IACH,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAAC;CAC9D"}
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- /** Receiving half — unframe the call, run it, frame what comes out. */
2
- import { type SignedCall, type Transport } from '@fougere/core/contract';
1
+ /** Receiving half — unframe the call, run it, frame what comes out: a door, in JSON-RPC terms. */
2
+ import { type Answering, type Door, type InvocationContext, type SignedCall, type Transport } from '@fougere/core/contract';
3
3
  import { type RpcResponse } from './jsonrpc/RpcResponse.js';
4
4
  /** What a receiver does with the caller's envelope. */
5
5
  export interface ReceiveOptions {
@@ -12,6 +12,37 @@ export interface ReceiveOptions {
12
12
  * Refuse a call carrying no verifiable identity — the whole of "secure by default" at the wire.
13
13
  */
14
14
  requireIdentity?: boolean;
15
+ /** The app's cycle, which watches the request; a receiver holding only a runner runs it unwatched. */
16
+ answer?: Answering;
15
17
  }
18
+ /** One call, unframed. */
19
+ export interface RpcCall {
20
+ address: string;
21
+ op: string;
22
+ invocation: InvocationContext;
23
+ }
24
+ /**
25
+ * The door a process calls another through. The caller is a peer: its state is established by
26
+ * `verify` when it signed, and the call is marked `fromAnotherProcess` so the receiver does not judge again
27
+ * what the sender's own boundary already judged.
28
+ */
29
+ export declare function peerDoor(runner: Transport, options?: ReceiveOptions): Door<unknown, RpcCall, RpcResponse>;
30
+ /**
31
+ * The door a page calls through — the same wire, another trust boundary. The browser sits outside
32
+ * the topology: its state is what the host resolved server-side, never what the payload claims,
33
+ * and nothing it sends is taken as already judged. Nor does it write `args`, which are taken as
34
+ * written — a peer's right — and `[id, draft, { id: 'admin' }]` would hand the handler a user no
35
+ * collector read.
36
+ */
37
+ export interface PageDoorOptions {
38
+ /** What the host resolved of the caller, server-side. */
39
+ state: Record<string, unknown>;
40
+ /** The page's own HTTP request — what a cookie-kept state arrives and leaves in, written back by the host. */
41
+ carrier?: unknown;
42
+ /** Whether an announcement calls this op — one a page may not name. */
43
+ listens?: (address: string, op: string) => boolean | Promise<boolean>;
44
+ }
45
+ export declare function pageDoor(runner: Transport, { state, carrier, listens }: PageDoorOptions): Door<unknown, RpcCall, RpcResponse>;
46
+ /** A peer's call, through the app's cycle when the receiver holds one. */
16
47
  export declare function handleRpc(runner: Transport, raw: unknown, options?: ReceiveOptions): Promise<RpcResponse>;
17
48
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,OAAO,EAAkE,KAAK,UAAU,EAAE,KAAK,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAGzI,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D,uDAAuD;AACvD,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IAClH;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,wBAAsB,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,WAAW,CAAC,CA4EnH"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,OAAO,EAC8B,KAAK,SAAS,EAAE,KAAK,IAAI,EAAE,KAAK,iBAAiB,EAAE,KAAK,UAAU,EAAE,KAAK,SAAS,EACtH,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D,uDAAuD;AACvD,MAAM,WAAW,cAAc;IAC7B,yCAAyC;IACzC,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAC,CAAC;IAClH;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,sGAAsG;IACtG,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,0BAA0B;AAC1B,MAAM,WAAW,OAAO;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,UAAU,EAAE,iBAAiB,CAAC;CAC/B;AAED;;;;GAIG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,GAAE,cAAmB,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,CA+C7G;AAED;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,8GAA8G;IAC9G,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uEAAuE;IACvE,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACvE;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,eAAe,GAAG,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,CAgC7H;AAED,0EAA0E;AAC1E,wBAAgB,SAAS,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,WAAW,CAAC,CAI7G"}
package/dist/server.js CHANGED
@@ -1,20 +1,115 @@
1
- /** Receiving half — unframe the call, run it, frame what comes out. */
2
- import { FougereError, ErrorCode, toPublicError } from '@fougere/core/contract';
1
+ /** Receiving half — unframe the call, run it, frame what comes out: a door, in JSON-RPC terms. */
2
+ import { answerAt, FougereError, ErrorCode, } from '@fougere/core/contract';
3
3
  import { APP_ERROR, INVALID_REQUEST } from './jsonrpc/RpcErrorShape.js';
4
- export async function handleRpc(runner, raw, options = {}) {
4
+ /**
5
+ * The door a process calls another through. The caller is a peer: its state is established by
6
+ * `verify` when it signed, and the call is marked `fromAnotherProcess` so the receiver does not judge again
7
+ * what the sender's own boundary already judged.
8
+ */
9
+ export function peerDoor(runner, options = {}) {
10
+ return {
11
+ name: 'rpc',
12
+ decode: async (raw) => {
13
+ const { address, op, sent } = envelopeOf(raw);
14
+ /** State is ESTABLISHED here, or it is only claimed. */
15
+ let state = sent.state ?? {};
16
+ let caller;
17
+ if (options.verify && sent.identity) {
18
+ try {
19
+ // What ARRIVED, never what we would rather it had been — the comparison is the point.
20
+ ({ caller, state } = await options.verify(sent.identity, { address, op,
21
+ params: sent.params ?? {},
22
+ query: sent.query ?? {},
23
+ input: sent.input,
24
+ args: sent.args,
25
+ runAt: sent.runAt,
26
+ }));
27
+ }
28
+ catch (err) {
29
+ throw unauthorized(err?.message ?? 'unverifiable identity', address, op);
30
+ }
31
+ }
32
+ else if (options.requireIdentity) {
33
+ throw unauthorized('this receiver takes signed calls only', address, op);
34
+ }
35
+ // Built field by field, never spread from `sent`: everything here is either validated
36
+ // above or carried deliberately, and `caller` is ours to write alone.
37
+ return {
38
+ address,
39
+ op,
40
+ invocation: {
41
+ params: sent.params ?? {},
42
+ query: sent.query ?? {},
43
+ input: sent.input,
44
+ ...(sent.args === undefined ? {} : { args: sent.args }),
45
+ state,
46
+ trace: sent.trace,
47
+ ...(sent.runAt === undefined ? {} : { runAt: sent.runAt }),
48
+ ...(caller ? { caller } : {}),
49
+ fromAnotherProcess: true,
50
+ },
51
+ };
52
+ },
53
+ run: ({ address, op, invocation }) => called(runner, address, op, invocation),
54
+ encode: framed,
55
+ };
56
+ }
57
+ export function pageDoor(runner, { state, carrier, listens }) {
58
+ return {
59
+ name: 'rpc',
60
+ ...(carrier !== undefined && { carries: 'http', carrier: () => carrier }),
61
+ decode: async (raw) => {
62
+ const { address, op, sent } = envelopeOf(raw);
63
+ if (sent.args !== undefined) {
64
+ throw new FougereError({
65
+ code: ErrorCode.BAD_REQUEST,
66
+ message: `${address}.${op}: a page sends params, query and input, not \`args\` (only fronds send \`args\`).`,
67
+ address,
68
+ operation: op,
69
+ });
70
+ }
71
+ if (await listens?.(address, op)) {
72
+ throw new FougereError({
73
+ code: ErrorCode.NOT_FOUND,
74
+ message: `${address}.${op} is called by an announcement, never by a page: call the command that announces it.`,
75
+ address,
76
+ operation: op,
77
+ });
78
+ }
79
+ return {
80
+ address,
81
+ op,
82
+ invocation: { params: sent.params ?? {}, query: sent.query ?? {}, input: sent.input, state, trace: sent.trace },
83
+ };
84
+ },
85
+ run: ({ address, op, invocation }) => called(runner, address, op, invocation),
86
+ encode: framed,
87
+ };
88
+ }
89
+ /** A peer's call, through the app's cycle when the receiver holds one. */
90
+ export function handleRpc(runner, raw, options = {}) {
91
+ const door = peerDoor(runner, options);
92
+ return options.answer ? options.answer(door, raw) : answerAt(door, raw);
93
+ }
94
+ /**
95
+ * The members a caller may send. `caller` and `fromAnotherProcess` are written by the receiver and never claimed, and
96
+ * a key outside the list is refused by name: `body` used to be dropped, and the call then ran
97
+ * with no input and no validation.
98
+ */
99
+ const SENT = {
100
+ params: true, query: true, input: true, args: true, state: true, trace: true, identity: true, runAt: true, caller: false,
101
+ fromAnotherProcess: false,
102
+ };
103
+ /** Where a refusal of the envelope itself says which JSON-RPC code it is, rather than an app's. */
104
+ const ENVELOPE = 'jsonrpc';
105
+ function envelopeOf(raw) {
5
106
  const req = raw;
6
- const id = typeof req?.id === 'number' || typeof req?.id === 'string' ? req.id : null;
7
- if (!req || req.jsonrpc !== '2.0' || typeof req.method !== 'string' || id === null) {
8
- return { jsonrpc: '2.0', id, error: { code: INVALID_REQUEST, message: 'Invalid JSON-RPC 2.0 request' } };
107
+ if (!req || req.jsonrpc !== '2.0' || typeof req.method !== 'string' || idOf(raw) === null) {
108
+ throw invalid('Invalid JSON-RPC 2.0 request');
9
109
  }
10
110
  const dot = req.method.indexOf('.');
11
- if (dot <= 0 || dot === req.method.length - 1) {
12
- return {
13
- jsonrpc: '2.0',
14
- id,
15
- error: { code: INVALID_REQUEST, message: `Invalid method '${req.method}' — expected 'address.op'` },
16
- };
17
- }
111
+ if (dot <= 0 || dot === req.method.length - 1)
112
+ throw invalid(`Invalid method '${req.method}' — expected 'address.op'`);
18
113
  const address = req.method.slice(0, dot);
19
114
  const op = req.method.slice(dot + 1);
20
115
  // Fresh objects — middlewares deposit into state, nothing may be shared.
@@ -22,81 +117,50 @@ export async function handleRpc(runner, raw, options = {}) {
22
117
  const strangers = Object.entries(sent)
23
118
  .filter(([key, value]) => value !== undefined && !SENT[key])
24
119
  .map(([key]) => key);
25
- if (strangers.length > 0)
26
- return malformed(id, strangers, address, op);
27
- /** State is ESTABLISHED here, or it is only claimed. */
28
- let state = sent.state ?? {};
29
- let caller;
30
- if (options.verify && sent.identity) {
31
- try {
32
- // What ARRIVED, never what we would rather it had been — the comparison is the point.
33
- ({ caller, state } = await options.verify(sent.identity, { address, op,
34
- params: sent.params ?? {},
35
- query: sent.query ?? {},
36
- input: sent.input,
37
- args: sent.args,
38
- runAt: sent.runAt,
39
- }));
40
- }
41
- catch (err) {
42
- return refused(id, err?.message ?? 'unverifiable identity', address, op);
43
- }
44
- }
45
- else if (options.requireIdentity) {
46
- return refused(id, 'this receiver takes signed calls only', address, op);
120
+ if (strangers.length > 0) {
121
+ const admitted = Object.keys(SENT).filter((key) => SENT[key]);
122
+ throw new FougereError({
123
+ code: ErrorCode.BAD_REQUEST,
124
+ message: `Unknown invocation member ${strangers.map((key) => `'${key}'`).join(', ')} — one of ${admitted.join(', ')}.`,
125
+ address,
126
+ operation: op,
127
+ });
47
128
  }
48
- // Built field by field, never spread from `sent`: everything here is either validated
49
- // above or carried deliberately, and `caller` is ours to write alone.
50
- const invocation = {
51
- params: sent.params ?? {},
52
- query: sent.query ?? {},
53
- input: sent.input,
54
- ...(sent.args === undefined ? {} : { args: sent.args }),
55
- state,
56
- trace: sent.trace,
57
- ...(sent.runAt === undefined ? {} : { runAt: sent.runAt }),
58
- ...(caller ? { caller } : {}),
59
- crossed: true,
60
- };
129
+ return { address, op, sent };
130
+ }
131
+ /** An accident names the op it happened in, so the receiver's log says where. */
132
+ async function called(runner, address, op, invocation) {
61
133
  try {
62
- return { jsonrpc: '2.0', id, result: await runner({ address, op }, invocation) };
134
+ return await runner({ address, op }, invocation);
63
135
  }
64
136
  catch (err) {
65
- const failure = err instanceof FougereError
66
- ? err
67
- : new FougereError({
68
- code: ErrorCode.INTERNAL_ERROR,
69
- message: err?.message ?? 'Internal error',
70
- address,
71
- operation: op,
72
- cause: err,
73
- });
74
- const data = toPublicError(failure);
75
- return { jsonrpc: '2.0', id, error: { code: APP_ERROR, message: data.message, data } };
137
+ if (err instanceof FougereError)
138
+ throw err;
139
+ throw new FougereError({
140
+ code: ErrorCode.INTERNAL_ERROR, message: err?.message ?? 'Internal error', address, operation: op, cause: err,
141
+ });
76
142
  }
77
143
  }
78
- /**
79
- * The members a caller may send. `caller` and `crossed` are written by the receiver and never claimed, and
80
- * a key outside the list is refused by name: `body` used to be dropped, and the call then ran
81
- * with no input and no validation.
82
- */
83
- const SENT = {
84
- params: true, query: true, input: true, args: true, state: true, trace: true, identity: true, runAt: true, caller: false,
85
- crossed: false,
86
- };
87
- function malformed(id, strangers, address, op) {
88
- const admitted = Object.keys(SENT).filter((key) => SENT[key]);
89
- const data = toPublicError(new FougereError({
90
- code: ErrorCode.BAD_REQUEST,
91
- message: `Unknown invocation member ${strangers.map((key) => `'${key}'`).join(', ')} — one of ${admitted.join(', ')}.`,
92
- address,
93
- operation: op,
94
- }));
144
+ function framed(outcome, raw) {
145
+ const id = idOf(raw);
146
+ if ('answered' in outcome)
147
+ return { jsonrpc: '2.0', id: id, result: outcome.answered };
148
+ const { refused } = outcome;
149
+ const envelope = refused.details?.[ENVELOPE];
150
+ if (typeof envelope === 'number')
151
+ return { jsonrpc: '2.0', id, error: { code: envelope, message: refused.message } };
152
+ const data = refused.toJSON();
95
153
  return { jsonrpc: '2.0', id, error: { code: APP_ERROR, message: data.message, data } };
96
154
  }
155
+ function idOf(raw) {
156
+ const id = raw?.id;
157
+ return typeof id === 'number' || typeof id === 'string' ? id : null;
158
+ }
159
+ function invalid(message) {
160
+ return new FougereError({ code: ErrorCode.BAD_REQUEST, message, details: { [ENVELOPE]: INVALID_REQUEST } });
161
+ }
97
162
  /** An admission refusal, framed like any other failure so a caller reads one vocabulary. */
98
- function refused(id, why, address, op) {
99
- const data = toPublicError(new FougereError({ code: ErrorCode.UNAUTHORIZED, message: `Refused: ${why}`, address, operation: op }));
100
- return { jsonrpc: '2.0', id, error: { code: APP_ERROR, message: data.message, data } };
163
+ function unauthorized(why, address, op) {
164
+ return new FougereError({ code: ErrorCode.UNAUTHORIZED, message: `Refused: ${why}`, address, operation: op });
101
165
  }
102
166
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fougere/transport-http",
3
- "version": "0.24.0-alpha.0",
3
+ "version": "0.26.0-alpha.0",
4
4
  "description": "The call contract on the wire, in JSON-RPC 2.0 (server + client).",
5
5
  "keywords": [
6
6
  "fougere",
@@ -38,13 +38,13 @@
38
38
  "src"
39
39
  ],
40
40
  "dependencies": {
41
- "@fougere/core": "0.24.0-alpha.0"
41
+ "@fougere/core": "0.26.0-alpha.0"
42
42
  },
43
43
  "devDependencies": {
44
- "@fougere/schema": "0.24.0-alpha.0",
44
+ "@fougere/schema": "0.26.0-alpha.0",
45
45
  "jiti": "^2.6.1",
46
- "@fougere/container": "0.24.0-alpha.0",
47
- "@fougere/compiler": "0.24.0-alpha.0"
46
+ "@fougere/container": "0.26.0-alpha.0",
47
+ "@fougere/compiler": "0.26.0-alpha.0"
48
48
  },
49
49
  "publishConfig": {
50
50
  "access": "public"
package/src/client.ts CHANGED
@@ -76,9 +76,9 @@ export function createHttpTransport(baseUrl: string, options: HttpTransportOptio
76
76
  // receiver choosing between a proof and a claim about the same thing.
77
77
  // `caller` is dropped on every hop: it names who signed THIS call, so carrying the
78
78
  // one this process was handed would make `shop → catalog → billing` read `shop`.
79
- // It travels outside the envelope anyway, so a stale one would be unsigned too. `crossed`
79
+ // It travels outside the envelope anyway, so a stale one would be unsigned too. `fromAnotherProcess`
80
80
  // is the next receiver's to write, for the same reason.
81
- const { caller: _established, crossed: _received, ...forwarded } = invocation;
81
+ const { caller: _established, fromAnotherProcess: _received, ...forwarded } = invocation;
82
82
  const sent = options.sign
83
83
  ? { ...forwarded, state: {}, identity: await options.sign({ ...call, ...invocation }) }
84
84
  : forwarded;
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { createHttpTransport, frameCall, unframeResponse } from './client.js';
2
- export { handleRpc } from './server.js';
2
+ export { handleRpc, pageDoor, peerDoor, type PageDoorOptions, type ReceiveOptions, type RpcCall } from './server.js';
3
3
  export { receive } from './receive.js';
4
4
  export { maxFrameBytes } from './policy.js';
5
5
  export { serve, type RunningReceiver } from './serve/RunningReceiver.js';
package/src/receive.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  */
8
8
  import { handleRpc, type ReceiveOptions } from './server.js';
9
9
  import { maxFrameBytes, CALL_PATH, parseError, tooLarge } from './policy.js';
10
- import type { Transport } from '@fougere/core/contract';
10
+ import { ErrorCode, FougereError, readWithin, type Transport } from '@fougere/core/contract';
11
11
 
12
12
  export interface ReceiveHttpOptions extends ReceiveOptions {
13
13
  /** The path this facade answers. Default: `/_fougere/call`. */
@@ -19,48 +19,6 @@ export interface ReceiveHttpOptions extends ReceiveOptions {
19
19
  const json = (body: unknown, status = 200) =>
20
20
  new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json' } });
21
21
 
22
- /** Distinguishes "the body was too large" from any body a caller could actually send. */
23
- const TOO_LARGE = Symbol('too large');
24
-
25
- /**
26
- * The declared length is a cheap refusal; the count is the real one. A `content-length` may
27
- * be absent, and it may lie — the copy that trusted it alone had no cap at all in that case.
28
- */
29
- async function bodyWithin(request: Request, max: number): Promise<string | typeof TOO_LARGE> {
30
- const declared = Number(request.headers.get('content-length'));
31
- if (Number.isFinite(declared) && declared > max) return TOO_LARGE;
32
-
33
- const reader = request.body?.getReader();
34
- if (!reader) return '';
35
-
36
- const chunks: Uint8Array[] = [];
37
- let size = 0;
38
- for (;;) {
39
- const { done, value } = await reader.read();
40
- if (done) break;
41
- size += value.byteLength;
42
- if (size > max) {
43
- await reader.cancel();
44
-
45
- return TOO_LARGE;
46
- }
47
- chunks.push(value);
48
- }
49
-
50
- return new TextDecoder().decode(concat(chunks, size));
51
- }
52
-
53
- function concat(chunks: readonly Uint8Array[], size: number): Uint8Array {
54
- const all = new Uint8Array(size);
55
- let at = 0;
56
- for (const chunk of chunks) {
57
- all.set(chunk, at);
58
- at += chunk.byteLength;
59
- }
60
-
61
- return all;
62
- }
63
-
64
22
  export function receive(
65
23
  runner: Transport,
66
24
  options: ReceiveHttpOptions = {},
@@ -81,8 +39,14 @@ export function receive(
81
39
  return new Response(null, { status: 404 });
82
40
  }
83
41
 
84
- const raw = await bodyWithin(request, maxFrameBytes());
85
- if (raw === TOO_LARGE) return json(tooLarge(), 413);
42
+ let raw: string;
43
+ try {
44
+ raw = await readWithin(request.body, maxFrameBytes(), request.headers.get('content-length'));
45
+ } catch (refused) {
46
+ if (!(refused instanceof FougereError && refused.code === ErrorCode.PAYLOAD_TOO_LARGE)) throw refused;
47
+
48
+ return json(tooLarge(), 413);
49
+ }
86
50
 
87
51
  let parsed: unknown;
88
52
  try {
@@ -1,5 +1,6 @@
1
1
  import { createServer, type IncomingMessage, type Server, type ServerResponse } from 'node:http';
2
- import type { Transport } from '@fougere/core';
2
+ import { Readable } from 'node:stream';
3
+ import { ErrorCode, FougereError, readWithin, type Transport } from '@fougere/core';
3
4
  import type { RpcResponse } from '../jsonrpc/RpcResponse.js';
4
5
  import { handleRpc } from '../server.js';
5
6
  import { maxFrameBytes, CALL_PATH, parseError, tooLarge } from '../policy.js';
@@ -52,28 +53,30 @@ function whyNotHere(host: string | undefined, options: ServeOptions): Error | un
52
53
  function answering(runner: Transport, options: ServeOptions) {
53
54
  return async (req: IncomingMessage, res: ServerResponse): Promise<void> => {
54
55
  if (req.method !== 'POST' || req.url !== CALL_PATH) {
55
- res.writeHead(404).end();
56
+ const answered = options.beside && await options.beside(webRequestOf(req));
57
+ if (!answered) {
58
+ res.writeHead(404).end();
59
+
60
+ return;
61
+ }
62
+ res.writeHead(answered.status, Object.fromEntries(answered.headers)).end(Buffer.from(await answered.arrayBuffer()));
56
63
 
57
64
  return;
58
65
  }
59
66
 
60
- const maxBodyBytes = maxFrameBytes();
61
- const chunks: Buffer[] = [];
62
- let size = 0;
63
- for await (const value of req) {
64
- const chunk = Buffer.from(value as Uint8Array);
65
- size += chunk.length;
66
- if (size > maxBodyBytes) {
67
- res.writeHead(413, { 'content-type': 'application/json' }).end(JSON.stringify(tooLarge()));
67
+ let raw: string;
68
+ try {
69
+ raw = await readWithin(req, maxFrameBytes(), req.headers['content-length']);
70
+ } catch (refused) {
71
+ if (!(refused instanceof FougereError && refused.code === ErrorCode.PAYLOAD_TOO_LARGE)) throw refused;
72
+ res.writeHead(413, { 'content-type': 'application/json' }).end(JSON.stringify(tooLarge()));
68
73
 
69
- return;
70
- }
71
- chunks.push(chunk);
74
+ return;
72
75
  }
73
76
 
74
77
  let response: RpcResponse;
75
78
  try {
76
- response = await handleRpc(runner, JSON.parse(Buffer.concat(chunks).toString('utf8')), options);
79
+ response = await handleRpc(runner, JSON.parse(raw), options);
77
80
  } catch {
78
81
  response = parseError();
79
82
  }
@@ -82,6 +85,22 @@ function answering(runner: Transport, options: ServeOptions) {
82
85
  };
83
86
  }
84
87
 
88
+ /** A Node request as a Web one, its body still a stream — built only for what is not a call. */
89
+ function webRequestOf(req: IncomingMessage): Request {
90
+ const headers = new Headers();
91
+ for (const [name, value] of Object.entries(req.headers)) {
92
+ if (value !== undefined) headers.set(name, Array.isArray(value) ? value.join(', ') : value);
93
+ }
94
+ // A message carries a body when it says so — a length, or chunks — and a POST with neither has none.
95
+ const reads = Number(req.headers['content-length']) > 0 || req.headers['transfer-encoding'] !== undefined;
96
+
97
+ return new Request(`http://${req.headers.host ?? 'localhost'}${req.url ?? '/'}`, {
98
+ method: req.method,
99
+ headers,
100
+ ...(reads && { body: Readable.toWeb(req) as ReadableStream<Uint8Array>, duplex: 'half' }),
101
+ } as RequestInit);
102
+ }
103
+
85
104
  /** The port a receiver ended up on — asked of the server, since 0 means "whatever is free". */
86
105
  function listening(server: Server, host: string, options: ServeOptions): Promise<RunningReceiver> {
87
106
  return new Promise((resolve, reject) => {
@@ -11,4 +11,9 @@ export interface ServeOptions extends ReceiveOptions {
11
11
  allowUnsigned?: boolean;
12
12
  /** Time allowed to receive a request. Default: 15 seconds. */
13
13
  requestTimeoutMs?: number;
14
+ /**
15
+ * What answers a request that is not a call — the REST door of the fronds this process hosts.
16
+ * `undefined` from it, or no `beside` at all, is a 404.
17
+ */
18
+ beside?: (request: Request) => Promise<Response | undefined>;
14
19
  }
package/src/server.ts CHANGED
@@ -1,5 +1,7 @@
1
- /** Receiving half — unframe the call, run it, frame what comes out. */
2
- import { FougereError, ErrorCode, toPublicError, type InvocationContext, type SignedCall, type Transport } from '@fougere/core/contract';
1
+ /** Receiving half — unframe the call, run it, frame what comes out: a door, in JSON-RPC terms. */
2
+ import {
3
+ answerAt, FougereError, ErrorCode, type Answering, type Door, type InvocationContext, type SignedCall, type Transport,
4
+ } from '@fougere/core/contract';
3
5
  import { APP_ERROR, INVALID_REQUEST } from './jsonrpc/RpcErrorShape.js';
4
6
  import { type RpcRequest } from './jsonrpc/RpcRequest.js';
5
7
  import { type RpcResponse } from './jsonrpc/RpcResponse.js';
@@ -12,24 +14,149 @@ export interface ReceiveOptions {
12
14
  * Refuse a call carrying no verifiable identity — the whole of "secure by default" at the wire.
13
15
  */
14
16
  requireIdentity?: boolean;
17
+ /** The app's cycle, which watches the request; a receiver holding only a runner runs it unwatched. */
18
+ answer?: Answering;
15
19
  }
16
20
 
17
- export async function handleRpc(runner: Transport, raw: unknown, options: ReceiveOptions = {}): Promise<RpcResponse> {
18
- const req = raw as Partial<RpcRequest> | null;
19
- const id = typeof req?.id === 'number' || typeof req?.id === 'string' ? req.id : null;
21
+ /** One call, unframed. */
22
+ export interface RpcCall {
23
+ address: string;
24
+ op: string;
25
+ invocation: InvocationContext;
26
+ }
27
+
28
+ /**
29
+ * The door a process calls another through. The caller is a peer: its state is established by
30
+ * `verify` when it signed, and the call is marked `fromAnotherProcess` so the receiver does not judge again
31
+ * what the sender's own boundary already judged.
32
+ */
33
+ export function peerDoor(runner: Transport, options: ReceiveOptions = {}): Door<unknown, RpcCall, RpcResponse> {
34
+ return {
35
+ name: 'rpc',
36
+ decode: async (raw) => {
37
+ const { address, op, sent } = envelopeOf(raw);
38
+
39
+ /** State is ESTABLISHED here, or it is only claimed. */
40
+ let state: Record<string, unknown> = sent.state ?? {};
41
+ let caller: string | undefined;
42
+ if (options.verify && sent.identity) {
43
+ try {
44
+ // What ARRIVED, never what we would rather it had been — the comparison is the point.
45
+ ({ caller, state } = await options.verify(sent.identity, { address, op,
46
+ params: sent.params ?? {},
47
+ query: sent.query ?? {},
48
+ input: sent.input,
49
+ args: sent.args,
50
+ runAt: sent.runAt,
51
+ }));
52
+ } catch (err) {
53
+ throw unauthorized((err as Error)?.message ?? 'unverifiable identity', address, op);
54
+ }
55
+ } else if (options.requireIdentity) {
56
+ throw unauthorized('this receiver takes signed calls only', address, op);
57
+ }
20
58
 
21
- if (!req || req.jsonrpc !== '2.0' || typeof req.method !== 'string' || id === null) {
22
- return { jsonrpc: '2.0', id, error: { code: INVALID_REQUEST, message: 'Invalid JSON-RPC 2.0 request' } };
59
+ // Built field by field, never spread from `sent`: everything here is either validated
60
+ // above or carried deliberately, and `caller` is ours to write alone.
61
+ return {
62
+ address,
63
+ op,
64
+ invocation: {
65
+ params: sent.params ?? {},
66
+ query: sent.query ?? {},
67
+ input: sent.input,
68
+ ...(sent.args === undefined ? {} : { args: sent.args }),
69
+ state,
70
+ trace: sent.trace,
71
+ ...(sent.runAt === undefined ? {} : { runAt: sent.runAt }),
72
+ ...(caller ? { caller } : {}),
73
+ fromAnotherProcess: true,
74
+ },
75
+ };
76
+ },
77
+ run: ({ address, op, invocation }) => called(runner, address, op, invocation),
78
+ encode: framed,
79
+ };
80
+ }
81
+
82
+ /**
83
+ * The door a page calls through — the same wire, another trust boundary. The browser sits outside
84
+ * the topology: its state is what the host resolved server-side, never what the payload claims,
85
+ * and nothing it sends is taken as already judged. Nor does it write `args`, which are taken as
86
+ * written — a peer's right — and `[id, draft, { id: 'admin' }]` would hand the handler a user no
87
+ * collector read.
88
+ */
89
+ export interface PageDoorOptions {
90
+ /** What the host resolved of the caller, server-side. */
91
+ state: Record<string, unknown>;
92
+ /** The page's own HTTP request — what a cookie-kept state arrives and leaves in, written back by the host. */
93
+ carrier?: unknown;
94
+ /** Whether an announcement calls this op — one a page may not name. */
95
+ listens?: (address: string, op: string) => boolean | Promise<boolean>;
96
+ }
97
+
98
+ export function pageDoor(runner: Transport, { state, carrier, listens }: PageDoorOptions): Door<unknown, RpcCall, RpcResponse> {
99
+ return {
100
+ name: 'rpc',
101
+ ...(carrier !== undefined && { carries: 'http', carrier: () => carrier }),
102
+ decode: async (raw) => {
103
+ const { address, op, sent } = envelopeOf(raw);
104
+ if (sent.args !== undefined) {
105
+ throw new FougereError({
106
+ code: ErrorCode.BAD_REQUEST,
107
+ message: `${address}.${op}: a page sends params, query and input, not \`args\` (only fronds send \`args\`).`,
108
+ address,
109
+ operation: op,
110
+ });
111
+ }
112
+ if (await listens?.(address, op)) {
113
+ throw new FougereError({
114
+ code: ErrorCode.NOT_FOUND,
115
+ message: `${address}.${op} is called by an announcement, never by a page: call the command that announces it.`,
116
+ address,
117
+ operation: op,
118
+ });
119
+ }
120
+
121
+ return {
122
+ address,
123
+ op,
124
+ invocation: { params: sent.params ?? {}, query: sent.query ?? {}, input: sent.input, state, trace: sent.trace },
125
+ };
126
+ },
127
+ run: ({ address, op, invocation }) => called(runner, address, op, invocation),
128
+ encode: framed,
129
+ };
130
+ }
131
+
132
+ /** A peer's call, through the app's cycle when the receiver holds one. */
133
+ export function handleRpc(runner: Transport, raw: unknown, options: ReceiveOptions = {}): Promise<RpcResponse> {
134
+ const door = peerDoor(runner, options);
135
+
136
+ return options.answer ? options.answer(door, raw) : answerAt(door, raw);
137
+ }
138
+
139
+ /**
140
+ * The members a caller may send. `caller` and `fromAnotherProcess` are written by the receiver and never claimed, and
141
+ * a key outside the list is refused by name: `body` used to be dropped, and the call then ran
142
+ * with no input and no validation.
143
+ */
144
+ const SENT = {
145
+ params: true, query: true, input: true, args: true, state: true, trace: true, identity: true, runAt: true, caller: false,
146
+ fromAnotherProcess: false,
147
+ } satisfies Record<keyof InvocationContext, boolean>;
148
+
149
+ /** Where a refusal of the envelope itself says which JSON-RPC code it is, rather than an app's. */
150
+ const ENVELOPE = 'jsonrpc';
151
+
152
+ function envelopeOf(raw: unknown): { address: string; op: string; sent: Partial<InvocationContext> } {
153
+ const req = raw as Partial<RpcRequest> | null;
154
+ if (!req || req.jsonrpc !== '2.0' || typeof req.method !== 'string' || idOf(raw) === null) {
155
+ throw invalid('Invalid JSON-RPC 2.0 request');
23
156
  }
24
157
 
25
158
  const dot = req.method.indexOf('.');
26
- if (dot <= 0 || dot === req.method.length - 1) {
27
- return {
28
- jsonrpc: '2.0',
29
- id,
30
- error: { code: INVALID_REQUEST, message: `Invalid method '${req.method}' — expected 'address.op'` },
31
- };
32
- }
159
+ if (dot <= 0 || dot === req.method.length - 1) throw invalid(`Invalid method '${req.method}' — expected 'address.op'`);
33
160
  const address = req.method.slice(0, dot);
34
161
  const op = req.method.slice(dot + 1);
35
162
 
@@ -38,87 +165,54 @@ export async function handleRpc(runner: Transport, raw: unknown, options: Receiv
38
165
  const strangers = Object.entries(sent)
39
166
  .filter(([key, value]) => value !== undefined && !SENT[key as keyof InvocationContext])
40
167
  .map(([key]) => key);
41
- if (strangers.length > 0) return malformed(id, strangers, address, op);
42
-
43
- /** State is ESTABLISHED here, or it is only claimed. */
44
- let state: Record<string, unknown> = sent.state ?? {};
45
- let caller: string | undefined;
46
- if (options.verify && sent.identity) {
47
- try {
48
- // What ARRIVED, never what we would rather it had been — the comparison is the point.
49
- ({ caller, state } = await options.verify(sent.identity, { address, op,
50
- params: sent.params ?? {},
51
- query: sent.query ?? {},
52
- input: sent.input,
53
- args: sent.args,
54
- runAt: sent.runAt,
55
- }));
56
- } catch (err) {
57
- return refused(id, (err as Error)?.message ?? 'unverifiable identity', address, op);
58
- }
59
- } else if (options.requireIdentity) {
60
- return refused(id, 'this receiver takes signed calls only', address, op);
168
+ if (strangers.length > 0) {
169
+ const admitted = Object.keys(SENT).filter((key) => SENT[key as keyof InvocationContext]);
170
+ throw new FougereError({
171
+ code: ErrorCode.BAD_REQUEST,
172
+ message: `Unknown invocation member ${strangers.map((key) => `'${key}'`).join(', ')} — one of ${admitted.join(', ')}.`,
173
+ address,
174
+ operation: op,
175
+ });
61
176
  }
62
177
 
63
- // Built field by field, never spread from `sent`: everything here is either validated
64
- // above or carried deliberately, and `caller` is ours to write alone.
65
- const invocation: InvocationContext = {
66
- params: sent.params ?? {},
67
- query: sent.query ?? {},
68
- input: sent.input,
69
- ...(sent.args === undefined ? {} : { args: sent.args }),
70
- state,
71
- trace: sent.trace,
72
- ...(sent.runAt === undefined ? {} : { runAt: sent.runAt }),
73
- ...(caller ? { caller } : {}),
74
- crossed: true,
75
- };
178
+ return { address, op, sent };
179
+ }
76
180
 
181
+ /** An accident names the op it happened in, so the receiver's log says where. */
182
+ async function called(runner: Transport, address: string, op: string, invocation: InvocationContext): Promise<unknown> {
77
183
  try {
78
- return { jsonrpc: '2.0', id, result: await runner({ address, op }, invocation) };
184
+ return await runner({ address, op }, invocation);
79
185
  } catch (err) {
80
- const failure = err instanceof FougereError
81
- ? err
82
- : new FougereError({
83
- code: ErrorCode.INTERNAL_ERROR,
84
- message: (err as Error)?.message ?? 'Internal error',
85
- address,
86
- operation: op,
87
- cause: err,
88
- });
89
- const data = toPublicError(failure);
90
-
91
- return { jsonrpc: '2.0', id, error: { code: APP_ERROR, message: data.message, data } };
186
+ if (err instanceof FougereError) throw err;
187
+ throw new FougereError({
188
+ code: ErrorCode.INTERNAL_ERROR, message: (err as Error)?.message ?? 'Internal error', address, operation: op, cause: err,
189
+ });
92
190
  }
93
191
  }
94
192
 
95
- /**
96
- * The members a caller may send. `caller` and `crossed` are written by the receiver and never claimed, and
97
- * a key outside the list is refused by name: `body` used to be dropped, and the call then ran
98
- * with no input and no validation.
99
- */
100
- const SENT = {
101
- params: true, query: true, input: true, args: true, state: true, trace: true, identity: true, runAt: true, caller: false,
102
- crossed: false,
103
- } satisfies Record<keyof InvocationContext, boolean>;
193
+ function framed(outcome: { answered: unknown } | { refused: FougereError }, raw: unknown): RpcResponse {
194
+ const id = idOf(raw);
195
+ if ('answered' in outcome) return { jsonrpc: '2.0', id: id!, result: outcome.answered };
104
196
 
105
- function malformed(id: string | number, strangers: string[], address: string, op: string): RpcResponse {
106
- const admitted = Object.keys(SENT).filter((key) => SENT[key as keyof InvocationContext]);
107
- const data = toPublicError(new FougereError({
108
- code: ErrorCode.BAD_REQUEST,
109
- message: `Unknown invocation member ${strangers.map((key) => `'${key}'`).join(', ')} — one of ${admitted.join(', ')}.`,
110
- address,
111
- operation: op,
112
- }));
197
+ const { refused } = outcome;
198
+ const envelope = (refused.details as Record<string, unknown> | undefined)?.[ENVELOPE];
199
+ if (typeof envelope === 'number') return { jsonrpc: '2.0', id, error: { code: envelope, message: refused.message } };
200
+ const data = refused.toJSON();
113
201
 
114
202
  return { jsonrpc: '2.0', id, error: { code: APP_ERROR, message: data.message, data } };
115
203
  }
116
204
 
117
- /** An admission refusal, framed like any other failure so a caller reads one vocabulary. */
118
- function refused(id: string | number, why: string, address: string, op: string): RpcResponse {
119
- const data = toPublicError(
120
- new FougereError({ code: ErrorCode.UNAUTHORIZED, message: `Refused: ${why}`, address, operation: op }),
121
- );
205
+ function idOf(raw: unknown): string | number | null {
206
+ const id = (raw as Partial<RpcRequest> | null)?.id;
122
207
 
123
- return { jsonrpc: '2.0', id, error: { code: APP_ERROR, message: data.message, data } };
208
+ return typeof id === 'number' || typeof id === 'string' ? id : null;
209
+ }
210
+
211
+ function invalid(message: string): FougereError {
212
+ return new FougereError({ code: ErrorCode.BAD_REQUEST, message, details: { [ENVELOPE]: INVALID_REQUEST } });
213
+ }
214
+
215
+ /** An admission refusal, framed like any other failure so a caller reads one vocabulary. */
216
+ function unauthorized(why: string, address: string, op: string): FougereError {
217
+ return new FougereError({ code: ErrorCode.UNAUTHORIZED, message: `Refused: ${why}`, address, operation: op });
124
218
  }