@dyrected/core 2.8.3 → 2.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{app-config-BccpBUqJ.d.cts → app-config-DQhcrGDE.d.cts} +472 -85
- package/dist/{app-config-BccpBUqJ.d.ts → app-config-DQhcrGDE.d.ts} +472 -85
- package/dist/chunk-JSBRDJBE.js +30 -0
- package/dist/{chunk-GFFGDHST.js → chunk-TYPDQTE2.js} +1879 -42
- package/dist/index.cjs +1954 -41
- package/dist/index.d.cts +20 -5
- package/dist/index.d.ts +20 -5
- package/dist/index.js +87 -9
- package/dist/{observability-KCMTV7GO.js → observability-HQF4EUSV.js} +1 -0
- package/dist/server.cjs +2109 -45
- package/dist/server.d.cts +38 -16
- package/dist/server.d.ts +38 -16
- package/dist/server.js +250 -17
- package/dist/{where-sanitizer-WJ2W2QCE.js → where-sanitizer-3MNFMYA2.js} +2 -0
- package/package.json +3 -3
- package/dist/__tests__/access-features.test.d.ts +0 -2
- package/dist/__tests__/access-features.test.d.ts.map +0 -1
- package/dist/__tests__/access-features.test.js +0 -159
- package/dist/__tests__/access-features.test.js.map +0 -1
- package/dist/__tests__/access-parity.test.d.ts +0 -2
- package/dist/__tests__/access-parity.test.d.ts.map +0 -1
- package/dist/__tests__/access-parity.test.js +0 -338
- package/dist/__tests__/access-parity.test.js.map +0 -1
- package/dist/__tests__/access.test.d.ts +0 -2
- package/dist/__tests__/access.test.d.ts.map +0 -1
- package/dist/__tests__/access.test.js +0 -137
- package/dist/__tests__/access.test.js.map +0 -1
- package/dist/__tests__/admin-auth.test.d.ts +0 -2
- package/dist/__tests__/admin-auth.test.d.ts.map +0 -1
- package/dist/__tests__/admin-auth.test.js +0 -440
- package/dist/__tests__/admin-auth.test.js.map +0 -1
- package/dist/__tests__/app.test.d.ts +0 -2
- package/dist/__tests__/app.test.d.ts.map +0 -1
- package/dist/__tests__/app.test.js +0 -112
- package/dist/__tests__/app.test.js.map +0 -1
- package/dist/__tests__/audit.test.d.ts +0 -2
- package/dist/__tests__/audit.test.d.ts.map +0 -1
- package/dist/__tests__/audit.test.js +0 -125
- package/dist/__tests__/audit.test.js.map +0 -1
- package/dist/__tests__/auth-hydration.test.d.ts +0 -2
- package/dist/__tests__/auth-hydration.test.d.ts.map +0 -1
- package/dist/__tests__/auth-hydration.test.js +0 -90
- package/dist/__tests__/auth-hydration.test.js.map +0 -1
- package/dist/__tests__/auth-invite.test.d.ts +0 -2
- package/dist/__tests__/auth-invite.test.d.ts.map +0 -1
- package/dist/__tests__/auth-invite.test.js +0 -152
- package/dist/__tests__/auth-invite.test.js.map +0 -1
- package/dist/__tests__/auth-lockout.test.d.ts +0 -2
- package/dist/__tests__/auth-lockout.test.d.ts.map +0 -1
- package/dist/__tests__/auth-lockout.test.js +0 -154
- package/dist/__tests__/auth-lockout.test.js.map +0 -1
- package/dist/__tests__/auth-sessions.test.d.ts +0 -2
- package/dist/__tests__/auth-sessions.test.d.ts.map +0 -1
- package/dist/__tests__/auth-sessions.test.js +0 -118
- package/dist/__tests__/auth-sessions.test.js.map +0 -1
- package/dist/__tests__/config.test.d.ts +0 -2
- package/dist/__tests__/config.test.d.ts.map +0 -1
- package/dist/__tests__/config.test.js +0 -402
- package/dist/__tests__/config.test.js.map +0 -1
- package/dist/__tests__/define-field.test.d.ts +0 -2
- package/dist/__tests__/define-field.test.d.ts.map +0 -1
- package/dist/__tests__/define-field.test.js +0 -121
- package/dist/__tests__/define-field.test.js.map +0 -1
- package/dist/__tests__/deleteMany.test.d.ts +0 -2
- package/dist/__tests__/deleteMany.test.d.ts.map +0 -1
- package/dist/__tests__/deleteMany.test.js +0 -75
- package/dist/__tests__/deleteMany.test.js.map +0 -1
- package/dist/__tests__/depth.test.d.ts +0 -2
- package/dist/__tests__/depth.test.d.ts.map +0 -1
- package/dist/__tests__/depth.test.js +0 -115
- package/dist/__tests__/depth.test.js.map +0 -1
- package/dist/__tests__/dynamic-options.test.d.ts +0 -2
- package/dist/__tests__/dynamic-options.test.d.ts.map +0 -1
- package/dist/__tests__/dynamic-options.test.js +0 -168
- package/dist/__tests__/dynamic-options.test.js.map +0 -1
- package/dist/__tests__/email.service.test.d.ts +0 -2
- package/dist/__tests__/email.service.test.d.ts.map +0 -1
- package/dist/__tests__/email.service.test.js +0 -52
- package/dist/__tests__/email.service.test.js.map +0 -1
- package/dist/__tests__/field-inference.test-types.d.ts +0 -161
- package/dist/__tests__/field-inference.test-types.d.ts.map +0 -1
- package/dist/__tests__/field-inference.test-types.js +0 -87
- package/dist/__tests__/field-inference.test-types.js.map +0 -1
- package/dist/__tests__/fixtures/editorial-workflow.d.ts +0 -14
- package/dist/__tests__/fixtures/editorial-workflow.d.ts.map +0 -1
- package/dist/__tests__/fixtures/editorial-workflow.js +0 -12
- package/dist/__tests__/fixtures/editorial-workflow.js.map +0 -1
- package/dist/__tests__/hooks.test.d.ts +0 -2
- package/dist/__tests__/hooks.test.d.ts.map +0 -1
- package/dist/__tests__/hooks.test.js +0 -320
- package/dist/__tests__/hooks.test.js.map +0 -1
- package/dist/__tests__/mocks.d.ts +0 -75
- package/dist/__tests__/mocks.d.ts.map +0 -1
- package/dist/__tests__/mocks.js +0 -170
- package/dist/__tests__/mocks.js.map +0 -1
- package/dist/__tests__/observability.test.d.ts +0 -2
- package/dist/__tests__/observability.test.d.ts.map +0 -1
- package/dist/__tests__/observability.test.js +0 -233
- package/dist/__tests__/observability.test.js.map +0 -1
- package/dist/__tests__/openapi.test.d.ts +0 -2
- package/dist/__tests__/openapi.test.d.ts.map +0 -1
- package/dist/__tests__/openapi.test.js +0 -105
- package/dist/__tests__/openapi.test.js.map +0 -1
- package/dist/__tests__/parse-sort.test.d.ts +0 -2
- package/dist/__tests__/parse-sort.test.d.ts.map +0 -1
- package/dist/__tests__/parse-sort.test.js +0 -24
- package/dist/__tests__/parse-sort.test.js.map +0 -1
- package/dist/__tests__/preferences.test.d.ts +0 -2
- package/dist/__tests__/preferences.test.d.ts.map +0 -1
- package/dist/__tests__/preferences.test.js +0 -168
- package/dist/__tests__/preferences.test.js.map +0 -1
- package/dist/__tests__/router.test.d.ts +0 -2
- package/dist/__tests__/router.test.d.ts.map +0 -1
- package/dist/__tests__/router.test.js +0 -70
- package/dist/__tests__/router.test.js.map +0 -1
- package/dist/__tests__/search.test.d.ts +0 -2
- package/dist/__tests__/search.test.d.ts.map +0 -1
- package/dist/__tests__/search.test.js +0 -174
- package/dist/__tests__/search.test.js.map +0 -1
- package/dist/__tests__/upload-validation.test.d.ts +0 -2
- package/dist/__tests__/upload-validation.test.d.ts.map +0 -1
- package/dist/__tests__/upload-validation.test.js +0 -41
- package/dist/__tests__/upload-validation.test.js.map +0 -1
- package/dist/__tests__/where.test.d.ts +0 -2
- package/dist/__tests__/where.test.d.ts.map +0 -1
- package/dist/__tests__/where.test.js +0 -105
- package/dist/__tests__/where.test.js.map +0 -1
- package/dist/__tests__/workflow.test.d.ts +0 -2
- package/dist/__tests__/workflow.test.d.ts.map +0 -1
- package/dist/__tests__/workflow.test.js +0 -684
- package/dist/__tests__/workflow.test.js.map +0 -1
- package/dist/app-config-7G-3tF_I.d.cts +0 -2259
- package/dist/app-config-7G-3tF_I.d.ts +0 -2259
- package/dist/app-config-9hs8DRED.d.cts +0 -1718
- package/dist/app-config-9hs8DRED.d.ts +0 -1718
- package/dist/app-config-B3ByBjCN.d.cts +0 -2222
- package/dist/app-config-B3ByBjCN.d.ts +0 -2222
- package/dist/app-config-B5tYsZaH.d.cts +0 -2404
- package/dist/app-config-B5tYsZaH.d.ts +0 -2404
- package/dist/app-config-BEuyPktb.d.cts +0 -2556
- package/dist/app-config-BEuyPktb.d.ts +0 -2556
- package/dist/app-config-BIkjQWzU.d.cts +0 -2218
- package/dist/app-config-BIkjQWzU.d.ts +0 -2218
- package/dist/app-config-BcuZIpvL.d.cts +0 -1951
- package/dist/app-config-BcuZIpvL.d.ts +0 -1951
- package/dist/app-config-BfpeE3bF.d.cts +0 -2100
- package/dist/app-config-BfpeE3bF.d.ts +0 -2100
- package/dist/app-config-Bm9-2Am6.d.cts +0 -1875
- package/dist/app-config-Bm9-2Am6.d.ts +0 -1875
- package/dist/app-config-BqhrBrSM.d.cts +0 -1522
- package/dist/app-config-BqhrBrSM.d.ts +0 -1522
- package/dist/app-config-Bxg_DVxv.d.cts +0 -1413
- package/dist/app-config-Bxg_DVxv.d.ts +0 -1413
- package/dist/app-config-C0twGIaF.d.cts +0 -1597
- package/dist/app-config-C0twGIaF.d.ts +0 -1597
- package/dist/app-config-C0uxEU7Q.d.cts +0 -1874
- package/dist/app-config-C0uxEU7Q.d.ts +0 -1874
- package/dist/app-config-C3X5BAyK.d.cts +0 -1539
- package/dist/app-config-C3X5BAyK.d.ts +0 -1539
- package/dist/app-config-C9VJ6rTv.d.cts +0 -2092
- package/dist/app-config-C9VJ6rTv.d.ts +0 -2092
- package/dist/app-config-CBOn8IyZ.d.cts +0 -1838
- package/dist/app-config-CBOn8IyZ.d.ts +0 -1838
- package/dist/app-config-CCe-yq9q.d.cts +0 -2222
- package/dist/app-config-CCe-yq9q.d.ts +0 -2222
- package/dist/app-config-CJAGGPrk.d.cts +0 -1900
- package/dist/app-config-CJAGGPrk.d.ts +0 -1900
- package/dist/app-config-CPOs7THF.d.cts +0 -2217
- package/dist/app-config-CPOs7THF.d.ts +0 -2217
- package/dist/app-config-CYV70eoF.d.cts +0 -2100
- package/dist/app-config-CYV70eoF.d.ts +0 -2100
- package/dist/app-config-ChsrBB7p.d.cts +0 -2100
- package/dist/app-config-ChsrBB7p.d.ts +0 -2100
- package/dist/app-config-CiEDJm0e.d.cts +0 -1912
- package/dist/app-config-CiEDJm0e.d.ts +0 -1912
- package/dist/app-config-CstocO9M.d.cts +0 -1580
- package/dist/app-config-CstocO9M.d.ts +0 -1580
- package/dist/app-config-Ctp454zB.d.cts +0 -2214
- package/dist/app-config-Ctp454zB.d.ts +0 -2214
- package/dist/app-config-CwaU1de2.d.cts +0 -1868
- package/dist/app-config-CwaU1de2.d.ts +0 -1868
- package/dist/app-config-DCDh8Gbx.d.cts +0 -1926
- package/dist/app-config-DCDh8Gbx.d.ts +0 -1926
- package/dist/app-config-DCWinlNK.d.cts +0 -2091
- package/dist/app-config-DCWinlNK.d.ts +0 -2091
- package/dist/app-config-DIwvzbpe.d.cts +0 -1555
- package/dist/app-config-DIwvzbpe.d.ts +0 -1555
- package/dist/app-config-DJmNYgYh.d.cts +0 -2305
- package/dist/app-config-DJmNYgYh.d.ts +0 -2305
- package/dist/app-config-DO3laV58.d.cts +0 -2397
- package/dist/app-config-DO3laV58.d.ts +0 -2397
- package/dist/app-config-DVdSospO.d.cts +0 -1842
- package/dist/app-config-DVdSospO.d.ts +0 -1842
- package/dist/app-config-DZf_mmi9.d.cts +0 -2211
- package/dist/app-config-DZf_mmi9.d.ts +0 -2211
- package/dist/app-config-DbK2Tv6i.d.cts +0 -2080
- package/dist/app-config-DbK2Tv6i.d.ts +0 -2080
- package/dist/app-config-Dkndq5ni.d.cts +0 -1580
- package/dist/app-config-Dkndq5ni.d.ts +0 -1580
- package/dist/app-config-Dms6kvE-.d.cts +0 -2072
- package/dist/app-config-Dms6kvE-.d.ts +0 -2072
- package/dist/app-config-DqCipvSI.d.cts +0 -1411
- package/dist/app-config-DqCipvSI.d.ts +0 -1411
- package/dist/app-config-Dv5XACR4.d.cts +0 -2065
- package/dist/app-config-Dv5XACR4.d.ts +0 -2065
- package/dist/app-config-P-SF5nnR.d.cts +0 -1568
- package/dist/app-config-P-SF5nnR.d.ts +0 -1568
- package/dist/app-config-SkUoN5JU.d.cts +0 -2192
- package/dist/app-config-SkUoN5JU.d.ts +0 -2192
- package/dist/app-config-WzqpuZMr.d.cts +0 -2566
- package/dist/app-config-WzqpuZMr.d.ts +0 -2566
- package/dist/app-config-_kkj71CB.d.cts +0 -2010
- package/dist/app-config-_kkj71CB.d.ts +0 -2010
- package/dist/app-config-fEjRm1o9.d.cts +0 -2402
- package/dist/app-config-fEjRm1o9.d.ts +0 -2402
- package/dist/app-config-oU1EGHND.d.cts +0 -1538
- package/dist/app-config-oU1EGHND.d.ts +0 -1538
- package/dist/app-config-ouBRb6Bf.d.cts +0 -1716
- package/dist/app-config-ouBRb6Bf.d.ts +0 -1716
- package/dist/app-config-oz8H_MOX.d.cts +0 -2224
- package/dist/app-config-oz8H_MOX.d.ts +0 -2224
- package/dist/app-config-tITj_0sn.d.cts +0 -1926
- package/dist/app-config-tITj_0sn.d.ts +0 -1926
- package/dist/app-config-wnLtFu6c.d.cts +0 -1538
- package/dist/app-config-wnLtFu6c.d.ts +0 -1538
- package/dist/app-config-x-CnipcA.d.cts +0 -2072
- package/dist/app-config-x-CnipcA.d.ts +0 -2072
- package/dist/app.d.ts +0 -35
- package/dist/app.d.ts.map +0 -1
- package/dist/app.js +0 -164
- package/dist/app.js.map +0 -1
- package/dist/auth/access.d.ts +0 -5
- package/dist/auth/access.d.ts.map +0 -1
- package/dist/auth/access.js +0 -59
- package/dist/auth/access.js.map +0 -1
- package/dist/auth/jexl.d.ts +0 -13
- package/dist/auth/jexl.d.ts.map +0 -1
- package/dist/auth/jexl.js +0 -30
- package/dist/auth/jexl.js.map +0 -1
- package/dist/auth/lockout.d.ts +0 -11
- package/dist/auth/lockout.d.ts.map +0 -1
- package/dist/auth/lockout.js +0 -37
- package/dist/auth/lockout.js.map +0 -1
- package/dist/auth/password.d.ts +0 -10
- package/dist/auth/password.d.ts.map +0 -1
- package/dist/auth/password.js +0 -28
- package/dist/auth/password.js.map +0 -1
- package/dist/auth/sessions.d.ts +0 -39
- package/dist/auth/sessions.d.ts.map +0 -1
- package/dist/auth/sessions.js +0 -126
- package/dist/auth/sessions.js.map +0 -1
- package/dist/auth/token.d.ts +0 -23
- package/dist/auth/token.d.ts.map +0 -1
- package/dist/auth/token.js +0 -40
- package/dist/auth/token.js.map +0 -1
- package/dist/chunk-23NM4LDU.js +0 -2883
- package/dist/chunk-2TKDCCIR.js +0 -2622
- package/dist/chunk-2UXLA4YN.js +0 -3139
- package/dist/chunk-2WODKOUB.js +0 -1627
- package/dist/chunk-35NM2WPO.js +0 -1658
- package/dist/chunk-3CMG2BH2.js +0 -1618
- package/dist/chunk-3SFITPX4.js +0 -2628
- package/dist/chunk-3TRRTN6U.js +0 -3441
- package/dist/chunk-57FNM42D.js +0 -2392
- package/dist/chunk-5U2WRCNH.js +0 -3140
- package/dist/chunk-5ZIA5COH.js +0 -3414
- package/dist/chunk-6TWQ6GTD.js +0 -3480
- package/dist/chunk-AKYYGMFS.js +0 -3365
- package/dist/chunk-AQO4HI2Y.js +0 -3204
- package/dist/chunk-BAMX7YUC.js +0 -1815
- package/dist/chunk-BQV3QW3Y.js +0 -2588
- package/dist/chunk-CQVTQ67D.js +0 -2632
- package/dist/chunk-CUOPCOU2.js +0 -1644
- package/dist/chunk-DCY7QHRB.js +0 -1637
- package/dist/chunk-DFBLNYUH.js +0 -3402
- package/dist/chunk-EH3MJGB5.js +0 -2571
- package/dist/chunk-EJG6AMYB.js +0 -3537
- package/dist/chunk-ELHRRPKX.js +0 -2863
- package/dist/chunk-EOW4YMAV.js +0 -1637
- package/dist/chunk-G43REXPX.js +0 -3147
- package/dist/chunk-GFKBPBJJ.js +0 -3637
- package/dist/chunk-HRLSFTLR.js +0 -3061
- package/dist/chunk-JFA22YFB.js +0 -1625
- package/dist/chunk-K3ASMUKV.js +0 -2601
- package/dist/chunk-KEKOEAX6.js +0 -3139
- package/dist/chunk-L65PQYDP.js +0 -1536
- package/dist/chunk-LFGSRQLX.js +0 -3568
- package/dist/chunk-M3HKRN7E.js +0 -1665
- package/dist/chunk-MQZH7RQC.js +0 -1667
- package/dist/chunk-NNZRYF3U.js +0 -3537
- package/dist/chunk-PRQ2RF5C.js +0 -3485
- package/dist/chunk-PYE2JSY4.js +0 -3568
- package/dist/chunk-PZMDK3MH.js +0 -3203
- package/dist/chunk-QCXLDKZO.js +0 -3272
- package/dist/chunk-QTIAQBNH.js +0 -3097
- package/dist/chunk-ROECUARS.js +0 -2615
- package/dist/chunk-SM32ND5M.js +0 -3472
- package/dist/chunk-T2DGC4MD.js +0 -3008
- package/dist/chunk-T626OZMH.js +0 -2602
- package/dist/chunk-TEGRS6J7.js +0 -2609
- package/dist/chunk-TLU77TLE.js +0 -3203
- package/dist/chunk-UKUEKTAO.js +0 -513
- package/dist/chunk-VCYYBN5J.js +0 -1873
- package/dist/chunk-VM43G3IG.js +0 -506
- package/dist/chunk-VWJKFZQU.js +0 -3139
- package/dist/chunk-WF5A6PDJ.js +0 -1618
- package/dist/chunk-WVD7PORQ.js +0 -1672
- package/dist/chunk-XX7N64NL.js +0 -3550
- package/dist/chunk-XZLIBQSO.js +0 -2397
- package/dist/chunk-YEBCT77K.js +0 -3269
- package/dist/chunk-YSSHLGQ6.js +0 -3139
- package/dist/chunk-YXEKNN5X.js +0 -3085
- package/dist/chunk-Z7CFUYVP.js +0 -3653
- package/dist/chunk-ZISFMAEL.js +0 -3550
- package/dist/controllers/admin-auth.controller.d.ts +0 -71
- package/dist/controllers/admin-auth.controller.d.ts.map +0 -1
- package/dist/controllers/admin-auth.controller.js +0 -450
- package/dist/controllers/admin-auth.controller.js.map +0 -1
- package/dist/controllers/audit.controller.d.ts +0 -42
- package/dist/controllers/audit.controller.d.ts.map +0 -1
- package/dist/controllers/audit.controller.js +0 -149
- package/dist/controllers/audit.controller.js.map +0 -1
- package/dist/controllers/auth.controller.d.ts +0 -143
- package/dist/controllers/auth.controller.d.ts.map +0 -1
- package/dist/controllers/auth.controller.js +0 -572
- package/dist/controllers/auth.controller.js.map +0 -1
- package/dist/controllers/collection.controller.d.ts +0 -170
- package/dist/controllers/collection.controller.d.ts.map +0 -1
- package/dist/controllers/collection.controller.js +0 -1033
- package/dist/controllers/collection.controller.js.map +0 -1
- package/dist/controllers/global.controller.d.ts +0 -20
- package/dist/controllers/global.controller.d.ts.map +0 -1
- package/dist/controllers/global.controller.js +0 -182
- package/dist/controllers/global.controller.js.map +0 -1
- package/dist/controllers/media.controller.d.ts +0 -39
- package/dist/controllers/media.controller.d.ts.map +0 -1
- package/dist/controllers/media.controller.js +0 -178
- package/dist/controllers/media.controller.js.map +0 -1
- package/dist/controllers/preview.controller.d.ts +0 -37
- package/dist/controllers/preview.controller.d.ts.map +0 -1
- package/dist/controllers/preview.controller.js +0 -48
- package/dist/controllers/preview.controller.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/middleware/auth.d.ts +0 -19
- package/dist/middleware/auth.d.ts.map +0 -1
- package/dist/middleware/auth.js +0 -157
- package/dist/middleware/auth.js.map +0 -1
- package/dist/middleware/rate-limit.d.ts +0 -9
- package/dist/middleware/rate-limit.d.ts.map +0 -1
- package/dist/middleware/rate-limit.js +0 -75
- package/dist/middleware/rate-limit.js.map +0 -1
- package/dist/network.d.ts +0 -6
- package/dist/network.d.ts.map +0 -1
- package/dist/network.js +0 -70
- package/dist/network.js.map +0 -1
- package/dist/observability-CNM4QFE4.js +0 -34
- package/dist/observability-L5BORBB2.js +0 -34
- package/dist/observability.d.ts +0 -113
- package/dist/observability.d.ts.map +0 -1
- package/dist/observability.js +0 -497
- package/dist/observability.js.map +0 -1
- package/dist/router.d.ts +0 -8
- package/dist/router.d.ts.map +0 -1
- package/dist/router.js +0 -704
- package/dist/router.js.map +0 -1
- package/dist/server.d.ts.map +0 -1
- package/dist/server.js.map +0 -1
- package/dist/services/audit.service.d.ts +0 -23
- package/dist/services/audit.service.d.ts.map +0 -1
- package/dist/services/audit.service.js +0 -39
- package/dist/services/audit.service.js.map +0 -1
- package/dist/services/defaults.service.d.ts +0 -8
- package/dist/services/defaults.service.d.ts.map +0 -1
- package/dist/services/defaults.service.js +0 -55
- package/dist/services/defaults.service.js.map +0 -1
- package/dist/services/email-template.d.ts +0 -46
- package/dist/services/email-template.d.ts.map +0 -1
- package/dist/services/email-template.js +0 -88
- package/dist/services/email-template.js.map +0 -1
- package/dist/services/email.service.d.ts +0 -34
- package/dist/services/email.service.d.ts.map +0 -1
- package/dist/services/email.service.js +0 -129
- package/dist/services/email.service.js.map +0 -1
- package/dist/services/media.service.d.ts +0 -20
- package/dist/services/media.service.d.ts.map +0 -1
- package/dist/services/media.service.js +0 -49
- package/dist/services/media.service.js.map +0 -1
- package/dist/services/population.service.d.ts +0 -20
- package/dist/services/population.service.d.ts.map +0 -1
- package/dist/services/population.service.js +0 -224
- package/dist/services/population.service.js.map +0 -1
- package/dist/types/access.d.ts +0 -48
- package/dist/types/access.d.ts.map +0 -1
- package/dist/types/access.js +0 -2
- package/dist/types/access.js.map +0 -1
- package/dist/types/adapters.d.ts +0 -152
- package/dist/types/adapters.d.ts.map +0 -1
- package/dist/types/adapters.js +0 -2
- package/dist/types/adapters.js.map +0 -1
- package/dist/types/admin-auth.d.ts +0 -132
- package/dist/types/admin-auth.d.ts.map +0 -1
- package/dist/types/admin-auth.js +0 -2
- package/dist/types/admin-auth.js.map +0 -1
- package/dist/types/admin.d.ts +0 -86
- package/dist/types/admin.d.ts.map +0 -1
- package/dist/types/admin.js +0 -2
- package/dist/types/admin.js.map +0 -1
- package/dist/types/app-config.d.ts +0 -279
- package/dist/types/app-config.d.ts.map +0 -1
- package/dist/types/app-config.js +0 -2
- package/dist/types/app-config.js.map +0 -1
- package/dist/types/documents.d.ts +0 -65
- package/dist/types/documents.d.ts.map +0 -1
- package/dist/types/documents.js +0 -2
- package/dist/types/documents.js.map +0 -1
- package/dist/types/hooks.d.ts +0 -122
- package/dist/types/hooks.d.ts.map +0 -1
- package/dist/types/hooks.js +0 -2
- package/dist/types/hooks.js.map +0 -1
- package/dist/types/index.d.ts +0 -13
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -13
- package/dist/types/index.js.map +0 -1
- package/dist/types/request.d.ts +0 -42
- package/dist/types/request.d.ts.map +0 -1
- package/dist/types/request.js +0 -2
- package/dist/types/request.js.map +0 -1
- package/dist/types/schema-config.d.ts +0 -443
- package/dist/types/schema-config.d.ts.map +0 -1
- package/dist/types/schema-config.js +0 -2
- package/dist/types/schema-config.js.map +0 -1
- package/dist/types/schema-core.d.ts +0 -628
- package/dist/types/schema-core.d.ts.map +0 -1
- package/dist/types/schema-core.js +0 -2
- package/dist/types/schema-core.js.map +0 -1
- package/dist/types/schema-inference.d.ts +0 -73
- package/dist/types/schema-inference.d.ts.map +0 -1
- package/dist/types/schema-inference.js +0 -2
- package/dist/types/schema-inference.js.map +0 -1
- package/dist/types/workflows.d.ts +0 -121
- package/dist/types/workflows.d.ts.map +0 -1
- package/dist/types/workflows.js +0 -7
- package/dist/types/workflows.js.map +0 -1
- package/dist/utils/access-control.d.ts +0 -35
- package/dist/utils/access-control.d.ts.map +0 -1
- package/dist/utils/access-control.js +0 -161
- package/dist/utils/access-control.js.map +0 -1
- package/dist/utils/admin-auth.d.ts +0 -5
- package/dist/utils/admin-auth.d.ts.map +0 -1
- package/dist/utils/admin-auth.js +0 -39
- package/dist/utils/admin-auth.js.map +0 -1
- package/dist/utils/block-references.d.ts +0 -10
- package/dist/utils/block-references.d.ts.map +0 -1
- package/dist/utils/block-references.js +0 -100
- package/dist/utils/block-references.js.map +0 -1
- package/dist/utils/collection-search.d.ts +0 -11
- package/dist/utils/collection-search.d.ts.map +0 -1
- package/dist/utils/collection-search.js +0 -204
- package/dist/utils/collection-search.js.map +0 -1
- package/dist/utils/config.d.ts +0 -8
- package/dist/utils/config.d.ts.map +0 -1
- package/dist/utils/config.js +0 -336
- package/dist/utils/config.js.map +0 -1
- package/dist/utils/hooks.d.ts +0 -41
- package/dist/utils/hooks.d.ts.map +0 -1
- package/dist/utils/hooks.js +0 -177
- package/dist/utils/hooks.js.map +0 -1
- package/dist/utils/openapi.d.ts +0 -6
- package/dist/utils/openapi.d.ts.map +0 -1
- package/dist/utils/openapi.js +0 -981
- package/dist/utils/openapi.js.map +0 -1
- package/dist/utils/parse-sort.d.ts +0 -43
- package/dist/utils/parse-sort.d.ts.map +0 -1
- package/dist/utils/parse-sort.js +0 -70
- package/dist/utils/parse-sort.js.map +0 -1
- package/dist/utils/parse-where.d.ts +0 -69
- package/dist/utils/parse-where.d.ts.map +0 -1
- package/dist/utils/parse-where.js +0 -202
- package/dist/utils/parse-where.js.map +0 -1
- package/dist/utils/readonly-db.d.ts +0 -9
- package/dist/utils/readonly-db.d.ts.map +0 -1
- package/dist/utils/readonly-db.js +0 -21
- package/dist/utils/readonly-db.js.map +0 -1
- package/dist/utils/swagger.d.ts +0 -11
- package/dist/utils/swagger.d.ts.map +0 -1
- package/dist/utils/swagger.js +0 -62
- package/dist/utils/swagger.js.map +0 -1
- package/dist/utils/upload-validation.d.ts +0 -23
- package/dist/utils/upload-validation.d.ts.map +0 -1
- package/dist/utils/upload-validation.js +0 -46
- package/dist/utils/upload-validation.js.map +0 -1
- package/dist/utils/where-sanitizer.d.ts +0 -10
- package/dist/utils/where-sanitizer.d.ts.map +0 -1
- package/dist/utils/where-sanitizer.js +0 -67
- package/dist/utils/where-sanitizer.js.map +0 -1
- package/dist/workflows.d.ts +0 -86
- package/dist/workflows.d.ts.map +0 -1
- package/dist/workflows.js +0 -405
- package/dist/workflows.js.map +0 -1
|
@@ -1,684 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect, vi, beforeEach } from "vitest";
|
|
2
|
-
import { InMemoryAdapter } from "./mocks.js";
|
|
3
|
-
import { defineCollection, defineConfig } from "../index.js";
|
|
4
|
-
import { publishingWorkflow, simplePublishingWorkflow, initializeWorkflowDocument, materializeWorkflowDocument, transitionWorkflow, saveWorkflowDraft, createWorkflowDocument, dispatchPendingLifecycleEvents, WORKFLOW_HISTORY_COLLECTION, LIFECYCLE_EVENTS_COLLECTION, } from "../workflows.js";
|
|
5
|
-
import { EditorialPosts } from "./fixtures/editorial-workflow.js";
|
|
6
|
-
import { generateOpenApi } from "../utils/openapi.js";
|
|
7
|
-
import { createDyrectedApp } from "../app.js";
|
|
8
|
-
// ─── Shared config builders ───────────────────────────────────────────────────
|
|
9
|
-
function makeConfig(db) {
|
|
10
|
-
return defineConfig({ collections: [EditorialPosts], globals: [], db });
|
|
11
|
-
}
|
|
12
|
-
function makeUser(role) {
|
|
13
|
-
return { sub: `user-${role}`, email: `${role}@test.com`, roles: [role], collection: "users" };
|
|
14
|
-
}
|
|
15
|
-
async function seedPost(db, id) {
|
|
16
|
-
const raw = { id, title: "Hello", body: "World" };
|
|
17
|
-
const initialized = initializeWorkflowDocument(raw, publishingWorkflow());
|
|
18
|
-
const doc = { id, ...initialized };
|
|
19
|
-
await db.create({ collection: "posts", data: doc });
|
|
20
|
-
return doc;
|
|
21
|
-
}
|
|
22
|
-
// ─── Tests ────────────────────────────────────────────────────────────────────
|
|
23
|
-
describe("publishingWorkflow template", () => {
|
|
24
|
-
it("initializes a document with __workflow in draft state", () => {
|
|
25
|
-
const doc = initializeWorkflowDocument({ title: "Test" }, publishingWorkflow());
|
|
26
|
-
const workflow = doc.__workflow;
|
|
27
|
-
expect(workflow.state).toBe("draft");
|
|
28
|
-
expect(workflow.revision).toBe(1);
|
|
29
|
-
expect(workflow.publishedRevision).toBeUndefined();
|
|
30
|
-
expect(workflow.publishedAt).toBeUndefined();
|
|
31
|
-
expect(workflow.publishedBy).toBeUndefined();
|
|
32
|
-
});
|
|
33
|
-
it("includes correct states and transitions in the template", () => {
|
|
34
|
-
const wf = publishingWorkflow();
|
|
35
|
-
const stateNames = wf.states.map((s) => s.name);
|
|
36
|
-
expect(stateNames).toContain("draft");
|
|
37
|
-
expect(stateNames).toContain("in_review");
|
|
38
|
-
expect(stateNames).toContain("published");
|
|
39
|
-
const transitionNames = wf.transitions.map((t) => t.name);
|
|
40
|
-
expect(transitionNames).toContain("submit");
|
|
41
|
-
expect(transitionNames).toContain("publish");
|
|
42
|
-
expect(transitionNames).toContain("reject");
|
|
43
|
-
expect(transitionNames).toContain("unpublish");
|
|
44
|
-
});
|
|
45
|
-
it("adds workflow operations and metadata to OpenAPI", () => {
|
|
46
|
-
const spec = generateOpenApi(defineConfig({ collections: [EditorialPosts], globals: [] }));
|
|
47
|
-
expect(spec.paths["/api/collections/posts/{id}/transitions/{transition}"]?.post).toBeTruthy();
|
|
48
|
-
expect(spec.paths["/api/collections/posts/{id}/workflow-history"]?.get).toBeTruthy();
|
|
49
|
-
expect(spec.components.schemas.posts.properties._workflow.$ref).toBe("#/components/schemas/WorkflowMetadata");
|
|
50
|
-
});
|
|
51
|
-
});
|
|
52
|
-
describe("simplePublishingWorkflow template", () => {
|
|
53
|
-
it("initializes a document in draft state", () => {
|
|
54
|
-
const doc = initializeWorkflowDocument({ title: "Test" }, simplePublishingWorkflow());
|
|
55
|
-
const workflow = doc.__workflow;
|
|
56
|
-
expect(workflow.state).toBe("draft");
|
|
57
|
-
expect(workflow.revision).toBe(1);
|
|
58
|
-
});
|
|
59
|
-
it("includes only draft and published states", () => {
|
|
60
|
-
const wf = simplePublishingWorkflow();
|
|
61
|
-
const stateNames = wf.states.map((s) => s.name);
|
|
62
|
-
expect(wf.states).toHaveLength(2);
|
|
63
|
-
expect(stateNames).toContain("draft");
|
|
64
|
-
expect(stateNames).toContain("published");
|
|
65
|
-
expect(wf.states.find((s) => s.name === "published")?.published).toBe(true);
|
|
66
|
-
});
|
|
67
|
-
it("includes only publish and unpublish transitions", () => {
|
|
68
|
-
const wf = simplePublishingWorkflow();
|
|
69
|
-
expect(wf.transitions).toHaveLength(2);
|
|
70
|
-
const publish = wf.transitions.find((t) => t.name === "publish");
|
|
71
|
-
expect(publish?.from).toBe("draft");
|
|
72
|
-
expect(publish?.to).toBe("published");
|
|
73
|
-
const unpublish = wf.transitions.find((t) => t.name === "unpublish");
|
|
74
|
-
expect(unpublish?.from).toBe("published");
|
|
75
|
-
expect(unpublish?.to).toBe("draft");
|
|
76
|
-
expect(unpublish?.unpublish).toBe(true);
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
describe("workflow document visibility", () => {
|
|
80
|
-
const stored = {
|
|
81
|
-
id: "post-live",
|
|
82
|
-
title: "Working draft",
|
|
83
|
-
__published: { title: "Live title" },
|
|
84
|
-
__workflow: { state: "draft", revision: 2, publishedRevision: 1 },
|
|
85
|
-
};
|
|
86
|
-
it("serves the published snapshot to anonymous users and viewers", () => {
|
|
87
|
-
const workflow = publishingWorkflow();
|
|
88
|
-
expect(materializeWorkflowDocument(stored, workflow)?.title).toBe("Live title");
|
|
89
|
-
expect(materializeWorkflowDocument(stored, workflow, makeUser("admin"))?.title).toBe("Working draft");
|
|
90
|
-
expect(materializeWorkflowDocument(stored, workflow, {
|
|
91
|
-
sub: "viewer",
|
|
92
|
-
roles: ["viewer"],
|
|
93
|
-
collection: "users",
|
|
94
|
-
})?.title).toBe("Live title");
|
|
95
|
-
});
|
|
96
|
-
it("does not expose an unpublished draft to a viewer", () => {
|
|
97
|
-
const draft = { id: "draft", title: "Secret", __workflow: { state: "draft", revision: 1 } };
|
|
98
|
-
expect(materializeWorkflowDocument(draft, publishingWorkflow(), {
|
|
99
|
-
sub: "viewer",
|
|
100
|
-
roles: ["viewer"],
|
|
101
|
-
collection: "users",
|
|
102
|
-
})).toBeNull();
|
|
103
|
-
});
|
|
104
|
-
it("keeps published-state docs readable even when the published snapshot is missing", () => {
|
|
105
|
-
const publishedWithoutSnapshot = {
|
|
106
|
-
id: "post-compat",
|
|
107
|
-
title: "Live title",
|
|
108
|
-
__workflow: { state: "published", revision: 3 },
|
|
109
|
-
};
|
|
110
|
-
expect(materializeWorkflowDocument(publishedWithoutSnapshot, publishingWorkflow(), {
|
|
111
|
-
sub: "viewer",
|
|
112
|
-
roles: ["viewer"],
|
|
113
|
-
collection: "users",
|
|
114
|
-
})?.title).toBe("Live title");
|
|
115
|
-
});
|
|
116
|
-
it("keeps legacy published docs queryable through public collection reads", async () => {
|
|
117
|
-
const db = new InMemoryAdapter();
|
|
118
|
-
db.seed("pages", [
|
|
119
|
-
{
|
|
120
|
-
id: "page-about",
|
|
121
|
-
title: "About",
|
|
122
|
-
slug: "about",
|
|
123
|
-
},
|
|
124
|
-
{
|
|
125
|
-
id: "page-draft",
|
|
126
|
-
title: "Draft only",
|
|
127
|
-
slug: "draft-only",
|
|
128
|
-
__workflow: { state: "draft", revision: 1 },
|
|
129
|
-
},
|
|
130
|
-
]);
|
|
131
|
-
const app = await createDyrectedApp(defineConfig({
|
|
132
|
-
collections: [
|
|
133
|
-
defineCollection({
|
|
134
|
-
slug: "pages",
|
|
135
|
-
drafts: true,
|
|
136
|
-
fields: [
|
|
137
|
-
{ name: "title", type: "text" },
|
|
138
|
-
{ name: "slug", type: "text" },
|
|
139
|
-
],
|
|
140
|
-
}),
|
|
141
|
-
],
|
|
142
|
-
globals: [],
|
|
143
|
-
db,
|
|
144
|
-
}));
|
|
145
|
-
const aboutRes = await app.request(`/api/collections/pages?where=${encodeURIComponent(JSON.stringify({ slug: { equals: "about" } }))}`);
|
|
146
|
-
const aboutBody = await aboutRes.json();
|
|
147
|
-
expect(aboutRes.status).toBe(200);
|
|
148
|
-
expect(aboutBody.total).toBe(1);
|
|
149
|
-
expect(aboutBody.docs[0].slug).toBe("about");
|
|
150
|
-
const draftRes = await app.request(`/api/collections/pages?where=${encodeURIComponent(JSON.stringify({ slug: { equals: "draft-only" } }))}`);
|
|
151
|
-
const draftBody = await draftRes.json();
|
|
152
|
-
expect(draftRes.status).toBe(200);
|
|
153
|
-
expect(draftBody.total).toBe(0);
|
|
154
|
-
});
|
|
155
|
-
it("keeps published-state docs queryable through public collection reads when __published is missing", async () => {
|
|
156
|
-
const db = new InMemoryAdapter();
|
|
157
|
-
db.seed("pages", [
|
|
158
|
-
{
|
|
159
|
-
id: "page-about",
|
|
160
|
-
title: "About",
|
|
161
|
-
slug: "about",
|
|
162
|
-
__workflow: { state: "published", revision: 2 },
|
|
163
|
-
},
|
|
164
|
-
]);
|
|
165
|
-
const app = await createDyrectedApp(defineConfig({
|
|
166
|
-
collections: [
|
|
167
|
-
defineCollection({
|
|
168
|
-
slug: "pages",
|
|
169
|
-
drafts: true,
|
|
170
|
-
fields: [
|
|
171
|
-
{ name: "title", type: "text" },
|
|
172
|
-
{ name: "slug", type: "text" },
|
|
173
|
-
],
|
|
174
|
-
}),
|
|
175
|
-
],
|
|
176
|
-
globals: [],
|
|
177
|
-
db,
|
|
178
|
-
}));
|
|
179
|
-
const aboutRes = await app.request(`/api/collections/pages?where=${encodeURIComponent(JSON.stringify({ slug: { equals: "about" } }))}`);
|
|
180
|
-
const aboutBody = await aboutRes.json();
|
|
181
|
-
expect(aboutRes.status).toBe(200);
|
|
182
|
-
expect(aboutBody.total).toBe(1);
|
|
183
|
-
expect(aboutBody.docs[0].slug).toBe("about");
|
|
184
|
-
});
|
|
185
|
-
});
|
|
186
|
-
describe("saveWorkflowDraft", () => {
|
|
187
|
-
let db;
|
|
188
|
-
let config;
|
|
189
|
-
beforeEach(() => {
|
|
190
|
-
db = new InMemoryAdapter();
|
|
191
|
-
config = makeConfig(db);
|
|
192
|
-
});
|
|
193
|
-
it("increments the revision and writes an outbox event", async () => {
|
|
194
|
-
const post = await seedPost(db, "post-1");
|
|
195
|
-
const collection = config.collections[0];
|
|
196
|
-
await saveWorkflowDraft({
|
|
197
|
-
config,
|
|
198
|
-
collection,
|
|
199
|
-
id: "post-1",
|
|
200
|
-
originalDoc: post,
|
|
201
|
-
data: { title: "Updated Title" },
|
|
202
|
-
});
|
|
203
|
-
const updated = await db.findOne({ collection: "posts", id: "post-1" });
|
|
204
|
-
const workflow = updated.__workflow;
|
|
205
|
-
expect(workflow.revision).toBe(2);
|
|
206
|
-
expect(updated.title).toBe("Updated Title");
|
|
207
|
-
// Outbox event written
|
|
208
|
-
const events = await db.find({ collection: LIFECYCLE_EVENTS_COLLECTION });
|
|
209
|
-
expect(events.total).toBeGreaterThan(0);
|
|
210
|
-
const event = events.docs[0];
|
|
211
|
-
expect(event.name).toBe("revision.created");
|
|
212
|
-
expect(event.status).toBe("pending");
|
|
213
|
-
});
|
|
214
|
-
it("upgrades a legacy published doc into workflow draft mode without breaking the public snapshot", async () => {
|
|
215
|
-
const collection = defineCollection({
|
|
216
|
-
slug: "pages",
|
|
217
|
-
workflow: simplePublishingWorkflow(),
|
|
218
|
-
fields: [
|
|
219
|
-
{ name: "title", type: "text" },
|
|
220
|
-
{ name: "slug", type: "text" },
|
|
221
|
-
],
|
|
222
|
-
});
|
|
223
|
-
const legacyConfig = defineConfig({ collections: [collection], globals: [], db });
|
|
224
|
-
await db.create({
|
|
225
|
-
collection: "pages",
|
|
226
|
-
data: {
|
|
227
|
-
id: "page-about",
|
|
228
|
-
title: "About",
|
|
229
|
-
slug: "about",
|
|
230
|
-
},
|
|
231
|
-
});
|
|
232
|
-
const original = await db.findOne({ collection: "pages", id: "page-about" });
|
|
233
|
-
const workflowCollection = legacyConfig.collections[0];
|
|
234
|
-
await saveWorkflowDraft({
|
|
235
|
-
config: legacyConfig,
|
|
236
|
-
collection: workflowCollection,
|
|
237
|
-
id: "page-about",
|
|
238
|
-
originalDoc: original,
|
|
239
|
-
data: { title: "About us", slug: "about" },
|
|
240
|
-
});
|
|
241
|
-
const updated = await db.findOne({ collection: "pages", id: "page-about" });
|
|
242
|
-
expect(updated.title).toBe("About us");
|
|
243
|
-
expect(updated.__published.title).toBe("About");
|
|
244
|
-
expect(updated.__workflow).toMatchObject({
|
|
245
|
-
state: "draft",
|
|
246
|
-
revision: 2,
|
|
247
|
-
publishedRevision: 1,
|
|
248
|
-
});
|
|
249
|
-
const app = await createDyrectedApp(legacyConfig);
|
|
250
|
-
const publicRes = await app.request(`/api/collections/pages?where=${encodeURIComponent(JSON.stringify({ slug: { equals: "about" } }))}`);
|
|
251
|
-
const publicBody = await publicRes.json();
|
|
252
|
-
expect(publicRes.status).toBe(200);
|
|
253
|
-
expect(publicBody.total).toBe(1);
|
|
254
|
-
expect(publicBody.docs[0].title).toBe("About");
|
|
255
|
-
});
|
|
256
|
-
it("returns materialized workflow metadata when updating a legacy published doc through the API", async () => {
|
|
257
|
-
const collection = defineCollection({
|
|
258
|
-
slug: "pages",
|
|
259
|
-
drafts: true,
|
|
260
|
-
fields: [
|
|
261
|
-
{ name: "title", type: "text" },
|
|
262
|
-
{ name: "slug", type: "text" },
|
|
263
|
-
],
|
|
264
|
-
});
|
|
265
|
-
const app = await createDyrectedApp(defineConfig({
|
|
266
|
-
collections: [collection],
|
|
267
|
-
globals: [],
|
|
268
|
-
db,
|
|
269
|
-
}));
|
|
270
|
-
await db.create({
|
|
271
|
-
collection: "pages",
|
|
272
|
-
data: {
|
|
273
|
-
id: "page-about",
|
|
274
|
-
title: "About",
|
|
275
|
-
slug: "about",
|
|
276
|
-
},
|
|
277
|
-
});
|
|
278
|
-
const res = await app.request("/api/collections/pages/page-about", {
|
|
279
|
-
method: "PATCH",
|
|
280
|
-
headers: { "content-type": "application/json" },
|
|
281
|
-
body: JSON.stringify({ title: "About us" }),
|
|
282
|
-
});
|
|
283
|
-
const body = await res.json();
|
|
284
|
-
expect(res.status).toBe(200);
|
|
285
|
-
expect(body.title).toBe("About");
|
|
286
|
-
expect(body._workflow).toMatchObject({
|
|
287
|
-
state: "draft",
|
|
288
|
-
revision: 2,
|
|
289
|
-
publishedRevision: 1,
|
|
290
|
-
});
|
|
291
|
-
});
|
|
292
|
-
});
|
|
293
|
-
describe("createWorkflowDocument", () => {
|
|
294
|
-
let db;
|
|
295
|
-
let config;
|
|
296
|
-
beforeEach(() => {
|
|
297
|
-
db = new InMemoryAdapter();
|
|
298
|
-
config = makeConfig(db);
|
|
299
|
-
});
|
|
300
|
-
it("creates a doc in draft state with revision 1 and an outbox event", async () => {
|
|
301
|
-
const collection = config.collections[0];
|
|
302
|
-
const rawData = { title: "Brand New" };
|
|
303
|
-
const initializedData = initializeWorkflowDocument(rawData, publishingWorkflow());
|
|
304
|
-
const { doc } = await createWorkflowDocument({
|
|
305
|
-
config,
|
|
306
|
-
collection,
|
|
307
|
-
data: initializedData,
|
|
308
|
-
});
|
|
309
|
-
const workflow = doc.__workflow;
|
|
310
|
-
expect(workflow.state).toBe("draft");
|
|
311
|
-
expect(workflow.revision).toBe(1);
|
|
312
|
-
const events = await db.find({ collection: LIFECYCLE_EVENTS_COLLECTION });
|
|
313
|
-
expect(events.total).toBe(1);
|
|
314
|
-
expect(events.docs[0].name).toBe("revision.created");
|
|
315
|
-
});
|
|
316
|
-
});
|
|
317
|
-
describe("transitionWorkflow", () => {
|
|
318
|
-
let db;
|
|
319
|
-
let config;
|
|
320
|
-
beforeEach(() => {
|
|
321
|
-
db = new InMemoryAdapter();
|
|
322
|
-
config = makeConfig(db);
|
|
323
|
-
});
|
|
324
|
-
it("editor can submit a draft for review", async () => {
|
|
325
|
-
await seedPost(db, "post-1");
|
|
326
|
-
const collection = config.collections[0];
|
|
327
|
-
const editor = makeUser("editor");
|
|
328
|
-
const result = await transitionWorkflow({
|
|
329
|
-
config,
|
|
330
|
-
collection,
|
|
331
|
-
id: "post-1",
|
|
332
|
-
transitionName: "submit",
|
|
333
|
-
user: editor,
|
|
334
|
-
req: { query: {}, headers: {} },
|
|
335
|
-
});
|
|
336
|
-
const workflow = result.__workflow;
|
|
337
|
-
expect(workflow.state).toBe("in_review");
|
|
338
|
-
// History entry created
|
|
339
|
-
const history = await db.find({ collection: WORKFLOW_HISTORY_COLLECTION });
|
|
340
|
-
expect(history.total).toBe(1);
|
|
341
|
-
expect(history.docs[0].from).toBe("draft");
|
|
342
|
-
expect(history.docs[0].to).toBe("in_review");
|
|
343
|
-
});
|
|
344
|
-
it("editor cannot publish directly (lacks capability)", async () => {
|
|
345
|
-
await seedPost(db, "post-2");
|
|
346
|
-
// Move to in_review first using a publisher
|
|
347
|
-
const collection = config.collections[0];
|
|
348
|
-
await transitionWorkflow({
|
|
349
|
-
config,
|
|
350
|
-
collection,
|
|
351
|
-
id: "post-2",
|
|
352
|
-
transitionName: "submit",
|
|
353
|
-
user: makeUser("editor"),
|
|
354
|
-
req: { query: {}, headers: {} },
|
|
355
|
-
});
|
|
356
|
-
// Now editor tries to publish — should be denied
|
|
357
|
-
const editor = makeUser("editor");
|
|
358
|
-
await expect(transitionWorkflow({
|
|
359
|
-
config,
|
|
360
|
-
collection,
|
|
361
|
-
id: "post-2",
|
|
362
|
-
transitionName: "publish",
|
|
363
|
-
user: editor,
|
|
364
|
-
req: { query: {}, headers: {} },
|
|
365
|
-
})).rejects.toThrow(/not permitted|403|permission/i);
|
|
366
|
-
});
|
|
367
|
-
it("publisher can publish a submitted doc — promotes __published snapshot", async () => {
|
|
368
|
-
await seedPost(db, "post-3");
|
|
369
|
-
const collection = config.collections[0];
|
|
370
|
-
const editor = makeUser("editor");
|
|
371
|
-
const publisher = makeUser("publisher");
|
|
372
|
-
await transitionWorkflow({
|
|
373
|
-
config,
|
|
374
|
-
collection,
|
|
375
|
-
id: "post-3",
|
|
376
|
-
transitionName: "submit",
|
|
377
|
-
user: editor,
|
|
378
|
-
req: { query: {}, headers: {} },
|
|
379
|
-
});
|
|
380
|
-
const result = await transitionWorkflow({
|
|
381
|
-
config,
|
|
382
|
-
collection,
|
|
383
|
-
id: "post-3",
|
|
384
|
-
transitionName: "publish",
|
|
385
|
-
user: publisher,
|
|
386
|
-
req: { query: {}, headers: {} },
|
|
387
|
-
});
|
|
388
|
-
const workflow = result.__workflow;
|
|
389
|
-
expect(workflow.state).toBe("published");
|
|
390
|
-
expect(workflow.publishedRevision).toBe(1);
|
|
391
|
-
expect(workflow.publishedAt).toBeTruthy();
|
|
392
|
-
expect(workflow.publishedBy).toBe(publisher.sub);
|
|
393
|
-
// __published snapshot present
|
|
394
|
-
const stored = await db.findOne({ collection: "posts", id: "post-3" });
|
|
395
|
-
expect(stored.__published).toBeTruthy();
|
|
396
|
-
expect(stored.__published.title).toBe("Hello");
|
|
397
|
-
});
|
|
398
|
-
it("publisher can reject and the state returns to draft", async () => {
|
|
399
|
-
await seedPost(db, "post-4");
|
|
400
|
-
const collection = config.collections[0];
|
|
401
|
-
await transitionWorkflow({
|
|
402
|
-
config,
|
|
403
|
-
collection,
|
|
404
|
-
id: "post-4",
|
|
405
|
-
transitionName: "submit",
|
|
406
|
-
user: makeUser("editor"),
|
|
407
|
-
req: { query: {}, headers: {} },
|
|
408
|
-
});
|
|
409
|
-
const result = await transitionWorkflow({
|
|
410
|
-
config,
|
|
411
|
-
collection,
|
|
412
|
-
id: "post-4",
|
|
413
|
-
transitionName: "reject",
|
|
414
|
-
user: makeUser("publisher"),
|
|
415
|
-
comment: "Needs more detail.",
|
|
416
|
-
req: { query: {}, headers: {} },
|
|
417
|
-
});
|
|
418
|
-
const workflow = result.__workflow;
|
|
419
|
-
expect(workflow.state).toBe("draft");
|
|
420
|
-
const history = await db.find({ collection: WORKFLOW_HISTORY_COLLECTION });
|
|
421
|
-
const rejectEntry = history.docs.find((h) => h.transition === "reject");
|
|
422
|
-
expect(rejectEntry).toBeTruthy();
|
|
423
|
-
expect(rejectEntry.comment).toBe("Needs more detail.");
|
|
424
|
-
});
|
|
425
|
-
it("unpublish clears the __published snapshot and moves back to draft", async () => {
|
|
426
|
-
await seedPost(db, "post-5");
|
|
427
|
-
const collection = config.collections[0];
|
|
428
|
-
const editor = makeUser("editor");
|
|
429
|
-
const publisher = makeUser("publisher");
|
|
430
|
-
await transitionWorkflow({
|
|
431
|
-
config,
|
|
432
|
-
collection,
|
|
433
|
-
id: "post-5",
|
|
434
|
-
transitionName: "submit",
|
|
435
|
-
user: editor,
|
|
436
|
-
req: { query: {}, headers: {} },
|
|
437
|
-
});
|
|
438
|
-
await transitionWorkflow({
|
|
439
|
-
config,
|
|
440
|
-
collection,
|
|
441
|
-
id: "post-5",
|
|
442
|
-
transitionName: "publish",
|
|
443
|
-
user: publisher,
|
|
444
|
-
req: { query: {}, headers: {} },
|
|
445
|
-
});
|
|
446
|
-
const result = await transitionWorkflow({
|
|
447
|
-
config,
|
|
448
|
-
collection,
|
|
449
|
-
id: "post-5",
|
|
450
|
-
transitionName: "unpublish",
|
|
451
|
-
user: publisher,
|
|
452
|
-
req: { query: {}, headers: {} },
|
|
453
|
-
});
|
|
454
|
-
const workflow = result.__workflow;
|
|
455
|
-
expect(workflow.state).toBe("draft");
|
|
456
|
-
const stored = await db.findOne({ collection: "posts", id: "post-5" });
|
|
457
|
-
expect(stored.__published).toBeNull();
|
|
458
|
-
});
|
|
459
|
-
it("rejects a stale transition when expectedRevision does not match", async () => {
|
|
460
|
-
await seedPost(db, "post-6");
|
|
461
|
-
const collection = config.collections[0];
|
|
462
|
-
await expect(transitionWorkflow({
|
|
463
|
-
config,
|
|
464
|
-
collection,
|
|
465
|
-
id: "post-6",
|
|
466
|
-
transitionName: "submit",
|
|
467
|
-
user: makeUser("editor"),
|
|
468
|
-
expectedRevision: 99, // wrong
|
|
469
|
-
req: { query: {}, headers: {} },
|
|
470
|
-
})).rejects.toThrow(/changed since it was loaded|stale|revision/i);
|
|
471
|
-
});
|
|
472
|
-
it("leaves the document unchanged when a transition is rejected (atomic guarantee)", async () => {
|
|
473
|
-
await seedPost(db, "post-7");
|
|
474
|
-
const collection = config.collections[0];
|
|
475
|
-
// Attempt a forbidden transition
|
|
476
|
-
try {
|
|
477
|
-
await transitionWorkflow({
|
|
478
|
-
config,
|
|
479
|
-
collection,
|
|
480
|
-
id: "post-7",
|
|
481
|
-
transitionName: "publish",
|
|
482
|
-
user: makeUser("editor"), // no publish capability
|
|
483
|
-
req: { query: {}, headers: {} },
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
catch {
|
|
487
|
-
// expected
|
|
488
|
-
}
|
|
489
|
-
const stored = await db.findOne({ collection: "posts", id: "post-7" });
|
|
490
|
-
const workflow = stored.__workflow;
|
|
491
|
-
expect(workflow.state).toBe("draft"); // unchanged
|
|
492
|
-
const history = await db.find({ collection: WORKFLOW_HISTORY_COLLECTION });
|
|
493
|
-
expect(history.total).toBe(0); // no history written
|
|
494
|
-
});
|
|
495
|
-
it("emits a workflow.transitioned outbox event on every successful transition", async () => {
|
|
496
|
-
await seedPost(db, "post-8");
|
|
497
|
-
const collection = config.collections[0];
|
|
498
|
-
await transitionWorkflow({
|
|
499
|
-
config,
|
|
500
|
-
collection,
|
|
501
|
-
id: "post-8",
|
|
502
|
-
transitionName: "submit",
|
|
503
|
-
user: makeUser("editor"),
|
|
504
|
-
req: { query: {}, headers: {} },
|
|
505
|
-
});
|
|
506
|
-
const events = await db.find({ collection: LIFECYCLE_EVENTS_COLLECTION });
|
|
507
|
-
const transitioned = events.docs.find((e) => e.name === "workflow.transitioned");
|
|
508
|
-
expect(transitioned).toBeTruthy();
|
|
509
|
-
expect(transitioned.payload.transition).toBe("submit");
|
|
510
|
-
});
|
|
511
|
-
it("emits an entry.published event when the doc enters the published state", async () => {
|
|
512
|
-
await seedPost(db, "post-9");
|
|
513
|
-
const collection = config.collections[0];
|
|
514
|
-
await transitionWorkflow({
|
|
515
|
-
config,
|
|
516
|
-
collection,
|
|
517
|
-
id: "post-9",
|
|
518
|
-
transitionName: "submit",
|
|
519
|
-
user: makeUser("editor"),
|
|
520
|
-
req: { query: {}, headers: {} },
|
|
521
|
-
});
|
|
522
|
-
await transitionWorkflow({
|
|
523
|
-
config,
|
|
524
|
-
collection,
|
|
525
|
-
id: "post-9",
|
|
526
|
-
transitionName: "publish",
|
|
527
|
-
user: makeUser("publisher"),
|
|
528
|
-
req: { query: {}, headers: {} },
|
|
529
|
-
});
|
|
530
|
-
const events = await db.find({ collection: LIFECYCLE_EVENTS_COLLECTION });
|
|
531
|
-
const published = events.docs.find((e) => e.name === "entry.published");
|
|
532
|
-
expect(published).toBeTruthy();
|
|
533
|
-
expect(published.collection).toBe("posts");
|
|
534
|
-
});
|
|
535
|
-
it("collections without workflow are unaffected (backward compat)", async () => {
|
|
536
|
-
// Plain collection, no workflow
|
|
537
|
-
const plain = defineCollection({
|
|
538
|
-
slug: "plain",
|
|
539
|
-
fields: [{ name: "title", type: "text" }],
|
|
540
|
-
});
|
|
541
|
-
const plainConfig = defineConfig({ collections: [plain], globals: [], db });
|
|
542
|
-
await db.create({ collection: "plain", data: { id: "p-1", title: "Hello" } });
|
|
543
|
-
// Reading back a plain doc should have no __workflow
|
|
544
|
-
const doc = await db.findOne({ collection: "plain", id: "p-1" });
|
|
545
|
-
expect(doc.__workflow).toBeUndefined();
|
|
546
|
-
// No lifecycle events from plain collection operations
|
|
547
|
-
const events = await db.find({ collection: LIFECYCLE_EVENTS_COLLECTION });
|
|
548
|
-
expect(events.total).toBe(0);
|
|
549
|
-
});
|
|
550
|
-
});
|
|
551
|
-
describe("dispatchPendingLifecycleEvents", () => {
|
|
552
|
-
let db;
|
|
553
|
-
let config;
|
|
554
|
-
beforeEach(() => {
|
|
555
|
-
db = new InMemoryAdapter();
|
|
556
|
-
config = makeConfig(db);
|
|
557
|
-
});
|
|
558
|
-
it("calls registered handlers and marks events delivered", async () => {
|
|
559
|
-
// Seed a pending event directly
|
|
560
|
-
const handler = vi.fn().mockResolvedValue(undefined);
|
|
561
|
-
const configWithHandler = {
|
|
562
|
-
...config,
|
|
563
|
-
events: {
|
|
564
|
-
handlers: [handler],
|
|
565
|
-
maxAttempts: 3,
|
|
566
|
-
retryDelayMs: 100,
|
|
567
|
-
},
|
|
568
|
-
};
|
|
569
|
-
await db.create({
|
|
570
|
-
collection: LIFECYCLE_EVENTS_COLLECTION,
|
|
571
|
-
data: {
|
|
572
|
-
id: "evt-1",
|
|
573
|
-
name: "revision.created",
|
|
574
|
-
collection: "posts",
|
|
575
|
-
documentId: "p1",
|
|
576
|
-
status: "pending",
|
|
577
|
-
attempts: 0,
|
|
578
|
-
payload: { collection: "posts", documentId: "p1", revision: 1 },
|
|
579
|
-
occurredAt: new Date().toISOString(),
|
|
580
|
-
createdAt: new Date().toISOString(),
|
|
581
|
-
nextAttemptAt: new Date().toISOString(),
|
|
582
|
-
},
|
|
583
|
-
});
|
|
584
|
-
const dispatched = await dispatchPendingLifecycleEvents(configWithHandler, 50);
|
|
585
|
-
expect(dispatched).toBe(1);
|
|
586
|
-
expect(handler).toHaveBeenCalledOnce();
|
|
587
|
-
const events = await db.find({ collection: LIFECYCLE_EVENTS_COLLECTION });
|
|
588
|
-
expect(events.docs[0].status).toBe("delivered");
|
|
589
|
-
});
|
|
590
|
-
it("increments attempt count and retries on handler failure", async () => {
|
|
591
|
-
const handler = vi.fn().mockRejectedValue(new Error("webhook down"));
|
|
592
|
-
const configWithHandler = {
|
|
593
|
-
...config,
|
|
594
|
-
events: {
|
|
595
|
-
handlers: [handler],
|
|
596
|
-
maxAttempts: 3,
|
|
597
|
-
retryDelayMs: 100,
|
|
598
|
-
},
|
|
599
|
-
};
|
|
600
|
-
await db.create({
|
|
601
|
-
collection: LIFECYCLE_EVENTS_COLLECTION,
|
|
602
|
-
data: {
|
|
603
|
-
id: "evt-2",
|
|
604
|
-
name: "revision.created",
|
|
605
|
-
collection: "posts",
|
|
606
|
-
documentId: "p1",
|
|
607
|
-
status: "pending",
|
|
608
|
-
attempts: 0,
|
|
609
|
-
payload: { collection: "posts", documentId: "p1", revision: 1 },
|
|
610
|
-
occurredAt: new Date().toISOString(),
|
|
611
|
-
createdAt: new Date().toISOString(),
|
|
612
|
-
nextAttemptAt: new Date().toISOString(),
|
|
613
|
-
},
|
|
614
|
-
});
|
|
615
|
-
await dispatchPendingLifecycleEvents(configWithHandler, 50);
|
|
616
|
-
const events = await db.find({ collection: LIFECYCLE_EVENTS_COLLECTION });
|
|
617
|
-
const event = events.docs[0];
|
|
618
|
-
expect(event.status).toBe("failed"); // transitioned to failed status first
|
|
619
|
-
expect(event.attempts).toBe(1);
|
|
620
|
-
});
|
|
621
|
-
it("preserves stable event IDs across retries (no duplicate IDs)", async () => {
|
|
622
|
-
const eventId = "stable-evt-id";
|
|
623
|
-
await db.create({
|
|
624
|
-
collection: LIFECYCLE_EVENTS_COLLECTION,
|
|
625
|
-
data: {
|
|
626
|
-
id: eventId,
|
|
627
|
-
name: "revision.created",
|
|
628
|
-
collection: "posts",
|
|
629
|
-
documentId: "p1",
|
|
630
|
-
status: "pending",
|
|
631
|
-
attempts: 0,
|
|
632
|
-
payload: { collection: "posts", documentId: "p1", revision: 1 },
|
|
633
|
-
occurredAt: new Date().toISOString(),
|
|
634
|
-
createdAt: new Date().toISOString(),
|
|
635
|
-
nextAttemptAt: new Date().toISOString(),
|
|
636
|
-
},
|
|
637
|
-
});
|
|
638
|
-
// No handlers configured — event should stay with same ID
|
|
639
|
-
await dispatchPendingLifecycleEvents(config, 50);
|
|
640
|
-
const stored = await db.findOne({ collection: LIFECYCLE_EVENTS_COLLECTION, id: eventId });
|
|
641
|
-
expect(stored).not.toBeNull();
|
|
642
|
-
expect(stored.id).toBe(eventId);
|
|
643
|
-
});
|
|
644
|
-
it("does not process events whose nextAttemptAt is in the future", async () => {
|
|
645
|
-
const futureDate = new Date(Date.now() + 60_000).toISOString();
|
|
646
|
-
await db.create({
|
|
647
|
-
collection: LIFECYCLE_EVENTS_COLLECTION,
|
|
648
|
-
data: {
|
|
649
|
-
id: "evt-future",
|
|
650
|
-
name: "revision.created",
|
|
651
|
-
collection: "posts",
|
|
652
|
-
documentId: "p1",
|
|
653
|
-
status: "pending",
|
|
654
|
-
attempts: 1,
|
|
655
|
-
payload: {},
|
|
656
|
-
occurredAt: new Date().toISOString(),
|
|
657
|
-
createdAt: new Date().toISOString(),
|
|
658
|
-
nextAttemptAt: futureDate,
|
|
659
|
-
},
|
|
660
|
-
});
|
|
661
|
-
const dispatched = await dispatchPendingLifecycleEvents(config, 50);
|
|
662
|
-
expect(dispatched).toBe(0);
|
|
663
|
-
});
|
|
664
|
-
it("does not retry events that exhausted maxAttempts", async () => {
|
|
665
|
-
const handler = vi.fn();
|
|
666
|
-
const exhaustedConfig = { ...config, events: { handlers: [handler], maxAttempts: 3 } };
|
|
667
|
-
await db.create({
|
|
668
|
-
collection: LIFECYCLE_EVENTS_COLLECTION,
|
|
669
|
-
data: {
|
|
670
|
-
id: "evt-exhausted",
|
|
671
|
-
name: "revision.created",
|
|
672
|
-
collection: "posts",
|
|
673
|
-
documentId: "p1",
|
|
674
|
-
status: "failed",
|
|
675
|
-
attempts: 3,
|
|
676
|
-
payload: {},
|
|
677
|
-
occurredAt: new Date().toISOString(),
|
|
678
|
-
},
|
|
679
|
-
});
|
|
680
|
-
expect(await dispatchPendingLifecycleEvents(exhaustedConfig, 50)).toBe(0);
|
|
681
|
-
expect(handler).not.toHaveBeenCalled();
|
|
682
|
-
});
|
|
683
|
-
});
|
|
684
|
-
//# sourceMappingURL=workflow.test.js.map
|