@svgrid/enterprise 2.3.2 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (165) hide show
  1. package/dist/cdn/svgrid-enterprise.svelte-external.js +5917 -5545
  2. package/dist/designer/assets/index-oYok52zd.css +1 -0
  3. package/dist/designer/assets/{index-DVrv9dH6.js → index-qL7ir9C7.js} +89 -89
  4. package/dist/designer/assets/src-96d31O63.js +2873 -0
  5. package/dist/designer/assets/src-D-O2F805.css +1 -0
  6. package/dist/designer/index.html +6 -6
  7. package/dist/node/studio.js +681 -84
  8. package/package.json +3 -3
  9. package/src/SvGridEditPanel.dom.test.ts +174 -0
  10. package/src/SvGridEditPanel.svelte +209 -46
  11. package/src/edit-panel.test.ts +145 -36
  12. package/src/edit-panel.ts +167 -16
  13. package/src/expressions/evaluate.ts +1 -1
  14. package/src/expressions/parse.ts +3 -3
  15. package/src/index.ts +8 -0
  16. package/src/license-core.ts +77 -5
  17. package/src/license-expiry.test.ts +91 -0
  18. package/src/license.ts +31 -2
  19. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/_svelte_metadata.json +1 -0
  20. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/_metadata.json +178 -0
  21. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/attachments-ZrPsKxob.js +3875 -0
  22. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/attachments-ZrPsKxob.js.map +1 -0
  23. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/constants-ACqvWGE2.js +21 -0
  24. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/constants-ACqvWGE2.js.map +1 -0
  25. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/dev-D3w5jt92.js +1938 -0
  26. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/dev-D3w5jt92.js.map +1 -0
  27. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/events-CC2V3H0y.js +205 -0
  28. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/events-CC2V3H0y.js.map +1 -0
  29. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/flags-BXP37tAa.js +18 -0
  30. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/flags-BXP37tAa.js.map +1 -0
  31. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/hydration-DwubnU5b.js +264 -0
  32. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/hydration-DwubnU5b.js.map +1 -0
  33. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/legacy-client-hD7f6PUz.js +697 -0
  34. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/legacy-client-hD7f6PUz.js.map +1 -0
  35. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/package.json +3 -0
  36. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte.js +134 -0
  37. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte.js.map +1 -0
  38. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_animate.js +55 -0
  39. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_animate.js.map +1 -0
  40. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_attachments.js +2 -0
  41. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_compiler.js +46843 -0
  42. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_compiler.js.map +1 -0
  43. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_easing.js +237 -0
  44. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_easing.js.map +1 -0
  45. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_events.js +2 -0
  46. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal.js +4 -0
  47. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal.js.map +1 -0
  48. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_client.js +7 -0
  49. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_disclose-version.js +4 -0
  50. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_disclose-version.js.map +1 -0
  51. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_async.js +5 -0
  52. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_async.js.map +1 -0
  53. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_legacy.js +5 -0
  54. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_legacy.js.map +1 -0
  55. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_tracing.js +5 -0
  56. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_tracing.js.map +1 -0
  57. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_server.js +6 -0
  58. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_legacy.js +83 -0
  59. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_legacy.js.map +1 -0
  60. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_motion.js +559 -0
  61. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_motion.js.map +1 -0
  62. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity.js +24 -0
  63. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity.js.map +1 -0
  64. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity_window.js +122 -0
  65. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity_window.js.map +1 -0
  66. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_server.js +2 -0
  67. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_store.js +89 -0
  68. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_store.js.map +1 -0
  69. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_transition.js +241 -0
  70. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_transition.js.map +1 -0
  71. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-DBx1SCN6.js +4938 -0
  72. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-DBx1SCN6.js.map +1 -0
  73. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-gjfpSTFS.js +495 -0
  74. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-gjfpSTFS.js.map +1 -0
  75. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/validate-DKoOKg8S.js +190 -0
  76. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/validate-DKoOKg8S.js.map +1 -0
  77. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/version-C0MiR7eR.js +8 -0
  78. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/version-C0MiR7eR.js.map +1 -0
  79. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/warnings-B6Y7P0Q-.js +138 -0
  80. package/src/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/warnings-B6Y7P0Q-.js.map +1 -0
  81. package/src/schema.ts +63 -0
  82. package/src/studio/emit-project.test.ts +191 -0
  83. package/src/studio/emit-project.ts +237 -27
  84. package/src/studio/emit-schema.ts +4 -1
  85. package/src/studio/index.ts +2 -0
  86. package/src/studio/init-flow.test.ts +150 -0
  87. package/src/studio/init-flow.ts +169 -22
  88. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/_svelte_metadata.json +1 -0
  89. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/_metadata.json +178 -0
  90. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/attachments-BOv1rBKp.js +3875 -0
  91. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/attachments-BOv1rBKp.js.map +1 -0
  92. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/constants-B9vgIdCd.js +21 -0
  93. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/constants-B9vgIdCd.js.map +1 -0
  94. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/dev-T1qbW_qD.js +1938 -0
  95. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/dev-T1qbW_qD.js.map +1 -0
  96. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/events-Bo11SfVK.js +205 -0
  97. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/events-Bo11SfVK.js.map +1 -0
  98. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/flags-X7D4vfrZ.js +18 -0
  99. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/flags-X7D4vfrZ.js.map +1 -0
  100. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/hydration-oyUje5i1.js +264 -0
  101. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/hydration-oyUje5i1.js.map +1 -0
  102. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/legacy-client-DeSQgSFm.js +697 -0
  103. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/legacy-client-DeSQgSFm.js.map +1 -0
  104. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/package.json +3 -0
  105. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte.js +134 -0
  106. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte.js.map +1 -0
  107. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_animate.js +55 -0
  108. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_animate.js.map +1 -0
  109. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_attachments.js +2 -0
  110. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_compiler.js +46843 -0
  111. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_compiler.js.map +1 -0
  112. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_easing.js +237 -0
  113. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_easing.js.map +1 -0
  114. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_events.js +2 -0
  115. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal.js +4 -0
  116. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal.js.map +1 -0
  117. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_client.js +7 -0
  118. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_disclose-version.js +4 -0
  119. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_disclose-version.js.map +1 -0
  120. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_async.js +5 -0
  121. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_async.js.map +1 -0
  122. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_legacy.js +5 -0
  123. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_legacy.js.map +1 -0
  124. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_tracing.js +5 -0
  125. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_flags_tracing.js.map +1 -0
  126. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_internal_server.js +6 -0
  127. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_legacy.js +83 -0
  128. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_legacy.js.map +1 -0
  129. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_motion.js +559 -0
  130. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_motion.js.map +1 -0
  131. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity.js +24 -0
  132. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity.js.map +1 -0
  133. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity_window.js +122 -0
  134. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_reactivity_window.js.map +1 -0
  135. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_server.js +2 -0
  136. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_store.js +89 -0
  137. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_store.js.map +1 -0
  138. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_transition.js +241 -0
  139. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/svelte_transition.js.map +1 -0
  140. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-BbWyXNk1.js +4938 -0
  141. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-BbWyXNk1.js.map +1 -0
  142. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-DI_fC7vt.js +495 -0
  143. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/utils-DI_fC7vt.js.map +1 -0
  144. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/validate-Dyr3lQv9.js +190 -0
  145. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/validate-Dyr3lQv9.js.map +1 -0
  146. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/version-_9FHH-LF.js +8 -0
  147. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/version-_9FHH-LF.js.map +1 -0
  148. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/warnings-CUbSPnyP.js +138 -0
  149. package/src/studio/node_modules/.vite/vitest/be65d27ae088a0e03fd8e1331d90b01649464cb6/deps___vitest__/warnings-CUbSPnyP.js.map +1 -0
  150. package/src/studio/project.test.ts +30 -0
  151. package/src/studio/project.ts +72 -8
  152. package/src/studio/samples/crm.ts +11 -2
  153. package/src/studio/samples/insurance.ts +10 -4
  154. package/src/studio/samples/samples.test.ts +36 -0
  155. package/src/studio/samples/shared.ts +14 -0
  156. package/src/studio/samples/support.ts +12 -5
  157. package/src/studio/screen-suites.test.ts +43 -0
  158. package/src/studio/screen-suites.ts +5 -2
  159. package/src/studio/user-error.ts +31 -0
  160. package/src/upgrade-prompt.ts +23 -10
  161. package/src/version.test.ts +17 -0
  162. package/src/version.ts +13 -0
  163. package/dist/designer/assets/index-DjPp1mn7.css +0 -1
  164. package/dist/designer/assets/src-BRKu53tV.js +0 -2842
  165. package/dist/designer/assets/src-DseB1VRN.css +0 -1
@@ -1,3 +1,4 @@
1
+ import "@svgrid/grid/filtering";
1
2
  import { defaultThemePreset, getThemePreset, resolveThemeTokens as resolveThemeTokens$1, themePresets } from "@svgrid/grid/themes";
2
3
  //#region src/schema.ts
3
4
  /** `first_name` / `firstName` / `first-name` -> `First Name`. */
@@ -128,7 +129,8 @@ function schemaToFormFields(schema) {
128
129
  placeholder: f.input?.placeholder,
129
130
  help: f.input?.help,
130
131
  span: f.input?.span ?? (f.type === "json" ? 2 : 1),
131
- computed: f.computed
132
+ computed: f.computed,
133
+ when: f.when
132
134
  };
133
135
  });
134
136
  }
@@ -1119,7 +1121,7 @@ function introspectOpenApi(specText) {
1119
1121
  }
1120
1122
  //#endregion
1121
1123
  //#region src/studio/introspect-db.ts
1122
- function truthy(v) {
1124
+ function truthy$1(v) {
1123
1125
  return v === 1 || v === "1" || v === true;
1124
1126
  }
1125
1127
  function identQuote(name) {
@@ -1154,7 +1156,7 @@ function fieldFrom(name, rawType, pk, notNull) {
1154
1156
  }
1155
1157
  /** Map catalog rows (aliased name/type/nullable/pk) from information_schema dialects. */
1156
1158
  function infoSchemaFields(rows) {
1157
- return rows.map((r) => fieldFrom(String(r.name), String(r.type ?? ""), truthy(r.pk), !truthy(r.nullable)));
1159
+ return rows.map((r) => fieldFrom(String(r.name), String(r.type ?? ""), truthy$1(r.pk), !truthy$1(r.nullable)));
1158
1160
  }
1159
1161
  /** Map SQLite `PRAGMA table_info` rows (name/type/notnull/pk-as-index). */
1160
1162
  function sqliteFields(rows) {
@@ -1920,6 +1922,145 @@ function scaffoldApp(schemas, options = {}) {
1920
1922
  return { files };
1921
1923
  }
1922
1924
  //#endregion
1925
+ //#region src/expressions/expression-columns.ts
1926
+ var TEXT = ["text"];
1927
+ var NUM = ["number"];
1928
+ var DATES = ["date", "datetime"];
1929
+ var ALL = [
1930
+ "text",
1931
+ "number",
1932
+ "date",
1933
+ "datetime",
1934
+ "boolean"
1935
+ ];
1936
+ /** The full operator catalogue, ordered as the editor should present it. */
1937
+ var OPERATORS = [
1938
+ {
1939
+ value: "equals",
1940
+ label: "Equals",
1941
+ types: ALL
1942
+ },
1943
+ {
1944
+ value: "notEquals",
1945
+ label: "Not equals",
1946
+ types: ALL
1947
+ },
1948
+ {
1949
+ value: "contains",
1950
+ label: "Contains",
1951
+ types: TEXT
1952
+ },
1953
+ {
1954
+ value: "notContains",
1955
+ label: "Not contains",
1956
+ types: TEXT
1957
+ },
1958
+ {
1959
+ value: "startsWith",
1960
+ label: "Starts with",
1961
+ types: TEXT
1962
+ },
1963
+ {
1964
+ value: "endsWith",
1965
+ label: "Ends with",
1966
+ types: TEXT
1967
+ },
1968
+ {
1969
+ value: "regex",
1970
+ label: "Matches regex",
1971
+ types: TEXT
1972
+ },
1973
+ {
1974
+ value: "greaterThan",
1975
+ label: "Greater than",
1976
+ types: [...NUM, ...DATES]
1977
+ },
1978
+ {
1979
+ value: "lessThan",
1980
+ label: "Less than",
1981
+ types: [...NUM, ...DATES]
1982
+ },
1983
+ {
1984
+ value: "between",
1985
+ label: "Between",
1986
+ types: [...NUM, ...DATES],
1987
+ range: true
1988
+ },
1989
+ {
1990
+ value: "in",
1991
+ label: "In",
1992
+ types: ["text", "number"],
1993
+ set: true
1994
+ },
1995
+ {
1996
+ value: "notIn",
1997
+ label: "Not in",
1998
+ types: ["text", "number"],
1999
+ set: true
2000
+ },
2001
+ {
2002
+ value: "isBlank",
2003
+ label: "Is blank",
2004
+ types: ALL,
2005
+ valueless: true
2006
+ },
2007
+ {
2008
+ value: "isNotBlank",
2009
+ label: "Is not blank",
2010
+ types: ALL,
2011
+ valueless: true
2012
+ }
2013
+ ];
2014
+ new Map(OPERATORS.map((op) => [op.value, op]));
2015
+ //#endregion
2016
+ //#region src/expressions/parse.ts
2017
+ /** Every column id referenced anywhere in a predicate (deduped, in order). */
2018
+ function collectColumnRefs(expr) {
2019
+ const out = /* @__PURE__ */ new Set();
2020
+ walkPredicate(expr, out);
2021
+ return [...out];
2022
+ }
2023
+ function walkPredicate(expr, out) {
2024
+ switch (expr.kind) {
2025
+ case "and":
2026
+ case "or":
2027
+ expr.parts.forEach((p) => walkPredicate(p, out));
2028
+ return;
2029
+ case "not":
2030
+ walkPredicate(expr.expr, out);
2031
+ return;
2032
+ case "cmp":
2033
+ out.add(expr.column);
2034
+ return;
2035
+ case "scalarCmp":
2036
+ walkScalar(expr.left, out);
2037
+ walkScalar(expr.right, out);
2038
+ return;
2039
+ case "const": return;
2040
+ }
2041
+ }
2042
+ function walkScalar(expr, out) {
2043
+ switch (expr.kind) {
2044
+ case "col":
2045
+ out.add(expr.id);
2046
+ return;
2047
+ case "agg":
2048
+ out.add(expr.column);
2049
+ return;
2050
+ case "neg":
2051
+ walkScalar(expr.expr, out);
2052
+ return;
2053
+ case "bin":
2054
+ walkScalar(expr.left, out);
2055
+ walkScalar(expr.right, out);
2056
+ return;
2057
+ case "func":
2058
+ expr.args.forEach((a) => walkScalar(a, out));
2059
+ return;
2060
+ case "lit": return;
2061
+ }
2062
+ }
2063
+ //#endregion
1923
2064
  //#region src/studio/ui-components.generated.ts
1924
2065
  var GENERATED_UI_SURFACE = {
1925
2066
  "SvButton": {
@@ -11402,11 +11543,15 @@ var SSR_READ_KINDS = /* @__PURE__ */ new Set([
11402
11543
  * null when it must stay SPA. */
11403
11544
  function ssrScreenShape(project, screen) {
11404
11545
  if (!screen.entity || screen.code) return null;
11405
- const kind = project.dataSources?.[screen.entity]?.kind ?? project.dataSource;
11406
- if (kind !== "memory" && kind !== "sql") return null;
11546
+ const source = project.dataSources?.[screen.entity];
11547
+ const kind = source?.kind ?? project.dataSource;
11548
+ if (kind === "rest") {
11549
+ if (!(source?.kind === "rest" && /^https?:\/\//i.test(source.baseUrl ?? ""))) return null;
11550
+ } else if (kind !== "memory" && kind !== "sql") return null;
11407
11551
  const blocks = screen.blocks ?? [];
11408
- if (blocks.length === 1 && blocks[0].config.kind === "grid") {
11409
- const g = blocks[0].config;
11552
+ const grids = blocks.filter((b) => b.config.kind === "grid");
11553
+ if (grids.length === 1 && blocks.every((b) => b.config.kind === "grid" || b.config.kind === "filter")) {
11554
+ const g = grids[0].config;
11410
11555
  if (g.treeData || g.scheduler) return null;
11411
11556
  return "grid";
11412
11557
  }
@@ -11431,6 +11576,34 @@ function setScreenRenderMode(project, screenId, mode) {
11431
11576
  function isSsrScreen(project, screen) {
11432
11577
  return screenRenderMode(project, screen) === "ssr" && ssrEligible(project, screen);
11433
11578
  }
11579
+ /**
11580
+ * Turn server rendering on for every screen that can support it, for entities
11581
+ * backed by a real database. Applied when a project is first generated, so a new
11582
+ * app reads as idiomatic SvelteKit (a `load` and form `actions`) rather than a
11583
+ * client SPA. Screens that already state a mode are left alone, so this never
11584
+ * overrides a choice somebody made.
11585
+ *
11586
+ * Deliberately limited to sources that hold one copy of the data. In-memory and
11587
+ * PGlite sources are module singletons, so an SSR screen would read and write the
11588
+ * *server's* copy of the rows while the app's remaining SPA screens read the
11589
+ * browser's - create a row on one and the other never sees it. SQL and REST have
11590
+ * no such split: every path goes to the same database or the same remote API.
11591
+ */
11592
+ var SSR_DEFAULT_KINDS = /* @__PURE__ */ new Set(["sql", "rest"]);
11593
+ function withSsrDefaults(project) {
11594
+ return {
11595
+ ...project,
11596
+ screens: project.screens.map((screen) => {
11597
+ if (screen.renderMode || !screen.entity) return screen;
11598
+ const kind = project.dataSources?.[screen.entity]?.kind ?? project.dataSource;
11599
+ if (!SSR_DEFAULT_KINDS.has(kind)) return screen;
11600
+ return ssrEligible(project, screen) ? {
11601
+ ...screen,
11602
+ renderMode: "ssr"
11603
+ } : screen;
11604
+ })
11605
+ };
11606
+ }
11434
11607
  /** The triggers configured for an entity (or an empty object). */
11435
11608
  function triggersOf(project, entity) {
11436
11609
  return project.triggers?.[entity] ?? {};
@@ -13333,6 +13506,19 @@ function validateProject(project) {
13333
13506
  level: "warning",
13334
13507
  message: "No screens yet."
13335
13508
  });
13509
+ for (const entity of project.entities) {
13510
+ const names = new Set(entity.fields.map((f) => f.field));
13511
+ for (const field of entity.fields) {
13512
+ if (!field.when) continue;
13513
+ for (const [flag, expr] of Object.entries(field.when)) {
13514
+ if (!expr) continue;
13515
+ for (const ref of collectColumnRefs(expr)) if (!names.has(ref)) issues.push({
13516
+ level: "error",
13517
+ message: `"${entity.name}.${field.field}" has a ${flag} condition on "${ref}", which is not a field on ${entity.name}.`
13518
+ });
13519
+ }
13520
+ }
13521
+ }
13336
13522
  const routes = /* @__PURE__ */ new Set();
13337
13523
  for (const s of project.screens) {
13338
13524
  if (s.entity !== void 0 && !entityOf(project, s.entity)) issues.push({
@@ -13854,7 +14040,7 @@ function namesFor(schema) {
13854
14040
  /** Lookup variable name for a relation field, e.g. `postAuthorIdLookup`. */
13855
14041
  var lookupVar = (schema, field) => `${camel(schema.name)}${pascal(field)}Lookup`;
13856
14042
  function rowType(schema, derived) {
13857
- const optional = (f) => derived.has(f.field) || !!f.formula || !!f.computed;
14043
+ const optional = (f) => derived.has(f.field) || !!f.formula || !!f.computed || !!f.when?.visible;
13858
14044
  const lines = schema.fields.map((f) => ` ${f.field}${optional(f) ? "?" : ""}: ${tsType(f)}`);
13859
14045
  return `export type ${pascal(schema.name)} = {\n${lines.join("\n")}\n}`;
13860
14046
  }
@@ -14925,6 +15111,27 @@ function themeStyleString(theme) {
14925
15111
  return `${Object.entries(tokens).map(([k, v]) => `${k}: ${v}`).join("; ")}; color-scheme: ${isDarkTheme(theme) ? "dark" : "light"}; font-family: ${tokens["--sg-font"]}`;
14926
15112
  }
14927
15113
  //#endregion
15114
+ //#region src/edit-panel.ts
15115
+ /** True when any field's behaviour depends on the values (i.e. has a `when`). */
15116
+ function hasFieldConditions(schema) {
15117
+ return schema.fields.some((f) => !!f.when && (!!f.when.visible || !!f.when.disabled || !!f.when.required));
15118
+ }
15119
+ //#endregion
15120
+ //#region src/version.ts
15121
+ /**
15122
+ * The version of this package, as a value the pure (browser-safe) generator can
15123
+ * read - it cannot open `package.json` at runtime.
15124
+ *
15125
+ * Generated apps depend on `^SVGRID_VERSION` rather than `latest`, so an app
15126
+ * always installs a runtime at least as new as the generator that wrote it.
15127
+ * `latest` made that a coin toss: an app scaffolded today and the same app
15128
+ * scaffolded tomorrow could resolve different runtimes, and an unrelated publish
15129
+ * could break an app nobody had touched.
15130
+ *
15131
+ * `version.test.ts` asserts this matches `package.json`, so the two cannot drift.
15132
+ */
15133
+ var SVGRID_VERSION = "2.4.0";
15134
+ //#endregion
14928
15135
  //#region src/studio/emit-project.ts
14929
15136
  var has = (blocks, kind) => blocks.some((b) => b.config.kind === kind);
14930
15137
  /** Per-Tabs-block active-tab state var + a stable tab id. */
@@ -16828,9 +17035,6 @@ function screenChildEntities(blocks, resolve) {
16828
17035
  }
16829
17036
  return [...out.values()];
16830
17037
  }
16831
- /** `+page.server.ts` for a read-only SSR screen (data-viz / detail / master-
16832
- * detail): a `load` that returns the full dataset (+ each child collection) -
16833
- * the page renders real SSR HTML from `data.*`. No actions (read-only). */
16834
17038
  function ssrReadServerFile(schema, screen, sourceKind, accessEnabled, screenIds, resolve) {
16835
17039
  const n = namesFor(schema);
16836
17040
  const isSql = sourceKind === "sql";
@@ -16845,8 +17049,8 @@ function ssrReadServerFile(schema, screen, sourceKind, accessEnabled, screenIds,
16845
17049
  contents: `import type { PageServerLoad } from './$types'
16846
17050
  import type { ServerRequest } from '@svgrid/grid'
16847
17051
  ${isSql ? "import { createKitDataSource } from '@svgrid/enterprise'\n" : ""}${rbac ? "import { error } from '@sveltejs/kit'\nimport { authorizeAction, getServerRole } from '$lib/access'\n" : ""}${memImports.length ? `import { ${memImports.join(", ")} } from '$lib/data'\n` : ""}
16848
- // The app shell is a client SPA (+layout.ts ssr=false); this screen opts back
16849
- // INTO server rendering - real SSR HTML, not just a server-side load.
17052
+ // This screen renders on the server: real SSR HTML, not just a server-side
17053
+ // load. Stated explicitly so it holds whichever way the root layout is set.
16850
17054
  export const ssr = true
16851
17055
  ${rbac ? `\nconst SCREEN_IDS = ${JSON.stringify(screenIds)}\n` : ""}
16852
17056
  const PAGE: ServerRequest = { startRow: 0, endRow: 1000, pageIndex: 0, pageSize: 1000, sortModel: [], filterModel: {} }
@@ -16896,18 +17100,42 @@ function ssrQueryHelperFile() {
16896
17100
  };
16897
17101
  }
16898
17102
  var htmlEsc = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
17103
+ /** Escape a value going inside a double-quoted HTML attribute. */
17104
+ var attrEsc = (s) => htmlEsc(s).replace(/"/g, "&quot;");
17105
+ /**
17106
+ * The field's own constraints as native HTML attributes, so a server-rendered
17107
+ * form validates in the browser too. The server still re-checks every one of
17108
+ * them: these only save a round-trip, they never decide anything.
17109
+ */
17110
+ function attrs(f) {
17111
+ const out = [];
17112
+ if (f.input?.placeholder) out.push(`placeholder="${attrEsc(f.input.placeholder)}"`);
17113
+ if (f.type === "number") {
17114
+ if (f.min != null) out.push(`min="${f.min}"`);
17115
+ if (f.max != null) out.push(`max="${f.max}"`);
17116
+ if (f.input?.step != null) out.push(`step="${f.input.step}"`);
17117
+ } else {
17118
+ if (f.minLength != null) out.push(`minlength="${f.minLength}"`);
17119
+ if (f.maxLength != null) out.push(`maxlength="${f.maxLength}"`);
17120
+ if (f.pattern && !f.format) out.push(`pattern="${attrEsc(f.pattern)}"`);
17121
+ }
17122
+ return out.length ? " " + out.join(" ") : "";
17123
+ }
16899
17124
  /** The `+page.server.ts` + SSR `+page.svelte` for a single-grid CRUD screen. */
16900
17125
  function emitSsrGridScreen(schema, screen, sourceKind, accessEnabled, screenIds, byName) {
16901
17126
  const n = namesFor(schema);
16902
17127
  const isSql = sourceKind === "sql";
17128
+ const isRest = sourceKind === "rest";
16903
17129
  const relSchemaOf = (f) => byName.get(f.relation.entity);
16904
17130
  const relIdOf = (rs) => rs.idField ?? rs.fields.find((x) => x.primaryKey)?.field ?? "id";
16905
- const grid = screen.blocks[0].config;
17131
+ const grid = screen.blocks.find((b) => b.config.kind === "grid").config;
17132
+ const facetPanel = screen.blocks.find((b) => b.config.kind === "filter")?.config;
16906
17133
  const pageSize = grid.pageSize && grid.pageSize > 0 ? grid.pageSize : 25;
16907
17134
  const wantsFilter = grid.filterable !== false;
16908
17135
  const idField = schema.idField ?? schema.fields.find((f) => f.primaryKey)?.field ?? "id";
16909
17136
  const isFormHidden = (f) => f.hidden === true || typeof f.hidden === "object" && !!f.hidden.form;
16910
- const formFields = schema.fields.filter((f) => f.field !== idField && !f.primaryKey && !f.readonly && !f.computed && !f.formula && !isFormHidden(f));
17137
+ const editable = schema.fields.filter((f) => f.field !== idField && !f.primaryKey && !f.readonly && !f.computed && !f.formula && !isFormHidden(f));
17138
+ const formFields = grid.formFields?.length ? grid.formFields.map((name) => editable.find((f) => f.field === name)).filter(Boolean) : editable;
16911
17139
  const normType = (t) => t === "number" ? "number" : t === "boolean" ? "boolean" : "text";
16912
17140
  const fieldTypesLit = `{ ${formFields.map((f) => `${jsStr(f.field)}: ${jsStr(normType(f.type))}`).join(", ")} }`;
16913
17141
  const isRel = (f) => f.type === "relation" && !!f.relation && byName.has(f.relation.entity);
@@ -16924,12 +17152,18 @@ function emitSsrGridScreen(schema, screen, sourceKind, accessEnabled, screenIds,
16924
17152
  const relReturn = relFields.map((f) => `, ${f.field}Options`).join("");
16925
17153
  const src = isSql ? "source(fetch)" : n.sourceVar;
16926
17154
  const fetchArg = isSql ? ", fetch" : "";
16927
- const enterpriseImports = isSql ? "validateAll, createKitDataSource" : "validateAll";
16928
- const sourceImport = isSql ? "" : `import { ${[.../* @__PURE__ */ new Set([n.sourceVar, ...relSourceVars]), "nextId"].join(", ")} } from '$lib/data'\n`;
17155
+ const conditional = hasFieldConditions(schema);
17156
+ const readValues = (fd) => conditional ? `stripHiddenValues(${n.schemaVar}, formToValues(${fd}))` : `formToValues(${fd})`;
17157
+ const enterpriseImports = [
17158
+ "validateAll",
17159
+ ...conditional ? ["stripHiddenValues"] : [],
17160
+ ...isSql ? ["createKitDataSource"] : []
17161
+ ].join(", ");
17162
+ const sourceImport = isSql ? "" : `import { ${[.../* @__PURE__ */ new Set([n.sourceVar, ...relSourceVars]), ...isRest ? [] : ["nextId"]].join(", ")} } from '$lib/data'\n`;
16929
17163
  const schemaImport = `import { ${n.schemaVar}${isSql ? `, type ${n.type}` : ""} } from '$lib/schemas'`;
16930
- const idConst = isSql ? "" : `\nconst ID_FIELD = ${jsStr(idField)}`;
17164
+ const idConst = isSql || isRest ? "" : `\nconst ID_FIELD = ${jsStr(idField)}`;
16931
17165
  const srcHelper = isSql ? `\n// Same-origin client over the connected /api/${n.route} route (that route runs\n// validation, RBAC, triggers + audit via createKitHandlers).\nconst source = (fetch: typeof globalThis.fetch) => createKitDataSource<${n.type}>({ endpoint: ${jsStr("/api/" + n.route)}, fetch })\n` : "";
16932
- const createCall = isSql ? `await ${src}.createRow(values)` : `await ${n.sourceVar}.createRow({ [ID_FIELD]: nextId(${jsStr(n.idPrefix)}), ...values })`;
17166
+ const createCall = isSql || isRest ? `await ${src}.createRow(values)` : `await ${n.sourceVar}.createRow({ [ID_FIELD]: nextId(${jsStr(n.idPrefix)}), ...values })`;
16933
17167
  const rbac = accessEnabled && !isSql;
16934
17168
  const localsArg = rbac ? ", locals" : "";
16935
17169
  const readGuard = rbac ? ` if (!authorizeAction(getServerRole({ locals }), 'read', SCREEN_IDS)) throw error(403, 'Not allowed')\n` : "";
@@ -16940,8 +17174,8 @@ import { ${enterpriseImports} } from '@svgrid/enterprise'
16940
17174
  ${rbac ? "import { authorizeAction, getServerRole } from '$lib/access'\n" : ""}${sourceImport}${schemaImport}
16941
17175
  import { planFromSearchParams } from '$lib/server/query'
16942
17176
 
16943
- // The app shell is a client SPA (+layout.ts ssr=false); this screen opts back
16944
- // INTO server rendering - real SSR HTML, not just a server-side load.
17177
+ // This screen renders on the server: real SSR HTML, not just a server-side
17178
+ // load. Stated explicitly so it holds whichever way the root layout is set.
16945
17179
  export const ssr = true
16946
17180
  ${idConst}${rbac ? `\nconst SCREEN_IDS = ${JSON.stringify(screenIds)}` : ""}
16947
17181
  const FIELD_TYPES: Record<string, 'text' | 'number' | 'boolean'> = ${fieldTypesLit}
@@ -16967,7 +17201,7 @@ ${relPrefetch ? relPrefetch + "\n" : ""} return { rows, total: rowCount, page:
16967
17201
 
16968
17202
  export const actions: Actions = {
16969
17203
  create: async ({ request${fetchArg}${localsArg} }) => {
16970
- ${writeGuard("create")} const values = formToValues(await request.formData())
17204
+ ${writeGuard("create")} const values = ${readValues("await request.formData()")}
16971
17205
  const errors = await validateAll(${n.schemaVar}, values)
16972
17206
  if (Object.keys(errors).length) return fail(422, { errors, values })
16973
17207
  ${createCall}
@@ -16976,7 +17210,7 @@ ${writeGuard("create")} const values = formToValues(await request.formData())
16976
17210
  update: async ({ request${fetchArg}${localsArg} }) => {
16977
17211
  const fd = await request.formData()
16978
17212
  ${writeGuard("update")} const id = String(fd.get('__id') ?? '')
16979
- const values = formToValues(fd)
17213
+ const values = ${readValues("fd")}
16980
17214
  const errors = await validateAll(${n.schemaVar}, values)
16981
17215
  if (Object.keys(errors).length) return fail(422, { errors, values })
16982
17216
  await ${src}.updateRow(id, values)
@@ -16989,6 +17223,58 @@ ${writeGuard("delete")} const fd = await request.formData()
16989
17223
  },
16990
17224
  }
16991
17225
  `;
17226
+ const facetControls = {
17227
+ select: false,
17228
+ input: false
17229
+ };
17230
+ const facetForm = (() => {
17231
+ if (!facetPanel) return "";
17232
+ const fields = filterFieldsOf(schema, facetPanel);
17233
+ if (!fields.length) return "";
17234
+ const cur = (f) => `(page.url.searchParams.get(${jsStr("f_" + f.field)}) ?? '')`;
17235
+ const optionList = (f, opts) => [`<option value="" selected={${cur(f)} === ''}>Any</option>`].concat(opts.map((o) => `<option value=${jsStr(o.value)} selected={${cur(f)} === ${jsStr(o.value)}}>${htmlEsc(o.label)}</option>`)).join("\n ");
17236
+ return `
17237
+ <form method="GET" class="sk-facets">
17238
+ <input type="hidden" name="sort" value={page.url.searchParams.get('sort') ?? ''} />
17239
+ ${fields.map((f) => {
17240
+ const key = jsStr("f_" + f.field);
17241
+ const opts = enumOpts(f);
17242
+ let control;
17243
+ if (opts.length) {
17244
+ facetControls.select = true;
17245
+ control = `<select name=${key}>\n ${optionList(f, opts)}\n </select>`;
17246
+ } else if (f.type === "boolean") {
17247
+ facetControls.select = true;
17248
+ control = `<select name=${key}>\n ${optionList(f, [{
17249
+ value: "true",
17250
+ label: "Yes"
17251
+ }, {
17252
+ value: "false",
17253
+ label: "No"
17254
+ }])}\n </select>`;
17255
+ } else {
17256
+ facetControls.input = true;
17257
+ control = `<input name=${key} value={${cur(f)}} placeholder="Any" />`;
17258
+ }
17259
+ return ` <label class="sk-facet">\n <span>${htmlEsc(f.label ?? f.field)}</span>\n ${control}\n </label>`;
17260
+ }).join("\n")}
17261
+ <div class="sk-facet-actions">
17262
+ <button type="submit">Filter</button>
17263
+ <a href={page.url.pathname}>Clear</a>
17264
+ </div>
17265
+ </form>
17266
+ `;
17267
+ })();
17268
+ const facetCss = !facetForm ? "" : [
17269
+ " .sk-facets { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px; margin-bottom: 14px; padding: 12px 14px; border: 1px solid var(--sg-border, #cbd5e1); border-radius: 10px; background: var(--sg-surface, #f8fafc); }",
17270
+ " .sk-facet { display: flex; flex-direction: column; gap: 4px; font-size: 13px; }",
17271
+ " .sk-facet span { color: var(--sg-muted, #64748b); }",
17272
+ ...facetControls.select && facetControls.input ? [" .sk-facet input, .sk-facet select { font: inherit; padding: 6px 9px; border-radius: 8px; border: 1px solid var(--sg-border, #cbd5e1); background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a); }"] : facetControls.select ? [" .sk-facet select { font: inherit; padding: 6px 9px; border-radius: 8px; border: 1px solid var(--sg-border, #cbd5e1); background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a); }"] : [" .sk-facet input { font: inherit; padding: 6px 9px; border-radius: 8px; border: 1px solid var(--sg-border, #cbd5e1); background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a); }"],
17273
+ " .sk-facet-actions { display: flex; align-items: center; gap: 10px; margin-left: auto; }",
17274
+ " .sk-facet-actions button { font: inherit; padding: 7px 14px; border-radius: 8px; border: 1px solid var(--sg-accent, #4f46e5); background: var(--sg-accent, #4f46e5); color: #fff; cursor: pointer; }",
17275
+ " .sk-facet-actions a { font-size: 13px; color: var(--sg-muted, #64748b); }",
17276
+ ""
17277
+ ].join("\n");
16992
17278
  const row = `(editing as Record<string, unknown>)`;
16993
17279
  const fieldBlocks = formFields.map((f) => {
16994
17280
  const key = jsStr(f.field);
@@ -16997,13 +17283,49 @@ ${writeGuard("delete")} const fd = await request.formData()
16997
17283
  if (normType(f.type) === "boolean") input = `<input type="checkbox" name=${key} checked={!isCreate && !!${row}[${key}]} />`;
16998
17284
  else if (f.options && f.options.length) input = `<select name=${key}${req}>${f.options.map((o) => `<option value=${jsStr(String(o.value))} selected={!isCreate && ${row}[${key}] === ${jsStr(String(o.value))}}>${htmlEsc(o.label ?? String(o.value))}</option>`).join("")}</select>`;
16999
17285
  else if (isRel(f)) input = `<select name=${key}${req}>\n <option value="">-</option>\n {#each data.${f.field}Options as o (o.value)}<option value={o.value} selected={!isCreate && String(${row}[${key}] ?? '') === o.value}>{o.label}</option>{/each}\n </select>`;
17000
- else input = `<input type="${normType(f.type) === "number" ? "number" : f.type === "date" || f.type === "dateString" ? "date" : "text"}" name=${key} value={isCreate ? '' : (${row}[${key}] ?? '')}${req} />`;
17001
- return ` <label class="sk-field">
17286
+ else if (f.input?.editorType === "textarea" || f.type === "json") input = `<textarea name=${key}${req}${attrs(f)}>{isCreate ? '' : (${row}[${key}] ?? '')}</textarea>`;
17287
+ else input = `<input type="${f.format === "email" ? "email" : f.format === "url" ? "url" : normType(f.type) === "number" ? "number" : f.type === "date" || f.type === "dateString" ? "date" : "text"}" name=${key} value={isCreate ? '' : (${row}[${key}] ?? '')}${req}${attrs(f)} />`;
17288
+ const hint = f.input?.help;
17289
+ return ` <label class="sk-field${f.input?.span === 2 ? " sk-field--wide" : ""}">
17002
17290
  <span>${htmlEsc(f.label ?? f.field)}${f.required ? " *" : ""}</span>
17003
17291
  ${input}
17004
- {#if form?.errors?.[${key}]}<em class="sk-err">{form.errors[${key}]}</em>{/if}
17292
+ ${hint ? ` {#if !form?.errors?.[${key}]}<small class="sk-hint">${htmlEsc(hint)}</small>{/if}\n` : ""} {#if form?.errors?.[${key}]}<em class="sk-err">{form.errors[${key}]}</em>{/if}
17005
17293
  </label>`;
17006
- }).join("\n");
17294
+ });
17295
+ const blockFor = (name) => {
17296
+ const i = formFields.findIndex((f) => f.field === name);
17297
+ return i === -1 ? null : fieldBlocks[i];
17298
+ };
17299
+ const layout = {
17300
+ columns: grid.formColumns ?? schema.form?.columns,
17301
+ sections: grid.formSections?.length ? grid.formSections : schema.form?.sections
17302
+ };
17303
+ const ssrSections = layout?.sections?.length ? (() => {
17304
+ const assigned = /* @__PURE__ */ new Set();
17305
+ const groups = layout.sections.map((s) => {
17306
+ const items = s.fields.map((name) => {
17307
+ assigned.add(name);
17308
+ return blockFor(name);
17309
+ }).filter(Boolean);
17310
+ return {
17311
+ title: s.title,
17312
+ description: s.description,
17313
+ columns: s.columns,
17314
+ items
17315
+ };
17316
+ }).filter((g) => g.items.length);
17317
+ const rest = formFields.filter((f) => !assigned.has(f.field)).map((f) => blockFor(f.field)).filter(Boolean);
17318
+ return rest.length ? [...groups, {
17319
+ title: void 0,
17320
+ description: void 0,
17321
+ columns: void 0,
17322
+ items: rest
17323
+ }] : groups;
17324
+ })() : null;
17325
+ const formCols = layout?.columns ?? 1;
17326
+ const fieldsMarkup = ssrSections ? ssrSections.map((g) => ` <fieldset class="sk-group" style="--sk-cols: ${g.columns ?? formCols}">
17327
+ ${g.title ? ` <legend>${htmlEsc(g.title)}</legend>\n` : ""}${g.description ? ` <p class="sk-group__desc">${htmlEsc(g.description)}</p>\n` : ""}${g.items.join("\n")}
17328
+ </fieldset>`).join("\n") : ` <div class="sk-group" style="--sk-cols: ${formCols}">\n${fieldBlocks.join("\n")}\n </div>`;
17007
17329
  const page = `<script lang="ts">
17008
17330
  import { SvGrid, renderSnippet, type ColumnDef, type CellContext } from '@svgrid/grid'
17009
17331
  import { schemaToColumns } from '@svgrid/enterprise'
@@ -17072,7 +17394,7 @@ ${wantsFilter ? `
17072
17394
  <h1>{TITLE}</h1>
17073
17395
  <button type="button" class="sk-btn sk-btn--primary" onclick={() => (editing = 'create')}>{NEW_LABEL}</button>
17074
17396
  </header>
17075
-
17397
+ ${facetForm}
17076
17398
  <SvGrid
17077
17399
  data={data.rows}
17078
17400
  {columns}
@@ -17091,7 +17413,7 @@ ${wantsFilter ? `
17091
17413
  <form method="POST" action={isCreate ? '?/create' : '?/update'} class="sk-form" use:enhance={onSubmit}>
17092
17414
  <h2>{isCreate ? NEW_LABEL : 'Edit ${htmlEsc(n.label)}'}</h2>
17093
17415
  {#if !isCreate}<input type="hidden" name="__id" value={${row}[ID_FIELD] as string} />{/if}
17094
- ${fieldBlocks}
17416
+ ${fieldsMarkup}
17095
17417
  <div class="sk-form__actions">
17096
17418
  <button type="button" class="sk-btn" onclick={() => (editing = null)}>Cancel</button>
17097
17419
  <button type="submit" class="sk-btn sk-btn--primary">Save</button>
@@ -17105,12 +17427,19 @@ ${fieldBlocks}
17105
17427
  .sk-head h1 { margin: 0; font-size: 20px; }
17106
17428
  .sk-btn { font: inherit; padding: 7px 14px; border-radius: 8px; border: 1px solid var(--sg-border, #cbd5e1); background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a); cursor: pointer; }
17107
17429
  .sk-btn--primary { background: var(--sg-accent, #4f46e5); border-color: var(--sg-accent, #4f46e5); color: #fff; }
17108
- .sk-rowact { display: flex; gap: 10px; }
17430
+ ${facetCss} .sk-rowact { display: flex; gap: 10px; }
17109
17431
  .sk-link { background: none; border: none; padding: 0; font: inherit; color: var(--sg-accent, #4f46e5); cursor: pointer; }
17110
17432
  .sk-danger { color: #dc2626; }
17111
17433
  .sk-overlay { position: fixed; inset: 0; background: rgba(15, 23, 42, 0.4); display: grid; place-items: center; z-index: 50; }
17112
17434
  .sk-form { width: min(480px, 92vw); max-height: 90vh; overflow: auto; background: var(--sg-bg, #fff); color: var(--sg-fg, #0f172a); border-radius: 12px; padding: 22px; display: flex; flex-direction: column; gap: 12px; box-shadow: 0 24px 60px -20px rgba(15, 23, 42, 0.5); }
17113
17435
  .sk-form h2 { margin: 0 0 4px; font-size: 16px; }
17436
+ .sk-group { display: grid; grid-template-columns: repeat(var(--sk-cols, 1), minmax(0, 1fr)); gap: 12px; border: 0; padding: 0; margin: 0; min-width: 0; }
17437
+ .sk-group + .sk-group { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--sg-border, #e2e8f0); }
17438
+ .sk-group legend { grid-column: 1 / -1; padding: 0; font-size: 12px; font-weight: 700; letter-spacing: 0.02em; text-transform: uppercase; color: var(--sg-muted, #64748b); }
17439
+ .sk-group__desc { grid-column: 1 / -1; margin: 0; font-size: 12px; color: var(--sg-muted, #64748b); }
17440
+ .sk-field--wide { grid-column: 1 / -1; }
17441
+ .sk-hint { color: var(--sg-muted, #64748b); font-size: 11.5px; }
17442
+ @media (max-width: 560px) { .sk-group { grid-template-columns: 1fr; } }
17114
17443
  .sk-field { display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
17115
17444
  .sk-field span { color: var(--sg-muted, #64748b); }
17116
17445
  .sk-field input, .sk-field select { font: inherit; padding: 7px 9px; border-radius: 8px; border: 1px solid var(--sg-border, #cbd5e1); background: var(--sg-input-bg, #fff); color: var(--sg-fg, #0f172a); }
@@ -17182,13 +17511,22 @@ function emitStudioProject(project) {
17182
17511
  const schema = byName.get(screen.entity);
17183
17512
  if (!schema) throw new Error(`emitStudioProject: screen "${screen.title}" references missing entity "${screen.entity}"`);
17184
17513
  if (isSsrScreen(project, screen)) {
17185
- const srcKind = sources[screen.entity]?.kind === "sql" ? "sql" : "memory";
17514
+ const kind = sources[screen.entity]?.kind;
17515
+ const srcKind = kind === "sql" ? "sql" : kind === "rest" ? "rest" : "memory";
17186
17516
  if (ssrScreenShape(project, screen) === "grid") pages.push(...emitSsrGridScreen(schema, screen, srcKind, accessEnabled, screensByEntity.get(screen.entity) ?? [], byName));
17187
17517
  else pages.push(ssrReadServerFile(schema, screen, srcKind, accessEnabled, screensByEntity.get(screen.entity) ?? [], resolve), screenPage(schema, rawByName.get(screen.entity) ?? schema, screen, resolve, (name) => rawByName.get(name), accessEnabled, i18nEnabled, routeById, drillEnabled, true));
17188
17518
  continue;
17189
17519
  }
17190
17520
  pages.push(screenPage(schema, rawByName.get(screen.entity) ?? schema, screen, resolve, (name) => rawByName.get(name), accessEnabled, i18nEnabled, routeById, drillEnabled, false, sources[screen.entity]));
17191
17521
  }
17522
+ if (project.screens.some((s) => isSsrScreen(project, s))) for (const screen of project.screens) {
17523
+ if (isSsrScreen(project, screen) || !screen.route) continue;
17524
+ pages.push({
17525
+ path: `src/routes/${screen.route}/+page.ts`,
17526
+ description: `${screen.title} renders in the browser (client data source).`,
17527
+ contents: `// This screen loads its rows in the browser, so it opts out of the root\n// layout's server rendering.\nexport const ssr = false\n`
17528
+ });
17529
+ }
17192
17530
  const ssrHelpers = project.screens.some((s) => isSsrScreen(project, s)) ? [ssrQueryHelperFile()] : [];
17193
17531
  const actionRouteFiles = [...actionsById.values()].map(({ action, screenId }) => actionRouteFile(action, screenId, accessEnabled));
17194
17532
  const nav = [...project.screens].filter((s) => s.nav?.show !== false).sort((a, b) => (a.nav?.order ?? 0) - (b.nav?.order ?? 0)).map((s) => ({
@@ -19162,6 +19500,23 @@ main().then(() => process.exit(0)).catch((err) => { console.error(err); process.
19162
19500
  ];
19163
19501
  }
19164
19502
  var appSlug = (title) => (title || "studio-app").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "studio-app";
19503
+ /**
19504
+ * The root `+layout.ts`.
19505
+ *
19506
+ * An app with server-rendered screens leaves SvelteKit's own default in place
19507
+ * (SSR on) and lets each client-only screen opt out in its own `+page.ts` - so
19508
+ * the app reads the way a hand-written SvelteKit app does, instead of announcing
19509
+ * itself as a SPA in the first file anyone opens. With nothing to server-render,
19510
+ * the whole app stays a SPA as before.
19511
+ */
19512
+ function rootLayoutModule(project) {
19513
+ const anySsr = project.screens.some((s) => isSsrScreen(project, s));
19514
+ return {
19515
+ path: "src/routes/+layout.ts",
19516
+ description: anySsr ? "Root layout options (SSR on; client-only screens opt out per page)." : "Client SPA (in-memory sources persist across navigation).",
19517
+ contents: anySsr ? `// Server rendering stays on (SvelteKit's default). Screens that have to run in\n// the browser - anything on an in-memory source, or a shape that can't render on\n// the server - opt out in their own +page.ts.\nexport const prerender = false\n` : `// In-memory sources are module singletons, so render as a client SPA. Move an\n// entity to SQL / Supabase and its /api route still runs server-side.\nexport const ssr = false\nexport const prerender = false\n`
19518
+ };
19519
+ }
19165
19520
  /** The static SvelteKit + Vite scaffolding around the generated screens. */
19166
19521
  var SCAFFOLD_STATIC = [
19167
19522
  {
@@ -19184,11 +19539,6 @@ var SCAFFOLD_STATIC = [
19184
19539
  description: "SvelteKit app types.",
19185
19540
  contents: `declare global {\n namespace App {}\n}\n\nexport {}\n`
19186
19541
  },
19187
- {
19188
- path: "src/routes/+layout.ts",
19189
- description: "Client SPA (in-memory sources persist across navigation).",
19190
- contents: `// In-memory sources are module singletons, so render as a client SPA. Move an\n// entity to SQL / Supabase and its /api route still runs server-side.\nexport const ssr = false\nexport const prerender = false\n`
19191
- },
19192
19542
  {
19193
19543
  path: ".npmrc",
19194
19544
  description: "npm config.",
@@ -19523,9 +19873,10 @@ function svelteConfig(plan) {
19523
19873
  /** The runtime npm deps the generated source imports, keyed by package -> range.
19524
19874
  * One source of truth for package.json AND the fragment-mode dependency report. */
19525
19875
  function runtimeDeps(project, allSource) {
19876
+ const svgrid = `^${SVGRID_VERSION}`;
19526
19877
  const dependencies = {
19527
- "@svgrid/grid": "latest",
19528
- "@svgrid/enterprise": "latest"
19878
+ "@svgrid/grid": svgrid,
19879
+ "@svgrid/enterprise": svgrid
19529
19880
  };
19530
19881
  if (allSource.includes("from '@supabase/supabase-js'")) dependencies["@supabase/supabase-js"] = "^2.45.0";
19531
19882
  if (allSource.includes("import pg from 'pg'")) dependencies["pg"] = "^8.11.0";
@@ -19730,6 +20081,7 @@ function emitStudioAppBundle(project) {
19730
20081
  },
19731
20082
  ...plan.files,
19732
20083
  ...cronScheduleFiles(project, plan),
20084
+ rootLayoutModule(project),
19733
20085
  ...SCAFFOLD_STATIC,
19734
20086
  ...envExample(allSource) ? [{
19735
20087
  path: ".env.example",
@@ -20325,14 +20677,14 @@ function crudAppFromSchemas(schemas, opts = {}) {
20325
20677
  seed
20326
20678
  } : source;
20327
20679
  }
20328
- return sanitizeProject({
20680
+ return withSsrDefaults(sanitizeProject({
20329
20681
  title: opts.title ?? "My Studio App",
20330
20682
  entities: [...schemas],
20331
20683
  screens,
20332
20684
  dataSource: kind,
20333
20685
  dataSources,
20334
20686
  ...opts.theme ? { theme: opts.theme } : {}
20335
- });
20687
+ }));
20336
20688
  }
20337
20689
  //#endregion
20338
20690
  //#region src/studio/samples/shared.ts
@@ -20379,6 +20731,28 @@ function pad(schema, rows, target, fkPools = {}) {
20379
20731
  }
20380
20732
  /** The `id` values of a seed array (for building foreign-key pools). */
20381
20733
  var ids = (rows) => rows.map((r) => String(r.id));
20734
+ /**
20735
+ * `field = value` as a form condition (see `EntityField.when`). Helpers rather
20736
+ * than hand-written literals because an operator the filter engine does not know
20737
+ * silently matches everything, which would leave a conditional field permanently
20738
+ * on-screen.
20739
+ */
20740
+ var whenIs = (field, value) => ({
20741
+ kind: "cmp",
20742
+ column: field,
20743
+ op: "equals",
20744
+ value
20745
+ });
20746
+ /** `field` is any one of `values`. */
20747
+ var whenIsAny = (field, values) => ({
20748
+ kind: "or",
20749
+ parts: values.map((value) => ({
20750
+ kind: "cmp",
20751
+ column: field,
20752
+ op: "equals",
20753
+ value
20754
+ }))
20755
+ });
20382
20756
  /** A Kanban board screen: an optional filter panel + a board grouping rows by an
20383
20757
  * enum field into draggable columns (the signature "pipeline" view). */
20384
20758
  function boardScreen(entity, meta, opts) {
@@ -20859,6 +21233,38 @@ var deals$1 = {
20859
21233
  field: "closeDate",
20860
21234
  type: "date",
20861
21235
  label: "Close date"
21236
+ },
21237
+ {
21238
+ field: "lostReason",
21239
+ type: "enum",
21240
+ label: "Lost to",
21241
+ hidden: { grid: true },
21242
+ options: [
21243
+ {
21244
+ value: "price",
21245
+ label: "Price"
21246
+ },
21247
+ {
21248
+ value: "competitor",
21249
+ label: "Competitor"
21250
+ },
21251
+ {
21252
+ value: "timing",
21253
+ label: "Timing"
21254
+ },
21255
+ {
21256
+ value: "no-budget",
21257
+ label: "No budget"
21258
+ },
21259
+ {
21260
+ value: "other",
21261
+ label: "Other"
21262
+ }
21263
+ ],
21264
+ when: {
21265
+ visible: whenIs("stage", "lost"),
21266
+ required: whenIs("stage", "lost")
21267
+ }
20862
21268
  }
20863
21269
  ]
20864
21270
  };
@@ -21175,7 +21581,8 @@ var seed$17 = {
21175
21581
  value: 15e3,
21176
21582
  probability: 0,
21177
21583
  owner: "Priya Patel",
21178
- closeDate: "2026-05-10"
21584
+ closeDate: "2026-05-10",
21585
+ lostReason: "competitor"
21179
21586
  },
21180
21587
  {
21181
21588
  id: "dl5",
@@ -23787,6 +24194,21 @@ var tickets = {
23787
24194
  min: 0,
23788
24195
  input: { help: "Time to the first agent reply" }
23789
24196
  },
24197
+ {
24198
+ field: "resolution",
24199
+ type: "text",
24200
+ label: "Resolution",
24201
+ hidden: { grid: true },
24202
+ input: {
24203
+ editorType: "textarea",
24204
+ span: 2,
24205
+ help: "What fixed it - required to resolve or close"
24206
+ },
24207
+ when: {
24208
+ visible: whenIsAny("status", ["resolved", "closed"]),
24209
+ required: whenIsAny("status", ["resolved", "closed"])
24210
+ }
24211
+ },
23790
24212
  {
23791
24213
  field: "satisfaction",
23792
24214
  type: "number",
@@ -23796,7 +24218,8 @@ var tickets = {
23796
24218
  input: {
23797
24219
  editorType: "rating",
23798
24220
  help: "Customer rating on close"
23799
- }
24221
+ },
24222
+ when: { visible: whenIsAny("status", ["resolved", "closed"]) }
23800
24223
  },
23801
24224
  {
23802
24225
  field: "estimateHrs",
@@ -23991,7 +24414,8 @@ var seed$14 = {
23991
24414
  firstResponseMins: 20,
23992
24415
  satisfaction: 5,
23993
24416
  estimateHrs: 1,
23994
- hourlyRate: 60
24417
+ hourlyRate: 60,
24418
+ resolution: "Walked the customer through Export > CSV in the toolbar."
23995
24419
  },
23996
24420
  {
23997
24421
  id: "tk5",
@@ -24006,7 +24430,8 @@ var seed$14 = {
24006
24430
  firstResponseMins: 8,
24007
24431
  satisfaction: 4,
24008
24432
  estimateHrs: 1,
24009
- hourlyRate: 75
24433
+ hourlyRate: 75,
24434
+ resolution: "Upgraded the account to Business and prorated the invoice."
24010
24435
  },
24011
24436
  {
24012
24437
  id: "tk6",
@@ -24036,7 +24461,8 @@ var seed$14 = {
24036
24461
  firstResponseMins: 15,
24037
24462
  satisfaction: 4,
24038
24463
  estimateHrs: 2,
24039
- hourlyRate: 90
24464
+ hourlyRate: 90,
24465
+ resolution: "Refunded the duplicate charge; payment retry guard added."
24040
24466
  },
24041
24467
  {
24042
24468
  id: "tk8",
@@ -35563,14 +35989,31 @@ var claims = {
35563
35989
  type: "number",
35564
35990
  label: "Amount ($)",
35565
35991
  min: 0,
35566
- required: true
35992
+ required: true,
35993
+ when: { disabled: whenIs("status", "paid") }
35567
35994
  },
35568
35995
  {
35569
35996
  field: "deductible",
35570
35997
  type: "number",
35571
35998
  label: "Deductible ($)",
35572
35999
  min: 0,
35573
- defaultValue: 500
36000
+ defaultValue: 500,
36001
+ when: { disabled: whenIs("status", "paid") }
36002
+ },
36003
+ {
36004
+ field: "denialReason",
36005
+ type: "text",
36006
+ label: "Reason for denial",
36007
+ hidden: { grid: true },
36008
+ input: {
36009
+ editorType: "textarea",
36010
+ span: 2,
36011
+ help: "Required to deny a claim"
36012
+ },
36013
+ when: {
36014
+ visible: whenIs("status", "denied"),
36015
+ required: whenIs("status", "denied")
36016
+ }
35574
36017
  },
35575
36018
  {
35576
36019
  field: "net",
@@ -35863,7 +36306,8 @@ var seed = {
35863
36306
  fraudRisk: 5,
35864
36307
  tags: ["suspicious", "late-report"],
35865
36308
  incidentDate: "2026-01-20",
35866
- filedAt: "2026-01-28T16:45:00.000Z"
36309
+ filedAt: "2026-01-28T16:45:00.000Z",
36310
+ denialReason: "Loss reported 8 days after the incident, outside the policy window."
35867
36311
  },
35868
36312
  {
35869
36313
  id: "cl5",
@@ -39372,6 +39816,34 @@ async function introspectSupabaseTable(options) {
39372
39816
  return buildEntitySchema(o.table, columns);
39373
39817
  }
39374
39818
  //#endregion
39819
+ //#region src/studio/user-error.ts
39820
+ /**
39821
+ * UserError - a mistake the person at the keyboard can fix (a typo in a
39822
+ * connection string, an empty table selection, a folder that already holds
39823
+ * another app), as opposed to a defect in Studio.
39824
+ *
39825
+ * The CLI prints these plainly and stops. Everything else is treated as a crash
39826
+ * and gets a sanitized bug report plus a prefilled issue link, so ordinary
39827
+ * fat-fingering never looks like something worth reporting.
39828
+ *
39829
+ * `isUserError` is duck-typed rather than an `instanceof` check: the class can
39830
+ * cross a bundle boundary (browser ESM source vs. the Node build), and a
39831
+ * mis-detected user error would send someone to file an issue about their own
39832
+ * typo.
39833
+ */
39834
+ var UserError = class extends Error {
39835
+ constructor(message, hint) {
39836
+ super(message);
39837
+ this.isUserError = true;
39838
+ this.name = "UserError";
39839
+ if (hint !== void 0) this.hint = hint;
39840
+ }
39841
+ };
39842
+ /** True for anything thrown as a `UserError`, across module instances. */
39843
+ function isUserError(err) {
39844
+ return !!err && typeof err === "object" && err.isUserError === true;
39845
+ }
39846
+ //#endregion
39375
39847
  //#region src/studio/init-flow.ts
39376
39848
  var SOURCES = [
39377
39849
  {
@@ -39407,6 +39879,28 @@ var DIALECTS = [
39407
39879
  "mssql",
39408
39880
  "sqlite"
39409
39881
  ];
39882
+ /** Shown above the connection prompt so the expected shape is never a guess. */
39883
+ var CONNECTION_EXAMPLES = {
39884
+ postgres: "postgres://user:password@localhost:5432/mydb",
39885
+ supabase: "postgres://postgres:password@db.abcdefgh.supabase.co:5432/postgres",
39886
+ mysql: "mysql://user:password@localhost:3306/mydb",
39887
+ mssql: "Server=localhost,1433;Database=mydb;User Id=sa;Password=secret;Encrypt=false",
39888
+ sqlite: "./data.db"
39889
+ };
39890
+ /**
39891
+ * Files that mean the target folder already holds an app. Writing the bundle
39892
+ * over one of these replaces it wholesale (they carry no managed markers), so
39893
+ * the flow stops and asks first. A `studio.config.json` is the exception: that
39894
+ * folder IS a Studio app, and re-running init there is a regenerate.
39895
+ */
39896
+ var CLOBBER_MARKERS = [
39897
+ "package.json",
39898
+ "svelte.config.js",
39899
+ "svelte.config.ts",
39900
+ "vite.config.ts",
39901
+ "src/routes/+layout.svelte",
39902
+ ".env"
39903
+ ];
39410
39904
  /** Ask a numbered-list question; returns the chosen index. */
39411
39905
  async function pick(prompts, heading, items, def = 1) {
39412
39906
  prompts.say("");
@@ -39473,7 +39967,7 @@ async function gatherData(flags, prompts, db, fetchText) {
39473
39967
  hint: d.description
39474
39968
  })))].id;
39475
39969
  const dataset = getStarterDataset(datasetId);
39476
- if (!dataset) throw new Error(`Unknown dataset "${datasetId}". Try: ${starterDatasets.map((d) => d.id).join(", ")}`);
39970
+ if (!dataset) throw new UserError(`Unknown dataset "${datasetId}".`, `Try one of: ${starterDatasets.map((d) => d.id).join(", ")}`);
39477
39971
  const { entities, seed } = dataset.build();
39478
39972
  const kind = source === "pglite" ? "pglite" : "memory";
39479
39973
  const sources = {};
@@ -39491,27 +39985,37 @@ async function gatherData(flags, prompts, db, fetchText) {
39491
39985
  };
39492
39986
  }
39493
39987
  async function gatherFromDatabase(flags, prompts, db) {
39494
- if (!db) throw new Error("Connecting to a database is not available here.");
39988
+ if (!db) throw new UserError("Connecting to a database is not available here.");
39495
39989
  let dialect = flags.db;
39496
39990
  if (!dialect) dialect = DIALECTS[await pick(prompts, "Which database?", DIALECTS.map((d) => ({ label: d })))];
39497
39991
  let url = flags.url ?? "";
39498
39992
  if (!url) {
39499
39993
  if (isFileDialect(dialect)) url = (await prompts.ask("Database file:", "./data.db")).trim();
39500
- else url = (await prompts.ask("Connection string (blank to enter host/port instead):", "")).trim() || buildConnectionString(dialect, await askConnectionParts(prompts));
39994
+ else {
39995
+ prompts.say("");
39996
+ prompts.say(` Example: ${CONNECTION_EXAMPLES[dialect] ?? CONNECTION_EXAMPLES.postgres}`);
39997
+ url = (await prompts.ask("Connection string (blank to enter host/port instead):", "")).trim() || buildConnectionString(dialect, await askConnectionParts(prompts));
39998
+ }
39501
39999
  }
39502
- if (!url) throw new Error("A connection string is required.");
40000
+ if (!url) throw new UserError("A connection string is required.");
39503
40001
  const driver = await db.ensureDriver(dialect);
39504
- if (!driver.ok) throw new Error(driver.message ?? `Could not install the ${dialect} driver.`);
40002
+ if (!driver.ok) throw new UserError(driver.message ?? `Could not install the ${dialect} driver.`);
39505
40003
  prompts.say(" Connecting...");
39506
- const execute = await db.connect(dialect, url);
39507
- const tables = await listDatabaseTables(dialect, execute);
39508
- if (tables.length === 0) throw new Error("Connected, but the database has no tables.");
40004
+ let execute;
40005
+ let tables;
40006
+ try {
40007
+ execute = await db.connect(dialect, url);
40008
+ tables = await listDatabaseTables(dialect, execute);
40009
+ } catch (e) {
40010
+ throw new UserError(`Could not read the database: ${e instanceof Error ? e.message : String(e)}`, `Check the connection string. Expected shape: ${CONNECTION_EXAMPLES[dialect] ?? CONNECTION_EXAMPLES.postgres}`);
40011
+ }
40012
+ if (tables.length === 0) throw new UserError("Connected, but the database has no tables.", "Check that the connection string points at the right database and schema.");
39509
40013
  const counts = await countTableRows(dialect, execute, tables);
39510
40014
  prompts.say("");
39511
40015
  prompts.say(`Found ${tables.length} ${tables.length === 1 ? "table" : "tables"}:`);
39512
40016
  counts.forEach((t, i) => prompts.say(` ${i + 1}. ${t.name}${t.rows == null ? "" : ` (${t.rows} rows)`}`));
39513
40017
  const picked = parseSelection(flags.yes ? "all" : await prompts.ask("Which tables? (all, or a comma list of names/numbers)", "all"), tables);
39514
- if (picked.length === 0) throw new Error("No tables picked - nothing to build.");
40018
+ if (picked.length === 0) throw new UserError("No tables picked - nothing to build.");
39515
40019
  const schemas = [];
39516
40020
  const sources = {};
39517
40021
  for (const table of picked) try {
@@ -39529,7 +40033,7 @@ async function gatherFromDatabase(flags, prompts, db) {
39529
40033
  } catch (e) {
39530
40034
  prompts.say(` ! Skipped ${table}: ${e instanceof Error ? e.message : String(e)}`);
39531
40035
  }
39532
- if (schemas.length === 0) throw new Error("None of the picked tables could be read.");
40036
+ if (schemas.length === 0) throw new UserError("None of the picked tables could be read.");
39533
40037
  prompts.say(` Read ${schemas.length} ${schemas.length === 1 ? "table" : "tables"}.`);
39534
40038
  return {
39535
40039
  schemas,
@@ -39543,11 +40047,21 @@ async function gatherFromDatabase(flags, prompts, db) {
39543
40047
  * DbGateway - just the project URL and the anon key.
39544
40048
  */
39545
40049
  async function gatherFromSupabase(flags, prompts) {
40050
+ if (!flags.supabaseUrl || !flags.supabaseKey) {
40051
+ prompts.say("");
40052
+ prompts.say(" Both are in your Supabase project's API settings (Project Settings -> API).");
40053
+ prompts.say(" The URL looks like https://abcdefgh.supabase.co");
40054
+ }
39546
40055
  const url = (flags.supabaseUrl ?? await prompts.ask("Project URL:", "")).trim();
39547
40056
  const key = (flags.supabaseKey ?? await prompts.ask("Anon key:", "")).trim();
39548
- if (!url || !key) throw new Error("Both the project URL and the anon key are required.");
40057
+ if (!url || !key) throw new UserError("Both the project URL and the anon key are required.");
39549
40058
  prompts.say(" Reading the API...");
39550
- const tables = await listSupabaseTables(url, key);
40059
+ let tables;
40060
+ try {
40061
+ tables = await listSupabaseTables(url, key);
40062
+ } catch (e) {
40063
+ throw new UserError(`Could not reach ${url}: ${e instanceof Error ? e.message : String(e)}`, "Check the project URL and anon key in Project Settings -> API.");
40064
+ }
39551
40065
  let picked;
39552
40066
  if (tables.length) {
39553
40067
  prompts.say("");
@@ -39558,7 +40072,7 @@ async function gatherFromSupabase(flags, prompts) {
39558
40072
  prompts.say(" Couldn't list the tables (the API doc may be restricted for this key).");
39559
40073
  picked = (await prompts.ask("Table names (comma separated):", "")).split(",").map((t) => t.trim()).filter(Boolean);
39560
40074
  }
39561
- if (picked.length === 0) throw new Error("No tables picked - nothing to build.");
40075
+ if (picked.length === 0) throw new UserError("No tables picked - nothing to build.");
39562
40076
  const schemas = [];
39563
40077
  const sources = {};
39564
40078
  for (const table of picked) {
@@ -39579,7 +40093,7 @@ async function gatherFromSupabase(flags, prompts) {
39579
40093
  key
39580
40094
  };
39581
40095
  }
39582
- if (schemas.length === 0) throw new Error("None of the picked tables could be read.");
40096
+ if (schemas.length === 0) throw new UserError("None of the picked tables could be read.");
39583
40097
  prompts.say(` Read ${schemas.length} ${schemas.length === 1 ? "table" : "tables"}.`);
39584
40098
  return {
39585
40099
  schemas,
@@ -39598,14 +40112,14 @@ async function askConnectionParts(prompts) {
39598
40112
  };
39599
40113
  }
39600
40114
  async function gatherFromRest(prompts, fetchText) {
39601
- if (!fetchText) throw new Error("Reading a REST endpoint is not available here.");
40115
+ if (!fetchText) throw new UserError("Reading a REST endpoint is not available here.");
39602
40116
  const src = (await prompts.ask("OpenAPI spec (file or URL), or a REST endpoint returning rows:", "")).trim();
39603
- if (!src) throw new Error("A spec path/URL or endpoint is required.");
40117
+ if (!src) throw new UserError("A spec path/URL or endpoint is required.");
39604
40118
  const text = await fetchText(src);
39605
40119
  if (/"(openapi|swagger|paths)"\s*:/.test(text)) {
39606
40120
  const { entities, sources, warnings } = introspectOpenApi(text);
39607
40121
  for (const w of warnings) prompts.say(` ! ${w}`);
39608
- if (entities.length === 0) throw new Error("The spec described no collection resources.");
40122
+ if (entities.length === 0) throw new UserError("The spec described no collection resources.");
39609
40123
  prompts.say(` Read ${entities.length} REST ${entities.length === 1 ? "resource" : "resources"}.`);
39610
40124
  return {
39611
40125
  schemas: entities,
@@ -39614,9 +40128,19 @@ async function gatherFromRest(prompts, fetchText) {
39614
40128
  kind: "rest"
39615
40129
  };
39616
40130
  }
39617
- const rows = unwrapRows(JSON.parse(text));
39618
- if (rows.length === 0) throw new Error("The endpoint returned no rows to read a shape from.");
39619
- const url = new URL(src);
40131
+ let rows;
40132
+ try {
40133
+ rows = unwrapRows(JSON.parse(text));
40134
+ } catch {
40135
+ throw new UserError(`${src} did not return JSON.`, "Point this at an endpoint that returns rows, or at an OpenAPI (JSON) document.");
40136
+ }
40137
+ if (rows.length === 0) throw new UserError("The endpoint returned no rows to read a shape from.");
40138
+ let url;
40139
+ try {
40140
+ url = new URL(src);
40141
+ } catch {
40142
+ throw new UserError(`"${src}" is not a full URL, so there is no endpoint to call at runtime.`, "Pass the absolute endpoint, for example https://api.example.com/widgets");
40143
+ }
39620
40144
  const path = url.pathname.replace(/^\/+|\/+$/g, "");
39621
40145
  const suggested = path.split("/").filter(Boolean).pop() || "items";
39622
40146
  const name = ((await prompts.ask("Table name:", suggested)).trim() || suggested).replace(/\W+/g, "_");
@@ -39690,12 +40214,45 @@ async function askTheme(flags, prompts) {
39690
40214
  mode: dark ? "dark" : "light"
39691
40215
  };
39692
40216
  }
40217
+ /** Prefix a path with the output folder (`.` writes into the current one). */
40218
+ var inDir = (outDir, path) => (!outDir || outDir === "." ? "" : outDir.replace(/[\\/]+$/, "") + "/") + path;
40219
+ /** Which marker files are present in `outDir` right now. */
40220
+ async function markersIn(outDir, io) {
40221
+ const found = /* @__PURE__ */ new Set();
40222
+ for (const marker of CLOBBER_MARKERS) if (await io.readFile(inDir(outDir, marker)) != null) found.add(marker);
40223
+ return found;
40224
+ }
40225
+ /**
40226
+ * Stop before overwriting somebody else's app.
40227
+ *
40228
+ * `init` defaults to the current folder, and bare `svgrid-studio` in a terminal
40229
+ * runs it, so a mistaken run inside an existing project would replace its
40230
+ * package.json, svelte config and .env without a word. Re-running init on a
40231
+ * folder Studio itself generated is a regenerate, not a clobber, so a
40232
+ * `studio.config.json` waves the check through.
40233
+ *
40234
+ * `before` is the set of markers seen at startup, and only those count. Picking a
40235
+ * database makes Studio install a driver into this folder first, which writes a
40236
+ * package.json - without the snapshot the flow would trip over a file it had
40237
+ * just created itself and refuse to build anything.
40238
+ */
40239
+ async function confirmTargetFolder(outDir, flags, prompts, io, before) {
40240
+ if (flags.force) return;
40241
+ if (await io.readFile(inDir(outDir, "studio.config.json")) != null) return;
40242
+ const present = await markersIn(outDir, io);
40243
+ const found = CLOBBER_MARKERS.filter((m) => present.has(m) && (before == null || before.has(m)));
40244
+ if (found.length === 0) return;
40245
+ const message = `${!outDir || outDir === "." ? "This folder" : outDir} already has ${found.join(", ")}, and Studio would overwrite ${found.length === 1 ? "it" : "them"}.`;
40246
+ if (flags.yes) throw new UserError(message, "Pick an empty folder with --out <dir>, or pass --force to overwrite anyway.");
40247
+ prompts.say("");
40248
+ prompts.say(` ! ${message}`);
40249
+ if (!isYes(await prompts.ask("Overwrite? (y/N)", "n"), false)) throw new UserError("Stopped without writing anything.", "Re-run with --out <dir> to build somewhere else.");
40250
+ }
39693
40251
  /** Merge-write the generated files, preserving edits outside managed regions. */
39694
40252
  async function writeAll$1(files, outDir, io) {
39695
40253
  const written = [];
39696
- const prefix = !outDir || outDir === "." ? "" : outDir.replace(/[\\/]+$/, "") + "/";
39697
40254
  for (const file of files) {
39698
- const path = prefix + file.path;
40255
+ const path = inDir(outDir, file.path);
39699
40256
  const existing = await io.readFile(path);
39700
40257
  if (skipUserOwned(file, existing != null)) continue;
39701
40258
  await io.writeFile(path, mergeManaged(existing, file.contents));
@@ -39709,12 +40266,15 @@ async function writeAll$1(files, outDir, io) {
39709
40266
  */
39710
40267
  async function runStudioInit(flags, prompts, db, io, fetchText = null) {
39711
40268
  prompts.say("SvGrid Studio - let's build your app.");
40269
+ const candidateDir = flags.out ?? ".";
40270
+ const markersBefore = await markersIn(candidateDir, io);
39712
40271
  const data = await gatherData(flags, prompts, db, fetchText);
39713
40272
  const perEntity = await askScreens(data.schemas, flags, prompts);
39714
40273
  const theme = await askTheme(flags, prompts);
39715
40274
  const defaultTitle = flags.title ?? data.title ?? "My app";
39716
40275
  const title = flags.yes || flags.title ? defaultTitle : (await prompts.ask("\nApp name:", defaultTitle)).trim() || defaultTitle;
39717
40276
  const outDir = flags.out ?? (flags.yes ? "." : (await prompts.ask("Write it where?", ".")).trim() || ".");
40277
+ await confirmTargetFolder(outDir, flags, prompts, io, outDir === candidateDir ? markersBefore : null);
39718
40278
  const project = crudAppFromSchemas(data.schemas, {
39719
40279
  title,
39720
40280
  dataSource: data.kind,
@@ -39725,7 +40285,7 @@ async function runStudioInit(flags, prompts, db, io, fetchText = null) {
39725
40285
  ...theme ? { theme } : {}
39726
40286
  });
39727
40287
  const written = await writeAll$1(emitStudioAppBundle(project), outDir, io);
39728
- const configPath = (!outDir || outDir === "." ? "" : outDir.replace(/[\\/]+$/, "") + "/") + "studio.config.json";
40288
+ const configPath = inDir(outDir, "studio.config.json");
39729
40289
  await io.writeFile(configPath, serializeProject(project));
39730
40290
  written.push(configPath);
39731
40291
  prompts.say("");
@@ -40145,8 +40705,49 @@ async function runStudioAddApp(schemas, options, io) {
40145
40705
  //#endregion
40146
40706
  //#region src/revoked.ts
40147
40707
  var REVOKED_KEYS = /* @__PURE__ */ new Set([]);
40148
- /** Classify a license key string. Pure and side-effect-free. */
40149
- function checkLicenseKey(key) {
40708
+ /**
40709
+ * Pull the expiry out of a key.
40710
+ *
40711
+ * Keys are `SVENTERPRISE[-KIND]-<SLUG>-<SEATS>-<DATE>-<RANDOM>`, so the date is
40712
+ * always the second-to-last segment regardless of whether a KIND is present.
40713
+ * Two widths are accepted:
40714
+ *
40715
+ * YYYYMM - subscription granularity; expires at the end of that month.
40716
+ * YYYYMMDD - trial granularity; expires at the end of that day.
40717
+ *
40718
+ * The 6-digit form came first, so every key issued before day-precision trials
40719
+ * existed still parses. Anything else (including the older keys whose slug sits
40720
+ * in that position) yields null, which reads as "no expiry encoded".
40721
+ *
40722
+ * Boundaries are UTC so a key does not lapse at a different instant per
40723
+ * timezone. Kept in sync with `tools/issue-license.mjs`.
40724
+ */
40725
+ function parseLicenseExpiry(key) {
40726
+ if (!key) return null;
40727
+ const segments = key.split("-");
40728
+ if (segments.length < 3) return null;
40729
+ const stamp = segments[segments.length - 2];
40730
+ if (stamp == null) return null;
40731
+ if (/^\d{6}$/.test(stamp)) {
40732
+ const year = Number(stamp.slice(0, 4));
40733
+ const month = Number(stamp.slice(4, 6));
40734
+ if (month < 1 || month > 12) return null;
40735
+ return /* @__PURE__ */ new Date(Date.UTC(year, month, 1) - 1);
40736
+ }
40737
+ if (/^\d{8}$/.test(stamp)) {
40738
+ const year = Number(stamp.slice(0, 4));
40739
+ const month = Number(stamp.slice(4, 6));
40740
+ const day = Number(stamp.slice(6, 8));
40741
+ if (month < 1 || month > 12 || day < 1 || day > 31) return null;
40742
+ const end = new Date(Date.UTC(year, month - 1, day, 23, 59, 59, 999));
40743
+ if (end.getUTCMonth() !== month - 1) return null;
40744
+ return end;
40745
+ }
40746
+ return null;
40747
+ }
40748
+ /** Classify a license key string. Pure and side-effect-free.
40749
+ * `now` is injectable so expiry behaviour is testable without faking clocks. */
40750
+ function checkLicenseKey(key, now = /* @__PURE__ */ new Date()) {
40150
40751
  if (key == null || key === "") return {
40151
40752
  status: "unset",
40152
40753
  valid: false
@@ -40159,18 +40760,14 @@ function checkLicenseKey(key) {
40159
40760
  status: "revoked",
40160
40761
  valid: false
40161
40762
  };
40162
- if (key.startsWith("SVENTERPRISE-DEV")) return {
40163
- status: "dev",
40164
- valid: true
40165
- };
40166
- if (key.startsWith("SVENTERPRISE-EVAL")) return {
40167
- status: "eval",
40168
- valid: true
40169
- };
40763
+ const expiresAt = parseLicenseExpiry(key) ?? void 0;
40764
+ const expired = expiresAt ? now.getTime() > expiresAt.getTime() : void 0;
40170
40765
  return {
40171
- status: "licensed",
40172
- valid: true
40766
+ status: key.startsWith("SVENTERPRISE-DEV") ? "dev" : key.startsWith("SVENTERPRISE-EVAL") ? "eval" : "licensed",
40767
+ valid: true,
40768
+ expiresAt,
40769
+ expired
40173
40770
  };
40174
40771
  }
40175
40772
  //#endregion
40176
- export { CANVAS_COLS, CANVAS_GAP_PX, CANVAS_ROW_PX, CONTAINER_CHILD_KINDS, CONTAINER_DISPLAY_KINDS, CRUD_ACTIONS, DEFAULT_PORT, DRIVER_PACKAGE, FORM_SUBMIT, GRID_API_MEMBERS, GRID_CURATED_PROPS, GRID_EVENTS, GRID_PRESETS, MANAGED_END, MANAGED_START, ON_LOAD, PANE_LAYOUTS, STANDARD_UI_EVENTS, TAB_CHILD_KINDS, TRIGGER_EVENTS, UI_COMPONENT_REGISTRY, addAccordionBlock, addAccordionComponent, addAccordionSection, addBlock, addBlockAt, addComponentBlock, addCrudSuite, addEntity, addFreestandingScreen, addHandlerStep, addScreen, addScreenAction, addScreenFromTemplate, addStateVar, addTab, addTabBlock, addTabComponent, applyGridPreset, blockClassName, blockColumns, blockPalette, blockStyleCss, buildCanvasLayout, buildConnectionString, buildCopilotMessages, buildDockLayout, buildStudioBugReport, canvasOpts, canvasRectOf, changeSlot, checkLicenseKey, clickSlot, compileCondition, compileHandlerSteps, compileStep, compileTriggerStep, compileTriggerSteps, compileValue, componentHandleMembers, componentHandleName, componentHasBindings, countTableRows, createProject, crudAppFromSchemas, crudSuiteScreens, csvToEntity, ctxAmbientDts, ctxCompletions, dashScreen, defaultBlockConfig, defaultEntitySource, defaultScreenFor, defaultStudioTheme, deployCommands, detailScreen, detectDelimiter, disableScreenCode, dockOpts, dockPaneIds, dockPaneTitleOf, duplicateBlock, duplicateScreen, emitEntityModules, emitStudioApp, emitStudioAppBundle, emitStudioFragment, emitStudioProject, enableScreenCode, entityDataSource, entityOf, entityScreenPage, eventSlot, flattenBlocks, formScreen, generateRows, generateValue, getLiveDataSample, getSampleApp, getStarterDataset, getStudioTheme, gridApiSettableProps, gridColumns, gridConfig, gridOpts, gridPresetLabel, gridPropSurface, inferType, insertBlock, introspectDatabase, introspectDrizzle, introspectDrizzleAll, introspectJson, introspectOpenApi, introspectPrisma, introspectPrismaAll, isDarkTheme, isFileDialect, isPaneLayout, isProjectValid, isTenantScoped, linkRelationLabels, listDatabaseTables, listScreen, liveDataSamples, mapSqlType, mergeBlockStyle, mergeManaged, missingEnvKeys, moveBlock, moveHandlerStep, parseConnectionString, parseCsv, parseProject, parseSelection, pickLabelField, prepareEntities, probeConnection, projectFromModelText, reconcileDock, redactConnectionString, refineField, refineFields, removeAccordionBlock, removeAccordionSection, removeBlock, removeEntity, removeScreen, removeScreenAction, removeStateVar, removeTab, removeTabBlock, renameAccordionSection, renameTab, reorderBlock, reorderScreen, reseedScreenDock, resolveDeployTarget, resolveIdField, resolveSchema, resolveSchemas, resolveThemeTokens, resolveThemeTokensFor, roleCanAction, roleCanScreen, rowSelectSlot, runStudioAdd, runStudioAddApp, runStudioInit, runtimeDeps, sampleApps, sanitizeClassName, sanitizeProject, sanitizeStudioProject, scaffold, scaffoldApp, schemaToColumns, schemaToFormFields, screenDataset, screenFromTemplate, screenHandles, screenLayoutOf, seedUsers, serializeProject, setAccordionMultiple, setAuth, setCanvasRect, setComponentBinding, setComponentName, setDataLayer, setDataSource, setDeployTarget, setDockPaneTitle, setEntityDataSource, setHandlerBody, setHandlerSteps, setJob, setLayoutOpts, setScreenDock, setScreenHandlersSource, setScreenLayout, setScreenRenderGrid, setScreenRenderMode, setShell, setTenancy, setTheme, setThemePreset, setTrigger, skipUserOwned, splitOpts, ssrEligible, ssrScreenShape, stackOpts, starterDatasets, starterProject, stateInitExpr, stateTsType, statusPills, stepsToCode, studioDeployInfo, studioThemes, summarizeVerify, syncDockPanes, tenantField, themeStyleString, triggersOf, uiComponentSpec, updateBlock, updateEntity, updateHandlerStep, updateScreen, updateStateVar, validateProject, verifyScaffold };
40773
+ export { CANVAS_COLS, CANVAS_GAP_PX, CANVAS_ROW_PX, CONTAINER_CHILD_KINDS, CONTAINER_DISPLAY_KINDS, CRUD_ACTIONS, DEFAULT_PORT, DRIVER_PACKAGE, FORM_SUBMIT, GRID_API_MEMBERS, GRID_CURATED_PROPS, GRID_EVENTS, GRID_PRESETS, MANAGED_END, MANAGED_START, ON_LOAD, PANE_LAYOUTS, STANDARD_UI_EVENTS, TAB_CHILD_KINDS, TRIGGER_EVENTS, UI_COMPONENT_REGISTRY, UserError, addAccordionBlock, addAccordionComponent, addAccordionSection, addBlock, addBlockAt, addComponentBlock, addCrudSuite, addEntity, addFreestandingScreen, addHandlerStep, addScreen, addScreenAction, addScreenFromTemplate, addStateVar, addTab, addTabBlock, addTabComponent, applyGridPreset, blockClassName, blockColumns, blockPalette, blockStyleCss, buildCanvasLayout, buildConnectionString, buildCopilotMessages, buildDockLayout, buildStudioBugReport, canvasOpts, canvasRectOf, changeSlot, checkLicenseKey, clickSlot, compileCondition, compileHandlerSteps, compileStep, compileTriggerStep, compileTriggerSteps, compileValue, componentHandleMembers, componentHandleName, componentHasBindings, countTableRows, createProject, crudAppFromSchemas, crudSuiteScreens, csvToEntity, ctxAmbientDts, ctxCompletions, dashScreen, defaultBlockConfig, defaultEntitySource, defaultScreenFor, defaultStudioTheme, deployCommands, detailScreen, detectDelimiter, disableScreenCode, dockOpts, dockPaneIds, dockPaneTitleOf, duplicateBlock, duplicateScreen, emitEntityModules, emitStudioApp, emitStudioAppBundle, emitStudioFragment, emitStudioProject, enableScreenCode, entityDataSource, entityOf, entityScreenPage, eventSlot, flattenBlocks, formScreen, generateRows, generateValue, getLiveDataSample, getSampleApp, getStarterDataset, getStudioTheme, gridApiSettableProps, gridColumns, gridConfig, gridOpts, gridPresetLabel, gridPropSurface, inferType, insertBlock, introspectDatabase, introspectDrizzle, introspectDrizzleAll, introspectJson, introspectOpenApi, introspectPrisma, introspectPrismaAll, isDarkTheme, isFileDialect, isPaneLayout, isProjectValid, isTenantScoped, isUserError, linkRelationLabels, listDatabaseTables, listScreen, liveDataSamples, mapSqlType, mergeBlockStyle, mergeManaged, missingEnvKeys, moveBlock, moveHandlerStep, parseConnectionString, parseCsv, parseProject, parseSelection, pickLabelField, prepareEntities, probeConnection, projectFromModelText, reconcileDock, redactConnectionString, refineField, refineFields, removeAccordionBlock, removeAccordionSection, removeBlock, removeEntity, removeScreen, removeScreenAction, removeStateVar, removeTab, removeTabBlock, renameAccordionSection, renameTab, reorderBlock, reorderScreen, reseedScreenDock, resolveDeployTarget, resolveIdField, resolveSchema, resolveSchemas, resolveThemeTokens, resolveThemeTokensFor, roleCanAction, roleCanScreen, rowSelectSlot, runStudioAdd, runStudioAddApp, runStudioInit, runtimeDeps, sampleApps, sanitizeClassName, sanitizeProject, sanitizeStudioProject, scaffold, scaffoldApp, schemaToColumns, schemaToFormFields, screenDataset, screenFromTemplate, screenHandles, screenLayoutOf, seedUsers, serializeProject, setAccordionMultiple, setAuth, setCanvasRect, setComponentBinding, setComponentName, setDataLayer, setDataSource, setDeployTarget, setDockPaneTitle, setEntityDataSource, setHandlerBody, setHandlerSteps, setJob, setLayoutOpts, setScreenDock, setScreenHandlersSource, setScreenLayout, setScreenRenderGrid, setScreenRenderMode, setShell, setTenancy, setTheme, setThemePreset, setTrigger, skipUserOwned, splitOpts, ssrEligible, ssrScreenShape, stackOpts, starterDatasets, starterProject, stateInitExpr, stateTsType, statusPills, stepsToCode, studioDeployInfo, studioThemes, summarizeVerify, syncDockPanes, tenantField, themeStyleString, triggersOf, uiComponentSpec, updateBlock, updateEntity, updateHandlerStep, updateScreen, updateStateVar, validateProject, verifyScaffold, withSsrDefaults };