@burojs/hasura 0.2.0 → 0.5.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/README.md +15 -16
- package/dist/index.cjs +912 -772
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +594 -285
- package/dist/index.d.ts +594 -285
- package/dist/index.js +899 -770
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/endpoint.ts","../src/live-provider.ts","../src/data-provider.ts"],"names":["createClient","normalizeDataError","isDataError","token","query","data","defineDataProvider"],"mappings":";;;;;;;;AAyBO,IAAM,uBAAA,GAA0B;ACuDhC,SAAS,YAAY,QAAA,EAA0B;AAEpD,EAAA,IAAI,OAAO,MAAA,KAAW,WAAA,EAAa,OAAO,QAAA;AAC1C,EAAA,MAAM,MAAM,IAAI,GAAA,CAAI,QAAA,EAAU,MAAA,CAAO,SAAS,MAAM,CAAA;AACpD,EAAA,GAAA,CAAI,QAAA,GAAW,GAAA,CAAI,QAAA,KAAa,QAAA,GAAW,MAAA,GAAS,KAAA;AACpD,EAAA,OAAO,IAAI,QAAA,EAAS;AACtB;AAEA,SAAS,iBAAiB,OAAA,EAA+C;AACvE,EAAA,MAAM,QAAQ,OAAA,CAAQ,WAAA;AACtB,EAAA,OAAO;AAAA,IACL,OAAO,KAAA,GACH,CAAA,kBAAA,EAAqB,QAAQ,KAAK,CAAA,cAAA,EAAiB,QAAQ,KAAK,CAAA;AAAA,UAAA,EAC5D,QAAQ,KAAK,CAAA;AAAA,SAAA,CAAA,GAEjB,CAAA,kBAAA,EAAqB,QAAQ,KAAK,CAAA;AAAA,UAAA,EAC9B,QAAQ,KAAK,CAAA;AAAA,SAAA,CAAA;AAAA,IAErB,GAAI,UAAU,MAAA,GAAY,KAAK,EAAE,SAAA,EAAW,EAAE,KAAA,EAAM;AAAE,GACxD;AACF;AAEA,SAAS,eAAA,CACP,SACA,EAAA,EAC4B;AAC5B,EAAA,MAAM,OAAA,GAAU,QAAQ,OAAA,IAAW,IAAA;AACnC,EAAA,MAAM,MAAA,GAAS,QAAQ,MAAA,IAAU,QAAA;AACjC,EAAA,MAAM,IAAA,GAAO,QAAQ,QAAA,KAAa,KAAA,GAAQ,OAAQ,OAAA,CAAQ,QAAA,IAAY,CAAA,EAAG,OAAA,CAAQ,KAAK,CAAA,MAAA,CAAA;AACtF,EAAA,IAAI,IAAA,KAAS,MAAM,OAAO,IAAA;AAC1B,EAAA,OAAO;AAAA,IACL,KAAA,EAAO,CAAA,kBAAA,EAAqB,OAAA,CAAQ,KAAK,aAAa,MAAM,CAAA;AAAA,MAAA,EACxD,IAAI,CAAA,CAAA,EAAI,OAAO,CAAA,SAAA,EAAY,OAAO,CAAA;AAAA,KAAA,CAAA;AAAA,IAEtC,SAAA,EAAW,EAAE,EAAA;AAAG,GAClB;AACF;AAOO,SAAS,yBAAyB,MAAA,EAAoD;AAC3F,EAAA,MAAM,SAAA,uBAAgB,GAAA,EAA4C;AAClE,EAAA,MAAM,QAAA,GAAW,OAAO,QAAA,IAAY,uBAAA;AACpC,EAAA,MAAM,IAAA,GAAO,OAAO,IAAA,IAAQ,IAAA;AAC5B,EAAA,IAAI,MAAA;AAEJ,EAAA,MAAM,MAAA,GAAS,CAAC,KAAA,KAAgC;AAC9C,IAAA,KAAA,MAAW,QAAA,IAAY,SAAA,EAAW,QAAA,CAAS,KAAK,CAAA;AAAA,EAClD,CAAA;AAEA,EAAA,SAAS,SAAA,GAA6B;AACpC,IAAA,IAAI,MAAA,KAAW,QAAW,OAAO,MAAA;AACjC,IAAA,MAAM,OAAA,GAAkC;AAAA,MACtC,GAAA,EAAK,YAAY,QAAQ,CAAA;AAAA,MACzB,IAAA,EAAM,IAAA;AAAA,MACN,SAAA,EAAW,GAAA;AAAA,MACX,eAAe,MAAA,CAAO,iBAAA;AAAA,MACtB,aAAa,MAAM,IAAA;AAAA,MACnB,kBAAkB,YAAY;AAC5B,QAAA,MAAM,KAAA,GAAQ,MAAM,MAAA,CAAO,QAAA,EAAS;AACpC,QAAA,OAAO;AAAA,UACL,OAAA,EAAS;AAAA,YACP,GAAI,QAAQ,EAAE,aAAA,EAAe,UAAU,KAAK,CAAA,CAAA,KAAO,EAAC;AAAA,YACpD,GAAI,IAAA,GAAO,EAAE,eAAA,EAAiB,IAAA,KAAS;AAAC;AAC1C,SACF;AAAA,MACF,CAAA;AAAA,MACA,EAAA,EAAI;AAAA,QACF,UAAA,EAAY,MAAM,MAAA,CAAO,YAAY,CAAA;AAAA,QACrC,SAAA,EAAW,MAAM,MAAA,CAAO,WAAW,CAAA;AAAA,QACnC,MAAA,EAAQ,MAAM,MAAA,CAAO,UAAU;AAAA;AACjC,KACF;AACA,IAAA,MAAA,GACE,MAAA,CAAO,aAAA,GAAgB,OAAO,CAAA,IAC7BA,uBAAa,OAAgB,CAAA;AAChC,IAAA,OAAO,MAAA;AAAA,EACT;AAEA,EAAA,OAAO;AAAA,IACL,MAAM,IAAA,CAAK,UAAA,EAAY,WAAA,EAAa;AAClC,MAAA,IAAI,WAAA,CAAY,MAAA,CAAO,OAAA,EAAS,OAAO,IAAA;AACvC,MAAA,MAAM,OAAA,GAAU,MAAA,CAAO,SAAA,CAAU,UAAA,CAAW,QAAQ,CAAA;AACpD,MAAA,IAAI,OAAA,EAAS,IAAA,KAAS,IAAA,EAAM,OAAO,IAAA;AACnC,MAAA,MAAM,OAAA,GACJ,UAAA,CAAW,IAAA,KAAS,MAAA,GAChB,iBAAiB,OAAO,CAAA,GACxB,UAAA,CAAW,IAAA,KAAS,KAAA,GAClB,eAAA,CAAgB,OAAA,EAAS,UAAA,CAAW,EAAE,CAAA,GACtC,IAAA;AACR,MAAA,IAAI,OAAA,KAAY,MAAM,OAAO,IAAA;AAE7B,MAAA,OAAO;AAAA,QACL,IAAA,EAAM,YAAA;AAAA,QACN,OAAA,EAAS,OAAA;AAAA,QACT,SAAA,CAAU,UAAwB,gBAAA,EAAkB;AAClD,UAAA,IAAI,YAAY,MAAA,CAAO,OAAA,IAAW,iBAAiB,MAAA,CAAO,OAAA,SAAgB,MAAM,MAAA;AAChF,UAAA,IAAI,MAAA,GAAS,KAAA;AACb,UAAA,IAAI,MAAA,GAAS,KAAA;AACb,UAAA,IAAI,QAAA,GAAqC,MAAA;AAEzC,UAAA,MAAM,QAAQ,MAAM;AAClB,YAAA,IAAI,MAAA,EAAQ;AACZ,YAAA,MAAA,GAAS,IAAA;AACT,YAAA,SAAA,CAAU,OAAO,QAAQ,CAAA;AACzB,YAAA,WAAA,CAAY,MAAA,CAAO,mBAAA,CAAoB,OAAA,EAAS,KAAK,CAAA;AACrD,YAAA,gBAAA,CAAiB,MAAA,CAAO,mBAAA,CAAoB,OAAA,EAAS,KAAK,CAAA;AAC1D,YAAA,QAAA,IAAW;AAAA,UACb,CAAA;AACA,UAAA,MAAM,QAAA,GAAW,CAAC,KAAA,KAAgC;AAChD,YAAA,IAAI,CAAC,MAAA,EAAQ,QAAA,CAAS,MAAA,CAAO,KAAK,CAAA;AAAA,UACpC,CAAA;AACA,UAAA,MAAM,IAAA,GAAyB;AAAA,YAC7B,MAAM,MAAM;AACV,cAAA,IAAI,MAAA,EAAQ;AAIZ,cAAA,IAAI,CAAC,MAAA,EAAQ;AACX,gBAAA,MAAA,GAAS,IAAA;AACT,gBAAA;AAAA,cACF;AACA,cAAA,QAAA,CAAS,KAAK,MAAS,CAAA;AAAA,YACzB,CAAA;AAAA,YACA,KAAA,EAAO,CAAC,KAAA,KAAU;AAChB,cAAA,IAAI,MAAA,EAAQ;AACZ,cAAA,QAAA,CAAS,KAAA,CAAMC,uBAAA,CAAmB,KAAK,CAAC,CAAA;AAIxC,cAAA,KAAA,EAAM;AAAA,YACR,CAAA;AAAA,YACA,UAAU,MAAM;AAGd,cAAA,IAAI,CAAC,MAAA,EAAQ,QAAA,CAAS,MAAA,CAAO,UAAU,CAAA;AAAA,YACzC;AAAA,WACF;AAEA,UAAA,SAAA,CAAU,IAAI,QAAQ,CAAA;AACtB,UAAA,WAAA,CAAY,OAAO,gBAAA,CAAiB,OAAA,EAAS,OAAO,EAAE,IAAA,EAAM,MAAM,CAAA;AAClE,UAAA,gBAAA,CAAiB,OAAO,gBAAA,CAAiB,OAAA,EAAS,OAAO,EAAE,IAAA,EAAM,MAAM,CAAA;AACvE,UAAA,QAAA,GAAW,SAAA,EAAU,CAAE,SAAA,CAAU,OAAA,EAAS,IAAI,CAAA;AAe9C,UAAA,IAAI,QAAQ,QAAA,EAAS;AACrB,UAAA,IAAI,YAAY,MAAA,CAAO,OAAA,IAAW,gBAAA,CAAiB,MAAA,CAAO,SAAS,KAAA,EAAM;AAGzE,UAAA,OAAO,OAAA,CAAQ,QAAQ,KAAK,CAAA;AAAA,QAC9B;AAAA,OACF;AAAA,IACF;AAAA,GACF;AACF;;;ACmBA,SAAS,aAAA,CAAc,GAAA,EAA8B,IAAA,EAAc,KAAA,EAAsB;AACvF,EAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA;AAC5B,EAAA,MAAM,IAAA,GAAO,MAAM,GAAA,EAAI;AAGvB,EAAA,IAAI,SAAS,MAAA,EAAW;AACxB,EAAA,IAAI,GAAA,GAAM,GAAA;AACV,EAAA,KAAA,MAAW,KAAK,KAAA,EAAO;AACrB,IAAA,MAAM,IAAA,GAAO,IAAI,CAAC,CAAA;AAClB,IAAA,IAAI,OAAO,SAAS,QAAA,IAAY,IAAA,KAAS,MAAM,GAAA,CAAI,CAAC,IAAI,EAAC;AACzD,IAAA,GAAA,GAAM,IAAI,CAAC,CAAA;AAAA,EACb;AACA,EAAA,GAAA,CAAI,IAAI,CAAA,GAAI,KAAA;AACd;AAGA,SAAS,aAAA,CAAc,KAA8B,IAAA,EAAuB;AAC1E,EAAA,IAAI,GAAA,GAAe,GAAA;AACnB,EAAA,KAAA,MAAW,CAAA,IAAK,IAAA,CAAK,KAAA,CAAM,GAAG,CAAA,EAAG;AAC/B,IAAA,IAAI,GAAA,IAAO,IAAA,IAAQ,OAAO,GAAA,KAAQ,UAAU,OAAO,MAAA;AACnD,IAAA,GAAA,GAAO,IAAgC,CAAC,CAAA;AAAA,EAC1C;AACA,EAAA,OAAO,GAAA;AACT;AAgCO,SAAS,iBAAA,CACd,QACA,GAAA,EAC4F;AAC5F,EAAA,MAAM,MAAA,GAAS,IAAI,GAAA,CAAI,GAAA,CAAI,IAAI,CAAA;AAC/B,EAAA,MAAM,WAAoC,EAAC;AAC3C,EAAA,MAAM,OAAgC,EAAC;AACvC,EAAA,IAAI,WAAA,GAAc,KAAA;AAClB,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AACjD,IAAA,IAAI,GAAA,KAAQ,IAAI,YAAA,EAAc;AAC9B,IAAA,IAAI,MAAA,CAAO,GAAA,CAAI,GAAG,CAAA,EAAG;AACnB,MAAA,IAAI,UAAU,MAAA,EAAW;AACvB,QAAA,QAAA,CAAS,GAAG,CAAA,GAAI,KAAA;AAChB,QAAA,WAAA,GAAc,IAAA;AAAA,MAChB;AAAA,IACF,CAAA,MAAO;AACL,MAAA,IAAA,CAAK,GAAG,CAAA,GAAI,KAAA;AAAA,IACd;AAAA,EACF;AACA,EAAA,OAAO,EAAE,QAAA,EAAU,IAAA,EAAM,WAAA,EAAY;AACvC;AAqBO,SAAS,mBAAA,CACd,MAAA,EACA,KAAA,EACA,WAAA,EACyB;AACzB,EAAA,MAAM,OAAA,GAAU,IAAI,GAAA,CAAI,WAAW,CAAA;AACnC,EAAA,MAAM,OAAA,GAAU,IAAI,GAAA,CAAI,KAAA,CAAM,QAAQ,CAAA;AACtC,EAAA,MAAM,UAAA,GAAa,KAAA,CAAM,UAAA,IAAc,EAAC;AACxC,EAAA,MAAM,OAAgC,EAAC;AACvC,EAAA,MAAM,eAAwC,EAAC;AAC/C,EAAA,MAAM,eAAwC,EAAC;AAC/C,EAAA,MAAM,YAAqC,EAAC;AAC5C,EAAA,MAAM,YAAqC,EAAC;AAC5C,EAAA,MAAM,cAAwC,EAAC;AAE/C,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AACjD,IAAA,IAAI,UAAU,MAAA,EAAW;AACzB,IAAA,MAAM,UAAA,GAAa,WAAW,GAAG,CAAA;AACjC,IAAA,IAAI,GAAA,KAAQ,MAAM,MAAA,EAAQ;AACxB,MAAA,IAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA;AAC5B,QAAA,MAAA,CAAO,MAAA,CAAO,cAAc,KAAgC,CAAA;AAAA,IAChE,CAAA,MAAA,IAAW,GAAA,KAAQ,KAAA,CAAM,UAAA,EAAY;AACnC,MAAA,IAAI,KAAA,IAAS,OAAO,KAAA,KAAU,QAAA;AAC5B,QAAA,MAAA,CAAO,MAAA,CAAO,cAAc,KAAgC,CAAA;AAAA,IAChE,CAAA,MAAA,IAAW,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA,EAAG;AAC3B,MAAA,SAAA,CAAU,GAAG,CAAA,GAAI,KAAA;AAAA,IACnB,WAAW,UAAA,EAAY;AACrB,MAAA,WAAA,CAAY,IAAA,CAAK,CAAC,UAAA,EAAY,KAAK,CAAC,CAAA;AAAA,IACtC,CAAA,MAAA,IAAW,OAAA,CAAQ,GAAA,CAAI,GAAG,CAAA,EAAG;AAC3B,MAAA,IAAA,CAAK,GAAG,CAAA,GAAI,KAAA;AAAA,IACd,CAAA,MAAO;AACL,MAAA,SAAA,CAAU,GAAG,CAAA,GAAI,KAAA;AAAA,IACnB;AAAA,EACF;AAIA,EAAA,MAAM,IAAA,GAAO,EAAE,GAAG,YAAA,EAAc,GAAG,SAAA,EAAU;AAE7C,EAAA,KAAA,MAAW,CAAC,MAAM,KAAK,CAAA,IAAK,aAAa,aAAA,CAAc,IAAA,EAAM,MAAM,KAAK,CAAA;AACxE,EAAA,MAAM,IAAA,GAAO,EAAE,GAAG,YAAA,EAAc,GAAG,SAAA,EAAU;AAC7C,EAAA,MAAM,GAAA,GAA+B,EAAE,GAAG,IAAA,EAAK;AAC/C,EAAA,IAAI,MAAA,CAAO,KAAK,IAAI,CAAA,CAAE,SAAS,CAAA,EAAG,GAAA,CAAI,KAAA,CAAM,MAAM,CAAA,GAAI,IAAA;AACtD,EAAA,IAAI,MAAA,CAAO,KAAK,IAAI,CAAA,CAAE,SAAS,CAAA,EAAG,GAAA,CAAI,KAAA,CAAM,UAAU,CAAA,GAAI,IAAA;AAC1D,EAAA,OAAO,GAAA;AACT;AAaO,SAAS,iBAAA,CAAkB,MAAe,KAAA,EAAkC;AACjF,EAAA,IAAI,CAAC,IAAA,IAAQ,OAAO,IAAA,KAAS,UAAU,OAAO,IAAA;AAC9C,EAAA,MAAM,GAAA,GAAM,IAAA;AACZ,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,KAAA,CAAM,MAAM,CAAA;AAChC,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,KAAA,CAAM,UAAU,CAAA;AACpC,EAAA,MAAM,GAAA,GAA+B;AAAA,IACnC,GAAI,OAAA,IAAW,OAAO,OAAA,KAAY,QAAA,GAAY,UAAsC,EAAC;AAAA,IACrF,GAAI,OAAA,IAAW,OAAO,OAAA,KAAY,QAAA,GAAY,UAAsC,EAAC;AAAA,IACrF,GAAG;AAAA,GACL;AAEA,EAAA,IAAI,KAAA,CAAM,UAAA,IAAc,OAAA,IAAW,OAAO,YAAY,QAAA,EAAU;AAC9D,IAAA,KAAA,MAAW,CAAC,SAAS,IAAI,CAAA,IAAK,OAAO,OAAA,CAAQ,KAAA,CAAM,UAAU,CAAA,EAAG;AAC9D,MAAA,MAAM,CAAA,GAAI,aAAA,CAAc,OAAA,EAAoC,IAAI,CAAA;AAChE,MAAA,IAAI,CAAA,KAAM,MAAA,EAAW,GAAA,CAAI,OAAO,CAAA,GAAI,CAAA;AAAA,IACtC;AAAA,EACF;AACA,EAAA,OAAO,GAAA;AACT;AAiBO,IAAM,sBAAA,GAAyB;AAYtC,IAAM,MAAA,GAAiC;AAAA,EACrC,EAAA,EAAI,KAAA;AAAA,EACJ,GAAA,EAAK,MAAA;AAAA,EACL,EAAA,EAAI,MAAA;AAAA,EACJ,EAAA,EAAI,KAAA;AAAA,EACJ,GAAA,EAAK,MAAA;AAAA,EACL,EAAA,EAAI,KAAA;AAAA,EACJ,GAAA,EAAK,MAAA;AAAA,EACL,EAAA,EAAI,KAAA;AAAA,EACJ,KAAA,EAAO,MAAA;AAAA,EACP,GAAA,EAAK,MAAA;AAAA,EACL,QAAA,EAAU,QAAA;AAAA,EACV,SAAA,EAAW,OAAA;AAAA,EACX,UAAA,EAAY,QAAA;AAAA,EACZ,QAAA,EAAU,QAAA;AAAA,EACV,UAAA,EAAY,QAAA;AAAA,EACZ,QAAA,EAAU,QAAA;AAAA,EACV,IAAA,EAAM;AACR,CAAA;AAQO,IAAM,gCAAA,GAAmC;AAwChD,IAAM,iBAAA,GAAsD;AAAA,EAC1D,GAAA,EAAK,8BAAA;AAAA,EACL,MAAA,EAAQ,8BAAA;AAAA,EACR,MAAA,EAAQ;AACV,CAAA;AAEA,SAAS,mBAAA,CAAoB,OAAe,EAAA,EAAuB;AACjE,EAAA,MAAM,GAAA,GAAM,kBAAkB,EAAE,CAAA;AAChC,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,YAAA;AAAA,IACN,IAAA,EAAM,gCAAA;AAAA,IACN,OAAA,EAAS,GAAA,GACL,CAAA,mCAAA,EAAsC,EAAE,CAAA,sBAAA,EAAyB,KAAK,CAAA,GAAA,EAAM,GAAG,CAAA,yMAAA,CAAA,GAC/E,CAAA,qDAAA,EAAwD,EAAE,CAAA,OAAA,EAAU,KAAK,CAAA,GAAA;AAAA,GAC/E;AACF;AAOO,SAAS,YAAA,CACd,OACA,WAAA,EACqC;AACrC,EAAA,MAAM,UAA0C,EAAC;AACjD,EAAA,KAAA,MAAW,OAAO,WAAA,EAAa;AAC7B,IAAA,MAAM,CAAA,GAAI,MAAM,GAAG,CAAA;AACnB,IAAA,IAAI,CAAA,IAAK,IAAA,IAAQ,CAAA,KAAM,EAAA,EAAI;AAC3B,IAAA,OAAA,CAAQ,IAAA,CAAK,EAAE,CAAC,GAAG,GAAG,EAAE,GAAA,EAAK,CAAA,EAAE,EAAG,CAAA;AAAA,EACpC;AACA,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG,OAAO,MAAA;AACjC,EAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG,OAAO,QAAQ,CAAC,CAAA;AAC1C,EAAA,OAAO,EAAE,MAAM,OAAA,EAAQ;AACzB;AAKO,SAAS,UAAA,CACd,GACA,CAAA,EACqC;AACrC,EAAA,IAAI,CAAC,GAAG,OAAO,CAAA;AACf,EAAA,IAAI,CAAC,GAAG,OAAO,CAAA;AACf,EAAA,OAAO,EAAE,IAAA,EAAM,CAAC,CAAA,EAAG,CAAC,CAAA,EAAE;AACxB;AAGO,SAAS,eACd,MAAA,EACqC;AACrC,EAAA,IAAI,CAAC,MAAA,IAAU,OAAO,MAAA,KAAW,UAAU,OAAO,MAAA;AAiClD,EAAA,IAAI,SAAS,MAAA,EAAQ;AACnB,IAAA,IAAI,MAAA,CAAO,IAAI,MAAA,KAAW,CAAA,SAAU,EAAE,IAAA,EAAM,EAAC,EAAE;AAC/C,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,GAAA,CACpB,GAAA,CAAI,cAAc,EAClB,MAAA,CAAO,CAAC,IAAA,KAA0C,IAAA,KAAS,MAAS,CAAA;AACvE,IAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG,OAAO,MAAA;AACjC,IAAA,OAAO,OAAA,CAAQ,WAAW,CAAA,GAAI,OAAA,CAAQ,CAAC,CAAA,GAAI,EAAE,MAAM,OAAA,EAAQ;AAAA,EAC7D;AACA,EAAA,IAAI,QAAQ,MAAA,EAAQ;AAClB,IAAA,IAAI,MAAA,CAAO,GAAG,MAAA,KAAW,CAAA,SAAU,EAAE,GAAA,EAAK,EAAC,EAAE;AAC7C,IAAA,MAAM,OAAA,GAAU,MAAA,CAAO,EAAA,CACpB,GAAA,CAAI,cAAc,EAClB,MAAA,CAAO,CAAC,IAAA,KAA0C,IAAA,KAAS,MAAS,CAAA;AACvE,IAAA,IAAI,OAAA,CAAQ,MAAA,KAAW,CAAA,EAAG,OAAO,MAAA;AACjC,IAAA,OAAO,OAAA,CAAQ,WAAW,CAAA,GAAI,OAAA,CAAQ,CAAC,CAAA,GAAI,EAAE,KAAK,OAAA,EAAQ;AAAA,EAC5D;AACA,EAAA,IAAI,SAAS,MAAA,EAAQ;AACnB,IAAA,MAAM,MAAA,GAAS,cAAA,CAAe,MAAA,CAAO,GAAG,CAAA;AACxC,IAAA,OAAO,MAAA,KAAW,MAAA,GAAY,MAAA,GAAY,EAAE,MAAM,MAAA,EAAO;AAAA,EAC3D;AACA,EAAA,IAAI,EAAE,WAAW,MAAA,CAAA,IAAW,OAAO,OAAO,KAAA,KAAU,QAAA,IAAY,EAAE,IAAA,IAAQ,MAAA,CAAA;AACxE,IAAA,OAAO,MAAA;AAET,EAAA,MAAM,EAAE,KAAA,EAAO,EAAA,EAAG,GAAI,MAAA;AACtB,EAAA,IAAI,EAAA,KAAO,QAAA,EAAU,OAAO,EAAE,CAAC,KAAK,GAAG,EAAE,QAAA,EAAU,IAAA,EAAK,EAAE;AAC1D,EAAA,IAAI,EAAA,KAAO,WAAA,EAAa,OAAO,EAAE,CAAC,KAAK,GAAG,EAAE,QAAA,EAAU,KAAA,EAAM,EAAE;AAgB9D,EAAA,IAAI,EAAE,OAAA,IAAW,MAAA,CAAA,IAAW,MAAA,CAAO,KAAA,KAAU,QAAW,OAAO,MAAA;AAiB/D,EAAA,MAAM,QAAA,GAAW,OAAO,EAAE,CAAA;AAC1B,EAAA,IAAI,QAAA,KAAa,MAAA,EAAW,MAAM,mBAAA,CAAoB,OAAO,EAAE,CAAA;AAC/D,EAAA,IAAI,QAAiB,MAAA,CAAO,KAAA;AAC5B,EAAA,IAAI,QAAA,KAAa,QAAA,IAAY,QAAA,KAAa,OAAA,EAAS;AACjD,IAAA,MAAM,IAAA,GAAO,OAAO,KAAK,CAAA;AACzB,IAAA,KAAA,GAAQ,EAAA,KAAO,YAAA,GAAe,CAAA,EAAG,IAAI,CAAA,CAAA,CAAA,GAAM,EAAA,KAAO,UAAA,GAAa,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,GAAK,CAAA,CAAA,EAAI,IAAI,CAAA,CAAA,CAAA;AAAA,EACtF;AACA,EAAA,OAAO,EAAE,CAAC,KAAK,GAAG,EAAE,CAAC,QAAQ,GAAG,KAAA,EAAM,EAAE;AAC1C;AAEA,SAAS,iBAAA,CACP,QACA,MAAA,EACoD;AACpD,EAAA,IAAI,CAAC,MAAA,IAAU,CAAC,MAAA,EAAQ,QAAQ,OAAO,MAAA;AACvC,EAAA,IAAI,KAAA,IAAS,MAAA;AACX,IAAA,OAAO;AAAA,MACL,GAAA,EAAK,OAAO,GAAA,CAAI,GAAA;AAAA,QACd,CAAC,KAAA,KAAU,iBAAA,CAAkB,KAAA,EAAO,MAAM;AAAA;AAC5C,KACF;AACF,EAAA,IAAI,IAAA,IAAQ,MAAA;AACV,IAAA,OAAO;AAAA,MACL,EAAA,EAAI,OAAO,EAAA,CAAG,GAAA;AAAA,QACZ,CAAC,KAAA,KAAU,iBAAA,CAAkB,KAAA,EAAO,MAAM;AAAA;AAC5C,KACF;AACF,EAAA,IAAI,KAAA,IAAS,MAAA;AACX,IAAA,OAAO,EAAE,GAAA,EAAK,iBAAA,CAAkB,MAAA,CAAO,GAAA,EAAK,MAAM,CAAA,EAA4C;AAChG,EAAA,IACE,OAAA,IAAW,MAAA,IACX,MAAA,CAAO,QAAA,CAAS,MAAA,CAAO,KAAK,CAAA,IAC5B,OAAA,IAAW,MAAA,IACX,MAAA,CAAO,KAAA,IAAS,IAAA,EAChB;AACA,IAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,OAAA,CAAQ,MAAA,CAAO,KAAK,CAAA,GAAI,MAAA,CAAO,KAAA,CAAM,GAAA,CAAI,MAAM,CAAA,GAAI,MAAA,CAAO,OAAO,KAAK,CAAA;AAC1F,IAAA,OAAO,EAAE,GAAG,MAAA,EAAQ,KAAA,EAAM;AAAA,EAC5B;AACA,EAAA,OAAO,MAAA;AACT;AAEA,SAAS,WAAA,CAAY,KAAA,EAAgB,MAAA,EAAqB,QAAA,EAA6B;AACrF,EAAA,IAAIC,gBAAA,CAAY,KAAK,CAAA,EAAG,OAAO,KAAA;AAC/B,EAAA,IAAI,MAAA,CAAO,OAAA;AACT,IAAA,OAAO;AAAA,MACL,IAAA,EAAM,WAAA;AAAA,MACN,IAAA,EAAM,iBAAA;AAAA,MACN,OAAA,EAAS,6BAAA;AAAA,MACT,KAAA,EAAO;AAAA,KACT;AACF,EAAA,OAAO;AAAA,IACL,IAAA,EAAM,WAAA;AAAA,IACN,IAAA,EAAM,uBAAA;AAAA,IACN,SAAS,KAAA,YAAiB,KAAA,IAAS,KAAA,CAAM,OAAA,GAAU,MAAM,OAAA,GAAU,QAAA;AAAA,IACnE,KAAA,EAAO;AAAA,GACT;AACF;AASO,SAAS,cAAc,CAAA,EAAmC;AAC/D,EAAA,IAAI,CAAA,CAAE,SAAA,KAAc,KAAA,EAAO,OAAO,IAAA;AAClC,EAAA,OAAO,OAAO,EAAE,SAAA,KAAc,QAAA,GAAW,EAAE,SAAA,GAAY,CAAA,EAAG,EAAE,KAAK,CAAA,UAAA,CAAA;AACnE;AAOO,SAAS,SAAS,CAAA,EAAmC;AAC1D,EAAA,IAAI,CAAA,CAAE,QAAA,KAAa,KAAA,EAAO,OAAO,IAAA;AACjC,EAAA,OAAO,CAAA,CAAE,QAAA,IAAY,CAAA,EAAG,CAAA,CAAE,KAAK,CAAA,MAAA,CAAA;AACjC;AAEA,SAAS,0BACP,MAAA,EAC2E;AAC3E,EAAA,IAAI,WAAW,MAAA,EAAW,OAAO,MAAA,CAAO,MAAA,CAAO,EAAE,CAAA;AACjD,EAAA,MAAM,YAA6E,EAAC;AACpF,EAAA,KAAA,MAAW,CAAC,QAAA,EAAU,UAAU,KAAK,MAAA,CAAO,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC3D,IAAA,MAAM,UAAiD,EAAC;AACxD,IAAA,KAAA,MAAW,CAAC,IAAA,EAAM,SAAS,KAAK,MAAA,CAAO,OAAA,CAAQ,UAAU,CAAA,EAAG;AAC1D,MAAA,OAAA,CAAQ,IAAI,CAAA,GAAI,MAAA,CAAO,OAAO,EAAE,GAAG,WAAW,CAAA;AAAA,IAChD;AACA,IAAA,SAAA,CAAU,QAAQ,CAAA,GAAI,MAAA,CAAO,MAAA,CAAO,OAAO,CAAA;AAAA,EAC7C;AACA,EAAA,OAAO,MAAA,CAAO,OAAO,SAAS,CAAA;AAChC;AAEA,SAAS,0BAAA,CACP,YACA,IAAA,EACS;AACT,EAAA,OAAO,MAAA,CAAO,MAAA,CAAO,UAAU,CAAA,CAAE,IAAA;AAAA,IAAK,CAAC,MAAA,KACrC,MAAA,CAAO,MAAA,CAAO,MAAM,CAAA,CAAE,IAAA,CAAK,CAAC,SAAA,KAAc,SAAA,CAAU,IAAA,KAAS,IAAI;AAAA,GACnE;AACF;AAmBA,SAAS,cAAc,IAAA,EAAgD;AACrE,EAAA,OAAO,SAAS,MAAA,IAAa,IAAA,KAAS,IAAA,IAAQ,IAAA,KAAS,KAAK,IAAA,GAAO,IAAA;AACrE;AAEO,SAAS,yBAAyB,MAAA,EAAgD;AACvF,EAAA,MAAM,QAAA,GAAW,OAAO,QAAA,IAAY,uBAAA;AACpC,EAAA,MAAM,WAAA,GAAc,aAAA,CAAc,MAAA,CAAO,WAAW,CAAA;AACpD,EAAA,MAAM,UAAA,GAAa,yBAAA,CAA0B,MAAA,CAAO,UAAU,CAAA;AAC9D,EAAA,MAAM,gBAAA,GAAmB,0BAAA,CAA2B,UAAA,EAAY,OAAO,CAAA;AACvE,EAAA,MAAM,kBAAA,GAAqB,0BAAA,CAA2B,UAAA,EAAY,UAAU,CAAA;AAE5E,EAAA,eAAe,GAAA,CACb,KAAA,EACA,SAAA,EACA,IAAA,EACA,MAAA,EACY;AACZ,IAAA,IAAI;AACF,MAAA,MAAM,OAAA,GAAU,OAAOC,MAAAA,KAAkB;AACvC,QAAA,MAAM,OAAA,GAAkC;AAAA,UACtC,cAAA,EAAgB,kBAAA;AAAA,UAChB,aAAA,EAAe,UAAUA,MAAK,CAAA;AAAA,SAChC;AAQA,QAAA,MAAM,aAAA,GAAgB,IAAA,KAAS,KAAA,CAAA,GAAY,WAAA,GAAc,cAAc,IAAI,CAAA;AAC3E,QAAA,IAAI,aAAA,KAAkB,IAAA,EAAM,OAAA,CAAQ,eAAe,CAAA,GAAI,aAAA;AAYvD,QAAA,MAAM,SAAA,GAAY,OAAO,SAAA,IAAa,KAAA;AACtC,QAAA,OAAO,UAAU,QAAA,EAAU;AAAA,UACzB,MAAA,EAAQ,MAAA;AAAA,UACR,OAAA;AAAA,UACA,MAAM,IAAA,CAAK,SAAA,CAAU,EAAE,KAAA,EAAO,WAAW,CAAA;AAAA,UACzC;AAAA,SACD,CAAA;AAAA,MACH,CAAA;AAEA,MAAA,IAAI,KAAA,GAAQ,MAAM,MAAA,CAAO,QAAA,EAAS;AAClC,MAAA,IAAI,CAAC,KAAA;AACH,QAAA,MAAM;AAAA,UACJ,IAAA,EAAM,MAAA;AAAA,UACN,IAAA,EAAM,sBAAA;AAAA,UACN,OAAA,EAAS;AAAA,SACX;AACF,MAAA,IAAI,GAAA,GAAM,MAAM,OAAA,CAAQ,KAAK,CAAA;AAC7B,MAAA,IAAI,GAAA,CAAI,WAAW,GAAA,IAAO,MAAA,CAAO,eAAgB,MAAM,MAAA,CAAO,aAAY,EAAI;AAC5E,QAAA,KAAA,GAAQ,MAAM,OAAO,QAAA,EAAS;AAC9B,QAAA,IAAI,CAAC,KAAA;AACH,UAAA,MAAM;AAAA,YACJ,IAAA,EAAM,MAAA;AAAA,YACN,IAAA,EAAM,sBAAA;AAAA,YACN,OAAA,EAAS;AAAA,WACX;AACF,QAAA,GAAA,GAAM,MAAM,QAAQ,KAAK,CAAA;AAAA,MAC3B;AACA,MAAA,IAAI,CAAC,GAAA,CAAI,EAAA;AACP,QAAA,MAAM;AAAA,UACJ,IAAA,EAAM,WAAA;AAAA,UACN,IAAA,EAAM,oBAAA;AAAA,UACN,OAAA,EAAS,CAAA,aAAA,EAAgB,GAAA,CAAI,MAAM,CAAA,CAAA;AAAA,UACnC,QAAQ,GAAA,CAAI;AAAA,SACd;AACF,MAAA,MAAM,IAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAC7B,MAAA,IAAI,KAAK,MAAA,EAAQ,MAAA;AACf,QAAA,MAAM;AAAA,UACJ,IAAA,EAAM,UAAA;AAAA,UACN,IAAA,EAAM,eAAA;AAAA,UACN,OAAA,EAAS,IAAA,CAAK,MAAA,CAAO,GAAA,CAAI,CAAC,MAAM,CAAA,CAAE,OAAO,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA;AAAA,UACpD,SAAS,IAAA,CAAK;AAAA,SAChB;AACF,MAAA,IAAI,KAAK,IAAA,KAAS,KAAA,CAAA;AAChB,QAAA,MAAM;AAAA,UACJ,IAAA,EAAM,UAAA;AAAA,UACN,IAAA,EAAM,wBAAA;AAAA,UACN,OAAA,EAAS;AAAA,SACX;AACF,MAAA,OAAO,IAAA,CAAK,IAAA;AAAA,IACd,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,WAAA,CAAY,KAAA,EAAO,MAAA,EAAQ,wBAAwB,CAAA;AAAA,IAC3D;AAAA,EACF;AAEA,EAAA,SAAS,QAAQ,QAAA,EAAmC;AAClD,IAAA,MAAM,CAAA,GAAI,MAAA,CAAO,SAAA,CAAU,QAAQ,CAAA;AACnC,IAAA,IAAI,CAAC,CAAA;AACH,MAAA,MAAM;AAAA,QACJ,IAAA,EAAM,eAAA;AAAA,QACN,IAAA,EAAM,kBAAA;AAAA,QACN,OAAA,EAAS,qBAAqB,QAAQ,CAAA;AAAA,OACxC;AACF,IAAA,OAAO,CAAA;AAAA,EACT;AAEA,EAAA,SAAS,mBAAA,CACP,QAAA,EACA,SAAA,EACA,IAAA,EACuB;AACvB,IAAA,MAAM,UAAA,GAAa,UAAA,CAAW,QAAQ,CAAA,GAAI,SAAS,CAAA;AACnD,IAAA,IAAI,UAAA,KAAe,MAAA,IAAa,UAAA,CAAW,IAAA,KAAS,IAAA,EAAM;AACxD,MAAA,MAAM;AAAA,QACJ,IAAA,EAAM,YAAA;AAAA,QACN,IAAA,EAAM,qCAAA;AAAA,QACN,SAAS,CAAA,OAAA,EAAU,IAAI,CAAA,YAAA,EAAe,SAAS,0BAA0B,QAAQ,CAAA,EAAA;AAAA,OACnF;AAAA,IACF;AACA,IAAA,OAAO,UAAA;AAAA,EACT;AAEA,EAAA,eAAe,sBAAA,CACb,QAAA,EACA,aAAA,EACA,KAAA,EACA,MACA,MAAA,EACkB;AAClB,IAAA,MAAM,SAAA,GAAY,mBAAA,CAAoB,QAAA,EAAU,aAAA,EAAe,IAAI,CAAA;AAGnE,IAAA,MAAM,SAAS,MAAM,GAAA;AAAA,MACnB,SAAA,CAAU,QAAA;AAAA,MACV,SAAA,CAAU,UAAU,KAAK,CAAA;AAAA,MACzB,SAAA,CAAU,IAAA,IAAQ,MAAA,CAAO,SAAA,CAAU,QAAQ,CAAA,EAAG,IAAA;AAAA,MAC9C;AAAA,KACF;AACA,IAAA,OAAO,SAAA,CAAU,SAAA,GAAY,MAAM,CAAA,IAAK,MAAA;AAAA,EAC1C;AAoBA,EAAA,SAAS,gBAAA,CACP,CAAA,EACA,UAAA,EACA,OAAA,EACqC;AACrC,IAAA,MAAM,YAAY,cAAA,CAAe,iBAAA,CAAkB,WAAW,MAAA,EAAQ,CAAA,CAAE,WAAW,CAAC,CAAA;AAMpF,IAAA,MAAM,WAAA,GAAc,UAAA,CAAW,eAAA,EAAiB,WAAA,KAAgB,IAAA;AAChE,IAAA,MAAM,YAAA,GACJ,QAAQ,OAAA,CAAQ,QAAA,KAAa,QAC7B,OAAO,OAAA,CAAQ,QAAQ,QAAA,KAAa,QAAA,IACpC,CAAC,KAAA,CAAM,OAAA,CAAQ,QAAQ,OAAA,CAAQ,QAAQ,IAClC,OAAA,CAAQ,OAAA,CAAQ,WACjB,EAAC;AACP,IAAA,MAAM,KAAA,GAAQ,WAAA,GAAc,EAAC,GAAI,EAAE,GAAG,YAAA,EAAc,GAAI,MAAA,CAAO,QAAA,GAAW,OAAO,CAAA,IAAK,EAAC,EAAG;AAC1F,IAAA,MAAM,aAAa,YAAA,CAAa,KAAA,EAAO,CAAA,CAAE,WAAA,IAAe,EAAE,CAAA;AAE1D,IAAA,OAAO,WAAW,UAAA,CAAW,SAAA,EAAW,UAAU,CAAA,EAAG,EAAE,WAAW,CAAA;AAAA,EACpE;AAQA,EAAA,eAAe,iBAAA,CACb,CAAA,EACA,IAAA,EACA,KAAA,EACA,MAAA,EACiB;AACjB,IAAA,MAAM,WAAA,GAAc,KAAA,GAAQ,WAAA,GAAc,CAAA,CAAE,QAAQ,YAAA,GAAe,EAAA;AACnE,IAAA,MAAM,aAAA,GAAgB,QAAQ,iBAAA,GAAoB,EAAA;AAClD,IAAA,MAAM,SAAA,GAAY,CAAA,YAAA,EAAe,CAAA,CAAE,KAAK,IAAI,WAAW,CAAA;AAAA,YAAA,EAC7C,IAAI,IAAI,aAAa,CAAA;AAAA,KAAA,CAAA;AAE/B,IAAA,MAAM,UAAmC,EAAC;AAC1C,IAAA,IAAI,KAAA,EAAO,OAAA,CAAQ,OAAO,CAAA,GAAI,KAAA;AAC9B,IAAA,MAAM,WAAW,MAAM,GAAA;AAAA,MACrB,SAAA;AAAA,MACA,QAAQ,OAAA,GAAU,MAAA;AAAA,MAClB,CAAA,CAAE,IAAA;AAAA,MACF;AAAA,KACF;AACA,IAAA,OAAO,QAAA,CAAS,KAAK,SAAA,CAAU,KAAA;AAAA,EACjC;AASA,EAAA,eAAe,cAAA,CACb,EAAA,EACA,QAAA,EACA,IAAA,EACA,MACA,MAAA,EACe;AACf,IAAA,MAAM,QAAA,GAAW,CAAA,iCAAA,EAAoC,EAAA,CAAG,KAAK,CAAA;AAAA,qBAAA,EAC1C,GAAG,KAAK,CAAA;AAAA;AAAA,mCAAA,EAEM,EAAA,CAAG,UAAU,CAAA,mBAAA,EAAsB,EAAA,CAAG,MAAM,CAAA;AAAA,UAAA,EACrE,GAAG,EAAE,CAAA;AAAA,KAAA,CAAA;AAEb,IAAA,MAAM,IAAI,QAAA,EAAU,EAAE,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,QAAA,EAAU,CAAC,GAAG,MAAM,GAAG,MAAK,EAAE,EAAG,MAAM,MAAM,CAAA;AAAA,EACxF;AAEA,EAAA,SAAS,cAAA,CACP,CAAA,EACA,OAAA,EACA,IAAA,EACA,UACA,UAAA,EACA;AACA,IAAA,IAAI,IAAA,KAAS,QAAQ,OAAO,IAAA,KAAS,YAAY,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA,EAAG;AACpE,MAAA,MAAM;AAAA,QACJ,IAAA,EAAM,UAAA;AAAA,QACN,IAAA,EAAM,kBAAA;AAAA,QACN,OAAA,EAAS,cAAc,QAAQ,CAAA;AAAA,OACjC;AAAA,IACF;AACA,IAAA,MAAM,MAAA,GAAS,IAAA;AACf,IAAA,MAAM,EAAA,GAAK,MAAA,CAAO,CAAA,CAAE,OAAA,IAAW,OAAO,CAAA;AACtC,IAAA,IAAI,OAAO,EAAA,KAAO,QAAA,KAAa,OAAO,EAAA,KAAO,YAAY,CAAC,MAAA,CAAO,QAAA,CAAS,EAAE,CAAA,CAAA,EAAI;AAC9E,MAAA,MAAM;AAAA,QACJ,IAAA,EAAM,UAAA;AAAA,QACN,IAAA,EAAM,4BAAA;AAAA,QACN,SAAS,CAAA,cAAA,EAAiB,QAAQ,CAAA,qBAAA,EAAwB,CAAA,CAAE,WAAW,OAAO,CAAA,EAAA;AAAA,OAChF;AAAA,IACF;AACA,IAAA,IAAI,UAAA,KAAe,MAAA,IAAa,EAAA,KAAO,UAAA,EAAY;AACjD,MAAA,MAAM;AAAA,QACJ,IAAA,EAAM,UAAA;AAAA,QACN,IAAA,EAAM,sBAAA;AAAA,QACN,OAAA,EAAS,CAAA,cAAA,EAAiB,QAAQ,CAAA,YAAA,EAAe,MAAA,CAAO,EAAE,CAAC,CAAA,cAAA,EAAiB,MAAA,CAAO,UAAU,CAAC,CAAA,EAAA;AAAA,OAChG;AAAA,IACF;AAKA,IAAA,OAAO,EAAE,IAAA,EAAM,QAAA,EAAmB,IAAI,MAAA,EAAQ,IAAA,EAAM,cAAc,SAAA,EAAmB;AAAA,EACvF;AAEA,EAAA,eAAe,kBAAA,CACb,CAAA,EACA,EAAA,EACA,MAAA,EACkB;AAClB,IAAA,MAAM,MAAA,GAAS,CAAA,CAAE,YAAA,CAAa,IAAA,CAAK,GAAG,CAAA;AACtC,IAAA,MAAM,MAAA,GAAS,EAAE,MAAA,IAAU,QAAA;AAC3B,IAAA,MAAM,OAAA,GAAU,EAAE,OAAA,IAAW,IAAA;AAC7B,IAAA,MAAM,IAAA,GAAO,SAAS,CAAC,CAAA;AACvB,IAAA,IAAI,CAAA,CAAE,WAAA,IAAe,IAAA,KAAS,IAAA,EAAM;AAClC,MAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,EAAE,CAAC,OAAO,GAAG,EAAE,GAAA,EAAK,EAAA,EAAG,EAAE,EAAG,CAAA,CAAE,WAAW,CAAA;AAClE,MAAA,MAAMC,SAAQ,CAAA,UAAA,EAAa,CAAA,CAAE,KAAK,CAAA,SAAA,EAAY,EAAE,KAAK,CAAA;AAAA,eAAA,EAC1C,CAAA,CAAE,KAAK,CAAA,4BAAA,EAA+B,MAAM,CAAA;AAAA,OAAA,CAAA;AAEvD,MAAA,MAAMC,KAAAA,GAAO,MAAM,GAAA,CAA0BD,MAAAA,EAAO,EAAE,KAAA,EAAM,EAAG,CAAA,CAAE,IAAA,EAAM,MAAM,CAAA;AAC7E,MAAA,OAAOC,KAAAA,CAAK,KAAA,GAAQ,CAAC,CAAA,IAAK,IAAA;AAAA,IAC5B;AAYA,IAAA,MAAM,KAAA,GAAQ,CAAA,UAAA,EAAa,CAAA,CAAE,KAAK,SAAS,MAAM,CAAA;AAAA,YAAA,EACvC,IAAI,CAAA,CAAA,EAAI,OAAO,CAAA,SAAA,EAAY,MAAM,CAAA;AAAA,KAAA,CAAA;AAE3C,IAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAuB,KAAA,EAAO,EAAE,EAAA,EAAG,EAAG,CAAA,CAAE,IAAA,EAAM,MAAM,CAAA;AACvE,IAAA,OAAO,IAAA,CAAK,IAAA;AAAA,EACd;AAEA,EAAA,OAAOC,uBAAA,CAAmB;AAAA,IACxB,OAAA,EAAS;AAAA,MACP,MAAM,IAAA,CAAK,UAAA,EAAY,OAAA,EAAS;AAC9B,QAAA,MAAM,CAAA,GAAI,OAAA,CAAQ,UAAA,CAAW,QAAQ,CAAA;AACrC,QAAA,IAAI,UAAA,CAAW,UAAA,EAAY,IAAA,KAAS,QAAA,EAAU;AAC5C,UAAA,MAAM;AAAA,YACJ,IAAA,EAAM,YAAA;AAAA,YACN,IAAA,EAAM,+BAAA;AAAA,YACN,OAAA,EAAS;AAAA,WACX;AAAA,QACF;AACA,QAAA,MAAM,QACJ,UAAA,CAAW,UAAA,EAAY,SAAS,QAAA,GAAW,UAAA,CAAW,WAAW,QAAA,GAAW,EAAA;AAC9E,QAAA,MAAM,OAAO,UAAA,CAAW,UAAA,EAAY,SAAS,QAAA,GAAW,UAAA,CAAW,WAAW,IAAA,GAAO,CAAA;AACrF,QAAA,MAAM,MAAA,GAAA,CAAU,OAAO,CAAA,IAAK,KAAA;AAK5B,QAAA,MAAM,KAAA,GAAQ,gBAAA,CAAiB,CAAA,EAAG,UAAA,EAAY,OAAO,CAAA;AAKrD,QAAA,MAAM,UAAU,UAAA,CAAW,IAAA,EAAM,OAAO,CAAC,CAAA,KAAM,EAAE,KAAK,CAAA;AACtD,QAAA,IAAI,WAAA;AACJ,QAAA,IAAI,OAAA,IAAW,OAAA,CAAQ,MAAA,GAAS,CAAA,EAAG;AAGjC,UAAA,WAAA,GAAc,CAAA,CAAA,EAAI,OAAA,CAAQ,GAAA,CAAI,CAAC,MAAM,CAAA,CAAA,EAAI,CAAA,CAAE,KAAK,CAAA,EAAA,EAAK,EAAE,SAAS,CAAA,CAAA,CAAG,CAAA,CAAE,IAAA,CAAK,IAAI,CAAC,CAAA,CAAA,CAAA;AAAA,QACjF,CAAA,MAAO;AACL,UAAA,MAAM,YAAA,GAAe,EAAE,YAAA,IAAgB,IAAA;AACvC,UAAA,WAAA,GAAc,IAAI,YAAY,CAAA,OAAA,CAAA;AAAA,QAChC;AAEA,QAAA,MAAM,MAAA,GAAS,CAAA,CAAE,YAAA,CAAa,IAAA,CAAK,GAAG,CAAA;AACtC,QAAA,MAAM,QAAA,GAAW,KAAA,GAAQ,YAAA,GAAe,CAAA,CAAE,QAAQ,WAAA,GAAc,EAAA;AAChE,QAAA,MAAM,UAAA,GAAa,QAAQ,iBAAA,GAAoB,EAAA;AAC/C,QAAA,MAAM,UAAA,GAAa,CAAA,WAAA,EAAc,CAAA,CAAE,KAAK,6BAA6B,QAAQ,CAAA;AAAA,eAAA,EACpE,EAAE,KAAK,CAAA,2CAAA,EAA8C,WAAW,CAAA,EAAG,UAAU,OAAO,MAAM,CAAA;AAAA,OAAA,CAAA;AAEnG,QAAA,MAAM,SAAA,GAAqC,EAAE,KAAA,EAAO,MAAA,EAAO;AAC3D,QAAA,IAAI,KAAA,EAAO,SAAA,CAAU,OAAO,CAAA,GAAI,KAAA;AAChC,QAAA,MAAM,YAAY,MAAM,GAAA;AAAA,UACtB,UAAA;AAAA,UACA,SAAA;AAAA,UACA,CAAA,CAAE,IAAA;AAAA,UACF,OAAA,CAAQ;AAAA,SACV;AAQA,QAAA,IAAI,KAAA;AACJ,QAAA,MAAM,WAAA,GAAc,cAAc,CAAC,CAAA;AACnC,QAAA,IAAI,CAAC,CAAA,CAAE,WAAA,IAAe,WAAA,EAAa;AACjC,UAAA,IAAI;AACF,YAAA,KAAA,GAAQ,MAAM,iBAAA,CAAkB,CAAA,EAAG,WAAA,EAAa,KAAA,EAAO,QAAQ,MAAM,CAAA;AAAA,UACvE,SAAS,GAAA,EAAK;AAKZ,YAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,0BAAA,EAA6B,CAAA,CAAE,KAAK,6BAA6B,GAAG,CAAA;AAAA,UACnF;AAAA,QACF;AAKA,QAAA,MAAM,QAAQ,CAAA,CAAE,UAAA;AAChB,QAAA,MAAM,KAAA,GAAQ,KAAA,GACV,SAAA,CAAU,KAAA,CAAM,GAAA,CAAI,CAAC,EAAA,KAAO,iBAAA,CAAkB,EAAA,EAAI,KAAK,CAAC,CAAA,GACxD,SAAA,CAAU,KAAA;AACd,QAAA,OAAO,EAAE,IAAA,EAAM,KAAA,EAAO,KAAA,EAAM;AAAA,MAC9B,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAkBA,MAAM,KAAA,CAAM,UAAA,EAAY,OAAA,EAA+B;AAIrD,QAAA,MAAM,CAAA,GAAI,MAAA,CAAO,SAAA,CAAU,UAAA,CAAW,QAAQ,CAAA;AAC9C,QAAA,IAAI,CAAC,CAAA,EAAG,OAAO,EAAE,OAAO,IAAA,EAAK;AAC7B,QAAA,MAAM,IAAA,GAAO,cAAc,CAAC,CAAA;AAC5B,QAAA,IAAI,CAAC,IAAA,EAAM,OAAO,EAAE,OAAO,IAAA,EAAK;AAChC,QAAA,OAAO;AAAA,UACL,OAAO,MAAM,iBAAA;AAAA,YACX,CAAA;AAAA,YACA,IAAA;AAAA,YACA,gBAAA,CAAiB,CAAA,EAAG,UAAA,EAAY,OAAO,CAAA;AAAA,YACvC,OAAA,CAAQ;AAAA;AACV,SACF;AAAA,MACF,CAAA;AAAA,MAEA,MAAM,IAAA,CAAK,UAAA,EAAY,OAAA,EAAS;AAC9B,QAAA,MAAM,CAAA,GAAI,OAAA,CAAQ,UAAA,CAAW,QAAQ,CAAA;AACrC,QAAA,IAAI,UAAA,CAAW,IAAI,MAAA,KAAW,CAAA,SAAU,EAAE,IAAA,EAAM,EAAC,EAAE;AAEnD,QAAA,MAAM,OAAA,GAAU,EAAE,OAAA,IAAW,IAAA;AAC7B,QAAA,MAAM,QAAA,GAAW,EAAE,CAAC,OAAO,GAAG,EAAE,GAAA,EAAK,UAAA,CAAW,GAAA,EAAI,EAAE;AACtD,QAAA,MAAM,KAAA,GAAQ,UAAA;AAAA,UACZ,QAAA;AAAA,UACA,gBAAA,CAAiB,CAAA,EAAG,UAAA,EAA4C,OAAO;AAAA,SACzE;AACA,QAAA,MAAM,MAAA,GAAS,CAAA,CAAE,YAAA,CAAa,IAAA,CAAK,GAAG,CAAA;AACtC,QAAA,MAAM,QAAQ,CAAA,WAAA,EAAc,CAAA,CAAE,KAAK,CAAA,SAAA,EAAY,EAAE,KAAK,CAAA;AAAA,eAAA,EAC7C,CAAA,CAAE,KAAK,CAAA,kBAAA,EAAqB,MAAM,CAAA;AAAA,OAAA,CAAA;AAE3C,QAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAA0B,KAAA,EAAO,EAAE,OAAM,EAAG,CAAA,CAAE,IAAA,EAAM,OAAA,CAAQ,MAAM,CAAA;AACrF,QAAA,MAAM,QAAQ,CAAA,CAAE,UAAA;AAChB,QAAA,MAAM,KAAA,GAAQ,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,GAAA,CAAI,CAAC,IAAA,KAAS,iBAAA,CAAkB,IAAA,EAAM,KAAK,CAAC,CAAA,GAAI,IAAA,CAAK,KAAA;AACtF,QAAA,OAAO,EAAE,MAAM,KAAA,EAAM;AAAA,MACvB,CAAA;AAAA,MAEA,MAAM,GAAA,CAAI,UAAA,EAAY,OAAA,EAAS;AAC7B,QAAA,MAAM,CAAA,GAAI,OAAA,CAAQ,UAAA,CAAW,QAAQ,CAAA;AAGrC,QAAA,MAAM,QAAA,GAAW,CAAA,CAAE,QAAA,GAAW,CAAA,CAAA,EAAI,CAAA,CAAE,QAAA,CAAS,YAAY,CAAA,GAAA,EAAM,CAAA,CAAE,QAAA,CAAS,MAAM,CAAA,EAAA,CAAA,GAAO,EAAA;AACvF,QAAA,MAAM,MAAA,GAAS,CAAA,CAAE,YAAA,CAAa,IAAA,CAAK,GAAG,CAAA,GAAI,QAAA;AAC1C,QAAA,MAAM,MAAA,GAAS,EAAE,MAAA,IAAU,QAAA;AAC3B,QAAA,MAAM,OAAA,GAAU,EAAE,OAAA,IAAW,IAAA;AAC7B,QAAA,IAAI,IAAA;AACJ,QAAA,MAAM,IAAA,GAAO,SAAS,CAAC,CAAA;AACvB,QAAA,IAAI,CAAA,CAAE,WAAA,IAAe,IAAA,KAAS,IAAA,EAAM;AAKlC,UAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,EAAE,CAAC,OAAO,GAAG,EAAE,GAAA,EAAK,UAAA,CAAW,EAAA,EAAG,EAAE,EAAG,EAAE,WAAW,CAAA;AAC7E,UAAA,MAAM,QAAQ,CAAA,UAAA,EAAa,CAAA,CAAE,KAAK,CAAA,SAAA,EAAY,EAAE,KAAK,CAAA;AAAA,iBAAA,EAC5C,CAAA,CAAE,KAAK,CAAA,4BAAA,EAA+B,MAAM,CAAA;AAAA,SAAA,CAAA;AAErD,UAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAA0B,KAAA,EAAO,EAAE,OAAM,EAAG,CAAA,CAAE,IAAA,EAAM,OAAA,CAAQ,MAAM,CAAA;AACrF,UAAA,IAAA,GAAO,IAAA,CAAK,KAAA,GAAQ,CAAC,CAAA,IAAK,IAAA;AAAA,QAC5B,CAAA,MAAO;AAWL,UAAA,MAAM,KAAA,GAAQ,CAAA,UAAA,EAAa,CAAA,CAAE,KAAK,SAAS,MAAM,CAAA;AAAA,gBAAA,EACzC,IAAI,CAAA,CAAA,EAAI,OAAO,CAAA,SAAA,EAAY,MAAM,CAAA;AAAA,SAAA,CAAA;AAEzC,UAAA,MAAM,OAAO,MAAM,GAAA;AAAA,YACjB,KAAA;AAAA,YACA,EAAE,EAAA,EAAI,UAAA,CAAW,EAAA,EAAG;AAAA,YACpB,CAAA,CAAE,IAAA;AAAA,YACF,OAAA,CAAQ;AAAA,WACV;AACA,UAAA,IAAA,GAAO,IAAA,CAAK,IAAA;AAAA,QACd;AACA,QAAA,IAAI,IAAA,IAAQ,IAAA;AACV,UAAA,MAAM;AAAA,YACJ,IAAA,EAAM,UAAA;AAAA,YACN,IAAA,EAAM,kBAAA;AAAA,YACN,SAAS,CAAA,WAAA,EAAc,UAAA,CAAW,QAAQ,CAAA,CAAA,EAAI,WAAW,EAAE,CAAA;AAAA,WAC7D;AAMF,QAAA,IAAI,SAAkB,CAAA,CAAE,UAAA,GAAa,kBAAkB,IAAA,EAAM,CAAA,CAAE,UAAU,CAAA,GAAI,IAAA;AAI7E,QAAA,IAAI,CAAA,CAAE,QAAA,IAAY,MAAA,IAAU,OAAO,WAAW,QAAA,EAAU;AACtD,UAAA,MAAM,GAAA,GAAM,EAAE,GAAI,MAAA,EAAmC;AACrD,UAAA,MAAM,OAAA,GAAU,GAAA,CAAI,CAAA,CAAE,QAAA,CAAS,YAAY,CAAA;AAC3C,UAAA,MAAM,IAAA,GACJ,WAAW,OAAO,OAAA,KAAY,WACzB,OAAA,CAAoC,CAAA,CAAE,QAAA,CAAS,MAAM,CAAA,GACtD,IAAA;AACN,UAAA,OAAO,GAAA,CAAI,CAAA,CAAE,QAAA,CAAS,YAAY,CAAA;AAClC,UAAA,MAAA,GACE,IAAA,IAAQ,OAAO,IAAA,KAAS,QAAA,GACpB,EAAE,GAAG,GAAA,EAAK,GAAI,IAAA,EAAiC,GAC/C,GAAA;AAAA,QACR;AACA,QAAA,OAAO,EAAE,MAAM,MAAA,EAAO;AAAA,MACxB,CAAA;AAAA,MAEA,GAAI,gBAAA,GACA;AAAA,QACE,MAAM,MAAA,CAAO,UAAA,EAAY,OAAA,EAAS;AAChC,UAAA,OAAO,sBAAA;AAAA,YACL,UAAA,CAAW,QAAA;AAAA,YACX,UAAA,CAAW,SAAA;AAAA,YACX,UAAA,CAAW,KAAA;AAAA,YACX,OAAA;AAAA,YACA,OAAA,CAAQ;AAAA,WACV;AAAA,QACF;AAAA,UAEF;AAAC,KACP;AAAA,IACA,SAAA,EAAW;AAAA,MACT,MAAM,SAAA,CAAU,UAAA,EAAY,OAAA,EAAS;AACnC,QAAA,MAAM,CAAA,GAAI,OAAA,CAAQ,UAAA,CAAW,QAAQ,CAAA;AACrC,QAAA,MAAM,MAAA,GAAS,CAAA,CAAE,YAAA,CAAa,IAAA,CAAK,GAAG,CAAA;AACtC,QAAA,MAAM,WAAW,UAAA,CAAW,KAAA;AAC5B,QAAA,IAAI,SAAA,GAAY,QAAA;AAChB,QAAA,IAAI,QAAA;AACJ,QAAA,IAAI,EAAE,QAAA,EAAU;AACd,UAAA,MAAM,KAAA,GAAQ,iBAAA,CAAkB,QAAA,EAAU,CAAA,CAAE,QAAQ,CAAA;AACpD,UAAA,SAAA,GAAY,KAAA,CAAM,IAAA;AAClB,UAAA,QAAA,GAAW,KAAA,CAAM,WAAA,GAAc,KAAA,CAAM,QAAA,GAAW,MAAA;AAAA,QAClD;AACA,QAAA,MAAM,MAAA,GAAS,EAAE,UAAA,GACb,mBAAA,CAAoB,WAAW,CAAA,CAAE,UAAA,EAAY,CAAA,CAAE,YAAY,CAAA,GAC3D,SAAA;AACJ,QAAA,MAAM,QAAQ,CAAA,gBAAA,EAAmB,CAAA,CAAE,KAAK,CAAA,UAAA,EAAa,EAAE,KAAK,CAAA;AAAA,qBAAA,EAC7C,CAAA,CAAE,KAAK,CAAA,wBAAA,EAA2B,MAAM,CAAA;AAAA,OAAA,CAAA;AAEvD,QAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAuB,KAAA,EAAO,EAAE,QAAO,EAAG,CAAA,CAAE,IAAA,EAAM,OAAA,CAAQ,MAAM,CAAA;AACnF,QAAA,MAAM,MAAA,GAAS,eAAe,CAAA,EAAG,UAAA,CAAW,SAAS,IAAA,CAAK,IAAA,EAAM,WAAW,QAAQ,CAAA;AACnF,QAAA,IAAI,EAAE,QAAA,IAAY,QAAA;AAChB,UAAA,MAAM,cAAA,CAAe,EAAE,QAAA,EAAU,MAAA,CAAO,IAAI,QAAA,EAAU,CAAA,CAAE,IAAA,EAAM,OAAA,CAAQ,MAAM,CAAA;AAC9E,QAAA,OAAO,MAAA;AAAA,MACT,CAAA;AAAA,MAEA,MAAM,SAAA,CAAU,UAAA,EAAY,OAAA,EAAS;AACnC,QAAA,MAAM,CAAA,GAAI,OAAA,CAAQ,UAAA,CAAW,QAAQ,CAAA;AACrC,QAAA,MAAM,MAAA,GAAS,CAAA,CAAE,YAAA,CAAa,IAAA,CAAK,GAAG,CAAA;AACtC,QAAA,MAAM,WAAW,UAAA,CAAW,KAAA;AAC5B,QAAA,IAAI,SAAA,GAAY,QAAA;AAChB,QAAA,IAAI,QAAA;AACJ,QAAA,IAAI,EAAE,QAAA,EAAU;AACd,UAAA,MAAM,KAAA,GAAQ,iBAAA,CAAkB,QAAA,EAAU,CAAA,CAAE,QAAQ,CAAA;AACpD,UAAA,SAAA,GAAY,KAAA,CAAM,IAAA;AAClB,UAAA,QAAA,GAAW,KAAA,CAAM,WAAA,GAAc,KAAA,CAAM,QAAA,GAAW,MAAA;AAAA,QAClD;AACA,QAAA,MAAM,GAAA,GAAM,EAAE,UAAA,GACV,mBAAA,CAAoB,WAAW,CAAA,CAAE,UAAA,EAAY,CAAA,CAAE,YAAY,CAAA,GAC3D,SAAA;AACJ,QAAA,MAAM,OAAA,GAAU,CAAA,CAAE,OAAA,IAAW,UAAA,CAAW,OAAA;AAiBxC,QAAA,MAAM,WAAA,GACJ,WAAW,eAAA,EACV,WAAA;AACH,QAAA,IAAI,IAAA;AACJ,QAAA,IAAI,MAAA,CAAO,IAAA,CAAK,GAAG,CAAA,CAAE,WAAW,CAAA,EAAG;AACjC,UAAA,IAAA,GAAO,MAAM,kBAAA,CAAmB,CAAA,EAAG,UAAA,CAAW,EAAA,EAAI,QAAQ,MAAM,CAAA;AAAA,QAClE,CAAA,MAAA,IAAW,CAAA,CAAE,WAAA,IAAe,WAAA,IAAe,IAAA,EAAM;AAC/C,UAAA,IAAI,KAAA,GAAQ,UAAA,CAAW,EAAE,CAAC,OAAO,GAAG,EAAE,GAAA,EAAK,UAAA,CAAW,EAAA,EAAG,EAAE,EAAG,EAAE,WAAW,CAAA;AAC3E,UAAA,IAAI,WAAA,IAAe,IAAA,EAAM,KAAA,GAAQ,UAAA,CAAW,KAAA,EAAO,EAAE,UAAA,EAAY,EAAE,GAAA,EAAK,WAAA,EAAY,EAAG,CAAA;AACvF,UAAA,MAAM,KAAA,GAAQ,mBAAmB,CAAA,CAAE,KAAK,YAAY,CAAA,CAAE,KAAK,CAAA,kBAAA,EAAqB,CAAA,CAAE,KAAK,CAAA;AAAA,yBAAA,EACtE,CAAA,CAAE,KAAK,CAAA,wDAAA,EAA2D,MAAM,CAAA;AAAA,SAAA,CAAA;AAEzF,UAAA,MAAM,OAAO,MAAM,GAAA;AAAA,YACjB,KAAA;AAAA,YACA,EAAE,OAAO,GAAA,EAAI;AAAA,YACb,CAAA,CAAE,IAAA;AAAA,YACF,OAAA,CAAQ;AAAA,WACV;AAKA,UAAA,IACE,WAAA,IAAe,IAAA,KAAA,CACb,IAAA,CAAK,MAAA,CAAO,aAAA,IAAiB,CAAA,MAAO,CAAA,IAAA,CAAM,IAAA,CAAK,MAAA,CAAO,SAAA,EAAW,MAAA,IAAU,CAAA,MAAO,CAAA,CAAA,EACpF;AACA,YAAA,MAAM;AAAA,cACJ,IAAA,EAAM,UAAA;AAAA,cACN,IAAA,EAAM,cAAA;AAAA,cACN,SAAS,CAAA,YAAA,EAAe,UAAA,CAAW,QAAQ,CAAA,CAAA,EAAI,WAAW,EAAE,CAAA,6DAAA;AAAA,aAC9D;AAAA,UACF;AACA,UAAA,IAAA,GAAO,IAAA,CAAK,MAAA,CAAO,SAAA,GAAY,CAAC,CAAA,IAAK,IAAA;AAAA,QACvC,CAAA,MAAO;AACL,UAAA,MAAM,MAAA,GAAS,EAAE,MAAA,IAAU,QAAA;AAC3B,UAAA,MAAM,KAAA,GAAQ,mBAAmB,CAAA,CAAE,KAAK,SAAS,MAAM,CAAA,SAAA,EAAY,EAAE,KAAK,CAAA;AAAA,uBAAA,EAC3D,CAAA,CAAE,KAAK,CAAA,oBAAA,EAAuB,OAAO,yBAAyB,MAAM,CAAA;AAAA,SAAA,CAAA;AAEnF,UAAA,MAAM,OAAO,MAAM,GAAA;AAAA,YACjB,KAAA;AAAA,YACA,EAAE,EAAA,EAAI,UAAA,CAAW,EAAA,EAAI,GAAA,EAAI;AAAA,YACzB,CAAA,CAAE,IAAA;AAAA,YACF,OAAA,CAAQ;AAAA,WACV;AACA,UAAA,IAAA,GAAO,IAAA,CAAK,IAAA;AAAA,QACd;AACA,QAAA,MAAM,MAAA,GAAS,cAAA;AAAA,UACb,CAAA;AAAA,UACA,UAAA,CAAW,OAAA;AAAA,UACX,IAAA;AAAA,UACA,UAAA,CAAW,QAAA;AAAA,UACX,UAAA,CAAW;AAAA,SACb;AACA,QAAA,IAAI,EAAE,QAAA,IAAY,QAAA;AAChB,UAAA,MAAM,cAAA,CAAe,EAAE,QAAA,EAAU,MAAA,CAAO,IAAI,QAAA,EAAU,CAAA,CAAE,IAAA,EAAM,OAAA,CAAQ,MAAM,CAAA;AAC9E,QAAA,OAAO,MAAA;AAAA,MACT,CAAA;AAAA,MAEA,MAAM,SAAA,CAAU,UAAA,EAAY,OAAA,EAAS;AACnC,QAAA,MAAM,CAAA,GAAI,OAAA,CAAQ,UAAA,CAAW,QAAQ,CAAA;AACrC,QAAA,MAAM,MAAA,GAAS,CAAA,CAAE,YAAA,CAAa,IAAA,CAAK,GAAG,CAAA;AACtC,QAAA,MAAM,OAAA,GAAU,CAAA,CAAE,OAAA,IAAW,UAAA,CAAW,OAAA;AACxC,QAAA,IAAI,IAAA;AACJ,QAAA,IAAI,EAAE,WAAA,EAAa;AACjB,UAAA,MAAM,KAAA,GAAQ,UAAA,CAAW,EAAE,CAAC,OAAO,GAAG,EAAE,GAAA,EAAK,UAAA,CAAW,EAAA,EAAG,EAAE,EAAG,EAAE,WAAW,CAAA;AAC7E,UAAA,MAAM,QAAQ,CAAA,gBAAA,EAAmB,CAAA,CAAE,KAAK,CAAA,SAAA,EAAY,EAAE,KAAK,CAAA;AAAA,yBAAA,EAC1C,CAAA,CAAE,KAAK,CAAA,4CAAA,EAA+C,MAAM,CAAA;AAAA,SAAA,CAAA;AAE7E,UAAA,MAAM,OAAO,MAAM,GAAA;AAAA,YACjB,KAAA;AAAA,YACA,EAAE,KAAA,EAAM;AAAA,YACR,CAAA,CAAE,IAAA;AAAA,YACF,OAAA,CAAQ;AAAA,WACV;AACA,UAAA,IAAA,GAAO,IAAA,CAAK,MAAA,CAAO,SAAA,GAAY,CAAC,CAAA,IAAK,IAAA;AAAA,QACvC,CAAA,MAAO;AACL,UAAA,MAAM,MAAA,GAAS,EAAE,MAAA,IAAU,QAAA;AAC3B,UAAA,MAAM,KAAA,GAAQ,CAAA,gBAAA,EAAmB,CAAA,CAAE,KAAK,SAAS,MAAM,CAAA;AAAA,uBAAA,EACxC,CAAA,CAAE,KAAK,CAAA,OAAA,EAAU,OAAO,YAAY,MAAM,CAAA;AAAA,SAAA,CAAA;AAEzD,UAAA,MAAM,OAAO,MAAM,GAAA;AAAA,YACjB,KAAA;AAAA,YACA,EAAE,EAAA,EAAI,UAAA,CAAW,EAAA,EAAG;AAAA,YACpB,CAAA,CAAE,IAAA;AAAA,YACF,OAAA,CAAQ;AAAA,WACV;AACA,UAAA,IAAA,GAAO,IAAA,CAAK,IAAA;AAAA,QACd;AACA,QAAA,cAAA,CAAe,GAAG,UAAA,CAAW,OAAA,EAAS,MAAM,UAAA,CAAW,QAAA,EAAU,WAAW,EAAE,CAAA;AAC9E,QAAA,OAAO,EAAE,IAAA,EAAM,UAAA,EAAqB,EAAA,EAAI,WAAW,EAAA,EAAG;AAAA,MACxD,CAAA;AAAA,MAEA,GAAI,kBAAA,GACA;AAAA,QACE,MAAM,MAAA,CAAO,UAAA,EAAY,OAAA,EAAS;AAChC,UAAA,OAAO,sBAAA;AAAA,YACL,UAAA,CAAW,QAAA;AAAA,YACX,UAAA,CAAW,SAAA;AAAA,YACX,UAAA,CAAW,KAAA;AAAA,YACX,UAAA;AAAA,YACA,OAAA,CAAQ;AAAA,WACV;AAAA,QACF;AAAA,UAEF;AAAC,KACP;AAAA,IACA,MAAM,wBAAA,CAAyB;AAAA,MAC7B,WAAW,MAAA,CAAO,SAAA;AAAA,MAClB,UAAU,MAAA,CAAO,QAAA;AAAA,MACjB,QAAA;AAAA,MACA,IAAA,EAAM;AAAA,KACP;AAAA,GACF,CAAA;AACH","file":"index.cjs","sourcesContent":["/**\n * Same-origin GraphQL path Hasura serves by default. The single source for\n * both halves of this package: `createHasuraDataProvider`'s POST target and\n * `createHasuraLiveProvider`'s WebSocket derivation.\n *\n * It lives in its own module rather than in `data-provider.ts` because\n * `live-provider.ts` needs it too, and `data-provider.ts` already imports\n * `createHasuraLiveProvider` from `live-provider.ts`. `live-provider.ts`'s\n * existing back-reference is `import type { ResourceMapping }` — TYPE-only, so\n * it is erased and no runtime cycle exists. Importing a VALUE back across that\n * edge would create a real one. This third module keeps the constant defined\n * exactly once without introducing it.\n *\n * DISTILLATION NOTE (divergence 5 of 7 — deployment fact, neither side wins).\n * The two source apps read this from their OWN `env.ts`: playground as\n * `API_BASE_URL` (`import.meta.env.VITE_API_BASE_URL ?? ''`, an origin-root\n * deployment), l2-web-ui as `API_PREFIX` (`?? '/tandt'`, a non-empty default\n * that exists because every GraphQL call 404'd behind an nginx rule stripping\n * that prefix while the dev proxy passed paths through unprefixed). Both\n * defaults are correct FOR THEIR OWN DEPLOYMENT and neither belongs in a\n * published package, so this package takes no opinion: it exposes the\n * `endpoint` config field (the mechanism l2-web-ui had already added) and\n * defaults to the bare same-origin path. An app behind a path prefix passes\n * `endpoint: `${prefix}/gql/v1/graphql``.\n */\nexport const DEFAULT_HASURA_ENDPOINT = '/gql/v1/graphql';\n","import {\n type DataLiveProvider,\n type LiveConnectionStatus,\n type LiveObserver,\n normalizeDataError,\n} from '@burojs/core';\nimport { createClient } from 'graphql-ws';\n\nimport type { ResourceMapping } from './data-provider.js';\nimport { DEFAULT_HASURA_ENDPOINT } from './endpoint.js';\n\ninterface SubscriptionPayload {\n readonly query: string;\n readonly variables?: Record<string, unknown>;\n}\n\ninterface SubscriptionSink {\n next(value: unknown): void;\n error(error: unknown): void;\n complete(): void;\n}\n\ninterface GraphqlWsClient {\n subscribe(payload: SubscriptionPayload, sink: SubscriptionSink): () => void;\n}\n\ninterface GraphqlWsClientOptions {\n readonly url: string;\n readonly lazy: boolean;\n readonly keepAlive: number;\n readonly retryAttempts: number;\n readonly shouldRetry: () => boolean;\n readonly connectionParams: () => Promise<Record<string, unknown>>;\n readonly on: {\n connecting(): void;\n connected(): void;\n closed(): void;\n };\n}\n\nexport interface HasuraLiveProviderConfig {\n readonly resources: Record<string, ResourceMapping>;\n readonly getToken: () => Promise<string | null>;\n /**\n * The SAME GraphQL endpoint the data provider POSTs to — Hasura serves\n * subscriptions on that exact path. Converted to `ws(s)://` by\n * {@link hasuraWsUrl}. Defaults to {@link DEFAULT_HASURA_ENDPOINT}.\n *\n * `createHasuraDataProvider` forwards its own resolved endpoint here, so the\n * socket and the POST target can never drift apart.\n */\n readonly endpoint?: string;\n /** `X-Hasura-Role` sent in the socket's connection params. `null`/omitted sends none. */\n readonly role?: string | null;\n /** Test seam; production uses graphql-ws directly. */\n readonly clientFactory?: (options: GraphqlWsClientOptions) => GraphqlWsClient;\n}\n\n/**\n * `https://host/path` → `wss://host/path` (Hasura serves subscriptions on the\n * same path it serves queries on). `endpoint` may be `''`, a same-origin path\n * (`/gql/v1/graphql`, `/tandt/gql/v1/graphql`) or an absolute http(s) URL.\n *\n * DISTILLATION NOTE (divergence 6 of 7 — NEITHER a bug fix, verified\n * equivalent; playground's FORM taken, l2-web-ui's TESTS taken). The two apps\n * implement this differently: playground resolves through\n * `new URL(path, window.location.origin)` and flips the protocol; l2-web-ui\n * string-concatenates `origin.replace(/^http/, 'ws') + prefix + path` under a\n * comment warning about a \"prior version\" bug. That warning describes a\n * since-superseded implementation in l2-web-ui's OWN history, NOT playground's\n * current code — the research pass computed both functions for `('', https)`,\n * `('/tandt', https)`, `('/tandt', http)` and `('', http)` and got\n * byte-identical output every time. playground's `new URL()` form is taken\n * because its input domain is strictly wider (it is also correct when handed\n * an absolute `http(s)://` endpoint, which l2-web-ui's concat form explicitly\n * is not designed for), and l2-web-ui's CONTRACT-level tests (scheme + host +\n * prefix placement, never a literal echo of the implementation) are carried\n * into this package's suite because that is the testing style which caught the\n * original production regression.\n */\nexport function hasuraWsUrl(endpoint: string): string {\n // No window (SSR/unit import without a DOM): fall back to the bare path.\n if (typeof window === 'undefined') return endpoint;\n const url = new URL(endpoint, window.location.origin);\n url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';\n return url.toString();\n}\n\nfunction listSubscription(mapping: ResourceMapping): SubscriptionPayload {\n const where = mapping.fixedFilter;\n return {\n query: where\n ? `subscription Live_${mapping.table}_list($where: ${mapping.table}_bool_exp) {\n ${mapping.table}_aggregate(where: $where) { aggregate { count max { updated_at } } }\n }`\n : `subscription Live_${mapping.table}_list {\n ${mapping.table}_aggregate { aggregate { count max { updated_at } } }\n }`,\n ...(where === undefined ? {} : { variables: { where } }),\n };\n}\n\nfunction oneSubscription(\n mapping: ResourceMapping,\n id: string | number,\n): SubscriptionPayload | null {\n const pkField = mapping.pkField ?? 'id';\n const pkType = mapping.pkType ?? 'bigint';\n const root = mapping.byPkRoot === false ? null : (mapping.byPkRoot ?? `${mapping.table}_by_pk`);\n if (root === null) return null;\n return {\n query: `subscription Live_${mapping.table}_one($id: ${pkType}!) {\n ${root}(${pkField}: $id) { ${pkField} updated_at }\n }`,\n variables: { id },\n };\n}\n\n/**\n * Native Hasura live provider. The socket transmits only a change signal; rows\n * always come from the regular descriptor query, so mapping/pagination remain\n * single-sourced in the data provider.\n */\nexport function createHasuraLiveProvider(config: HasuraLiveProviderConfig): DataLiveProvider {\n const listeners = new Set<(status: LiveConnectionStatus) => void>();\n const endpoint = config.endpoint ?? DEFAULT_HASURA_ENDPOINT;\n const role = config.role ?? null;\n let client: GraphqlWsClient | undefined;\n\n const status = (value: LiveConnectionStatus) => {\n for (const listener of listeners) listener(value);\n };\n\n function getClient(): GraphqlWsClient {\n if (client !== undefined) return client;\n const options: GraphqlWsClientOptions = {\n url: hasuraWsUrl(endpoint),\n lazy: true,\n keepAlive: 10_000,\n retryAttempts: Number.POSITIVE_INFINITY,\n shouldRetry: () => true,\n connectionParams: async () => {\n const token = await config.getToken();\n return {\n headers: {\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...(role ? { 'X-Hasura-Role': role } : {}),\n },\n };\n },\n on: {\n connecting: () => status('connecting'),\n connected: () => status('connected'),\n closed: () => status('degraded'),\n },\n };\n client =\n config.clientFactory?.(options) ??\n (createClient(options as never) as unknown as GraphqlWsClient);\n return client;\n }\n\n return {\n async open(descriptor, openContext) {\n if (openContext.signal.aborted) return null;\n const mapping = config.resources[descriptor.resource];\n if (mapping?.live !== true) return null;\n const payload =\n descriptor.kind === 'list'\n ? listSubscription(mapping)\n : descriptor.kind === 'one'\n ? oneSubscription(mapping, descriptor.id)\n : null;\n if (payload === null) return null;\n\n return {\n kind: 'invalidate' as const,\n initial: 'query' as const,\n subscribe(observer: LiveObserver, subscribeContext) {\n if (openContext.signal.aborted || subscribeContext.signal.aborted) return () => undefined;\n let primed = false;\n let closed = false;\n let upstream: (() => void) | undefined = undefined;\n\n const close = () => {\n if (closed) return;\n closed = true;\n listeners.delete(onStatus);\n openContext.signal.removeEventListener('abort', close);\n subscribeContext.signal.removeEventListener('abort', close);\n upstream?.();\n };\n const onStatus = (value: LiveConnectionStatus) => {\n if (!closed) observer.status(value);\n };\n const sink: SubscriptionSink = {\n next: () => {\n if (closed) return;\n // Hasura immediately emits the subscription's current snapshot.\n // The normal query is the baseline for this plan, so only later\n // payloads signal a refetch.\n if (!primed) {\n primed = true;\n return;\n }\n observer.next(undefined);\n },\n error: (error) => {\n if (closed) return;\n observer.error(normalizeDataError(error));\n // An operation-level GraphQL error is terminal for this\n // subscription. Close it eagerly; socket-level reconnect events\n // arrive through the client hooks instead.\n close();\n },\n complete: () => {\n // Socket reconnect is transport-owned. Do not complete the plan:\n // graphql-ws can resubscribe this operation after reconnect.\n if (!closed) observer.status('degraded');\n },\n };\n\n listeners.add(onStatus);\n openContext.signal.addEventListener('abort', close, { once: true });\n subscribeContext.signal.addEventListener('abort', close, { once: true });\n upstream = getClient().subscribe(payload, sink);\n // DISTILLATION NOTE (divergence 7 of 7 — bug fix, playground only,\n // the ONE that went the opposite way from every other divergence).\n // If `sink.error(...)`\n // fires SYNCHRONOUSLY inside `subscribe(...)` — before the\n // assignment above completes — `close()` runs with `upstream` still\n // `undefined`, so its `upstream?.()` is a silent no-op and the real\n // unsubscribe handle, assigned moments later, is never called: an\n // open graphql-ws subscription registration leaks. playground\n // shipped this one-line guard (git-confirmed as a dedicated fix —\n // commit c89ca660 — with its regression test carried into this\n // package's suite verbatim); l2-web-ui never received it, and leaked\n // until this branch backported the guard to it (99e670aa). Stated in\n // the past tense on purpose: the earlier wording said \"leaks to this\n // day\", which the backport made false four commits later.\n if (closed) upstream();\n if (openContext.signal.aborted || subscribeContext.signal.aborted) close();\n // Promise form intentionally makes coordinator-owned late-handle\n // disposal observable; close itself is idempotent across both paths.\n return Promise.resolve(close);\n },\n };\n },\n };\n}\n","import {\n defineDataProvider,\n isDataError,\n type CountResult,\n type DataError,\n type DataOperationContext,\n type DataProvider,\n type ErasedResourceHandle,\n type FilterExpression,\n} from '@burojs/core';\nimport { DEFAULT_HASURA_ENDPOINT } from './endpoint.js';\nimport { createHasuraLiveProvider } from './live-provider.js';\n\nexport interface HasuraDataProviderConfig {\n /** Returns a fresh JWT or null if user is not authed. */\n getToken: () => Promise<string | null>;\n /**\n * GraphQL endpoint every query/mutation is POSTed to, and the endpoint the\n * live provider derives its WebSocket URL from. May be a same-origin path\n * (`/gql/v1/graphql`, `/tandt/gql/v1/graphql`) or an absolute http(s) URL.\n * Defaults to {@link DEFAULT_HASURA_ENDPOINT}.\n */\n endpoint?: string;\n /**\n * Hasura role sent as `X-Hasura-Role` for resources whose mapping does not\n * declare a `role` of its own. `null` (the default) sends no role header at\n * all, leaving the JWT's own default role in force.\n *\n * A resource mapping's `role` always wins when it is defined — including\n * `role: null`, which suppresses the header for that one resource.\n *\n * `''` is treated as `null`, in both halves of the package: it is the shape\n * of a missing env var, not a role. See {@link normalizeRole}.\n */\n defaultRole?: string | null;\n /**\n * Revalidates authentication after a request comes back with HTTP **401**.\n * The callback owns any terminal sign-out; returning true permits one retry\n * with a newly obtained token, while false preserves the original 401\n * response.\n *\n * ⚠️ KNOWN GAP, stated here because this is where a consumer reads: HTTP 401\n * is NOT how the Hasura deployment this provider was distilled from rejects\n * a credential. The one capture in this repository of a real rejection\n * (`packages/mock-kit/fixtures/hasura/error-unauthorized.json`, taken from\n * the live backend with a deliberately malformed bearer token) came back\n * **HTTP 200** with an `invalid-jwt` errors envelope. Replayed through this\n * provider it surfaces as `{ kind: 'protocol', code: 'graphql-error' }` and\n * `recoverAuth` is never consulted — no refresh, no retry, and app logic\n * keyed on `kind: 'auth'` never fires either. Pinned by\n * `tests/fixture-fidelity.test.ts`'s `error-unauthorized` case.\n *\n * So this callback fires only for a deployment that really answers 401 (a\n * proxy or gateway in front of Hasura, typically). Keying recovery on the\n * error envelope instead is a product decision, deliberately not taken here;\n * the existing 401 tests use a FABRICATED 401 and must not be read as proof\n * that recovery works against Hasura itself.\n */\n recoverAuth?: () => Promise<boolean>;\n /** Map our resource name → table + field selection. */\n resources: Record<string, ResourceMapping>;\n /**\n * Optional global scope read on every getList. Returns key → value pairs\n * that get merged into the where clause as `{ <key>: { _eq: <value> } }`,\n * but only for resources that whitelist the key in `scopeFields`.\n *\n * Example: `() => ({ pg: 'radio', location_id: 5 })` — applied to documents\n * (which have both fields) but not products (which only have pg).\n */\n getScope?: (context: DataOperationContext) => Record<string, unknown>;\n /**\n * Closed, application-owned custom operation registry. A descriptor selects\n * an entry by its already-declared `{ resource, operation }` binding; it can\n * never supply a GraphQL document, URL, or result mapper itself.\n */\n operations?: HasuraOperationRegistry;\n fetchImpl?: typeof fetch;\n}\n\nexport interface HasuraCustomOperation {\n /** Which native provider slot may execute this declaration. */\n readonly kind: 'query' | 'mutation';\n /** Static GraphQL document owned by the application/provider integration. */\n readonly document: string;\n /** Maps the declared operation input to the document's variables. */\n readonly variables: (input: unknown) => Record<string, unknown> | undefined;\n /** Maps the full GraphQL response to the declared operation output. */\n readonly mapResult?: (data: unknown) => unknown;\n /** Overrides the mapped resource role; undefined keeps that mapped role. */\n readonly role?: string | null;\n}\n\n/**\n * Registry entries are keyed by the resource binding first, then the declared\n * custom operation name. This makes cross-resource operation reuse explicit\n * rather than allowing a descriptor to name an arbitrary backend operation.\n */\nexport type HasuraOperationRegistry = Readonly<\n Record<string, Readonly<Record<string, HasuraCustomOperation>>>\n>;\n\nexport interface ResourceMapping {\n table: string;\n selectFields: string[]; // fields to select in queries\n /**\n * GraphQL scalar name for this table's primary key, interpolated into the\n * `$id` variable declaration of every by-pk query/mutation. Default\n * `'bigint'`.\n *\n * DISTILLATION NOTE (divergence 3 of 7 — feature, additive, l2-web-ui only).\n * l2-web-ui added `'String'` for tables whose pk is a genuine string\n * business key (a GS1 marking code), not a uuid/Int/bigint\n * wire type: Hasura's by-pk root for such a table is literally\n * `<table>_by_pk(<pk>: $id)` with `$id: String!`, which none of the other\n * three names can express (playground's union stopped at\n * `'bigint' | 'uuid' | 'Int'`). Purely a type-union widening — no runtime\n * branch reads `pkType` beyond interpolating it — so l2-web-ui's wider union\n * is taken wholesale rather than forked.\n */\n pkType?: 'bigint' | 'uuid' | 'Int' | 'String'; // default 'bigint'\n /**\n * Skip the aggregate on every `getList`. This is a COST decision, not a\n * schema fact: the table has an `_aggregate` root but counting it on each\n * page is too slow to pay for unprompted. (Measured in one of the apps this\n * provider was distilled from: ~9.8M-row and ~28.9M-row tables, the latter\n * taking ~55 s per count.) `total` is then omitted and the list renders the\n * unknown-total shape.\n *\n * ⚠️ NOT the same thing as {@link ResourceMapping.aggregate} `: false`, and\n * NOT a proxy for it — most `noAggregate` tables CAN be counted on demand,\n * which is exactly what `getCount` is for. Deriving one from the other gets\n * the flagship resources backwards.\n */\n noAggregate?: boolean;\n /**\n * Which aggregate root field backs this table, for the on-demand `getCount`\n * (and for `getList`'s own count when it runs):\n * - absent — derive `${table}_aggregate` (the Hasura default; today's behaviour);\n * - `false` — no aggregate root exists at all, under any name. `getCount`\n * answers `{ total: null }` without a round trip and the UI drops the\n * affordance for the session;\n * - a string — an explicitly-named root, for tables whose custom root fields\n * break the derivation.\n *\n * ⚠️ Establish this by introspecting the live schema under the role the app\n * actually runs as, not by reading `noAggregate` or a code comment. Both\n * have been wrong here.\n */\n aggregate?: false | string;\n /**\n * Root field for a single-row fetch by primary key. Defaults to the derived\n * `${table}_by_pk`. Set `false` when the active role exposes only the\n * collection root; `getOne` then queries it with pk `where` + `limit: 1`.\n *\n * ⚠️ Same class of fact as `aggregate`, and established the same way — by\n * introspecting the live schema, not by reading a comment. In one of the\n * source apps exactly one table of forty had a non-derivable by-pk root (a\n * plural table whose root was singular), so the derived name 400'd and its\n * Show page was a dead-end error reachable in two clicks from the sidebar.\n */\n byPkRoot?: string | false;\n /** Field to order by in getList. Defaults to 'id'. */\n orderByField?: string;\n /** Name of the primary-key field in the row (for synthetic id in list rows). Defaults to 'id'. */\n pkField?: string;\n /**\n * Hasura role for this resource. `null` (or `''`) means no role header (use\n * the JWT's default role). Undefined falls back to\n * {@link HasuraDataProviderConfig.defaultRole}.\n */\n role?: string | null;\n /**\n * Columns typed `int64` in a Hasura ClickHouse source. Hasura serialises\n * int64 as a JSON STRING and REJECTS a numeric filter value with\n * «expected a string for type 'int64'» — so a `document_id` filter sent as a\n * number matched nothing and the affected list tabs came back empty.\n * Filter values for these fields are coerced to strings before the where\n * clause is built. (Verified live: numeric/int literal → error, string → rows.)\n */\n int64Fields?: string[];\n /**\n * Whitelist of scope keys this table actually has columns for. Only these\n * keys from `getScope()` get injected into where clauses. Defaults to `[]`\n * (no scope injection — useful for tables that lack the scope columns).\n */\n scopeFields?: string[];\n /**\n * JSONB-spill routing for tables whose forms submit a flat values record but\n * whose real columns nest most of those keys inside JSONB blobs (e.g. a\n * `documents` table where template head fields live inside `head`, a couple\n * live inside `meta`, and only a handful — type/status/… — are real columns).\n *\n * When set, create/update partition the incoming flat `variables`:\n * - keys that ARE real columns (present in `selectFields`) stay top-level;\n * - keys listed in `metaKeys` nest under `metaColumn`;\n * - every OTHER key nests under `column` (the head spill).\n * getOne does the inverse — it spreads the fetched `column`/`metaColumn`\n * objects back onto the flat record so edit forms / show pages read those\n * keys as flat fields (the raw blobs are kept too).\n *\n * SCOPE: scalar / relation / enum head+meta fields only. Data-body arrays\n * (children) live in a sibling table — see `dataBody`.\n */\n jsonbSpill?: JsonbSpillConfig;\n /**\n * Data-body routing for resources whose heavy array payload lives in a\n * SEPARATE 1:1 sibling table rather than a JSONB column on the row itself\n * (e.g. a `documents.items[]` / `products[]` payload living in\n * `documents_data.data`, keyed by `document_id`).\n *\n * When set, create/update peel the `keys` off the flat `variables` FIRST\n * (before `jsonbSpill` runs, so they never leak into `head`), write the parent\n * row, then upsert the sibling row (`insert_<table>_one … on_conflict`). getOne\n * nested-selects the sibling via `relationship` and spreads its `column` blob\n * back onto the flat record (so edit forms read `items` as a top-level field),\n * dropping the raw relationship object so it can't round-trip into `head`.\n * getList is left untouched — list columns are head/meta scalars, never the\n * heavy data body.\n */\n dataBody?: DataBodyConfig;\n /**\n * Fixed, always-on Hasura `where` fragment ANDed into every getList /\n * getOne query for this resource. Powers \"per-type\" / projection resources:\n * several resource names can map to the SAME table, each scoped to a\n * different slice — e.g. `fixedFilter: { type: { _eq: 'work_order' } }` turns\n * a `work_orders` resource into a work-orders-only view of a shared\n * `documents` table. The fragment is a raw Hasura `<table>_bool_exp` object\n * (same shape `filtersToWhere` emits), so nested / `_and` / `_or` clauses are\n * all valid. Merged with `mergeWhere` (AND) after user filters + scope, so\n * the user can still filter/search WITHIN the fixed slice.\n *\n * getOne: because `<table>_by_pk` takes no `where`, a resource with a\n * `fixedFilter` fetches its single row via a `where`-based `limit: 1` query\n * (pk `_eq` id AND fixedFilter) instead — so the row is only returned when it\n * actually belongs to the slice (a work-order id won't resolve under\n * `movements`). Resources without a `fixedFilter` keep the `_by_pk` path.\n */\n fixedFilter?: Record<string, unknown>;\n /**\n * Opt this resource into real-time updates. The live provider then opens a\n * cheap change-signal subscription for its list / single-record views and\n * invalidates matching native runtime queries when the table changes.\n *\n * Off by default on purpose: every live resource holds an open server-side\n * subscription, so it is a cost you opt into for data that actually moves\n * (documents change constantly during processing; a country lookup does not).\n */\n live?: boolean;\n}\n\n/** @see ResourceMapping.jsonbSpill */\nexport interface JsonbSpillConfig {\n /** JSONB column that catches every non-base, non-meta key (e.g. `head`). */\n column: string;\n /** JSONB column that catches the `metaKeys` (e.g. `meta`). */\n metaColumn: string;\n /** Flat keys routed to `metaColumn` instead of the `column` spill. */\n metaKeys: string[];\n /**\n * Flat form key → dotted NESTED path inside the head `column`. Lets a flat\n * field (e.g. `vsd_number`) persist to a nested head location (e.g.\n * `regulator_data.crpt.vsd_number`) without dotted field keys. getOne reads\n * the nested value back out to the flat key.\n */\n nestedKeys?: Record<string, string>;\n}\n\n/** Set `obj.a.b.c = value`, creating intermediate objects. */\nfunction setNestedPath(obj: Record<string, unknown>, path: string, value: unknown): void {\n const parts = path.split('.');\n const leaf = parts.pop();\n // `String.split` always yields at least one element, so this never fires at\n // runtime; it is how the leaf is narrowed without a non-null assertion.\n if (leaf === undefined) return;\n let cur = obj;\n for (const p of parts) {\n const next = cur[p];\n if (typeof next !== 'object' || next === null) cur[p] = {};\n cur = cur[p] as Record<string, unknown>;\n }\n cur[leaf] = value;\n}\n\n/** Read `obj.a.b.c` (undefined if any segment is missing). */\nfunction getNestedPath(obj: Record<string, unknown>, path: string): unknown {\n let cur: unknown = obj;\n for (const p of path.split('.')) {\n if (cur == null || typeof cur !== 'object') return undefined;\n cur = (cur as Record<string, unknown>)[p];\n }\n return cur;\n}\n\n/** @see ResourceMapping.dataBody */\nexport interface DataBodyConfig {\n /** Sibling table holding the heavy data body, 1:1 with the parent (e.g. `documents_data`). */\n table: string;\n /** FK column on the sibling pointing back at the parent pk (e.g. `document_id`). */\n fk: string;\n /** JSONB column on the sibling that holds the whole data body (e.g. `data`). */\n column: string;\n /** Reverse object-relationship name on the PARENT row → sibling, for the getOne nested select (e.g. `document_data`). */\n relationship: string;\n /** `on_conflict` constraint enum for the upsert (e.g. `document_data_pkey`). */\n constraint: string;\n /** Flat value keys that belong to the data body — routed to the sibling `column` (e.g. `['items', 'products']`). */\n keys: string[];\n}\n\n/**\n * Split a flat `values` record for a data-body resource into the sibling-table\n * payload and the remainder. Keys listed in `cfg.keys` whose value is defined\n * are collected into `dataBody` (they become the sibling `column` blob); every\n * other key passes through in `rest` (to be further partitioned by jsonbSpill).\n * The raw `cfg.relationship` key (the nested object getOne leaves behind on a\n * re-submitted record) is dropped from both so it can never leak into `head`.\n *\n * `hasDataBody` is true iff at least one data-body key was present — callers\n * skip the sibling write entirely when false, so a document type WITHOUT a data\n * body (e.g. a work order) never gets an empty sibling row.\n *\n * Exported for unit testing; the provider calls it with `m.dataBody`.\n */\nexport function partitionDataBody(\n values: Record<string, unknown>,\n cfg: DataBodyConfig,\n): { dataBody: Record<string, unknown>; rest: Record<string, unknown>; hasDataBody: boolean } {\n const keySet = new Set(cfg.keys);\n const dataBody: Record<string, unknown> = {};\n const rest: Record<string, unknown> = {};\n let hasDataBody = false;\n for (const [key, value] of Object.entries(values)) {\n if (key === cfg.relationship) continue; // never round-trip the raw nested rel object\n if (keySet.has(key)) {\n if (value !== undefined) {\n dataBody[key] = value;\n hasDataBody = true;\n }\n } else {\n rest[key] = value;\n }\n }\n return { dataBody, rest, hasDataBody };\n}\n\n/**\n * Partition a flat `values` record for a jsonb-spill resource.\n *\n * Routing rule (first match wins per key):\n * 1. `key === spill.column` / `key === spill.metaColumn` — an explicitly passed\n * JSONB object (e.g. an edit form re-submitting the raw `head` blob). Its\n * entries are merged UNDER any individually-spilled keys so freshly edited\n * flat values win over the stale blob, and un-modelled legacy keys survive.\n * 2. `key ∈ metaKeys` — nests under `metaColumn`.\n * 3. `key ∈ baseColumns` — stays a top-level column.\n * 4. otherwise — nests under the `column` spill (head).\n *\n * `undefined` values are dropped (a never-set optional field must not clobber\n * an existing column / blob key). Empty JSONB objects are omitted entirely.\n *\n * Exported for unit testing; the provider calls it with `baseColumns =\n * m.selectFields` (the table's real columns, which include `column`/`metaColumn`\n * themselves — handled by rule 1 before the base check).\n */\nexport function partitionJsonbSpill(\n values: Record<string, unknown>,\n spill: JsonbSpillConfig,\n baseColumns: string[],\n): Record<string, unknown> {\n const baseSet = new Set(baseColumns);\n const metaSet = new Set(spill.metaKeys);\n const nestedKeys = spill.nestedKeys ?? {};\n const base: Record<string, unknown> = {};\n const headExplicit: Record<string, unknown> = {};\n const metaExplicit: Record<string, unknown> = {};\n const headSpill: Record<string, unknown> = {};\n const metaSpill: Record<string, unknown> = {};\n const nestedSpill: Array<[string, unknown]> = []; // [nested head path, value]\n\n for (const [key, value] of Object.entries(values)) {\n if (value === undefined) continue;\n const nestedPath = nestedKeys[key];\n if (key === spill.column) {\n if (value && typeof value === 'object')\n Object.assign(headExplicit, value as Record<string, unknown>);\n } else if (key === spill.metaColumn) {\n if (value && typeof value === 'object')\n Object.assign(metaExplicit, value as Record<string, unknown>);\n } else if (metaSet.has(key)) {\n metaSpill[key] = value;\n } else if (nestedPath) {\n nestedSpill.push([nestedPath, value]);\n } else if (baseSet.has(key)) {\n base[key] = value;\n } else {\n headSpill[key] = value;\n }\n }\n\n // Individually-spilled keys win over the merged raw blob (edited value beats\n // the stale object; keys only present in the blob are preserved).\n const head = { ...headExplicit, ...headSpill };\n // Nested head keys (e.g. regulator_data.crpt.vsd_number) applied last.\n for (const [path, value] of nestedSpill) setNestedPath(head, path, value);\n const meta = { ...metaExplicit, ...metaSpill };\n const out: Record<string, unknown> = { ...base };\n if (Object.keys(head).length > 0) out[spill.column] = head;\n if (Object.keys(meta).length > 0) out[spill.metaColumn] = meta;\n return out;\n}\n\n/**\n * Inverse of {@link partitionJsonbSpill}: spread a fetched row's `head` / `meta`\n * JSONB blobs back onto the flat record so head/meta keys (e.g. `batch_number`,\n * `regulatory_system`) read as top-level fields on show pages, edit forms, AND\n * per-type list columns. Base columns win on collision (`...rec` last), and the\n * raw `head` / `meta` objects are preserved so tabs / re-submission still have\n * them. A non-object / null blob is ignored.\n *\n * Shared by getOne (single record) and getList (each row) so the two stay in\n * lockstep. Returns the input unchanged when it isn't an object.\n */\nexport function flattenJsonbSpill(item: unknown, spill: JsonbSpillConfig): unknown {\n if (!item || typeof item !== 'object') return item;\n const rec = item as Record<string, unknown>;\n const headObj = rec[spill.column];\n const metaObj = rec[spill.metaColumn];\n const out: Record<string, unknown> = {\n ...(headObj && typeof headObj === 'object' ? (headObj as Record<string, unknown>) : {}),\n ...(metaObj && typeof metaObj === 'object' ? (metaObj as Record<string, unknown>) : {}),\n ...rec,\n };\n // Read nested head paths back out to their flat form keys.\n if (spill.nestedKeys && headObj && typeof headObj === 'object') {\n for (const [flatKey, path] of Object.entries(spill.nestedKeys)) {\n const v = getNestedPath(headObj as Record<string, unknown>, path);\n if (v !== undefined) out[flatKey] = v;\n }\n }\n return out;\n}\n\n/**\n * This provider's own code for \"no token to send\" — `getToken()` resolved `null`, so no\n * request was ever attempted. Exported so downstream error-classification code can tell\n * this apart from a genuine rejected-credentials answer (a real 401 the retry/`recoverAuth`\n * path still couldn't clear) without inventing a parallel distinction of its own: a request\n * that never left the client is the \"we never asked\" case, not a session failure.\n *\n * DISTILLATION NOTE (divergence 4 of 7 — hygiene, l2-web-ui only, zero\n * behaviour change). playground inlined the literal `'not-authenticated'` at\n * both throw sites; l2-web-ui extracted and exported this constant so its own\n * `classify-data-error.ts` could match a symbol instead of a string. The STRING\n * VALUE is identical either way — nothing observable differs — so l2-web-ui's\n * exported form is taken purely because a published package owes consumers a\n * stable symbol.\n */\nexport const NOT_AUTHENTICATED_CODE = 'not-authenticated';\n\ninterface GqlResponse<T> {\n data?: T;\n errors?: Array<{ message: string }>;\n}\n\ninterface HasuraScopedQuery {\n readonly filter?: FilterExpression<ErasedResourceHandle>;\n readonly providerOptions?: { readonly ignoreScope?: boolean };\n}\n\nconst OP_MAP: Record<string, string> = {\n eq: '_eq',\n neq: '_neq',\n ne: '_neq',\n lt: '_lt',\n lte: '_lte',\n gt: '_gt',\n gte: '_gte',\n in: '_in',\n notIn: '_nin',\n nin: '_nin',\n contains: '_ilike',\n containss: '_like',\n startsWith: '_ilike',\n endsWith: '_ilike',\n startswith: '_ilike',\n endswith: '_ilike',\n null: '_is_null',\n};\n\n/**\n * The shared refusal code for \"this provider cannot express that filter\n * operator\". `@burojs/pocketbase` throws the SAME `kind` and the SAME code, so\n * an application handling one handles the other — see\n * {@link filtersToWhere} for why that agreement is load-bearing.\n */\nexport const FILTER_OPERATOR_UNSUPPORTED_CODE = 'filter-operator-unsupported';\n\n/**\n * Operators this provider knowingly cannot express, kept separate from \"we\n * have never heard of this\" so the error can say WHY rather than just \"no\".\n *\n * `has` / `hasAny` / `hasAll` are first-class `@burojs/core` operators over a\n * COLLECTION field (`CollectionPredicate`, `packages/core/src/data/filters.ts`).\n * Hasura can express collection membership — but WHICH operator is correct\n * depends on the column's Postgres type, and nothing this provider is given\n * declares it:\n *\n * - a `jsonb` column takes `_has_key` / `_has_keys_any` / `_has_keys_all`\n * (Postgres `?`, `?|`, `?&` — \"does this string exist as a top-level key or\n * array element\"), or `_contains` (`@>`);\n * - a Postgres ARRAY column (`text[]`) has no `_has_key*` at all; containment\n * is `_contains` / `_contained_in`.\n *\n * `ResourceMapping` declares `table`, `selectFields`, `pkType`, `int64Fields`\n * and the JSONB SPILL columns — nothing that distinguishes \"this column is\n * jsonb\" from \"this column is a Postgres array\" for an arbitrary filterable\n * field. Emitting one family for a column of the other type is either a\n * Hasura validation error or, worse, a valid query with different meaning.\n *\n * PROVENANCE, kept separate as the PocketBase truth table does:\n * - DOCUMENTED HASURA SEMANTICS settle which operator families exist and\n * which column types accept them (above).\n * - NOTHING IN THIS REPOSITORY settles which family any given resource needs.\n * All 37 captured live fixtures filter on scalar columns only — the\n * operators appearing across the whole corpus are `_eq` (16), `_and` (4),\n * `_or` (3), `_is_null` (2), `_in` (1), `_not` (1), and no capture touches a\n * collection column. `hasuraAdapter` implements three where-operators\n * (`_eq`, `_in`, `_is_null`), so it cannot settle it either.\n *\n * So this refuses, LOUDLY, rather than guessing. The way to implement it later\n * is to declare the column's shape on the mapping (e.g. a\n * `collectionFields: Record<string, 'jsonb' | 'array'>`) and translate per\n * declaration — the same registry-driven pattern `int64Fields` already uses —\n * ideally after capturing one real response per family.\n */\nconst UNEXPRESSIBLE_OPS: Readonly<Record<string, string>> = {\n has: 'a collection-membership test',\n hasAny: 'a collection-membership test',\n hasAll: 'a collection-membership test',\n};\n\nfunction unsupportedOperator(field: string, op: string): DataError {\n const why = UNEXPRESSIBLE_OPS[op];\n return {\n kind: 'capability',\n code: FILTER_OPERATOR_UNSUPPORTED_CODE,\n message: why\n ? `@burojs/hasura cannot express the '${op}' filter operator on '${field}': ${why}, whose Hasura encoding depends on whether the column is jsonb or a Postgres array — something no ResourceMapping field declares. Declare it and translate explicitly, or filter on a scalar column.`\n : `@burojs/hasura does not support the filter operator '${op}' (on '${field}').`,\n };\n}\n\n/**\n * Filter `scope` to keys whitelisted by `scopeFields`, drop nullish/empty\n * values, and translate to a Hasura where object using `_eq`.\n * Returns `undefined` when nothing applies.\n */\nexport function scopeToWhere(\n scope: Record<string, unknown>,\n scopeFields: string[],\n): Record<string, unknown> | undefined {\n const clauses: Array<Record<string, unknown>> = [];\n for (const key of scopeFields) {\n const v = scope[key];\n if (v == null || v === '') continue;\n clauses.push({ [key]: { _eq: v } });\n }\n if (clauses.length === 0) return undefined;\n if (clauses.length === 1) return clauses[0];\n return { _and: clauses };\n}\n\n/**\n * Combine two where objects with `_and`. Either or both may be undefined.\n */\nexport function mergeWhere(\n a: Record<string, unknown> | undefined,\n b: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (!a) return b;\n if (!b) return a;\n return { _and: [a, b] };\n}\n\n/** Translate the native filter AST into a Hasura bool expression. */\nexport function filtersToWhere(\n filter?: FilterExpression<ErasedResourceHandle>,\n): Record<string, unknown> | undefined {\n if (!filter || typeof filter !== 'object') return undefined;\n // ── The empty-combinator rule (both branches below) ────────────────────────\n //\n // THE CLASS, stated once: this function uses `undefined` as a sentinel\n // meaning \"no constraint was expressed here — drop me\". A branchless\n // combinator is NOT that. `_and: []` is vacuously TRUE and `_or: []` is\n // vacuously FALSE, and dropping a node with a truth value is only sound in\n // an AND position, where a TRUE conjunct is genuinely absorbed. Under `not`,\n // or beside a disjunct in an `or`, dropping it changes what the query means.\n //\n // Both forms are therefore emitted EXPLICITLY, so they survive negation and\n // disjunction with their truth value intact. Checked on the SOURCE array\n // before mapping, because a combinator whose branches all mapped away\n // because NO VALUE was supplied for them really is the drop-me case. That\n // drop condition is a missing or `undefined` value — never `''`, which both\n // this package and `@burojs/pocketbase` translate.\n //\n // Provenance, kept separate on purpose:\n // - The two truth values are settled by CAPTURE, not by reasoning.\n // `packages/mock-kit/fixtures/hasura/where-or-empty.json` sends\n // `where: {_or: []}` to the live backend and comes back with `items: []`;\n // `where-and-empty.json` sends `{_and: []}` and comes back with the same\n // five rows `where-and-empty-baseline-no-filter.json` gets for no `where`\n // at all.\n // - How those two values COMPOSE under `_not`/`_or` is Hasura's documented\n // boolean semantics; no capture exercises a composed empty combinator.\n // The composed forms are verified end to end against `hasuraAdapter`\n // instead (`examples/marketplace-ops/tests/hasura-provider-contract.test.ts`),\n // whose own implementation was written from the GraphQL spec.\n //\n // The `or` half was fixed first (it was the one a capture caught directly);\n // the `and` half followed from sweeping the class rather than the instance —\n // before it, `{not: {and: []}}` matched every row where it must match none.\n if ('and' in filter) {\n if (filter.and.length === 0) return { _and: [] };\n const clauses = filter.and\n .map(filtersToWhere)\n .filter((item): item is Record<string, unknown> => item !== undefined);\n if (clauses.length === 0) return undefined;\n return clauses.length === 1 ? clauses[0] : { _and: clauses };\n }\n if ('or' in filter) {\n if (filter.or.length === 0) return { _or: [] };\n const clauses = filter.or\n .map(filtersToWhere)\n .filter((item): item is Record<string, unknown> => item !== undefined);\n if (clauses.length === 0) return undefined;\n return clauses.length === 1 ? clauses[0] : { _or: clauses };\n }\n if ('not' in filter) {\n const clause = filtersToWhere(filter.not);\n return clause === undefined ? undefined : { _not: clause };\n }\n if (!('field' in filter) || typeof filter.field !== 'string' || !('op' in filter))\n return undefined;\n\n const { field, op } = filter;\n if (op === 'isNull') return { [field]: { _is_null: true } };\n if (op === 'isNotNull') return { [field]: { _is_null: false } };\n // Missing / `undefined` value: no constraint was expressed, so drop.\n //\n // `''` is deliberately NOT in that set any more, and the two providers now\n // agree on it. It used to be dropped here, which meant `{name: {op: 'eq',\n // value: ''}}` — \"name IS the empty string\", an ordinary query — silently\n // matched every row, while `@burojs/pocketbase` emitted it. That is the\n // BLOCKER's own shape (one filter AST, two providers, different answers)\n // outside the branchless subclass.\n //\n // The drop existed for the empty-SEARCH-BOX idiom the two source apps had.\n // That idiom is already handled one layer up, in the only place every search\n // box in this framework goes through: `searchToFilter`\n // (`packages/core/src/data/search.ts`) returns `undefined` for an empty or\n // whitespace-only query and never builds a predicate at all. So suppressing\n // it a second time here bought nothing and cost the `eq: ''` case.\n if (!('value' in filter) || filter.value === undefined) return undefined;\n\n // NO `?? '_eq'` FALLBACK. An operator this provider cannot express must fail\n // loudly, not silently become equality against a value of the wrong shape.\n // The fallback is what turned `hasAny: []` — \"match nothing\" — into\n // `{tags: {_eq: []}}`, and it guaranteed that EVERY operator added to\n // `@burojs/core` in future would arrive here as a silent `_eq` rather than as\n // something someone noticed.\n //\n // WHERE IT FAILS: at request time, in the translator, not at construction.\n // That is forced rather than chosen — the operator is a property of the\n // FILTER, which arrives on the descriptor per request, and `ResourceMapping`\n // (the only thing available at construction) declares tables and columns, not\n // which operators a caller will use. There is nothing to validate at\n // construction time. It throws from `filtersToWhere` rather than returning a\n // sentinel so that every caller — `list`, `count`, `many`, the `fixedFilter`\n // merge — inherits the refusal without each having to check.\n const operator = OP_MAP[op];\n if (operator === undefined) throw unsupportedOperator(field, op);\n let value: unknown = filter.value;\n if (operator === '_ilike' || operator === '_like') {\n const text = String(value);\n value = op === 'startsWith' ? `${text}%` : op === 'endsWith' ? `%${text}` : `%${text}%`;\n }\n return { [field]: { [operator]: value } };\n}\n\nfunction coerceInt64Filter(\n filter: FilterExpression<ErasedResourceHandle> | undefined,\n fields: readonly string[] | undefined,\n): FilterExpression<ErasedResourceHandle> | undefined {\n if (!filter || !fields?.length) return filter;\n if ('and' in filter)\n return {\n and: filter.and.map(\n (entry) => coerceInt64Filter(entry, fields) as FilterExpression<ErasedResourceHandle>,\n ),\n };\n if ('or' in filter)\n return {\n or: filter.or.map(\n (entry) => coerceInt64Filter(entry, fields) as FilterExpression<ErasedResourceHandle>,\n ),\n };\n if ('not' in filter)\n return { not: coerceInt64Filter(filter.not, fields) as FilterExpression<ErasedResourceHandle> };\n if (\n 'field' in filter &&\n fields.includes(filter.field) &&\n 'value' in filter &&\n filter.value != null\n ) {\n const value = Array.isArray(filter.value) ? filter.value.map(String) : String(filter.value);\n return { ...filter, value } as FilterExpression<ErasedResourceHandle>;\n }\n return filter;\n}\n\nfunction hasuraError(error: unknown, signal: AbortSignal, fallback: string): DataError {\n if (isDataError(error)) return error;\n if (signal.aborted)\n return {\n kind: 'cancelled',\n code: 'request-aborted',\n message: 'Hasura request was aborted.',\n cause: error,\n };\n return {\n kind: 'transport',\n code: 'hasura-request-failed',\n message: error instanceof Error && error.message ? error.message : fallback,\n cause: error,\n };\n}\n\n/**\n * Resolve the Hasura aggregate root field for a mapping, or `null` when the\n * registry declares that none exists. Registry-driven on purpose: which tables\n * can be counted is a per-resource FACT, so it is declared once on the mapping\n * (`aggregate`) and read here — never a `if (resource === 'users')` chain in the\n * provider, which is the shape this codebase has been deleting.\n */\nexport function aggregateRoot(m: ResourceMapping): string | null {\n if (m.aggregate === false) return null;\n return typeof m.aggregate === 'string' ? m.aggregate : `${m.table}_aggregate`;\n}\n\n/**\n * Root field for a by-pk read. Declarative per resource for the same reason\n * {@link aggregateRoot} is: the name is a fact about the backend schema, not\n * something to branch on by resource name inside the provider.\n */\nexport function byPkRoot(m: ResourceMapping): string | null {\n if (m.byPkRoot === false) return null;\n return m.byPkRoot ?? `${m.table}_by_pk`;\n}\n\nfunction snapshotOperationRegistry(\n source: HasuraOperationRegistry | undefined,\n): Readonly<Record<string, Readonly<Record<string, HasuraCustomOperation>>>> {\n if (source === undefined) return Object.freeze({});\n const resources: Record<string, Readonly<Record<string, HasuraCustomOperation>>> = {};\n for (const [resource, operations] of Object.entries(source)) {\n const entries: Record<string, HasuraCustomOperation> = {};\n for (const [name, operation] of Object.entries(operations)) {\n entries[name] = Object.freeze({ ...operation });\n }\n resources[resource] = Object.freeze(entries);\n }\n return Object.freeze(resources);\n}\n\nfunction hasRegisteredOperationKind(\n operations: Readonly<Record<string, Readonly<Record<string, HasuraCustomOperation>>>>,\n kind: HasuraCustomOperation['kind'],\n): boolean {\n return Object.values(operations).some((byName) =>\n Object.values(byName).some((operation) => operation.kind === kind),\n );\n}\n\n/**\n * `''` is not a role — it is the ordinary shape of a missing env var, and it is\n * exactly how both source apps built their endpoint constants\n * (`import.meta.env.VITE_X ?? ''`). It is normalised to `null` — \"send no role\n * header\" — once, at the two places a role enters the provider.\n *\n * Why once and not at each use: the HTTP half gated on `!== null` and the\n * socket half on truthiness, so `defaultRole: ''` used to send an empty\n * `X-Hasura-Role` on every POST and NO header on the subscription — one config\n * value, two answers, in a package whose `endpoint` comment promises the two\n * halves \"can never drift apart\". Normalising at the boundary is what makes\n * that promise structural instead of a convention two call sites have to keep.\n *\n * `undefined` is handled by the CALLER, not here, because it means different\n * things in the two positions: on a mapping it means \"inherit the default\", on\n * the config it means \"no default\".\n */\nfunction normalizeRole(role: string | null | undefined): string | null {\n return role === undefined || role === null || role === '' ? null : role;\n}\n\nexport function createHasuraDataProvider(config: HasuraDataProviderConfig): DataProvider {\n const endpoint = config.endpoint ?? DEFAULT_HASURA_ENDPOINT;\n const defaultRole = normalizeRole(config.defaultRole);\n const operations = snapshotOperationRegistry(config.operations);\n const hasCustomQueries = hasRegisteredOperationKind(operations, 'query');\n const hasCustomMutations = hasRegisteredOperationKind(operations, 'mutation');\n\n async function gql<T>(\n query: string,\n variables: Record<string, unknown> | undefined,\n role: string | null | undefined,\n signal: AbortSignal,\n ): Promise<T> {\n try {\n const request = async (token: string) => {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n };\n // Divergence 5 of 7, role half (see `endpoint.ts` for the endpoint\n // half). `defaultRole` is what both apps imported from their own\n // `env.ts` as `HASURA_ROLE` — playground's `'company-admin'`,\n // l2-web-ui's `'operator'`, each an app deployment fact. It is\n // configuration here, defaulting to `null` (no role header at all).\n // A mapping `role` of `undefined` inherits it; an explicit `null`\n // suppresses the header for that one resource.\n const effectiveRole = role === undefined ? defaultRole : normalizeRole(role);\n if (effectiveRole !== null) headers['X-Hasura-Role'] = effectiveRole;\n // Divergence 5 of 7, endpoint half: both apps interpolated their own\n // app-owned env constant into a URL here. The package posts to the\n // configured `endpoint` and imports no app module.\n // Resolved HERE, per call — not once at construction. Captured at\n // construction, a `fetch` installed AFTERWARDS is unreachable for the\n // life of the provider: a provider built at module scope, before an MSW\n // server or worker has started, silently escapes the mock and goes to\n // the real network instead — and nothing throws. `@burojs/pocketbase`'s\n // `auth-provider.ts` carries the same note; it had shipped this exact\n // bug once and it stayed invisible until something needed real fixture\n // data from it. The three provider packages agree on this.\n const fetchImpl = config.fetchImpl ?? fetch;\n return fetchImpl(endpoint, {\n method: 'POST',\n headers,\n body: JSON.stringify({ query, variables }),\n signal,\n });\n };\n\n let token = await config.getToken();\n if (!token)\n throw {\n kind: 'auth',\n code: NOT_AUTHENTICATED_CODE,\n message: 'Not authenticated.',\n } satisfies DataError;\n let res = await request(token);\n if (res.status === 401 && config.recoverAuth && (await config.recoverAuth())) {\n token = await config.getToken();\n if (!token)\n throw {\n kind: 'auth',\n code: NOT_AUTHENTICATED_CODE,\n message: 'Not authenticated.',\n } satisfies DataError;\n res = await request(token);\n }\n if (!res.ok)\n throw {\n kind: 'transport',\n code: 'graphql-http-error',\n message: `GraphQL HTTP ${res.status}`,\n status: res.status,\n } satisfies DataError;\n const body = (await res.json()) as GqlResponse<T>;\n if (body.errors?.length)\n throw {\n kind: 'protocol',\n code: 'graphql-error',\n message: body.errors.map((e) => e.message).join('; '),\n details: body.errors,\n } satisfies DataError;\n if (body.data === undefined)\n throw {\n kind: 'protocol',\n code: 'empty-graphql-response',\n message: 'Empty GraphQL response.',\n } satisfies DataError;\n return body.data;\n } catch (error) {\n throw hasuraError(error, signal, 'Hasura request failed.');\n }\n }\n\n function mapping(resource: string): ResourceMapping {\n const m = config.resources[resource];\n if (!m)\n throw {\n kind: 'configuration',\n code: 'unknown-resource',\n message: `Unknown resource: ${resource}`,\n } satisfies DataError;\n return m;\n }\n\n function registeredOperation(\n resource: string,\n operation: string,\n kind: HasuraCustomOperation['kind'],\n ): HasuraCustomOperation {\n const registered = operations[resource]?.[operation];\n if (registered === undefined || registered.kind !== kind) {\n throw {\n kind: 'capability',\n code: 'hasura-custom-operation-unsupported',\n message: `Hasura ${kind} operation '${operation}' is not declared for '${resource}'.`,\n } satisfies DataError;\n }\n return registered;\n }\n\n async function executeCustomOperation(\n resource: string,\n operationName: string,\n input: unknown,\n kind: HasuraCustomOperation['kind'],\n signal: AbortSignal,\n ): Promise<unknown> {\n const operation = registeredOperation(resource, operationName, kind);\n // Do not inspect descriptor providerOptions here. The operation's static\n // registry entry is the only source of GraphQL text and variable mapping.\n const result = await gql(\n operation.document,\n operation.variables(input),\n operation.role ?? config.resources[resource]?.role,\n signal,\n );\n return operation.mapResult?.(result) ?? result;\n }\n\n /**\n * The ONE composition of a list-shaped `where`, shared by `getList` and\n * `getCount`. Four things go in, in this order:\n * 1. int64 coercion — ClickHouse-sourced columns typed `int64` are rejected\n * as numbers, so those filter values become strings (see `int64Fields`);\n * 2. the user's filters (`filtersToWhere`);\n * 3. the active sidebar scope, whitelisted by `scopeFields`, unless the\n * caller opted out with `meta.ignoreScope`;\n * 4. the mapping's `fixedFilter` (per-type / projection resources), ANDed in\n * LAST so the user still filters WITHIN the slice.\n *\n * ⚠️ `getCount` MUST NOT re-derive this from `filtersToWhere` alone. Scope and\n * `fixedFilter` are invisible to the caller — a resource declaring\n * `scopeFields: ['pg', 'location_id']` would have a filters-only count report\n * the total across every product group and location. A confidently wrong\n * number is strictly worse than no number, which is what the list already\n * degrades to.\n */\n function composeListWhere(\n m: ResourceMapping,\n descriptor: HasuraScopedQuery,\n context: DataOperationContext,\n ): Record<string, unknown> | undefined {\n const userWhere = filtersToWhere(coerceInt64Filter(descriptor.filter, m.int64Fields));\n\n // A query may opt OUT of scope via `meta.ignoreScope` when it must search\n // the whole table regardless of the active sidebar scope — e.g. resolving a\n // product by its globally-unique GTIN, which shouldn't 404 just because the\n // sidebar is filtered to a different product group.\n const ignoreScope = descriptor.providerOptions?.ignoreScope === true;\n const sessionScope =\n context.session.scopeKey !== null &&\n typeof context.session.scopeKey === 'object' &&\n !Array.isArray(context.session.scopeKey)\n ? (context.session.scopeKey as Record<string, unknown>)\n : {};\n const scope = ignoreScope ? {} : { ...sessionScope, ...(config.getScope?.(context) ?? {}) };\n const scopeWhere = scopeToWhere(scope, m.scopeFields ?? []);\n\n return mergeWhere(mergeWhere(userWhere, scopeWhere), m.fixedFilter);\n }\n\n /**\n * Issue the aggregate COUNT for a mapping against an already-composed `where`.\n * Shared by `getList`'s inline count and the on-demand `getCount` so the two\n * emit byte-identical queries. `root` comes from {@link aggregateRoot}; the\n * caller has already established that one exists.\n */\n async function runAggregateCount(\n m: ResourceMapping,\n root: string,\n where: Record<string, unknown> | undefined,\n signal: AbortSignal,\n ): Promise<number> {\n const aggWhereArg = where ? '($where: ' + m.table + '_bool_exp)' : '';\n const aggWhereParam = where ? '(where: $where)' : '';\n const metaQuery = `query Count_${m.table} ${aggWhereArg} {\n meta: ${root} ${aggWhereParam} { aggregate { count } }\n }`;\n const aggVars: Record<string, unknown> = {};\n if (where) aggVars['where'] = where;\n const metaData = await gql<{ meta: { aggregate: { count: number } } }>(\n metaQuery,\n where ? aggVars : undefined,\n m.role,\n signal,\n );\n return metaData.meta.aggregate.count;\n }\n\n /**\n * Upsert the data-body sibling row for a parent record. Uses Hasura's\n * `insert_<table>_one … on_conflict` so the same call inserts (create) or\n * replaces (edit) the single FK-keyed row. `constraint` / `column`\n * are unquoted GraphQL enum literals — they come from static mapping config,\n * never user input, so string-interpolating them is safe.\n */\n async function upsertDataBody(\n db: DataBodyConfig,\n parentId: string | number,\n body: Record<string, unknown>,\n role: string | null | undefined,\n signal: AbortSignal,\n ): Promise<void> {\n const mutation = `mutation UpsertDataBody($object: ${db.table}_insert_input!) {\n result: insert_${db.table}_one(\n object: $object,\n on_conflict: { constraint: ${db.constraint}, update_columns: [${db.column}] }\n ) { ${db.fk} }\n }`;\n await gql(mutation, { object: { [db.fk]: parentId, [db.column]: body } }, role, signal);\n }\n\n function mutationRecord(\n m: ResourceMapping,\n idField: string,\n item: unknown,\n resource: string,\n expectedId?: string | number,\n ) {\n if (item === null || typeof item !== 'object' || Array.isArray(item)) {\n throw {\n kind: 'protocol',\n code: 'record-not-found',\n message: `Not found: ${resource}`,\n } satisfies DataError;\n }\n const record = item as Record<string, unknown>;\n const id = record[m.pkField ?? idField];\n if (typeof id !== 'string' && (typeof id !== 'number' || !Number.isFinite(id))) {\n throw {\n kind: 'protocol',\n code: 'mutation-record-id-missing',\n message: `Mutation for '${resource}' returned no valid '${m.pkField ?? idField}'.`,\n } satisfies DataError;\n }\n if (expectedId !== undefined && id !== expectedId) {\n throw {\n kind: 'protocol',\n code: 'unexpected-record-id',\n message: `Mutation for '${resource}' returned '${String(id)}' instead of '${String(expectedId)}'.`,\n } satisfies DataError;\n }\n // Mutations select the declared table fields but do not re-read relation\n // data bodies or server-computed presentation values. Marking them partial\n // makes React invalidate dependent reads rather than write an incomplete\n // record into a one-record cache.\n return { kind: 'record' as const, id, record: item, completeness: 'partial' as const };\n }\n\n async function readMutationRecord(\n m: ResourceMapping,\n id: string | number,\n signal: AbortSignal,\n ): Promise<unknown> {\n const fields = m.selectFields.join(' ');\n const pkType = m.pkType ?? 'bigint';\n const pkField = m.pkField ?? 'id';\n const root = byPkRoot(m);\n if (m.fixedFilter || root === null) {\n const where = mergeWhere({ [pkField]: { _eq: id } }, m.fixedFilter);\n const query = `query One_${m.table}($where: ${m.table}_bool_exp) {\n items: ${m.table}(where: $where, limit: 1) { ${fields} }\n }`;\n const data = await gql<{ items: unknown[] }>(query, { where }, m.role, signal);\n return data.items?.[0] ?? null;\n }\n // DISTILLATION FIX — not a port. Neither source app had fixed this branch\n // when the package was distilled (both were backported from here later in\n // the same branch, 99e670aa; the earlier \"NEITHER source app fixed this\"\n // wording went stale four commits after it was written).\n // l2-web-ui fixed the identical hardcoded-`id` bug in `one()` (see\n // divergence 1 of 7 there) and left this direct-root branch — reached by\n // `updateOne` when the `_set` is empty, i.e. a data-body-only edit that\n // just re-reads the row — still emitting `${root}(id: $id)`. For any\n // mapping whose `pkField` is not literally `id` that argument name does\n // not exist on the by-pk root, so Hasura 400s. Same bug class, same fix,\n // now applied in both places.\n const query = `query One_${m.table}($id: ${pkType}!) {\n item: ${root}(${pkField}: $id) { ${fields} }\n }`;\n const data = await gql<{ item: unknown }>(query, { id }, m.role, signal);\n return data.item;\n }\n\n return defineDataProvider({\n queries: {\n async list(descriptor, context) {\n const m = mapping(descriptor.resource);\n if (descriptor.pagination?.kind === 'cursor') {\n throw {\n kind: 'capability',\n code: 'cursor-pagination-unsupported',\n message: 'Hasura provider supports offset pagination only.',\n } satisfies DataError;\n }\n const limit =\n descriptor.pagination?.kind === 'offset' ? descriptor.pagination.pageSize : 25;\n const page = descriptor.pagination?.kind === 'offset' ? descriptor.pagination.page : 1;\n const offset = (page - 1) * limit;\n\n // int64 coercion + user filters + scope + fixedFilter, composed exactly\n // once — `getCount` calls the SAME helper so an on-demand count can never\n // describe a different set of rows than the page it sits under.\n const where = composeListWhere(m, descriptor, context);\n\n // Sort: take native sort expressions if present, fall back to mapping default.\n // Guard against entries with falsy `field` — Hasura would reject the\n // resulting `[{undefined: asc}]` and bring down the whole query.\n const sorters = descriptor.sort?.filter((s) => s.field);\n let orderByExpr: string;\n if (sorters && sorters.length > 0) {\n // Hasura order_by accepts an array of single-field bool exps.\n // We emit `[{field1: asc}, {field2: desc}]` — exact spec for multi-sort.\n orderByExpr = `[${sorters.map((s) => `{${s.field}: ${s.direction}}`).join(', ')}]`;\n } else {\n const defaultField = m.orderByField ?? 'id';\n orderByExpr = `{${defaultField}: desc}`;\n }\n\n const fields = m.selectFields.join(' ');\n const whereArg = where ? ', $where: ' + m.table + '_bool_exp' : '';\n const whereParam = where ? ', where: $where' : '';\n const itemsQuery = `query List_${m.table}($limit: Int, $offset: Int${whereArg}) {\n items: ${m.table}(limit: $limit, offset: $offset, order_by: ${orderByExpr}${whereParam}) { ${fields} }\n }`;\n const itemsVars: Record<string, unknown> = { limit, offset };\n if (where) itemsVars['where'] = where;\n const itemsData = await gql<{ items: unknown[] }>(\n itemsQuery,\n itemsVars,\n m.role,\n context.signal,\n );\n // `total` is OMITTED (undefined) when we cannot honestly produce a count:\n // `noAggregate` tables skip the count deliberately (a COUNT over ~29M rows\n // took ~55 s and hung the page in one of the source apps). The list's\n // unknown-total contract then takes over — range without a grand total,\n // Next while the page is full, no jump-to-end. NEVER substitute\n // `items.length`: that is the CURRENT PAGE's length, and reporting it as\n // the total collapses the list to one page.\n let total: number | undefined;\n const listAggRoot = aggregateRoot(m);\n if (!m.noAggregate && listAggRoot) {\n try {\n total = await runAggregateCount(m, listAggRoot, where, context.signal);\n } catch (err) {\n // A TRANSIENT failure on a countable table. Keeping the page length\n // here would be the same lie, so `total` stays undefined and the list\n // degrades to unknown-total — but log it, because (unlike\n // `noAggregate`) this is an error, not a design decision.\n console.warn(`[hasura] count failed for ${m.table}; rendering unknown-total`, err);\n }\n }\n // jsonb-spill inverse per row: flatten head/meta so per-type list columns\n // (batch_number, releaseMethodType, …) read as flat fields. Base columns\n // win; a list whose columns are all base is unaffected (the extra\n // flattened keys are simply never rendered).\n const spill = m.jsonbSpill;\n const items = spill\n ? itemsData.items.map((it) => flattenJsonbSpill(it, spill))\n : itemsData.items;\n return { data: items, total };\n },\n\n /**\n * On-demand row count — the answer to \"how many are there?\" on the lists\n * that page without a grand total (`noAggregate`). Never fired implicitly:\n * one source app measured ~55 s for a single COUNT, which is exactly why\n * the list stopped issuing one on every page.\n *\n * `{ total: null }` means \"not countable here at all\" — the registry says\n * this table has no aggregate root, or the resource isn't mapped by this\n * provider. It is a DECISION, and the UI latches the affordance off for the\n * session. A transient backend failure REJECTS instead (it propagates out of\n * `gql`), so a retry stays possible; never conflate the two.\n *\n * The `where` comes from the same {@link composeListWhere} `getList` uses —\n * same filters, same scope, same `fixedFilter` — so the number always\n * describes the rows the list is paging through.\n */\n async count(descriptor, context): Promise<CountResult> {\n // Deny by default: `mapping()` THROWS on an unmapped resource, and a\n // count is an optional nicety — it must degrade to \"unavailable\", never\n // take the page down.\n const m = config.resources[descriptor.resource];\n if (!m) return { total: null };\n const root = aggregateRoot(m);\n if (!root) return { total: null };\n return {\n total: await runAggregateCount(\n m,\n root,\n composeListWhere(m, descriptor, context),\n context.signal,\n ),\n };\n },\n\n async many(descriptor, context) {\n const m = mapping(descriptor.resource);\n if (descriptor.ids.length === 0) return { data: [] };\n\n const pkField = m.pkField ?? 'id';\n const idsWhere = { [pkField]: { _in: descriptor.ids } };\n const where = mergeWhere(\n idsWhere,\n composeListWhere(m, descriptor as unknown as HasuraScopedQuery, context),\n );\n const fields = m.selectFields.join(' ');\n const query = `query Many_${m.table}($where: ${m.table}_bool_exp) {\n items: ${m.table}(where: $where) { ${fields} }\n }`;\n const data = await gql<{ items: unknown[] }>(query, { where }, m.role, context.signal);\n const spill = m.jsonbSpill;\n const items = spill ? data.items.map((item) => flattenJsonbSpill(item, spill)) : data.items;\n return { data: items };\n },\n\n async one(descriptor, context) {\n const m = mapping(descriptor.resource);\n // When a data body lives in a sibling table, nested-select it alongside the\n // row (`document_data { data }`) so the whole record loads in one round trip.\n const dbSelect = m.dataBody ? ` ${m.dataBody.relationship} { ${m.dataBody.column} }` : '';\n const fields = m.selectFields.join(' ') + dbSelect;\n const pkType = m.pkType ?? 'bigint';\n const pkField = m.pkField ?? 'id';\n let item: unknown;\n const root = byPkRoot(m);\n if (m.fixedFilter || root === null) {\n // `<table>_by_pk` accepts no `where`, so a fixed-filter resource fetches\n // its single row via a `where`-scoped `limit: 1` query: the id must ALSO\n // satisfy the fixed slice, so a row of the wrong slice (e.g. a movement\n // id under a work-orders resource) resolves to null → Not found.\n const where = mergeWhere({ [pkField]: { _eq: descriptor.id } }, m.fixedFilter);\n const query = `query One_${m.table}($where: ${m.table}_bool_exp) {\n items: ${m.table}(where: $where, limit: 1) { ${fields} }\n }`;\n const data = await gql<{ items: unknown[] }>(query, { where }, m.role, context.signal);\n item = data.items?.[0] ?? null;\n } else {\n // DISTILLATION NOTE (divergence 1 of 7 — bug fix, l2-web-ui only).\n // The by-pk root's argument is named after the RESOURCE'S pk field\n // (`pkField`, default `'id'`) — not always literally `id`:\n // `marking_codes_by_pk(code: $id)`, not `marking_codes_by_pk(id: $id)`.\n // l2-web-ui shipped this fix (with three regression tests, ported into\n // this package's suite); playground still hardcoded `id` here and\n // would 400 for any mapping with a non-`id` `pkField`. `many()` /\n // `updateOne` / `deleteOne` already parametrized it — the two direct\n // by-pk READ branches (here and `readMutationRecord`) were the\n // stragglers.\n const query = `query One_${m.table}($id: ${pkType}!) {\n item: ${root}(${pkField}: $id) { ${fields} }\n }`;\n const data = await gql<{ item: unknown }>(\n query,\n { id: descriptor.id },\n m.role,\n context.signal,\n );\n item = data.item;\n }\n if (item == null)\n throw {\n kind: 'protocol',\n code: 'record-not-found',\n message: `Not found: ${descriptor.resource}/${descriptor.id}`,\n } satisfies DataError;\n // jsonb-spill inverse: spread the fetched head/meta blobs back onto the\n // flat record so edit forms + the show page read `regulatory_system` etc.\n // as flat fields. Base columns win on collision; the raw head/meta objects\n // are kept (so update can merge them, and tabs that want the blob still\n // have it). Same helper flattens getList rows.\n let result: unknown = m.jsonbSpill ? flattenJsonbSpill(item, m.jsonbSpill) : item;\n // data-body inverse: spread the sibling `data` blob onto the flat record\n // (so the form reads `items` / `products` top-level) and DROP the raw\n // relationship object so it can't round-trip back into `head` on save.\n if (m.dataBody && result && typeof result === 'object') {\n const rec = { ...(result as Record<string, unknown>) };\n const sibling = rec[m.dataBody.relationship];\n const body =\n sibling && typeof sibling === 'object'\n ? (sibling as Record<string, unknown>)[m.dataBody.column]\n : null;\n delete rec[m.dataBody.relationship];\n result =\n body && typeof body === 'object'\n ? { ...rec, ...(body as Record<string, unknown>) }\n : rec;\n }\n return { data: result };\n },\n\n ...(hasCustomQueries\n ? {\n async custom(descriptor, context) {\n return executeCustomOperation(\n descriptor.resource,\n descriptor.operation,\n descriptor.input,\n 'query',\n context.signal,\n );\n },\n }\n : {}),\n },\n mutations: {\n async createOne(descriptor, context) {\n const m = mapping(descriptor.resource);\n const fields = m.selectFields.join(' ');\n const rawInput = descriptor.input as Record<string, unknown>;\n let mainInput = rawInput;\n let dataBody: Record<string, unknown> | undefined;\n if (m.dataBody) {\n const split = partitionDataBody(rawInput, m.dataBody);\n mainInput = split.rest;\n dataBody = split.hasDataBody ? split.dataBody : undefined;\n }\n const object = m.jsonbSpill\n ? partitionJsonbSpill(mainInput, m.jsonbSpill, m.selectFields)\n : mainInput;\n const query = `mutation Create_${m.table}($object: ${m.table}_insert_input!) {\n item: insert_${m.table}_one(object: $object) { ${fields} }\n }`;\n const data = await gql<{ item: unknown }>(query, { object }, m.role, context.signal);\n const result = mutationRecord(m, descriptor.idField, data.item, descriptor.resource);\n if (m.dataBody && dataBody)\n await upsertDataBody(m.dataBody, result.id, dataBody, m.role, context.signal);\n return result;\n },\n\n async updateOne(descriptor, context) {\n const m = mapping(descriptor.resource);\n const fields = m.selectFields.join(' ');\n const rawInput = descriptor.input as Record<string, unknown>;\n let mainInput = rawInput;\n let dataBody: Record<string, unknown> | undefined;\n if (m.dataBody) {\n const split = partitionDataBody(rawInput, m.dataBody);\n mainInput = split.rest;\n dataBody = split.hasDataBody ? split.dataBody : undefined;\n }\n const set = m.jsonbSpill\n ? partitionJsonbSpill(mainInput, m.jsonbSpill, m.selectFields)\n : mainInput;\n const pkField = m.pkField ?? descriptor.idField;\n // DISTILLATION NOTE (divergence 2 of 7 — feature, l2-web-ui only, inert\n // unless asked for). Optimistic-concurrency precondition, added by\n // l2-web-ui (a shop-floor MES) which needs last-write-wins conflict\n // detection on its config/line records; playground never needed it and\n // has no equivalent.\n // Carried here as an OPT-IN: with no `providerOptions.ifUpdatedAt` the\n // two `!= null` checks below are the only added work and every emitted\n // query is byte-identical to the version without this feature.\n //\n // `!= null` (not `!== undefined`) is load-bearing and is itself a\n // shipped bug fix: a caller seeded from a row whose `updated_at` really\n // was `null` (a `string | null` column) passed the `!== undefined`\n // guard, built `{ updated_at: { _eq: null } }`, which Hasura's `_eq`\n // matches NEVER, so every save threw the same `stale-record` conflict a\n // real staleness would and locked that operator out permanently. Both\n // `undefined` and `null` must mean \"no precondition\".\n const ifUpdatedAt = (\n descriptor.providerOptions as { ifUpdatedAt?: string | null } | undefined\n )?.ifUpdatedAt;\n let item: unknown;\n if (Object.keys(set).length === 0) {\n item = await readMutationRecord(m, descriptor.id, context.signal);\n } else if (m.fixedFilter || ifUpdatedAt != null) {\n let where = mergeWhere({ [pkField]: { _eq: descriptor.id } }, m.fixedFilter);\n if (ifUpdatedAt != null) where = mergeWhere(where, { updated_at: { _eq: ifUpdatedAt } });\n const query = `mutation Update_${m.table}($where: ${m.table}_bool_exp!, $set: ${m.table}_set_input!) {\n result: update_${m.table}(where: $where, _set: $set) { affected_rows returning { ${fields} } }\n }`;\n const data = await gql<{ result: { affected_rows: number; returning: unknown[] } }>(\n query,\n { where, set },\n m.role,\n context.signal,\n );\n // A precondition that matches no row must never look like a silent\n // no-op success — whether the server signals it via `affected_rows`\n // or via an empty `returning`, either shape is the same \"someone else\n // changed this record\" fact and must reach React as a `DataError`.\n if (\n ifUpdatedAt != null &&\n ((data.result.affected_rows ?? 0) === 0 || (data.result.returning?.length ?? 0) === 0)\n ) {\n throw {\n kind: 'conflict',\n code: 'stale-record',\n message: `Update for '${descriptor.resource}/${descriptor.id}' matched no row: the record was changed since it was loaded.`,\n } satisfies DataError;\n }\n item = data.result.returning?.[0] ?? null;\n } else {\n const pkType = m.pkType ?? 'bigint';\n const query = `mutation Update_${m.table}($id: ${pkType}!, $set: ${m.table}_set_input!) {\n item: update_${m.table}_by_pk(pk_columns: {${pkField}: $id}, _set: $set) { ${fields} }\n }`;\n const data = await gql<{ item: unknown }>(\n query,\n { id: descriptor.id, set },\n m.role,\n context.signal,\n );\n item = data.item;\n }\n const result = mutationRecord(\n m,\n descriptor.idField,\n item,\n descriptor.resource,\n descriptor.id,\n );\n if (m.dataBody && dataBody)\n await upsertDataBody(m.dataBody, result.id, dataBody, m.role, context.signal);\n return result;\n },\n\n async deleteOne(descriptor, context) {\n const m = mapping(descriptor.resource);\n const fields = m.selectFields.join(' ');\n const pkField = m.pkField ?? descriptor.idField;\n let item: unknown;\n if (m.fixedFilter) {\n const where = mergeWhere({ [pkField]: { _eq: descriptor.id } }, m.fixedFilter);\n const query = `mutation Delete_${m.table}($where: ${m.table}_bool_exp!) {\n result: delete_${m.table}(where: $where) { affected_rows returning { ${fields} } }\n }`;\n const data = await gql<{ result: { returning: unknown[] } }>(\n query,\n { where },\n m.role,\n context.signal,\n );\n item = data.result.returning?.[0] ?? null;\n } else {\n const pkType = m.pkType ?? 'bigint';\n const query = `mutation Delete_${m.table}($id: ${pkType}!) {\n item: delete_${m.table}_by_pk(${pkField}: $id) { ${fields} }\n }`;\n const data = await gql<{ item: unknown }>(\n query,\n { id: descriptor.id },\n m.role,\n context.signal,\n );\n item = data.item;\n }\n mutationRecord(m, descriptor.idField, item, descriptor.resource, descriptor.id);\n return { kind: 'identity' as const, id: descriptor.id };\n },\n\n ...(hasCustomMutations\n ? {\n async custom(descriptor, context) {\n return executeCustomOperation(\n descriptor.resource,\n descriptor.operation,\n descriptor.input,\n 'mutation',\n context.signal,\n );\n },\n }\n : {}),\n },\n live: createHasuraLiveProvider({\n resources: config.resources,\n getToken: config.getToken,\n endpoint,\n role: defaultRole,\n }),\n });\n}\n"]}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["createClient","normalizeDataError","isDataError","record","defineDataProvider"],"sources":["../src/cursor.ts","../src/endpoint.ts","../src/live-provider.ts","../src/data-provider.ts"],"sourcesContent":["import type { DataError } from '@burojs/core';\n\n/**\n * Keyset (\"seek\") cursor pagination for Hasura.\n *\n * ── What a cursor IS here, and why it is synthesised ──────────────────────\n *\n * Hasura issues no opaque cursor tokens of its own — there is no `endCursor`\n * to echo back, no server-side scroll state, nothing. A Relay-style\n * `pageInfo.endCursor` on a Hasura root is a Relay-connection feature of\n * *other* GraphQL servers; the roots this provider talks to take\n * `limit`/`offset`/`order_by`/`where` and nothing else.\n *\n * So the cursor is BUILT by this provider out of the only durable thing a\n * page leaves behind: the ordering values of its last row. The next page is\n * then \"every row that sorts strictly after that row\", expressed as an\n * ordinary `where` — which is why this needs no backend feature at all and\n * works against any Hasura table.\n *\n * It is nevertheless **opaque above the provider**. Nothing outside this\n * package parses, constructs, or compares a cursor: callers receive\n * `pageInfo.nextCursor` and hand it back as `pagination.after`. The encoding\n * below (base64url of a small JSON envelope) exists to make that boundary\n * obvious at a glance, not to hide anything — a decoded cursor is inert\n * data, and the provider re-validates every field of it before it can shape\n * a query.\n *\n * ── Why a total order is mandatory, not a nicety ──────────────────────────\n *\n * \"The rows after this one\" is only well defined if the ordering is TOTAL —\n * i.e. no two rows compare equal. Order a product list by `price_cents` and\n * seven rows share the value 1999: a page ending inside that run leaves the\n * provider unable to say which of the seven it stopped at. `price_cents >\n * 1999` SKIPS the rest of the run; `price_cents >= 1999` REPEATS the ones\n * already shown. Both answers look perfectly plausible on screen, which is\n * what makes the bug expensive.\n *\n * The fix is the standard one: append the primary key as a final\n * tie-breaker, and compare rows lexicographically over the whole ordering\n * (see {@link keysetWhere}). The pk is unique, so ordering columns + pk is\n * always total.\n *\n * ── Why this module refuses instead of falling back ───────────────────────\n *\n * Every refusal below is a case where the provider cannot express the\n * request faithfully. None of them degrades to offset pagination, and that\n * is deliberate: converting \"not expressible\" into a plausible wrong answer\n * is the single defect class this codebase has caught most often (a\n * branchless `_or: []` matching every row; `OP_MAP[op] ?? '_eq'` turning an\n * unknown operator into equality). A cursor list silently served as offset\n * would duplicate and skip rows under concurrent writes and would never\n * report that it had done so.\n */\n\n/** One column of the effective ordering, after the tie-breaker has been applied. */\nexport interface CursorOrderingColumn {\n readonly field: string;\n readonly direction: 'asc' | 'desc';\n}\n\n/**\n * Every refusal this module can raise, as a frozen record so a consumer\n * matches a symbol rather than re-typing a string literal — the same reason\n * `NOT_AUTHENTICATED_CODE` and `FILTER_OPERATOR_UNSUPPORTED_CODE` are\n * exported.\n *\n * `cursor-pagination-unsupported`, the blanket refusal this package threw\n * before cursors existed, is GONE — nothing throws it any more. A consumer\n * that matched on it was matching \"this provider has no cursors at all\",\n * which is no longer true; the honest replacements are these six, each\n * naming a specific request the provider still cannot serve.\n */\nexport const HASURA_CURSOR_CODES = Object.freeze({\n /** The effective ordering cannot be made total — see {@link assertCursorOrderable}. */\n unorderable: 'cursor-unorderable',\n /** `pagination.before` was supplied; only forward paging is implemented. */\n backward: 'cursor-backward-unsupported',\n /** `pagination.limit` is not a positive integer. */\n limit: 'cursor-limit-invalid',\n /** The `after` string is not a cursor this provider minted. */\n malformed: 'cursor-malformed',\n /** The cursor was minted under a different ordering than this request asks for. */\n mismatch: 'cursor-ordering-mismatch',\n /** The boundary row's ordering value is NULL, so no keyset predicate can address it. */\n boundaryNull: 'cursor-boundary-null',\n} as const);\n\nconst CURSOR_VERSION = 1;\n\ninterface CursorEnvelope {\n readonly v: number;\n /** Ordering signature — see {@link orderingSignature}. */\n readonly o: string;\n /** Boundary values, positionally aligned with the ordering. */\n readonly k: readonly (string | number | boolean)[];\n}\n\nfunction cursorError(\n code: (typeof HASURA_CURSOR_CODES)[keyof typeof HASURA_CURSOR_CODES],\n kind: DataError['kind'],\n message: string,\n): DataError {\n return { kind, code, message };\n}\n\n/**\n * The ordering a cursor was minted under, as a single comparable string.\n * Recorded inside the cursor and re-checked on the way back in, so a cursor\n * taken from a list sorted by price cannot be replayed against the same list\n * sorted by name: the keyset predicate would be built from the wrong columns\n * and the resulting page would be arbitrary rather than wrong-looking.\n */\nexport function orderingSignature(ordering: readonly CursorOrderingColumn[]): string {\n return ordering.map((column) => `${column.field}:${column.direction}`).join(',');\n}\n\n/**\n * The ordering this provider will actually emit for a cursor page: the\n * caller's sort (or the mapping's default when there is none), with the\n * primary key appended as the tie-breaker unless the caller already sorts by\n * it.\n *\n * The tie-breaker inherits the direction of the last ordering column rather\n * than being pinned to `asc`. Either choice yields a total order; inheriting\n * keeps a `desc` list reading in one direction all the way down instead of\n * flipping inside each run of equal values, which is what a reader would\n * expect from \"newest first, ties broken by newest id\".\n */\nexport function effectiveCursorOrdering(\n sort: readonly { readonly field: string; readonly direction: 'asc' | 'desc' }[] | undefined,\n mapping: { readonly pkField?: string; readonly orderByField?: string },\n): readonly CursorOrderingColumn[] {\n const pkField = mapping.pkField ?? 'id';\n const requested = (sort ?? []).filter((entry) => Boolean(entry.field));\n const columns: CursorOrderingColumn[] =\n requested.length > 0\n ? requested.map((entry) => ({ field: entry.field, direction: entry.direction }))\n : // The offset path's default is `{<orderByField>: desc}`; the cursor\n // path takes the same default so switching a list between the two\n // pagination modes does not silently reorder it.\n [{ field: mapping.orderByField ?? pkField, direction: 'desc' }];\n if (columns.some((column) => column.field === pkField)) return columns;\n const last = columns[columns.length - 1];\n return [...columns, { field: pkField, direction: last?.direction ?? 'asc' }];\n}\n\n/**\n * REFUSAL 1 — the ordering cannot produce a total order this provider can\n * both emit and read back.\n *\n * Two conditions, both checked EAGERLY (before any request leaves the\n * client), because a refusal that depends on how many rows happen to be in\n * the table is not a refusal — it is a coin flip. That lesson is recorded in\n * `@burojs/mock-kit`: an unsupported operator checked lazily answered `[]`\n * for an empty table and threw for a populated one.\n *\n * 1. An ordering column is not in `selectFields`. The cursor is made of the\n * boundary row's ordering values, and a column the query does not select\n * is not in the row that comes back. This is also how the pk tie-breaker\n * is enforced: a mapping that does not select its own primary key cannot\n * be cursor-paginated at all.\n * 2. A sort entry carries `nulls: 'first' | 'last'`. Hasura expresses that\n * as `asc_nulls_first`/`desc_nulls_last`, and a keyset predicate over a\n * nullable column under those semantics needs `IS NULL` branches this\n * provider does not build. The OFFSET path ignores `nulls` outright —\n * which is a real (pre-existing) infidelity there, but harmless in the\n * sense that offset paging stays self-consistent. Ignoring it here would\n * not: pages would be cut at boundaries the emitted `order_by` does not\n * actually produce, so rows would be skipped.\n */\nexport function assertCursorOrderable(\n ordering: readonly CursorOrderingColumn[],\n sort: readonly { readonly field: string; readonly nulls?: 'first' | 'last' }[] | undefined,\n mapping: { readonly table: string; readonly selectFields: readonly string[] },\n): void {\n const nulled = (sort ?? []).find((entry) => entry.nulls !== undefined);\n if (nulled !== undefined) {\n throw cursorError(\n HASURA_CURSOR_CODES.unorderable,\n 'capability',\n `@burojs/hasura cannot cursor-paginate '${mapping.table}' ordered by '${nulled.field}' with an explicit nulls placement: a keyset predicate over NULLs is not expressible here. Sort without \\`nulls\\`, or use offset pagination.`,\n );\n }\n const selected = new Set(mapping.selectFields);\n const missing = ordering.filter((column) => !selected.has(column.field));\n if (missing.length > 0) {\n throw cursorError(\n HASURA_CURSOR_CODES.unorderable,\n 'capability',\n `@burojs/hasura cannot cursor-paginate '${mapping.table}': the ordering needs ${missing\n .map((column) => `'${column.field}'`)\n .join(\n ', ',\n )}, which the mapping does not select, so no page can carry the value the next cursor is made of. Cursor pagination orders by the sort columns plus the primary key ('${ordering[ordering.length - 1]?.field ?? 'id'}'), and every one of them must be in \\`selectFields\\`.`,\n );\n }\n}\n\n/** Positive-integer page size. A cursor page has no other way to bound itself. */\nexport function assertCursorLimit(limit: unknown, table: string): asserts limit is number {\n if (typeof limit !== 'number' || !Number.isInteger(limit) || limit <= 0) {\n throw cursorError(\n HASURA_CURSOR_CODES.limit,\n 'validation',\n `@burojs/hasura cursor pagination on '${table}' needs a positive integer \\`limit\\`, got ${JSON.stringify(limit)}.`,\n );\n }\n}\n\nfunction base64UrlEncode(text: string): string {\n const bytes = new TextEncoder().encode(text);\n let binary = '';\n for (const byte of bytes) binary += String.fromCharCode(byte);\n const base64 = typeof btoa === 'function' ? btoa(binary) : Buffer.from(bytes).toString('base64');\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\nfunction base64UrlDecode(text: string): string {\n const base64 = text.replace(/-/g, '+').replace(/_/g, '/');\n if (typeof atob === 'function') {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let index = 0; index < binary.length; index += 1) bytes[index] = binary.charCodeAt(index);\n return new TextDecoder().decode(bytes);\n }\n return Buffer.from(base64, 'base64').toString('utf8');\n}\n\n/**\n * Mints the cursor that addresses the row AFTER `row`.\n *\n * REFUSAL 2 — a NULL (or absent) ordering value on the boundary row. A\n * keyset predicate compares with `_gt`/`_lt`, and in SQL every comparison\n * against NULL is NULL, i.e. matches nothing: a cursor minted from a NULL\n * boundary would return an empty next page and the list would look finished\n * while rows remained. Refused rather than silently truncating the list.\n */\nexport function encodeCursor(\n ordering: readonly CursorOrderingColumn[],\n row: Record<string, unknown>,\n table: string,\n): string {\n const values: (string | number | boolean)[] = [];\n for (const column of ordering) {\n const value = row[column.field];\n if (value === null || value === undefined) {\n throw cursorError(\n HASURA_CURSOR_CODES.boundaryNull,\n 'capability',\n `@burojs/hasura cannot mint a cursor for '${table}': the last row of the page has no value for the ordering column '${column.field}', and a keyset predicate over NULL matches nothing. Order by a non-null column, or use offset pagination.`,\n );\n }\n if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') {\n throw cursorError(\n HASURA_CURSOR_CODES.boundaryNull,\n 'capability',\n `@burojs/hasura cannot mint a cursor for '${table}': ordering column '${column.field}' holds ${typeof value}, which has no total order to seek on.`,\n );\n }\n values.push(value);\n }\n const envelope: CursorEnvelope = {\n v: CURSOR_VERSION,\n o: orderingSignature(ordering),\n k: values,\n };\n return base64UrlEncode(JSON.stringify(envelope));\n}\n\n/**\n * REFUSAL 3 / 4 — a cursor this provider did not mint (`malformed`), or one\n * minted under a different ordering (`mismatch`).\n *\n * The mismatch check is the load-bearing half. Sorting a list re-issues the\n * query, and a UI that kept the old `after` around would otherwise get a\n * page built by comparing the NEW ordering's columns against the OLD\n * ordering's values — a query that succeeds and returns rows that are simply\n * not the ones asked for.\n */\nexport function decodeCursor(\n raw: unknown,\n ordering: readonly CursorOrderingColumn[],\n table: string,\n): readonly (string | number | boolean)[] {\n if (typeof raw !== 'string' || raw.length === 0) {\n throw cursorError(\n HASURA_CURSOR_CODES.malformed,\n 'validation',\n `@burojs/hasura got a non-string cursor for '${table}': ${JSON.stringify(raw)}. A cursor is opaque — pass back exactly what \\`pageInfo.nextCursor\\` returned.`,\n );\n }\n let envelope: CursorEnvelope;\n try {\n envelope = JSON.parse(base64UrlDecode(raw)) as CursorEnvelope;\n } catch (cause) {\n throw {\n ...cursorError(\n HASURA_CURSOR_CODES.malformed,\n 'validation',\n `@burojs/hasura could not decode the cursor for '${table}'. A cursor is opaque — pass back exactly what \\`pageInfo.nextCursor\\` returned.`,\n ),\n cause,\n } satisfies DataError;\n }\n if (\n envelope === null ||\n typeof envelope !== 'object' ||\n envelope.v !== CURSOR_VERSION ||\n typeof envelope.o !== 'string' ||\n !Array.isArray(envelope.k)\n ) {\n throw cursorError(\n HASURA_CURSOR_CODES.malformed,\n 'validation',\n `@burojs/hasura got a cursor for '${table}' it did not mint (envelope version ${String(\n (envelope as { v?: unknown } | null)?.v,\n )}).`,\n );\n }\n const signature = orderingSignature(ordering);\n if (envelope.o !== signature) {\n throw cursorError(\n HASURA_CURSOR_CODES.mismatch,\n 'validation',\n `@burojs/hasura refuses a cursor for '${table}' minted under ordering '${envelope.o}' against a request ordered '${signature}': the page it addressed no longer exists. Restart from the first page when the sort changes.`,\n );\n }\n if (envelope.k.length !== ordering.length) {\n throw cursorError(\n HASURA_CURSOR_CODES.malformed,\n 'validation',\n `@burojs/hasura got a cursor for '${table}' carrying ${envelope.k.length} boundary values for ${ordering.length} ordering columns.`,\n );\n }\n return envelope.k;\n}\n\n/**\n * The keyset predicate: \"every row that sorts strictly after the boundary\",\n * as the lexicographic expansion of a row comparison.\n *\n * For ordering `(a asc, b desc, id asc)` and boundary `(a0, b0, id0)` this\n * emits\n *\n * ```\n * _or: [ {a: {_gt: a0}},\n * {_and: [{a: {_eq: a0}}, {b: {_lt: b0}}]},\n * {_and: [{a: {_eq: a0}}, {b: {_eq: b0}}, {id: {_gt: id0}}]} ]\n * ```\n *\n * Postgres's own `(a, b) > (a0, b0)` row comparison would be shorter, but\n * Hasura's `bool_exp` has no row-comparison operator — this expansion is the\n * only form expressible through it, and it is what every keyset\n * implementation over Hasura ends up emitting.\n *\n * NOT written as `>=` on the leading column with a `_neq` correction, and\n * not as a single `_gte`: both of those are the shapes that duplicate or\n * skip rows across a run of equal values, which is the entire failure this\n * function exists to avoid.\n *\n * `int64Fields` coercion is applied for the same reason\n * `coerceInt64Filter` applies it to user filters: Hasura serialises an\n * `int64` column as a JSON string and REJECTS a numeric filter value for it.\n * A keyset predicate is an ordinary filter and gets no exemption.\n */\nexport function keysetWhere(\n ordering: readonly CursorOrderingColumn[],\n boundary: readonly (string | number | boolean)[],\n int64Fields: readonly string[] | undefined,\n): Record<string, unknown> {\n const int64 = new Set(int64Fields ?? []);\n const value = (index: number, field: string): unknown => {\n const raw = boundary[index];\n return int64.has(field) ? String(raw) : raw;\n };\n const branches: Record<string, unknown>[] = [];\n for (let index = 0; index < ordering.length; index += 1) {\n const column = ordering[index];\n if (column === undefined) continue;\n const strict = column.direction === 'desc' ? '_lt' : '_gt';\n const tail = { [column.field]: { [strict]: value(index, column.field) } };\n if (index === 0) {\n branches.push(tail);\n continue;\n }\n const clauses: Record<string, unknown>[] = [];\n for (let prior = 0; prior < index; prior += 1) {\n const priorColumn = ordering[prior];\n if (priorColumn === undefined) continue;\n clauses.push({ [priorColumn.field]: { _eq: value(prior, priorColumn.field) } });\n }\n clauses.push(tail);\n branches.push({ _and: clauses });\n }\n return { _or: branches };\n}\n","/**\n * Same-origin GraphQL path Hasura serves by default. The single source for\n * both halves of this package: `createHasuraDataProvider`'s POST target and\n * `createHasuraLiveProvider`'s WebSocket derivation.\n *\n * It lives in its own module rather than in `data-provider.ts` because\n * `live-provider.ts` needs it too, and `data-provider.ts` already imports\n * `createHasuraLiveProvider` from `live-provider.ts`. `live-provider.ts`'s\n * existing back-reference is `import type { ResourceMapping }` — TYPE-only, so\n * it is erased and no runtime cycle exists. Importing a VALUE back across that\n * edge would create a real one. This third module keeps the constant defined\n * exactly once without introducing it.\n *\n * DISTILLATION NOTE (divergence 5 of 7 — deployment fact, neither side wins).\n * The two source apps read this from their OWN `env.ts`: playground as\n * `API_BASE_URL` (`import.meta.env.VITE_API_BASE_URL ?? ''`, an origin-root\n * deployment), l2-web-ui as `API_PREFIX` (`?? '/tandt'`, a non-empty default\n * that exists because every GraphQL call 404'd behind an nginx rule stripping\n * that prefix while the dev proxy passed paths through unprefixed). Both\n * defaults are correct FOR THEIR OWN DEPLOYMENT and neither belongs in a\n * published package, so this package takes no opinion: it exposes the\n * `endpoint` config field (the mechanism l2-web-ui had already added) and\n * defaults to the bare same-origin path. An app behind a path prefix passes\n * `endpoint: `${prefix}/gql/v1/graphql``.\n */\nexport const DEFAULT_HASURA_ENDPOINT = '/gql/v1/graphql';\n","import {\n type DataLiveProvider,\n type LiveConnectionStatus,\n type LiveObserver,\n normalizeDataError,\n} from '@burojs/core';\nimport { createClient } from 'graphql-ws';\n\nimport type { ResourceMapping } from './data-provider.js';\nimport { DEFAULT_HASURA_ENDPOINT } from './endpoint.js';\n\ninterface SubscriptionPayload {\n readonly query: string;\n readonly variables?: Record<string, unknown>;\n}\n\ninterface SubscriptionSink {\n next(value: unknown): void;\n error(error: unknown): void;\n complete(): void;\n}\n\ninterface GraphqlWsClient {\n subscribe(payload: SubscriptionPayload, sink: SubscriptionSink): () => void;\n}\n\ninterface GraphqlWsClientOptions {\n readonly url: string;\n readonly lazy: boolean;\n readonly keepAlive: number;\n readonly retryAttempts: number;\n readonly shouldRetry: () => boolean;\n readonly connectionParams: () => Promise<Record<string, unknown>>;\n readonly on: {\n connecting(): void;\n connected(): void;\n closed(): void;\n };\n}\n\nexport interface HasuraLiveProviderConfig {\n readonly resources: Record<string, ResourceMapping>;\n readonly getToken: () => Promise<string | null>;\n /**\n * The SAME GraphQL endpoint the data provider POSTs to — Hasura serves\n * subscriptions on that exact path. Converted to `ws(s)://` by\n * {@link hasuraWsUrl}. Defaults to {@link DEFAULT_HASURA_ENDPOINT}.\n *\n * `createHasuraDataProvider` forwards its own resolved endpoint here, so the\n * socket and the POST target can never drift apart.\n */\n readonly endpoint?: string;\n /** `X-Hasura-Role` sent in the socket's connection params. `null`/omitted sends none. */\n readonly role?: string | null;\n /** Test seam; production uses graphql-ws directly. */\n readonly clientFactory?: (options: GraphqlWsClientOptions) => GraphqlWsClient;\n}\n\n/**\n * `https://host/path` → `wss://host/path` (Hasura serves subscriptions on the\n * same path it serves queries on). `endpoint` may be `''`, a same-origin path\n * (`/gql/v1/graphql`, `/tandt/gql/v1/graphql`) or an absolute http(s) URL.\n *\n * DISTILLATION NOTE (divergence 6 of 7 — NEITHER a bug fix, verified\n * equivalent; playground's FORM taken, l2-web-ui's TESTS taken). The two apps\n * implement this differently: playground resolves through\n * `new URL(path, window.location.origin)` and flips the protocol; l2-web-ui\n * string-concatenates `origin.replace(/^http/, 'ws') + prefix + path` under a\n * comment warning about a \"prior version\" bug. That warning describes a\n * since-superseded implementation in l2-web-ui's OWN history, NOT playground's\n * current code — the research pass computed both functions for `('', https)`,\n * `('/tandt', https)`, `('/tandt', http)` and `('', http)` and got\n * byte-identical output every time. playground's `new URL()` form is taken\n * because its input domain is strictly wider (it is also correct when handed\n * an absolute `http(s)://` endpoint, which l2-web-ui's concat form explicitly\n * is not designed for), and l2-web-ui's CONTRACT-level tests (scheme + host +\n * prefix placement, never a literal echo of the implementation) are carried\n * into this package's suite because that is the testing style which caught the\n * original production regression.\n */\nexport function hasuraWsUrl(endpoint: string): string {\n // No window (SSR/unit import without a DOM): fall back to the bare path.\n if (typeof window === 'undefined') return endpoint;\n const url = new URL(endpoint, window.location.origin);\n url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:';\n return url.toString();\n}\n\nfunction listSubscription(mapping: ResourceMapping): SubscriptionPayload {\n const where = mapping.fixedFilter;\n return {\n query: where\n ? `subscription Live_${mapping.table}_list($where: ${mapping.table}_bool_exp) {\n ${mapping.table}_aggregate(where: $where) { aggregate { count max { updated_at } } }\n }`\n : `subscription Live_${mapping.table}_list {\n ${mapping.table}_aggregate { aggregate { count max { updated_at } } }\n }`,\n ...(where === undefined ? {} : { variables: { where } }),\n };\n}\n\nfunction oneSubscription(\n mapping: ResourceMapping,\n id: string | number,\n): SubscriptionPayload | null {\n const pkField = mapping.pkField ?? 'id';\n const pkType = mapping.pkType ?? 'bigint';\n const root = mapping.byPkRoot === false ? null : (mapping.byPkRoot ?? `${mapping.table}_by_pk`);\n if (root === null) return null;\n return {\n query: `subscription Live_${mapping.table}_one($id: ${pkType}!) {\n ${root}(${pkField}: $id) { ${pkField} updated_at }\n }`,\n variables: { id },\n };\n}\n\n/**\n * Native Hasura live provider. The socket transmits only a change signal; rows\n * always come from the regular descriptor query, so mapping/pagination remain\n * single-sourced in the data provider.\n */\nexport function createHasuraLiveProvider(config: HasuraLiveProviderConfig): DataLiveProvider {\n const listeners = new Set<(status: LiveConnectionStatus) => void>();\n const endpoint = config.endpoint ?? DEFAULT_HASURA_ENDPOINT;\n const role = config.role ?? null;\n let client: GraphqlWsClient | undefined;\n\n const status = (value: LiveConnectionStatus) => {\n for (const listener of listeners) listener(value);\n };\n\n function getClient(): GraphqlWsClient {\n if (client !== undefined) return client;\n const options: GraphqlWsClientOptions = {\n url: hasuraWsUrl(endpoint),\n lazy: true,\n keepAlive: 10_000,\n retryAttempts: Number.POSITIVE_INFINITY,\n shouldRetry: () => true,\n connectionParams: async () => {\n const token = await config.getToken();\n return {\n headers: {\n ...(token ? { Authorization: `Bearer ${token}` } : {}),\n ...(role ? { 'X-Hasura-Role': role } : {}),\n },\n };\n },\n on: {\n connecting: () => status('connecting'),\n connected: () => status('connected'),\n closed: () => status('degraded'),\n },\n };\n client =\n config.clientFactory?.(options) ??\n (createClient(options as never) as unknown as GraphqlWsClient);\n return client;\n }\n\n return {\n /**\n * The push-invalidation opt-in list, read straight off the same\n * `resources` map `open` consults — one source, so the two can never\n * disagree. The runtime uses it at registration to refuse an opt-in\n * naming a resource this provider does not serve, which `open` alone can\n * never notice: it is only ever called for descriptors already routed\n * here.\n */\n declaredPushInvalidation() {\n return Object.entries(config.resources)\n .filter(([, mapping]) => mapping?.live === true)\n .map(([resource]) => resource);\n },\n async open(descriptor, openContext) {\n if (openContext.signal.aborted) return null;\n const mapping = config.resources[descriptor.resource];\n if (mapping?.live !== true) return null;\n const payload =\n descriptor.kind === 'list'\n ? listSubscription(mapping)\n : descriptor.kind === 'one'\n ? oneSubscription(mapping, descriptor.id)\n : null;\n if (payload === null) return null;\n\n return {\n kind: 'invalidate' as const,\n initial: 'query' as const,\n subscribe(observer: LiveObserver, subscribeContext) {\n if (openContext.signal.aborted || subscribeContext.signal.aborted) return () => undefined;\n let primed = false;\n let closed = false;\n let upstream: (() => void) | undefined = undefined;\n\n const close = () => {\n if (closed) return;\n closed = true;\n listeners.delete(onStatus);\n openContext.signal.removeEventListener('abort', close);\n subscribeContext.signal.removeEventListener('abort', close);\n upstream?.();\n };\n const onStatus = (value: LiveConnectionStatus) => {\n if (!closed) observer.status(value);\n };\n const sink: SubscriptionSink = {\n next: () => {\n if (closed) return;\n // Hasura immediately emits the subscription's current snapshot.\n // The normal query is the baseline for this plan, so only later\n // payloads signal a refetch.\n if (!primed) {\n primed = true;\n return;\n }\n observer.next(undefined);\n },\n error: (error) => {\n if (closed) return;\n observer.error(normalizeDataError(error));\n // An operation-level GraphQL error is terminal for this\n // subscription. Close it eagerly; socket-level reconnect events\n // arrive through the client hooks instead.\n close();\n },\n complete: () => {\n // Socket reconnect is transport-owned. Do not complete the plan:\n // graphql-ws can resubscribe this operation after reconnect.\n if (!closed) observer.status('degraded');\n },\n };\n\n listeners.add(onStatus);\n openContext.signal.addEventListener('abort', close, { once: true });\n subscribeContext.signal.addEventListener('abort', close, { once: true });\n upstream = getClient().subscribe(payload, sink);\n // DISTILLATION NOTE (divergence 7 of 7 — bug fix, playground only,\n // the ONE that went the opposite way from every other divergence).\n // If `sink.error(...)`\n // fires SYNCHRONOUSLY inside `subscribe(...)` — before the\n // assignment above completes — `close()` runs with `upstream` still\n // `undefined`, so its `upstream?.()` is a silent no-op and the real\n // unsubscribe handle, assigned moments later, is never called: an\n // open graphql-ws subscription registration leaks. playground\n // shipped this one-line guard (git-confirmed as a dedicated fix —\n // commit c89ca660 — with its regression test carried into this\n // package's suite verbatim); l2-web-ui never received it, and leaked\n // until this branch backported the guard to it (99e670aa). Stated in\n // the past tense on purpose: the earlier wording said \"leaks to this\n // day\", which the backport made false four commits later.\n if (closed) upstream();\n if (openContext.signal.aborted || subscribeContext.signal.aborted) close();\n // Promise form intentionally makes coordinator-owned late-handle\n // disposal observable; close itself is idempotent across both paths.\n return Promise.resolve(close);\n },\n };\n },\n };\n}\n","import {\n type CountResult,\n type DataError,\n type DataOperationContext,\n type DataProvider,\n type ErasedResourceHandle,\n type FilterExpression,\n defineDataProvider,\n isDataError,\n} from '@burojs/core';\nimport {\n HASURA_CURSOR_CODES,\n assertCursorLimit,\n assertCursorOrderable,\n decodeCursor,\n effectiveCursorOrdering,\n encodeCursor,\n keysetWhere,\n} from './cursor.js';\nimport { DEFAULT_HASURA_ENDPOINT } from './endpoint.js';\nimport { createHasuraLiveProvider } from './live-provider.js';\n\n/**\n * One entry of a descriptor's `sort`, in the shape this provider reads it.\n * Structural rather than imported from `@burojs/core`'s `SortExpression`,\n * which is generic over a resource handle the erased provider slots never\n * see.\n */\ninterface HasuraSortEntry {\n readonly field: string;\n readonly direction: 'asc' | 'desc';\n readonly nulls?: 'first' | 'last';\n}\n\nexport interface HasuraDataProviderConfig {\n /** Returns a fresh JWT or null if user is not authed. */\n getToken: () => Promise<string | null>;\n /**\n * GraphQL endpoint every query/mutation is POSTed to, and the endpoint the\n * live provider derives its WebSocket URL from. May be a same-origin path\n * (`/gql/v1/graphql`, `/tandt/gql/v1/graphql`) or an absolute http(s) URL.\n * Defaults to {@link DEFAULT_HASURA_ENDPOINT}.\n */\n endpoint?: string;\n /**\n * Hasura role sent as `X-Hasura-Role` for resources whose mapping does not\n * declare a `role` of its own. `null` (the default) sends no role header at\n * all, leaving the JWT's own default role in force.\n *\n * A resource mapping's `role` always wins when it is defined — including\n * `role: null`, which suppresses the header for that one resource.\n *\n * `''` is treated as `null`, in both halves of the package: it is the shape\n * of a missing env var, not a role. See {@link normalizeRole}.\n */\n defaultRole?: string | null;\n /**\n * Revalidates authentication after a request comes back with HTTP **401**.\n * The callback owns any terminal sign-out; returning true permits one retry\n * with a newly obtained token, while false preserves the original 401\n * response.\n *\n * HTTP 401 is what a proxy or gateway in front of Hasura answers. Hasura\n * itself rejects a credential with **HTTP 200** and an `invalid-jwt` errors\n * envelope (`packages/mock-kit/fixtures/hasura/error-unauthorized.json`,\n * captured from a live backend). This provider reports that envelope as\n * `{ kind: 'auth', code: 'invalid-jwt' }`. When the app has an\n * `authProvider`, the runtime hands it that error; if the provider chooses\n * to refresh, a read is replayed once, and a mutation once when it is\n * replay-safe. `recoverAuth` is\n * not consulted for the envelope, so one rejection is never refreshed twice.\n * Pinned by `tests/fixture-fidelity.test.ts`'s `error-unauthorized` case.\n */\n recoverAuth?: () => Promise<boolean>;\n /** Map our resource name → table + field selection. */\n resources: Record<string, ResourceMapping>;\n /**\n * Optional global scope read on every getList. Returns key → value pairs\n * that get merged into the where clause as `{ <key>: { _eq: <value> } }`,\n * but only for resources that whitelist the key in `scopeFields`.\n *\n * Example: `() => ({ pg: 'radio', location_id: 5 })` — applied to documents\n * (which have both fields) but not products (which only have pg).\n */\n getScope?: (context: DataOperationContext) => Record<string, unknown>;\n /**\n * Closed, application-owned custom operation registry. A descriptor selects\n * an entry by its already-declared `{ resource, operation }` binding; it can\n * never supply a GraphQL document, URL, or result mapper itself.\n */\n operations?: HasuraOperationRegistry;\n fetchImpl?: typeof fetch;\n}\n\nexport interface HasuraCustomOperation {\n /** Which native provider slot may execute this declaration. */\n readonly kind: 'query' | 'mutation';\n /** Static GraphQL document owned by the application/provider integration. */\n readonly document: string;\n /** Maps the declared operation input to the document's variables. */\n readonly variables: (input: unknown) => Record<string, unknown> | undefined;\n /** Maps the full GraphQL response to the declared operation output. */\n readonly mapResult?: (data: unknown) => unknown;\n /** Overrides the mapped resource role; undefined keeps that mapped role. */\n readonly role?: string | null;\n}\n\n/**\n * Registry entries are keyed by the resource binding first, then the declared\n * custom operation name. This makes cross-resource operation reuse explicit\n * rather than allowing a descriptor to name an arbitrary backend operation.\n */\nexport type HasuraOperationRegistry = Readonly<\n Record<string, Readonly<Record<string, HasuraCustomOperation>>>\n>;\n\nexport interface ResourceMapping {\n table: string;\n selectFields: string[]; // fields to select in queries\n /**\n * GraphQL scalar name for this table's primary key, interpolated into the\n * `$id` variable declaration of every by-pk query/mutation. Default\n * `'bigint'`.\n *\n * DISTILLATION NOTE (divergence 3 of 7 — feature, additive, l2-web-ui only).\n * l2-web-ui added `'String'` for tables whose pk is a genuine string\n * business key (a GS1 marking code), not a uuid/Int/bigint\n * wire type: Hasura's by-pk root for such a table is literally\n * `<table>_by_pk(<pk>: $id)` with `$id: String!`, which none of the other\n * three names can express (playground's union stopped at\n * `'bigint' | 'uuid' | 'Int'`). Purely a type-union widening — no runtime\n * branch reads `pkType` beyond interpolating it — so l2-web-ui's wider union\n * is taken wholesale rather than forked.\n */\n pkType?: 'bigint' | 'uuid' | 'Int' | 'String'; // default 'bigint'\n /**\n * Skip the aggregate on every `getList`. This is a COST decision, not a\n * schema fact: the table has an `_aggregate` root but counting it on each\n * page is too slow to pay for unprompted. (Measured in one of the apps this\n * provider was distilled from: ~9.8M-row and ~28.9M-row tables, the latter\n * taking ~55 s per count.) `total` is then omitted and the list renders the\n * unknown-total shape.\n *\n * ⚠️ NOT the same thing as {@link ResourceMapping.aggregate} `: false`, and\n * NOT a proxy for it — most `noAggregate` tables CAN be counted on demand,\n * which is exactly what `getCount` is for. Deriving one from the other gets\n * the flagship resources backwards.\n */\n noAggregate?: boolean;\n /**\n * Which aggregate root field backs this table, for the on-demand `getCount`\n * (and for `getList`'s own count when it runs):\n * - absent — derive `${table}_aggregate` (the Hasura default; today's behaviour);\n * - `false` — no aggregate root exists at all, under any name. `getCount`\n * answers `{ total: null }` without a round trip and the UI drops the\n * affordance for the session;\n * - a string — an explicitly-named root, for tables whose custom root fields\n * break the derivation.\n *\n * ⚠️ Establish this by introspecting the live schema under the role the app\n * actually runs as, not by reading `noAggregate` or a code comment. Both\n * have been wrong here.\n */\n aggregate?: false | string;\n /**\n * Root field for a single-row fetch by primary key. Defaults to the derived\n * `${table}_by_pk`. Set `false` when the active role exposes only the\n * collection root; `getOne` then queries it with pk `where` + `limit: 1`.\n *\n * ⚠️ Same class of fact as `aggregate`, and established the same way — by\n * introspecting the live schema, not by reading a comment. In one of the\n * source apps exactly one table of forty had a non-derivable by-pk root (a\n * plural table whose root was singular), so the derived name 400'd and its\n * Show page was a dead-end error reachable in two clicks from the sidebar.\n */\n byPkRoot?: string | false;\n /** Field to order by in getList. Defaults to 'id'. */\n orderByField?: string;\n /** Name of the primary-key field in the row (for synthetic id in list rows). Defaults to 'id'. */\n pkField?: string;\n /**\n * Hasura role for this resource. `null` (or `''`) means no role header (use\n * the JWT's default role). Undefined falls back to\n * {@link HasuraDataProviderConfig.defaultRole}.\n */\n role?: string | null;\n /**\n * Columns typed `int64` in a Hasura ClickHouse source. Hasura serialises\n * int64 as a JSON STRING and REJECTS a numeric filter value with\n * «expected a string for type 'int64'» — so a `document_id` filter sent as a\n * number matched nothing and the affected list tabs came back empty.\n * Filter values for these fields are coerced to strings before the where\n * clause is built. (Verified live: numeric/int literal → error, string → rows.)\n */\n int64Fields?: string[];\n /**\n * Whitelist of scope keys this table actually has columns for. Only these\n * keys from `getScope()` get injected into where clauses. Defaults to `[]`\n * (no scope injection — useful for tables that lack the scope columns).\n *\n * ⚠️ SCOPE NARROWS THREE READ PATHS, NOT FIVE. `scopeToWhere` is reached\n * only through {@link composeListWhere}, i.e. from `queries.list` (both\n * branches), `queries.count` and `queries.many`. **`queries.one` composes\n * `{pk _eq id} ∧ fixedFilter ∧ localeSlice` and carries NO scope**, so a\n * record excluded from a scoped list is still readable by its id through\n * this provider. `fixedFilter` — a slice that cannot depend on the session —\n * does cover `one`; the session-dependent pair (`getScope` + `scopeFields`)\n * does not. Treat a scoped projection as a LIST narrowing, and do not rely\n * on it as an authorization boundary: that belongs in the backend's own row\n * permissions, which is where a real Hasura deployment enforces it. Raised\n * by the SP4 final review (M3); closing it is a change to the by-pk path\n * with its own blast radius and is carried as a follow-up rather than made\n * here.\n */\n scopeFields?: string[];\n /**\n * JSONB-spill routing for tables whose forms submit a flat values record but\n * whose real columns nest most of those keys inside JSONB blobs (e.g. a\n * `documents` table where template head fields live inside `head`, a couple\n * live inside `meta`, and only a handful — type/status/… — are real columns).\n *\n * When set, create/update partition the incoming flat `variables`:\n * - keys that ARE real columns (present in `selectFields`) stay top-level;\n * - keys listed in `metaKeys` nest under `metaColumn`;\n * - every OTHER key nests under `column` (the head spill).\n * getOne does the inverse — it spreads the fetched `column`/`metaColumn`\n * objects back onto the flat record so edit forms / show pages read those\n * keys as flat fields (the raw blobs are kept too).\n *\n * SCOPE: scalar / relation / enum head+meta fields only. Data-body arrays\n * (children) live in a sibling table — see `dataBody`.\n */\n jsonbSpill?: JsonbSpillConfig;\n /**\n * Data-body routing for resources whose heavy array payload lives in a\n * SEPARATE 1:1 sibling table rather than a JSONB column on the row itself\n * (e.g. a `documents.items[]` / `products[]` payload living in\n * `documents_data.data`, keyed by `document_id`).\n *\n * When set, create/update peel the `keys` off the flat `variables` FIRST\n * (before `jsonbSpill` runs, so they never leak into `head`), write the parent\n * row, then upsert the sibling row (`insert_<table>_one … on_conflict`). getOne\n * nested-selects the sibling via `relationship` and spreads its `column` blob\n * back onto the flat record (so edit forms read `items` as a top-level field),\n * dropping the raw relationship object so it can't round-trip into `head`.\n * getList is left untouched — list columns are head/meta scalars, never the\n * heavy data body.\n */\n dataBody?: DataBodyConfig;\n /**\n * Fixed, always-on Hasura `where` fragment ANDed into every getList /\n * getOne query for this resource. Powers \"per-type\" / projection resources:\n * several resource names can map to the SAME table, each scoped to a\n * different slice — e.g. `fixedFilter: { type: { _eq: 'work_order' } }` turns\n * a `work_orders` resource into a work-orders-only view of a shared\n * `documents` table. The fragment is a raw Hasura `<table>_bool_exp` object\n * (same shape `filtersToWhere` emits), so nested / `_and` / `_or` clauses are\n * all valid. Merged with `mergeWhere` (AND) after user filters + scope, so\n * the user can still filter/search WITHIN the fixed slice.\n *\n * getOne: because `<table>_by_pk` takes no `where`, a resource with a\n * `fixedFilter` fetches its single row via a `where`-based `limit: 1` query\n * (pk `_eq` id AND fixedFilter) instead — so the row is only returned when it\n * actually belongs to the slice (a work-order id won't resolve under\n * `movements`). Resources without a `fixedFilter` keep the `_by_pk` path.\n */\n fixedFilter?: Record<string, unknown>;\n /**\n * The column on this table that holds a row's LANGUAGE, for a backend that\n * translates by storing one row per locale (the \"sidecar translation table\"\n * shape: `catalog_product_translation(product_id, locale, name, …)`).\n *\n * When set, every read handler of this resource ANDs `{ <localeField>: {\n * _eq: <descriptor.locale> } }` into its `where` — `queries.list` (both the\n * offset and the cursor branch), `queries.count`, `queries.many` and the\n * by-pk `queries.one` alike — so the caller gets exactly the active\n * language's rows.\n *\n * ⚠️ EXACTLY FOUR HANDLERS, not \"every read anywhere in this module\", and\n * the difference is worth stating because an earlier revision of this\n * sentence was broader than the code. Two reads are deliberately outside it:\n *\n * - `readMutationRecord` — the re-read a mutation performs to return the\n * row it just wrote. It addresses ONE row the caller has just named by\n * primary key, and per the write rule below the caller supplied its\n * `locale` themselves. Narrowing it by the UI's active language could\n * only turn a successful write into a `null` result for a row the caller\n * is holding in its hand.\n * - `queries.many` with an EMPTY id set — see the note at its early\n * return; it refuses eagerly (`localeWhere` is called before the empty\n * read short-circuits) precisely so this list does not need a third\n * exception.\n * `descriptor.locale` is put there by `@burojs/core`'s\n * `resolveDescriptorLocale`, which fills it ONLY for a resource declaring\n * `data: { localeVarying: true }`.\n *\n * ⚠️ THE TWO DECLARATIONS MUST AGREE, and this provider REFUSES rather than\n * guessing when they do not. A mapping with a `localeField` whose descriptor\n * carries no locale means the resource forgot `localeVarying`, and the\n * consequence is not cosmetic: the query returns EVERY language's rows —\n * three rows per product, in mixed languages, with duplicate business keys —\n * which is a plausible-looking wrong answer, the defect class this package\n * refuses on four other paths (cursor orderability, unsupported operators,\n * unmapped resources, absent aggregate roots). So the read throws\n * {@link LOCALE_REQUIRED_CODE} naming both halves of the fix.\n *\n * ⚠️ WRITES ARE NOT TOUCHED. `create`/`update`/`delete` do not inject the\n * locale: a mutation on a translation row must name its own `locale` in the\n * submitted values (it is a real column), and a by-pk update addresses one\n * row by its own primary key, which already identifies the language. Silently\n * stamping the UI's active language onto a write would be a different and\n * much worse guess than the one refused above.\n *\n * Why a column name here rather than a Hasura ARGUMENT: this provider selects\n * FLAT COLUMNS (see `selectFields`), and `ARRAY_RELATION_ARGS` carries no\n * nested `where`, so a translated value cannot be pulled through a nested\n * relation selection on the parent row at all. Reading the translation table\n * as its own root query, narrowed by locale, is the access path the schema\n * shape actually supports.\n */\n localeField?: string;\n /**\n * Opt this resource into real-time updates. The live provider then opens a\n * cheap change-signal subscription for its list / single-record views and\n * invalidates matching native runtime queries when the table changes.\n *\n * Off by default on purpose: every live resource holds an open server-side\n * subscription, so it is a cost you opt into for data that actually moves\n * (documents change constantly during processing; a country lookup does not).\n */\n live?: boolean;\n}\n\n/** @see ResourceMapping.jsonbSpill */\nexport interface JsonbSpillConfig {\n /** JSONB column that catches every non-base, non-meta key (e.g. `head`). */\n column: string;\n /** JSONB column that catches the `metaKeys` (e.g. `meta`). */\n metaColumn: string;\n /** Flat keys routed to `metaColumn` instead of the `column` spill. */\n metaKeys: string[];\n /**\n * Flat form key → dotted NESTED path inside the head `column`. Lets a flat\n * field (e.g. `vsd_number`) persist to a nested head location (e.g.\n * `regulator_data.crpt.vsd_number`) without dotted field keys. getOne reads\n * the nested value back out to the flat key.\n */\n nestedKeys?: Record<string, string>;\n}\n\n/** Set `obj.a.b.c = value`, creating intermediate objects. */\nfunction setNestedPath(obj: Record<string, unknown>, path: string, value: unknown): void {\n const parts = path.split('.');\n const leaf = parts.pop();\n // `String.split` always yields at least one element, so this never fires at\n // runtime; it is how the leaf is narrowed without a non-null assertion.\n if (leaf === undefined) return;\n let cur = obj;\n for (const p of parts) {\n const next = cur[p];\n if (typeof next !== 'object' || next === null) cur[p] = {};\n cur = cur[p] as Record<string, unknown>;\n }\n cur[leaf] = value;\n}\n\n/** Read `obj.a.b.c` (undefined if any segment is missing). */\nfunction getNestedPath(obj: Record<string, unknown>, path: string): unknown {\n let cur: unknown = obj;\n for (const p of path.split('.')) {\n if (cur == null || typeof cur !== 'object') return undefined;\n cur = (cur as Record<string, unknown>)[p];\n }\n return cur;\n}\n\n/** @see ResourceMapping.dataBody */\nexport interface DataBodyConfig {\n /** Sibling table holding the heavy data body, 1:1 with the parent (e.g. `documents_data`). */\n table: string;\n /** FK column on the sibling pointing back at the parent pk (e.g. `document_id`). */\n fk: string;\n /** JSONB column on the sibling that holds the whole data body (e.g. `data`). */\n column: string;\n /** Reverse object-relationship name on the PARENT row → sibling, for the getOne nested select (e.g. `document_data`). */\n relationship: string;\n /** `on_conflict` constraint enum for the upsert (e.g. `document_data_pkey`). */\n constraint: string;\n /** Flat value keys that belong to the data body — routed to the sibling `column` (e.g. `['items', 'products']`). */\n keys: string[];\n}\n\n/**\n * Split a flat `values` record for a data-body resource into the sibling-table\n * payload and the remainder. Keys listed in `cfg.keys` whose value is defined\n * are collected into `dataBody` (they become the sibling `column` blob); every\n * other key passes through in `rest` (to be further partitioned by jsonbSpill).\n * The raw `cfg.relationship` key (the nested object getOne leaves behind on a\n * re-submitted record) is dropped from both so it can never leak into `head`.\n *\n * `hasDataBody` is true iff at least one data-body key was present — callers\n * skip the sibling write entirely when false, so a document type WITHOUT a data\n * body (e.g. a work order) never gets an empty sibling row.\n *\n * Exported for unit testing; the provider calls it with `m.dataBody`.\n */\nexport function partitionDataBody(\n values: Record<string, unknown>,\n cfg: DataBodyConfig,\n): { dataBody: Record<string, unknown>; rest: Record<string, unknown>; hasDataBody: boolean } {\n const keySet = new Set(cfg.keys);\n const dataBody: Record<string, unknown> = {};\n const rest: Record<string, unknown> = {};\n let hasDataBody = false;\n for (const [key, value] of Object.entries(values)) {\n if (key === cfg.relationship) continue; // never round-trip the raw nested rel object\n if (keySet.has(key)) {\n if (value !== undefined) {\n dataBody[key] = value;\n hasDataBody = true;\n }\n } else {\n rest[key] = value;\n }\n }\n return { dataBody, rest, hasDataBody };\n}\n\n/**\n * Partition a flat `values` record for a jsonb-spill resource.\n *\n * Routing rule (first match wins per key):\n * 1. `key === spill.column` / `key === spill.metaColumn` — an explicitly passed\n * JSONB object (e.g. an edit form re-submitting the raw `head` blob). Its\n * entries are merged UNDER any individually-spilled keys so freshly edited\n * flat values win over the stale blob, and un-modelled legacy keys survive.\n * 2. `key ∈ metaKeys` — nests under `metaColumn`.\n * 3. `key ∈ baseColumns` — stays a top-level column.\n * 4. otherwise — nests under the `column` spill (head).\n *\n * `undefined` values are dropped (a never-set optional field must not clobber\n * an existing column / blob key). Empty JSONB objects are omitted entirely.\n *\n * Exported for unit testing; the provider calls it with `baseColumns =\n * m.selectFields` (the table's real columns, which include `column`/`metaColumn`\n * themselves — handled by rule 1 before the base check).\n */\nexport function partitionJsonbSpill(\n values: Record<string, unknown>,\n spill: JsonbSpillConfig,\n baseColumns: string[],\n): Record<string, unknown> {\n const baseSet = new Set(baseColumns);\n const metaSet = new Set(spill.metaKeys);\n const nestedKeys = spill.nestedKeys ?? {};\n const base: Record<string, unknown> = {};\n const headExplicit: Record<string, unknown> = {};\n const metaExplicit: Record<string, unknown> = {};\n const headSpill: Record<string, unknown> = {};\n const metaSpill: Record<string, unknown> = {};\n const nestedSpill: Array<[string, unknown]> = []; // [nested head path, value]\n\n for (const [key, value] of Object.entries(values)) {\n if (value === undefined) continue;\n const nestedPath = nestedKeys[key];\n if (key === spill.column) {\n if (value && typeof value === 'object')\n Object.assign(headExplicit, value as Record<string, unknown>);\n } else if (key === spill.metaColumn) {\n if (value && typeof value === 'object')\n Object.assign(metaExplicit, value as Record<string, unknown>);\n } else if (metaSet.has(key)) {\n metaSpill[key] = value;\n } else if (nestedPath) {\n nestedSpill.push([nestedPath, value]);\n } else if (baseSet.has(key)) {\n base[key] = value;\n } else {\n headSpill[key] = value;\n }\n }\n\n // Individually-spilled keys win over the merged raw blob (edited value beats\n // the stale object; keys only present in the blob are preserved).\n const head = { ...headExplicit, ...headSpill };\n // Nested head keys (e.g. regulator_data.crpt.vsd_number) applied last.\n for (const [path, value] of nestedSpill) setNestedPath(head, path, value);\n const meta = { ...metaExplicit, ...metaSpill };\n const out: Record<string, unknown> = { ...base };\n if (Object.keys(head).length > 0) out[spill.column] = head;\n if (Object.keys(meta).length > 0) out[spill.metaColumn] = meta;\n return out;\n}\n\n/**\n * Inverse of {@link partitionJsonbSpill}: spread a fetched row's `head` / `meta`\n * JSONB blobs back onto the flat record so head/meta keys (e.g. `batch_number`,\n * `regulatory_system`) read as top-level fields on show pages, edit forms, AND\n * per-type list columns. Base columns win on collision (`...rec` last), and the\n * raw `head` / `meta` objects are preserved so tabs / re-submission still have\n * them. A non-object / null blob is ignored.\n *\n * Shared by getOne (single record) and getList (each row) so the two stay in\n * lockstep. Returns the input unchanged when it isn't an object.\n */\nexport function flattenJsonbSpill(item: unknown, spill: JsonbSpillConfig): unknown {\n if (!item || typeof item !== 'object') return item;\n const rec = item as Record<string, unknown>;\n const headObj = rec[spill.column];\n const metaObj = rec[spill.metaColumn];\n const out: Record<string, unknown> = {\n ...(headObj && typeof headObj === 'object' ? (headObj as Record<string, unknown>) : {}),\n ...(metaObj && typeof metaObj === 'object' ? (metaObj as Record<string, unknown>) : {}),\n ...rec,\n };\n // Read nested head paths back out to their flat form keys.\n if (spill.nestedKeys && headObj && typeof headObj === 'object') {\n for (const [flatKey, path] of Object.entries(spill.nestedKeys)) {\n const v = getNestedPath(headObj as Record<string, unknown>, path);\n if (v !== undefined) out[flatKey] = v;\n }\n }\n return out;\n}\n\n/**\n * This provider's own code for \"no token to send\" — `getToken()` resolved `null`, so no\n * request was ever attempted. Exported so downstream error-classification code can tell\n * this apart from a genuine rejected-credentials answer (a real 401 the retry/`recoverAuth`\n * path still couldn't clear) without inventing a parallel distinction of its own: a request\n * that never left the client is the \"we never asked\" case, not a session failure.\n *\n * DISTILLATION NOTE (divergence 4 of 7 — hygiene, l2-web-ui only, zero\n * behaviour change). playground inlined the literal `'not-authenticated'` at\n * both throw sites; l2-web-ui extracted and exported this constant so its own\n * `classify-data-error.ts` could match a symbol instead of a string. The STRING\n * VALUE is identical either way — nothing observable differs — so l2-web-ui's\n * exported form is taken purely because a published package owes consumers a\n * stable symbol.\n */\nexport const NOT_AUTHENTICATED_CODE = 'not-authenticated';\n\ninterface GqlError {\n readonly message: string;\n readonly extensions?: { readonly code?: unknown };\n}\n\ninterface GqlResponse<T> {\n data?: T;\n errors?: GqlError[];\n}\n\ntype GqlOperation = 'query' | 'mutation';\n\nconst FORBIDDEN_CODES: ReadonlySet<string> = new Set(['access-denied', 'permission-error']);\n\n/**\n * The envelope codes with which Hasura reports a failure of work it ran OUTSIDE its own\n * transaction — an Action's webhook or a remote schema — on a custom mutation. Such a side effect\n * may have happened before the failure, so a custom mutation answered with one of them is\n * `sent` (spec 4.7, P4-20). A table mutation keeps `protocol/graphql-error` for them: Hasura\n * rolled its transaction back.\n *\n * Spellings confirmed against the Hasura GraphQL Engine v2 source, `instance ToJSON Code` in\n * `server/lib/hasura-base/src/Hasura/Base/Error.hs` (hasura/graphql-engine@0a1dd53b):\n * `Unexpected -> \"unexpected\"`, `RemoteSchemaError -> \"remote-schema-error\"`,\n * `ParseFailed -> \"parse-failed\"`. Action failures are raised in\n * `server/src-lib/Hasura/GraphQL/Execute/Action.hs`: an HTTP exception calling the webhook, a\n * webhook 5xx, an unparsable webhook answer and a failed request or response transform are all\n * `throw500…`, i.e. `unexpected`; a webhook that answered 2xx (its side effect done) with JSON\n * that does not decode fails in `decodeValue` (`Action.hs:793`), which raises `ParseFailed`\n * through `liftIResult` (`Error.hs:424`), i.e. `parse-failed`. Hasura has no\n * `action-webhook-error` or `http-exception` code; a webhook's own 4xx answer carries the code\n * the webhook chose (`ActionWebhookCode`), or `unexpected` when it chose none, and only that\n * last case is caught here — a webhook-chosen code is the handler's own definite answer.\n *\n * Hasura gives no marker telling an Action-backed or remote-schema mutation apart from any\n * other registered `operations` document, so every custom mutation is treated this way\n * (fail-safe: a wrongly `sent` answer costs a \"may have been applied\" screen, never a duplicate).\n */\nexport const HASURA_EXTERNAL_FAILURE_CODES: ReadonlySet<string> = new Set([\n 'unexpected',\n 'remote-schema-error',\n 'parse-failed',\n]);\n\n/** Envelope codes that are Hasura's definite refusal of a mutation, and the kind each becomes. */\nconst MUTATION_REFUSAL_CODES: ReadonlyMap<string, 'conflict' | 'validation'> = new Map([\n ['constraint-violation', 'conflict'],\n ['validation-failed', 'validation'],\n]);\n\n/**\n * Hasura answers every GraphQL-level failure with HTTP 200, so the envelope's\n * `extensions.code` is the only classification it sends. A refused caller is\n * `forbidden` and a rejected credential `auth` (which the runtime recovers\n * from). On a mutation, `constraint-violation` is `conflict` and\n * `validation-failed` is `validation`. On a custom mutation an external failure\n * ({@link HASURA_EXTERNAL_FAILURE_CODES}) is `sent`, and so are those two refusal\n * codes, and the permission codes (`access-denied`, `permission-error`): a custom\n * document may combine an Action or remote-schema root field with a table root field,\n * and the envelope does not say whether the external field ran before the table field\n * was refused. Telling them apart would need the document's\n * schema, so every custom mutation takes the fail-safe answer (a \"may have been\n * applied\" screen, never a duplicate). Everything else stays\n * `protocol/graphql-error` — on a read, `validation-failed` too: it names a\n * query that does not fit the schema, not input the user can correct.\n */\nfunction graphqlError(\n errors: readonly GqlError[],\n operation: GqlOperation,\n external: boolean,\n): DataError {\n const message = errors.map((e) => e.message).join('; ');\n const codes = errors.flatMap((e) =>\n typeof e.extensions?.code === 'string' ? [e.extensions.code] : [],\n );\n if (codes.includes('invalid-jwt'))\n return { kind: 'auth', code: 'invalid-jwt', message, details: errors };\n // On a custom mutation a permission refusal is no proof either (P4-A review M7): a table root\n // field refused by its insert check may follow an Action that already ran. Fail safe: `sent`.\n const outside =\n operation === 'mutation' && external\n ? codes.find(\n (code) =>\n HASURA_EXTERNAL_FAILURE_CODES.has(code) ||\n MUTATION_REFUSAL_CODES.has(code) ||\n FORBIDDEN_CODES.has(code),\n )\n : undefined;\n if (outside !== undefined)\n return { kind: 'sent', code: outside, message, retryable: false, details: errors };\n const forbidden = codes.find((code) => FORBIDDEN_CODES.has(code));\n if (forbidden !== undefined)\n return { kind: 'forbidden', code: forbidden, message, details: errors };\n if (operation === 'mutation') {\n for (const code of codes) {\n const kind = MUTATION_REFUSAL_CODES.get(code);\n if (kind !== undefined) return { kind, code, message, details: errors };\n }\n }\n return { kind: 'protocol', code: 'graphql-error', message, details: errors };\n}\n\n/** A mutation answered, but with nothing readable: it may have been applied. */\nfunction answerLost(code: string, message: string, status: number, cause?: unknown): DataError {\n return {\n kind: 'sent',\n code,\n message,\n status,\n retryable: false,\n ...(cause === undefined ? {} : { cause }),\n };\n}\n\ninterface HasuraScopedQuery {\n readonly filter?: FilterExpression<ErasedResourceHandle>;\n readonly providerOptions?: { readonly ignoreScope?: boolean };\n /**\n * The active locale, present only when the resource declares\n * `data: { localeVarying: true }` (`@burojs/core`'s `resolveDescriptorLocale`\n * puts it on every read descriptor of such a resource and omits the key\n * entirely otherwise). Consumed by {@link ResourceMapping.localeField}.\n */\n readonly locale?: string;\n}\n\n/**\n * Refusal code for \"this mapping declares a `localeField` but the descriptor\n * carries no locale\". Exported so an application can match a symbol rather\n * than a string — same treatment as {@link FILTER_OPERATOR_UNSUPPORTED_CODE}.\n *\n * @see ResourceMapping.localeField for why this is a refusal and not a default.\n */\nexport const LOCALE_REQUIRED_CODE = 'locale-required';\n\n/**\n * The locale conjunct for a mapping that declares one, or `undefined` for a\n * mapping that does not.\n *\n * The refusal is EAGER — it fires before any request leaves, so it does not\n * depend on how many rows the table happens to hold. (Task 1 of this\n * sub-project recorded the opposite mistake: an absence assertion that stayed\n * green because a LAZY check downstream threw for its own reasons.)\n */\nexport function localeWhere(\n m: ResourceMapping,\n locale: string | undefined,\n): Record<string, unknown> | undefined {\n if (m.localeField === undefined) return undefined;\n if (typeof locale !== 'string' || locale.length === 0) {\n throw {\n kind: 'capability',\n code: LOCALE_REQUIRED_CODE,\n message: `@burojs/hasura: '${m.table}' maps a localeField ('${m.localeField}') but this read carries no locale. Declare \\`data: { localeVarying: true }\\` on the resource so @burojs/core puts the active locale on the descriptor, or drop the localeField. Serving the request anyway would return every language's rows at once.`,\n } satisfies DataError;\n }\n return { [m.localeField]: { _eq: locale } };\n}\n\nconst OP_MAP: Record<string, string> = {\n eq: '_eq',\n neq: '_neq',\n ne: '_neq',\n lt: '_lt',\n lte: '_lte',\n gt: '_gt',\n gte: '_gte',\n in: '_in',\n notIn: '_nin',\n nin: '_nin',\n contains: '_ilike',\n containss: '_like',\n startsWith: '_ilike',\n endsWith: '_ilike',\n startswith: '_ilike',\n endswith: '_ilike',\n null: '_is_null',\n};\n\n/**\n * The shared refusal code for \"this provider cannot express that filter\n * operator\". `@burojs/pocketbase` throws the SAME `kind` and the SAME code, so\n * an application handling one handles the other — see\n * {@link filtersToWhere} for why that agreement is load-bearing.\n */\nexport const FILTER_OPERATOR_UNSUPPORTED_CODE = 'filter-operator-unsupported';\n\n/**\n * Operators this provider knowingly cannot express, kept separate from \"we\n * have never heard of this\" so the error can say WHY rather than just \"no\".\n *\n * `has` / `hasAny` / `hasAll` are first-class `@burojs/core` operators over a\n * COLLECTION field (`CollectionPredicate`, `packages/core/src/data/filters.ts`).\n * Hasura can express collection membership — but WHICH operator is correct\n * depends on the column's Postgres type, and nothing this provider is given\n * declares it:\n *\n * - a `jsonb` column takes `_has_key` / `_has_keys_any` / `_has_keys_all`\n * (Postgres `?`, `?|`, `?&` — \"does this string exist as a top-level key or\n * array element\"), or `_contains` (`@>`);\n * - a Postgres ARRAY column (`text[]`) has no `_has_key*` at all; containment\n * is `_contains` / `_contained_in`.\n *\n * `ResourceMapping` declares `table`, `selectFields`, `pkType`, `int64Fields`\n * and the JSONB SPILL columns — nothing that distinguishes \"this column is\n * jsonb\" from \"this column is a Postgres array\" for an arbitrary filterable\n * field. Emitting one family for a column of the other type is either a\n * Hasura validation error or, worse, a valid query with different meaning.\n *\n * PROVENANCE, kept separate as the PocketBase truth table does:\n * - DOCUMENTED HASURA SEMANTICS settle which operator families exist and\n * which column types accept them (above).\n * - NOTHING IN THIS REPOSITORY settles which family any given resource needs.\n * All 37 captured live fixtures filter on scalar columns only — the\n * operators appearing across the whole corpus are `_eq` (16), `_and` (4),\n * `_or` (3), `_is_null` (2), `_in` (1), `_not` (1), and no capture touches a\n * collection column. `hasuraAdapter` implements three where-operators\n * (`_eq`, `_in`, `_is_null`), so it cannot settle it either.\n *\n * So this refuses, LOUDLY, rather than guessing. The way to implement it later\n * is to declare the column's shape on the mapping (e.g. a\n * `collectionFields: Record<string, 'jsonb' | 'array'>`) and translate per\n * declaration — the same registry-driven pattern `int64Fields` already uses —\n * ideally after capturing one real response per family.\n */\nconst UNEXPRESSIBLE_OPS: Readonly<Record<string, string>> = {\n has: 'a collection-membership test',\n hasAny: 'a collection-membership test',\n hasAll: 'a collection-membership test',\n};\n\nfunction unsupportedOperator(field: string, op: string): DataError {\n const why = UNEXPRESSIBLE_OPS[op];\n return {\n kind: 'capability',\n code: FILTER_OPERATOR_UNSUPPORTED_CODE,\n message: why\n ? `@burojs/hasura cannot express the '${op}' filter operator on '${field}': ${why}, whose Hasura encoding depends on whether the column is jsonb or a Postgres array — something no ResourceMapping field declares. Declare it and translate explicitly, or filter on a scalar column.`\n : `@burojs/hasura does not support the filter operator '${op}' (on '${field}').`,\n };\n}\n\n/**\n * Filter `scope` to keys whitelisted by `scopeFields`, drop nullish/empty\n * values, and translate to a Hasura where object using `_eq`.\n * Returns `undefined` when nothing applies.\n */\nexport function scopeToWhere(\n scope: Record<string, unknown>,\n scopeFields: string[],\n): Record<string, unknown> | undefined {\n const clauses: Array<Record<string, unknown>> = [];\n for (const key of scopeFields) {\n const v = scope[key];\n if (v == null || v === '') continue;\n clauses.push({ [key]: { _eq: v } });\n }\n if (clauses.length === 0) return undefined;\n if (clauses.length === 1) return clauses[0];\n return { _and: clauses };\n}\n\n/**\n * Combine two where objects with `_and`. Either or both may be undefined.\n */\nexport function mergeWhere(\n a: Record<string, unknown> | undefined,\n b: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (!a) return b;\n if (!b) return a;\n return { _and: [a, b] };\n}\n\n/**\n * Places an already-built operator object at a field PATH.\n *\n * `documents.document_id` + `{_in: […]}` becomes\n * `{documents: {document_id: {_in: […]}}}` — Hasura's own encoding for\n * \"there exists a related row where …\" on a to-many relationship, and the\n * same syntax for a to-one. A field with no dots comes back as the single\n * key it always was, so every existing filter is untouched.\n *\n * The framework's type layer has permitted dotted paths through to-one\n * relations since the filter AST was written, and this translator emitted\n * them as one flat key, which Hasura rejects. No adopter had written one, so\n * nothing had stepped on it — this closes that latent bug as much as it adds\n * the feature.\n */\nfunction atFieldPath(field: string, predicate: Record<string, unknown>): Record<string, unknown> {\n const parts = field.split('.');\n let out = predicate;\n for (let i = parts.length - 1; i >= 0; i -= 1) {\n out = { [parts[i] as string]: out };\n }\n return out;\n}\n\n/** Translate the native filter AST into a Hasura bool expression. */\nexport function filtersToWhere(\n filter?: FilterExpression<ErasedResourceHandle>,\n): Record<string, unknown> | undefined {\n if (!filter || typeof filter !== 'object') return undefined;\n // ── The empty-combinator rule (both branches below) ────────────────────────\n //\n // THE CLASS, stated once: this function uses `undefined` as a sentinel\n // meaning \"no constraint was expressed here — drop me\". A branchless\n // combinator is NOT that. `_and: []` is vacuously TRUE and `_or: []` is\n // vacuously FALSE, and dropping a node with a truth value is only sound in\n // an AND position, where a TRUE conjunct is genuinely absorbed. Under `not`,\n // or beside a disjunct in an `or`, dropping it changes what the query means.\n //\n // Both forms are therefore emitted EXPLICITLY, so they survive negation and\n // disjunction with their truth value intact. Checked on the SOURCE array\n // before mapping, because a combinator whose branches all mapped away\n // because NO VALUE was supplied for them really is the drop-me case. That\n // drop condition is a missing or `undefined` value — never `''`, which both\n // this package and `@burojs/pocketbase` translate.\n //\n // Provenance, kept separate on purpose:\n // - The two truth values are settled by CAPTURE, not by reasoning.\n // `packages/mock-kit/fixtures/hasura/where-or-empty.json` sends\n // `where: {_or: []}` to the live backend and comes back with `items: []`;\n // `where-and-empty.json` sends `{_and: []}` and comes back with the same\n // five rows `where-and-empty-baseline-no-filter.json` gets for no `where`\n // at all.\n // - How those two values COMPOSE under `_not`/`_or` is Hasura's documented\n // boolean semantics; no capture exercises a composed empty combinator.\n // The composed forms are verified end to end against `hasuraAdapter`\n // instead (`examples/marketplace-ops/tests/hasura-provider-contract.test.ts`),\n // whose own implementation was written from the GraphQL spec.\n //\n // The `or` half was fixed first (it was the one a capture caught directly);\n // the `and` half followed from sweeping the class rather than the instance —\n // before it, `{not: {and: []}}` matched every row where it must match none.\n if ('and' in filter) {\n if (filter.and.length === 0) return { _and: [] };\n const clauses = filter.and\n .map(filtersToWhere)\n .filter((item): item is Record<string, unknown> => item !== undefined);\n if (clauses.length === 0) return undefined;\n return clauses.length === 1 ? clauses[0] : { _and: clauses };\n }\n if ('or' in filter) {\n if (filter.or.length === 0) return { _or: [] };\n const clauses = filter.or\n .map(filtersToWhere)\n .filter((item): item is Record<string, unknown> => item !== undefined);\n if (clauses.length === 0) return undefined;\n return clauses.length === 1 ? clauses[0] : { _or: clauses };\n }\n if ('not' in filter) {\n const clause = filtersToWhere(filter.not);\n return clause === undefined ? undefined : { _not: clause };\n }\n if (!('field' in filter) || typeof filter.field !== 'string' || !('op' in filter))\n return undefined;\n\n const { field, op } = filter;\n if (op === 'isNull') return atFieldPath(field, { _is_null: true });\n if (op === 'isNotNull') return atFieldPath(field, { _is_null: false });\n // Missing / `undefined` value: no constraint was expressed, so drop.\n //\n // `''` is deliberately NOT in that set any more, and the two providers now\n // agree on it. It used to be dropped here, which meant `{name: {op: 'eq',\n // value: ''}}` — \"name IS the empty string\", an ordinary query — silently\n // matched every row, while `@burojs/pocketbase` emitted it. That is the\n // BLOCKER's own shape (one filter AST, two providers, different answers)\n // outside the branchless subclass.\n //\n // The drop existed for the empty-SEARCH-BOX idiom the two source apps had.\n // That idiom is already handled one layer up, in the only place every search\n // box in this framework goes through: `searchToFilter`\n // (`packages/core/src/data/search.ts`) returns `undefined` for an empty or\n // whitespace-only query and never builds a predicate at all. So suppressing\n // it a second time here bought nothing and cost the `eq: ''` case.\n if (!('value' in filter) || filter.value === undefined) return undefined;\n\n // NO `?? '_eq'` FALLBACK. An operator this provider cannot express must fail\n // loudly, not silently become equality against a value of the wrong shape.\n // The fallback is what turned `hasAny: []` — \"match nothing\" — into\n // `{tags: {_eq: []}}`, and it guaranteed that EVERY operator added to\n // `@burojs/core` in future would arrive here as a silent `_eq` rather than as\n // something someone noticed.\n //\n // WHERE IT FAILS: at request time, in the translator, not at construction.\n // That is forced rather than chosen — the operator is a property of the\n // FILTER, which arrives on the descriptor per request, and `ResourceMapping`\n // (the only thing available at construction) declares tables and columns, not\n // which operators a caller will use. There is nothing to validate at\n // construction time. It throws from `filtersToWhere` rather than returning a\n // sentinel so that every caller — `list`, `count`, `many`, the `fixedFilter`\n // merge — inherits the refusal without each having to check.\n const operator = OP_MAP[op];\n if (operator === undefined) throw unsupportedOperator(field, op);\n let value: unknown = filter.value;\n if (operator === '_ilike' || operator === '_like') {\n const text = String(value);\n value = op === 'startsWith' ? `${text}%` : op === 'endsWith' ? `%${text}` : `%${text}%`;\n }\n return atFieldPath(field, { [operator]: value });\n}\n\n/**\n * Exported for its own test: bigint coercion matches the WHOLE field string,\n * so a nested predicate needs its full path declared in `int64Fields`.\n * Matching the leaf would coerce a same-named column on any other table the\n * path happens to traverse.\n */\nexport function coerceInt64Filter(\n filter: FilterExpression<ErasedResourceHandle> | undefined,\n fields: readonly string[] | undefined,\n): FilterExpression<ErasedResourceHandle> | undefined {\n if (!filter || !fields?.length) return filter;\n if ('and' in filter)\n return {\n and: filter.and.map(\n (entry) => coerceInt64Filter(entry, fields) as FilterExpression<ErasedResourceHandle>,\n ),\n };\n if ('or' in filter)\n return {\n or: filter.or.map(\n (entry) => coerceInt64Filter(entry, fields) as FilterExpression<ErasedResourceHandle>,\n ),\n };\n if ('not' in filter)\n return { not: coerceInt64Filter(filter.not, fields) as FilterExpression<ErasedResourceHandle> };\n if (\n 'field' in filter &&\n fields.includes(filter.field) &&\n 'value' in filter &&\n filter.value != null\n ) {\n const value = Array.isArray(filter.value) ? filter.value.map(String) : String(filter.value);\n return { ...filter, value } as FilterExpression<ErasedResourceHandle>;\n }\n return filter;\n}\n\nfunction hasuraError(error: unknown, signal: AbortSignal, fallback: string): DataError {\n if (isDataError(error)) return error;\n if (signal.aborted)\n return {\n kind: 'cancelled',\n code: 'request-aborted',\n message: 'Hasura request was aborted.',\n cause: error,\n };\n return {\n kind: 'transport',\n code: 'hasura-request-failed',\n message: error instanceof Error && error.message ? error.message : fallback,\n cause: error,\n };\n}\n\n/**\n * Resolve the Hasura aggregate root field for a mapping, or `null` when the\n * registry declares that none exists. Registry-driven on purpose: which tables\n * can be counted is a per-resource FACT, so it is declared once on the mapping\n * (`aggregate`) and read here — never a `if (resource === 'users')` chain in the\n * provider, which is the shape this codebase has been deleting.\n */\nexport function aggregateRoot(m: ResourceMapping): string | null {\n if (m.aggregate === false) return null;\n return typeof m.aggregate === 'string' ? m.aggregate : `${m.table}_aggregate`;\n}\n\n/**\n * Root field for a by-pk read. Declarative per resource for the same reason\n * {@link aggregateRoot} is: the name is a fact about the backend schema, not\n * something to branch on by resource name inside the provider.\n */\nexport function byPkRoot(m: ResourceMapping): string | null {\n if (m.byPkRoot === false) return null;\n return m.byPkRoot ?? `${m.table}_by_pk`;\n}\n\nfunction snapshotOperationRegistry(\n source: HasuraOperationRegistry | undefined,\n): Readonly<Record<string, Readonly<Record<string, HasuraCustomOperation>>>> {\n if (source === undefined) return Object.freeze({});\n const resources: Record<string, Readonly<Record<string, HasuraCustomOperation>>> = {};\n for (const [resource, operations] of Object.entries(source)) {\n const entries: Record<string, HasuraCustomOperation> = {};\n for (const [name, operation] of Object.entries(operations)) {\n entries[name] = Object.freeze({ ...operation });\n }\n resources[resource] = Object.freeze(entries);\n }\n return Object.freeze(resources);\n}\n\nfunction hasRegisteredOperationKind(\n operations: Readonly<Record<string, Readonly<Record<string, HasuraCustomOperation>>>>,\n kind: HasuraCustomOperation['kind'],\n): boolean {\n return Object.values(operations).some((byName) =>\n Object.values(byName).some((operation) => operation.kind === kind),\n );\n}\n\n/**\n * `''` is not a role — it is the ordinary shape of a missing env var, and it is\n * exactly how both source apps built their endpoint constants\n * (`import.meta.env.VITE_X ?? ''`). It is normalised to `null` — \"send no role\n * header\" — once, at the two places a role enters the provider.\n *\n * Why once and not at each use: the HTTP half gated on `!== null` and the\n * socket half on truthiness, so `defaultRole: ''` used to send an empty\n * `X-Hasura-Role` on every POST and NO header on the subscription — one config\n * value, two answers, in a package whose `endpoint` comment promises the two\n * halves \"can never drift apart\". Normalising at the boundary is what makes\n * that promise structural instead of a convention two call sites have to keep.\n *\n * `undefined` is handled by the CALLER, not here, because it means different\n * things in the two positions: on a mapping it means \"inherit the default\", on\n * the config it means \"no default\".\n */\nfunction normalizeRole(role: string | null | undefined): string | null {\n return role === undefined || role === null || role === '' ? null : role;\n}\n\nexport function createHasuraDataProvider(config: HasuraDataProviderConfig): DataProvider {\n const endpoint = config.endpoint ?? DEFAULT_HASURA_ENDPOINT;\n const defaultRole = normalizeRole(config.defaultRole);\n const operations = snapshotOperationRegistry(config.operations);\n const hasCustomQueries = hasRegisteredOperationKind(operations, 'query');\n const hasCustomMutations = hasRegisteredOperationKind(operations, 'mutation');\n\n async function gql<T>(\n query: string,\n variables: Record<string, unknown> | undefined,\n role: string | null | undefined,\n signal: AbortSignal,\n operation: GqlOperation,\n /** A registered custom mutation, which may run an Action or a remote schema. */\n external = false,\n ): Promise<T> {\n try {\n // Serialised before the request is handed over: a body that cannot be\n // serialised (a BigInt, a cycle) never left, so it must not be `sent`.\n const requestBody = JSON.stringify({ query, variables });\n const request = async (token: string) => {\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${token}`,\n };\n // Divergence 5 of 7, role half (see `endpoint.ts` for the endpoint\n // half). `defaultRole` is what both apps imported from their own\n // `env.ts` as `HASURA_ROLE` — playground's `'company-admin'`,\n // l2-web-ui's `'operator'`, each an app deployment fact. It is\n // configuration here, defaulting to `null` (no role header at all).\n // A mapping `role` of `undefined` inherits it; an explicit `null`\n // suppresses the header for that one resource.\n const effectiveRole = role === undefined ? defaultRole : normalizeRole(role);\n if (effectiveRole !== null) headers['X-Hasura-Role'] = effectiveRole;\n // Divergence 5 of 7, endpoint half: both apps interpolated their own\n // app-owned env constant into a URL here. The package posts to the\n // configured `endpoint` and imports no app module.\n // Resolved HERE, per call — not once at construction. Captured at\n // construction, a `fetch` installed AFTERWARDS is unreachable for the\n // life of the provider: a provider built at module scope, before an MSW\n // server or worker has started, silently escapes the mock and goes to\n // the real network instead — and nothing throws. `@burojs/pocketbase`'s\n // `auth-provider.ts` carries the same note; it had shipped this exact\n // bug once and it stayed invisible until something needed real fixture\n // data from it. The three provider packages agree on this.\n const fetchImpl = config.fetchImpl ?? fetch;\n try {\n return await fetchImpl(endpoint, {\n method: 'POST',\n headers,\n body: requestBody,\n signal,\n });\n } catch (cause) {\n // The request was handed over and no answer came back. A write may\n // have been applied, so it must not look like a retryable transport\n // failure; a read in the same state is one.\n if (operation === 'mutation' && !signal.aborted)\n throw {\n kind: 'sent',\n code: 'hasura-request-sent',\n message: 'Hasura did not answer a mutation; it may have been applied.',\n retryable: false,\n cause,\n } satisfies DataError;\n throw cause;\n }\n };\n\n let token = await config.getToken();\n if (!token)\n throw {\n kind: 'auth',\n code: NOT_AUTHENTICATED_CODE,\n message: 'Not authenticated.',\n } satisfies DataError;\n let res = await request(token);\n // Hasura's own 401 retry (M3): a proxy or gateway in front of Hasura answered 401, the\n // request was not applied, so it is resent once with a fresh token whatever the\n // mutation's `replay` declares. It is documented, not removed.\n if (res.status === 401 && config.recoverAuth && (await config.recoverAuth())) {\n token = await config.getToken();\n if (!token)\n throw {\n kind: 'auth',\n code: NOT_AUTHENTICATED_CODE,\n message: 'Not authenticated.',\n } satisfies DataError;\n res = await request(token);\n }\n // The status decides before the body: a gateway 5xx after a mutation reached Hasura may\n // follow an applied write, so it is `sent`; a read in that state is `transport`.\n if (!res.ok) {\n if (operation === 'mutation' && res.status >= 500)\n throw answerLost(\n 'hasura-answer-lost',\n `GraphQL HTTP ${res.status} after a mutation was sent; it may have been applied.`,\n res.status,\n );\n throw {\n kind: 'transport',\n code: 'graphql-http-error',\n message: `GraphQL HTTP ${res.status}`,\n status: res.status,\n } satisfies DataError;\n }\n let body: GqlResponse<T>;\n try {\n body = (await res.json()) as GqlResponse<T>;\n if (body === null || typeof body !== 'object')\n throw new TypeError('The GraphQL answer is not an object.');\n } catch (cause) {\n // A 2xx answer to a mutation that cannot be read: the write may have been applied.\n if (operation === 'mutation' && !signal.aborted)\n throw answerLost(\n 'hasura-answer-unreadable',\n 'Hasura answered a mutation with a body that could not be read; it may have been applied.',\n res.status,\n cause,\n );\n throw cause;\n }\n if (body.errors?.length) throw graphqlError(body.errors, operation, external);\n if (body.data === undefined) {\n if (operation === 'mutation')\n throw answerLost(\n 'hasura-answer-unreadable',\n 'Hasura answered a mutation without data or errors; it may have been applied.',\n res.status,\n );\n throw {\n kind: 'protocol',\n code: 'empty-graphql-response',\n message: 'Empty GraphQL response.',\n } satisfies DataError;\n }\n return body.data;\n } catch (error) {\n throw hasuraError(error, signal, 'Hasura request failed.');\n }\n }\n\n function mapping(resource: string): ResourceMapping {\n const m = config.resources[resource];\n if (!m)\n throw {\n kind: 'configuration',\n code: 'unknown-resource',\n message: `Unknown resource: ${resource}`,\n } satisfies DataError;\n return m;\n }\n\n function registeredOperation(\n resource: string,\n operation: string,\n kind: HasuraCustomOperation['kind'],\n ): HasuraCustomOperation {\n const registered = operations[resource]?.[operation];\n if (registered === undefined || registered.kind !== kind) {\n throw {\n kind: 'capability',\n code: 'hasura-custom-operation-unsupported',\n message: `Hasura ${kind} operation '${operation}' is not declared for '${resource}'.`,\n } satisfies DataError;\n }\n return registered;\n }\n\n async function executeCustomOperation(\n resource: string,\n operationName: string,\n input: unknown,\n kind: HasuraCustomOperation['kind'],\n signal: AbortSignal,\n ): Promise<unknown> {\n const operation = registeredOperation(resource, operationName, kind);\n // Do not inspect descriptor providerOptions here. The operation's static\n // registry entry is the only source of GraphQL text and variable mapping.\n const result = await gql(\n operation.document,\n operation.variables(input),\n operation.role ?? config.resources[resource]?.role,\n signal,\n kind,\n kind === 'mutation',\n );\n // A mapper that answers `null` (a nullable output: \"no such run\") is an answer; only the\n // absence of a mapper returns the raw response.\n return operation.mapResult === undefined ? result : operation.mapResult(result);\n }\n\n /**\n * The ONE composition of a list-shaped `where`, shared by `getList` and\n * `getCount`. Four things go in, in this order:\n * 1. int64 coercion — ClickHouse-sourced columns typed `int64` are rejected\n * as numbers, so those filter values become strings (see `int64Fields`);\n * 2. the user's filters (`filtersToWhere`);\n * 3. the active sidebar scope, whitelisted by `scopeFields`, unless the\n * caller opted out with `meta.ignoreScope`;\n * 4. the mapping's `fixedFilter` (per-type / projection resources), ANDed in\n * LAST so the user still filters WITHIN the slice;\n * 5. the active locale, for a mapping that declares a `localeField` — the\n * same position as `fixedFilter` and for the same reason: it selects the\n * SLICE, and the user filters within it.\n *\n * ⚠️ `getCount` MUST NOT re-derive this from `filtersToWhere` alone. Scope and\n * `fixedFilter` are invisible to the caller — a resource declaring\n * `scopeFields: ['pg', 'location_id']` would have a filters-only count report\n * the total across every product group and location. A confidently wrong\n * number is strictly worse than no number, which is what the list already\n * degrades to.\n */\n function composeListWhere(\n m: ResourceMapping,\n descriptor: HasuraScopedQuery,\n context: DataOperationContext,\n ): Record<string, unknown> | undefined {\n const userWhere = filtersToWhere(coerceInt64Filter(descriptor.filter, m.int64Fields));\n\n // A query may opt OUT of scope via `meta.ignoreScope` when it must search\n // the whole table regardless of the active sidebar scope — e.g. resolving a\n // product by its globally-unique GTIN, which shouldn't 404 just because the\n // sidebar is filtered to a different product group.\n const ignoreScope = descriptor.providerOptions?.ignoreScope === true;\n const sessionScope =\n context.session.scopeKey !== null &&\n typeof context.session.scopeKey === 'object' &&\n !Array.isArray(context.session.scopeKey)\n ? (context.session.scopeKey as Record<string, unknown>)\n : {};\n const scope = ignoreScope ? {} : { ...sessionScope, ...(config.getScope?.(context) ?? {}) };\n const scopeWhere = scopeToWhere(scope, m.scopeFields ?? []);\n\n return mergeWhere(\n mergeWhere(mergeWhere(userWhere, scopeWhere), m.fixedFilter),\n localeWhere(m, descriptor.locale),\n );\n }\n\n /**\n * Issue the aggregate COUNT for a mapping against an already-composed `where`.\n * Shared by `getList`'s inline count and the on-demand `getCount` so the two\n * emit byte-identical queries. `root` comes from {@link aggregateRoot}; the\n * caller has already established that one exists.\n */\n async function runAggregateCount(\n m: ResourceMapping,\n root: string,\n where: Record<string, unknown> | undefined,\n signal: AbortSignal,\n ): Promise<number> {\n const aggWhereArg = where ? `($where: ${m.table}_bool_exp)` : '';\n const aggWhereParam = where ? '(where: $where)' : '';\n const metaQuery = `query Count_${m.table} ${aggWhereArg} {\n meta: ${root} ${aggWhereParam} { aggregate { count } }\n }`;\n const aggVars: Record<string, unknown> = {};\n if (where) aggVars.where = where;\n const metaData = await gql<{ meta: { aggregate: { count: number } } }>(\n metaQuery,\n where ? aggVars : undefined,\n m.role,\n signal,\n 'query',\n );\n return metaData.meta.aggregate.count;\n }\n\n /**\n * Upsert the data-body sibling row for a parent record. Uses Hasura's\n * `insert_<table>_one … on_conflict` so the same call inserts (create) or\n * replaces (edit) the single FK-keyed row. `constraint` / `column`\n * are unquoted GraphQL enum literals — they come from static mapping config,\n * never user input, so string-interpolating them is safe.\n */\n async function upsertDataBody(\n db: DataBodyConfig,\n parentId: string | number,\n body: Record<string, unknown>,\n role: string | null | undefined,\n signal: AbortSignal,\n ): Promise<void> {\n const mutation = `mutation UpsertDataBody($object: ${db.table}_insert_input!) {\n result: insert_${db.table}_one(\n object: $object,\n on_conflict: { constraint: ${db.constraint}, update_columns: [${db.column}] }\n ) { ${db.fk} }\n }`;\n await gql(\n mutation,\n { object: { [db.fk]: parentId, [db.column]: body } },\n role,\n signal,\n 'mutation',\n );\n }\n\n function mutationRecord(\n m: ResourceMapping,\n idField: string,\n item: unknown,\n resource: string,\n expectedId?: string | number,\n ) {\n if (item === null || typeof item !== 'object' || Array.isArray(item)) {\n throw {\n kind: 'not-found',\n code: 'record-not-found',\n message: `Not found: ${resource}`,\n } satisfies DataError;\n }\n const record = item as Record<string, unknown>;\n const id = record[m.pkField ?? idField];\n if (typeof id !== 'string' && (typeof id !== 'number' || !Number.isFinite(id))) {\n throw {\n kind: 'protocol',\n code: 'mutation-record-id-missing',\n message: `Mutation for '${resource}' returned no valid '${m.pkField ?? idField}'.`,\n } satisfies DataError;\n }\n if (expectedId !== undefined && id !== expectedId) {\n throw {\n kind: 'protocol',\n code: 'unexpected-record-id',\n message: `Mutation for '${resource}' returned '${String(id)}' instead of '${String(expectedId)}'.`,\n } satisfies DataError;\n }\n // Mutations select the declared table fields but do not re-read relation\n // data bodies or server-computed presentation values. Marking them partial\n // makes React invalidate dependent reads rather than write an incomplete\n // record into a one-record cache.\n return { kind: 'record' as const, id, record: item, completeness: 'partial' as const };\n }\n\n async function readMutationRecord(\n m: ResourceMapping,\n id: string | number,\n signal: AbortSignal,\n ): Promise<unknown> {\n const fields = m.selectFields.join(' ');\n const pkType = m.pkType ?? 'bigint';\n const pkField = m.pkField ?? 'id';\n const root = byPkRoot(m);\n if (m.fixedFilter || root === null) {\n const where = mergeWhere({ [pkField]: { _eq: id } }, m.fixedFilter);\n const query = `query One_${m.table}($where: ${m.table}_bool_exp) {\n items: ${m.table}(where: $where, limit: 1) { ${fields} }\n }`;\n const data = await gql<{ items: unknown[] }>(query, { where }, m.role, signal, 'query');\n return data.items?.[0] ?? null;\n }\n // DISTILLATION FIX — not a port. Neither source app had fixed this branch\n // when the package was distilled (both were backported from here later in\n // the same branch, 99e670aa; the earlier \"NEITHER source app fixed this\"\n // wording went stale four commits after it was written).\n // l2-web-ui fixed the identical hardcoded-`id` bug in `one()` (see\n // divergence 1 of 7 there) and left this direct-root branch — reached by\n // `updateOne` when the `_set` is empty, i.e. a data-body-only edit that\n // just re-reads the row — still emitting `${root}(id: $id)`. For any\n // mapping whose `pkField` is not literally `id` that argument name does\n // not exist on the by-pk root, so Hasura 400s. Same bug class, same fix,\n // now applied in both places.\n const query = `query One_${m.table}($id: ${pkType}!) {\n item: ${root}(${pkField}: $id) { ${fields} }\n }`;\n const data = await gql<{ item: unknown }>(query, { id }, m.role, signal, 'query');\n return data.item;\n }\n\n /**\n * The cursor (keyset) branch of `getList`. Separated from the offset branch\n * rather than folded into it because almost nothing is shared: no `offset`\n * variable, a different `order_by` (the tie-breaker is appended), an extra\n * `where` conjunct, one row over-fetched, and a `pageInfo` to mint. Folding\n * the two would have meant five conditionals inside a query builder whose\n * offset behaviour is pinned byte-for-byte by the captured-fixture fidelity\n * suite.\n *\n * See `./cursor.ts` for what a cursor is made of and why each refusal\n * exists.\n */\n async function listByCursor(\n m: ResourceMapping,\n descriptor: HasuraScopedQuery & { readonly sort?: readonly HasuraSortEntry[] },\n pagination: { readonly after?: string; readonly before?: string; readonly limit: number },\n context: DataOperationContext,\n ): Promise<{\n data: unknown[];\n total?: number;\n pageInfo: { nextCursor?: string; hasNextPage: boolean };\n }> {\n if (pagination.before !== undefined) {\n throw {\n kind: 'capability',\n code: HASURA_CURSOR_CODES.backward,\n message: `@burojs/hasura implements forward cursor pagination only; '${m.table}' was asked to page backwards with \\`before\\`. Nothing in this framework drives backward keyset paging today, and an unexercised implementation of it would be a worse answer than a refusal.`,\n } satisfies DataError;\n }\n assertCursorLimit(pagination.limit, m.table);\n const sorters = descriptor.sort?.filter((s) => s.field);\n const ordering = effectiveCursorOrdering(sorters, m);\n // EAGER, before any request: a refusal that depends on how many rows the\n // table happens to hold is not a refusal.\n assertCursorOrderable(ordering, sorters, m);\n\n // The base `where` — filters + scope + fixedFilter — WITHOUT the keyset\n // conjunct. Kept separate on purpose: the count below must describe the\n // whole filtered set, not \"the rows after the cursor\", or `total` would\n // shrink on every load-more and mean something different on every page.\n const baseWhere = composeListWhere(m, descriptor, context);\n const where =\n pagination.after === undefined\n ? baseWhere\n : mergeWhere(\n baseWhere,\n keysetWhere(ordering, decodeCursor(pagination.after, ordering, m.table), m.int64Fields),\n );\n\n const fields = m.selectFields.join(' ');\n const orderByExpr = `[${ordering.map((c) => `{${c.field}: ${c.direction}}`).join(', ')}]`;\n const whereArg = where ? `, $where: ${m.table}_bool_exp` : '';\n const whereParam = where ? ', where: $where' : '';\n const itemsQuery = `query ListCursor_${m.table}($limit: Int${whereArg}) {\n items: ${m.table}(limit: $limit, order_by: ${orderByExpr}${whereParam}) { ${fields} }\n }`;\n // One row MORE than asked for. That extra row is never returned; it is the\n // only way to answer `hasNextPage` exactly. Deriving it from\n // `rows.length === limit` instead would report a next page for every list\n // whose row count is a multiple of the page size, and the load-more button\n // would survive one click past the end before disappearing.\n const itemsVars: Record<string, unknown> = { limit: pagination.limit + 1 };\n if (where) itemsVars.where = where;\n const itemsData = await gql<{ items: unknown[] }>(\n itemsQuery,\n itemsVars,\n m.role,\n context.signal,\n 'query',\n );\n const fetched = itemsData.items ?? [];\n const hasNextPage = fetched.length > pagination.limit;\n const rows = hasNextPage ? fetched.slice(0, pagination.limit) : fetched;\n\n let total: number | undefined;\n const listAggRoot = aggregateRoot(m);\n if (!m.noAggregate && listAggRoot) {\n try {\n total = await runAggregateCount(m, listAggRoot, baseWhere, context.signal);\n } catch (err) {\n console.warn(`[hasura] count failed for ${m.table}; rendering unknown-total`, err);\n }\n }\n\n // Minted from the RAW row: the ordering columns are real columns, and the\n // jsonb-spill flatten below only adds keys.\n const boundary = rows[rows.length - 1];\n const nextCursor =\n hasNextPage && boundary !== null && typeof boundary === 'object'\n ? encodeCursor(ordering, boundary as Record<string, unknown>, m.table)\n : undefined;\n\n const spill = m.jsonbSpill;\n const data = spill ? rows.map((it) => flattenJsonbSpill(it, spill)) : rows;\n // `previousCursor`/`hasPreviousPage` are deliberately absent rather than\n // synthesised from `after !== undefined`: this provider cannot serve a\n // backward page (see the `before` refusal above), so advertising one would\n // hand the UI an affordance that only leads to an error.\n return { data, total, pageInfo: { nextCursor, hasNextPage } };\n }\n\n return defineDataProvider({\n queries: {\n async list(descriptor, context) {\n const m = mapping(descriptor.resource);\n if (descriptor.pagination?.kind === 'cursor') {\n return await listByCursor(m, descriptor, descriptor.pagination, context);\n }\n const limit =\n descriptor.pagination?.kind === 'offset' ? descriptor.pagination.pageSize : 25;\n const page = descriptor.pagination?.kind === 'offset' ? descriptor.pagination.page : 1;\n const offset = (page - 1) * limit;\n\n // int64 coercion + user filters + scope + fixedFilter, composed exactly\n // once — `getCount` calls the SAME helper so an on-demand count can never\n // describe a different set of rows than the page it sits under.\n const where = composeListWhere(m, descriptor, context);\n\n // Sort: take native sort expressions if present, fall back to mapping default.\n // Guard against entries with falsy `field` — Hasura would reject the\n // resulting `[{undefined: asc}]` and bring down the whole query.\n const sorters = descriptor.sort?.filter((s) => s.field);\n let orderByExpr: string;\n if (sorters && sorters.length > 0) {\n // Hasura order_by accepts an array of single-field bool exps.\n // We emit `[{field1: asc}, {field2: desc}]` — exact spec for multi-sort.\n orderByExpr = `[${sorters.map((s) => `{${s.field}: ${s.direction}}`).join(', ')}]`;\n } else {\n const defaultField = m.orderByField ?? 'id';\n orderByExpr = `{${defaultField}: desc}`;\n }\n\n const fields = m.selectFields.join(' ');\n const whereArg = where ? `, $where: ${m.table}_bool_exp` : '';\n const whereParam = where ? ', where: $where' : '';\n const itemsQuery = `query List_${m.table}($limit: Int, $offset: Int${whereArg}) {\n items: ${m.table}(limit: $limit, offset: $offset, order_by: ${orderByExpr}${whereParam}) { ${fields} }\n }`;\n const itemsVars: Record<string, unknown> = { limit, offset };\n if (where) itemsVars.where = where;\n const itemsData = await gql<{ items: unknown[] }>(\n itemsQuery,\n itemsVars,\n m.role,\n context.signal,\n 'query',\n );\n // `total` is OMITTED (undefined) when we cannot honestly produce a count:\n // `noAggregate` tables skip the count deliberately (a COUNT over ~29M rows\n // took ~55 s and hung the page in one of the source apps). The list's\n // unknown-total contract then takes over — range without a grand total,\n // Next while the page is full, no jump-to-end. NEVER substitute\n // `items.length`: that is the CURRENT PAGE's length, and reporting it as\n // the total collapses the list to one page.\n let total: number | undefined;\n const listAggRoot = aggregateRoot(m);\n if (!m.noAggregate && listAggRoot) {\n try {\n total = await runAggregateCount(m, listAggRoot, where, context.signal);\n } catch (err) {\n // A TRANSIENT failure on a countable table. Keeping the page length\n // here would be the same lie, so `total` stays undefined and the list\n // degrades to unknown-total — but log it, because (unlike\n // `noAggregate`) this is an error, not a design decision.\n console.warn(`[hasura] count failed for ${m.table}; rendering unknown-total`, err);\n }\n }\n // jsonb-spill inverse per row: flatten head/meta so per-type list columns\n // (batch_number, releaseMethodType, …) read as flat fields. Base columns\n // win; a list whose columns are all base is unaffected (the extra\n // flattened keys are simply never rendered).\n const spill = m.jsonbSpill;\n const items = spill\n ? itemsData.items.map((it) => flattenJsonbSpill(it, spill))\n : itemsData.items;\n return { data: items, total };\n },\n\n /**\n * On-demand row count — the answer to \"how many are there?\" on the lists\n * that page without a grand total (`noAggregate`). Never fired implicitly:\n * one source app measured ~55 s for a single COUNT, which is exactly why\n * the list stopped issuing one on every page.\n *\n * `{ total: null }` means \"not countable here at all\" — the registry says\n * this table has no aggregate root, or the resource isn't mapped by this\n * provider. It is a DECISION, and the UI latches the affordance off for the\n * session. A transient backend failure REJECTS instead (it propagates out of\n * `gql`), so a retry stays possible; never conflate the two.\n *\n * The `where` comes from the same {@link composeListWhere} `getList` uses —\n * same filters, same scope, same `fixedFilter` — so the number always\n * describes the rows the list is paging through.\n */\n async count(descriptor, context): Promise<CountResult> {\n // Deny by default: `mapping()` THROWS on an unmapped resource, and a\n // count is an optional nicety — it must degrade to \"unavailable\", never\n // take the page down.\n //\n // ⚠️ THE DEGRADATION PROMISE IS ABOUT WHAT THE BACKEND CAN ANSWER, NOT\n // ABOUT A MISDECLARED MAPPING, and the two returns below are the whole\n // of it. `composeListWhere` further down can still THROW — a\n // `localeField` mapping whose descriptor carries no locale raises\n // `locale-required` — and that is deliberate: it is a wiring bug, the\n // same one refused eagerly on `list`/`many`/`one`, and swallowing it\n // here into `{ total: null }` would make the ONE read path that\n // degrades also the one path where the misdeclaration is invisible.\n // Practically it is unreachable from the list screen (the same\n // composer runs there first and fails), but an on-demand \"how many?\"\n // affordance can reach it, and it will surface as an error rather than\n // a missing number.\n const m = config.resources[descriptor.resource];\n if (!m) return { total: null };\n const root = aggregateRoot(m);\n if (!root) return { total: null };\n return {\n total: await runAggregateCount(\n m,\n root,\n composeListWhere(m, descriptor, context),\n context.signal,\n ),\n };\n },\n\n async many(descriptor, context) {\n const m = mapping(descriptor.resource);\n // BEFORE the empty-set short-circuit, on purpose. A misdeclared\n // `localeField` mapping is a bug in the wiring, not a property of how\n // many ids the caller happened to ask for, so it must be refused for\n // `ids: []` exactly as it is for `ids: [1, 2]`. Letting the empty read\n // through would make the refusal depend on the data — the same\n // eager-vs-lazy trap SP4 task 1 recorded, in its empty-set spelling,\n // and it would leave \"refuses on every read path\" true only of the\n // populated case.\n localeWhere(m, descriptor.locale);\n if (descriptor.ids.length === 0) return { data: [] };\n\n const pkField = m.pkField ?? 'id';\n const idsWhere = { [pkField]: { _in: descriptor.ids } };\n const where = mergeWhere(\n idsWhere,\n composeListWhere(m, descriptor as unknown as HasuraScopedQuery, context),\n );\n const fields = m.selectFields.join(' ');\n const query = `query Many_${m.table}($where: ${m.table}_bool_exp) {\n items: ${m.table}(where: $where) { ${fields} }\n }`;\n const data = await gql<{ items: unknown[] }>(\n query,\n { where },\n m.role,\n context.signal,\n 'query',\n );\n const spill = m.jsonbSpill;\n const items = spill ? data.items.map((item) => flattenJsonbSpill(item, spill)) : data.items;\n return { data: items };\n },\n\n async one(descriptor, context) {\n const m = mapping(descriptor.resource);\n // When a data body lives in a sibling table, nested-select it alongside the\n // row (`document_data { data }`) so the whole record loads in one round trip.\n const dbSelect = m.dataBody ? ` ${m.dataBody.relationship} { ${m.dataBody.column} }` : '';\n const fields = m.selectFields.join(' ') + dbSelect;\n const pkType = m.pkType ?? 'bigint';\n const pkField = m.pkField ?? 'id';\n let item: unknown;\n const root = byPkRoot(m);\n // A `localeField` mapping takes the same branch as `fixedFilter`, and for\n // the same reason: the locale selects a SLICE of the table, and\n // `<table>_by_pk` cannot express one. A row of another language is then\n // \"not found\" under this resource — exactly how a movement id behaves\n // under a work-orders resource. That is the honest answer for a sidecar\n // translation table, where the id of an `en` row is genuinely not a\n // member of the `es` projection.\n const localeSlice = localeWhere(m, (descriptor as { locale?: string }).locale);\n if (m.fixedFilter || localeSlice || root === null) {\n // `<table>_by_pk` accepts no `where`, so a fixed-filter resource fetches\n // its single row via a `where`-scoped `limit: 1` query: the id must ALSO\n // satisfy the fixed slice, so a row of the wrong slice (e.g. a movement\n // id under a work-orders resource) resolves to null → Not found.\n const where = mergeWhere(\n mergeWhere({ [pkField]: { _eq: descriptor.id } }, m.fixedFilter),\n localeSlice,\n );\n const query = `query One_${m.table}($where: ${m.table}_bool_exp) {\n items: ${m.table}(where: $where, limit: 1) { ${fields} }\n }`;\n const data = await gql<{ items: unknown[] }>(\n query,\n { where },\n m.role,\n context.signal,\n 'query',\n );\n item = data.items?.[0] ?? null;\n } else {\n // DISTILLATION NOTE (divergence 1 of 7 — bug fix, l2-web-ui only).\n // The by-pk root's argument is named after the RESOURCE'S pk field\n // (`pkField`, default `'id'`) — not always literally `id`:\n // `marking_codes_by_pk(code: $id)`, not `marking_codes_by_pk(id: $id)`.\n // l2-web-ui shipped this fix (with three regression tests, ported into\n // this package's suite); playground still hardcoded `id` here and\n // would 400 for any mapping with a non-`id` `pkField`. `many()` /\n // `updateOne` / `deleteOne` already parametrized it — the two direct\n // by-pk READ branches (here and `readMutationRecord`) were the\n // stragglers.\n const query = `query One_${m.table}($id: ${pkType}!) {\n item: ${root}(${pkField}: $id) { ${fields} }\n }`;\n const data = await gql<{ item: unknown }>(\n query,\n { id: descriptor.id },\n m.role,\n context.signal,\n 'query',\n );\n item = data.item;\n }\n if (item == null)\n throw {\n kind: 'not-found',\n code: 'record-not-found',\n message: `Not found: ${descriptor.resource}/${descriptor.id}`,\n } satisfies DataError;\n // jsonb-spill inverse: spread the fetched head/meta blobs back onto the\n // flat record so edit forms + the show page read `regulatory_system` etc.\n // as flat fields. Base columns win on collision; the raw head/meta objects\n // are kept (so update can merge them, and tabs that want the blob still\n // have it). Same helper flattens getList rows.\n let result: unknown = m.jsonbSpill ? flattenJsonbSpill(item, m.jsonbSpill) : item;\n // data-body inverse: spread the sibling `data` blob onto the flat record\n // (so the form reads `items` / `products` top-level) and DROP the raw\n // relationship object so it can't round-trip back into `head` on save.\n if (m.dataBody && result && typeof result === 'object') {\n const rec = { ...(result as Record<string, unknown>) };\n const sibling = rec[m.dataBody.relationship];\n const body =\n sibling && typeof sibling === 'object'\n ? (sibling as Record<string, unknown>)[m.dataBody.column]\n : null;\n delete rec[m.dataBody.relationship];\n result =\n body && typeof body === 'object'\n ? { ...rec, ...(body as Record<string, unknown>) }\n : rec;\n }\n return { data: result };\n },\n\n ...(hasCustomQueries\n ? {\n async custom(descriptor, context) {\n return executeCustomOperation(\n descriptor.resource,\n descriptor.operation,\n descriptor.input,\n 'query',\n context.signal,\n );\n },\n }\n : {}),\n },\n mutations: {\n async createOne(descriptor, context) {\n const m = mapping(descriptor.resource);\n const fields = m.selectFields.join(' ');\n const rawInput = descriptor.input as Record<string, unknown>;\n let mainInput = rawInput;\n let dataBody: Record<string, unknown> | undefined;\n if (m.dataBody) {\n const split = partitionDataBody(rawInput, m.dataBody);\n mainInput = split.rest;\n dataBody = split.hasDataBody ? split.dataBody : undefined;\n }\n const object = m.jsonbSpill\n ? partitionJsonbSpill(mainInput, m.jsonbSpill, m.selectFields)\n : mainInput;\n const query = `mutation Create_${m.table}($object: ${m.table}_insert_input!) {\n item: insert_${m.table}_one(object: $object) { ${fields} }\n }`;\n const data = await gql<{ item: unknown }>(\n query,\n { object },\n m.role,\n context.signal,\n 'mutation',\n );\n const result = mutationRecord(m, descriptor.idField, data.item, descriptor.resource);\n if (m.dataBody && dataBody)\n await upsertDataBody(m.dataBody, result.id, dataBody, m.role, context.signal);\n return result;\n },\n\n async updateOne(descriptor, context) {\n const m = mapping(descriptor.resource);\n const fields = m.selectFields.join(' ');\n const rawInput = descriptor.input as Record<string, unknown>;\n let mainInput = rawInput;\n let dataBody: Record<string, unknown> | undefined;\n if (m.dataBody) {\n const split = partitionDataBody(rawInput, m.dataBody);\n mainInput = split.rest;\n dataBody = split.hasDataBody ? split.dataBody : undefined;\n }\n const set = m.jsonbSpill\n ? partitionJsonbSpill(mainInput, m.jsonbSpill, m.selectFields)\n : mainInput;\n const pkField = m.pkField ?? descriptor.idField;\n // DISTILLATION NOTE (divergence 2 of 7 — feature, l2-web-ui only, inert\n // unless asked for). Optimistic-concurrency precondition, added by\n // l2-web-ui (a shop-floor MES) which needs last-write-wins conflict\n // detection on its config/line records; playground never needed it and\n // has no equivalent.\n // Carried here as an OPT-IN: with no `providerOptions.ifUpdatedAt` the\n // two `!= null` checks below are the only added work and every emitted\n // query is byte-identical to the version without this feature.\n //\n // `!= null` (not `!== undefined`) is load-bearing and is itself a\n // shipped bug fix: a caller seeded from a row whose `updated_at` really\n // was `null` (a `string | null` column) passed the `!== undefined`\n // guard, built `{ updated_at: { _eq: null } }`, which Hasura's `_eq`\n // matches NEVER, so every save threw the same `stale-record` conflict a\n // real staleness would and locked that operator out permanently. Both\n // `undefined` and `null` must mean \"no precondition\".\n const ifUpdatedAt = (\n descriptor.providerOptions as { ifUpdatedAt?: string | null } | undefined\n )?.ifUpdatedAt;\n let item: unknown;\n if (Object.keys(set).length === 0) {\n item = await readMutationRecord(m, descriptor.id, context.signal);\n } else if (m.fixedFilter || ifUpdatedAt != null) {\n let where = mergeWhere({ [pkField]: { _eq: descriptor.id } }, m.fixedFilter);\n if (ifUpdatedAt != null) where = mergeWhere(where, { updated_at: { _eq: ifUpdatedAt } });\n const query = `mutation Update_${m.table}($where: ${m.table}_bool_exp!, $set: ${m.table}_set_input!) {\n result: update_${m.table}(where: $where, _set: $set) { affected_rows returning { ${fields} } }\n }`;\n const data = await gql<{ result: { affected_rows: number; returning: unknown[] } }>(\n query,\n { where, set },\n m.role,\n context.signal,\n 'mutation',\n );\n // A precondition that matches no row must never look like a silent\n // no-op success — whether the server signals it via `affected_rows`\n // or via an empty `returning`, either shape is the same \"someone else\n // changed this record\" fact and must reach React as a `DataError`.\n if (\n ifUpdatedAt != null &&\n ((data.result.affected_rows ?? 0) === 0 || (data.result.returning?.length ?? 0) === 0)\n ) {\n throw {\n kind: 'conflict',\n code: 'stale-record',\n message: `Update for '${descriptor.resource}/${descriptor.id}' matched no row: the record was changed since it was loaded.`,\n } satisfies DataError;\n }\n item = data.result.returning?.[0] ?? null;\n } else {\n const pkType = m.pkType ?? 'bigint';\n const query = `mutation Update_${m.table}($id: ${pkType}!, $set: ${m.table}_set_input!) {\n item: update_${m.table}_by_pk(pk_columns: {${pkField}: $id}, _set: $set) { ${fields} }\n }`;\n const data = await gql<{ item: unknown }>(\n query,\n { id: descriptor.id, set },\n m.role,\n context.signal,\n 'mutation',\n );\n item = data.item;\n }\n const result = mutationRecord(\n m,\n descriptor.idField,\n item,\n descriptor.resource,\n descriptor.id,\n );\n if (m.dataBody && dataBody)\n await upsertDataBody(m.dataBody, result.id, dataBody, m.role, context.signal);\n return result;\n },\n\n async deleteOne(descriptor, context) {\n const m = mapping(descriptor.resource);\n const fields = m.selectFields.join(' ');\n const pkField = m.pkField ?? descriptor.idField;\n let item: unknown;\n if (m.fixedFilter) {\n const where = mergeWhere({ [pkField]: { _eq: descriptor.id } }, m.fixedFilter);\n const query = `mutation Delete_${m.table}($where: ${m.table}_bool_exp!) {\n result: delete_${m.table}(where: $where) { affected_rows returning { ${fields} } }\n }`;\n const data = await gql<{ result: { returning: unknown[] } }>(\n query,\n { where },\n m.role,\n context.signal,\n 'mutation',\n );\n item = data.result.returning?.[0] ?? null;\n } else {\n const pkType = m.pkType ?? 'bigint';\n const query = `mutation Delete_${m.table}($id: ${pkType}!) {\n item: delete_${m.table}_by_pk(${pkField}: $id) { ${fields} }\n }`;\n const data = await gql<{ item: unknown }>(\n query,\n { id: descriptor.id },\n m.role,\n context.signal,\n 'mutation',\n );\n item = data.item;\n }\n mutationRecord(m, descriptor.idField, item, descriptor.resource, descriptor.id);\n return { kind: 'identity' as const, id: descriptor.id };\n },\n\n ...(hasCustomMutations\n ? {\n async custom(descriptor, context) {\n return executeCustomOperation(\n descriptor.resource,\n descriptor.operation,\n descriptor.input,\n 'mutation',\n context.signal,\n );\n },\n }\n : {}),\n },\n live: createHasuraLiveProvider({\n resources: config.resources,\n getToken: config.getToken,\n endpoint,\n role: defaultRole,\n }),\n });\n}\n"],"mappings":"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"}
|