@smthrs/notifications 1.0.0-rc.7 → 1.0.0-rc.8
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 +3 -1
- package/dist/cjs/NotificationQueue.d.ts +2 -1
- package/dist/cjs/NotificationQueue.d.ts.map +1 -1
- package/dist/cjs/NotificationQueue.js +9 -10
- package/dist/cjs/NotificationQueue.js.map +2 -2
- package/dist/cjs/Projection.d.ts +4 -1
- package/dist/cjs/Projection.d.ts.map +1 -1
- package/dist/cjs/Projection.js +2 -3
- package/dist/cjs/Projection.js.map +2 -2
- package/dist/cjs/internal/replay.d.ts +22 -0
- package/dist/cjs/internal/replay.d.ts.map +1 -0
- package/dist/cjs/internal/replay.js +46 -0
- package/dist/cjs/internal/replay.js.map +7 -0
- package/dist/esm/NotificationQueue.d.ts +2 -1
- package/dist/esm/NotificationQueue.d.ts.map +1 -1
- package/dist/esm/NotificationQueue.js +11 -12
- package/dist/esm/NotificationQueue.js.map +1 -1
- package/dist/esm/Projection.d.ts +4 -1
- package/dist/esm/Projection.d.ts.map +1 -1
- package/dist/esm/Projection.js +6 -2
- package/dist/esm/Projection.js.map +1 -1
- package/dist/esm/internal/replay.d.ts +22 -0
- package/dist/esm/internal/replay.d.ts.map +1 -0
- package/dist/esm/internal/replay.js +31 -0
- package/dist/esm/internal/replay.js.map +1 -0
- package/docs/README.md +3 -1
- package/docs/api.md +12 -8
- package/docs/installation.md +5 -1
- package/docs/quickstart.md +1 -1
- package/package.json +5 -5
- package/src/NotificationQueue.ts +15 -16
- package/src/Projection.ts +6 -4
- package/src/internal/replay.ts +39 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NotificationQueue.js","sourceRoot":"","sources":["../../src/NotificationQueue.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAClD,OAAO,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,QAAQ,CAAA;AAChG,OAAO,KAAK,SAAS,MAAM,yBAAyB,CAAA;AACpD,OAAO,KAAK,iBAAiB,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAC3D,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAE3D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,iBAAkB,SAAQ,MAAM,CAAC,WAAW,EAAqB,CAC5E,kCAAkC,EAClC;IACE,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC;QACpB,0BAA0B;QAC1B,qBAAqB;QACrB,mBAAmB;QACnB,wBAAwB;QACxB,sBAAsB;QACtB,sBAAsB;KACvB,CAAC,CAAC,IAAI,CACL,MAAM,CAAC,sBAAsB,CAAC,MAAM,CAAC,OAAO,CAAC,0BAAmC,CAAC,CAAC,CACnF;IACD,OAAO,EAAE,MAAM,CAAC,MAAM;IACtB,oEAAoE;IACpE,cAAc,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;IAC9C,sEAAsE;IACtE,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC;CACrC,CACF;CAAG;AAkGJ;;;;;GAKG;AACH,MAAM,OAAO,iBAAkB,SAAQ,OAAO,CAAC,OAAO,EAA8B,CAClF,kCAAkC,CACnC;CAAG;AAEJ;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,cAAuB,EAAW,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC,cAAc,CAAC,CAAA;AAE9F,MAAM,WAAW,GAAG,CAAC,SAAiB,EAAqB,EAAE,CAC3D,IAAI,iBAAiB,CAAC,EAAE,IAAI,EAAE,0BAA0B,EAAE,OAAO,EAAE,GAAG,SAAS,iBAAiB,EAAE,CAAC,CAAA;AAErG;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,YAA8B,EAAE,EAAW,EAAE,CACpE,IAAI,CAAC;IACH,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,yBAAyB,CAAC,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;IACpF,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,yBAAyB,CAAC,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,CAAC;IACpF,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,2BAA2B,CAAC,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC,CAAC;IAC1F,GAAG,SAAS;CACb,CAAC,CAAA;AAEJ;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,YAA8B,EAAE,EAAkC,EAAE,CAC5F,KAAK,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAA;AAEvD;;;;;;GAMG;AACH,MAAM,mBAAmB,GAAG,GAAG,CAAA;AAE/B;;;;;;;;GAQG;AACH,MAAM,4BAA4B,GAAG,SAAS,CAAA;AAE9C,2EAA2E;AAC3E,MAAM,qBAAqB,GAAG,GAAG,CAAA;AAEjC;;;;;;GAMG;AACH,MAAM,aAAa,GAAG,CAAC,KAAc,EAAgC,EAAE;IACrE,MAAM,OAAO,GAA+D,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAA;IACjG,IAAI,IAAI,GAAG,CAAC,CAAA;IACZ,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,EAAG,CAAA;QAC3B,IAAI,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;QAC9D,IAAI,IAAI,GAAG,4BAA4B;YAAE,OAAO,MAAM,CAAA;QACtD,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;YAAE,SAAQ;QACnE,IAAI,IAAI,CAAC,KAAK,IAAI,mBAAmB;YAAE,OAAO,OAAO,CAAA;QACrD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9B,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,KAAK;gBAAE,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,CAAA;YACrF,SAAQ;QACV,CAAC;QACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACtD,IAAI,IAAI,GAAG,CAAC,MAAM,CAAA;YAClB,OAAO,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,CAAA;QACvD,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,GAAG,CAAC,KAAc,EAAW,EAAE;IACzC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;IAClD,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,EAAE,CAAC;QAChD,OAAO,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAA;IAChG,CAAC;IACD,OAAO,KAAK,CAAA;AACd,CAAC,CAAA;AAED,MAAM,WAAW,GAAG,WAAW,CAAC,6BAA6B,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAA;AAElG,MAAM,OAAO,GAAG,CAAC,IAAY,EAAU,EAAE,CACvC,IAAI,CAAC,MAAM,IAAI,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,GAAG,CAAC,CAAC,KAAK,CAAA;AAEhG,MAAM,YAAY,GAAG,CAAC,KAAc,EAAsB,EAAE;IAC1D,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,SAAS,CAAA;IACjE,MAAM,EAAE,GAAI,KAA2C,CAAC,IAAI,CAAC,CAAA;IAC7D,OAAO,OAAO,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,CAAA;AAChD,CAAC,CAAA;AAED,MAAM,kBAAkB,GAAG,MAAM,CAAC,mBAAmB,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAA;AAErF;;;;;;;;;GASG;AACH,MAAM,SAAS,GAAG,CAChB,YAA4C,EACsB,EAAE,CACpE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,cAAc,GAAG,YAAY,CAAC,YAAY,CAAC,CAAA;IACjD,MAAM,QAAQ,GAAG,aAAa,CAAC,YAAY,CAAC,CAAA;IAC5C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC3B,OAAO,KAAK,CAAC,CAAC,IAAI,iBAAiB,CAAC;YAClC,IAAI,EAAE,sBAAsB;YAC5B,OAAO,EAAE,QAAQ,KAAK,OAAO;gBAC3B,CAAC,CAAC,wCAAwC,mBAAmB,cAAc;gBAC3E,CAAC,CAAC,qCAAqC,4BAA4B,kBAAkB;YACvF,GAAG,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC;SAC5D,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CACzD,MAAM,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE;QACxB,mEAAmE;QACnE,oEAAoE;QACpE,mEAAmE;QACnE,MAAM,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAG9C,CAAA;QACD,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAC7C,OAAO,IAAI,iBAAiB,CAAC;YAC3B,IAAI,EAAE,sBAAsB;YAC5B,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC;YAC/B,GAAG,CAAC,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,CAAC;YAC3D,GAAG,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC;SACjC,CAAC,CAAA;IACJ,CAAC,CAAC,CACH,CAAA;AACH,CAAC,CAAC,CAAA;AAEJ,gFAAgF;AAChF,MAAM,SAAS,GAAG,CAAC,KAAc,EAAU,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,EAAE,cAAc,EAAE,QAAQ,EAAE,CAAC,CAAA;AAE/F,8EAA8E;AAC9E,MAAM,WAAW,GAAG,CAAC,YAA4C,EAAyB,EAAE,CAC1F,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,CAAA;AAExD,2EAA2E;AAC3E,MAAM,MAAM,GAAG,CAAC,KAAc,EAAQ,EAAE;IACtC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAM;IACvD,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;QAAE,MAAM,CAAC,MAAM,CAAC,CAAA;IACzD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AACtB,CAAC,CAAA;AAiCD;;;;;GAKG;AACH,MAAM,iBAAiB,GAAG,EAAE,CAAA;AAE5B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CACvB,UAAsD,EAAE,EACA,EAAE,CAC1D,KAAK,CAAC,MAAM,CACV,iBAAiB,EACjB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,CAAA;IACtC,sEAAsE;IACtE,6DAA6D;IAC7D,qEAAqE;IACrE,oEAAoE;IACpE,qEAAqE;IACrE,wEAAwE;IACxE,yEAAyE;IACzE,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC3C,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,iBAAiB,CAAC,eAAe,CAAA;IACtE,kEAAkE;IAClE,sEAAsE;IACtE,oEAAoE;IACpE,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,CAAS,iBAAiB,CAAC,CAAA;IAExD;;;;;OAKG;IACH,MAAM,OAAO,GAAG,CACd,KAAyB,EACzB,GAAW,EAC6D,EAAE,CAC1E,MAAM,CAAC,GAAG,CACR,OAAO,CAAC,OAAO,CAAC;QACd,KAAK;QACL,GAAG,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,CAAC,EAAE,CAAC;QAC/D,KAAK,EAAE,CAAC;KACT,CAAC,EACF,CAAC,IAAI,EAAE,EAAE;QACP,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAA;QAC7B,OAAO,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,GAAG,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,CAAA;IACtF,CAAC,CACF,CAAA;IAEH,MAAM,UAAU,GAAG,CACjB,KAAyB,EACzB,GAAW,EACqE,EAAE,CAClF,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;QACxC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC,IAAI,EAAE,CAAA;QAC9C,MAAM,OAAO,GAAG,iBAAiB,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACxD,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC,IAAI,EAAE,CAAA;QAChG,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;QAClC,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IACnC,CAAC,CAAC,CAAA;IAEJ,MAAM,UAAU,GAAG,CACjB,KAAyB,EACzB,GAAW,EACqE,EAAE,CAClF,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE;QACxC,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC,IAAI,EAAE,CAAA;QAC9C,MAAM,OAAO,GAAG,iBAAiB,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;QACxD,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC;YAAE,OAAO,MAAM,CAAC,IAAI,EAAE,CAAA;QAChG,OAAO,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IACnC,CAAC,CAAC,CAAA;IAEJ,MAAM,IAAI,GAAG,CAAC,KAAyB,EAA+C,EAAE,CACtF,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,MAAM,IAAI,GAAW,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI;YACxC,KAAK,EAAE,iBAAiB,CAAC,KAAK,CAAC,QAAQ,CAAC;YACxC,UAAU,EAAE,OAAO,CAAC,KAAK,EAAE;YAC3B,UAAU,EAAE,OAAO,CAAC,KAAK,EAAE;YAC3B,MAAM,EAAE,SAAS;SAClB,CAAA;QACD,MAAM,KAAK,GAA8B,EAAE,CAAA;QAC3C,IAAI,KAAK,GAAG,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACtF,OAAO,IAAI,EAAE,CAAC;YACZ,iEAAiE;YACjE,gDAAgD;YAChD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC;gBAClC,KAAK;gBACL,UAAU,EAAE,CAAC,iBAAiB,CAAC,iBAAiB,EAAE,iBAAiB,CAAC,iBAAiB,CAAC;gBACtF,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC;gBACzC,KAAK,EAAE,GAAG;aACX,CAAC,CAAA;YACF,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAA;YAC3B,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,MAAK;YACrD,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,CAAE,CAAC,GAAG,CAAA;QAClC,CAAC;QACD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAA;QAEnC,IAAI,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;QACtB,IAAI,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;QACxB,iEAAiE;QACjE,mEAAmE;QACnE,oEAAoE;QACpE,2CAA2C;QAC3C,IAAI,UAAU,GAAG,IAAI,CAAC,UAAU,CAAA;QAChC,IAAI,UAAU,GAAG,IAAI,CAAC,UAAU,CAAA;QAChC,KAAK,MAAM,KAAK,IAAI,KAAK,EAAE,CAAC;YAC1B,MAAM,GAAG,KAAK,CAAC,GAAG,CAAA;YAClB,MAAM,OAAO,GAAG,iBAAiB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;YAClD,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;gBAAE,SAAQ;YACpC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAA;YAC3B,IAAI,iBAAiB,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;gBACxC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAA;gBAC1B,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,YAAY,CAAC,EAAE,EAAE;oBAC1D,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,WAAW,EAAE,KAAK,CAAC,WAAW;oBAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;oBACxB,GAAG,EAAE,KAAK,CAAC,GAAG;iBACf,CAAC,CAAA;gBACF,IACE,KAAK,CAAC,OAAO,KAAK,SAAS;oBAC3B,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,KAAK,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC,EAC1E,CAAC;oBACD,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;wBACpB,GAAG,KAAK;wBACR,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC5C,IAAI,CAAC,YAAY,CAAC,EAAE,KAAK,KAAK,CAAC,YAAY,CAAC,EAAE;4BAC5C,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC;4BACpE,CAAC,CAAC,IAAI,CACT,CAAC;qBACH,CAAC,CAAA;gBACJ,CAAC;qBAAM,CAAC;oBACN,KAAK,GAAG,iBAAiB,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;gBAChG,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,UAAU,GAAG,OAAO,CAAC,GAAG,CACtB,UAAU,EACV,iBAAiB,CAAC,QAAQ,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,QAAQ,CAAC,EACjE,KAAK,CAAC,GAAG,CACV,CAAA;gBACD,KAAK,GAAG,iBAAiB,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;YAC3D,CAAC;QACH,CAAC;QAED,MAAM,MAAM,GAAW,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,EAAE,CAAA;QAChE,KAAK,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;QAC1E,OAAO,MAAM,CAAA;IACf,CAAC,CAAC,CAAA;IAEJ;;;;;;;;;;;;;;;OAeG;IACH,MAAM,kBAAkB,GAAG,CACzB,KAAyB,EACzB,QAAwC,EACxC,mBAA2B,EAC3B,OAAgB,EAC2D,EAAE,CAC7E,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACjC,MAAM,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAA;QAChF,MAAM,SAAS,GAAG,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,CAAA;QAChG,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;YACpD,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5B,OAAO,KAAK,CAAC,CAAC,IAAI,iBAAiB,CAAC;oBAClC,IAAI,EAAE,0BAA0B;oBAChC,cAAc,EAAE,QAAQ,CAAC,EAAE;oBAC3B,OAAO,EAAE,uCAAuC;iBACjD,CAAC,CAAA;YACJ,CAAC;YACD,MAAM,QAAQ,GAAG,CAAC,KAAqC,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAA;YACzF,IAAI,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC,KAAK,SAAS,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;gBACvF,OAAO,KAAK,CAAC,CAAC,IAAI,iBAAiB,CAAC;oBAClC,IAAI,EAAE,wBAAwB;oBAC9B,cAAc,EAAE,QAAQ,CAAC,EAAE;oBAC3B,OAAO,EAAE,kEAAkE;iBAC5E,CAAC,CAAA;YACJ,CAAC;QACH,CAAC;QACD,IAAI,SAAS,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,KAAK,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;YAC1F,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;YACpD,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5B,OAAO,KAAK,CAAC,CAAC,IAAI,iBAAiB,CAAC;oBAClC,IAAI,EAAE,0BAA0B;oBAChC,OAAO,EAAE,8BAA8B;iBACxC,CAAC,CAAA;YACJ,CAAC;YACD,OAAO;gBACL,cAAc,EAAE,QAAQ,CAAC,EAAE;gBAC3B,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,SAAS,EAAE,IAAI;gBACf,QAAQ,EAAE,IAAI;gBACd,oBAAoB,EAAE,QAAQ,CAAC,KAAK,CAAC,YAAY;aAClD,CAAA;QACH,CAAC;QACD,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,EAAE,CAAC;YACnF,OAAO,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,CAAA;QACnG,CAAC;QACD,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,SAAS,EAAE,CAAC;YACtC,mEAAmE;YACnE,iEAAiE;YACjE,+DAA+D;YAC/D,6DAA6D;YAC7D,IAAI,IAAI,GAAG,KAAK,CAAC,WAAW,KAAK,mBAAmB,CAAA;YACpD,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS,EAAE,CAAC;gBACpC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAA;gBACpD,IAAI,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;oBAC5B,OAAO,KAAK,CAAC,CAAC,IAAI,iBAAiB,CAAC;wBAClC,IAAI,EAAE,0BAA0B;wBAChC,cAAc,EAAE,QAAQ,CAAC,EAAE;wBAC3B,OAAO,EAAE,kCAAkC,QAAQ,CAAC,EAAE,wBAAwB;qBAC/E,CAAC,CAAA;gBACJ,CAAC;gBACD,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,YAAY,CAAC,KAAK,SAAS,CAAC,QAAQ,CAAC,CAAA;YACvE,CAAC;YACD,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,KAAK,CAAC,CAAC,IAAI,iBAAiB,CAAC;oBAClC,IAAI,EAAE,wBAAwB;oBAC9B,cAAc,EAAE,QAAQ,CAAC,EAAE;oBAC3B,OAAO,EAAE,mBAAmB,QAAQ,CAAC,EAAE,8CAA8C;iBACtF,CAAC,CAAA;YACJ,CAAC;YACD,OAAO;gBACL,cAAc,EAAE,QAAQ,CAAC,EAAE;gBAC3B,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,GAAG,EAAE,KAAK,CAAC,GAAG;gBACd,SAAS,EAAE,IAAI;aAChB,CAAA;QACH,CAAC;QAED,MAAM,SAAS,GAAG,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,WAAoB,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,KAAK,CACxF,MAAM,CAAC,KAAK,EACZ,QAAQ,EACR,MAAM,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CACpD,CAAA;QACD,IAAI,SAAS,CAAC,QAAQ,KAAK,eAAe,EAAE,CAAC;YAC3C,6DAA6D;YAC7D,8DAA8D;YAC9D,+DAA+D;YAC/D,2CAA2C;YAC3C,OAAO;gBACL,cAAc,EAAE,QAAQ,CAAC,EAAE;gBAC3B,QAAQ,EAAE,SAAS,CAAC,QAAQ;gBAC5B,GAAG,EAAE,SAAS;gBACd,SAAS,EAAE,KAAK;aACjB,CAAA;QACH,CAAC;QAED,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAChD,IAAI,YAAY,CAAC,KAAK,CAAC;YACrB,KAAK;YACL,QAAQ,EAAE,iBAAiB,CAAC,iBAAiB,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC1D,SAAS,EAAE,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;YACpD,6DAA6D;YAC7D,0DAA0D;YAC1D,8DAA8D;YAC9D,4DAA4D;YAC5D,qCAAqC;YACrC,MAAM,EAAE,UAAU;YAClB,SAAS,EAAE,iBAAiB,CAAC,iBAAiB;YAC9C,OAAO,EAAE;gBACP,YAAY,EAAE,QAAQ;gBACtB,QAAQ,EAAE,SAAS,CAAC,QAAQ;gBAC5B,WAAW,EAAE,mBAAmB;gBAChC,GAAG,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC;aAC9C;SACF,CAAC,CACH,CAAA;QACD,MAAM,SAAS,GAAG,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAA;QAClG,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;YAC5B,6DAA6D;YAC7D,6DAA6D;YAC7D,0BAA0B;YAC1B,OAAO,KAAK,CAAC,CAAC,IAAI,iBAAiB,CAAC;gBAClC,IAAI,EAAE,wBAAwB;gBAC9B,cAAc,EAAE,QAAQ,CAAC,EAAE;gBAC3B,OAAO,EAAE,yCAAyC,QAAQ,CAAC,EAAE,0CAA0C;aACxG,CAAC,CAAA;QACJ,CAAC;QACD,OAAO;YACL,cAAc,EAAE,QAAQ,CAAC,EAAE;YAC3B,QAAQ,EAAE,SAAS,CAAC,QAAQ;YAC5B,GAAG,EAAE,SAAS,CAAC,GAAG;YAClB,SAAS,EAAE,OAAO,CAAC,IAAI,KAAK,WAAW;SACxC,CAAA;IACH,CAAC,CAAC,CAAA;IAEJ,OAAO,IAAI,CAAC;QACV,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,yBAAyB,CAAC,CAAC,CAAC,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,EAAE,CAC9E,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAClB,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC,EAAE,CAAC;gBAC7E,OAAO,KAAK,CAAC,CAAC,IAAI,iBAAiB,CAAC;oBAClC,IAAI,EAAE,sBAAsB;oBAC5B,OAAO,EAAE,yCAAyC;iBACnD,CAAC,CAAA;YACJ,CAAC;YACD,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAA;YAC/C,uEAAuE;YACvE,wEAAwE;YACxE,qEAAqE;YACrE,MAAM,OAAO,GAAG,QAAQ,CAAC,OAAO,CAAA;YAChC,IACE,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;gBACzE,OAA6C,CAAC,MAAM,CAAC,KAAK,gBAAgB,EAC3E,CAAC;gBACD,OAAO,KAAK,CAAC,CAAC,IAAI,iBAAiB,CAAC;oBAClC,IAAI,EAAE,sBAAsB;oBAC5B,cAAc,EAAE,QAAQ,CAAC,EAAE;oBAC3B,OAAO,EAAE,0FAA0F;iBACpG,CAAC,CAAA;YACJ,CAAC;YACD,MAAM,mBAAmB,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAA;YACxD,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,QAAQ,CAC5B,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CACvB,kBAAkB,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,QAAQ,EAAE,mBAAmB,EAAE,OAAO,CAAC,CAC9F,CACF,CAAA;QACH,CAAC,CAAC,CACH;QACD,KAAK,EAAE,MAAM,CAAC,EAAE,CAAC,yBAAyB,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CACpD,OAAO,CAAC,QAAQ,CACd,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;YAC5C,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YAClD,MAAM,GAAG,GAAG,iBAAiB,CAAC,QAAQ,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;YAC7E,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACjC,6DAA6D;YAC7D,gEAAgE;YAChE,+DAA+D;YAC/D,MAAM,SAAS,GAAG,CAChB,MAAkC,EAClC,UAA8C,EAI9C,EAAE,CACF,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,CAChC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAClB,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,EAAE,CAAC,CAAA;gBAC7C,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC;oBACpC,CAAC,CAAC,MAAM,CAAC,IAAI,EAA8B;oBAC3C,CAAC,CAAC,KAAK,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;gBACjD,6DAA6D;gBAC7D,8DAA8D;gBAC9D,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC;oBACzB,OAAO,KAAK,CAAC,CAAC,IAAI,iBAAiB,CAAC;wBAClC,IAAI,EAAE,0BAA0B;wBAChC,cAAc,EAAE,EAAE;wBAClB,OAAO,EAAE,8BAA8B,EAAE,wBAAwB;qBAClE,CAAC,CAAA;gBACJ,CAAC;gBACD,OAAO,KAAK,CAAC,KAAK,CAAC,YAAY,CAAA;YACjC,CAAC,CAAC,CAAC,CAAA;YAEP,MAAM,QAAQ,GAAG,CAAC,GAAW,EAAE,UAA8C,EAAE,EAAE,CAC/E,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;gBAClB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,UAAU,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;gBAC5C,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC1B,OAAO,KAAK,CAAC,CAAC,IAAI,iBAAiB,CAAC;wBAClC,IAAI,EAAE,0BAA0B;wBAChC,OAAO,EAAE,iCAAiC,KAAK,CAAC,QAAQ,wBAAwB;qBACjF,CAAC,CAAA;gBACJ,CAAC;gBACD,OAAO;oBACL,aAAa,EAAE,KAAK,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,EAAE,UAAU,CAAC;oBACzD,QAAQ,EAAE,KAAK,CAAC,QAAQ;oBACxB,SAAS,EAAE,IAAI;iBAChB,CAAA;YACH,CAAC,CAAC,CAAA;YAEJ,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,EAAE,GAAG,CAAC,CAAA;YACjD,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC,CAAA;YAEhF,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,CAAA;YACpD,+DAA+D;YAC/D,+DAA+D;YAC/D,kEAAkE;YAClE,mEAAmE;YACnE,+DAA+D;YAC/D,MAAM,cAAc,GAAG,6BAA6B,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC;gBACvE,CAAC,CAAC;oBACA,GAAG,MAAM,CAAC,KAAK;oBACf,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;wBACxC,MAAM,OAAO,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,CAAA;wBACzC,OAAO,CAAC,CAAC,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;4BAChF,OAA6C,CAAC,IAAI,KAAK,gBAAgB,CAAC,CAAA;oBAC7E,CAAC,CAAC;iBACH;gBACD,CAAC,CAAC,MAAM,CAAC,KAAK,CAAA;YAChB,MAAM,MAAM,GAAG,iBAAiB,CAAC,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,CAAC,eAAe,CAAC,CAAA;YAC7F,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAC5D,CAAC,CAAC,iBAAiB,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,eAAe,CAAC;gBACtE,CAAC,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAA;YACzC,MAAM,QAAQ,GAAG,CAAC,GAAG,MAAM,CAAC,QAAQ,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;YAC1F,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,mBAAmB,CAChD,IAAI,YAAY,CAAC,KAAK,CAAC;gBACrB,KAAK;gBACL,QAAQ,EAAE,iBAAiB,CAAC,aAAa,CAAC,KAAK,CAAC,eAAe,EAAE,KAAK,CAAC,QAAQ,CAAC;gBAChF,SAAS,EAAE,YAAY,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;gBACzC,uDAAuD;gBACvD,+DAA+D;gBAC/D,yDAAyD;gBACzD,MAAM,EAAE,UAAU;gBAClB,SAAS,EAAE,iBAAiB,CAAC,iBAAiB;gBAC9C,OAAO,EAAE;oBACP,QAAQ,EAAE,KAAK,CAAC,QAAQ;oBACxB,eAAe,EAAE,KAAK,CAAC,eAAe;oBACtC,GAAG,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC;iBACrD;aACF,CAAC,CACH,CAAA;YACD,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YAClC,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,GAAG,CAAC,CAAA;YACtD,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC7B,OAAO,KAAK,CAAC,CAAC,IAAI,iBAAiB,CAAC;oBAClC,IAAI,EAAE,0BAA0B;oBAChC,OAAO,EAAE,qCAAqC,KAAK,CAAC,QAAQ,0CAA0C;iBACvG,CAAC,CAAA;YACJ,CAAC;YACD,gEAAgE;YAChE,gDAAgD;YAChD,IAAI,OAAO,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAChC,OAAO,EAAE,aAAa,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,CAAA;YAChF,CAAC;YACD,OAAO,KAAK,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,CAAA;QAC7D,CAAC,CAAC,CAAC,CACJ,CACF;QACD,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,2BAA2B,CAAC,CAAC,CAAC,QAAQ,EAAE,EAAE,CAC3D,MAAM,CAAC,GAAG,CACR,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACvC,+DAA+D;QAC/D,iEAAiE;QACjE,iEAAiE;QACjE,6DAA6D;QAC7D,sCAAsC;QACtC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,CAChE,CACF;KACF,CAAC,CAAA;AACJ,CAAC,CAAC,CACH,CAAA;AAEH;;;;;GAKG;AACH,MAAM,CAAC,MAAM,KAAK,GAA2D,SAAS,EAAE,CAAA","sourcesContent":["/**\n * Journal-backed durable notification admission and turn-boundary drain.\n *\n * Governing contract: `packages/smithers/notifications/docs/api.md`, published as\n * `/api/notifications`.\n *\n * @since 0.1.0\n */\n\nimport { canonicalize } from \"@smthrs/canonical\"\nimport { digestSync } from \"@smthrs/crypto/Sha256\"\nimport { Journal, JournalEvent } from \"@smthrs/journal\"\nimport { Context, Effect, HashMap, Layer, Option, Schema, SchemaIssue, Semaphore } from \"effect\"\nimport * as FoldCache from \"./internal/foldCache.ts\"\nimport * as NotificationModel from \"./Notification.ts\"\nimport * as NotificationEvent from \"./NotificationEvent.ts\"\nimport * as NotificationState from \"./NotificationState.ts\"\n\n/**\n * The queue could not be reached, or it refused what a caller handed it.\n *\n * `code` is the stable half and the one to branch on. `notification_unavailable`\n * is the seam reporting that it serves nothing; `notification_id_reused` says a\n * stable id was admitted once already with different content, which is a\n * producer bug rather than a storage failure; `notification_invalid` says the\n * value is not a notification, and `path` names the field inside it that failed.\n * `notification_closed` means the receiver has finished and the caller must\n * start a new request; `notification_full` means nothing was retained and the\n * same notification may be retried after pending work drains. Neither the\n * message nor `path` ever carries the offending value.\n * `notification_refused` rejects reserved outside-change payloads before admission.\n *\n * @category errors\n * @since 0.1.0\n */\nexport class NotificationError extends Schema.TaggedError<NotificationError>()(\n \"/notifications/NotificationError\",\n {\n code: Schema.Literals([\n \"notification_unavailable\",\n \"notification_closed\",\n \"notification_full\",\n \"notification_id_reused\",\n \"notification_invalid\",\n \"notification_refused\"\n ]).pipe(\n Schema.withConstructorDefault(Effect.succeed(\"notification_unavailable\" as const))\n ),\n message: Schema.String,\n /** The notification the failure is about, when one was readable. */\n notificationId: Schema.optional(Schema.String),\n /** Dotted path of the offending field, for `notification_invalid`. */\n path: Schema.optional(Schema.String)\n }\n) {}\n\n/**\n * What admitting one notification decided.\n *\n * `seq` is the journal sequence the admission committed at, and it is absent\n * exactly when nothing was written, which today means a `rejected-full`\n * refusal. `duplicate` says the id was already admitted, in which case\n * `decision` and `seq` are read back from the committed record rather than\n * recomputed here.\n *\n * A caller MUST inspect `decision`: `rejected-full` means the queue was at\n * capacity and retained nothing, so the notification never reaches the model\n * unless the caller admits it again once a boundary has drained.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface AdmissionReceipt {\n readonly notificationId: string\n readonly decision: NotificationState.AdmissionDecision\n readonly seq: number | undefined\n readonly duplicate: boolean\n readonly consumed?: boolean\n readonly consumedNotification?: NotificationModel.Notification\n}\n\n/**\n * The boundary a drain is attempted at, and whether the run would go idle\n * if nothing were delivered.\n *\n * `(runId, targetLineageId, boundary)` is the unit of drain: two lineages\n * closing a turn under the same boundary name are two drains, and each is\n * recorded separately.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface DrainInput {\n readonly runId: string\n readonly targetLineageId: string\n readonly boundary: string\n readonly wouldIdle: boolean\n /**\n * The journal sequence that opened this turn. A steer admitted after it is\n * held for the next boundary, which is what keeps a message that arrived\n * mid-turn out of the turn already in flight. Omitting it delivers\n * everything pending for the lineage.\n */\n readonly cutoffSeq?: number | undefined\n}\n\n/**\n * The notifications this boundary delivers, and whether the boundary had\n * already drained.\n *\n * The notifications are the ones the committed promotion record names, so two\n * processes draining one boundary report the same delivery rather than two\n * divergent guesses.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface DrainReceipt {\n readonly notifications: ReadonlyArray<NotificationModel.Notification>\n readonly boundary: string\n readonly duplicate: boolean\n}\n\n/**\n * The durable pending queue: admit a notification exactly once, and drain\n * what a boundary is allowed to deliver.\n *\n * @category services\n * @since 0.1.0\n */\nexport interface Service {\n readonly admit: (\n runId: string,\n notification: NotificationModel.Notification,\n version?: number\n ) => Effect.Effect<AdmissionReceipt, Journal.JournalError | NotificationError>\n readonly drain: (\n input: DrainInput\n ) => Effect.Effect<DrainReceipt, Journal.JournalError | NotificationError>\n /**\n * What this run has been told that no boundary has delivered yet, in\n * admission order.\n *\n * The queue is the owner of that fact: pending is admitted minus promoted,\n * and both halves are its own journal records. A supervisor that counted\n * admissions alone would report a steer as waiting forever.\n */\n readonly pending: (\n runId: string\n ) => Effect.Effect<ReadonlyArray<NotificationModel.Notification>, Journal.JournalError | NotificationError>\n}\n\n/**\n * The {@link Service} tag.\n *\n * @category services\n * @since 0.1.0\n */\nexport class NotificationQueue extends Context.Service<NotificationQueue, Service>()(\n \"/notifications/NotificationQueue\"\n) {}\n\n/**\n * Builds a {@link Service} from an implementation of its methods.\n *\n * @param implementation the methods to serve\n * @category constructors\n * @since 0.1.0\n */\nexport const make = (implementation: Service): Service => NotificationQueue.of(implementation)\n\nconst unavailable = (operation: string): NotificationError =>\n new NotificationError({ code: \"notification_unavailable\", message: `${operation} is unavailable` })\n\n/**\n * A {@link Service} that fails every method as unavailable. Overrides\n * replace individual methods.\n *\n * @param overrides methods to serve instead of failing\n * @category constructors\n * @since 0.1.0\n */\nexport const makeNoop = (overrides: Partial<Service> = {}): Service =>\n make({\n admit: Effect.fn(\"NotificationQueue.admit\")(() => Effect.fail(unavailable(\"admit\"))),\n drain: Effect.fn(\"NotificationQueue.drain\")(() => Effect.fail(unavailable(\"drain\"))),\n pending: Effect.fn(\"NotificationQueue.pending\")(() => Effect.fail(unavailable(\"pending\"))),\n ...overrides\n })\n\n/**\n * Provides {@link makeNoop}.\n *\n * @param overrides methods to serve instead of failing\n * @category layers\n * @since 0.1.0\n */\nexport const layerNoop = (overrides: Partial<Service> = {}): Layer.Layer<NotificationQueue> =>\n Layer.succeed(NotificationQueue)(makeNoop(overrides))\n\n/**\n * The deepest a notification's JSON may nest.\n *\n * The bound exists because the value is walked and serialized on the way to\n * the journal, and an unbounded walk turns a hostile payload into an untyped\n * `RangeError` instead of a refusal a caller can read.\n */\nconst maximumPayloadDepth = 256\n\n/**\n * The largest a notification may be, in UTF-16 code units: every string and\n * object key counted at its length, and every other value as one.\n *\n * The bound exists because an admission copies, decodes, canonicalizes,\n * fingerprints, and journals the whole value, so a producer admitting a huge\n * array or string spends the notifying process's CPU and memory and bloats\n * every replay of the run. It is checked before any of that work starts.\n */\nconst maximumNotificationCodeUnits = 1_048_576\n\n/** Bounds an issue message so a refusal cannot journal or log an essay. */\nconst maximumIssueCodeUnits = 200\n\n/**\n * The bound a value breaks, if any, walked iteratively so the walk itself\n * cannot exhaust the stack. A cycle is caught by the depth bound: it has no\n * finite depth, so it trips the limit rather than looping forever. The size\n * count stops the walk as soon as it passes the bound, so an oversized value\n * costs the walk no more than the bound does.\n */\nconst exceededBound = (value: unknown): \"depth\" | \"size\" | undefined => {\n const pending: Array<{ readonly value: unknown; readonly depth: number }> = [{ value, depth: 0 }]\n let size = 0\n while (pending.length > 0) {\n const next = pending.pop()!\n size += typeof next.value === \"string\" ? next.value.length : 1\n if (size > maximumNotificationCodeUnits) return \"size\"\n if (typeof next.value !== \"object\" || next.value === null) continue\n if (next.depth >= maximumPayloadDepth) return \"depth\"\n if (Array.isArray(next.value)) {\n for (const child of next.value) pending.push({ value: child, depth: next.depth + 1 })\n continue\n }\n for (const [key, child] of Object.entries(next.value)) {\n size += key.length\n pending.push({ value: child, depth: next.depth + 1 })\n }\n }\n return undefined\n}\n\n/**\n * A structural copy of the caller's value, so nothing the queue journals or\n * retains can be edited afterwards through a reference the caller kept. Values\n * that are not arrays or objects pass through, which is what leaves a function\n * or a symbol in place for the schema to reject.\n */\nconst copied = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(copied)\n if (typeof value === \"object\" && value !== null) {\n return Object.fromEntries(Object.entries(value).map(([key, nested]) => [key, copied(nested)]))\n }\n return value\n}\n\nconst formatIssue = SchemaIssue.makeFormatterStandardSchemaV1({ leafHook: (issue) => issue._tag })\n\nconst bounded = (text: string): string =>\n text.length <= maximumIssueCodeUnits ? text : `${text.slice(0, maximumIssueCodeUnits - 3)}...`\n\nconst identifiedBy = (value: unknown): string | undefined => {\n if (typeof value !== \"object\" || value === null) return undefined\n const id = (value as Readonly<Record<string, unknown>>)[\"id\"]\n return typeof id === \"string\" ? id : undefined\n}\n\nconst decodeNotification = Schema.decodeUnknownEffect(NotificationModel.Notification)\n\n/**\n * Decodes the caller's argument at the durability boundary and returns a value\n * the caller cannot reach.\n *\n * Nothing downstream re-checks the shape: a structurally invalid notification\n * that reached the journal would be acknowledged at a real sequence and then\n * skipped by every replay, which is acknowledged data loss. The depth and size\n * bounds run first so a hostile payload can neither exhaust the stack nor make\n * the copy, the decode, and the fingerprint walk it in full.\n */\nconst validated = (\n notification: NotificationModel.Notification\n): Effect.Effect<NotificationModel.Notification, NotificationError> =>\n Effect.gen(function*() {\n const notificationId = identifiedBy(notification)\n const exceeded = exceededBound(notification)\n if (exceeded !== undefined) {\n return yield* new NotificationError({\n code: \"notification_invalid\",\n message: exceeded === \"depth\"\n ? `A notification nests deeper than the ${maximumPayloadDepth} level bound`\n : `A notification is larger than the ${maximumNotificationCodeUnits} code unit bound`,\n ...(notificationId === undefined ? {} : { notificationId })\n })\n }\n return yield* decodeNotification(copied(notification)).pipe(\n Effect.mapError((error) => {\n // The formatter reports at least one issue for a failure and gives\n // every issue a concrete path. Standard Schema's result type widens\n // both, so a fallback for either would be code no input can reach.\n const issue = formatIssue(error.issue).issues[0] as {\n readonly message: string\n readonly path: ReadonlyArray<PropertyKey>\n }\n const path = issue.path.map(String).join(\".\")\n return new NotificationError({\n code: \"notification_invalid\",\n message: bounded(issue.message),\n ...(notificationId === undefined ? {} : { notificationId }),\n ...(path === \"\" ? {} : { path })\n })\n })\n )\n })\n\n// Persisted notification JSON predates RFC 8785's rejection of lone surrogates.\nconst canonical = (value: unknown): string => canonicalize(value, { loneSurrogates: \"escape\" })\n\n/** Fingerprint the validated input before the journal redacts its payload. */\nconst fingerprint = (notification: NotificationModel.Notification): Effect.Effect<string> =>\n Effect.sync(() => digestSync(canonical(notification)))\n\n/** Freeze the decoded JSON graph before any queue output can expose it. */\nconst freeze = (value: unknown): void => {\n if (typeof value !== \"object\" || value === null) return\n for (const nested of Object.values(value)) freeze(nested)\n Object.freeze(value)\n}\n\n/**\n * One committed admission, as the journal recorded it.\n *\n * This is an identity, not a payload: the fold keeps the fingerprint that\n * settles a duplicate and the sequence the record lives at, and reads the\n * notification itself back from the journal on the rare path that needs it.\n * Retaining every admitted payload for a run's lifetime is what made a\n * capacity-one queue grow without bound.\n */\ninterface Committed {\n readonly version: number | undefined\n readonly fingerprint: string | undefined\n readonly decision: NotificationState.AdmissionDecision\n readonly seq: number\n}\n\n/**\n * One run's folded notification history.\n *\n * `cursor` is the sequence of the last entry folded, absent when the run has\n * no entries at all. It is what the next read pages from, so a fold costs the\n * entries added since the previous call rather than the run's whole journal.\n */\ninterface Loaded {\n readonly state: NotificationState.State\n readonly admissions: HashMap.HashMap<string, Committed>\n /** Drain identity to the sequence its record committed at. */\n readonly promotions: HashMap.HashMap<string, number>\n readonly cursor: number | undefined\n}\n\n/**\n * How many runs one layer keeps folded at a time. Evicting is safe: the next\n * call for an evicted run folds it again from the beginning, and the retention\n * policy is scan-resistant, so a supervisor sweeping more runs than this reads\n * journal tails for all but a fixed handful of them.\n */\nconst maximumCachedRuns = 64\n\n/**\n * Journal-backed production layer, with the pending capacity a composition\n * chooses.\n *\n * Admission and drain evidence is written through `emitDurableUnfenced`, so\n * both return only after the corresponding entry is durably committed and a\n * dropped lifecycle event is unrepresentable. The channel is the unfenced one\n * on purpose: the notifying process owns no run, so there is no ownership\n * fence to hand over, and both records are first-writer-wins on their own\n * identity instead.\n *\n * The capacity guard is a conditional write at the storage layer, not a check\n * against a fold one process happened to load: the fold, the guard, and the\n * admission record are decided and written inside ONE `journal.transact`\n * write transaction, which the durable writer serializes against every other\n * writer of the database and replays when a concurrent writer commits between\n * the guard's reads and the insert. Two processes admitting to one full queue\n * therefore produce one admission and one `rejected-full`, never two\n * admissions.\n *\n * @param options the pending capacity, defaulting to `NotificationState.defaultCapacity`\n * @category layers\n * @since 1.0.0\n */\nexport const layerWith = (\n options: { readonly capacity?: number | undefined } = {}\n): Layer.Layer<NotificationQueue, never, Journal.Journal> =>\n Layer.effect(\n NotificationQueue,\n Effect.gen(function*() {\n const journal = yield* Journal.Journal\n // Orders this layer's own fibers so one process never runs two of its\n // admissions or drains against the fold cache at once. It is\n // process-local by construction and is NOT what enforces capacity or\n // drain identity across processes: those are the conditional writes\n // inside `journal.transact`, decided and committed in one serialized\n // write transaction. Always enter the journal transaction before taking\n // this permit, matching callers already inside an enclosing transaction.\n const operations = yield* Semaphore.make(1)\n const capacity = options.capacity ?? NotificationState.defaultCapacity\n // Only committed folds may be shared. A transaction reads its own\n // inserts, but its read-back is still provisional until the OUTERMOST\n // commit; neither a retry nor a later caller may dedupe against it.\n const folded = FoldCache.make<Loaded>(maximumCachedRuns)\n\n /**\n * One entry by the sequence it committed at.\n *\n * The fold keeps identities and sequences rather than payloads, so the\n * paths that must report an admitted notification read it back here.\n */\n const entryAt = (\n runId: JournalEvent.RunId,\n seq: number\n ): Effect.Effect<Option.Option<JournalEvent.Entry>, Journal.JournalError> =>\n Effect.map(\n journal.entries({\n runId,\n ...(seq === 0 ? {} : { after: JournalEvent.Seq.make(seq - 1) }),\n limit: 1\n }),\n (page) => {\n const found = page.entries[0]\n return found !== undefined && found.seq === seq ? Option.some(found) : Option.none()\n }\n )\n\n const admittedAt = (\n runId: JournalEvent.RunId,\n seq: number\n ): Effect.Effect<Option.Option<NotificationEvent.Admitted>, Journal.JournalError> =>\n Effect.map(entryAt(runId, seq), (entry) => {\n if (Option.isNone(entry)) return Option.none()\n const decoded = NotificationEvent.fromEntry(entry.value)\n if (Option.isNone(decoded) || !NotificationEvent.isAdmitted(decoded.value)) return Option.none()\n freeze(decoded.value.notification)\n return Option.some(decoded.value)\n })\n\n const promotedAt = (\n runId: JournalEvent.RunId,\n seq: number\n ): Effect.Effect<Option.Option<NotificationEvent.Promoted>, Journal.JournalError> =>\n Effect.map(entryAt(runId, seq), (entry) => {\n if (Option.isNone(entry)) return Option.none()\n const decoded = NotificationEvent.fromEntry(entry.value)\n if (Option.isNone(decoded) || !NotificationEvent.isPromoted(decoded.value)) return Option.none()\n return Option.some(decoded.value)\n })\n\n const load = (runId: JournalEvent.RunId): Effect.Effect<Loaded, Journal.JournalError> =>\n Effect.gen(function*() {\n const base: Loaded = folded.get(runId) ?? {\n state: NotificationState.empty(capacity),\n admissions: HashMap.empty(),\n promotions: HashMap.empty(),\n cursor: undefined\n }\n const fresh: Array<JournalEvent.Entry> = []\n let after = base.cursor === undefined ? undefined : JournalEvent.Seq.make(base.cursor)\n while (true) {\n // `after` is an exact-optional property upstream, so an explicit\n // `undefined` is not the same as an absent key.\n const page = yield* journal.entries({\n runId,\n eventTypes: [NotificationEvent.AdmittedEventType, NotificationEvent.PromotedEventType],\n ...(after === undefined ? {} : { after }),\n limit: 512\n })\n fresh.push(...page.entries)\n if (!page.hasMore || page.entries.length === 0) break\n after = page.entries.at(-1)!.seq\n }\n if (fresh.length === 0) return base\n\n let state = base.state\n let cursor = base.cursor\n // Persistent maps: one event adds a node and leaves the fold the\n // previous load published untouched, where copying both maps would\n // cost the run's whole history on every event and make a long run's\n // folding quadratic in its own admissions.\n let admissions = base.admissions\n let promotions = base.promotions\n for (const entry of fresh) {\n cursor = entry.seq\n const decoded = NotificationEvent.fromEntry(entry)\n if (Option.isNone(decoded)) continue\n const event = decoded.value\n if (NotificationEvent.isAdmitted(event)) {\n freeze(event.notification)\n admissions = HashMap.set(admissions, event.notification.id, {\n version: event.version,\n fingerprint: event.fingerprint,\n decision: event.decision,\n seq: entry.seq\n })\n if (\n event.version !== undefined &&\n state.items.some((item) => item.notification.id === event.notification.id)\n ) {\n state = Object.freeze({\n ...state,\n items: Object.freeze(state.items.map((item) =>\n item.notification.id === event.notification.id\n ? Object.freeze({ notification: event.notification, seq: item.seq })\n : item\n ))\n })\n } else {\n state = NotificationState.applyAdmission(state, event.notification, entry.seq, event.decision)\n }\n } else {\n promotions = HashMap.set(\n promotions,\n NotificationEvent.drainKey(event.targetLineageId, event.boundary),\n entry.seq\n )\n state = NotificationState.applyPromoted(state, event.ids)\n }\n }\n\n const loaded: Loaded = { state, admissions, promotions, cursor }\n yield* journal.whenCommitted(Effect.sync(() => folded.put(runId, loaded)))\n return loaded\n })\n\n /**\n * One conditional admission: the duplicate check, the capacity guard,\n * and the admission record, decided and written inside ONE journal\n * write transaction.\n *\n * This is the queue's conditional insert, the same read-guarded write\n * the journal itself uses for its dedupe and fence conditions: the\n * guard's reads and the insert share one serialized write transaction,\n * and the durable writer replays the whole body when a concurrent\n * writer commits between them. A decision a stale fold produced can\n * therefore never commit — the replay re-folds against the committed\n * state and a queue that filled meanwhile refuses — however many\n * processes share the database. The process-local `operations`\n * semaphore orders this layer's own fibers only; it is not what\n * enforces capacity across processes.\n */\n const admitInTransaction = (\n runId: JournalEvent.RunId,\n admitted: NotificationModel.Notification,\n admittedFingerprint: string,\n version?: number\n ): Effect.Effect<AdmissionReceipt, Journal.JournalError | NotificationError> =>\n Effect.gen(function*() {\n const loaded = yield* load(runId)\n const prior = Option.getOrUndefined(HashMap.get(loaded.admissions, admitted.id))\n const replacing = prior !== undefined && version !== undefined && version > (prior.version ?? 0)\n if (replacing) {\n const original = yield* admittedAt(runId, prior.seq)\n if (Option.isNone(original)) {\n return yield* new NotificationError({\n code: \"notification_unavailable\",\n notificationId: admitted.id,\n message: \"Original steering input is unreadable\"\n })\n }\n const identity = (input: NotificationModel.Notification) => ({ ...input, payload: null })\n if (canonical(identity(original.value.notification)) !== canonical(identity(admitted))) {\n return yield* new NotificationError({\n code: \"notification_id_reused\",\n notificationId: admitted.id,\n message: \"An edit cannot change the notification owner or delivery binding\"\n })\n }\n }\n if (replacing && !loaded.state.items.some((item) => item.notification.id === admitted.id)) {\n const original = yield* admittedAt(runId, prior.seq)\n if (Option.isNone(original)) {\n return yield* new NotificationError({\n code: \"notification_unavailable\",\n message: \"Consumed input is unreadable\"\n })\n }\n return {\n notificationId: admitted.id,\n decision: prior.decision,\n seq: prior.seq,\n duplicate: true,\n consumed: true,\n consumedNotification: original.value.notification\n }\n }\n if (prior !== undefined && version !== undefined && version < (prior.version ?? 0)) {\n return { notificationId: admitted.id, decision: prior.decision, seq: prior.seq, duplicate: true }\n }\n if (prior !== undefined && !replacing) {\n // Legacy rows have no fingerprint; only their persisted content is\n // available for comparison, and the fold keeps identities rather\n // than payloads, so the original is read back at its sequence.\n // New rows settle on the fingerprint alone and read nothing.\n let same = prior.fingerprint === admittedFingerprint\n if (prior.fingerprint === undefined) {\n const original = yield* admittedAt(runId, prior.seq)\n if (Option.isNone(original)) {\n return yield* new NotificationError({\n code: \"notification_unavailable\",\n notificationId: admitted.id,\n message: `The admission for notification ${admitted.id} is no longer readable`\n })\n }\n same = canonical(original.value.notification) === canonical(admitted)\n }\n if (!same) {\n return yield* new NotificationError({\n code: \"notification_id_reused\",\n notificationId: admitted.id,\n message: `Notification id ${admitted.id} was already admitted with different content`\n })\n }\n return {\n notificationId: admitted.id,\n decision: prior.decision,\n seq: prior.seq,\n duplicate: true\n }\n }\n\n const admission = replacing ? { decision: \"coalesced\" as const } : NotificationState.admit(\n loaded.state,\n admitted,\n loaded.cursor === undefined ? 0 : loaded.cursor + 1\n )\n if (admission.decision === \"rejected-full\") {\n // Nothing is journaled. A rejection recorded as an admission\n // would match on every later attempt and burn the id forever,\n // so the queue refuses in the receipt alone and the caller may\n // admit again once a boundary has drained.\n return {\n notificationId: admitted.id,\n decision: admission.decision,\n seq: undefined,\n duplicate: false\n }\n }\n\n const receipt = yield* journal.emitDurableUnfenced(\n new JournalEvent.Input({\n runId,\n sourceId: NotificationEvent.admissionSourceId(admitted.id),\n sourceSeq: JournalEvent.SourceSeq.make(version ?? 0),\n // The sequence is derived from the notification's own id, so\n // a collision IS this admission observed twice. Comparing\n // bytes instead would fail the second writer over `decision`,\n // a field neither caller supplied and each derives from the\n // fill level it happened to load at.\n dedupe: \"identity\",\n eventType: NotificationEvent.AdmittedEventType,\n payload: {\n notification: admitted,\n decision: admission.decision,\n fingerprint: admittedFingerprint,\n ...(version === undefined ? {} : { version })\n }\n })\n )\n const committed = Option.getOrUndefined(HashMap.get((yield* load(runId)).admissions, admitted.id))\n if (committed === undefined) {\n // Only reachable when the emit deduped against an entry this\n // queue did not write, so nothing was committed and there is\n // no admission to report.\n return yield* new NotificationError({\n code: \"notification_id_reused\",\n notificationId: admitted.id,\n message: `The journal identity for notification ${admitted.id} holds an event this queue did not write`\n })\n }\n return {\n notificationId: admitted.id,\n decision: committed.decision,\n seq: committed.seq,\n duplicate: receipt._tag === \"Duplicate\"\n }\n })\n\n return make({\n admit: Effect.fn(\"NotificationQueue.admit\")((rawRunId, notification, version) =>\n Effect.gen(function*() {\n if (version !== undefined && (!Number.isSafeInteger(version) || version < 1)) {\n return yield* new NotificationError({\n code: \"notification_invalid\",\n message: \"version must be a positive safe integer\"\n })\n }\n const admitted = yield* validated(notification)\n // T-COL-12 stays dark: nothing yet authenticates a watcher burst, pins\n // its coding run or enforces fresh machine reads. Refusing here, before\n // the journal, keeps co-admitted steers deliverable on every replay.\n const payload = admitted.payload\n if (\n typeof payload === \"object\" && payload !== null && !Array.isArray(payload) &&\n (payload as Readonly<Record<string, unknown>>)[\"kind\"] === \"outside_change\"\n ) {\n return yield* new NotificationError({\n code: \"notification_refused\",\n notificationId: admitted.id,\n message: \"Outside-change delivery requires authenticated watcher facts and stale-write enforcement\"\n })\n }\n const admittedFingerprint = yield* fingerprint(admitted)\n return yield* journal.transact(\n operations.withPermits(1)(\n admitInTransaction(JournalEvent.RunId.make(rawRunId), admitted, admittedFingerprint, version)\n )\n )\n })\n ),\n drain: Effect.fn(\"NotificationQueue.drain\")((input) =>\n journal.transact(\n operations.withPermits(1)(Effect.gen(function*() {\n const runId = JournalEvent.RunId.make(input.runId)\n const key = NotificationEvent.drainKey(input.targetLineageId, input.boundary)\n const loaded = yield* load(runId)\n // A replay reports what the committed record delivered, read\n // back from the journal: the fold holds the identities, not the\n // notifications, so a run's drained payloads are not retained.\n const delivered = (\n record: NotificationEvent.Promoted,\n admissions: HashMap.HashMap<string, Committed>\n ): Effect.Effect<\n ReadonlyArray<NotificationModel.Notification>,\n Journal.JournalError | NotificationError\n > =>\n Effect.forEach(record.ids, (id) =>\n Effect.gen(function*() {\n const committed = HashMap.get(admissions, id)\n const event = Option.isNone(committed)\n ? Option.none<NotificationEvent.Admitted>()\n : yield* admittedAt(runId, committed.value.seq)\n // A committed drain promises this exact delivery. Missing or\n // corrupt payloads cannot become a successful partial replay.\n if (Option.isNone(event)) {\n return yield* new NotificationError({\n code: \"notification_unavailable\",\n notificationId: id,\n message: `The delivered notification ${id} is no longer readable`\n })\n }\n return event.value.notification\n }))\n\n const replayed = (seq: number, admissions: HashMap.HashMap<string, Committed>) =>\n Effect.gen(function*() {\n const record = yield* promotedAt(runId, seq)\n if (Option.isNone(record)) {\n return yield* new NotificationError({\n code: \"notification_unavailable\",\n message: `The drain record for boundary ${input.boundary} is no longer readable`\n })\n }\n return {\n notifications: yield* delivered(record.value, admissions),\n boundary: input.boundary,\n duplicate: true\n }\n })\n\n const prior = HashMap.get(loaded.promotions, key)\n if (Option.isSome(prior)) return yield* replayed(prior.value, loaded.admissions)\n\n const cutoff = input.cutoffSeq ?? loaded.cursor ?? 0\n // The existing coding park ladder waits for a person's answer.\n // System facts must remain pending there: promoting them would\n // make the harness treat an outside edit as an answered question.\n // Filtering this projection changes only promoted IDs; the journal\n // fold retains every held admission and its original sequence.\n const promotionState = /^\\d+:[a-f0-9]{64}:park:\\d+$/.test(input.boundary)\n ? {\n ...loaded.state,\n items: loaded.state.items.filter((item) => {\n const payload = item.notification.payload\n return !(typeof payload === \"object\" && payload !== null && !Array.isArray(payload) &&\n (payload as Readonly<Record<string, unknown>>).kind === \"outside_change\")\n })\n }\n : loaded.state\n const steers = NotificationState.promoteSteers(promotionState, cutoff, input.targetLineageId)\n const queued = input.wouldIdle && steers.promoted.length === 0\n ? NotificationState.promoteQueued(steers.state, input.targetLineageId)\n : { state: steers.state, promoted: [] }\n const promoted = [...steers.promoted, ...queued.promoted].map((item) => item.notification)\n const receipt = yield* journal.emitDurableUnfenced(\n new JournalEvent.Input({\n runId,\n sourceId: NotificationEvent.drainSourceId(input.targetLineageId, input.boundary),\n sourceSeq: JournalEvent.SourceSeq.make(0),\n // The identity is the drain: this lineage closing this\n // boundary. Two processes that both reach it have observed one\n // event, and the first record committed is the delivery.\n dedupe: \"identity\",\n eventType: NotificationEvent.PromotedEventType,\n payload: {\n boundary: input.boundary,\n targetLineageId: input.targetLineageId,\n ids: promoted.map((notification) => notification.id)\n }\n })\n )\n const settled = yield* load(runId)\n const committed = HashMap.get(settled.promotions, key)\n if (Option.isNone(committed)) {\n return yield* new NotificationError({\n code: \"notification_unavailable\",\n message: `The journal identity for boundary ${input.boundary} holds an event this queue did not write`\n })\n }\n // An accepted emit wrote THIS drain, so the promoted set is the\n // one already in hand and no read-back is owed.\n if (receipt._tag === \"Accepted\") {\n return { notifications: promoted, boundary: input.boundary, duplicate: false }\n }\n return yield* replayed(committed.value, settled.admissions)\n }))\n )\n ),\n pending: Effect.fn(\"NotificationQueue.pending\")((rawRunId) =>\n Effect.map(\n load(JournalEvent.RunId.make(rawRunId)),\n // The fold `load` already performs: admissions add, promotions\n // remove. Read outside the operations semaphore, because a count\n // taken while a boundary drains is a count taken at some instant\n // either side of it, and blocking a supervisor behind a turn\n // boundary would be the worse answer.\n (loaded) => loaded.state.items.map((item) => item.notification)\n )\n )\n })\n })\n )\n\n/**\n * Journal-backed production layer at the default capacity.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer: Layer.Layer<NotificationQueue, never, Journal.Journal> = layerWith()\n"]}
|
|
1
|
+
{"version":3,"file":"NotificationQueue.js","sourceRoot":"","sources":["../../src/NotificationQueue.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Journal-backed durable notification admission and turn-boundary drain.\n *\n * Governing contract: `packages/smithers/notifications/docs/api.md`, published as\n * `/api/notifications`.\n *\n * @since 0.1.0\n */\n\nimport { canonicalize } from \"@smthrs/canonical\"\nimport { digestSync } from \"@smthrs/crypto/Sha256\"\nimport { Journal, JournalEvent } from \"@smthrs/journal\"\nimport { Context, Effect, HashMap, Layer, Option, Schema, SchemaIssue, Semaphore } from \"effect\"\nimport * as FoldCache from \"./internal/foldCache.ts\"\nimport * as Replay from \"./internal/replay.ts\"\nimport * as NotificationModel from \"./Notification.ts\"\nimport * as NotificationEvent from \"./NotificationEvent.ts\"\nimport * as NotificationState from \"./NotificationState.ts\"\n\n/**\n * The queue could not be reached, or it refused what a caller handed it.\n *\n * `code` is the stable half and the one to branch on. `notification_unavailable`\n * is the seam reporting that it serves nothing; `notification_id_reused` says a\n * stable id was admitted once already with different content, which is a\n * producer bug rather than a storage failure; `notification_invalid` says the\n * value is not a notification, and `path` names the field inside it that failed.\n * `notification_closed` means the receiver has finished and the caller must\n * start a new request; `notification_full` means nothing was retained and the\n * same notification may be retried after pending work drains. Neither the\n * message nor `path` ever carries the offending value.\n * `notification_refused` rejects reserved outside-change payloads before admission.\n *\n * @category errors\n * @since 0.1.0\n */\nexport class NotificationError extends Schema.TaggedError<NotificationError>()(\n \"/notifications/NotificationError\",\n {\n code: Schema.Literals([\n \"notification_unavailable\",\n \"notification_closed\",\n \"notification_full\",\n \"notification_id_reused\",\n \"notification_invalid\",\n \"notification_refused\"\n ]).pipe(\n Schema.withConstructorDefault(Effect.succeed(\"notification_unavailable\" as const))\n ),\n message: Schema.String,\n /** The notification the failure is about, when one was readable. */\n notificationId: Schema.optional(Schema.String),\n /** Dotted path of the offending field, for `notification_invalid`. */\n path: Schema.optional(Schema.String)\n }\n) {}\n\n/**\n * What admitting one notification decided.\n *\n * `seq` is the journal sequence the admission committed at, and it is absent\n * exactly when nothing was written, which today means a `rejected-full`\n * refusal. `duplicate` says the id was already admitted, in which case\n * `decision` and `seq` are read back from the committed record rather than\n * recomputed here.\n *\n * A caller MUST inspect `decision`: `rejected-full` means the queue was at\n * capacity and retained nothing, so the notification never reaches the model\n * unless the caller admits it again once a boundary has drained.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface AdmissionReceipt {\n readonly notificationId: string\n readonly decision: NotificationState.AdmissionDecision\n readonly seq: number | undefined\n readonly duplicate: boolean\n readonly consumed?: boolean\n readonly consumedNotification?: NotificationModel.Notification\n}\n\n/**\n * The boundary a drain is attempted at, and whether the run would go idle\n * if nothing were delivered.\n *\n * `(runId, targetLineageId, boundary)` is the unit of drain: two lineages\n * closing a turn under the same boundary name are two drains, and each is\n * recorded separately.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface DrainInput {\n readonly runId: string\n readonly targetLineageId: string\n readonly boundary: string\n readonly wouldIdle: boolean\n /**\n * The journal sequence that opened this turn. A steer admitted after it is\n * held for the next boundary, which is what keeps a message that arrived\n * mid-turn out of the turn already in flight. Omitting it delivers\n * everything pending for the lineage. A value that is not a non-negative\n * integer fails the drain with `notification_invalid` and records nothing.\n */\n readonly cutoffSeq?: number | undefined\n}\n\n/**\n * The notifications this boundary delivers, and whether the boundary had\n * already drained.\n *\n * The notifications are the ones the committed promotion record names, so two\n * processes draining one boundary report the same delivery rather than two\n * divergent guesses.\n *\n * @category models\n * @since 0.1.0\n */\nexport interface DrainReceipt {\n readonly notifications: ReadonlyArray<NotificationModel.Notification>\n readonly boundary: string\n readonly duplicate: boolean\n}\n\n/**\n * The durable pending queue: admit a notification exactly once, and drain\n * what a boundary is allowed to deliver.\n *\n * @category services\n * @since 0.1.0\n */\nexport interface Service {\n readonly admit: (\n runId: string,\n notification: NotificationModel.Notification,\n version?: number\n ) => Effect.Effect<AdmissionReceipt, Journal.JournalError | NotificationError>\n readonly drain: (\n input: DrainInput\n ) => Effect.Effect<DrainReceipt, Journal.JournalError | NotificationError>\n /**\n * What this run has been told that no boundary has delivered yet, in\n * admission order.\n *\n * The queue is the owner of that fact: pending is admitted minus promoted,\n * and both halves are its own journal records. A supervisor that counted\n * admissions alone would report a steer as waiting forever.\n */\n readonly pending: (\n runId: string\n ) => Effect.Effect<ReadonlyArray<NotificationModel.Notification>, Journal.JournalError | NotificationError>\n}\n\n/**\n * The {@link Service} tag.\n *\n * @category services\n * @since 0.1.0\n */\nexport class NotificationQueue extends Context.Service<NotificationQueue, Service>()(\n \"/notifications/NotificationQueue\"\n) {}\n\n/**\n * Builds a {@link Service} from an implementation of its methods.\n *\n * @param implementation the methods to serve\n * @category constructors\n * @since 0.1.0\n */\nexport const make = (implementation: Service): Service => NotificationQueue.of(implementation)\n\nconst unavailable = (operation: string): NotificationError =>\n new NotificationError({ code: \"notification_unavailable\", message: `${operation} is unavailable` })\n\n/**\n * A {@link Service} that fails every method as unavailable. Overrides\n * replace individual methods.\n *\n * @param overrides methods to serve instead of failing\n * @category constructors\n * @since 0.1.0\n */\nexport const makeNoop = (overrides: Partial<Service> = {}): Service =>\n make({\n admit: Effect.fn(\"NotificationQueue.admit\")(() => Effect.fail(unavailable(\"admit\"))),\n drain: Effect.fn(\"NotificationQueue.drain\")(() => Effect.fail(unavailable(\"drain\"))),\n pending: Effect.fn(\"NotificationQueue.pending\")(() => Effect.fail(unavailable(\"pending\"))),\n ...overrides\n })\n\n/**\n * Provides {@link makeNoop}.\n *\n * @param overrides methods to serve instead of failing\n * @category layers\n * @since 0.1.0\n */\nexport const layerNoop = (overrides: Partial<Service> = {}): Layer.Layer<NotificationQueue> =>\n Layer.succeed(NotificationQueue)(makeNoop(overrides))\n\n/**\n * The deepest a notification's JSON may nest.\n *\n * The bound exists because the value is walked and serialized on the way to\n * the journal, and an unbounded walk turns a hostile payload into an untyped\n * `RangeError` instead of a refusal a caller can read.\n */\nconst maximumPayloadDepth = 256\n\n/**\n * The largest a notification may be, in UTF-16 code units: every string and\n * object key counted at its length, and every other value as one.\n *\n * The bound exists because an admission copies, decodes, canonicalizes,\n * fingerprints, and journals the whole value, so a producer admitting a huge\n * array or string spends the notifying process's CPU and memory and bloats\n * every replay of the run. It is checked before any of that work starts.\n */\nconst maximumNotificationCodeUnits = 1_048_576\n\n/** Bounds an issue message so a refusal cannot journal or log an essay. */\nconst maximumIssueCodeUnits = 200\n\n/**\n * The bound a value breaks, if any, walked iteratively so the walk itself\n * cannot exhaust the stack. A cycle is caught by the depth bound: it has no\n * finite depth, so it trips the limit rather than looping forever. The size\n * count stops the walk as soon as it passes the bound, so an oversized value\n * costs the walk no more than the bound does.\n */\nconst exceededBound = (value: unknown): \"depth\" | \"size\" | undefined => {\n const pending: Array<{ readonly value: unknown; readonly depth: number }> = [{ value, depth: 0 }]\n let size = 0\n while (pending.length > 0) {\n const next = pending.pop()!\n size += typeof next.value === \"string\" ? next.value.length : 1\n if (size > maximumNotificationCodeUnits) return \"size\"\n if (typeof next.value !== \"object\" || next.value === null) continue\n if (next.depth >= maximumPayloadDepth) return \"depth\"\n if (Array.isArray(next.value)) {\n for (const child of next.value) pending.push({ value: child, depth: next.depth + 1 })\n continue\n }\n for (const [key, child] of Object.entries(next.value)) {\n size += key.length\n pending.push({ value: child, depth: next.depth + 1 })\n }\n }\n return undefined\n}\n\n/**\n * A structural copy of the caller's value, so nothing the queue journals or\n * retains can be edited afterwards through a reference the caller kept. Values\n * that are not arrays or objects pass through, which is what leaves a function\n * or a symbol in place for the schema to reject.\n */\nconst copied = (value: unknown): unknown => {\n if (Array.isArray(value)) return value.map(copied)\n if (typeof value === \"object\" && value !== null) {\n return Object.fromEntries(Object.entries(value).map(([key, nested]) => [key, copied(nested)]))\n }\n return value\n}\n\nconst formatIssue = SchemaIssue.makeFormatterStandardSchemaV1({ leafHook: (issue) => issue._tag })\n\nconst bounded = (text: string): string =>\n text.length <= maximumIssueCodeUnits ? text : `${text.slice(0, maximumIssueCodeUnits - 3)}...`\n\nconst identifiedBy = (value: unknown): string | undefined => {\n if (typeof value !== \"object\" || value === null) return undefined\n const id = (value as Readonly<Record<string, unknown>>)[\"id\"]\n return typeof id === \"string\" ? id : undefined\n}\n\nconst decodeNotification = Schema.decodeUnknownEffect(NotificationModel.Notification)\n\n/**\n * Decodes the caller's argument at the durability boundary and returns a value\n * the caller cannot reach.\n *\n * Nothing downstream re-checks the shape: a structurally invalid notification\n * that reached the journal would be acknowledged at a real sequence and then\n * skipped by every replay, which is acknowledged data loss. The depth and size\n * bounds run first so a hostile payload can neither exhaust the stack nor make\n * the copy, the decode, and the fingerprint walk it in full.\n */\nconst validated = (\n notification: NotificationModel.Notification\n): Effect.Effect<NotificationModel.Notification, NotificationError> =>\n Effect.gen(function*() {\n const notificationId = identifiedBy(notification)\n const exceeded = exceededBound(notification)\n if (exceeded !== undefined) {\n return yield* new NotificationError({\n code: \"notification_invalid\",\n message: exceeded === \"depth\"\n ? `A notification nests deeper than the ${maximumPayloadDepth} level bound`\n : `A notification is larger than the ${maximumNotificationCodeUnits} code unit bound`,\n ...(notificationId === undefined ? {} : { notificationId })\n })\n }\n return yield* decodeNotification(copied(notification)).pipe(\n Effect.mapError((error) => {\n // The formatter reports at least one issue for a failure and gives\n // every issue a concrete path. Standard Schema's result type widens\n // both, so a fallback for either would be code no input can reach.\n const issue = formatIssue(error.issue).issues[0] as {\n readonly message: string\n readonly path: ReadonlyArray<PropertyKey>\n }\n const path = issue.path.map(String).join(\".\")\n return new NotificationError({\n code: \"notification_invalid\",\n message: bounded(issue.message),\n ...(notificationId === undefined ? {} : { notificationId }),\n ...(path === \"\" ? {} : { path })\n })\n })\n )\n })\n\n// Persisted notification JSON predates RFC 8785's rejection of lone surrogates.\nconst canonical = (value: unknown): string => canonicalize(value, { loneSurrogates: \"escape\" })\n\n/** Fingerprint the validated input before the journal redacts its payload. */\nconst fingerprint = (notification: NotificationModel.Notification): Effect.Effect<string> =>\n Effect.sync(() => digestSync(canonical(notification)))\n\n/** Freeze the decoded JSON graph before any queue output can expose it. */\nconst freeze = (value: unknown): void => {\n if (typeof value !== \"object\" || value === null) return\n for (const nested of Object.values(value)) freeze(nested)\n Object.freeze(value)\n}\n\n/**\n * One committed admission, as the journal recorded it.\n *\n * This is an identity, not a payload: the fold keeps the fingerprint that\n * settles a duplicate and the sequence the record lives at, and reads the\n * notification itself back from the journal on the rare path that needs it.\n * Retaining every admitted payload for a run's lifetime is what made a\n * capacity-one queue grow without bound.\n */\ninterface Committed {\n readonly version: number | undefined\n readonly fingerprint: string | undefined\n readonly decision: NotificationState.AdmissionDecision\n readonly seq: number\n}\n\n/**\n * One run's folded notification history.\n *\n * `cursor` is the sequence of the last entry folded, absent when the run has\n * no entries at all. It is what the next read pages from, so a fold costs the\n * entries added since the previous call rather than the run's whole journal.\n */\ninterface Loaded {\n readonly state: NotificationState.State\n readonly admissions: HashMap.HashMap<string, Committed>\n /** Drain identity to the sequence its record committed at. */\n readonly promotions: HashMap.HashMap<string, number>\n readonly cursor: number | undefined\n}\n\n/**\n * How many runs one layer keeps folded at a time. Evicting is safe: the next\n * call for an evicted run folds it again from the beginning, and the retention\n * policy is scan-resistant, so a supervisor sweeping more runs than this reads\n * journal tails for all but a fixed handful of them.\n */\nconst maximumCachedRuns = 64\n\n/**\n * Journal-backed production layer, with the pending capacity a composition\n * chooses.\n *\n * Admission and drain evidence is written through `emitDurableUnfenced`, so\n * both return only after the corresponding entry is durably committed and a\n * dropped lifecycle event is unrepresentable. The channel is the unfenced one\n * on purpose: the notifying process owns no run, so there is no ownership\n * fence to hand over, and both records are first-writer-wins on their own\n * identity instead.\n *\n * The capacity guard is a conditional write at the storage layer, not a check\n * against a fold one process happened to load: the fold, the guard, and the\n * admission record are decided and written inside ONE `journal.transact`\n * write transaction, which the durable writer serializes against every other\n * writer of the database and replays when a concurrent writer commits between\n * the guard's reads and the insert. Two processes admitting to one full queue\n * therefore produce one admission and one `rejected-full`, never two\n * admissions.\n *\n * @param options the pending capacity, defaulting to `NotificationState.defaultCapacity`\n * @category layers\n * @since 1.0.0\n */\nexport const layerWith = (\n options: { readonly capacity?: number | undefined } = {}\n): Layer.Layer<NotificationQueue, never, Journal.Journal> =>\n Layer.effect(\n NotificationQueue,\n Effect.gen(function*() {\n const journal = yield* Journal.Journal\n // Orders this layer's own fibers so one process never runs two of its\n // admissions or drains against the fold cache at once. It is\n // process-local by construction and is NOT what enforces capacity or\n // drain identity across processes: those are the conditional writes\n // inside `journal.transact`, decided and committed in one serialized\n // write transaction. Always enter the journal transaction before taking\n // this permit, matching callers already inside an enclosing transaction.\n const operations = yield* Semaphore.make(1)\n const capacity = options.capacity ?? NotificationState.defaultCapacity\n // Only committed folds may be shared. A transaction reads its own\n // inserts, but its read-back is still provisional until the OUTERMOST\n // commit; neither a retry nor a later caller may dedupe against it.\n const folded = FoldCache.make<Loaded>(maximumCachedRuns)\n\n /**\n * One entry by the sequence it committed at.\n *\n * The fold keeps identities and sequences rather than payloads, so the\n * paths that must report an admitted notification read it back here.\n */\n const entryAt = (\n runId: JournalEvent.RunId,\n seq: number\n ): Effect.Effect<Option.Option<JournalEvent.Entry>, Journal.JournalError> =>\n Effect.map(\n journal.entries({\n runId,\n ...(seq === 0 ? {} : { after: JournalEvent.Seq.make(seq - 1) }),\n limit: 1\n }),\n (page) => {\n const found = page.entries[0]\n return found !== undefined && found.seq === seq ? Option.some(found) : Option.none()\n }\n )\n\n const admittedAt = (\n runId: JournalEvent.RunId,\n seq: number\n ): Effect.Effect<Option.Option<NotificationEvent.Admitted>, Journal.JournalError> =>\n Effect.map(entryAt(runId, seq), (entry) => {\n if (Option.isNone(entry)) return Option.none()\n const decoded = NotificationEvent.fromEntry(entry.value)\n if (Option.isNone(decoded) || !NotificationEvent.isAdmitted(decoded.value)) return Option.none()\n freeze(decoded.value.notification)\n return Option.some(decoded.value)\n })\n\n const promotedAt = (\n runId: JournalEvent.RunId,\n seq: number\n ): Effect.Effect<Option.Option<NotificationEvent.Promoted>, Journal.JournalError> =>\n Effect.map(entryAt(runId, seq), (entry) => {\n if (Option.isNone(entry)) return Option.none()\n const decoded = NotificationEvent.fromEntry(entry.value)\n if (Option.isNone(decoded) || !NotificationEvent.isPromoted(decoded.value)) return Option.none()\n return Option.some(decoded.value)\n })\n\n const load = (runId: JournalEvent.RunId): Effect.Effect<Loaded, Journal.JournalError> =>\n Effect.gen(function*() {\n const base: Loaded = folded.get(runId) ?? {\n state: NotificationState.empty(capacity),\n admissions: HashMap.empty(),\n promotions: HashMap.empty(),\n cursor: undefined\n }\n const fresh: Array<JournalEvent.Entry> = []\n let after = base.cursor === undefined ? undefined : JournalEvent.Seq.make(base.cursor)\n while (true) {\n // `after` is an exact-optional property upstream, so an explicit\n // `undefined` is not the same as an absent key.\n const page = yield* journal.entries({\n runId,\n eventTypes: [NotificationEvent.AdmittedEventType, NotificationEvent.PromotedEventType],\n ...(after === undefined ? {} : { after }),\n limit: 512\n })\n fresh.push(...page.entries)\n if (!page.hasMore || page.entries.length === 0) break\n after = page.entries.at(-1)!.seq\n }\n if (fresh.length === 0) return base\n\n let state = base.state\n let cursor = base.cursor\n // Persistent maps: one event adds a node and leaves the fold the\n // previous load published untouched, where copying both maps would\n // cost the run's whole history on every event and make a long run's\n // folding quadratic in its own admissions.\n let admissions = base.admissions\n let promotions = base.promotions\n for (const entry of fresh) {\n cursor = entry.seq\n const decoded = NotificationEvent.fromEntry(entry)\n if (Option.isNone(decoded)) continue\n const event = decoded.value\n if (NotificationEvent.isAdmitted(event)) {\n freeze(event.notification)\n admissions = HashMap.set(admissions, event.notification.id, {\n version: event.version,\n fingerprint: event.fingerprint,\n decision: event.decision,\n seq: entry.seq\n })\n state = Replay.applyAdmitted(state, event, entry.seq)\n } else {\n promotions = HashMap.set(\n promotions,\n NotificationEvent.drainKey(event.targetLineageId, event.boundary),\n entry.seq\n )\n state = NotificationState.applyPromoted(state, event.ids)\n }\n }\n\n const loaded: Loaded = { state, admissions, promotions, cursor }\n yield* journal.whenCommitted(Effect.sync(() => folded.put(runId, loaded)))\n return loaded\n })\n\n /**\n * One conditional admission: the duplicate check, the capacity guard,\n * and the admission record, decided and written inside ONE journal\n * write transaction.\n *\n * This is the queue's conditional insert, the same read-guarded write\n * the journal itself uses for its dedupe and fence conditions: the\n * guard's reads and the insert share one serialized write transaction,\n * and the durable writer replays the whole body when a concurrent\n * writer commits between them. A decision a stale fold produced can\n * therefore never commit — the replay re-folds against the committed\n * state and a queue that filled meanwhile refuses — however many\n * processes share the database. The process-local `operations`\n * semaphore orders this layer's own fibers only; it is not what\n * enforces capacity across processes.\n */\n const admitInTransaction = (\n runId: JournalEvent.RunId,\n admitted: NotificationModel.Notification,\n admittedFingerprint: string,\n version?: number\n ): Effect.Effect<AdmissionReceipt, Journal.JournalError | NotificationError> =>\n Effect.gen(function*() {\n const loaded = yield* load(runId)\n const prior = Option.getOrUndefined(HashMap.get(loaded.admissions, admitted.id))\n const replacing = prior !== undefined && version !== undefined && version > (prior.version ?? 0)\n if (replacing) {\n const original = yield* admittedAt(runId, prior.seq)\n if (Option.isNone(original)) {\n return yield* new NotificationError({\n code: \"notification_unavailable\",\n notificationId: admitted.id,\n message: \"Original steering input is unreadable\"\n })\n }\n const identity = (input: NotificationModel.Notification) => ({ ...input, payload: null })\n if (canonical(identity(original.value.notification)) !== canonical(identity(admitted))) {\n return yield* new NotificationError({\n code: \"notification_id_reused\",\n notificationId: admitted.id,\n message: \"An edit cannot change the notification owner or delivery binding\"\n })\n }\n }\n if (replacing && !loaded.state.items.some((item) => item.notification.id === admitted.id)) {\n const original = yield* admittedAt(runId, prior.seq)\n if (Option.isNone(original)) {\n return yield* new NotificationError({\n code: \"notification_unavailable\",\n message: \"Consumed input is unreadable\"\n })\n }\n return {\n notificationId: admitted.id,\n decision: prior.decision,\n seq: prior.seq,\n duplicate: true,\n consumed: true,\n consumedNotification: original.value.notification\n }\n }\n if (prior !== undefined && version !== undefined && version < (prior.version ?? 0)) {\n return { notificationId: admitted.id, decision: prior.decision, seq: prior.seq, duplicate: true }\n }\n if (prior !== undefined && !replacing) {\n // Legacy rows have no fingerprint; only their persisted content is\n // available for comparison, and the fold keeps identities rather\n // than payloads, so the original is read back at its sequence.\n // New rows settle on the fingerprint alone and read nothing.\n let same = prior.fingerprint === admittedFingerprint\n if (prior.fingerprint === undefined) {\n const original = yield* admittedAt(runId, prior.seq)\n if (Option.isNone(original)) {\n return yield* new NotificationError({\n code: \"notification_unavailable\",\n notificationId: admitted.id,\n message: `The admission for notification ${admitted.id} is no longer readable`\n })\n }\n same = canonical(original.value.notification) === canonical(admitted)\n }\n if (!same) {\n return yield* new NotificationError({\n code: \"notification_id_reused\",\n notificationId: admitted.id,\n message: `Notification id ${admitted.id} was already admitted with different content`\n })\n }\n return {\n notificationId: admitted.id,\n decision: prior.decision,\n seq: prior.seq,\n duplicate: true\n }\n }\n\n const admission = replacing ? { decision: \"coalesced\" as const } : NotificationState.admit(\n loaded.state,\n admitted,\n loaded.cursor === undefined ? 0 : loaded.cursor + 1\n )\n if (admission.decision === \"rejected-full\") {\n // Nothing is journaled. A rejection recorded as an admission\n // would match on every later attempt and burn the id forever,\n // so the queue refuses in the receipt alone and the caller may\n // admit again once a boundary has drained.\n return {\n notificationId: admitted.id,\n decision: admission.decision,\n seq: undefined,\n duplicate: false\n }\n }\n\n const receipt = yield* journal.emitDurableUnfenced(\n new JournalEvent.Input({\n runId,\n sourceId: NotificationEvent.admissionSourceId(admitted.id),\n sourceSeq: JournalEvent.SourceSeq.make(version ?? 0),\n // The sequence is derived from the notification's own id, so\n // a collision IS this admission observed twice. Comparing\n // bytes instead would fail the second writer over `decision`,\n // a field neither caller supplied and each derives from the\n // fill level it happened to load at.\n dedupe: \"identity\",\n eventType: NotificationEvent.AdmittedEventType,\n payload: {\n notification: admitted,\n decision: admission.decision,\n fingerprint: admittedFingerprint,\n ...(version === undefined ? {} : { version })\n }\n })\n )\n const committed = Option.getOrUndefined(HashMap.get((yield* load(runId)).admissions, admitted.id))\n if (committed === undefined) {\n // Only reachable when the emit deduped against an entry this\n // queue did not write, so nothing was committed and there is\n // no admission to report.\n return yield* new NotificationError({\n code: \"notification_id_reused\",\n notificationId: admitted.id,\n message: `The journal identity for notification ${admitted.id} holds an event this queue did not write`\n })\n }\n return {\n notificationId: admitted.id,\n decision: committed.decision,\n seq: committed.seq,\n duplicate: receipt._tag === \"Duplicate\"\n }\n })\n\n return make({\n admit: Effect.fn(\"NotificationQueue.admit\")((rawRunId, notification, version) =>\n Effect.gen(function*() {\n if (version !== undefined && (!Number.isSafeInteger(version) || version < 1)) {\n return yield* new NotificationError({\n code: \"notification_invalid\",\n message: \"version must be a positive safe integer\"\n })\n }\n const admitted = yield* validated(notification)\n // T-COL-12 stays dark: nothing yet authenticates a watcher burst, pins\n // its coding run or enforces fresh machine reads. Refusing here, before\n // the journal, keeps co-admitted steers deliverable on every replay.\n const payload = admitted.payload\n if (\n typeof payload === \"object\" && payload !== null && !Array.isArray(payload) &&\n (payload as Readonly<Record<string, unknown>>)[\"kind\"] === \"outside_change\"\n ) {\n return yield* new NotificationError({\n code: \"notification_refused\",\n notificationId: admitted.id,\n message: \"Outside-change delivery requires authenticated watcher facts and stale-write enforcement\"\n })\n }\n const admittedFingerprint = yield* fingerprint(admitted)\n return yield* journal.transact(\n operations.withPermits(1)(\n admitInTransaction(JournalEvent.RunId.make(rawRunId), admitted, admittedFingerprint, version)\n )\n )\n })\n ),\n drain: Effect.fn(\"NotificationQueue.drain\")((input) =>\n journal.transact(\n operations.withPermits(1)(Effect.gen(function*() {\n // A cutoff no sequence can satisfy would commit this boundary as\n // delivering nothing, and every retry would replay that record.\n if (\n input.cutoffSeq !== undefined && !(Number.isSafeInteger(input.cutoffSeq) && input.cutoffSeq >= 0)\n ) {\n return yield* new NotificationError({\n code: \"notification_invalid\",\n message: \"A drain cutoff must be a non-negative integer journal sequence\",\n path: \"cutoffSeq\"\n })\n }\n const runId = JournalEvent.RunId.make(input.runId)\n const key = NotificationEvent.drainKey(input.targetLineageId, input.boundary)\n const loaded = yield* load(runId)\n // A replay reports what the committed record delivered, read\n // back from the journal: the fold holds the identities, not the\n // notifications, so a run's drained payloads are not retained.\n const delivered = (\n record: NotificationEvent.Promoted,\n admissions: HashMap.HashMap<string, Committed>\n ): Effect.Effect<\n ReadonlyArray<NotificationModel.Notification>,\n Journal.JournalError | NotificationError\n > =>\n Effect.forEach(record.ids, (id) =>\n Effect.gen(function*() {\n const committed = HashMap.get(admissions, id)\n const event = Option.isNone(committed)\n ? Option.none<NotificationEvent.Admitted>()\n : yield* admittedAt(runId, committed.value.seq)\n // A committed drain promises this exact delivery. Missing or\n // corrupt payloads cannot become a successful partial replay.\n if (Option.isNone(event)) {\n return yield* new NotificationError({\n code: \"notification_unavailable\",\n notificationId: id,\n message: `The delivered notification ${id} is no longer readable`\n })\n }\n return event.value.notification\n }))\n\n const replayed = (seq: number, admissions: HashMap.HashMap<string, Committed>) =>\n Effect.gen(function*() {\n const record = yield* promotedAt(runId, seq)\n if (Option.isNone(record)) {\n return yield* new NotificationError({\n code: \"notification_unavailable\",\n message: `The drain record for boundary ${input.boundary} is no longer readable`\n })\n }\n return {\n notifications: yield* delivered(record.value, admissions),\n boundary: input.boundary,\n duplicate: true\n }\n })\n\n const prior = HashMap.get(loaded.promotions, key)\n if (Option.isSome(prior)) return yield* replayed(prior.value, loaded.admissions)\n\n const cutoff = input.cutoffSeq ?? loaded.cursor ?? 0\n // The existing coding park ladder waits for a person's answer.\n // System facts must remain pending there: promoting them would\n // make the harness treat an outside edit as an answered question.\n // Filtering this projection changes only promoted IDs; the journal\n // fold retains every held admission and its original sequence.\n const promotionState = /^\\d+:[a-f0-9]{64}:park:\\d+$/.test(input.boundary)\n ? {\n ...loaded.state,\n items: loaded.state.items.filter((item) => {\n const payload = item.notification.payload\n return !(typeof payload === \"object\" && payload !== null && !Array.isArray(payload) &&\n (payload as Readonly<Record<string, unknown>>).kind === \"outside_change\")\n })\n }\n : loaded.state\n const steers = NotificationState.promoteSteers(promotionState, cutoff, input.targetLineageId)\n const queued = input.wouldIdle && steers.promoted.length === 0\n ? NotificationState.promoteQueued(steers.state, input.targetLineageId)\n : { state: steers.state, promoted: [] }\n const promoted = [...steers.promoted, ...queued.promoted].map((item) => item.notification)\n const receipt = yield* journal.emitDurableUnfenced(\n new JournalEvent.Input({\n runId,\n sourceId: NotificationEvent.drainSourceId(input.targetLineageId, input.boundary),\n sourceSeq: JournalEvent.SourceSeq.make(0),\n // The identity is the drain: this lineage closing this\n // boundary. Two processes that both reach it have observed one\n // event, and the first record committed is the delivery.\n dedupe: \"identity\",\n eventType: NotificationEvent.PromotedEventType,\n payload: {\n boundary: input.boundary,\n targetLineageId: input.targetLineageId,\n ids: promoted.map((notification) => notification.id)\n }\n })\n )\n const settled = yield* load(runId)\n const committed = HashMap.get(settled.promotions, key)\n if (Option.isNone(committed)) {\n return yield* new NotificationError({\n code: \"notification_unavailable\",\n message: `The journal identity for boundary ${input.boundary} holds an event this queue did not write`\n })\n }\n // An accepted emit wrote THIS drain, so the promoted set is the\n // one already in hand and no read-back is owed.\n if (receipt._tag === \"Accepted\") {\n return { notifications: promoted, boundary: input.boundary, duplicate: false }\n }\n return yield* replayed(committed.value, settled.admissions)\n }))\n )\n ),\n pending: Effect.fn(\"NotificationQueue.pending\")((rawRunId) =>\n Effect.map(\n load(JournalEvent.RunId.make(rawRunId)),\n // The fold `load` already performs: admissions add, promotions\n // remove. Read outside the operations semaphore, because a count\n // taken while a boundary drains is a count taken at some instant\n // either side of it, and blocking a supervisor behind a turn\n // boundary would be the worse answer.\n (loaded) => loaded.state.items.map((item) => item.notification)\n )\n )\n })\n })\n )\n\n/**\n * Journal-backed production layer at the default capacity.\n *\n * @category layers\n * @since 0.1.0\n */\nexport const layer: Layer.Layer<NotificationQueue, never, Journal.Journal> = layerWith()\n"]}
|
package/dist/esm/Projection.d.ts
CHANGED
|
@@ -13,7 +13,10 @@ import * as NotificationState from "./NotificationState.ts";
|
|
|
13
13
|
* `NotificationState.applyAdmission` applies the decision each record already
|
|
14
14
|
* committed, and an admitted record is retained however many are pending. A
|
|
15
15
|
* composition built with `NotificationQueue.layerWith` above the default
|
|
16
|
-
* replays every admission it wrote.
|
|
16
|
+
* replays every admission it wrote. A versioned edit of a steer that is still
|
|
17
|
+
* pending replaces that item in place, exactly as the queue's own fold reads
|
|
18
|
+
* it, so an edited steer projects as one pending item rather than one per
|
|
19
|
+
* version.
|
|
17
20
|
*
|
|
18
21
|
* The projected `capacity` is `NotificationState.defaultCapacity` because the
|
|
19
22
|
* initial state has to start somewhere and no journal record carries the bound
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Projection.d.ts","sourceRoot":"","sources":["../../src/Projection.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"Projection.d.ts","sourceRoot":"","sources":["../../src/Projection.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AAKjE,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAE3D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,MAAM,qEAajB,CAAA"}
|
package/dist/esm/Projection.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import { Projection as JournalProjection } from "@smthrs/journal";
|
|
7
7
|
import * as Effect from "effect/Effect";
|
|
8
8
|
import * as Option from "effect/Option";
|
|
9
|
+
import * as Replay from "./internal/replay.js";
|
|
9
10
|
import * as NotificationEvent from "./NotificationEvent.js";
|
|
10
11
|
import * as NotificationState from "./NotificationState.js";
|
|
11
12
|
/**
|
|
@@ -16,7 +17,10 @@ import * as NotificationState from "./NotificationState.js";
|
|
|
16
17
|
* `NotificationState.applyAdmission` applies the decision each record already
|
|
17
18
|
* committed, and an admitted record is retained however many are pending. A
|
|
18
19
|
* composition built with `NotificationQueue.layerWith` above the default
|
|
19
|
-
* replays every admission it wrote.
|
|
20
|
+
* replays every admission it wrote. A versioned edit of a steer that is still
|
|
21
|
+
* pending replaces that item in place, exactly as the queue's own fold reads
|
|
22
|
+
* it, so an edited steer projects as one pending item rather than one per
|
|
23
|
+
* version.
|
|
20
24
|
*
|
|
21
25
|
* The projected `capacity` is `NotificationState.defaultCapacity` because the
|
|
22
26
|
* initial state has to start somewhere and no journal record carries the bound
|
|
@@ -35,7 +39,7 @@ export const derive = JournalProjection.make({
|
|
|
35
39
|
return Effect.succeed(state);
|
|
36
40
|
const event = decoded.value;
|
|
37
41
|
if (NotificationEvent.isAdmitted(event)) {
|
|
38
|
-
return Effect.succeed(
|
|
42
|
+
return Effect.succeed(Replay.applyAdmitted(state, event, entry.seq));
|
|
39
43
|
}
|
|
40
44
|
return Effect.succeed(NotificationState.applyPromoted(state, event.ids));
|
|
41
45
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Projection.js","sourceRoot":"","sources":["../../src/Projection.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAC3D,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAE3D
|
|
1
|
+
{"version":3,"file":"Projection.js","sourceRoot":"","sources":["../../src/Projection.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,IAAI,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACjE,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,MAAM,MAAM,sBAAsB,CAAA;AAC9C,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAC3D,OAAO,KAAK,iBAAiB,MAAM,wBAAwB,CAAA;AAE3D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,iBAAiB,CAAC,IAAI,CAAC;IAC3C,IAAI,EAAE,qBAAqB;IAC3B,OAAO,EAAE,iBAAiB,CAAC,KAAK,CAAC,iBAAiB,CAAC,eAAe,CAAC;IACnE,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE;QACvB,MAAM,OAAO,GAAG,iBAAiB,CAAC,SAAS,CAAC,KAAK,CAAC,CAAA;QAClD,IAAI,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;YAAE,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;QAExD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAA;QAC3B,IAAI,iBAAiB,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YACxC,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;QACtE,CAAC;QACD,OAAO,MAAM,CAAC,OAAO,CAAC,iBAAiB,CAAC,aAAa,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;IAC1E,CAAC;CACF,CAAC,CAAA","sourcesContent":["/**\n * Journal projection for pending notifications.\n *\n * @since 0.1.0\n */\n\nimport { Projection as JournalProjection } from \"@smthrs/journal\"\nimport * as Effect from \"effect/Effect\"\nimport * as Option from \"effect/Option\"\nimport * as Replay from \"./internal/replay.ts\"\nimport * as NotificationEvent from \"./NotificationEvent.ts\"\nimport * as NotificationState from \"./NotificationState.ts\"\n\n/**\n * Re-derives pending notifications from admitted and promoted journal events.\n * Foreign journal entries leave the state unchanged.\n *\n * Replay never re-decides, so capacity does not truncate it:\n * `NotificationState.applyAdmission` applies the decision each record already\n * committed, and an admitted record is retained however many are pending. A\n * composition built with `NotificationQueue.layerWith` above the default\n * replays every admission it wrote. A versioned edit of a steer that is still\n * pending replaces that item in place, exactly as the queue's own fold reads\n * it, so an edited steer projects as one pending item rather than one per\n * version.\n *\n * The projected `capacity` is `NotificationState.defaultCapacity` because the\n * initial state has to start somewhere and no journal record carries the bound\n * the layer was built at. Read `items`, not `capacity`, to learn what a run\n * holds.\n *\n * @category projections\n * @since 0.1.0\n */\nexport const derive = JournalProjection.make({\n name: \"flows/notifications\",\n initial: NotificationState.empty(NotificationState.defaultCapacity),\n reduce: (state, entry) => {\n const decoded = NotificationEvent.fromEntry(entry)\n if (Option.isNone(decoded)) return Effect.succeed(state)\n\n const event = decoded.value\n if (NotificationEvent.isAdmitted(event)) {\n return Effect.succeed(Replay.applyAdmitted(state, event, entry.seq))\n }\n return Effect.succeed(NotificationState.applyPromoted(state, event.ids))\n }\n})\n"]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one replay rule for an admission record, shared by the queue's fold and
|
|
3
|
+
* the journal projection so the two read the same history to the same state.
|
|
4
|
+
*
|
|
5
|
+
* @private
|
|
6
|
+
* @since 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
import type * as NotificationEvent from "../NotificationEvent.ts";
|
|
9
|
+
import * as NotificationState from "../NotificationState.ts";
|
|
10
|
+
/**
|
|
11
|
+
* Applies one committed admission record to the state so far.
|
|
12
|
+
*
|
|
13
|
+
* A versioned record whose id is still pending is an edit: it replaces that
|
|
14
|
+
* item in place and keeps its original sequence, so the edit is delivered at
|
|
15
|
+
* the cutoff the first version was admitted under. Every other record applies
|
|
16
|
+
* the decision it committed.
|
|
17
|
+
*
|
|
18
|
+
* @private
|
|
19
|
+
* @since 1.0.0
|
|
20
|
+
*/
|
|
21
|
+
export declare const applyAdmitted: (state: NotificationState.State, event: NotificationEvent.Admitted, seq: number) => NotificationState.State;
|
|
22
|
+
//# sourceMappingURL=replay.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.d.ts","sourceRoot":"","sources":["../../../src/internal/replay.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,KAAK,iBAAiB,MAAM,yBAAyB,CAAA;AACjE,OAAO,KAAK,iBAAiB,MAAM,yBAAyB,CAAA;AAE5D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa,GACxB,OAAO,iBAAiB,CAAC,KAAK,EAC9B,OAAO,iBAAiB,CAAC,QAAQ,EACjC,KAAK,MAAM,KACV,iBAAiB,CAAC,KAYpB,CAAA"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one replay rule for an admission record, shared by the queue's fold and
|
|
3
|
+
* the journal projection so the two read the same history to the same state.
|
|
4
|
+
*
|
|
5
|
+
* @private
|
|
6
|
+
* @since 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
import * as NotificationState from "../NotificationState.js";
|
|
9
|
+
/**
|
|
10
|
+
* Applies one committed admission record to the state so far.
|
|
11
|
+
*
|
|
12
|
+
* A versioned record whose id is still pending is an edit: it replaces that
|
|
13
|
+
* item in place and keeps its original sequence, so the edit is delivered at
|
|
14
|
+
* the cutoff the first version was admitted under. Every other record applies
|
|
15
|
+
* the decision it committed.
|
|
16
|
+
*
|
|
17
|
+
* @private
|
|
18
|
+
* @since 1.0.0
|
|
19
|
+
*/
|
|
20
|
+
export const applyAdmitted = (state, event, seq) => {
|
|
21
|
+
if (event.version !== undefined && state.items.some((item) => item.notification.id === event.notification.id)) {
|
|
22
|
+
return Object.freeze({
|
|
23
|
+
...state,
|
|
24
|
+
items: Object.freeze(state.items.map((item) => item.notification.id === event.notification.id
|
|
25
|
+
? Object.freeze({ notification: event.notification, seq: item.seq })
|
|
26
|
+
: item))
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
return NotificationState.applyAdmission(state, event.notification, seq, event.decision);
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=replay.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay.js","sourceRoot":"","sources":["../../../src/internal/replay.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,iBAAiB,MAAM,yBAAyB,CAAA;AAE5D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAC3B,KAA8B,EAC9B,KAAiC,EACjC,GAAW,EACc,EAAE;IAC3B,IAAI,KAAK,CAAC,OAAO,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,KAAK,KAAK,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,CAAC;QAC9G,OAAO,MAAM,CAAC,MAAM,CAAC;YACnB,GAAG,KAAK;YACR,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC5C,IAAI,CAAC,YAAY,CAAC,EAAE,KAAK,KAAK,CAAC,YAAY,CAAC,EAAE;gBAC5C,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,KAAK,CAAC,YAAY,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC;gBACpE,CAAC,CAAC,IAAI,CACT,CAAC;SACH,CAAC,CAAA;IACJ,CAAC;IACD,OAAO,iBAAiB,CAAC,cAAc,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,EAAE,GAAG,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;AACzF,CAAC,CAAA","sourcesContent":["/**\n * The one replay rule for an admission record, shared by the queue's fold and\n * the journal projection so the two read the same history to the same state.\n *\n * @private\n * @since 1.0.0\n */\n\nimport type * as NotificationEvent from \"../NotificationEvent.ts\"\nimport * as NotificationState from \"../NotificationState.ts\"\n\n/**\n * Applies one committed admission record to the state so far.\n *\n * A versioned record whose id is still pending is an edit: it replaces that\n * item in place and keeps its original sequence, so the edit is delivered at\n * the cutoff the first version was admitted under. Every other record applies\n * the decision it committed.\n *\n * @private\n * @since 1.0.0\n */\nexport const applyAdmitted = (\n state: NotificationState.State,\n event: NotificationEvent.Admitted,\n seq: number\n): NotificationState.State => {\n if (event.version !== undefined && state.items.some((item) => item.notification.id === event.notification.id)) {\n return Object.freeze({\n ...state,\n items: Object.freeze(state.items.map((item) =>\n item.notification.id === event.notification.id\n ? Object.freeze({ notification: event.notification, seq: item.seq })\n : item\n ))\n })\n }\n return NotificationState.applyAdmission(state, event.notification, seq, event.decision)\n}\n"]}
|
package/docs/README.md
CHANGED
|
@@ -35,7 +35,9 @@ alerts about a run that has been stuck too long.
|
|
|
35
35
|
|
|
36
36
|
## Install
|
|
37
37
|
|
|
38
|
-
|
|
38
|
+
```bash
|
|
39
|
+
npm install @smthrs/notifications@next effect
|
|
40
|
+
```
|
|
39
41
|
|
|
40
42
|
Node.js 26.4.0 or later. `@smthrs/journal` is where the durable records go, and
|
|
41
43
|
the example below imports it directly. See [Installation](./installation.md) for
|
package/docs/api.md
CHANGED
|
@@ -126,13 +126,13 @@ interface DrainInput {
|
|
|
126
126
|
}
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
-
| Field | Meaning
|
|
130
|
-
| ----------------- |
|
|
131
|
-
| `runId` | The run whose journal holds the notifications.
|
|
132
|
-
| `targetLineageId` | The lineage this boundary belongs to. Notifications for other lineages are left pending.
|
|
133
|
-
| `boundary` | The name of this safe point. With `runId` and `targetLineageId` it is the drain identity.
|
|
134
|
-
| `wouldIdle` | Whether the run would have nothing to do if this boundary delivered nothing. When true and no steer was promoted, one queued notification is promoted.
|
|
135
|
-
| `cutoffSeq` | The journal sequence that opened this turn. Steers admitted after it are held for the next boundary. Omitting it delivers everything pending for the lineage. |
|
|
129
|
+
| Field | Meaning |
|
|
130
|
+
| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
131
|
+
| `runId` | The run whose journal holds the notifications. |
|
|
132
|
+
| `targetLineageId` | The lineage this boundary belongs to. Notifications for other lineages are left pending. |
|
|
133
|
+
| `boundary` | The name of this safe point. With `runId` and `targetLineageId` it is the drain identity. |
|
|
134
|
+
| `wouldIdle` | Whether the run would have nothing to do if this boundary delivered nothing. When true and no steer was promoted, one queued notification is promoted. |
|
|
135
|
+
| `cutoffSeq` | The journal sequence that opened this turn. Steers admitted after it are held for the next boundary. Omitting it delivers everything pending for the lineage. A value that is not a non-negative integer fails with `notification_invalid`. |
|
|
136
136
|
|
|
137
137
|
### DrainReceipt
|
|
138
138
|
|
|
@@ -175,7 +175,7 @@ class NotificationError extends Schema.TaggedError<NotificationError>()(
|
|
|
175
175
|
| `notification_closed` | New public refusal code: this receiver has finished. Start a new request; retrying this message at the closed receiver cannot deliver it. |
|
|
176
176
|
| `notification_full` | New public refusal code: nothing was retained. Retry the same notification after pending work drains. |
|
|
177
177
|
| `notification_id_reused` | A stable id was already admitted with different content. A producer bug, not a storage failure. |
|
|
178
|
-
| `notification_invalid` | The value is not a notification. `path` names the field that failed.
|
|
178
|
+
| `notification_invalid` | The value is not a notification, or a drain's `cutoffSeq` is not a non-negative integer. `path` names the field that failed. |
|
|
179
179
|
|
|
180
180
|
Storage failures surface as `Journal.JournalError` instead, so the two stay
|
|
181
181
|
distinguishable.
|
|
@@ -308,6 +308,10 @@ wrote. The projected `capacity` stays at the default because no journal record
|
|
|
308
308
|
carries the bound the layer was built at, so read `items` rather than
|
|
309
309
|
`capacity`.
|
|
310
310
|
|
|
311
|
+
A versioned edit of a steer that is still pending replaces that item in place
|
|
312
|
+
and keeps its original sequence, the same way the queue reads its own history,
|
|
313
|
+
so an edited steer projects as one pending item.
|
|
314
|
+
|
|
311
315
|
## SteerPayload
|
|
312
316
|
|
|
313
317
|
The steering vocabulary carried inside a notification payload. Import from
|
package/docs/installation.md
CHANGED
|
@@ -7,7 +7,11 @@ sidebar:
|
|
|
7
7
|
|
|
8
8
|
## Install the package
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
```bash
|
|
11
|
+
npm install @smthrs/notifications@next effect
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The `next` tag carries the 1.0 release candidates.
|
|
11
15
|
|
|
12
16
|
The package requires Node.js 26.4.0 or later and ships as both ESM and
|
|
13
17
|
CommonJS with TypeScript declarations. It is built on
|
package/docs/quickstart.md
CHANGED
|
@@ -14,7 +14,7 @@ back what each step decided.
|
|
|
14
14
|
## Prerequisites
|
|
15
15
|
|
|
16
16
|
- Node.js 26.4.0 or later.
|
|
17
|
-
- A
|
|
17
|
+
- A project with the packages installed: `npm install @smthrs/notifications@next @smthrs/journal@next effect`.
|
|
18
18
|
|
|
19
19
|
## Write the notifications
|
|
20
20
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@smthrs/notifications",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "1.0.0-rc.
|
|
4
|
+
"version": "1.0.0-rc.8",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"smthrs": {
|
|
7
7
|
"group": "agent"
|
|
@@ -145,15 +145,15 @@
|
|
|
145
145
|
"tag": "next"
|
|
146
146
|
},
|
|
147
147
|
"dependencies": {
|
|
148
|
-
"@smthrs/canonical": "1.0.0-rc.
|
|
149
|
-
"@smthrs/crypto": "1.0.0-rc.
|
|
150
|
-
"@smthrs/journal": "1.0.0-rc.
|
|
148
|
+
"@smthrs/canonical": "1.0.0-rc.8",
|
|
149
|
+
"@smthrs/crypto": "1.0.0-rc.8",
|
|
150
|
+
"@smthrs/journal": "1.0.0-rc.8"
|
|
151
151
|
},
|
|
152
152
|
"devDependencies": {
|
|
153
153
|
"@effect/language-service": "0.87.2",
|
|
154
154
|
"@effect/vitest": "4.0.4",
|
|
155
155
|
"@eslint/js": "9.39.5",
|
|
156
|
-
"@smthrs/database": "1.0.0-rc.
|
|
156
|
+
"@smthrs/database": "1.0.0-rc.8",
|
|
157
157
|
"@types/node": "26.4.1",
|
|
158
158
|
"@vitest/coverage-v8": "5.0.0",
|
|
159
159
|
"dprint": "0.57.1",
|
package/src/NotificationQueue.ts
CHANGED
|
@@ -12,6 +12,7 @@ import { digestSync } from "@smthrs/crypto/Sha256"
|
|
|
12
12
|
import { Journal, JournalEvent } from "@smthrs/journal"
|
|
13
13
|
import { Context, Effect, HashMap, Layer, Option, Schema, SchemaIssue, Semaphore } from "effect"
|
|
14
14
|
import * as FoldCache from "./internal/foldCache.ts"
|
|
15
|
+
import * as Replay from "./internal/replay.ts"
|
|
15
16
|
import * as NotificationModel from "./Notification.ts"
|
|
16
17
|
import * as NotificationEvent from "./NotificationEvent.ts"
|
|
17
18
|
import * as NotificationState from "./NotificationState.ts"
|
|
@@ -99,7 +100,8 @@ export interface DrainInput {
|
|
|
99
100
|
* The journal sequence that opened this turn. A steer admitted after it is
|
|
100
101
|
* held for the next boundary, which is what keeps a message that arrived
|
|
101
102
|
* mid-turn out of the turn already in flight. Omitting it delivers
|
|
102
|
-
* everything pending for the lineage.
|
|
103
|
+
* everything pending for the lineage. A value that is not a non-negative
|
|
104
|
+
* integer fails the drain with `notification_invalid` and records nothing.
|
|
103
105
|
*/
|
|
104
106
|
readonly cutoffSeq?: number | undefined
|
|
105
107
|
}
|
|
@@ -510,21 +512,7 @@ export const layerWith = (
|
|
|
510
512
|
decision: event.decision,
|
|
511
513
|
seq: entry.seq
|
|
512
514
|
})
|
|
513
|
-
|
|
514
|
-
event.version !== undefined &&
|
|
515
|
-
state.items.some((item) => item.notification.id === event.notification.id)
|
|
516
|
-
) {
|
|
517
|
-
state = Object.freeze({
|
|
518
|
-
...state,
|
|
519
|
-
items: Object.freeze(state.items.map((item) =>
|
|
520
|
-
item.notification.id === event.notification.id
|
|
521
|
-
? Object.freeze({ notification: event.notification, seq: item.seq })
|
|
522
|
-
: item
|
|
523
|
-
))
|
|
524
|
-
})
|
|
525
|
-
} else {
|
|
526
|
-
state = NotificationState.applyAdmission(state, event.notification, entry.seq, event.decision)
|
|
527
|
-
}
|
|
515
|
+
state = Replay.applyAdmitted(state, event, entry.seq)
|
|
528
516
|
} else {
|
|
529
517
|
promotions = HashMap.set(
|
|
530
518
|
promotions,
|
|
@@ -728,6 +716,17 @@ export const layerWith = (
|
|
|
728
716
|
drain: Effect.fn("NotificationQueue.drain")((input) =>
|
|
729
717
|
journal.transact(
|
|
730
718
|
operations.withPermits(1)(Effect.gen(function*() {
|
|
719
|
+
// A cutoff no sequence can satisfy would commit this boundary as
|
|
720
|
+
// delivering nothing, and every retry would replay that record.
|
|
721
|
+
if (
|
|
722
|
+
input.cutoffSeq !== undefined && !(Number.isSafeInteger(input.cutoffSeq) && input.cutoffSeq >= 0)
|
|
723
|
+
) {
|
|
724
|
+
return yield* new NotificationError({
|
|
725
|
+
code: "notification_invalid",
|
|
726
|
+
message: "A drain cutoff must be a non-negative integer journal sequence",
|
|
727
|
+
path: "cutoffSeq"
|
|
728
|
+
})
|
|
729
|
+
}
|
|
731
730
|
const runId = JournalEvent.RunId.make(input.runId)
|
|
732
731
|
const key = NotificationEvent.drainKey(input.targetLineageId, input.boundary)
|
|
733
732
|
const loaded = yield* load(runId)
|