@voxgig/build 4.3.0 → 4.11.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.
Files changed (100) hide show
  1. package/README.md +51 -2
  2. package/build.ts +20 -0
  3. package/dist/api/api_gen.d.ts +7 -0
  4. package/dist/api/api_gen.js +370 -0
  5. package/dist/api/api_gen.js.map +1 -0
  6. package/dist/build.d.ts +16 -1
  7. package/dist/build.js +17 -1
  8. package/dist/build.js.map +1 -1
  9. package/dist/doc/doc_gen.d.ts +8 -0
  10. package/dist/doc/doc_gen.js +397 -0
  11. package/dist/doc/doc_gen.js.map +1 -0
  12. package/dist/env/web/web_gen.js +159 -1
  13. package/dist/env/web/web_gen.js.map +1 -1
  14. package/dist/shape/ent.js +4 -2
  15. package/dist/shape/ent.js.map +1 -1
  16. package/env/web/web_gen.ts +174 -1
  17. package/package.json +6 -9
  18. package/tm/web/backend/env/shared/seed.ts.frag +23 -0
  19. package/tm/web/backend/env/web/api.ts.frag +157 -0
  20. package/tm/web/backend/env/web/web.ts.frag +37 -4
  21. package/tm/web/backend/srv/api/api-srv.ts.frag +4 -0
  22. package/tm/web/backend/srv/api/expose.ts.frag +33 -0
  23. package/tm/web/backend/srv/api/get_info.ts.frag +6 -0
  24. package/tm/web/backend/srv/api/on_ent.ts.frag +124 -0
  25. package/tm/web/backend/srv/auth/apikey_util.ts.frag +10 -0
  26. package/tm/web/backend/srv/auth/auth-srv.ts.frag +1 -0
  27. package/tm/web/backend/srv/auth/change_pass.ts.frag +12 -0
  28. package/tm/web/backend/srv/auth/create_apikey.ts.frag +33 -0
  29. package/tm/web/backend/srv/auth/get_info.ts.frag +1 -0
  30. package/tm/web/backend/srv/auth/list_apikey.ts.frag +15 -0
  31. package/tm/web/backend/srv/auth/load_auth.ts.frag +10 -1
  32. package/tm/web/backend/srv/auth/remind_pass.ts.frag +35 -0
  33. package/tm/web/backend/srv/auth/revoke_apikey.ts.frag +23 -0
  34. package/tm/web/backend/srv/auth/signin_user.ts.frag +12 -2
  35. package/tm/web/backend/srv/auth/signout_user.ts.frag +11 -1
  36. package/tm/web/backend/srv/auth/update_user.ts.frag +15 -0
  37. package/tm/web/backend/srv/auth/user_util.ts.frag +21 -0
  38. package/tm/web/backend/srv/auth/web_change_pass.ts.frag +16 -0
  39. package/tm/web/backend/srv/auth/web_create_apikey.ts.frag +15 -0
  40. package/tm/web/backend/srv/auth/web_list_apikey.ts.frag +11 -0
  41. package/tm/web/backend/srv/auth/web_load_auth.ts.frag +1 -0
  42. package/tm/web/backend/srv/auth/web_remind_pass.ts.frag +7 -0
  43. package/tm/web/backend/srv/auth/web_revoke_apikey.ts.frag +14 -0
  44. package/tm/web/backend/srv/auth/web_signin_user.ts.frag +1 -0
  45. package/tm/web/backend/srv/auth/web_signout_user.ts.frag +11 -3
  46. package/tm/web/backend/srv/auth/web_update_user.ts.frag +11 -0
  47. package/tm/web/backend/srv/ent/access.ts.frag +179 -0
  48. package/tm/web/backend/srv/ent/cmd_list.ts.frag +86 -0
  49. package/tm/web/backend/srv/ent/cmd_load.ts.frag +56 -0
  50. package/tm/web/backend/srv/ent/cmd_remove.ts.frag +60 -0
  51. package/tm/web/backend/srv/ent/cmd_save.ts.frag +141 -0
  52. package/tm/web/backend/srv/ent/ent-srv.ts.frag +4 -0
  53. package/tm/web/backend/srv/ent/get_info.ts.frag +6 -0
  54. package/tm/web/backend/srv/ent/web_cmd_list.ts.frag +17 -0
  55. package/tm/web/backend/srv/ent/web_cmd_load.ts.frag +17 -0
  56. package/tm/web/backend/srv/ent/web_cmd_remove.ts.frag +17 -0
  57. package/tm/web/backend/srv/ent/web_cmd_save.ts.frag +17 -0
  58. package/tm/web/backend/test/unit/env/web/api-router.test.ts.frag +315 -0
  59. package/tm/web/backend/test/unit/env/web/surface.test.ts.frag +109 -0
  60. package/tm/web/backend/test/unit/srv/api/api.setup.ts.frag +162 -0
  61. package/tm/web/backend/test/unit/srv/api/api.test.ts.frag +225 -0
  62. package/tm/web/backend/test/unit/srv/auth/apikey.test.ts.frag +152 -0
  63. package/tm/web/backend/test/unit/srv/auth/auth.setup.ts.frag +40 -0
  64. package/tm/web/backend/test/unit/srv/auth/session.test.ts.frag +189 -0
  65. package/tm/web/backend/test/unit/srv/ent/ent.setup.ts.frag +53 -0
  66. package/tm/web/backend/test/unit/srv/ent/proxy.test.ts.frag +109 -0
  67. package/tm/web/docs/explanation/web-architecture.md.frag +50 -0
  68. package/tm/web/docs/how-to/add-a-custom-entity-view.md.frag +53 -0
  69. package/tm/web/docs/how-to/change-the-theme.md.frag +63 -0
  70. package/tm/web/docs/how-to/customise-the-web-app.md.frag +68 -0
  71. package/tm/web/docs/how-to/use-the-api.md.frag +53 -0
  72. package/tm/web/docs/reference/web-app.md.frag +98 -0
  73. package/tm/web/web/AGENTS.md.frag +52 -0
  74. package/tm/web/web/e2e/smoke.spec.js.frag +13 -11
  75. package/tm/web/web/index.html.frag +3 -0
  76. package/tm/web/web/package.json.frag +4 -1
  77. package/tm/web/web/playwright.config.js.frag +6 -1
  78. package/tm/web/web/src/api.js.frag +92 -0
  79. package/tm/web/web/src/bus.js.frag +46 -23
  80. package/tm/web/web/src/cmp/admin.js.frag +292 -193
  81. package/tm/web/web/src/cmp/admin.md.frag +51 -0
  82. package/tm/web/web/src/cmp/app.js.frag +12 -15
  83. package/tm/web/web/src/cmp/app.md.frag +28 -0
  84. package/tm/web/web/src/cmp/auth.js.frag +69 -41
  85. package/tm/web/web/src/cmp/auth.md.frag +36 -0
  86. package/tm/web/web/src/cmp/public.js.frag +58 -0
  87. package/tm/web/web/src/cmp/public.md.frag +26 -0
  88. package/tm/web/web/src/cmp/settings.js.frag +178 -0
  89. package/tm/web/web/src/cmp/settings.md.frag +30 -0
  90. package/tm/web/web/src/cmp/shell.js.frag +231 -0
  91. package/tm/web/web/src/cmp/shell.md.frag +49 -0
  92. package/tm/web/web/src/cmp/view/custom-view.js.frag +47 -0
  93. package/tm/web/web/src/cmp/view/custom-view.md.frag +31 -0
  94. package/tm/web/web/src/custom.css.frag +8 -0
  95. package/tm/web/web/src/customise.js.frag +30 -0
  96. package/tm/web/web/src/hooks.js.frag +79 -0
  97. package/tm/web/web/src/main.js.frag +17 -4
  98. package/tm/web/web/src/model.js.frag +162 -0
  99. package/tm/web/web/src/style.css.frag +135 -21
  100. package/tm/web/web/src/theme.js.frag +82 -0
@@ -1 +1 @@
1
- {"version":3,"file":"web_gen.js","sourceRoot":"","sources":["../../../env/web/web_gen.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"web_gen.js","sourceRoot":"","sources":["../../../env/web/web_gen.ts"],"names":[],"mappings":";AAAA,+CAA+C;;;;;;AAE/C,2EAA2E;AAC3E,2EAA2E;AAC3E,yDAAyD;AACzD,EAAE;AACF,0EAA0E;AAC1E,0EAA0E;AAC1E,yEAAyE;AACzE,oEAAoE;AACpE,EAAE;AACF,uEAAuE;AACvE,gEAAgE;AAChE,8BAA8B;AAE9B,4CAAmB;AACnB,gDAAuB;AAEvB,uCAAuC;AAEvC,2CAAgD;AAEhD,iDAAiE;AAGjE,2EAA2E;AAC3E,qEAAqE;AACrE,gCAAgC;AAChC,MAAM,SAAS,GAAoC;IACjD,4EAA4E;IAC5E,wEAAwE;IACxE,oEAAoE;IACpE,0EAA0E;IAC1E,EAAE,IAAI,EAAE,uBAAuB,EAAE,GAAG,EAAE,kBAAkB,EAAE;IAC1D,EAAE,IAAI,EAAE,qBAAqB,EAAE,GAAG,EAAE,gBAAgB,EAAE;IACtD,EAAE,IAAI,EAAE,+BAA+B,EAAE,GAAG,EAAE,0BAA0B,EAAE;IAC1E,EAAE,IAAI,EAAE,sBAAsB,EAAE,GAAG,EAAE,iBAAiB,EAAE;IACxD,EAAE,IAAI,EAAE,qBAAqB,EAAE,GAAG,EAAE,gBAAgB,EAAE;IACtD,EAAE,IAAI,EAAE,uBAAuB,EAAE,GAAG,EAAE,kBAAkB,EAAE;IAC1D,EAAE,IAAI,EAAE,qBAAqB,EAAE,GAAG,EAAE,gBAAgB,EAAE;IACtD,EAAE,IAAI,EAAE,uBAAuB,EAAE,GAAG,EAAE,kBAAkB,EAAE;IAC1D,EAAE,IAAI,EAAE,uBAAuB,EAAE,GAAG,EAAE,kBAAkB,EAAE;IAC1D,EAAE,IAAI,EAAE,2BAA2B,EAAE,GAAG,EAAE,sBAAsB,EAAE;IAClE,EAAE,IAAI,EAAE,yBAAyB,EAAE,GAAG,EAAE,oBAAoB,EAAE;IAC9D,EAAE,IAAI,EAAE,wBAAwB,EAAE,GAAG,EAAE,mBAAmB,EAAE;IAC5D,EAAE,IAAI,EAAE,yBAAyB,EAAE,GAAG,EAAE,oBAAoB,EAAE;IAC9D,EAAE,IAAI,EAAE,4BAA4B,EAAE,GAAG,EAAE,uBAAuB,EAAE;IACpE,EAAE,IAAI,EAAE,0BAA0B,EAAE,GAAG,EAAE,qBAAqB,EAAE;IAChE,EAAE,IAAI,EAAE,2BAA2B,EAAE,GAAG,EAAE,sBAAsB,EAAE;IAClE,EAAE,IAAI,EAAE,2BAA2B,EAAE,GAAG,EAAE,sBAAsB,EAAE;IAClE,EAAE,IAAI,EAAE,8BAA8B,EAAE,GAAG,EAAE,yBAAyB,EAAE;IAExE,qEAAqE;IACrE,sCAAsC;IACtC,EAAE,IAAI,EAAE,yBAAyB,EAAE,GAAG,EAAE,oBAAoB,EAAE;IAC9D,EAAE,IAAI,EAAE,4BAA4B,EAAE,GAAG,EAAE,uBAAuB,EAAE;IACpE,EAAE,IAAI,EAAE,0BAA0B,EAAE,GAAG,EAAE,qBAAqB,EAAE;IAChE,EAAE,IAAI,EAAE,2BAA2B,EAAE,GAAG,EAAE,sBAAsB,EAAE;IAClE,EAAE,IAAI,EAAE,2BAA2B,EAAE,GAAG,EAAE,sBAAsB,EAAE;IAClE,EAAE,IAAI,EAAE,8BAA8B,EAAE,GAAG,EAAE,yBAAyB,EAAE;IACxE,EAAE,IAAI,EAAE,4BAA4B,EAAE,GAAG,EAAE,uBAAuB,EAAE;IACpE,EAAE,IAAI,EAAE,oBAAoB,EAAE,GAAG,EAAE,eAAe,EAAE;IAEpD,oEAAoE;IACpE,sEAAsE;IACtE,yDAAyD;IACzD,EAAE,IAAI,EAAE,2CAA2C,EAAE,GAAG,EAAE,sCAAsC,EAAE;IAClG,EAAE,IAAI,EAAE,iCAAiC,EAAE,GAAG,EAAE,4BAA4B,EAAE;IAC9E,EAAE,IAAI,EAAE,sCAAsC,EAAE,GAAG,EAAE,iCAAiC,EAAE;IACxF,EAAE,IAAI,EAAE,8CAA8C,EAAE,GAAG,EAAE,yCAAyC,EAAE;IACxG,EAAE,IAAI,EAAE,gCAAgC,EAAE,GAAG,EAAE,2BAA2B,EAAE;IAC5E,EAAE,IAAI,EAAE,2CAA2C,EAAE,GAAG,EAAE,sCAAsC,EAAE;IAElG,sDAAsD;IACtD,EAAE,IAAI,EAAE,6BAA6B,EAAE,GAAG,EAAE,4BAA4B,EAAE;IAC1E,EAAE,IAAI,EAAE,iCAAiC,EAAE,GAAG,EAAE,gCAAgC,EAAE;IAClF,EAAE,IAAI,EAAE,mCAAmC,EAAE,GAAG,EAAE,kCAAkC,EAAE;IACtF,EAAE,IAAI,EAAE,oCAAoC,EAAE,GAAG,EAAE,mCAAmC,EAAE;IACxF,EAAE,IAAI,EAAE,mCAAmC,EAAE,GAAG,EAAE,kCAAkC,EAAE;IACtF,EAAE,IAAI,EAAE,sCAAsC,EAAE,GAAG,EAAE,qCAAqC,EAAE;IAC5F,EAAE,IAAI,EAAE,uCAAuC,EAAE,GAAG,EAAE,sCAAsC,EAAE;IAC9F,EAAE,IAAI,EAAE,oCAAoC,EAAE,GAAG,EAAE,mCAAmC,EAAE;IACxF,EAAE,IAAI,EAAE,0CAA0C,EAAE,GAAG,EAAE,yCAAyC,EAAE;IACpG,EAAE,IAAI,EAAE,2CAA2C,EAAE,GAAG,EAAE,0CAA0C,EAAE;IACtG,EAAE,IAAI,EAAE,wCAAwC,EAAE,GAAG,EAAE,uCAAuC,EAAE;IAEhG,qCAAqC;IACrC,EAAE,IAAI,EAAE,sCAAsC,EAAE,GAAG,EAAE,qCAAqC,EAAE;IAC5F,EAAE,IAAI,EAAE,0CAA0C,EAAE,GAAG,EAAE,yCAAyC,EAAE;IACpG,EAAE,IAAI,EAAE,sCAAsC,EAAE,GAAG,EAAE,qCAAqC,EAAE;IAC5F,EAAE,IAAI,EAAE,0CAA0C,EAAE,GAAG,EAAE,yCAAyC,EAAE;IACpG,EAAE,IAAI,EAAE,sCAAsC,EAAE,GAAG,EAAE,qCAAqC,EAAE;IAC5F,EAAE,IAAI,EAAE,0CAA0C,EAAE,GAAG,EAAE,yCAAyC,EAAE;IAEpG,yDAAyD;IACzD,EAAE,IAAI,EAAE,sCAAsC,EAAE,GAAG,EAAE,qCAAqC,EAAE;IAC5F,EAAE,IAAI,EAAE,wCAAwC,EAAE,GAAG,EAAE,uCAAuC,EAAE;IAChG,EAAE,IAAI,EAAE,sCAAsC,EAAE,GAAG,EAAE,qCAAqC,EAAE;IAC5F,EAAE,IAAI,EAAE,wCAAwC,EAAE,GAAG,EAAE,uCAAuC,EAAE;IAChG,EAAE,IAAI,EAAE,4CAA4C,EAAE,GAAG,EAAE,2CAA2C,EAAE;IACxG,EAAE,IAAI,EAAE,0CAA0C,EAAE,GAAG,EAAE,yCAAyC,EAAE;IACpG,EAAE,IAAI,EAAE,4CAA4C,EAAE,GAAG,EAAE,2CAA2C,EAAE;IAExG,yEAAyE;IACzE,sEAAsE;IACtE,EAAE,IAAI,EAAE,iCAAiC,EAAE,GAAG,EAAE,gCAAgC,EAAE;IAClF,EAAE,IAAI,EAAE,kCAAkC,EAAE,GAAG,EAAE,iCAAiC,EAAE;IACpF,EAAE,IAAI,EAAE,gCAAgC,EAAE,GAAG,EAAE,+BAA+B,EAAE;IAChF,EAAE,IAAI,EAAE,gCAAgC,EAAE,GAAG,EAAE,+BAA+B,EAAE;IAChF,EAAE,IAAI,EAAE,6BAA6B,EAAE,GAAG,EAAE,4BAA4B,EAAE;IAE1E,6DAA6D;IAC7D,sEAAsE;IACtE,mEAAmE;IACnE,8DAA8D;IAC9D,EAAE,IAAI,EAAE,6CAA6C,EAAE,GAAG,EAAE,wCAAwC,EAAE;IACtG,EAAE,IAAI,EAAE,4CAA4C,EAAE,GAAG,EAAE,uCAAuC,EAAE;IACpG,EAAE,IAAI,EAAE,+CAA+C,EAAE,GAAG,EAAE,0CAA0C,EAAE;IAC1G,EAAE,IAAI,EAAE,gDAAgD,EAAE,GAAG,EAAE,2CAA2C,EAAE;IAC5G,EAAE,IAAI,EAAE,iDAAiD,EAAE,GAAG,EAAE,4CAA4C,EAAE;IAC9G,EAAE,IAAI,EAAE,mDAAmD,EAAE,GAAG,EAAE,8CAA8C,EAAE;IAClH,EAAE,IAAI,EAAE,gDAAgD,EAAE,GAAG,EAAE,2CAA2C,EAAE;IAC5G,EAAE,IAAI,EAAE,6CAA6C,EAAE,GAAG,EAAE,wCAAwC,EAAE;IACtG,EAAE,IAAI,EAAE,8CAA8C,EAAE,GAAG,EAAE,yCAAyC,EAAE;IAExG,kEAAkE;IAClE,EAAE,IAAI,EAAE,iCAAiC,EAAE,GAAG,EAAE,gCAAgC,EAAE;IAClF,EAAE,IAAI,EAAE,gCAAgC,EAAE,GAAG,EAAE,+BAA+B,EAAE;IAChF,EAAE,IAAI,EAAE,kCAAkC,EAAE,GAAG,EAAE,iCAAiC,EAAE;IACpF,EAAE,IAAI,EAAE,kCAAkC,EAAE,GAAG,EAAE,iCAAiC,EAAE;IACpF,EAAE,IAAI,EAAE,kCAAkC,EAAE,GAAG,EAAE,iCAAiC,EAAE;IACpF,EAAE,IAAI,EAAE,kCAAkC,EAAE,GAAG,EAAE,iCAAiC,EAAE;IACpF,EAAE,IAAI,EAAE,oCAAoC,EAAE,GAAG,EAAE,mCAAmC,EAAE;IAExF,sEAAsE;IACtE,uEAAuE;IACvE,mBAAmB;IACnB,EAAE,IAAI,EAAE,sCAAsC,EAAE,GAAG,EAAE,qCAAqC,EAAE;IAC5F,EAAE,IAAI,EAAE,sCAAsC,EAAE,GAAG,EAAE,qCAAqC,EAAE;IAC5F,EAAE,IAAI,EAAE,sCAAsC,EAAE,GAAG,EAAE,qCAAqC,EAAE;IAC5F,EAAE,IAAI,EAAE,wCAAwC,EAAE,GAAG,EAAE,uCAAuC,EAAE;CACjG,CAAA;AAgJC,8BAAS;AA7IX,uEAAuE;AACvE,MAAM,aAAa,GAAG;IACpB,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,mBAAmB,EAAE,QAAQ,EAAE,eAAe,EAAE;IAChF,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,aAAa,EAAE;CAC3E,CAAA;AA0IC,sCAAa;AAvIf,wEAAwE;AACxE,yEAAyE;AACzE,uDAAuD;AACvD,MAAM,OAAO,GAAG,KAAK,EAAE,KAAU,EAAE,IAKlC,EAAE,EAAE;IAEH,MAAM,IAAI,GAAG,IAAA,oBAAa,EAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IAChD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,EAAE,CAAA;IAE7B,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,IAAI,aAAa,CAAA;IAC3C,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,CAAA;IACzC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI;QACtB,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,IAAI,CAAA;IAEhE,MAAM,KAAK,GAAwB;QACjC,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,IAAI,EAAE,IAAA,eAAQ,EAAC,IAAI,CAAC,IAAI,CAAC;QACzB,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACrC,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,CAAC,IAAI,GAAG,EAAE,CAAC;QACtC,SAAS,EAAE,IAAI,CAAC,KAAK;QACrB,YAAY,EAAE,IAAI,CAAC,QAAQ;KAC5B,CAAA;IAED,MAAM,OAAO,GAAa,EAAE,CAAA;IAC5B,MAAM,OAAO,GAAa,EAAE,CAAA;IAE5B,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;QAC3B,MAAM,MAAM,GAAG,IAAA,yBAAc,EAAC,EAAE,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QAC5C,MAAM,IAAI,GAAG,cAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QAEzC,IAAI,YAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;YACvC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;YACpB,SAAQ;QACV,CAAC;QAED,MAAM,OAAO,GAAG,IAAA,yBAAc,EAAC,IAAA,uBAAY,EAAC,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAA;QACzE,YAAE,CAAC,SAAS,CAAC,cAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QACrD,YAAE,CAAC,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAC/B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;IACtB,CAAC;IAED,2EAA2E;IAC3E,4EAA4E;IAC5E,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,CAAA;IACrC,MAAM,OAAO,GAAoD,EAAE,CAAA;IACnE,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;YAC/C,MAAM,GAAG,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAA;YAChC,IAAI,GAAG,IAAI,GAAG,CAAC,EAAE,IAAI,QAAQ,KAAK,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;gBAC9C,OAAO,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,GAAG,GAAG,GAAG,IAAI,EAAE,CAAC,CAAA;YACxD,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,OAAO,EAAE,CAAC;QACxB,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,EAAE;YACtC,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,GAAG,EAAE,UAAU,GAAG,CAAC,CAAC,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC,IAAI;YACvC,SAAS,EAAE,QAAQ,GAAG,IAAA,eAAQ,EAAC,CAAC,CAAC,IAAI,CAAC,GAAG,IAAA,eAAQ,EAAC,CAAC,CAAC,IAAI,CAAC;YACzD,KAAK,EAAE,IAAA,eAAQ,EAAC,CAAC,CAAC,IAAI,CAAC;SACxB,CAAC,CAAA;QACF,gEAAgE;QAChE,KAAK,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC;YAC/B,MAAM,MAAM,GAAG,mBAAmB,GAAG,CAAC,CAAC,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC,IAAI,GAAG,GAAG,GAAG,GAAG,CAAA;YACtE,MAAM,IAAI,GAAG,cAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;YACzC,IAAI,YAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACvC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;gBACpB,SAAQ;YACV,CAAC;YACD,MAAM,OAAO,GAAG,IAAA,yBAAc,EAC5B,IAAA,uBAAY,EAAC,+BAA+B,GAAG,GAAG,GAAG,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,CAAA;YACrF,YAAE,CAAC,SAAS,CAAC,cAAI,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YACrD,YAAE,CAAC,aAAa,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;YAC/B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAA;QACtB,CAAC;IACH,CAAC;IAED,2EAA2E;IAC3E,4EAA4E;IAC5E,MAAM,QAAQ,GAAG,kBAAkB,CAAA;IACnC,MAAM,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAA;IAChD,MAAM,SAAS,GACb,iFAAiF;QACjF,gFAAgF;QAChF,yDAAyD;QACzD,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,sBAAsB,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,IAAI,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACpF,MAAM,aAAa,GAAG,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,YAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC1F,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,YAAE,CAAC,SAAS,CAAC,cAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;QAClE,YAAE,CAAC,aAAa,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;QACtC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IACxB,CAAC;IAED,wEAAwE;IACxE,8EAA8E;IAC9E,yEAAyE;IACzE,+DAA+D;IAC/D,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAA;IAC5C,IAAI,KAAK,IAAI,KAAK,CAAC,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC;QAC5D,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAA;QACzD,MAAM,SAAS,GAAG,CAAC,MAAW,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE;aAC1D,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACtD,MAAM,MAAM,GAAG,CAAC,iCAAiC,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,GAAG,KAAK,CAAC,CAAA;QACjH,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5C,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;gBACrB,MAAM,CAAC,IAAI,CAAC,yBAAyB,GAAG,IAAI,GAAG,QAAQ,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAA;YACjG,CAAC;QACH,CAAC;QACD,MAAM,SAAS,GACb,sEAAsE;YACtE,8EAA8E;YAC9E,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,IAAI,CAAA;QAC5B,MAAM,SAAS,GAAG,cAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAA;QAC3D,MAAM,aAAa,GAAG,YAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,YAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;QAC1F,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;YAChC,YAAE,CAAC,SAAS,CAAC,cAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;YAClE,YAAE,CAAC,aAAa,CAAC,SAAS,EAAE,SAAS,CAAC,CAAA;YACtC,OAAO,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAA;QACnC,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE,EAAE,CAAA;AAC7D,CAAC,CAAA;AAIC,0BAAO"}
package/dist/shape/ent.js CHANGED
@@ -3,7 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.EntShape = void 0;
4
4
  const gubu_1 = require("gubu");
5
5
  const { Open, Skip } = gubu_1.Gubu;
6
- const EntShape = (0, gubu_1.Gubu)({
6
+ // Open: entities may carry extra attributes beyond the deployment-relevant
7
+ // ones below (e.g. `ux` for frontend view hints), which this generator ignores.
8
+ const EntShape = (0, gubu_1.Gubu)(Open({
7
9
  id: {
8
10
  field: 'id'
9
11
  },
@@ -23,6 +25,6 @@ const EntShape = (0, gubu_1.Gubu)({
23
25
  active: false
24
26
  }),
25
27
  custom: Skip(String),
26
- }, { prefix: 'Entity' });
28
+ }), { prefix: 'Entity' });
27
29
  exports.EntShape = EntShape;
28
30
  //# sourceMappingURL=ent.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ent.js","sourceRoot":"","sources":["../../shape/ent.ts"],"names":[],"mappings":";;;AACA,+BAA2B;AAE3B,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,WAAI,CAAA;AAG3B,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC;IACpB,EAAE,EAAE;QACF,KAAK,EAAE,IAAI;KACZ;IACD,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EACrB,CAAC;IACF,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;IACf,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EACrB,CAAC;IACF,QAAQ,EAAE,IAAI,CAAC;QACb,IAAI,EAAE,EAAE;KACT,CAAC;IACF,MAAM,EAAE,IAAI,CAAC;QACX,MAAM,EAAE,KAAK;QACb,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,EAAE;KACX,CAAC;IACF,KAAK,EAAE,IAAI,CAAC;QACV,MAAM,EAAE,KAAK;KACd,CAAC;IACF,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;CACrB,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAA;AAItB,4BAAQ"}
1
+ {"version":3,"file":"ent.js","sourceRoot":"","sources":["../../shape/ent.ts"],"names":[],"mappings":";;;AACA,+BAA2B;AAE3B,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,WAAI,CAAA;AAG3B,2EAA2E;AAC3E,gFAAgF;AAChF,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,IAAI,CAAC;IACzB,EAAE,EAAE;QACF,KAAK,EAAE,IAAI;KACZ;IACD,KAAK,EAAE,OAAO;IACd,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EACrB,CAAC;IACF,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC;IACf,KAAK,EAAE,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,EACrB,CAAC;IACF,QAAQ,EAAE,IAAI,CAAC;QACb,IAAI,EAAE,EAAE;KACT,CAAC;IACF,MAAM,EAAE,IAAI,CAAC;QACX,MAAM,EAAE,KAAK;QACb,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,EAAE;KACX,CAAC;IACF,KAAK,EAAE,IAAI,CAAC;QACV,MAAM,EAAE,KAAK;KACd,CAAC;IACF,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC;CACrB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAA;AAIvB,4BAAQ"}
@@ -27,21 +27,55 @@ import { loadFragment, renderFragment } from '../lambda/generate'
27
27
  // the project root, which holds backend/ and web/). $$slots$$ in the
28
28
  // output path are rendered too.
29
29
  const WEB_FILES: { frag: string, out: string }[] = [
30
- // Frontend SPA (web/)
30
+ // Frontend SPA (web/) — a model-driven enterprise app: public site + login,
31
+ // an app shell (nav / project selector / user menu / collapsible entity
32
+ // menu), and generic CRUD with relationship navigation. All runtime
33
+ // model-driven (model.js reads /model.json), so it fits any entity graph.
31
34
  { frag: 'web/package.json.frag', out: 'web/package.json' },
32
35
  { frag: 'web/index.html.frag', out: 'web/index.html' },
33
36
  { frag: 'web/playwright.config.js.frag', out: 'web/playwright.config.js' },
34
37
  { frag: 'web/src/main.js.frag', out: 'web/src/main.js' },
35
38
  { frag: 'web/src/bus.js.frag', out: 'web/src/bus.js' },
39
+ { frag: 'web/src/model.js.frag', out: 'web/src/model.js' },
40
+ { frag: 'web/src/api.js.frag', out: 'web/src/api.js' },
41
+ { frag: 'web/src/hooks.js.frag', out: 'web/src/hooks.js' },
42
+ { frag: 'web/src/theme.js.frag', out: 'web/src/theme.js' },
43
+ { frag: 'web/src/customise.js.frag', out: 'web/src/customise.js' },
44
+ { frag: 'web/src/custom.css.frag', out: 'web/src/custom.css' },
36
45
  { frag: 'web/src/style.css.frag', out: 'web/src/style.css' },
37
46
  { frag: 'web/src/cmp/app.js.frag', out: 'web/src/cmp/app.js' },
47
+ { frag: 'web/src/cmp/public.js.frag', out: 'web/src/cmp/public.js' },
38
48
  { frag: 'web/src/cmp/auth.js.frag', out: 'web/src/cmp/auth.js' },
49
+ { frag: 'web/src/cmp/shell.js.frag', out: 'web/src/cmp/shell.js' },
39
50
  { frag: 'web/src/cmp/admin.js.frag', out: 'web/src/cmp/admin.js' },
51
+ { frag: 'web/src/cmp/settings.js.frag', out: 'web/src/cmp/settings.js' },
52
+
53
+ // Per-component doc sidecars (mermaid structure + message diagrams),
54
+ // create-once next to each component.
55
+ { frag: 'web/src/cmp/app.md.frag', out: 'web/src/cmp/app.md' },
56
+ { frag: 'web/src/cmp/public.md.frag', out: 'web/src/cmp/public.md' },
57
+ { frag: 'web/src/cmp/auth.md.frag', out: 'web/src/cmp/auth.md' },
58
+ { frag: 'web/src/cmp/shell.md.frag', out: 'web/src/cmp/shell.md' },
59
+ { frag: 'web/src/cmp/admin.md.frag', out: 'web/src/cmp/admin.md' },
60
+ { frag: 'web/src/cmp/settings.md.frag', out: 'web/src/cmp/settings.md' },
40
61
  { frag: 'web/e2e/smoke.spec.js.frag', out: 'web/e2e/smoke.spec.js' },
62
+ { frag: 'web/AGENTS.md.frag', out: 'web/AGENTS.md' },
63
+
64
+ // Project docs (Diátaxis) for the web app, alongside the scaffold's
65
+ // backend docs (@voxgig/create-system DocsPart). Create-once like all
66
+ // WEB_FILES: the project owns its docs after generation.
67
+ { frag: 'docs/how-to/customise-the-web-app.md.frag', out: 'docs/how-to/customise-the-web-app.md' },
68
+ { frag: 'docs/how-to/use-the-api.md.frag', out: 'docs/how-to/use-the-api.md' },
69
+ { frag: 'docs/how-to/change-the-theme.md.frag', out: 'docs/how-to/change-the-theme.md' },
70
+ { frag: 'docs/how-to/add-a-custom-entity-view.md.frag', out: 'docs/how-to/add-a-custom-entity-view.md' },
71
+ { frag: 'docs/reference/web-app.md.frag', out: 'docs/reference/web-app.md' },
72
+ { frag: 'docs/explanation/web-architecture.md.frag', out: 'docs/explanation/web-architecture.md' },
41
73
 
42
74
  // Backend web runner + auth service (backend/src/...)
43
75
  { frag: 'backend/env/web/web.ts.frag', out: 'backend/src/env/web/web.ts' },
76
+ { frag: 'backend/env/shared/seed.ts.frag', out: 'backend/src/env/shared/seed.ts' },
44
77
  { frag: 'backend/srv/auth/auth-srv.ts.frag', out: 'backend/src/srv/auth/auth-srv.ts' },
78
+ { frag: 'backend/srv/auth/user_util.ts.frag', out: 'backend/src/srv/auth/user_util.ts' },
45
79
  { frag: 'backend/srv/auth/get_info.ts.frag', out: 'backend/src/srv/auth/get_info.ts' },
46
80
  { frag: 'backend/srv/auth/signin_user.ts.frag', out: 'backend/src/srv/auth/signin_user.ts' },
47
81
  { frag: 'backend/srv/auth/signout_user.ts.frag', out: 'backend/src/srv/auth/signout_user.ts' },
@@ -49,6 +83,62 @@ const WEB_FILES: { frag: string, out: string }[] = [
49
83
  { frag: 'backend/srv/auth/web_signin_user.ts.frag', out: 'backend/src/srv/auth/web_signin_user.ts' },
50
84
  { frag: 'backend/srv/auth/web_signout_user.ts.frag', out: 'backend/src/srv/auth/web_signout_user.ts' },
51
85
  { frag: 'backend/srv/auth/web_load_auth.ts.frag', out: 'backend/src/srv/auth/web_load_auth.ts' },
86
+
87
+ // Auth settings/security extensions.
88
+ { frag: 'backend/srv/auth/change_pass.ts.frag', out: 'backend/src/srv/auth/change_pass.ts' },
89
+ { frag: 'backend/srv/auth/web_change_pass.ts.frag', out: 'backend/src/srv/auth/web_change_pass.ts' },
90
+ { frag: 'backend/srv/auth/update_user.ts.frag', out: 'backend/src/srv/auth/update_user.ts' },
91
+ { frag: 'backend/srv/auth/web_update_user.ts.frag', out: 'backend/src/srv/auth/web_update_user.ts' },
92
+ { frag: 'backend/srv/auth/remind_pass.ts.frag', out: 'backend/src/srv/auth/remind_pass.ts' },
93
+ { frag: 'backend/srv/auth/web_remind_pass.ts.frag', out: 'backend/src/srv/auth/web_remind_pass.ts' },
94
+
95
+ // API access keys (Settings & security + REST API auth).
96
+ { frag: 'backend/srv/auth/apikey_util.ts.frag', out: 'backend/src/srv/auth/apikey_util.ts' },
97
+ { frag: 'backend/srv/auth/create_apikey.ts.frag', out: 'backend/src/srv/auth/create_apikey.ts' },
98
+ { frag: 'backend/srv/auth/list_apikey.ts.frag', out: 'backend/src/srv/auth/list_apikey.ts' },
99
+ { frag: 'backend/srv/auth/revoke_apikey.ts.frag', out: 'backend/src/srv/auth/revoke_apikey.ts' },
100
+ { frag: 'backend/srv/auth/web_create_apikey.ts.frag', out: 'backend/src/srv/auth/web_create_apikey.ts' },
101
+ { frag: 'backend/srv/auth/web_list_apikey.ts.frag', out: 'backend/src/srv/auth/web_list_apikey.ts' },
102
+ { frag: 'backend/srv/auth/web_revoke_apikey.ts.frag', out: 'backend/src/srv/auth/web_revoke_apikey.ts' },
103
+
104
+ // The strict-JSON REST API (model main.api): api service + HTTP mapping.
105
+ // valid_gen.ts (request validation shapes) is REGENERATED by api_gen.
106
+ { frag: 'backend/srv/api/api-srv.ts.frag', out: 'backend/src/srv/api/api-srv.ts' },
107
+ { frag: 'backend/srv/api/get_info.ts.frag', out: 'backend/src/srv/api/get_info.ts' },
108
+ { frag: 'backend/srv/api/on_ent.ts.frag', out: 'backend/src/srv/api/on_ent.ts' },
109
+ { frag: 'backend/srv/api/expose.ts.frag', out: 'backend/src/srv/api/expose.ts' },
110
+ { frag: 'backend/env/web/api.ts.frag', out: 'backend/src/env/web/api.ts' },
111
+
112
+ // Unit tests for what EnvWeb generates (node:test). They are
113
+ // MODEL-DRIVEN - the entity under test and its payloads come from the
114
+ // model - so they hold for any entity graph, and they stand in for
115
+ // other services with mock messages rather than booting them.
116
+ { frag: 'backend/test/unit/srv/api/api.setup.ts.frag', out: 'backend/test/unit/srv/api/api.setup.ts' },
117
+ { frag: 'backend/test/unit/srv/api/api.test.ts.frag', out: 'backend/test/unit/srv/api/api.test.ts' },
118
+ { frag: 'backend/test/unit/srv/auth/auth.setup.ts.frag', out: 'backend/test/unit/srv/auth/auth.setup.ts' },
119
+ { frag: 'backend/test/unit/srv/auth/apikey.test.ts.frag', out: 'backend/test/unit/srv/auth/apikey.test.ts' },
120
+ { frag: 'backend/test/unit/srv/auth/session.test.ts.frag', out: 'backend/test/unit/srv/auth/session.test.ts' },
121
+ { frag: 'backend/test/unit/env/web/api-router.test.ts.frag', out: 'backend/test/unit/env/web/api-router.test.ts' },
122
+ { frag: 'backend/test/unit/env/web/surface.test.ts.frag', out: 'backend/test/unit/env/web/surface.test.ts' },
123
+ { frag: 'backend/test/unit/srv/ent/ent.setup.ts.frag', out: 'backend/test/unit/srv/ent/ent.setup.ts' },
124
+ { frag: 'backend/test/unit/srv/ent/proxy.test.ts.frag', out: 'backend/test/unit/srv/ent/proxy.test.ts' },
125
+
126
+ // Generic entity service (parameterised CRUD, membership-scoped).
127
+ { frag: 'backend/srv/ent/ent-srv.ts.frag', out: 'backend/src/srv/ent/ent-srv.ts' },
128
+ { frag: 'backend/srv/ent/access.ts.frag', out: 'backend/src/srv/ent/access.ts' },
129
+ { frag: 'backend/srv/ent/get_info.ts.frag', out: 'backend/src/srv/ent/get_info.ts' },
130
+ { frag: 'backend/srv/ent/cmd_list.ts.frag', out: 'backend/src/srv/ent/cmd_list.ts' },
131
+ { frag: 'backend/srv/ent/cmd_load.ts.frag', out: 'backend/src/srv/ent/cmd_load.ts' },
132
+ { frag: 'backend/srv/ent/cmd_save.ts.frag', out: 'backend/src/srv/ent/cmd_save.ts' },
133
+ { frag: 'backend/srv/ent/cmd_remove.ts.frag', out: 'backend/src/srv/ent/cmd_remove.ts' },
134
+
135
+ // Browser proxies: aim:web,on:ent,cmd:* -> aim:ent,cmd:* (the gateway
136
+ // accepts only aim:web, so the entity service is never posted directly
137
+ // from a browser).
138
+ { frag: 'backend/srv/ent/web_cmd_list.ts.frag', out: 'backend/src/srv/ent/web_cmd_list.ts' },
139
+ { frag: 'backend/srv/ent/web_cmd_load.ts.frag', out: 'backend/src/srv/ent/web_cmd_load.ts' },
140
+ { frag: 'backend/srv/ent/web_cmd_save.ts.frag', out: 'backend/src/srv/ent/web_cmd_save.ts' },
141
+ { frag: 'backend/srv/ent/web_cmd_remove.ts.frag', out: 'backend/src/srv/ent/web_cmd_remove.ts' },
52
142
  ]
53
143
 
54
144
 
@@ -104,6 +194,89 @@ const web_gen = async (model: any, spec: {
104
194
  created.push(outrel)
105
195
  }
106
196
 
197
+ // Custom entity views: for every entity declaring `ux:{ view: 'custom' }`,
198
+ // generate a hand-coded view component (CREATE-ONCE, so edits survive) plus
199
+ // an always-regenerated index (views.js) that imports them.
200
+ const entzones = model.main.ent || {}
201
+ const customs: { zone: string, name: string, canon: string }[] = []
202
+ for (const zone of Object.keys(entzones)) {
203
+ for (const name of Object.keys(entzones[zone])) {
204
+ const def = entzones[zone][name]
205
+ if (def && def.ux && 'custom' === def.ux.view) {
206
+ customs.push({ zone, name, canon: zone + '/' + name })
207
+ }
208
+ }
209
+ }
210
+
211
+ for (const c of customs) {
212
+ const vslots = Object.assign({}, slots, {
213
+ canon: c.canon,
214
+ zone: c.zone,
215
+ name: c.name,
216
+ tag: 'vg-view-' + c.zone + '-' + c.name,
217
+ className: 'VgView' + camelify(c.zone) + camelify(c.name),
218
+ Label: camelify(c.name),
219
+ })
220
+ // The component starter plus its doc sidecar, both create-once.
221
+ for (const ext of ['js', 'md']) {
222
+ const outrel = 'web/src/cmp/view/' + c.zone + '_' + c.name + '.' + ext
223
+ const dest = Path.join(spec.root, outrel)
224
+ if (Fs.existsSync(dest) && !spec.force) {
225
+ skipped.push(outrel)
226
+ continue
227
+ }
228
+ const content = renderFragment(
229
+ loadFragment('web/src/cmp/view/custom-view.' + ext + '.frag', spec, 'web'), vslots)
230
+ Fs.mkdirSync(Path.dirname(dest), { recursive: true })
231
+ Fs.writeFileSync(dest, content)
232
+ created.push(outrel)
233
+ }
234
+ }
235
+
236
+ // views.js — the generated index of custom views. Regenerated whenever the
237
+ // set of custom views changes; a no-op run leaves it (and `created`) alone.
238
+ const viewsRel = 'web/src/views.js'
239
+ const viewsPath = Path.join(spec.root, viewsRel)
240
+ const viewsBody =
241
+ '// AUTO-GENERATED: imports every custom entity view (model entities declaring\n' +
242
+ '// ux:{view:\'custom\'}). Regenerated from the model — do not edit. The view\n' +
243
+ '// files it imports are hand-coded and create-once.\n\n' +
244
+ customs.map((c) => `import './cmp/view/${c.zone}_${c.name}.js'`).join('\n') + '\n'
245
+ const existingViews = Fs.existsSync(viewsPath) ? Fs.readFileSync(viewsPath, 'utf8') : null
246
+ if (existingViews !== viewsBody) {
247
+ Fs.mkdirSync(Path.join(spec.root, 'web/src'), { recursive: true })
248
+ Fs.writeFileSync(viewsPath, viewsBody)
249
+ created.push(viewsRel)
250
+ }
251
+
252
+ // theme.css — the design theme from the model (main.theme). Each mode's
253
+ // tokens become CSS variables under :root[data-theme-mode="<mode>"], with the
254
+ // default mode also on plain :root. Regenerated from the model; override
255
+ // tokens in custom.css and add modes via the theme:modes hook.
256
+ const theme = model.main && model.main.theme
257
+ if (theme && theme.modes && Object.keys(theme.modes).length) {
258
+ const defMode = theme.mode || Object.keys(theme.modes)[0]
259
+ const tokensCss = (tokens: any) => Object.keys(tokens).sort()
260
+ .map((k) => ` --vg-${k}: ${tokens[k]};`).join('\n')
261
+ const blocks = [':root,\n:root[data-theme-mode="' + defMode + '"] {\n' + tokensCss(theme.modes[defMode]) + '\n}']
262
+ for (const mode of Object.keys(theme.modes)) {
263
+ if (mode !== defMode) {
264
+ blocks.push(':root[data-theme-mode="' + mode + '"] {\n' + tokensCss(theme.modes[mode]) + '\n}')
265
+ }
266
+ }
267
+ const themeBody =
268
+ '/* AUTO-GENERATED from the model theme (main.theme) — do not edit.\n' +
269
+ ' Override tokens in custom.css; add modes via the theme:modes hook. */\n\n' +
270
+ blocks.join('\n\n') + '\n'
271
+ const themePath = Path.join(spec.root, 'web/src/theme.css')
272
+ const existingTheme = Fs.existsSync(themePath) ? Fs.readFileSync(themePath, 'utf8') : null
273
+ if (existingTheme !== themeBody) {
274
+ Fs.mkdirSync(Path.join(spec.root, 'web/src'), { recursive: true })
275
+ Fs.writeFileSync(themePath, themeBody)
276
+ created.push('web/src/theme.css')
277
+ }
278
+ }
279
+
107
280
  return { created: created.sort(), skipped: skipped.sort() }
108
281
  }
109
282
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@voxgig/build",
3
- "version": "4.3.0",
3
+ "version": "4.11.0",
4
4
  "main": "dist/build.js",
5
5
  "type": "commonjs",
6
6
  "types": "dist/build.d.ts",
@@ -19,12 +19,12 @@
19
19
  },
20
20
  "scripts": {
21
21
  "model-s01": "voxgig-model test/s01/model/model.aontu",
22
- "test": "jest --coverage",
23
- "test-some": "jest -t",
24
- "test-watch": "jest --coverage --watchAll",
22
+ "test": "node --enable-source-maps --experimental-test-coverage --test-coverage-exclude='dist-test/test/**' --test-coverage-lines=95 --test-coverage-functions=85 --test-coverage-branches=78 --test-reporter=spec --test-reporter-destination=stdout --test-reporter=lcov --test-reporter-destination=coverage.lcov --test \"dist-test/test/**/*.test.js\"",
23
+ "test-some": "node --enable-source-maps --test --test-name-pattern=\"$npm_config_pattern\" \"dist-test/test/**/*.test.js\"",
24
+ "test-watch": "node --enable-source-maps --test --watch \"dist-test/test/**/*.test.js\"",
25
25
  "watch": "tsc -w -d",
26
- "build": "tsc -d",
27
- "clean": "rm -rf node_modules yarn.lock package-lock.json",
26
+ "build": "tsc -d && tsc -p test && cp -r test/fixture test/richmodel.js dist-test/test/",
27
+ "clean": "rm -rf dist dist-test node_modules yarn.lock package-lock.json",
28
28
  "reset": "npm run clean && npm i && npm run build && npm test",
29
29
  "repo-tag": "REPO_VERSION=`node -e \"console.log(require('./package').version)\"` && echo TAG: v$REPO_VERSION && git commit -a -m v$REPO_VERSION && git push && git tag v$REPO_VERSION && git push --tags;",
30
30
  "repo-publish": "npm run clean && npm i && npm run repo-publish-quick",
@@ -53,14 +53,11 @@
53
53
  "js-yaml": "^4.1.0"
54
54
  },
55
55
  "devDependencies": {
56
- "@types/jest": "^29.5.12",
57
56
  "@types/js-yaml": "^4.0.9",
58
57
  "@types/node": "26.1.2",
59
58
  "@voxgig/model": "^9.2.4",
60
59
  "@voxgig/system": "^1.2.0",
61
60
  "esbuild": "^0.23.0",
62
- "jest": "^29.7.0",
63
- "ts-jest": "^29.2.2",
64
61
  "typescript": "^5.5.3"
65
62
  }
66
63
  }
@@ -0,0 +1,23 @@
1
+ // Demo/seed data for local development. The web runner already seeds the
2
+ // predefined users; add your DOMAIN seed data here (projects, lists, items,
3
+ // ...). Runs once per boot against the in-memory store. Create-once —
4
+ // customise freely.
5
+
6
+ export async function seedDemo(
7
+ seneca: any,
8
+ usersByEmail: Record<string, any>,
9
+ ): Promise<void> {
10
+ // Nothing seeded by default. Example (uncomment and adapt to your model):
11
+ //
12
+ // const owner = Object.values(usersByEmail)[0]
13
+ // if (!owner) return
14
+ // const existing = await seneca.entity('proj/project').list$({})
15
+ // if (existing.length > 0) return
16
+ // const now = Date.now()
17
+ // const p = await seneca.entity('proj/project')
18
+ // .data$({ name: 'Example', owner_id: owner.id, t_c: now, t_m: now }).save$()
19
+ // await seneca.entity('proj/member')
20
+ // .data$({ project_id: p.id, user_id: owner.id, role: 'owner',
21
+ // owner_id: owner.id, t_c: now, t_m: now }).save$()
22
+ }
23
+
@@ -0,0 +1,157 @@
1
+ // The strict-JSON REST API endpoint (model main.api). Maps
2
+ //
3
+ // GET <prefix>/<version>/<zone>/<name> -> list (query -> q)
4
+ // GET <prefix>/<version>/<zone>/<name>/<id> -> load
5
+ // POST <prefix>/<version>/<zone>/<name> -> create
6
+ // PUT <prefix>/<version>/<zone>/<name>/<id> -> update
7
+ // DELETE <prefix>/<version>/<zone>/<name>/<id> -> remove
8
+ //
9
+ // onto aim:api,on:ent messages (src/srv/api/on_ent.ts). Uniform semantic
10
+ // paths and result shapes so an SDK can be generated (sdkgen); the
11
+ // OpenAPI spec is generated from the model (gen/api/openapi.json).
12
+ //
13
+ // Authentication: API access keys - Authorization: Bearer vk_... - looked
14
+ // up by sha-256 hash against sys/apikey and resolved to the owning user,
15
+ // which becomes the request principal (so project-membership access
16
+ // applies exactly as in the web app).
17
+
18
+ import Crypto from 'node:crypto'
19
+ import Path from 'node:path'
20
+
21
+ // Result 'why' -> HTTP status.
22
+ const STATUS: Record<string, number> = {
23
+ 'not-authenticated': 401,
24
+ 'forbidden': 403,
25
+ 'unknown-entity': 404,
26
+ 'not-found': 404,
27
+ 'unknown-op': 405,
28
+ 'read-only': 405,
29
+ 'invalid-data': 400,
30
+ 'project-required': 400,
31
+ 'invalid-args': 400,
32
+ }
33
+
34
+ export function apiHandler(seneca: any, model: any) {
35
+ const api = (model.main && model.main.api) || {}
36
+ const version = api.version || 'v1'
37
+
38
+ return async function handle(req: any, res: any) {
39
+ res.type('application/json')
40
+
41
+ try {
42
+ // The generated OpenAPI spec (unauthenticated; from gen/api/).
43
+ if ('GET' === req.method && '/openapi.json' === req.path) {
44
+ return res.sendFile(Path.join(
45
+ __dirname, '..', '..', '..', 'gen', 'api', 'openapi.json'))
46
+ }
47
+ // The same spec as YAML - the format most codegen/SDK tools want.
48
+ if ('GET' === req.method && '/openapi.yaml' === req.path) {
49
+ res.type('application/yaml')
50
+ return res.sendFile(Path.join(
51
+ __dirname, '..', '..', '..', 'gen', 'api', 'openapi.yaml'))
52
+ }
53
+
54
+ // Path (relative to the mount prefix): /<version>/<zone>/<name>[/<id>]
55
+ const parts = String(req.path || '').split('/').filter((p: string) => '' !== p)
56
+ if (version !== parts[0] || parts.length < 3 || 4 < parts.length) {
57
+ return fail(res, 404, 'not-found', 'unknown route')
58
+ }
59
+ const ent = parts[1] + '/' + parts[2]
60
+ const id = parts[3]
61
+
62
+ const user = await authenticate(seneca, req)
63
+ if (null == user) {
64
+ return fail(res, 401, 'not-authenticated',
65
+ 'provide a valid API key: Authorization: Bearer <key>')
66
+ }
67
+
68
+ const method = String(req.method || 'GET').toUpperCase()
69
+ let op: string | null = null
70
+ let body = undefined
71
+
72
+ if ('GET' === method) {
73
+ op = null == id ? 'list' : 'load'
74
+ }
75
+ else if ('POST' === method && null == id) {
76
+ op = 'create'
77
+ body = req.body
78
+ }
79
+ else if ('PUT' === method && null != id) {
80
+ op = 'update'
81
+ body = req.body
82
+ }
83
+ else if ('DELETE' === method && null != id) {
84
+ op = 'remove'
85
+ }
86
+ if (null == op) {
87
+ return fail(res, 405, 'unknown-op', 'method not supported on this path')
88
+ }
89
+
90
+ const out = await seneca.post('aim:api,on:ent', {
91
+ op,
92
+ ent,
93
+ id,
94
+ data: body,
95
+ q: 'list' === op ? Object.assign({}, req.query) : undefined,
96
+ custom$: { principal: { user } },
97
+ })
98
+
99
+ if (!out.ok) {
100
+ return fail(res, STATUS[out.why] || 500, out.why || 'failed',
101
+ out.message, out.details)
102
+ }
103
+
104
+ const status = 'create' === op ? 201 : 200
105
+ const result: any = {}
106
+ if (null != out.items) {
107
+ result.items = out.items
108
+ }
109
+ if (null != out.item) {
110
+ result.item = out.item
111
+ }
112
+ if ('remove' === op) {
113
+ result.ok = true
114
+ result.id = id
115
+ }
116
+ return res.status(status).send(result)
117
+ }
118
+ catch (e: any) {
119
+ return fail(res, 500, 'error', 'internal error')
120
+ }
121
+ }
122
+ }
123
+
124
+ function fail(res: any, status: number, code: string, message?: string, details?: any) {
125
+ const error: any = { code }
126
+ if (message) {
127
+ error.message = message
128
+ }
129
+ if (details) {
130
+ error.details = details
131
+ }
132
+ return res.status(status).send({ error })
133
+ }
134
+
135
+ // Resolve the Bearer API key to its owning user (the principal), or null.
136
+ async function authenticate(seneca: any, req: any): Promise<any> {
137
+ const header = String(req.headers['authorization'] || '')
138
+ const m = header.match(/^Bearer\s+(\S+)$/i)
139
+ if (null == m) {
140
+ return null
141
+ }
142
+ const hash = Crypto.createHash('sha256').update(m[1]).digest('hex')
143
+ const keys = await seneca.entity('sys/apikey').list$({ hash })
144
+ const key = keys.find((k: any) => !k.revoked)
145
+ if (null == key) {
146
+ return null
147
+ }
148
+ const user = await seneca.entity('sys/user').load$(key.user_id)
149
+ if (null == user) {
150
+ return null
151
+ }
152
+ const d = user.data$(false)
153
+ delete d.pass
154
+ delete d.salt
155
+ return d
156
+ }
157
+
@@ -18,6 +18,8 @@ import Seneca from 'seneca'
18
18
  import { Local } from '@voxgig/system'
19
19
 
20
20
  import { basic, base } from '../shared/basic'
21
+ import { seedDemo } from '../shared/seed'
22
+ import { apiHandler } from './api'
21
23
 
22
24
  import Pkg from '../../../package.json'
23
25
  import Model from '../../../model/model.json'
@@ -48,12 +50,25 @@ async function run() {
48
50
 
49
51
  basic(seneca)
50
52
 
53
+ // Dev REPL (@seneca/repl): poke the running system with messages -
54
+ // npx seneca-repl telnet://localhost:<port.repl>
55
+ // Disable with REPL=false; override the port with REPL_PORT.
56
+ if ('false' !== process.env.REPL) {
57
+ seneca.use('repl', {
58
+ port: parseInt(process.env.REPL_PORT || '', 10) ||
59
+ (Model as any).main.conf.port.repl,
60
+ })
61
+ }
62
+
51
63
  seneca
52
64
  .use('gateway', {
53
- // Allow auth + every service the model declares (aim:<srv>).
54
- allow: Object.keys((Model as any).main.srv).reduce(
55
- (a: any, n: string) => (a['aim:' + n] = true, a),
56
- { 'aim:req,on:auth': true }),
65
+ // THE BROWSER SURFACE. Only aim:web is reachable from a browser:
66
+ // every message the SPA may send is declared in the model as an
67
+ // aim:web PROXY that forwards to the real service message. Service
68
+ // namespaces (aim:auth, aim:ent, ...) stay internal, so a browser
69
+ // cannot post one directly. API-key clients have their own proxy
70
+ // layer (aim:api behind the REST router, see ./api.ts).
71
+ allow: { 'aim:web': true },
57
72
  })
58
73
  .use('gateway-express', {
59
74
  // gateway-express sets/clears the auth cookie when a result carries
@@ -89,13 +104,21 @@ async function run() {
89
104
  await seneca.ready()
90
105
 
91
106
  // Seed the predefined users (idempotent per boot: in-memory store).
107
+ const usersByEmail: Record<string, any> = {}
92
108
  for (const u of USERS) {
93
109
  const res = await seneca.post('sys:user,register:user', u)
94
110
  if (!res.ok) {
95
111
  console.log('SEED-USER-FAILED', u.email, res.why)
96
112
  }
113
+ const got = await seneca.post('sys:user,get:user', { email: u.email })
114
+ if (got.ok && got.user) {
115
+ usersByEmail[u.email] = got.user
116
+ }
97
117
  }
98
118
 
119
+ // Seed demo projects/todolists/items (collaborative, one shared project).
120
+ await seedDemo(seneca, usersByEmail)
121
+
99
122
  const app = Express()
100
123
  // web/ is a sibling of backend/; from dist/env/local go up to the
101
124
  // project root, then web/dist.
@@ -106,6 +129,15 @@ async function run() {
106
129
  .use(Express.json())
107
130
  .use(new (CookieParser as any)())
108
131
  .post('/seneca', seneca.export('gateway-express/handler'))
132
+
133
+ // The strict-JSON REST API (model main.api), if active. Mounted with
134
+ // use() so req.path inside the handler is relative to the prefix.
135
+ const apiconf = (Model as any).main.api
136
+ if (apiconf && false !== apiconf.active) {
137
+ app.use(apiconf.prefix || '/api', apiHandler(seneca, Model))
138
+ }
139
+
140
+ app
109
141
  .get('/model.json',
110
142
  (_req: any, res: any) => res.sendFile(
111
143
  Path.join(__dirname, '..', '..', '..', 'model', 'model.json')))
@@ -118,3 +150,4 @@ async function run() {
118
150
  version: Pkg.version,
119
151
  })
120
152
  }
153
+
@@ -0,0 +1,4 @@
1
+ import { MakeSrv } from '@voxgig/system'
2
+
3
+ module.exports = MakeSrv('api', require)
4
+
@@ -0,0 +1,33 @@
1
+ // Which entities the REST API exposes (model main.api). Application
2
+ // entities are exposed by default; the sys zone never is; per-entity
3
+ // overrides live under main.api.ent.
4
+ export function exposedCanon(model: any, canon: string): boolean {
5
+ const [zone, name] = String(canon).split('/')
6
+ const def = model && model.main && model.main.ent &&
7
+ model.main.ent[zone] && model.main.ent[zone][name]
8
+ if (null == def || 'sys' === zone) {
9
+ return false
10
+ }
11
+ const api = (model.main.api || {})
12
+ if (false === api.active) {
13
+ return false
14
+ }
15
+ const conf = (api.ent || {})[canon]
16
+ return !(conf && false === conf.active)
17
+ }
18
+
19
+ // All exposed canons, sorted (used by generators and get:info).
20
+ export function exposedCanons(model: any): string[] {
21
+ const out: string[] = []
22
+ const zones = (model && model.main && model.main.ent) || {}
23
+ for (const zone of Object.keys(zones)) {
24
+ for (const name of Object.keys(zones[zone])) {
25
+ const canon = zone + '/' + name
26
+ if (exposedCanon(model, canon)) {
27
+ out.push(canon)
28
+ }
29
+ }
30
+ }
31
+ return out.sort()
32
+ }
33
+
@@ -0,0 +1,6 @@
1
+ module.exports = function make_get_info() {
2
+ return async function get_info(this: any, _msg: any) {
3
+ return { ok: true, srv: 'api' }
4
+ }
5
+ }
6
+