gina 0.6.3 → 0.6.4

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 (567) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +32 -40
  3. package/ROADMAP.md +3 -1
  4. package/framework/v0.6.4/VERSION +1 -0
  5. package/framework/v0.6.4/core/asset/plugin/dist/vendor/gina/css/gina.min.css +1 -0
  6. package/framework/v0.6.4/core/asset/plugin/dist/vendor/gina/css/gina.min.css.br +0 -0
  7. package/framework/v0.6.4/core/asset/plugin/dist/vendor/gina/css/gina.min.css.gz +0 -0
  8. package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/dist/vendor/gina/js/gina.js +1522 -79
  9. package/framework/v0.6.4/core/asset/plugin/dist/vendor/gina/js/gina.min.js +640 -0
  10. package/framework/v0.6.4/core/asset/plugin/dist/vendor/gina/js/gina.min.js.br +0 -0
  11. package/framework/v0.6.4/core/asset/plugin/dist/vendor/gina/js/gina.min.js.gz +0 -0
  12. package/framework/{v0.6.3 → v0.6.4}/core/config.js +22 -4
  13. package/framework/{v0.6.3 → v0.6.4}/core/controller/controller.js +62 -2
  14. package/framework/{v0.6.3 → v0.6.4}/core/controller/controller.render-nunjucks.js +11 -3
  15. package/framework/{v0.6.3 → v0.6.4}/core/controller/controller.render-swig.js +15 -1
  16. package/framework/{v0.6.3 → v0.6.4}/core/gna.js +33 -1
  17. package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/validator/src/main.js +892 -21
  18. package/framework/{v0.6.3 → v0.6.4}/core/server.js +60 -2
  19. package/framework/v0.6.4/core/template/_gitignore +27 -0
  20. package/framework/{v0.6.3 → v0.6.4}/lib/cmd/secrets/arguments.json +1 -0
  21. package/framework/v0.6.4/lib/cmd/secrets/check.js +861 -0
  22. package/framework/{v0.6.3 → v0.6.4}/lib/cmd/secrets/help.txt +29 -5
  23. package/framework/v0.6.4/lib/loading-state/package.json +7 -0
  24. package/framework/v0.6.4/lib/loading-state/src/main.js +366 -0
  25. package/framework/{v0.6.3 → v0.6.4}/lib/logger/src/containers/mq/listener.js +23 -0
  26. package/framework/{v0.6.3 → v0.6.4}/lib/logger/src/containers/mq/speaker.js +27 -0
  27. package/framework/v0.6.4/lib/secrets/src/backends/file.js +180 -0
  28. package/framework/v0.6.4/lib/secrets/src/env-file.js +220 -0
  29. package/framework/{v0.6.3 → v0.6.4}/lib/secrets/src/main.js +84 -1
  30. package/framework/{v0.6.3 → v0.6.4}/package.json +1 -1
  31. package/gna.js +4 -4
  32. package/llms.txt +7 -7
  33. package/package.json +3 -3
  34. package/schema/settings.json +14 -0
  35. package/framework/v0.6.3/VERSION +0 -1
  36. package/framework/v0.6.3/core/asset/plugin/dist/vendor/gina/css/gina.min.css +0 -1
  37. package/framework/v0.6.3/core/asset/plugin/dist/vendor/gina/css/gina.min.css.br +0 -0
  38. package/framework/v0.6.3/core/asset/plugin/dist/vendor/gina/css/gina.min.css.gz +0 -0
  39. package/framework/v0.6.3/core/asset/plugin/dist/vendor/gina/js/gina.min.js +0 -625
  40. package/framework/v0.6.3/core/asset/plugin/dist/vendor/gina/js/gina.min.js.br +0 -0
  41. package/framework/v0.6.3/core/asset/plugin/dist/vendor/gina/js/gina.min.js.gz +0 -0
  42. package/framework/v0.6.3/core/template/_gitignore +0 -19
  43. package/framework/v0.6.3/lib/cmd/secrets/check.js +0 -507
  44. /package/framework/{v0.6.3 → v0.6.4}/AUTHORS +0 -0
  45. /package/framework/{v0.6.3 → v0.6.4}/LICENSE +0 -0
  46. /package/framework/{v0.6.3 → v0.6.4}/core/asset/html/nolayout.html +0 -0
  47. /package/framework/{v0.6.3 → v0.6.4}/core/asset/html/static.html +0 -0
  48. /package/framework/{v0.6.3 → v0.6.4}/core/asset/img/android-chrome-192x192.png +0 -0
  49. /package/framework/{v0.6.3 → v0.6.4}/core/asset/img/android-chrome-512x512.png +0 -0
  50. /package/framework/{v0.6.3 → v0.6.4}/core/asset/img/apple-touch-icon.png +0 -0
  51. /package/framework/{v0.6.3 → v0.6.4}/core/asset/img/favicon-16x16.png +0 -0
  52. /package/framework/{v0.6.3 → v0.6.4}/core/asset/img/favicon-32x32.png +0 -0
  53. /package/framework/{v0.6.3 → v0.6.4}/core/asset/img/favicon.ico +0 -0
  54. /package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/README.md +0 -0
  55. /package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/dist/vendor/gina/beemaster/beemaster.css +0 -0
  56. /package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/dist/vendor/gina/beemaster/beemaster.js +0 -0
  57. /package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/dist/vendor/gina/beemaster/index.html +0 -0
  58. /package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/dist/vendor/gina/html/statusbar.html +0 -0
  59. /package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/dist/vendor/gina/html/statusbar.html.br +0 -0
  60. /package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/dist/vendor/gina/html/statusbar.html.gz +0 -0
  61. /package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/dist/vendor/gina/inspector/have_heart_one-webfont.woff2 +0 -0
  62. /package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/dist/vendor/gina/inspector/index.html +0 -0
  63. /package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/dist/vendor/gina/inspector/inspector.css +0 -0
  64. /package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/dist/vendor/gina/inspector/inspector.js +0 -0
  65. /package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/dist/vendor/gina/inspector/logo.svg +0 -0
  66. /package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js +0 -0
  67. /package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js.br +0 -0
  68. /package/framework/{v0.6.3 → v0.6.4}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js.gz +0 -0
  69. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/ai/index.js +0 -0
  70. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/ai/lib/connector.js +0 -0
  71. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/couchbase/index.js +0 -0
  72. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/couchbase/lib/connector.js +0 -0
  73. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/couchbase/lib/connector.v3.js +0 -0
  74. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/couchbase/lib/connector.v4.js +0 -0
  75. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/couchbase/lib/n1ql.js +0 -0
  76. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/couchbase/lib/session-store.js +0 -0
  77. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/couchbase/lib/session-store.v3.js +0 -0
  78. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/couchbase/lib/session-store.v4.js +0 -0
  79. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/duckdb/index.js +0 -0
  80. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/duckdb/lib/connector.js +0 -0
  81. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/mongodb/index.js +0 -0
  82. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/mongodb/lib/connector.js +0 -0
  83. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/mongodb/lib/job-store.js +0 -0
  84. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/mongodb/lib/pipeline-loader.js +0 -0
  85. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/mongodb/lib/session-store.js +0 -0
  86. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/mysql/index.js +0 -0
  87. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/mysql/lib/connector.js +0 -0
  88. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/postgresql/index.js +0 -0
  89. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/postgresql/lib/connector.js +0 -0
  90. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/redis/index.js +0 -0
  91. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/redis/lib/connector.js +0 -0
  92. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/redis/lib/job-store.js +0 -0
  93. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/redis/lib/render-cache-store.js +0 -0
  94. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/redis/lib/session-store.js +0 -0
  95. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/scylladb/index.js +0 -0
  96. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/scylladb/lib/connector.js +0 -0
  97. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/scylladb/lib/session-store.js +0 -0
  98. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/sql-parser.js +0 -0
  99. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/sqlite/index.js +0 -0
  100. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/sqlite/lib/connector.js +0 -0
  101. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/sqlite/lib/job-store.js +0 -0
  102. /package/framework/{v0.6.3 → v0.6.4}/core/connectors/sqlite/lib/session-store.js +0 -0
  103. /package/framework/{v0.6.3 → v0.6.4}/core/content.encoding +0 -0
  104. /package/framework/{v0.6.3 → v0.6.4}/core/controller/controller.framework.js +0 -0
  105. /package/framework/{v0.6.3 → v0.6.4}/core/controller/controller.render-json.js +0 -0
  106. /package/framework/{v0.6.3 → v0.6.4}/core/controller/controller.render-nunjucks-async.js +0 -0
  107. /package/framework/{v0.6.3 → v0.6.4}/core/controller/controller.render-stream.js +0 -0
  108. /package/framework/{v0.6.3 → v0.6.4}/core/controller/controller.render-swig-async.js +0 -0
  109. /package/framework/{v0.6.3 → v0.6.4}/core/controller/controller.render-v1.js +0 -0
  110. /package/framework/{v0.6.3 → v0.6.4}/core/controller/index.js +0 -0
  111. /package/framework/{v0.6.3 → v0.6.4}/core/controller/inspector-window-emit.js +0 -0
  112. /package/framework/{v0.6.3 → v0.6.4}/core/controller/release-banner.js +0 -0
  113. /package/framework/{v0.6.3 → v0.6.4}/core/dev/index.js +0 -0
  114. /package/framework/{v0.6.3 → v0.6.4}/core/dev/lib/class.js +0 -0
  115. /package/framework/{v0.6.3 → v0.6.4}/core/dev/lib/factory.js +0 -0
  116. /package/framework/{v0.6.3 → v0.6.4}/core/dev/lib/tools.js +0 -0
  117. /package/framework/{v0.6.3 → v0.6.4}/core/locales/README.md +0 -0
  118. /package/framework/{v0.6.3 → v0.6.4}/core/locales/currency.json +0 -0
  119. /package/framework/{v0.6.3 → v0.6.4}/core/locales/dist/language/en.json +0 -0
  120. /package/framework/{v0.6.3 → v0.6.4}/core/locales/dist/language/fr.json +0 -0
  121. /package/framework/{v0.6.3 → v0.6.4}/core/locales/dist/region/en.json +0 -0
  122. /package/framework/{v0.6.3 → v0.6.4}/core/locales/dist/region/fr.json +0 -0
  123. /package/framework/{v0.6.3 → v0.6.4}/core/locales/index.js +0 -0
  124. /package/framework/{v0.6.3 → v0.6.4}/core/mime.types +0 -0
  125. /package/framework/{v0.6.3 → v0.6.4}/core/model/entity.js +0 -0
  126. /package/framework/{v0.6.3 → v0.6.4}/core/model/index.js +0 -0
  127. /package/framework/{v0.6.3 → v0.6.4}/core/model/template/entityFactory.js +0 -0
  128. /package/framework/{v0.6.3 → v0.6.4}/core/model/template/index.js +0 -0
  129. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/README.md +0 -0
  130. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/index.js +0 -0
  131. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/csrf/README.md +0 -0
  132. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/csrf/package.json +0 -0
  133. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/csrf/src/main.js +0 -0
  134. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/README.md +0 -0
  135. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/coep/README.md +0 -0
  136. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/coep/package.json +0 -0
  137. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/coep/src/main.js +0 -0
  138. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/coop/README.md +0 -0
  139. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/coop/package.json +0 -0
  140. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/coop/src/main.js +0 -0
  141. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/corp/README.md +0 -0
  142. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/corp/package.json +0 -0
  143. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/corp/src/main.js +0 -0
  144. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/csp/README.md +0 -0
  145. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/csp/package.json +0 -0
  146. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/csp/src/main.js +0 -0
  147. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/hide-powered-by/README.md +0 -0
  148. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/hide-powered-by/package.json +0 -0
  149. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/hide-powered-by/src/main.js +0 -0
  150. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/hsts/README.md +0 -0
  151. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/hsts/package.json +0 -0
  152. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/hsts/src/main.js +0 -0
  153. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/origin-agent-cluster/README.md +0 -0
  154. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/origin-agent-cluster/package.json +0 -0
  155. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/origin-agent-cluster/src/main.js +0 -0
  156. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/package.json +0 -0
  157. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/referrer-policy/README.md +0 -0
  158. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/referrer-policy/package.json +0 -0
  159. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/referrer-policy/src/main.js +0 -0
  160. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/src/main.js +0 -0
  161. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-content-type-options/README.md +0 -0
  162. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-content-type-options/package.json +0 -0
  163. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-content-type-options/src/main.js +0 -0
  164. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-dns-prefetch-control/README.md +0 -0
  165. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-dns-prefetch-control/package.json +0 -0
  166. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-dns-prefetch-control/src/main.js +0 -0
  167. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-download-options/README.md +0 -0
  168. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-download-options/package.json +0 -0
  169. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-download-options/src/main.js +0 -0
  170. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-frame-options/README.md +0 -0
  171. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-frame-options/package.json +0 -0
  172. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-frame-options/src/main.js +0 -0
  173. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-permitted-cross-domain-policies/README.md +0 -0
  174. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-permitted-cross-domain-policies/package.json +0 -0
  175. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-permitted-cross-domain-policies/src/main.js +0 -0
  176. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-xss-protection/README.md +0 -0
  177. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-xss-protection/package.json +0 -0
  178. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/security-headers/x-xss-protection/src/main.js +0 -0
  179. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/session/README.md +0 -0
  180. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/session/package.json +0 -0
  181. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/session/src/main.js +0 -0
  182. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/storage/README.md +0 -0
  183. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/storage/build.json +0 -0
  184. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/storage/package.json +0 -0
  185. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/storage/src/main.js +0 -0
  186. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/validator/README.md +0 -0
  187. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/validator/build.json +0 -0
  188. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/validator/package.json +0 -0
  189. /package/framework/{v0.6.3 → v0.6.4}/core/plugins/lib/validator/src/form-validator.js +0 -0
  190. /package/framework/{v0.6.3 → v0.6.4}/core/router.js +0 -0
  191. /package/framework/{v0.6.3 → v0.6.4}/core/server.express.js +0 -0
  192. /package/framework/{v0.6.3 → v0.6.4}/core/server.isaac.js +0 -0
  193. /package/framework/{v0.6.3 → v0.6.4}/core/status.codes +0 -0
  194. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle/config/app.json +0 -0
  195. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle/config/connectors.json +0 -0
  196. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle/config/routing.json +0 -0
  197. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle/config/settings.json +0 -0
  198. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle/config/settings.server.json +0 -0
  199. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle/config/templates.json +0 -0
  200. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle/config/watchers.json +0 -0
  201. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle/controllers/controller.content.js +0 -0
  202. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle/controllers/controller.js +0 -0
  203. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle/controllers/setup.js +0 -0
  204. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle/index.js +0 -0
  205. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle/locales/en.json +0 -0
  206. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_namespace/controllers/controller.js +0 -0
  207. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_public/css/default.css +0 -0
  208. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_public/css/home.css +0 -0
  209. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_public/css/vendor/readme.md +0 -0
  210. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_public/favicon.ico +0 -0
  211. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_public/js/components/x-checklist.js +0 -0
  212. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_public/js/vendor/readme.md +0 -0
  213. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_public/manifest.webmanifest +0 -0
  214. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_public/readme.md +0 -0
  215. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_public/sw.js +0 -0
  216. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_templates/handlers/main.js +0 -0
  217. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_templates/html/content/homepage.html +0 -0
  218. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_templates/html/includes/error-msg-noscript.html +0 -0
  219. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_templates/html/includes/error-msg-outdated-browser.html +0 -0
  220. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_templates/html/includes/x-checklist.html +0 -0
  221. /package/framework/{v0.6.3 → v0.6.4}/core/template/boilerplate/bundle_templates/html/layouts/main.html +0 -0
  222. /package/framework/{v0.6.3 → v0.6.4}/core/template/command/gina.bat.tpl +0 -0
  223. /package/framework/{v0.6.3 → v0.6.4}/core/template/command/gina.tpl +0 -0
  224. /package/framework/{v0.6.3 → v0.6.4}/core/template/conf/env.json +0 -0
  225. /package/framework/{v0.6.3 → v0.6.4}/core/template/conf/manifest.json +0 -0
  226. /package/framework/{v0.6.3 → v0.6.4}/core/template/conf/package.json +0 -0
  227. /package/framework/{v0.6.3 → v0.6.4}/core/template/conf/settings.json +0 -0
  228. /package/framework/{v0.6.3 → v0.6.4}/core/template/conf/statics.json +0 -0
  229. /package/framework/{v0.6.3 → v0.6.4}/core/template/conf/templates.json +0 -0
  230. /package/framework/{v0.6.3 → v0.6.4}/core/template/error/client/json/401.json +0 -0
  231. /package/framework/{v0.6.3 → v0.6.4}/core/template/error/client/json/403.json +0 -0
  232. /package/framework/{v0.6.3 → v0.6.4}/core/template/error/client/json/404.json +0 -0
  233. /package/framework/{v0.6.3 → v0.6.4}/core/template/error/server/html/50x.html +0 -0
  234. /package/framework/{v0.6.3 → v0.6.4}/core/template/error/server/json/500.json +0 -0
  235. /package/framework/{v0.6.3 → v0.6.4}/core/template/error/server/json/503.json +0 -0
  236. /package/framework/{v0.6.3 → v0.6.4}/core/template/extensions/logger/config.json +0 -0
  237. /package/framework/{v0.6.3 → v0.6.4}/helpers/console.js +0 -0
  238. /package/framework/{v0.6.3 → v0.6.4}/helpers/context.js +0 -0
  239. /package/framework/{v0.6.3 → v0.6.4}/helpers/data/LICENSE +0 -0
  240. /package/framework/{v0.6.3 → v0.6.4}/helpers/data/README.md +0 -0
  241. /package/framework/{v0.6.3 → v0.6.4}/helpers/data/package.json +0 -0
  242. /package/framework/{v0.6.3 → v0.6.4}/helpers/data/src/main.js +0 -0
  243. /package/framework/{v0.6.3 → v0.6.4}/helpers/dateFormat.js +0 -0
  244. /package/framework/{v0.6.3 → v0.6.4}/helpers/index.js +0 -0
  245. /package/framework/{v0.6.3 → v0.6.4}/helpers/json/LICENSE +0 -0
  246. /package/framework/{v0.6.3 → v0.6.4}/helpers/json/README.md +0 -0
  247. /package/framework/{v0.6.3 → v0.6.4}/helpers/json/package.json +0 -0
  248. /package/framework/{v0.6.3 → v0.6.4}/helpers/json/src/main.js +0 -0
  249. /package/framework/{v0.6.3 → v0.6.4}/helpers/path.js +0 -0
  250. /package/framework/{v0.6.3 → v0.6.4}/helpers/plugins/README.md +0 -0
  251. /package/framework/{v0.6.3 → v0.6.4}/helpers/plugins/package.json +0 -0
  252. /package/framework/{v0.6.3 → v0.6.4}/helpers/plugins/src/api-error.js +0 -0
  253. /package/framework/{v0.6.3 → v0.6.4}/helpers/plugins/src/main.js +0 -0
  254. /package/framework/{v0.6.3 → v0.6.4}/helpers/prototypes.js +0 -0
  255. /package/framework/{v0.6.3 → v0.6.4}/helpers/task.js +0 -0
  256. /package/framework/{v0.6.3 → v0.6.4}/helpers/text.js +0 -0
  257. /package/framework/{v0.6.3 → v0.6.4}/lib/admin/package.json +0 -0
  258. /package/framework/{v0.6.3 → v0.6.4}/lib/admin/src/main.js +0 -0
  259. /package/framework/{v0.6.3 → v0.6.4}/lib/archiver/README.md +0 -0
  260. /package/framework/{v0.6.3 → v0.6.4}/lib/archiver/build.json +0 -0
  261. /package/framework/{v0.6.3 → v0.6.4}/lib/archiver/package.json +0 -0
  262. /package/framework/{v0.6.3 → v0.6.4}/lib/archiver/src/dep/jszip.min.js +0 -0
  263. /package/framework/{v0.6.3 → v0.6.4}/lib/archiver/src/main.js +0 -0
  264. /package/framework/{v0.6.3 → v0.6.4}/lib/async/package.json +0 -0
  265. /package/framework/{v0.6.3 → v0.6.4}/lib/async/src/main.js +0 -0
  266. /package/framework/{v0.6.3 → v0.6.4}/lib/audit/package.json +0 -0
  267. /package/framework/{v0.6.3 → v0.6.4}/lib/audit/src/main.js +0 -0
  268. /package/framework/{v0.6.3 → v0.6.4}/lib/audit-store.js +0 -0
  269. /package/framework/{v0.6.3 → v0.6.4}/lib/authn/package.json +0 -0
  270. /package/framework/{v0.6.3 → v0.6.4}/lib/authn/src/lockout.js +0 -0
  271. /package/framework/{v0.6.3 → v0.6.4}/lib/authn/src/main.js +0 -0
  272. /package/framework/{v0.6.3 → v0.6.4}/lib/authn/src/totp.js +0 -0
  273. /package/framework/{v0.6.3 → v0.6.4}/lib/authz-gate/package.json +0 -0
  274. /package/framework/{v0.6.3 → v0.6.4}/lib/authz-gate/src/main.js +0 -0
  275. /package/framework/{v0.6.3 → v0.6.4}/lib/cache/README.md +0 -0
  276. /package/framework/{v0.6.3 → v0.6.4}/lib/cache/build.json +0 -0
  277. /package/framework/{v0.6.3 → v0.6.4}/lib/cache/package.json +0 -0
  278. /package/framework/{v0.6.3 → v0.6.4}/lib/cache/src/main.js +0 -0
  279. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/aliases.json +0 -0
  280. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/audit/arguments.json +0 -0
  281. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/audit/help.txt +0 -0
  282. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/audit/verify.js +0 -0
  283. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/add.js +0 -0
  284. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/arguments.json +0 -0
  285. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/build.js +0 -0
  286. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/copy.js +0 -0
  287. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/cp.js +0 -0
  288. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/help.js +0 -0
  289. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/help.txt +0 -0
  290. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/inc/name-rewrite.js +0 -0
  291. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/list.js +0 -0
  292. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/man.js +0 -0
  293. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/mcp-start.js +0 -0
  294. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/mcp.js +0 -0
  295. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/oas.js +0 -0
  296. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/openapi.js +0 -0
  297. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/remove.js +0 -0
  298. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/rename.js +0 -0
  299. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/restart.js +0 -0
  300. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/rm.js +0 -0
  301. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/start.js +0 -0
  302. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/status.js +0 -0
  303. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/stop.js +0 -0
  304. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/bundle/types.js +0 -0
  305. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/cache/arguments.json +0 -0
  306. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/cache/clear.js +0 -0
  307. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/cache/help.txt +0 -0
  308. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/cache/stats.js +0 -0
  309. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/connector/add.js +0 -0
  310. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/connector/arguments.json +0 -0
  311. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/connector/help.js +0 -0
  312. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/connector/help.txt +0 -0
  313. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/connector/infer.js +0 -0
  314. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/connector/list.js +0 -0
  315. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/connector/migrate.js +0 -0
  316. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/connector/models.js +0 -0
  317. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/connector/remove.js +0 -0
  318. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/connector/rm.js +0 -0
  319. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/connector/test.js +0 -0
  320. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/container/arguments.json +0 -0
  321. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/container/help.js +0 -0
  322. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/container/help.txt +0 -0
  323. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/container/man.js +0 -0
  324. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/container/ps.js +0 -0
  325. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/container/stop.js +0 -0
  326. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/controller/add.js +0 -0
  327. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/controller/arguments.json +0 -0
  328. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/controller/help.txt +0 -0
  329. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/controller/inc/args.js +0 -0
  330. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/controller/inc/namespace.js +0 -0
  331. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/controller/inc/reference-rewrite.js +0 -0
  332. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/controller/inc/reference-scan.js +0 -0
  333. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/controller/inc/scaffold.js +0 -0
  334. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/controller/remove.js +0 -0
  335. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/controller/rename.js +0 -0
  336. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/controller/rm.js +0 -0
  337. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/env/add.js +0 -0
  338. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/env/get.js +0 -0
  339. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/env/help.js +0 -0
  340. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/env/help.txt +0 -0
  341. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/env/link-dev.js +0 -0
  342. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/env/list.js +0 -0
  343. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/env/remove.js +0 -0
  344. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/env/rm.js +0 -0
  345. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/env/set.js +0 -0
  346. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/env/unset.js +0 -0
  347. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/env/use.js +0 -0
  348. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/add.js +0 -0
  349. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/arguments.json +0 -0
  350. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/build.js +0 -0
  351. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/dot.js +0 -0
  352. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/get.js +0 -0
  353. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/help.js +0 -0
  354. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/help.txt +0 -0
  355. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/init.js +0 -0
  356. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/link-node-modules.js +0 -0
  357. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/link.js +0 -0
  358. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/list.js +0 -0
  359. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/man.js +0 -0
  360. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/msg.json +0 -0
  361. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/open.js +0 -0
  362. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/remove.js +0 -0
  363. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/reset.js +0 -0
  364. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/restart.js +0 -0
  365. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/set.js +0 -0
  366. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/start.js +0 -0
  367. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/status.js +0 -0
  368. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/stop.js +0 -0
  369. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/tail.js +0 -0
  370. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/update.js +0 -0
  371. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/framework/version.js +0 -0
  372. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/gina-dev.1.md +0 -0
  373. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/gina-framework.1.md +0 -0
  374. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/gina.1.md +0 -0
  375. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/helper.js +0 -0
  376. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/i18n/add.js +0 -0
  377. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/i18n/arguments.json +0 -0
  378. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/i18n/export.js +0 -0
  379. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/i18n/help.js +0 -0
  380. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/i18n/help.txt +0 -0
  381. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/i18n/import.js +0 -0
  382. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/i18n/scan.js +0 -0
  383. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/image/_host.js +0 -0
  384. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/image/arguments.json +0 -0
  385. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/image/build.js +0 -0
  386. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/image/help.js +0 -0
  387. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/image/help.txt +0 -0
  388. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/image/list.js +0 -0
  389. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/image/man.js +0 -0
  390. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/image/rm.js +0 -0
  391. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/image/run.js +0 -0
  392. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/index.js +0 -0
  393. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/inspector/help.js +0 -0
  394. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/inspector/help.txt +0 -0
  395. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/inspector/open.js +0 -0
  396. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/man-render.js +0 -0
  397. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/minion/arguments.json +0 -0
  398. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/minion/help.js +0 -0
  399. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/minion/help.txt +0 -0
  400. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/minion/kill.js +0 -0
  401. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/minion/list.js +0 -0
  402. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/msg.json +0 -0
  403. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/port/help.js +0 -0
  404. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/port/help.txt +0 -0
  405. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/port/inc/scan.js +0 -0
  406. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/port/list.js +0 -0
  407. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/port/reset.js +0 -0
  408. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/port/set.js +0 -0
  409. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/add.js +0 -0
  410. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/arguments.json +0 -0
  411. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/backup.js +0 -0
  412. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/build.js +0 -0
  413. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/help.js +0 -0
  414. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/help.txt +0 -0
  415. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/import.js +0 -0
  416. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/list.js +0 -0
  417. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/man.js +0 -0
  418. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/move.js +0 -0
  419. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/remove.js +0 -0
  420. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/rename.js +0 -0
  421. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/restart.js +0 -0
  422. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/restore.js +0 -0
  423. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/rm.js +0 -0
  424. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/start.js +0 -0
  425. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/status.js +0 -0
  426. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/project/stop.js +0 -0
  427. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/protocol/arguments.json +0 -0
  428. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/protocol/help.js +0 -0
  429. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/protocol/help.txt +0 -0
  430. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/protocol/list.js +0 -0
  431. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/protocol/remove.js +0 -0
  432. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/protocol/set.js +0 -0
  433. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/scope/add.js +0 -0
  434. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/scope/help.js +0 -0
  435. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/scope/help.txt +0 -0
  436. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/scope/link-local.js +0 -0
  437. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/scope/link-production.js +0 -0
  438. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/scope/list.js +0 -0
  439. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/scope/remove.js +0 -0
  440. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/scope/rm.js +0 -0
  441. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/scope/use.js +0 -0
  442. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/secrets/help.js +0 -0
  443. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/secrets/scan.js +0 -0
  444. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/service/help.js +0 -0
  445. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/service/help.txt +0 -0
  446. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/service/list.js +0 -0
  447. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/service/man.js +0 -0
  448. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/service/start.js +0 -0
  449. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd/view/add.js +0 -0
  450. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd-status-format/package.json +0 -0
  451. /package/framework/{v0.6.3 → v0.6.4}/lib/cmd-status-format/src/main.js +0 -0
  452. /package/framework/{v0.6.3 → v0.6.4}/lib/collection/README.md +0 -0
  453. /package/framework/{v0.6.3 → v0.6.4}/lib/collection/build.json +0 -0
  454. /package/framework/{v0.6.3 → v0.6.4}/lib/collection/package.json +0 -0
  455. /package/framework/{v0.6.3 → v0.6.4}/lib/collection/src/main.js +0 -0
  456. /package/framework/{v0.6.3 → v0.6.4}/lib/config.js +0 -0
  457. /package/framework/{v0.6.3 → v0.6.4}/lib/connector-config/package.json +0 -0
  458. /package/framework/{v0.6.3 → v0.6.4}/lib/connector-config/src/main.js +0 -0
  459. /package/framework/{v0.6.3 → v0.6.4}/lib/connector-error/package.json +0 -0
  460. /package/framework/{v0.6.3 → v0.6.4}/lib/connector-error/src/main.js +0 -0
  461. /package/framework/{v0.6.3 → v0.6.4}/lib/connector-registry/package.json +0 -0
  462. /package/framework/{v0.6.3 → v0.6.4}/lib/connector-registry/src/main.js +0 -0
  463. /package/framework/{v0.6.3 → v0.6.4}/lib/cron/README.md +0 -0
  464. /package/framework/{v0.6.3 → v0.6.4}/lib/cron/package.json +0 -0
  465. /package/framework/{v0.6.3 → v0.6.4}/lib/cron/src/main.js +0 -0
  466. /package/framework/{v0.6.3 → v0.6.4}/lib/domain/LICENSE +0 -0
  467. /package/framework/{v0.6.3 → v0.6.4}/lib/domain/README.md +0 -0
  468. /package/framework/{v0.6.3 → v0.6.4}/lib/domain/package.json +0 -0
  469. /package/framework/{v0.6.3 → v0.6.4}/lib/domain/src/main.js +0 -0
  470. /package/framework/{v0.6.3 → v0.6.4}/lib/dto/package.json +0 -0
  471. /package/framework/{v0.6.3 → v0.6.4}/lib/dto/src/main.js +0 -0
  472. /package/framework/{v0.6.3 → v0.6.4}/lib/dto-pipe/package.json +0 -0
  473. /package/framework/{v0.6.3 → v0.6.4}/lib/dto-pipe/src/main.js +0 -0
  474. /package/framework/{v0.6.3 → v0.6.4}/lib/dto-types/package.json +0 -0
  475. /package/framework/{v0.6.3 → v0.6.4}/lib/dto-types/src/main.js +0 -0
  476. /package/framework/{v0.6.3 → v0.6.4}/lib/generator/index.js +0 -0
  477. /package/framework/{v0.6.3 → v0.6.4}/lib/i18n/package.json +0 -0
  478. /package/framework/{v0.6.3 → v0.6.4}/lib/i18n/src/main.js +0 -0
  479. /package/framework/{v0.6.3 → v0.6.4}/lib/image-build/package.json +0 -0
  480. /package/framework/{v0.6.3 → v0.6.4}/lib/image-build/src/main.js +0 -0
  481. /package/framework/{v0.6.3 → v0.6.4}/lib/index.js +0 -0
  482. /package/framework/{v0.6.3 → v0.6.4}/lib/inherits/LICENSE +0 -0
  483. /package/framework/{v0.6.3 → v0.6.4}/lib/inherits/README.md +0 -0
  484. /package/framework/{v0.6.3 → v0.6.4}/lib/inherits/package.json +0 -0
  485. /package/framework/{v0.6.3 → v0.6.4}/lib/inherits/src/main.js +0 -0
  486. /package/framework/{v0.6.3 → v0.6.4}/lib/inspector-events/package.json +0 -0
  487. /package/framework/{v0.6.3 → v0.6.4}/lib/inspector-events/src/main.js +0 -0
  488. /package/framework/{v0.6.3 → v0.6.4}/lib/inspector-redact/package.json +0 -0
  489. /package/framework/{v0.6.3 → v0.6.4}/lib/inspector-redact/src/main.js +0 -0
  490. /package/framework/{v0.6.3 → v0.6.4}/lib/instrument/package.json +0 -0
  491. /package/framework/{v0.6.3 → v0.6.4}/lib/instrument/src/main.js +0 -0
  492. /package/framework/{v0.6.3 → v0.6.4}/lib/job/package.json +0 -0
  493. /package/framework/{v0.6.3 → v0.6.4}/lib/job/src/main.js +0 -0
  494. /package/framework/{v0.6.3 → v0.6.4}/lib/job-store.js +0 -0
  495. /package/framework/{v0.6.3 → v0.6.4}/lib/json-config-header/package.json +0 -0
  496. /package/framework/{v0.6.3 → v0.6.4}/lib/json-config-header/src/main.js +0 -0
  497. /package/framework/{v0.6.3 → v0.6.4}/lib/logger/README.md +0 -0
  498. /package/framework/{v0.6.3 → v0.6.4}/lib/logger/package.json +0 -0
  499. /package/framework/{v0.6.3 → v0.6.4}/lib/logger/src/containers/default/index.js +0 -0
  500. /package/framework/{v0.6.3 → v0.6.4}/lib/logger/src/containers/file/index.js +0 -0
  501. /package/framework/{v0.6.3 → v0.6.4}/lib/logger/src/containers/file/lib/logrotator/README.md +0 -0
  502. /package/framework/{v0.6.3 → v0.6.4}/lib/logger/src/containers/file/lib/logrotator/index.js +0 -0
  503. /package/framework/{v0.6.3 → v0.6.4}/lib/logger/src/containers/mq/index.js +0 -0
  504. /package/framework/{v0.6.3 → v0.6.4}/lib/logger/src/helper.js +0 -0
  505. /package/framework/{v0.6.3 → v0.6.4}/lib/logger/src/main.js +0 -0
  506. /package/framework/{v0.6.3 → v0.6.4}/lib/math/index.js +0 -0
  507. /package/framework/{v0.6.3 → v0.6.4}/lib/mcp-dispatch/package.json +0 -0
  508. /package/framework/{v0.6.3 → v0.6.4}/lib/mcp-dispatch/src/main.js +0 -0
  509. /package/framework/{v0.6.3 → v0.6.4}/lib/mcp-http/package.json +0 -0
  510. /package/framework/{v0.6.3 → v0.6.4}/lib/mcp-http/src/main.js +0 -0
  511. /package/framework/{v0.6.3 → v0.6.4}/lib/mcp-server/package.json +0 -0
  512. /package/framework/{v0.6.3 → v0.6.4}/lib/mcp-server/src/main.js +0 -0
  513. /package/framework/{v0.6.3 → v0.6.4}/lib/merge/README.md +0 -0
  514. /package/framework/{v0.6.3 → v0.6.4}/lib/merge/package.json +0 -0
  515. /package/framework/{v0.6.3 → v0.6.4}/lib/merge/src/main.js +0 -0
  516. /package/framework/{v0.6.3 → v0.6.4}/lib/metrics/package.json +0 -0
  517. /package/framework/{v0.6.3 → v0.6.4}/lib/metrics/src/main.js +0 -0
  518. /package/framework/{v0.6.3 → v0.6.4}/lib/model.js +0 -0
  519. /package/framework/{v0.6.3 → v0.6.4}/lib/net-locality/package.json +0 -0
  520. /package/framework/{v0.6.3 → v0.6.4}/lib/net-locality/src/main.js +0 -0
  521. /package/framework/{v0.6.3 → v0.6.4}/lib/nunjucks-filters/README.md +0 -0
  522. /package/framework/{v0.6.3 → v0.6.4}/lib/nunjucks-filters/package.json +0 -0
  523. /package/framework/{v0.6.3 → v0.6.4}/lib/nunjucks-filters/src/main.js +0 -0
  524. /package/framework/{v0.6.3 → v0.6.4}/lib/nunjucks-resolver/package.json +0 -0
  525. /package/framework/{v0.6.3 → v0.6.4}/lib/nunjucks-resolver/src/main.js +0 -0
  526. /package/framework/{v0.6.3 → v0.6.4}/lib/proc.js +0 -0
  527. /package/framework/{v0.6.3 → v0.6.4}/lib/release-watch/package.json +0 -0
  528. /package/framework/{v0.6.3 → v0.6.4}/lib/release-watch/src/main.js +0 -0
  529. /package/framework/{v0.6.3 → v0.6.4}/lib/render-cache/package.json +0 -0
  530. /package/framework/{v0.6.3 → v0.6.4}/lib/render-cache/src/main.js +0 -0
  531. /package/framework/{v0.6.3 → v0.6.4}/lib/render-cache-store.js +0 -0
  532. /package/framework/{v0.6.3 → v0.6.4}/lib/routing/README.md +0 -0
  533. /package/framework/{v0.6.3 → v0.6.4}/lib/routing/build.json +0 -0
  534. /package/framework/{v0.6.3 → v0.6.4}/lib/routing/package.json +0 -0
  535. /package/framework/{v0.6.3 → v0.6.4}/lib/routing/src/main.js +0 -0
  536. /package/framework/{v0.6.3 → v0.6.4}/lib/routing/src/radix.js +0 -0
  537. /package/framework/{v0.6.3 → v0.6.4}/lib/routing-introspect/package.json +0 -0
  538. /package/framework/{v0.6.3 → v0.6.4}/lib/routing-introspect/src/main.js +0 -0
  539. /package/framework/{v0.6.3 → v0.6.4}/lib/secrets/package.json +0 -0
  540. /package/framework/{v0.6.3 → v0.6.4}/lib/secrets/src/backends/env.js +0 -0
  541. /package/framework/{v0.6.3 → v0.6.4}/lib/session-store.js +0 -0
  542. /package/framework/{v0.6.3 → v0.6.4}/lib/shell.js +0 -0
  543. /package/framework/{v0.6.3 → v0.6.4}/lib/sqlite-driver.js +0 -0
  544. /package/framework/{v0.6.3 → v0.6.4}/lib/state.js +0 -0
  545. /package/framework/{v0.6.3 → v0.6.4}/lib/swig-filters/README.md +0 -0
  546. /package/framework/{v0.6.3 → v0.6.4}/lib/swig-filters/package.json +0 -0
  547. /package/framework/{v0.6.3 → v0.6.4}/lib/swig-filters/src/main.js +0 -0
  548. /package/framework/{v0.6.3 → v0.6.4}/lib/swig-resolver/package.json +0 -0
  549. /package/framework/{v0.6.3 → v0.6.4}/lib/swig-resolver/src/main.js +0 -0
  550. /package/framework/{v0.6.3 → v0.6.4}/lib/template-loaders/package.json +0 -0
  551. /package/framework/{v0.6.3 → v0.6.4}/lib/template-loaders/src/loaders/http.js +0 -0
  552. /package/framework/{v0.6.3 → v0.6.4}/lib/template-loaders/src/loaders/memory.js +0 -0
  553. /package/framework/{v0.6.3 → v0.6.4}/lib/template-loaders/src/main.js +0 -0
  554. /package/framework/{v0.6.3 → v0.6.4}/lib/url/README.md +0 -0
  555. /package/framework/{v0.6.3 → v0.6.4}/lib/url/index.js +0 -0
  556. /package/framework/{v0.6.3 → v0.6.4}/lib/url/routing.json +0 -0
  557. /package/framework/{v0.6.3 → v0.6.4}/lib/uuid/package.json +0 -0
  558. /package/framework/{v0.6.3 → v0.6.4}/lib/uuid/src/main.js +0 -0
  559. /package/framework/{v0.6.3 → v0.6.4}/lib/validator.js +0 -0
  560. /package/framework/{v0.6.3 → v0.6.4}/lib/watcher/package.json +0 -0
  561. /package/framework/{v0.6.3 → v0.6.4}/lib/watcher/src/main.js +0 -0
  562. /package/framework/{v0.6.3 → v0.6.4}/lib/ws-framing/package.json +0 -0
  563. /package/framework/{v0.6.3 → v0.6.4}/lib/ws-framing/src/main.js +0 -0
  564. /package/framework/{v0.6.3 → v0.6.4}/lib/ws-query/package.json +0 -0
  565. /package/framework/{v0.6.3 → v0.6.4}/lib/ws-query/src/main.js +0 -0
  566. /package/framework/{v0.6.3 → v0.6.4}/lib/ws-session/package.json +0 -0
  567. /package/framework/{v0.6.3 → v0.6.4}/lib/ws-session/src/main.js +0 -0
@@ -9274,10 +9274,378 @@ if ( _isNode ) {
9274
9274
  });
9275
9275
  }
9276
9276
  ;
9277
- define('gina/link', [ 'require', 'lib/domain', 'lib/merge', 'lib/uuid', 'utils/events' ], function (require) {
9277
+
9278
+ /**
9279
+ * This file is part of the gina package.
9280
+ * Copyright (c) 2009-2026 Rhinostone <contact@gina.io>
9281
+ *
9282
+ * For the full copyright and license information, please view the LICENSE
9283
+ * file that was distributed with this source code.
9284
+ */
9285
+
9286
+ /**
9287
+ * @module lib/loading-state
9288
+ * @description Framework-owned loading state for submit-like triggers. Works in
9289
+ * Node.js (CommonJS) and browser (AMD / GFF) contexts.
9290
+ *
9291
+ * A submit-like trigger — `<button type="submit">`, `<input type="submit">` or an
9292
+ * `<a data-gina-form-submit>` — carries `data-gina-loading="true"` for exactly as
9293
+ * long as the action it started is running, and `"false"` once that action
9294
+ * completes OR is interrupted. Consumers style the running state with CSS.
9295
+ *
9296
+ * The module owns three things and no more: resolving the configured attribute
9297
+ * NAME, writing the two values, and walking a click target up to the trigger that
9298
+ * actually owns it. It holds NO state — the caller decides which element is armed
9299
+ * and where to remember it, so the validator, popin and link plugins can share one
9300
+ * implementation without sharing a registry.
9301
+ *
9302
+ * ## The `"false"` value is deliberate, and it constrains readers
9303
+ *
9304
+ * `disarm()` writes the STRING `"false"`; it does not remove the attribute. That
9305
+ * diverges on purpose from the form-scoped `data-gina-form-loading`, which #B175
9306
+ * changed from `setAttribute(..., false)` to `removeAttribute()` precisely because
9307
+ * `"false"` is attribute-PRESENT and therefore truthy for a presence check.
9308
+ *
9309
+ * The consequence is a hard rule for every reader, in the framework and in
9310
+ * consumer CSS alike: **match on the VALUE, never on presence**. Use
9311
+ * `[data-gina-loading="true"]`, never a bare `[data-gina-loading]`, and in JS use
9312
+ * {@link module:lib/loading-state~isArmed} rather than reading the attribute
9313
+ * directly. A bare presence selector matches a disarmed trigger and pins the
9314
+ * loading style on forever.
9315
+ *
9316
+ * ## Configuring the attribute name
9317
+ *
9318
+ * The name is read from `gina.config.loadingAttribute`, lazily, at every call —
9319
+ * `gina.config` is populated after the bundle loads (via `setOptions`), so a value
9320
+ * captured at module-definition time would be stale. A project already built on
9321
+ * `data-loading` opts in with:
9322
+ *
9323
+ * ```html
9324
+ * <script src="/js/vendor/gina/gina.min.js"
9325
+ * data-gina-config="{ loadingAttribute: 'data-loading' }"></script>
9326
+ * ```
9327
+ *
9328
+ * or `gina.setOptions({ loadingAttribute: 'data-loading' })`. There is no
9329
+ * auto-detection: a DOM probe reads nothing at rest (the attribute only exists
9330
+ * while something is loading) and a stylesheet probe races late-loaded sheets and
9331
+ * throws on cross-origin ones. Configuration is explicit by design.
9332
+ *
9333
+ * @example
9334
+ * var loadingState = require('lib/loading-state');
9335
+ *
9336
+ * var $trigger = loadingState.resolveTrigger(event.target, $form);
9337
+ * loadingState.arm($trigger); // data-gina-loading="true"
9338
+ * // ... the action runs, then settles, errors, times out or is rejected ...
9339
+ * loadingState.disarm($trigger); // data-gina-loading="false"
9340
+ */
9341
+
9342
+ /**
9343
+ * Loading-state factory — returns the stateless API object.
9344
+ * Exported as the module itself via `module.exports = LoadingState()`.
9345
+ *
9346
+ * @function LoadingState
9347
+ * @returns {object} The `{ arm, disarm, isArmed, getAttributeName, resolveTrigger, DEFAULT_ATTRIBUTE }` API
9348
+ *
9349
+ * @memberof module:lib
9350
+ * @author Rhinostone <contact@gina.io>
9351
+ * @api Public
9352
+ */
9353
+ function LoadingState() {
9354
+
9355
+ /**
9356
+ * The attribute written when no `gina.config.loadingAttribute` override is set.
9357
+ *
9358
+ * @constant {string}
9359
+ * @inner
9360
+ */
9361
+ var DEFAULT_ATTRIBUTE = 'data-gina-loading';
9362
+
9363
+ /**
9364
+ * The value written by `arm()`.
9365
+ *
9366
+ * @constant {string}
9367
+ * @inner
9368
+ */
9369
+ var ARMED = 'true';
9370
+
9371
+ /**
9372
+ * The value written by `disarm()`. NOT `removeAttribute` — see the module
9373
+ * description for why, and for the presence-check rule it imposes on readers.
9374
+ *
9375
+ * @constant {string}
9376
+ * @inner
9377
+ */
9378
+ var DISARMED = 'false';
9379
+
9380
+ /**
9381
+ * How many ancestors `resolveTrigger()` will climb before giving up. A submit
9382
+ * trigger is a direct child of its form in every realistic markup, so this is a
9383
+ * runaway guard rather than a real limit — a detached or cyclic node can not
9384
+ * spin the loop.
9385
+ *
9386
+ * @constant {number}
9387
+ * @inner
9388
+ */
9389
+ var MAX_DEPTH = 10;
9390
+
9391
+ /**
9392
+ * isNode
9393
+ *
9394
+ * True when `$el` can carry attributes. Guards every public entry point so a
9395
+ * `null`, a text node, a `window` or a plain object is a silent no-op rather
9396
+ * than a thrown TypeError inside an event handler.
9397
+ *
9398
+ * @param {object} $el - candidate element
9399
+ *
9400
+ * @returns {boolean} true when `$el` exposes the attribute API
9401
+ *
9402
+ * @example
9403
+ * isNode(document.querySelector('button')); // true
9404
+ * isNode(null); // false
9405
+ *
9406
+ * @inner
9407
+ */
9408
+ var isNode = function($el) {
9409
+ return !!(
9410
+ $el
9411
+ && typeof($el.setAttribute) == 'function'
9412
+ && typeof($el.getAttribute) == 'function'
9413
+ );
9414
+ };
9415
+
9416
+ /**
9417
+ * isSubmitTrigger
9418
+ *
9419
+ * True when `$el` is an element a user submits a form WITH. Mirrors the shapes
9420
+ * the validator's click proxy already recognises: a `button`/`input` whose type
9421
+ * is `submit`, or an anchor carrying `data-gina-form-submit`.
9422
+ *
9423
+ * @param {object} $el - candidate element
9424
+ *
9425
+ * @returns {boolean} true when `$el` is a submit-like trigger
9426
+ *
9427
+ * @example
9428
+ * isSubmitTrigger($button); // true for <button type="submit">
9429
+ * isSubmitTrigger($span); // false
9430
+ *
9431
+ * @inner
9432
+ */
9433
+ var isSubmitTrigger = function($el) {
9434
+ if ( !isNode($el) || !$el.tagName ) {
9435
+ return false;
9436
+ }
9437
+ if ( /^(button|input)$/i.test($el.tagName) && /^submit$/i.test($el.type || '') ) {
9438
+ return true;
9439
+ }
9440
+ if ( /^a$/i.test($el.tagName) && $el.getAttribute('data-gina-form-submit') != null ) {
9441
+ return true;
9442
+ }
9443
+ return false;
9444
+ };
9445
+
9446
+ /**
9447
+ * getAttributeName
9448
+ *
9449
+ * Resolves the attribute name to write. Reads `gina.config.loadingAttribute`
9450
+ * LAZILY on every call: `gina.config` is populated after the bundle loads, so a
9451
+ * value captured once at definition time would always be the default. Falls
9452
+ * back to `data-gina-loading` when unset, empty or not a string.
9453
+ *
9454
+ * @returns {string} the configured attribute name, or `data-gina-loading`
9455
+ *
9456
+ * @example
9457
+ * getAttributeName(); // 'data-gina-loading'
9458
+ * gina.setOptions({ loadingAttribute: 'data-loading' });
9459
+ * getAttributeName(); // 'data-loading'
9460
+ *
9461
+ * @api Public
9462
+ */
9463
+ var getAttributeName = function() {
9464
+ if (
9465
+ typeof(gina) != 'undefined' && gina && gina.config
9466
+ && typeof(gina.config.loadingAttribute) == 'string'
9467
+ && gina.config.loadingAttribute !== ''
9468
+ ) {
9469
+ return gina.config.loadingAttribute;
9470
+ }
9471
+ return DEFAULT_ATTRIBUTE;
9472
+ };
9473
+
9474
+ /**
9475
+ * arm
9476
+ *
9477
+ * Marks `$el` as running by writing `<attribute>="true"`. Idempotent — arming an
9478
+ * already-armed trigger rewrites the same value. A non-element is a no-op.
9479
+ *
9480
+ * @param {object} $el - the trigger to mark (see `resolveTrigger`)
9481
+ *
9482
+ * @returns {boolean} true when the attribute was written
9483
+ *
9484
+ * @example
9485
+ * arm($button); // $button now carries data-gina-loading="true"
9486
+ *
9487
+ * @api Public
9488
+ */
9489
+ var arm = function($el) {
9490
+ if ( !isNode($el) ) {
9491
+ return false;
9492
+ }
9493
+ $el.setAttribute(getAttributeName(), ARMED);
9494
+ return true;
9495
+ };
9496
+
9497
+ /**
9498
+ * disarm
9499
+ *
9500
+ * Releases `$el` by writing `<attribute>="false"`. Called on EVERY terminal
9501
+ * outcome — settled, errored, timed out, aborted, rate-limited and
9502
+ * validation-rejected — so a trigger can not stay visually loading after its
9503
+ * action stopped. Idempotent; a non-element is a no-op.
9504
+ *
9505
+ * Writes `"false"` rather than removing the attribute; see the module
9506
+ * description for the reasoning and the presence-check rule it imposes.
9507
+ *
9508
+ * @param {object} $el - the trigger to release
9509
+ *
9510
+ * @returns {boolean} true when the attribute was written
9511
+ *
9512
+ * @example
9513
+ * disarm($button); // $button now carries data-gina-loading="false"
9514
+ *
9515
+ * @api Public
9516
+ */
9517
+ var disarm = function($el) {
9518
+ if ( !isNode($el) ) {
9519
+ return false;
9520
+ }
9521
+ $el.setAttribute(getAttributeName(), DISARMED);
9522
+ return true;
9523
+ };
9524
+
9525
+ /**
9526
+ * isArmed
9527
+ *
9528
+ * True only when `$el` is currently marked as running. Compares the VALUE
9529
+ * against `"true"` — never tests presence, because a disarmed trigger still
9530
+ * carries the attribute with `"false"`. Use this instead of reading the
9531
+ * attribute directly.
9532
+ *
9533
+ * @param {object} $el - the trigger to inspect
9534
+ *
9535
+ * @returns {boolean} true when `$el` carries `<attribute>="true"`
9536
+ *
9537
+ * @example
9538
+ * arm($button); isArmed($button); // true
9539
+ * disarm($button); isArmed($button); // false — attribute present, value "false"
9540
+ *
9541
+ * @api Public
9542
+ */
9543
+ var isArmed = function($el) {
9544
+ if ( !isNode($el) ) {
9545
+ return false;
9546
+ }
9547
+ return $el.getAttribute(getAttributeName()) === ARMED;
9548
+ };
9549
+
9550
+ /**
9551
+ * resolveTrigger
9552
+ *
9553
+ * Walks `$el` up to the submit trigger that owns it, so the state lands on the
9554
+ * control the user perceives as the button rather than on whatever inner node
9555
+ * happened to receive the click. `<button type="submit"><span>Save</span></button>`
9556
+ * clicked on the label yields the `button`, not the `span`.
9557
+ *
9558
+ * Returns `$el` itself when no submit-like ancestor is found, so a caller that
9559
+ * already knows it is handling a submit still gets a usable element. Stops at
9560
+ * the boundary element (typically the form), at `form`/`body`/`html`, or after
9561
+ * `MAX_DEPTH` hops.
9562
+ *
9563
+ * ## Recognising a non-submit trigger (`isTrigger`)
9564
+ *
9565
+ * The default predicate recognises submit-like controls only — a `button` or
9566
+ * `input` of type `submit`, or an `a[data-gina-form-submit]`. That is what the
9567
+ * validator needs and nothing else. Other plugins arm elements this module has
9568
+ * no business knowing about: link arms `a[data-gina-link]`, popin arms
9569
+ * `a[data-gina-dialog]`. Pass `isTrigger` to say what counts as YOUR trigger.
9570
+ *
9571
+ * Without it the climb finds no match, breaks at `form`/`body`/`html`, and
9572
+ * returns the ORIGINAL `$el` — so a click on the `<span>` inside
9573
+ * `<a data-gina-link><span>Go</span></a>` would arm the span and leave the
9574
+ * anchor unstyled.
9575
+ *
9576
+ * The predicate lives with the caller deliberately rather than being folded
9577
+ * into the default: this module owns the WALK, each plugin owns its own notion
9578
+ * of a trigger. Folding plugin attribute names in here would also couple the
9579
+ * module to the popin deprecation/alias work, which changes which attributes
9580
+ * are honoured over time.
9581
+ *
9582
+ * @param {object} $el - the clicked element (`event.target`)
9583
+ * @param {object} [$boundary] - element to stop at, usually the `<form>`
9584
+ * @param {function} [isTrigger] - `($node) => boolean`; defaults to the
9585
+ * submit-like predicate. Ignored when not a function. A predicate that
9586
+ * THROWS is deliberately not caught: every caller is framework code, so
9587
+ * swallowing would hide a framework bug behind a silently unarmed trigger.
9588
+ *
9589
+ * @returns {object} the owning trigger, or `$el`, or `null` when `$el` is not an element
9590
+ *
9591
+ * @example
9592
+ * // <button type="submit"><span>Save</span></button>
9593
+ * resolveTrigger(event.target, $form); // the <button>, even when the <span> was clicked
9594
+ *
9595
+ * @example
9596
+ * // <a data-gina-link><span>Go</span></a> — the span was clicked
9597
+ * resolveTrigger(event.target, null, function($n) {
9598
+ * return /^a$/i.test($n.tagName) && $n.getAttribute('data-gina-link') != null;
9599
+ * }); // the <a>, not the <span>
9600
+ *
9601
+ * @api Public
9602
+ */
9603
+ var resolveTrigger = function($el, $boundary, isTrigger) {
9604
+ if ( !isNode($el) ) {
9605
+ return null;
9606
+ }
9607
+ var matches = ( typeof(isTrigger) == 'function' ) ? isTrigger : isSubmitTrigger;
9608
+ var $node = $el;
9609
+ var depth = 0;
9610
+ while ( isNode($node) && depth < MAX_DEPTH ) {
9611
+ if ( matches($node) ) {
9612
+ return $node;
9613
+ }
9614
+ if ( $boundary && $node === $boundary ) {
9615
+ break;
9616
+ }
9617
+ if ( $node.tagName && /^(form|body|html)$/i.test($node.tagName) ) {
9618
+ break;
9619
+ }
9620
+ $node = $node.parentNode;
9621
+ ++depth;
9622
+ }
9623
+ return $el;
9624
+ };
9625
+
9626
+ return {
9627
+ 'DEFAULT_ATTRIBUTE' : DEFAULT_ATTRIBUTE,
9628
+ 'getAttributeName' : getAttributeName,
9629
+ 'arm' : arm,
9630
+ 'disarm' : disarm,
9631
+ 'isArmed' : isArmed,
9632
+ 'resolveTrigger' : resolveTrigger
9633
+ };
9634
+ }
9635
+
9636
+ if ( ( typeof(module) !== 'undefined' ) && module.exports ) {
9637
+ // Publish as node.js module
9638
+ module.exports = LoadingState()
9639
+ } else if ( typeof(define) === 'function' && define.amd) {
9640
+ // Publish as AMD module
9641
+ define('lib/loading-state', [],function() { return LoadingState() })
9642
+ }
9643
+ ;
9644
+ define('gina/link', [ 'require', 'lib/domain', 'lib/loading-state', 'lib/merge', 'lib/uuid', 'utils/events' ], function (require) {
9278
9645
 
9279
9646
  var Domain = require('lib/domain');
9280
9647
  var domainInstance = null;
9648
+ var loadingState = require('lib/loading-state');
9281
9649
  var merge = require('lib/merge');
9282
9650
  var uuid = require('lib/uuid');
9283
9651
 
@@ -9333,8 +9701,52 @@ define('gina/link', [ 'require', 'lib/domain', 'lib/merge', 'lib/uuid', 'utils/e
9333
9701
 
9334
9702
 
9335
9703
 
9336
- // XML Request
9337
- var xhr = null;
9704
+ // XML Request — the request currently in flight, plus a monotonic sequence.
9705
+ //
9706
+ // These replace a single module-scope `xhr` that every link click reused.
9707
+ // That is the #B175 class popin already fixed and nav deliberately avoids:
9708
+ // calling `open()` on an object that is still carrying a request implicitly
9709
+ // ABORTS it, and an aborted request reaches readyState 4 with status 0 —
9710
+ // which `handleXhr` has no branch for. So the first click's completion never
9711
+ // arrived at all, and anything waiting on it waited forever.
9712
+ //
9713
+ // The sequence exists because aborting is not enough on its own: the
9714
+ // superseded request may already have reached readyState 4 and queued its
9715
+ // handler. `_linkSeq` lets that stale response be dropped rather than acted
9716
+ // upon, so a slow first response can never overwrite a newer one.
9717
+ var _linkXhr = null;
9718
+ var _linkSeq = 0;
9719
+
9720
+ /**
9721
+ * Build a fresh transport for a single request.
9722
+ *
9723
+ * Was inlined in the `init` handler, which created ONE object for the whole
9724
+ * page lifetime. Per-request construction is what makes concurrent link
9725
+ * clicks independent.
9726
+ *
9727
+ * @returns {object} a new XHR-like object, or `null` when the browser has none
9728
+ *
9729
+ * @example
9730
+ * var xhr = createXhr(); // fresh per request — never shared
9731
+ *
9732
+ * @inner
9733
+ */
9734
+ var createXhr = function() {
9735
+ if (window.XMLHttpRequest) { // Mozilla, Safari, ...
9736
+ return new XMLHttpRequest();
9737
+ }
9738
+ if (window.ActiveXObject) { // IE
9739
+ try {
9740
+ return new ActiveXObject("Msxml2.XMLHTTP");
9741
+ } catch (e) {
9742
+ try {
9743
+ return new ActiveXObject("Microsoft.XMLHTTP");
9744
+ }
9745
+ catch (e) {}
9746
+ }
9747
+ }
9748
+ return null;
9749
+ };
9338
9750
 
9339
9751
  /**
9340
9752
  * XML Request options
@@ -9386,11 +9798,42 @@ define('gina/link', [ 'require', 'lib/domain', 'lib/merge', 'lib/uuid', 'utils/e
9386
9798
  /**
9387
9799
  * linkRequest
9388
9800
  *
9389
- * @param {string} url
9390
- * @param {object} [options]
9801
+ * Builds and sends the request for a bound link. Every entry point funnels
9802
+ * through here — the direct click, the `proxyClick` child delegation and the
9803
+ * public `gina.link.request()` — which is why the loading state is armed here
9804
+ * rather than in the click handlers: `$el` is already the anchor at this point,
9805
+ * so no click target ever has to be walked back up to it.
9806
+ *
9807
+ * Side effects: supersedes any request still in flight, arms `data-gina-loading`
9808
+ * on the anchor for the duration of this one, and releases it from a `loadend`
9809
+ * listener. An indefinite hang is the single outcome that never releases, because
9810
+ * no `xhr.timeout` is set.
9811
+ *
9812
+ * @param {string} url - URL to request
9813
+ * @param {object} [options] - XHR options, merged over the link's own
9814
+ *
9815
+ * @returns {void}
9816
+ *
9817
+ * @example
9818
+ * gina.link.request('/some/route'); // arms the bound anchor, releases on loadend
9819
+ *
9820
+ * @inner
9391
9821
  * */
9392
9822
  function linkRequest(url, options) {
9393
9823
 
9824
+ // One transport per request, and a sequence taken BEFORE the abort so a
9825
+ // supersede-abort is stale by construction — its handler sees
9826
+ // `seq !== _linkSeq` and drops out, which is what keeps the abort from
9827
+ // being mistaken for a real network failure.
9828
+ var seq = ++_linkSeq;
9829
+ if ( _linkXhr && _linkXhr.readyState !== 4 ) {
9830
+ try {
9831
+ _linkXhr.abort();
9832
+ } catch (abortErr) { /* already dead — nothing to unwind */ }
9833
+ }
9834
+ var xhr = createXhr();
9835
+ _linkXhr = xhr;
9836
+
9394
9837
  // link object
9395
9838
  var $link = getLinkByUrl(url);
9396
9839
  var id = $link.id;
@@ -9489,10 +9932,83 @@ define('gina/link', [ 'require', 'lib/domain', 'lib/merge', 'lib/uuid', 'utils/e
9489
9932
 
9490
9933
 
9491
9934
  options.$link = $link;
9935
+ // The CORS branches above can swap the transport (XDomainRequest) or drop
9936
+ // it entirely, so re-point the in-flight tracker at whatever will actually run.
9937
+ _linkXhr = xhr;
9492
9938
  //xhr = handleXhr(xhr, $el, options);
9493
9939
  handleXhr(xhr, $el, options, require);
9940
+
9941
+ // Sequence guard, applied by WRAPPING the handler `handleXhr` just installed
9942
+ // rather than by teaching `handleXhr` about link's sequence. `handleXhr` is
9943
+ // shared and the sequence belongs to the plugin that owns the clicks, so the
9944
+ // guard lives here and `utils/events.js` stays untouched.
9945
+ //
9946
+ // Only `onreadystatechange` needs wrapping: an aborted request fires
9947
+ // `onabort` (which `handleXhr` does not install), never `onerror`, so a
9948
+ // superseded request has exactly one way back into the completion path.
9949
+ var onSettled = xhr.onreadystatechange;
9950
+ xhr.onreadystatechange = function(event) {
9951
+ if ( seq !== _linkSeq ) {
9952
+ return; // superseded by a newer click — drop the stale response
9953
+ }
9954
+ if ( typeof(onSettled) == 'function' ) {
9955
+ onSettled.call(this, event);
9956
+ }
9957
+ };
9958
+
9959
+ // #B247 — loading state for the link the user actually clicked.
9960
+ //
9961
+ // Armed HERE rather than in the click handlers because `$el` is already the
9962
+ // anchor at this point (`getElementById($link.id)` above), and because this
9963
+ // is the one funnel every entry reaches: the direct click, the `proxyClick`
9964
+ // child delegation — which dispatches its custom event ON the anchor, so the
9965
+ // handler's `e.target` is never the inner node — and the public
9966
+ // `gina.link.request()`. Arming below the `!xhr` throw also means a transport
9967
+ // that never materialises can not leave a trigger lit.
9968
+ //
9969
+ // Released on `loadend`, the same fail-safe the validator uses: a LISTENER
9970
+ // emits no events of its own, so it covers success, error, abort AND the
9971
+ // readyState-4/status-0 network failure `handleXhr` has no branch for —
9972
+ // without any of the consumer-visible surface that adding such a branch
9973
+ // would carry (#B282). One listener per transport, and transports are built
9974
+ // per request, so nothing accumulates.
9975
+ //
9976
+ // Never arm what can not be released: the CORS branch above can swap in a
9977
+ // legacy `XDomainRequest`, which has no `addEventListener` and so no
9978
+ // `loadend` to release on. An armed trigger with no release is exactly the
9979
+ // permanent strand this feature exists to prevent.
9980
+ //
9981
+ // Deliberately NOT sequence-guarded, unlike the response wrapper above. The
9982
+ // sequence drops a stale RESPONSE; a superseded request must still RELEASE
9983
+ // its trigger. It releases before the newer click arms, because `abort()`
9984
+ // fires `loadend` synchronously — inside the abort at the top of this
9985
+ // function, which runs before this line. Guarding here would strand the
9986
+ // trigger it was meant to protect.
9987
+ //
9988
+ // Residual: a request that never terminates never fires `loadend`, so an
9989
+ // indefinite hang still strands the trigger. That is #B283 (no `xhr.timeout`
9990
+ // is set, which is also why the `ontimeout` handleXhr installs is dead code)
9991
+ // — a separate, consumer-visible behaviour change, NOT covered here.
9992
+ if ( typeof(xhr.addEventListener) == 'function' ) {
9993
+ loadingState.arm($el);
9994
+ xhr.addEventListener('loadend', function onLinkSettled() {
9995
+ loadingState.disarm($el);
9996
+ });
9997
+ }
9998
+
9494
9999
  // sending
9495
- xhr.send();
10000
+ try {
10001
+ xhr.send();
10002
+ } catch (sendErr) {
10003
+ // A synchronous throw out of `send()` (a cross-origin synchronous
10004
+ // request, a transport already in an invalid state) means no request ran,
10005
+ // so no `loadend` is ever coming. Release, then let the error out
10006
+ // unchanged — the caller's contract is untouched.
10007
+ if ( loadingState.isArmed($el) ) {
10008
+ loadingState.disarm($el);
10009
+ }
10010
+ throw sendErr;
10011
+ }
9496
10012
  }
9497
10013
 
9498
10014
  function registerLink($link, options) {
@@ -9661,19 +10177,10 @@ define('gina/link', [ 'require', 'lib/domain', 'lib/merge', 'lib/uuid', 'utils/e
9661
10177
  setupInstanceProto();
9662
10178
  instance.on('init', function(event) {
9663
10179
 
9664
- // setting up AJAX
9665
- if (window.XMLHttpRequest) { // Mozilla, Safari, ...
9666
- xhr = new XMLHttpRequest();
9667
- } else if (window.ActiveXObject) { // IE
9668
- try {
9669
- xhr = new ActiveXObject("Msxml2.XMLHTTP");
9670
- } catch (e) {
9671
- try {
9672
- xhr = new ActiveXObject("Microsoft.XMLHTTP");
9673
- }
9674
- catch (e) {}
9675
- }
9676
- }
10180
+ // AJAX setup used to happen HERE, building one transport for the whole
10181
+ // page lifetime. It moved to `createXhr()`, called per request from
10182
+ // `linkRequest`, so two link clicks no longer share one object — see the
10183
+ // `_linkXhr` / `_linkSeq` comment at the top of this closure.
9677
10184
 
9678
10185
  // proxies
9679
10186
  // click on main document
@@ -10321,6 +10828,7 @@ function ValidatorPlugin(rules, data, formId, culture) {
10321
10828
  var FormValidator = (isGFFCtx) ? require('lib/form-validator') : require('./form-validator');
10322
10829
  //var Collection = (isGFFCtx) ? require('lib/collection') : require('../../../../../lib/collection');
10323
10830
  var routing = (isGFFCtx) ? require('lib/routing') : require('../../../../../lib/routing');
10831
+ var loadingState = (isGFFCtx) ? require('lib/loading-state') : require('../../../../../lib/loading-state');
10324
10832
 
10325
10833
 
10326
10834
  /** definitions */
@@ -11005,9 +11513,14 @@ function ValidatorPlugin(rules, data, formId, culture) {
11005
11513
  if (isErrorMessageHidden) {
11006
11514
  // hide error messages
11007
11515
  $children[c].className = $children[c].className +' hidden';
11516
+ // #A11Y5 — this hide fires when the field regains focus, and it does not
11517
+ // touch aria-invalid: a committed error stays asserted while its message
11518
+ // would otherwise leave the a11y tree. Clip it instead.
11519
+ clipErrorMessage($children[c]);
11008
11520
  } else {
11009
11521
  // display error messages
11010
11522
  $children[c].className = $children[c].className.replace(/(\s+hidden|hidden)/, '');
11523
+ revealErrorMessage($children[c]);
11011
11524
  }
11012
11525
  break
11013
11526
  }
@@ -11015,17 +11528,38 @@ function ValidatorPlugin(rules, data, formId, culture) {
11015
11528
  }
11016
11529
 
11017
11530
  /**
11018
- * #A11Y1 (slice 4) — lazily create one visually-hidden aria-live="polite" status region per
11019
- * form and announce a blur-time committed error through it. A CSS :user-invalid display toggle
11020
- * is not reliably announced by screen readers, and on blur the field has lost focus so its own
11021
- * aria-invalid / aria-errormessage are not re-read; the polite region decouples the announcement
11022
- * from visual display. Submit-time errors use focus (see onValidate), not this region.
11531
+ * #A11Y2 — create (or recover) a form's visually-hidden polite status region WITHOUT
11532
+ * writing to it. Split out of announceA11yError so the region can exist LONG before the
11533
+ * first error: a region that is inserted and populated in the same tick reaches assistive
11534
+ * tech as a single mutation batch, which is commonly never spoken, so the very first
11535
+ * announcement per form — the one that matters most — was the one most likely lost.
11536
+ * bindForm calls this at bind time, which is what makes that first error audible.
11537
+ *
11538
+ * The region is a CHILD OF THE FORM deliberately. A popin renders its form inside a native
11539
+ * `<dialog>` opened with showModal(), which leaves everything outside the top layer inert;
11540
+ * a body-level region would sit in that inert subtree and go unspoken for exactly the forms
11541
+ * that live in popins. Keeping it inside the form keeps it inside the dialog. The price is
11542
+ * that a subtree replacement (a popin re-render's `innerHTML =`, or a nav fragment swap)
11543
+ * destroys it — hence create-OR-RECOVER, and hence the deferred first write in
11544
+ * announceA11yError, which is what stops a recovery from silently repeating the defect.
11545
+ *
11546
+ * The bookkeeping flag lives on the element rather than in a module-scope map so it dies
11547
+ * WITH the region: a destroyed region leaves no stale entry behind, and no form id keeps
11548
+ * an unbounded map alive. Browser-only, like readCsrfCookie — this engine is shared with
11549
+ * the server form-body path, which has no document.
11550
+ *
11551
+ * @inner
11023
11552
  * @param {object} $form - the HTMLFormElement the region belongs to
11024
- * @param {string} text - the error text to announce
11025
- * @returns {object|null} the live-region element, or null when nothing was announced
11553
+ * @returns {object|null} the live-region element, or null outside a browser document
11554
+ * @example
11555
+ * // at bind time: the region is in the a11y tree before any error can occur
11556
+ * ensureA11yLiveRegion($form.target);
11026
11557
  */
11027
- var announceA11yError = function($form, text) {
11028
- if ( !$form || !text ) return null;
11558
+ var ensureA11yLiveRegion = function($form) {
11559
+ if ( !$form ) return null;
11560
+ if ( typeof(document) === 'undefined' || !document || typeof(document.getElementById) !== 'function' ) {
11561
+ return null;
11562
+ }
11029
11563
  var _fid = ( typeof($form.id) != 'undefined' && $form.id ) ? $form.id : ( $form.getAttribute('id') || 'form' );
11030
11564
  var _liveId = 'gina-aria-live-' + _fid;
11031
11565
  var _live = document.getElementById(_liveId);
@@ -11037,33 +11571,253 @@ function ValidatorPlugin(rules, data, formId, culture) {
11037
11571
  _live.setAttribute('aria-atomic', 'true');
11038
11572
  _live.className = 'gina-visually-hidden';
11039
11573
  _live.style.cssText = 'position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;';
11574
+ _live.ginaA11yFresh = true;
11575
+ $form.appendChild(_live);
11576
+ } else if ( _live.parentNode !== $form ) {
11577
+ // Same id, different form node — a re-render replaced the form, or two forms share
11578
+ // an id. Re-home it so the region always sits inside the form that announces, which
11579
+ // is the property the inert reasoning above depends on; a move is a fresh insertion.
11580
+ _live.ginaA11yFresh = true;
11040
11581
  $form.appendChild(_live);
11041
11582
  }
11042
- _live.textContent = text;
11043
- return _live;
11583
+ return _live;
11584
+ };
11585
+
11586
+ /**
11587
+ * #A11Y1 (slice 4) — announce a blur-time committed error through the region created at
11588
+ * bind time. A CSS :user-invalid display toggle is not reliably announced by screen
11589
+ * readers, and on blur the field has lost focus so its own aria-invalid /
11590
+ * aria-errormessage are not re-read; the polite region decouples the announcement
11591
+ * from visual display. Submit-time errors use focus (see onValidate), not this region.
11592
+ *
11593
+ * #A11Y2 — when the region had to be created (or re-homed) HERE rather than at bind time,
11594
+ * the insertion and this write would land in the same tick and likely never be spoken, so
11595
+ * that first write is deferred by one macrotask. Only the fresh case defers; an already
11596
+ * bound region writes synchronously, exactly as before. While a deferred write is pending
11597
+ * a later call replaces the pending text, so the LATEST error wins and a stale first
11598
+ * message can never land on top of it. The timer re-enters this function, which then takes
11599
+ * the synchronous branch — keeping exactly ONE textContent write site.
11600
+ *
11601
+ * @param {object} $form - the HTMLFormElement the region belongs to
11602
+ * @param {string} text - the error text to announce
11603
+ * @returns {object|null} the live-region element, or null when nothing was announced
11604
+ * @example
11605
+ * announceA11yError($form, 'This field is required');
11606
+ */
11607
+ var announceA11yError = function($form, text) {
11608
+ if ( !$form || !text ) return null;
11609
+ var _live = ensureA11yLiveRegion($form);
11610
+ if ( !_live ) return null;
11611
+ if ( _live.ginaA11yFresh ) {
11612
+ _live.ginaA11yPending = text;
11613
+ if ( !_live.ginaA11yTimer ) {
11614
+ _live.ginaA11yTimer = setTimeout(function() {
11615
+ _live.ginaA11yTimer = null;
11616
+ _live.ginaA11yFresh = false;
11617
+ announceA11yError($form, _live.ginaA11yPending);
11618
+ }, 0);
11619
+ }
11620
+ return _live;
11621
+ }
11622
+ text = a11yDistinctText(_live, text);
11623
+ _live.textContent = text;
11624
+ return _live;
11625
+ };
11626
+
11627
+ /**
11628
+ * #B178 — announcement text for a field's error container.
11629
+ * The container renders one `<p>` per rule message; visually they stack as
11630
+ * blocks, but `textContent` concatenates them with NO separator, so an
11631
+ * aria-live announcement read multiple messages as one run-on string.
11632
+ * Joins the per-message texts with '. ' instead; falls back to the raw
11633
+ * `textContent` when the container has no `<p>` children.
11634
+ * @inner
11635
+ * @param {object} $err - the error-message container element
11636
+ * @returns {string} announcement text
11637
+ */
11638
+ var getA11yAnnounceText = function($err) {
11639
+ var $msgs = $err.getElementsByTagName('p');
11640
+ if ( !$msgs.length ) {
11641
+ return $err.textContent;
11642
+ }
11643
+ var parts = [];
11644
+ for (var m = 0, mLen = $msgs.length; m < mLen; ++m) {
11645
+ parts.push($msgs[m].textContent);
11646
+ }
11647
+ return parts.join('. ');
11648
+ };
11649
+
11650
+ /**
11651
+ * A11Y_LABELS — English defaults for gina's own accessibility status strings.
11652
+ *
11653
+ * Deliberately separate from `setErrorLabels`, which owns per-culture overrides
11654
+ * for *rule* error labels and is keyed by rule name. These are framework-authored
11655
+ * announcements about what gina is doing, so they need a home of their own.
11656
+ *
11657
+ * `%s` in a value is replaced with a runtime detail (currently the file name);
11658
+ * it follows the same placeholder convention the rule error labels already use,
11659
+ * so a translation keeps control of where the detail lands in the sentence.
11660
+ *
11661
+ * @constant {object}
11662
+ * @inner
11663
+ */
11664
+ var A11Y_LABELS = {
11665
+ submitting : 'Submitting…',
11666
+ // #A11Y7/U2 — staged uploads announce only their TRANSITIONS (start and
11667
+ // success), never per-tick progress: `aria-live="polite"` coalesces but does
11668
+ // not throttle, and one announcement per onprogress event would bury every
11669
+ // other message on the page. There is deliberately NO `uploadError` key —
11670
+ // the error path announces the server's own message instead (#A11Y7/U3), and
11671
+ // a generic second write in the same beat would clobber it (see the rationale
11672
+ // on `releaseSubmitA11y`).
11673
+ uploadStarted : 'Upload started',
11674
+ uploadComplete : 'Upload complete',
11675
+ // #A11Y7/U5 — spoken when a staged file's reset/delete control removes it.
11676
+ fileRemoved : '%s removed'
11677
+ };
11678
+
11679
+ /**
11680
+ * a11yLabel — resolve one accessibility status string.
11681
+ *
11682
+ * Follows the same philosophy `setErrorLabels` documents: an English default
11683
+ * ships inline and fills anything the project does not translate. A project
11684
+ * overrides by assigning `gina.config.a11y`, typically once at boot keyed off
11685
+ * the negotiated culture.
11686
+ *
11687
+ * @param {string} key - Label key, e.g. `submitting`.
11688
+ * @returns {string} the project's override when it supplies a non-empty string,
11689
+ * otherwise the English default (empty string for unknown keys).
11690
+ *
11691
+ * @example
11692
+ * // project side, once at boot:
11693
+ * gina.config.a11y = { submitting: 'Envoi…' };
11694
+ *
11695
+ * @inner
11696
+ */
11697
+ var a11yLabel = function(key) {
11698
+ var _over = ( typeof(gina) != 'undefined' && gina && gina.config && gina.config.a11y )
11699
+ ? gina.config.a11y
11700
+ : null;
11701
+ if ( _over && typeof(_over[key]) == 'string' && _over[key] ) {
11702
+ return _over[key];
11703
+ }
11704
+ return A11Y_LABELS[key] || '';
11705
+ };
11706
+
11707
+ /**
11708
+ * announceA11yStatus — announce a non-error status through the form's region.
11709
+ *
11710
+ * The region #A11Y2 stands up at bind time is `role="status" aria-live="polite"`,
11711
+ * i.e. already a status channel rather than an error-only one — so submit progress
11712
+ * rides that same element instead of a second region competing with it. Kept as a
11713
+ * named delegate purely so call sites read as status, not as errors.
11714
+ *
11715
+ * @param {object} $form - form target (HTMLFormElement), not the form instance.
11716
+ * @param {string} text - resolved, human-readable status text.
11717
+ * @returns {object|null} the live region, or null when it could not be resolved.
11718
+ *
11719
+ * @example
11720
+ * announceA11yStatus($myForm, 'Envoi…');
11721
+ *
11722
+ * @inner
11723
+ */
11724
+ var announceA11yStatus = function($form, text) {
11725
+ return announceA11yError($form, text);
11726
+ };
11727
+
11728
+ /**
11729
+ * a11yDistinctText — make a repeated announcement observably new.
11730
+ *
11731
+ * An `aria-live="polite"` region is announced when its content CHANGES, so re-writing
11732
+ * byte-identical text is commonly not spoken at all. That is the normal case for a
11733
+ * validation message: blurring a field that still fails the same rule produces exactly
11734
+ * the same string, and the user heard nothing the second time. Appending a no-break
11735
+ * space makes the node's content differ while leaving what is read out unchanged, and
11736
+ * it self-cancels — the next identical announcement compares against the suffixed text,
11737
+ * differs, and writes the clean string again.
11738
+ *
11739
+ * Lives outside `announceA11yError` deliberately: that function sits inside a fixed
11740
+ * character window asserted by a source pin, and the rationale above does not fit in it.
11741
+ *
11742
+ * ⚠️ Unverified: that a screen reader actually re-announces as a result. The string
11743
+ * demonstrably changes; no assistive-technology pass has been run to confirm the effect.
11744
+ *
11745
+ * @param {object} $live - the live region node.
11746
+ * @param {string} text - the announcement about to be written.
11747
+ * @returns {string} `text`, suffixed only when it would otherwise be a no-op write.
11748
+ *
11749
+ * @example
11750
+ * var next = a11yDistinctText($region, 'This field is required');
11751
+ *
11752
+ * @inner
11753
+ */
11754
+ var a11yDistinctText = function($live, text) {
11755
+ if ( !$live || typeof(text) != 'string' ) {
11756
+ return text;
11757
+ }
11758
+ return ( $live.textContent === text ) ? text + ' ' : text;
11759
+ };
11760
+
11761
+ /**
11762
+ * ERR_MSG_CLIP — inline declarations that hide an element from VIEW while leaving it
11763
+ * in the accessibility tree.
11764
+ *
11765
+ * `display` carries `!important` because gina's own `.hidden` helper is
11766
+ * `display: none !important`, and an inline `!important` is what outranks it (measured:
11767
+ * a plain inline `display:block` loses to it). The class is deliberately left in place
11768
+ * so consumer CSS keyed on `.hidden` keeps matching.
11769
+ *
11770
+ * @constant {string}
11771
+ * @inner
11772
+ */
11773
+ var ERR_MSG_CLIP = 'display:block !important;position:absolute;width:1px;height:1px;'
11774
+ + 'margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;';
11775
+
11776
+ /**
11777
+ * clipErrorMessage — hide a validation message visually, but keep it reachable.
11778
+ *
11779
+ * `.hidden` is `display: none !important`, which removes the node from the
11780
+ * accessibility tree outright. That is the wrong tool wherever `aria-invalid="true"`
11781
+ * stays asserted on the field, because the `aria-errormessage` association then points
11782
+ * assistive tech at a target it cannot resolve — the field is announced invalid with no
11783
+ * retrievable reason. Both hide paths that can coexist with an asserted `aria-invalid`
11784
+ * (the focus-driven hide in `refreshWarning`, and the refresh re-create) clip instead.
11785
+ *
11786
+ * gina creates and owns this element, so replacing `style.cssText` wholesale is safe.
11787
+ *
11788
+ * @param {object} $err - the `form-item-error-message` element.
11789
+ * @returns {boolean} true when the clip was applied.
11790
+ *
11791
+ * @example
11792
+ * clipErrorMessage($someMessageDiv);
11793
+ *
11794
+ * @inner
11795
+ */
11796
+ var clipErrorMessage = function($err) {
11797
+ if ( !$err || typeof($err.style) == 'undefined' || !$err.style ) {
11798
+ return false;
11799
+ }
11800
+ $err.style.cssText = ERR_MSG_CLIP;
11801
+ return true;
11044
11802
  };
11045
11803
 
11046
11804
  /**
11047
- * #B178 — announcement text for a field's error container.
11048
- * The container renders one `<p>` per rule message; visually they stack as
11049
- * blocks, but `textContent` concatenates them with NO separator, so an
11050
- * aria-live announcement read multiple messages as one run-on string.
11051
- * Joins the per-message texts with '. ' instead; falls back to the raw
11052
- * `textContent` when the container has no `<p>` children.
11805
+ * revealErrorMessage — undo `clipErrorMessage`, restoring normal visual flow.
11806
+ *
11807
+ * @param {object} $err - the `form-item-error-message` element.
11808
+ * @returns {boolean} true when the clip was cleared.
11809
+ *
11810
+ * @example
11811
+ * revealErrorMessage($someMessageDiv);
11812
+ *
11053
11813
  * @inner
11054
- * @param {object} $err - the error-message container element
11055
- * @returns {string} announcement text
11056
11814
  */
11057
- var getA11yAnnounceText = function($err) {
11058
- var $msgs = $err.getElementsByTagName('p');
11059
- if ( !$msgs.length ) {
11060
- return $err.textContent;
11061
- }
11062
- var parts = [];
11063
- for (var m = 0, mLen = $msgs.length; m < mLen; ++m) {
11064
- parts.push($msgs[m].textContent);
11815
+ var revealErrorMessage = function($err) {
11816
+ if ( !$err || typeof($err.style) == 'undefined' || !$err.style ) {
11817
+ return false;
11065
11818
  }
11066
- return parts.join('. ');
11819
+ $err.style.cssText = '';
11820
+ return true;
11067
11821
  };
11068
11822
 
11069
11823
  /**
@@ -11361,6 +12115,14 @@ function ValidatorPlugin(rules, data, formId, culture) {
11361
12115
  // global pass, so without this focus guard it re-shows the message mid-typing.
11362
12116
  // On blur (field no longer active) the message is created shown (focusout commits).
11363
12117
  $err.setAttribute('class', ( document.activeElement && document.activeElement.name == name ) ? 'form-item-error-message hidden' : 'form-item-error-message');
12118
+ // #A11Y5 — the branch below re-asserts aria-invalid="true" on this same
12119
+ // field, so when the message is hidden it must stay resolvable: clip it
12120
+ // out of view rather than out of the accessibility tree. Reads the class
12121
+ // just written instead of re-deriving the condition, so the ternary above
12122
+ // stays exactly as pinned.
12123
+ if ( /hidden/.test($err.className) ) {
12124
+ clipErrorMessage($err);
12125
+ }
11364
12126
 
11365
12127
  // #A11Y1 (slice 2) — preserve the aria-errormessage wire we own across refresh.
11366
12128
  if ( _ginaOwnsErrMsg && _ariaErrId ) {
@@ -11690,6 +12452,231 @@ function ValidatorPlugin(rules, data, formId, culture) {
11690
12452
  }
11691
12453
 
11692
12454
 
12455
+ /**
12456
+ * armSubmitLoading
12457
+ *
12458
+ * Marks `$trigger` as running (`data-gina-loading="true"`, or whatever
12459
+ * `gina.config.loadingAttribute` names) and remembers it on the form instance so
12460
+ * every terminal path can release it — including the ones that never reach an
12461
+ * XHR and therefore have no `$submitTrigger` of their own in scope.
12462
+ *
12463
+ * The element is stashed rather than re-derived because the two are NOT always
12464
+ * the same: `send()` resolves `$submitTrigger` once from `$form.submitTrigger`
12465
+ * (an id) at its own entry, which a rejected submit never reaches, and which a
12466
+ * second click on a different button would have overwritten by then. The stash
12467
+ * is a single reference per form, replaced on each arm and nulled on disarm, so
12468
+ * it can not retain a detached node past the submit that created it.
12469
+ *
12470
+ * @param {object} $formInstance - the form instance (`instance.$forms[id]`)
12471
+ * @param {object} $trigger - the element the user operated
12472
+ *
12473
+ * @returns {boolean} true when the trigger was marked
12474
+ *
12475
+ * FIRST ARM WINS until the state is released. A form runs one submit at a time — that
12476
+ * is what `withRateLimit` enforces — so a second attempt arriving mid-flight is about
12477
+ * to be refused, and must not take ownership: on a form with several submit buttons it
12478
+ * would move the stash to the refused trigger, leaving the trigger that actually owns
12479
+ * the request armed with nothing left to release it.
12480
+ *
12481
+ * @example
12482
+ * armSubmitLoading(instance.$forms[$form.id], loadingState.resolveTrigger($el, $target));
12483
+ *
12484
+ * @inner
12485
+ */
12486
+ var armSubmitLoading = function($formInstance, $trigger) {
12487
+ if ( !$formInstance || !$trigger ) {
12488
+ return false;
12489
+ }
12490
+ if ($formInstance.loadingTrigger) {
12491
+ return false;
12492
+ }
12493
+ $formInstance.loadingTrigger = $trigger;
12494
+ return loadingState.arm($trigger);
12495
+ };
12496
+
12497
+ /**
12498
+ * disarmSubmitLoading
12499
+ *
12500
+ * Releases whatever `armSubmitLoading` marked for this form
12501
+ * (`data-gina-loading="false"`) and drops the stash. Idempotent and safe to call
12502
+ * on a form that was never armed — every terminal path calls it unconditionally
12503
+ * rather than trying to work out whether arming happened.
12504
+ *
12505
+ * #B247: the validation-rejected path is the one that stranded consumer loading
12506
+ * state, because a rejected submit sends nothing and so never reaches any XHR
12507
+ * settle. `send()`'s rate-limit early return is the same shape and is covered
12508
+ * too; `loadend` covers success, error, timeout and abort alike.
12509
+ *
12510
+ * @param {object} $formInstance - the form instance (`instance.$forms[id]`)
12511
+ *
12512
+ * @returns {boolean} true when a marked trigger was released
12513
+ *
12514
+ * @example
12515
+ * disarmSubmitLoading(instance.$forms[_id]); // on reject, settle, timeout or abort
12516
+ *
12517
+ * @inner
12518
+ */
12519
+ var disarmSubmitLoading = function($formInstance) {
12520
+ if ( !$formInstance || !$formInstance.loadingTrigger ) {
12521
+ return false;
12522
+ }
12523
+ var released = loadingState.disarm($formInstance.loadingTrigger);
12524
+ $formInstance.loadingTrigger = null;
12525
+ return released;
12526
+ };
12527
+
12528
+ /**
12529
+ * holdTriggerFocus — remember the submit trigger gina is about to blur.
12530
+ *
12531
+ * A natively `disabled` control cannot hold focus: the instant gina disables an
12532
+ * in-flight submit trigger the browser moves focus to `<body>`, and re-enabling
12533
+ * does not bring it back (both measured). Every keyboard submit therefore loses
12534
+ * the user's place. Only the native-`disabled` branch is recorded — an `<a>`
12535
+ * receives `aria-disabled`, which does not blur (measured), so it needs no restore.
12536
+ *
12537
+ * @param {object} $form - the form instance, which carries the stash.
12538
+ * @param {object} $trigger - resolved submit trigger; may be null.
12539
+ * @returns {boolean} true when focus was captured for a later restore.
12540
+ *
12541
+ * @example
12542
+ * holdTriggerFocus($myFormInstance, $mySaveButton);
12543
+ *
12544
+ * @inner
12545
+ */
12546
+ var holdTriggerFocus = function($form, $trigger) {
12547
+ if ( !$form || !$trigger ) {
12548
+ return false;
12549
+ }
12550
+ if ( typeof(document) === 'undefined' || !document ) {
12551
+ return false;
12552
+ }
12553
+ if ( document.activeElement !== $trigger ) {
12554
+ return false;
12555
+ }
12556
+ $form.ginaA11yFocusReturn = $trigger;
12557
+ return true;
12558
+ };
12559
+
12560
+ /**
12561
+ * releaseTriggerFocus — put focus back on the trigger gina blurred.
12562
+ *
12563
+ * Deliberately conservative: it restores ONLY when focus is still exactly where
12564
+ * the disable dropped it (`<body>`) and the trigger is still in the document. If
12565
+ * anything else claimed focus while the request was in flight — a popin opened, a
12566
+ * redirect ran, the failed-submit branch moved focus to the first invalid field —
12567
+ * that decision wins and gina undoes nothing. gina restores what gina took, never
12568
+ * more.
12569
+ *
12570
+ * @param {object} $form - the form instance holding the stash.
12571
+ * @returns {boolean} true when focus was actually restored.
12572
+ *
12573
+ * @example
12574
+ * releaseTriggerFocus($myFormInstance);
12575
+ *
12576
+ * @inner
12577
+ */
12578
+ var releaseTriggerFocus = function($form) {
12579
+ if ( !$form || !$form.ginaA11yFocusReturn ) {
12580
+ return false;
12581
+ }
12582
+ var $trigger = $form.ginaA11yFocusReturn;
12583
+ $form.ginaA11yFocusReturn = null;
12584
+ if ( typeof(document) === 'undefined' || !document ) {
12585
+ return false;
12586
+ }
12587
+ // Nothing else may have claimed focus in the meantime.
12588
+ if ( document.activeElement && document.activeElement !== document.body ) {
12589
+ return false;
12590
+ }
12591
+ var _stillThere = ( typeof($trigger.isConnected) == 'boolean' )
12592
+ ? $trigger.isConnected
12593
+ : ( document.body && document.body.contains($trigger) );
12594
+ if ( !_stillThere || typeof($trigger.focus) != 'function' ) {
12595
+ return false;
12596
+ }
12597
+ $trigger.focus();
12598
+ return true;
12599
+ };
12600
+
12601
+ /**
12602
+ * armSubmitA11y — expose an in-flight submit to assistive technology.
12603
+ *
12604
+ * Three things at once, all scoped to the request window: capture the trigger's
12605
+ * focus before the caller's native disable blurs it, mark the trigger busy, and
12606
+ * announce the start once through the form's polite region.
12607
+ *
12608
+ * Two placement decisions worth keeping:
12609
+ * - `aria-busy` goes on the TRIGGER, never the form. The live region lives inside
12610
+ * the form, and an ancestor marked busy MAY be treated as "defer announcements in
12611
+ * this subtree", which would silence the very region used to announce. ARIA 1.2
12612
+ * defines no normative behaviour here, so this is a cheap hedge, not a spec
12613
+ * requirement — and it is explicitly NOT a claim about what implementations do:
12614
+ * measured 2026-08-05 on VoiceOver/Chrome, an announcement from inside an
12615
+ * `aria-busy="true"` ancestor WAS spoken, so at least that pairing does not defer.
12616
+ * The placement costs nothing and still guards ATs that might; do not restate it
12617
+ * as "assistive tech commonly defers" — that wording was wrong and was corrected.
12618
+ * - This window is only ever reached once a request is genuinely in flight, so a
12619
+ * validation-rejected submit never arms and never announces. That is what keeps
12620
+ * the arm-before-validate loading state from producing a busy/not-busy flap.
12621
+ *
12622
+ * @param {object} $form - the form instance.
12623
+ * @param {object} $trigger - resolved submit trigger; may be null.
12624
+ * @returns {boolean} true when this call produced the announcement.
12625
+ *
12626
+ * @example
12627
+ * armSubmitA11y($myFormInstance, $mySaveButton);
12628
+ *
12629
+ * @inner
12630
+ */
12631
+ var armSubmitA11y = function($form, $trigger) {
12632
+ if ( !$form ) {
12633
+ return false;
12634
+ }
12635
+ if ($trigger) {
12636
+ if ( !/^A$/i.test($trigger.tagName) ) {
12637
+ holdTriggerFocus($form, $trigger);
12638
+ }
12639
+ $trigger.setAttribute('aria-busy', 'true');
12640
+ }
12641
+ // readyState 1 and 3 both reach here; announce once per request.
12642
+ if ( $form.ginaA11yBusy ) {
12643
+ return false;
12644
+ }
12645
+ $form.ginaA11yBusy = true;
12646
+ if ( $form.target ) {
12647
+ announceA11yStatus($form.target, a11yLabel('submitting'));
12648
+ }
12649
+ return true;
12650
+ };
12651
+
12652
+ /**
12653
+ * releaseSubmitA11y — clear the in-flight submit exposure.
12654
+ *
12655
+ * Idempotent, because both the readyState-4 release and the `loadend` fail-safe
12656
+ * run it. Deliberately announces nothing on completion: an errored response is
12657
+ * already announced field-by-field by `handleErrorsDisplay`, and writing a second
12658
+ * status over a polite region in the same beat can cut that announcement off.
12659
+ *
12660
+ * @param {object} $form - the form instance.
12661
+ * @param {object} $trigger - resolved submit trigger; may be null.
12662
+ * @returns {boolean} true when focus was restored to the trigger.
12663
+ *
12664
+ * @example
12665
+ * releaseSubmitA11y($myFormInstance, $mySaveButton);
12666
+ *
12667
+ * @inner
12668
+ */
12669
+ var releaseSubmitA11y = function($form, $trigger) {
12670
+ if ( !$form ) {
12671
+ return false;
12672
+ }
12673
+ if ($trigger) {
12674
+ $trigger.removeAttribute('aria-busy');
12675
+ }
12676
+ $form.ginaA11yBusy = false;
12677
+ return releaseTriggerFocus($form);
12678
+ };
12679
+
11693
12680
  /**
11694
12681
  * send
11695
12682
  * N.B.: no validation here; if you want to validate against rules, use `.submit()` or `.validateFormById(formId)` before
@@ -11724,6 +12711,11 @@ function ValidatorPlugin(rules, data, formId, culture) {
11724
12711
  && typeof($form.sent) != 'undefined'
11725
12712
  && /^true$/i.test($form.sent)
11726
12713
  ) {
12714
+ // #B247 — deliberately NO loading-state release here. This return means a
12715
+ // request for this form is already in flight, so the trigger that owns it is
12716
+ // still legitimately loading and its own settle will release it. Releasing
12717
+ // here would clear the state mid-request; and because `armSubmitLoading` is
12718
+ // first-wins, the refused attempt never armed anything of its own to clear.
11727
12719
  return;
11728
12720
  }
11729
12721
 
@@ -11879,12 +12871,20 @@ function ValidatorPlugin(rules, data, formId, culture) {
11879
12871
  }
11880
12872
  }
11881
12873
  $form.target.removeAttribute('data-gina-form-loading');
12874
+ // #B247 — the trigger-scoped release rides the same fail-safe:
12875
+ // `loadend` is the one hook that covers abort and error too.
12876
+ disarmSubmitLoading($form);
12877
+ // #A11Y4 — last, so focus lands on a trigger that is fully settled:
12878
+ // re-enabled above and no longer flagged loading.
12879
+ releaseSubmitA11y($form, $submitTrigger);
11882
12880
  });
11883
12881
  }
11884
12882
  // catching ready state cb
11885
12883
  // Data loading ...
11886
12884
  if ( /^(1|3)$/.test(xhr.readyState) ) {
11887
12885
  $form.target.setAttribute('data-gina-form-loading', true);
12886
+ // #A11Y4 — must run BEFORE the native disable below, which blurs the trigger.
12887
+ armSubmitA11y($form, $submitTrigger);
11888
12888
  if ($submitTrigger) {
11889
12889
  // For A tag: aria-disabled=true
11890
12890
  if ( /^A$/i.test($submitTrigger.tagName) ) {
@@ -11913,6 +12913,8 @@ function ValidatorPlugin(rules, data, formId, culture) {
11913
12913
  // Data loading ...
11914
12914
  if ( /^(1|3)$/.test(xhr.readyState) ) {
11915
12915
  $form.target.setAttribute('data-gina-form-loading', true);
12916
+ // #A11Y4 — must run BEFORE the native disable below, which blurs the trigger.
12917
+ armSubmitA11y($form, $submitTrigger);
11916
12918
  if ($submitTrigger) {
11917
12919
  // For A tag: aria-disabled=true
11918
12920
  if ( /^A$/i.test($submitTrigger.tagName) ) {
@@ -11947,6 +12949,11 @@ function ValidatorPlugin(rules, data, formId, culture) {
11947
12949
  }
11948
12950
  }
11949
12951
  $form.target.removeAttribute('data-gina-form-loading');
12952
+ // #B247 — idempotent with the `loadend` release above
12953
+ disarmSubmitLoading($form);
12954
+ // #A11Y4 — last, so focus lands on a fully settled trigger; idempotent
12955
+ // with the `loadend` release, which may already have restored it.
12956
+ releaseSubmitA11y($form, $submitTrigger);
11950
12957
 
11951
12958
  var $popin = null;
11952
12959
  var blob = null;
@@ -12725,7 +13732,16 @@ function ValidatorPlugin(rules, data, formId, culture) {
12725
13732
  * computable) so the browser renders its native indeterminate animation;
12726
13733
  * `error` empties the bar (value 0 — NOT indeterminate, whose animation
12727
13734
  * would read as still working)
12728
- * - any other element: `textContent` shows the integer percentage (`42%`)
13735
+ * - any other element: `textContent` shows the integer percentage (`42%`), and
13736
+ * `role="progressbar"` + `aria-valuemin="0"` / `aria-valuemax="100"` /
13737
+ * `aria-valuenow="<percent>"` give it the semantics a native `<progress>` gets
13738
+ * for free (#A11Y7/U2 — without them the indicator is unlabelled text to
13739
+ * assistive tech). `aria-valuenow` tracks `data-gina-upload-progress` exactly:
13740
+ * both are ABSENT while indeterminate and on `error`, because an absent
13741
+ * `aria-valuenow` is how a progressbar signals "unknown" — a stale or zero
13742
+ * value would read as real, stalled progress. A native `<progress>` is left
13743
+ * untouched: it already exposes all of this, and a redundant explicit role
13744
+ * risks overriding the implicit one.
12729
13745
  *
12730
13746
  * Every target also carries two data attributes as styling hooks:
12731
13747
  * `data-gina-upload-progress` (integer percent, absent while indeterminate)
@@ -12761,10 +13777,28 @@ function ValidatorPlugin(rules, data, formId, culture) {
12761
13777
  }
12762
13778
  $indicator.removeAttribute('data-gina-upload-progress');
12763
13779
  $indicator.removeAttribute('data-gina-upload-progress-state');
13780
+ // #A11Y7/U2 — `reset` strips everything this layer ever set, and the ARIA
13781
+ // below is part of "everything": leaving a stale `role="progressbar"` on a
13782
+ // cleared element would expose a permanently 0%-looking bar to assistive tech.
13783
+ if (!isNativeProgress) {
13784
+ $indicator.removeAttribute('role');
13785
+ $indicator.removeAttribute('aria-valuemin');
13786
+ $indicator.removeAttribute('aria-valuemax');
13787
+ $indicator.removeAttribute('aria-valuenow');
13788
+ }
12764
13789
  return;
12765
13790
  }
12766
13791
 
12767
13792
  $indicator.setAttribute('data-gina-upload-progress-state', state);
13793
+ // #A11Y7/U2 — a native <progress> already carries implicit progressbar
13794
+ // semantics (and its own value/max), so it is deliberately left alone; every
13795
+ // other element is text with no role at all until we give it one. The bounds
13796
+ // are constant because `data-gina-upload-progress` is already a percentage.
13797
+ if (!isNativeProgress) {
13798
+ $indicator.setAttribute('role', 'progressbar');
13799
+ $indicator.setAttribute('aria-valuemin', '0');
13800
+ $indicator.setAttribute('aria-valuemax', '100');
13801
+ }
12768
13802
 
12769
13803
  if (state == 'processing') {
12770
13804
  // #R8 — byte-complete, server still post-processing. Advance the STATE only:
@@ -12786,6 +13820,9 @@ function ValidatorPlugin(rules, data, formId, culture) {
12786
13820
  $indicator.textContent = '100%';
12787
13821
  }
12788
13822
  $indicator.setAttribute('data-gina-upload-progress', 100);
13823
+ if (!isNativeProgress) {
13824
+ $indicator.setAttribute('aria-valuenow', '100');
13825
+ }
12789
13826
  return;
12790
13827
  }
12791
13828
 
@@ -12799,6 +13836,13 @@ function ValidatorPlugin(rules, data, formId, culture) {
12799
13836
  $indicator.textContent = '';
12800
13837
  }
12801
13838
  $indicator.removeAttribute('data-gina-upload-progress');
13839
+ // #A11Y7/U2 — an errored bar is EMPTY, not indeterminate (the native branch
13840
+ // above says the same about `value`): drop `aria-valuenow` so assistive tech
13841
+ // reports an unknown state rather than a confident 0% that reads as "no
13842
+ // progress yet, still working".
13843
+ if (!isNativeProgress) {
13844
+ $indicator.removeAttribute('aria-valuenow');
13845
+ }
12802
13846
  return;
12803
13847
  }
12804
13848
 
@@ -12807,6 +13851,12 @@ function ValidatorPlugin(rules, data, formId, culture) {
12807
13851
  $indicator.removeAttribute('value'); // native indeterminate animation
12808
13852
  }
12809
13853
  $indicator.removeAttribute('data-gina-upload-progress');
13854
+ // #A11Y7/U2 — mirrors the `value` removal above: an absent `aria-valuenow`
13855
+ // on a progressbar IS the indeterminate signal, so this must track the
13856
+ // percent attribute exactly rather than reporting a stale last-known value.
13857
+ if (!isNativeProgress) {
13858
+ $indicator.removeAttribute('aria-valuenow');
13859
+ }
12810
13860
  return;
12811
13861
  }
12812
13862
 
@@ -12816,6 +13866,7 @@ function ValidatorPlugin(rules, data, formId, culture) {
12816
13866
  $indicator.value = result.loaded;
12817
13867
  } else {
12818
13868
  $indicator.textContent = result.progress + '%';
13869
+ $indicator.setAttribute('aria-valuenow', result.progress);
12819
13870
  }
12820
13871
  $indicator.setAttribute('data-gina-upload-progress', result.progress);
12821
13872
  };
@@ -13030,9 +14081,27 @@ function ValidatorPlugin(rules, data, formId, culture) {
13030
14081
 
13031
14082
  $error.innerHTML = '<p>'+ errMsg +'</p>';
13032
14083
  fadeIn($error);
14084
+ // #A11Y7/U3 — the container is `display:none` when the text is written and
14085
+ // only revealed by the fadeIn above, which is exactly the case where a
14086
+ // `role="alert"` on it is unreliable; route through the form's own polite
14087
+ // region instead — the same channel field validation errors already use.
14088
+ // Announce the RENDERED text, not `errMsg`: the message can carry markup
14089
+ // (the url-action checker substitutes `<br>`), and textContent is what a
14090
+ // user would actually read.
14091
+ if ( $uploadTriger && $uploadTriger.form ) {
14092
+ announceA11yError($uploadTriger.form, $error.textContent);
14093
+ }
13033
14094
  } else if(!$error && status != 'success') {
13034
14095
  throw new Error(errMsg)
13035
14096
  } else {
14097
+ // #A11Y7/U2 — success is the other end of the `uploadStarted` transition.
14098
+ // It lives in this branch rather than at the indicator chokepoint above so
14099
+ // it can never fire alongside the error announcement and overwrite it: a
14100
+ // polite region is latest-wins, and a generic "complete" landing on top of
14101
+ // a specific server message would lose the message.
14102
+ if ( $uploadTriger && $uploadTriger.form ) {
14103
+ announceA11yStatus($uploadTriger.form, a11yLabel('uploadComplete'));
14104
+ }
13036
14105
 
13037
14106
  var fieldsObjectList = null
13038
14107
  , $li = null
@@ -13046,11 +14115,12 @@ function ValidatorPlugin(rules, data, formId, culture) {
13046
14115
  let resetLinkNeedToBeAdded = false;
13047
14116
  let $resetLink = document.getElementById(resetLinkId);
13048
14117
  let defaultClassNameArr = ['reset','js-upload-reset'];
14118
+ let resetLabel = $uploadTriger.getAttribute('data-gina-form-upload-reset-label') || 'Reset';
13049
14119
  if (!$resetLink) {
13050
14120
  resetLinkNeedToBeAdded = true;
13051
14121
  $resetLink = document.createElement('A');
13052
14122
  $resetLink.href = '#';
13053
- $resetLink.innerHTML = $uploadTriger.getAttribute('data-gina-form-upload-reset-label') || 'Reset';
14123
+ $resetLink.innerHTML = resetLabel;
13054
14124
  $resetLink.className = defaultClassNameArr.join(' ');
13055
14125
  $resetLink.id = resetLinkId;
13056
14126
  } else {
@@ -13058,7 +14128,7 @@ function ValidatorPlugin(rules, data, formId, culture) {
13058
14128
  $resetLink.href = '#';
13059
14129
  }
13060
14130
  if ( !$resetLink.innerHTML || $resetLink.innerHTML == '' ) {
13061
- $resetLink.innerHTML = $uploadTriger.getAttribute('data-gina-form-upload-reset-label') || 'Reset';
14131
+ $resetLink.innerHTML = resetLabel;
13062
14132
  }
13063
14133
  if ( typeof($resetLink.className) == 'undefined' ) {
13064
14134
  $resetLink.className = "";
@@ -13066,6 +14136,27 @@ function ValidatorPlugin(rules, data, formId, culture) {
13066
14136
  let classNameArr = merge($resetLink.className.split(/\s+/g), defaultClassNameArr);
13067
14137
  $resetLink.className = classNameArr.join(' ');
13068
14138
  }
14139
+ // #A11Y7/U5 — the control gina generates is an anchor, so assistive tech
14140
+ // announces it as a link and Space does not activate it. Give it button
14141
+ // semantics; the matching Space handler lives with the click binding in
14142
+ // `bindUploadResetOrDeleteTrigger`, because a role that PROMISES button
14143
+ // behaviour without delivering the key is worse than no role at all.
14144
+ // Stamped only on an anchor: a consumer supplying their own <button> under
14145
+ // this id already has the right semantics, and overriding an implicit role
14146
+ // is a regression. Strict `/^a$/i` here, unlike the loose `/a/i` guarding
14147
+ // `href` above (that one also matches TEXTAREA, CANVAS, LABEL…).
14148
+ if ( /^a$/i.test($resetLink.tagName) ) {
14149
+ $resetLink.setAttribute('role', 'button');
14150
+ // The accessible name names the file it acts on: these controls stack
14151
+ // one per staged file and are otherwise identical, so a controls list
14152
+ // reads "Reset, Reset, Reset". The visible label stays the PREFIX so
14153
+ // the name still contains it (WCAG 2.5.3 Label in Name), and it reuses
14154
+ // the consumer's own `-reset-label`, so it is translated wherever that
14155
+ // already is.
14156
+ if ( files[f].originalFilename ) {
14157
+ $resetLink.setAttribute('aria-label', resetLabel + ' ' + files[f].originalFilename);
14158
+ }
14159
+ }
13069
14160
  $resetLink.style.display = 'none';
13070
14161
 
13071
14162
  // image preview
@@ -13076,6 +14167,12 @@ function ValidatorPlugin(rules, data, formId, culture) {
13076
14167
  ) {
13077
14168
  let $img = document.createElement('IMG');
13078
14169
  $img.src = files[f].tmpUri;
14170
+ // #A11Y7/U1 — the preview tells the user WHICH file is staged, so it is
14171
+ // informative content, not decoration; without a text alternative assistive
14172
+ // tech falls back to reading the temp URI aloud, once per staged file.
14173
+ // Empty string when the name is missing: a silently ignored image beats
14174
+ // announcing a placeholder.
14175
+ $img.alt = files[f].originalFilename || '';
13079
14176
  $img.style.display = 'none';
13080
14177
  $img.setAttribute('data-upload-original-filename', files[f].originalFilename);
13081
14178
  $img.setAttribute('data-upload-reset-link-id', $resetLink.id);
@@ -13171,6 +14268,12 @@ function ValidatorPlugin(rules, data, formId, culture) {
13171
14268
  // creating IMG tag
13172
14269
  let $img = document.createElement('IMG');
13173
14270
  $img.src = files[f][key].tmpUri;
14271
+ // #A11Y7/U1 — name the file the user actually chose, not the
14272
+ // server-generated preview variant recorded just below: the
14273
+ // generated name is an internal artefact and means nothing to
14274
+ // the person listening. Same empty-string fallback as the
14275
+ // no-preview branch.
14276
+ $img.alt = files[f].originalFilename || '';
13174
14277
  $img.style.display = 'none';
13175
14278
  // retrieve img `originalFilename` (not the preview img[key] `originalFilename`)
13176
14279
  // these 2 metadatas will be used to remove files from the server
@@ -13411,6 +14514,39 @@ function ValidatorPlugin(rules, data, formId, culture) {
13411
14514
  // display upload input
13412
14515
  restoreUploadAffordance($uploadTrigger, uploadHiddenClass);
13413
14516
 
14517
+ // #A11Y7/U5 — the control about to be removed is the one the user
14518
+ // just activated, so it holds focus; removing a focused element
14519
+ // drops focus to <body> and the user loses their place with no
14520
+ // signal. Move focus first, onto the file input that owns the
14521
+ // staged files — which `restoreUploadAffordance` just made visible
14522
+ // again. Guarded on it actually holding focus, so a programmatic
14523
+ // reset never yanks focus away from wherever the user really is.
14524
+ if ( document.activeElement === $resetLink || $resetLink.contains(document.activeElement) ) {
14525
+ if ( typeof($uploadTrigger.focus) == 'function' ) {
14526
+ $uploadTrigger.focus();
14527
+ }
14528
+ // #A11Y5's lesson, applied: `typeof focus == 'function'` is true
14529
+ // for EVERY HTMLElement, and focus() is a silent no-op on one
14530
+ // that cannot take focus (hidden, disabled, detached) — so
14531
+ // confirm the move instead of assuming it. Signal-only: there is
14532
+ // no better fallback than the document, and failing loudly here
14533
+ // would break a removal that otherwise succeeded.
14534
+ if ( document.activeElement !== $uploadTrigger ) {
14535
+ console.debug('[FormValidator][upload]['+ ($uploadTrigger.id || '?') +'] '+ bindingType +': could not move focus to the file input before removing the '+ bindingType +' control; focus falls back to the document.');
14536
+ }
14537
+ }
14538
+ // Announce the removal. `aria-live="polite"` queues rather than
14539
+ // interrupts, so this lands after the focus move above has been
14540
+ // announced instead of racing it. Function replacer, not a string:
14541
+ // a file name may contain `$`, and a string replacement would expand
14542
+ // `$&` / `$1` patterns inside it.
14543
+ if ( $uploadTrigger.form && childNodeFile ) {
14544
+ announceA11yStatus(
14545
+ $uploadTrigger.form,
14546
+ a11yLabel('fileRemoved').replace('%s', function () { return childNodeFile; })
14547
+ );
14548
+ }
14549
+
13414
14550
  // remove link & image - must be done last
13415
14551
  // (the reset/delete link's own click listener dies with the node)
13416
14552
  $resetLink.remove();
@@ -15420,6 +16556,19 @@ function ValidatorPlugin(rules, data, formId, culture) {
15420
16556
 
15421
16557
  onUploadResetOrDelete($uploadTrigger, bindingType);
15422
16558
  });
16559
+ // #A11Y7/U5 — the generated control is an anchor carrying `role="button"`,
16560
+ // and a button MUST activate on Space; an anchor natively answers Enter only,
16561
+ // so without this the role would advertise an interaction that does not work.
16562
+ // Anchors only: a real <button> already fires click on Space, and binding
16563
+ // this there would run the removal twice.
16564
+ if ( /^a$/i.test($uploadResetOrDeleteTrigger.tagName) ) {
16565
+ addListener(gina, $uploadResetOrDeleteTrigger, 'keydown', function onUploadResetOrDeleteTriggerKeydown(e) {
16566
+ if ( e.key === ' ' || e.keyCode === 32 ) {
16567
+ e.preventDefault(); // Space would otherwise scroll the page
16568
+ onUploadResetOrDelete($uploadTrigger, bindingType);
16569
+ }
16570
+ });
16571
+ }
15423
16572
  $uploadResetOrDeleteTrigger.isBinded = true;
15424
16573
  } else {
15425
16574
  console.warn('[FormValidator::bindForm][upload]['+$uploadTrigger.id+'] : did not find `upload '+bindingType+' trigger`.\nPlease, make sure that your '+bindingType+' element ID is `'+ uploadResetOrDeleteTriggerId +'`, or add to your file input ('+ $uploadTrigger.id +') -> `data-gina-form-upload-'+bindingType+'-trigger="your-custom-id"` definition.');
@@ -15871,6 +17020,13 @@ function ValidatorPlugin(rules, data, formId, culture) {
15871
17020
  throw new Error('Validator::bindForm($target, customRule) could not bind form `'+ $target +'`\n'+err.stack );
15872
17021
  }
15873
17022
 
17023
+ // #A11Y2 — stand the polite live region up HERE, at bind time, rather than lazily on the
17024
+ // first error. Creating and populating it in one tick reaches assistive tech as a single
17025
+ // mutation batch that is commonly never spoken, so the first announcement per form used to
17026
+ // be the one most likely lost. Every registration path funnels through bindForm, so this
17027
+ // one call covers them all. Announcing stays announceA11yError's job; this only creates.
17028
+ ensureA11yLiveRegion($target);
17029
+
15874
17030
  // console.debug('binding for: '+ _id);
15875
17031
  var withRules = false, rule = null, evt = '', proceed = null;
15876
17032
 
@@ -16619,6 +17775,24 @@ function ValidatorPlugin(rules, data, formId, culture) {
16619
17775
  updateUploadProgressIndicator($uploadForm.uploadProperties.progressContainer, 'preparing');
16620
17776
  }
16621
17777
 
17778
+ // #A11Y7/U2 — announce the transition into "uploading". Note this
17779
+ // is deliberately NOT gated on `progressContainer`: the indicator is
17780
+ // opt-in by presence, but a user who cannot see it is precisely the
17781
+ // one who needs to be told the upload began.
17782
+ // The announcement targets the file input's REAL owning form, not
17783
+ // `$uploadForm` (the virtual one built just above): the real form is
17784
+ // where `bindForm` already stood up a live region, so upload status
17785
+ // and validation errors share one region instead of splitting across
17786
+ // two. Selection always sends today — the `.send()` below is
17787
+ // unconditional and `data-gina-file-autosend="false"` is still a TODO
17788
+ // — so this is a true start; gate it if that ever lands.
17789
+ if ( $uploadForm.uploadProperties && $uploadForm.uploadProperties.uploadTriggerId ) {
17790
+ var $a11yUploadInput = document.getElementById($uploadForm.uploadProperties.uploadTriggerId);
17791
+ if ( $a11yUploadInput && $a11yUploadInput.form ) {
17792
+ announceA11yStatus($a11yUploadInput.form, a11yLabel('uploadStarted'));
17793
+ }
17794
+ }
17795
+
16622
17796
  // binding form
16623
17797
  try {
16624
17798
  var $uploadFormValidator = getFormById(uploadFormId);
@@ -17544,6 +18718,44 @@ function ValidatorPlugin(rules, data, formId, culture) {
17544
18718
  _evt = 'reset.'+_evt
17545
18719
  }
17546
18720
 
18721
+ // #B246 — a disabled submit trigger must not run the submit cycle.
18722
+ // `updateSubmitTriggerState()` marks an invalid form's trigger with
18723
+ // `aria-disabled="true"` and never with native `disabled`, precisely so
18724
+ // the click still reaches us. Nothing READ that marker, so the trigger
18725
+ // stayed fully operable: a click ran the collect -> validate ->
18726
+ // `validate.<id>` chain and only the `isValid()` gate stopped the send.
18727
+ // The trigger was inert in appearance ONLY, and that also contradicted
18728
+ // the `aria-disabled` contract it advertises to assistive tech (which
18729
+ // requires the author to suppress the action).
18730
+ //
18731
+ // Intercept HERE — before the `submit.<id>` dispatch below — so
18732
+ // `bindSubmitEl`'s handler never runs, `isSubmitting` is never latched
18733
+ // and no send path is reachable. The click is still answered with a
18734
+ // display-only reveal, so the user learns WHY the trigger is disabled
18735
+ // (which is what the operable-trigger design was bought for).
18736
+ // Checked on the CLICKED element, not `$formInstance.submitTrigger`:
18737
+ // a form may carry several submit buttons and only one registers.
18738
+ if ( /^submit\./i.test(_evt) && isTriggerDisabled($el) ) {
18739
+ cancelEvent(event);
18740
+ revealValidationState( instance.$forms[$form.id] || $form );
18741
+ return false;
18742
+ }
18743
+
18744
+ // #B247 — arm the loading state on the element the user actually
18745
+ // operated. DELIBERATELY after the #B246 gate above: a disabled
18746
+ // trigger returns without starting anything, so arming it there
18747
+ // would strand exactly the state this feature exists to release.
18748
+ // `resolveTrigger` climbs to the owning button/anchor, because a
18749
+ // click on a wrapped label (`<button><span>Save</span></button>`)
18750
+ // targets the inner node, and the state belongs on the control the
18751
+ // user perceives as the trigger.
18752
+ if ( /^submit\./i.test(_evt) ) {
18753
+ armSubmitLoading(
18754
+ instance.$forms[$form.id] || $form,
18755
+ loadingState.resolveTrigger($el, $target)
18756
+ );
18757
+ }
18758
+
17547
18759
  if (gina.events[_evt]) {
17548
18760
  cancelEvent(event);
17549
18761
 
@@ -17679,6 +18891,22 @@ function ValidatorPlugin(rules, data, formId, culture) {
17679
18891
  instance.$forms[_id].isSubmitting = false;
17680
18892
  }
17681
18893
 
18894
+ // #B247 — release the trigger-scoped loading state. THIS is the path
18895
+ // that stranded it: a rejected submit produces no request at all, so
18896
+ // none of the XHR releases can ever run, and only the framework knows
18897
+ // the submit was refused before it started. Left armed, the trigger
18898
+ // stays visually loading until the page or popin is reloaded.
18899
+ //
18900
+ // Gated on nothing being in flight: a form can be submitted again
18901
+ // while an earlier request is still running, and that later attempt
18902
+ // may well be the one that fails validation. Releasing unconditionally
18903
+ // would clear the state belonging to the request still in progress —
18904
+ // whose own settle is what must release it.
18905
+ var _loadingForm = instance.$forms[_id] || $form;
18906
+ if ( !/^true$/i.test(_loadingForm.isSending) ) {
18907
+ disarmSubmitLoading(_loadingForm);
18908
+ }
18909
+
17682
18910
  // #A11Y1 (slice 3) — failed submit: move focus to the first invalid field so
17683
18911
  // assistive tech announces it (accessible name + aria-invalid + the
17684
18912
  // aria-errormessage text). DOM order; hidden / unfocusable fields are skipped.
@@ -17696,7 +18924,13 @@ function ValidatorPlugin(rules, data, formId, culture) {
17696
18924
  && typeof(_aField.focus) == 'function'
17697
18925
  ) {
17698
18926
  _aField.focus();
17699
- break;
18927
+ // #A11Y5 (V8) — kept in step with focusFirstInvalidField:
18928
+ // only stop once focus actually moved, since focus() is a
18929
+ // no-op on an unfocusable host that still passes the
18930
+ // typeof-function gate above.
18931
+ if ( typeof(document) === 'undefined' || !document || document.activeElement === _aField ) {
18932
+ break;
18933
+ }
17700
18934
  }
17701
18935
  }
17702
18936
  }
@@ -17917,6 +19151,27 @@ function ValidatorPlugin(rules, data, formId, culture) {
17917
19151
  return false;
17918
19152
  }
17919
19153
 
19154
+ // #B247 — a submit that never went through the click proxy still gets a
19155
+ // loading state: Enter inside a field, `form.submit()`, `$validator.submit()`,
19156
+ // and — less obviously — a click on a wrapped label such as
19157
+ // `<button type="submit"><span>Save</span></button>`, whose event.target is
19158
+ // the span (no `.type`), so the click proxy's submit branch never fires and
19159
+ // the event surfaces here instead. There is no clicked element on this path,
19160
+ // so the registered trigger is the best and only referent. Placed after the
19161
+ // disabled + defaultPrevented guards above, which both return first.
19162
+ // NB. the disabled check at the top of this handler reads a DOMParser copy of
19163
+ // the form's innerHTML, so it sees neither a live `disabled` set by JS nor the
19164
+ // `aria-disabled` marker `updateSubmitTriggerState` uses. Re-check the live
19165
+ // node with #B246's predicate: without it, every Enter-key submit on an
19166
+ // invalid live-check form would flash the trigger armed for the length of the
19167
+ // validation pass before the rejected branch released it.
19168
+ if ($formInstance) {
19169
+ var $loadingTrigger = document.getElementById($formInstance.submitTrigger);
19170
+ if ( !isTriggerDisabled($loadingTrigger) ) {
19171
+ armSubmitLoading($formInstance, $loadingTrigger);
19172
+ }
19173
+ }
19174
+
17920
19175
  if (withRules || isBinded) {
17921
19176
  cancelEvent(e);
17922
19177
  }
@@ -18106,13 +19361,17 @@ function ValidatorPlugin(rules, data, formId, culture) {
18106
19361
  } else if ( document.getElementById($formInstance.submitTrigger) ) {
18107
19362
  // Represent the "invalid + live-check-on" state with aria-disabled + a class
18108
19363
  // INSTEAD of the native `disabled` property. A natively-disabled <button>
18109
- // emits no click event, so the form-level click -> validate -> show-all-errors
18110
- // -> focus-first guard (the `validate.<id>` listener) never fires: the button
18111
- // looks dead and gives zero feedback. aria-disabled keeps the trigger operable,
18112
- // so the click still runs validation (revealing every invalid field + focusing
18113
- // the first) while `isValid()` (the real gate) still blocks the send. Mirrors
18114
- // gina's own <a>-tag submit-trigger handling, which already uses aria-disabled
18115
- // in-flight. The show branch KEEPS clearing native `disabled` so a trigger
19364
+ // emits no click event at all, so the click cannot be observed and the user
19365
+ // gets no feedback about WHY the trigger is dead. aria-disabled keeps the
19366
+ // trigger focusable and perceivable, and clickProxyHandler INTERCEPTS the
19367
+ // click (#B246) to answer it with a display-only reveal: every invalid field
19368
+ // is rendered and the first is focused, while the submit cycle is never
19369
+ // entered. The trigger is therefore genuinely not operable — which is what
19370
+ // the aria-disabled contract promises assistive tech, and what the earlier
19371
+ // "keep it operable so the click can still validate" shape got wrong: it
19372
+ // left the real submit path reachable from a control announced as disabled.
19373
+ // Mirrors gina's own <a>-tag submit-trigger handling, which already uses
19374
+ // aria-disabled in-flight. The show branch KEEPS clearing native `disabled` so a trigger
18116
19375
  // rendered `disabled` in markup still enables on valid / when live-check is off.
18117
19376
  // Tag-agnostic: setAttribute/classList work for both <button> and <a> submit
18118
19377
  // triggers. Consumers must style the [aria-disabled="true"] /
@@ -18133,6 +19392,125 @@ function ValidatorPlugin(rules, data, formId, culture) {
18133
19392
  }
18134
19393
  }
18135
19394
 
19395
+ /**
19396
+ * isTriggerDisabled
19397
+ *
19398
+ * Tells whether a submit trigger is currently marked as disabled.
19399
+ * `updateSubmitTriggerState()` marks an invalid form's trigger with
19400
+ * `aria-disabled="true"` rather than the native `disabled` property, so a
19401
+ * native-only check can never see it. The predicate shape mirrors the popin
19402
+ * plugin's own trigger gate, so both subsystems agree on what "disabled"
19403
+ * means for a trigger.
19404
+ *
19405
+ * @param {object} $el - candidate trigger (DOMObject)
19406
+ *
19407
+ * @returns {boolean} true when the trigger must not be operated
19408
+ *
19409
+ * @example
19410
+ * isTriggerDisabled($button); // true while aria-disabled="true" is set
19411
+ *
19412
+ * @inner
19413
+ */
19414
+ var isTriggerDisabled = function($el) {
19415
+ if ( !$el || typeof($el.getAttribute) != 'function' ) {
19416
+ return false;
19417
+ }
19418
+ return (
19419
+ $el.getAttribute('disabled') != null && $el.getAttribute('disabled') != 'false'
19420
+ ||
19421
+ $el.getAttribute('aria-disabled') == 'true'
19422
+ );
19423
+ };
19424
+
19425
+ /**
19426
+ * focusFirstInvalidField
19427
+ *
19428
+ * Moves focus to the first field carrying an error, in DOM order, skipping
19429
+ * hidden and unfocusable controls (#A11Y1) so assistive tech announces the
19430
+ * accessible name + `aria-invalid` + the `aria-errormessage` text.
19431
+ *
19432
+ * NB. the `validate.<id>` guard keeps its own inline twin of this loop rather
19433
+ * than calling here. That is DELIBERATE: the inline shape is locked by source
19434
+ * pins in `test/core/validator-aria-invalid.test.js` (`_a11yErrs`, `_aField`),
19435
+ * so collapsing the two would break pins unrelated to #B246. Keep the two in
19436
+ * step if the focus rule ever changes.
19437
+ *
19438
+ * @param {object} $form - form target (DOMObject), not the instance
19439
+ * @param {object} errors - error map keyed by field name
19440
+ *
19441
+ * @returns {boolean} true when a field was focused
19442
+ *
19443
+ * @example
19444
+ * focusFirstInvalidField($form, result['fields'] || result['error']);
19445
+ *
19446
+ * @inner
19447
+ */
19448
+ var focusFirstInvalidField = function($form, errors) {
19449
+ if ( !$form || !errors ) {
19450
+ return false;
19451
+ }
19452
+ for (var i = 0, len = $form.length; i < len; ++i) {
19453
+ var $field = $form[i];
19454
+ var name = $field.getAttribute('name');
19455
+ if (
19456
+ name
19457
+ && typeof(errors[name]) != 'undefined'
19458
+ && ( typeof(errors[name].count) != 'function' || errors[name].count() > 0 )
19459
+ && $field.type != 'hidden'
19460
+ && typeof($field.focus) == 'function'
19461
+ ) {
19462
+ $field.focus();
19463
+ // #A11Y5 (V8) — `typeof focus == 'function'` is true for EVERY HTMLElement,
19464
+ // a custom element with neither `tabindex` nor `delegatesFocus` included,
19465
+ // whose focus() is a silent no-op (measured). Without confirming the move,
19466
+ // the loop reported success and stopped on a control that never took focus,
19467
+ // so a failed submit announced nothing. Confirming it is also what makes the
19468
+ // "skips unfocusable controls" contract above true rather than aspirational.
19469
+ if ( typeof(document) === 'undefined' || !document || document.activeElement === $field ) {
19470
+ return true;
19471
+ }
19472
+ }
19473
+ }
19474
+ return false;
19475
+ };
19476
+
19477
+ /**
19478
+ * revealValidationState
19479
+ *
19480
+ * Display-only validation pass, used when a DISABLED submit trigger is
19481
+ * clicked (#B246). It runs the same collect + validate chain the submit path
19482
+ * uses, but routes the result straight to the error renderer instead of
19483
+ * dispatching `validate.<id>` — so `bindSubmitEl`'s handler never runs, the
19484
+ * `isSubmitting` latch is never set and the `isValid()` send gate is never
19485
+ * reached. No request can start from this path by construction.
19486
+ *
19487
+ * The trigger state is re-synced from the fresh result, which also self-heals
19488
+ * a stale disabled marker on a form that has since become valid.
19489
+ *
19490
+ * @param {object} $formInstance - form instance (carries `.target` / `.rules`)
19491
+ *
19492
+ * @returns {void}
19493
+ *
19494
+ * @example
19495
+ * revealValidationState(instance.$forms['myForm']);
19496
+ *
19497
+ * @inner
19498
+ */
19499
+ var revealValidationState = function($formInstance) {
19500
+ if ( !$formInstance || !$formInstance.target ) {
19501
+ return;
19502
+ }
19503
+ var $target = $formInstance.target;
19504
+ var validationInfo = getFormValidationInfos($target, $formInstance.rules);
19505
+
19506
+ validate($target, validationInfo.fields, validationInfo.$fields, $formInstance.rules, function onDisabledTriggerReveal(result) {
19507
+ var errors = result['fields'] || result['error'];
19508
+ handleErrorsDisplay($target, errors, result['data']);
19509
+ focusFirstInvalidField($target, errors);
19510
+ updateSubmitTriggerState($formInstance, result.isValid());
19511
+ });
19512
+ };
19513
+
18136
19514
  /**
18137
19515
  * getFormValidationInfos
18138
19516
  *
@@ -19691,13 +21069,14 @@ if ( ( typeof(module) !== 'undefined' ) && module.exports ) {
19691
21069
  module.exports = ValidatorPlugin
19692
21070
  } else if ( typeof(define) === 'function' && define.amd) {
19693
21071
  // Publish as AMD module
19694
- define('gina/validator', ['utils/events', 'utils/dom', 'utils/effects', 'utils/data', 'lib/form-validator', 'lib/routing'], function(){ return ValidatorPlugin })
21072
+ define('gina/validator', ['utils/events', 'utils/dom', 'utils/effects', 'utils/data', 'lib/form-validator', 'lib/routing', 'lib/loading-state'], function(){ return ValidatorPlugin })
19695
21073
  };
19696
- define('gina/popin', [ 'require', 'lib/domain', 'lib/merge', 'utils/events' ], function (require) {
21074
+ define('gina/popin', [ 'require', 'lib/domain', 'lib/loading-state', 'lib/merge', 'utils/events' ], function (require) {
19697
21075
 
19698
21076
  // TODO - Integrate dialog-polyfill : https://github.com/GoogleChrome/dialog-polyfill/blob/master/dist/dialog-polyfill.js
19699
21077
  var Domain = require('lib/domain');
19700
21078
  var domainInstance = null;
21079
+ var loadingState = require('lib/loading-state');
19701
21080
  var merge = require('lib/merge');
19702
21081
 
19703
21082
  require('utils/events'); // events
@@ -20163,7 +21542,19 @@ define('gina/popin', [ 'require', 'lib/domain', 'lib/merge', 'utils/events' ], f
20163
21542
  * a body scroll-lock attribute (CSS), and best-effort `inert` on background
20164
21543
  * siblings. Torn down by removeNonModalShims() on close.
20165
21544
  *
21545
+ * "Background siblings" has to include any OTHER popin still open inside the
21546
+ * shared container: popinOpen() never closes the popin it supersedes, so a
21547
+ * second non-modal dialog would otherwise leave the first one fully
21548
+ * keyboard-reachable behind it. The container itself must stay reachable — it
21549
+ * holds $el — so it is descended into rather than skipped.
21550
+ *
21551
+ * Only `[open]` dialogs are inerted. A closed `<dialog>` is already
21552
+ * `display:none` per the UA stylesheet, so it is unreachable without help.
21553
+ *
20166
21554
  * @inner
21555
+ * @param {object} $popin - popin descriptor; the Escape handler closes by $popin.name
21556
+ * @param {HTMLElement} $el - the dialog element being shown non-modally
21557
+ * @returns {void}
20167
21558
  */
20168
21559
  function applyNonModalShims($popin, $el) {
20169
21560
  if ( !$el ) {
@@ -20182,11 +21573,30 @@ define('gina/popin', [ 'require', 'lib/domain', 'lib/merge', 'utils/events' ], f
20182
21573
  $el.__ginaOnKeydown = onKeydown;
20183
21574
  $el.addEventListener('keydown', onKeydown);
20184
21575
 
20185
- // Best-effort background inert (skip the dialog, its container and ancestors).
21576
+ // Best-effort background inert (skip the dialog and its ancestors; descend
21577
+ // into the container so a superseded popin does not stay reachable).
20186
21578
  var siblings = document.body.children;
20187
21579
  var b = 0, len = siblings.length;
20188
21580
  for (; b < len; ++b) {
20189
- if ( siblings[b] === $el || siblings[b] === instance.target || siblings[b].contains($el) ) {
21581
+ if ( siblings[b] === instance.target ) {
21582
+ // The container holds $el, so it cannot be inerted wholesale — but any
21583
+ // OTHER popin still open inside it would remain keyboard-reachable
21584
+ // behind this one. Closed dialogs are already display:none (UA
21585
+ // stylesheet), so [open] is the whole set that needs help.
21586
+ var $open = siblings[b].querySelectorAll('dialog[open]');
21587
+ var d = 0, dLen = $open.length;
21588
+ for (; d < dLen; ++d) {
21589
+ if ( $open[d] === $el || $open[d].contains($el) ) {
21590
+ continue;
21591
+ }
21592
+ if ( $open[d].getAttribute('inert') == null ) {
21593
+ $open[d].setAttribute('inert', '');
21594
+ $open[d].setAttribute('data-gina-popin-inert', 'true');
21595
+ }
21596
+ }
21597
+ continue;
21598
+ }
21599
+ if ( siblings[b] === $el || siblings[b].contains($el) ) {
20190
21600
  continue;
20191
21601
  }
20192
21602
  if ( siblings[b].getAttribute('inert') == null ) {
@@ -21551,6 +22961,43 @@ define('gina/popin', [ 'require', 'lib/domain', 'lib/merge', 'utils/events' ], f
21551
22961
  }
21552
22962
  }
21553
22963
 
22964
+ /**
22965
+ * armPopinTrigger
22966
+ *
22967
+ * Makes the control that opened a popin inoperable for the duration of the load
22968
+ * and marks it with the shared `data-gina-loading`.
22969
+ *
22970
+ * That attribute is NOT a duplicate of the container's `data-gina-popin-loading`:
22971
+ * they sit on different elements and answer different questions — the container
22972
+ * one says "this popin is filling", this one says "this control is busy". The
22973
+ * second is the same attribute the validator and link plugins write, so a single
22974
+ * stylesheet covers every busy control a project has, whichever plugin started
22975
+ * the work.
22976
+ *
22977
+ * Scoped to the TRIGGER rather than absorbing the whole arm block, deliberately:
22978
+ * the two arm sites must keep their own literal `showLoadingShell($popin, $el)`
22979
+ * call, which is what `popin.test.js` pins to prove the skeleton shows at both.
22980
+ *
22981
+ * @inner
22982
+ * @param {HTMLElement} [$popinTrigger] - the control that opened it, when it has an id
22983
+ * @returns {void}
22984
+ *
22985
+ * @example
22986
+ * armPopinTrigger(document.getElementById($popin.openTrigger));
22987
+ */
22988
+ function armPopinTrigger($popinTrigger) {
22989
+ if ( !$popinTrigger ) {
22990
+ return;
22991
+ }
22992
+ // For A tag: aria-disabled=true
22993
+ if ( /^A$/i.test($popinTrigger.tagName) ) {
22994
+ $popinTrigger.setAttribute('aria-disabled', true);
22995
+ } else {
22996
+ $popinTrigger.setAttribute('disabled', true);
22997
+ }
22998
+ loadingState.arm($popinTrigger);
22999
+ }
23000
+
21554
23001
  /**
21555
23002
  * popinLoad
21556
23003
  *
@@ -21728,14 +23175,7 @@ define('gina/popin', [ 'require', 'lib/domain', 'lib/merge', 'utils/events' ], f
21728
23175
  if ( /^(1|3)$/.test(xhr.readyState) ) {
21729
23176
  $popin.target.setAttribute('data-gina-popin-loading', true);
21730
23177
  showLoadingShell($popin, $el);
21731
- if ($popinTrigger) {
21732
- // For A tag: aria-disabled=true
21733
- if ( /^A$/i.test($popinTrigger.tagName) ) {
21734
- $popinTrigger.setAttribute('aria-disabled', true);
21735
- } else {
21736
- $popinTrigger.setAttribute('disabled', true);
21737
- }
21738
- }
23178
+ armPopinTrigger($popinTrigger);
21739
23179
  }
21740
23180
 
21741
23181
  // catching ready state cb
@@ -21744,18 +23184,12 @@ define('gina/popin', [ 'require', 'lib/domain', 'lib/merge', 'utils/events' ], f
21744
23184
  if ( /^(1|3)$/.test(xhr.readyState) ) {
21745
23185
  $popin.target.setAttribute('data-gina-popin-loading', true);
21746
23186
  showLoadingShell($popin, $el);
21747
- if ($popinTrigger) {
21748
- // For A tag: aria-disabled=true
21749
- if ( /^A$/i.test($popinTrigger.tagName) ) {
21750
- $popinTrigger.setAttribute('aria-disabled', true);
21751
- } else {
21752
- $popinTrigger.setAttribute('disabled', true);
21753
- }
21754
- }
23187
+ armPopinTrigger($popinTrigger);
21755
23188
  }
21756
23189
  if (xhr.readyState == 4) {
21757
23190
  // Fixed: clear loading state on response complete — data-gina-popin-loading
21758
23191
  // was set on readyState 1|3 but never removed, leaving the overlay blocked.
23192
+ // Covers every status including 0, since the branch is on readyState alone.
21759
23193
  $popin.target.removeAttribute('data-gina-popin-loading');
21760
23194
  if ($popinTrigger) {
21761
23195
  if ( /^A$/i.test($popinTrigger.tagName) ) {
@@ -21763,6 +23197,7 @@ define('gina/popin', [ 'require', 'lib/domain', 'lib/merge', 'utils/events' ], f
21763
23197
  } else {
21764
23198
  $popinTrigger.removeAttribute('disabled');
21765
23199
  }
23200
+ loadingState.disarm($popinTrigger);
21766
23201
  }
21767
23202
  // 200, 201, 201' etc ...
21768
23203
  var result = null;
@@ -22343,6 +23778,12 @@ define('gina/popin', [ 'require', 'lib/domain', 'lib/merge', 'utils/events' ], f
22343
23778
  // Fixed: clear loading state on reset — defensive cleanup for navigation
22344
23779
  // within a popin that was in loading state when reset was called.
22345
23780
  $el.removeAttribute('data-gina-popin-loading');
23781
+ // Trigger-scoped release. This function is ALSO reached from routing
23782
+ // transitions that never call popinClose, so without this the control
23783
+ // that opened a popin torn down by a route change would stay armed.
23784
+ // Resolved here rather than passed in: popinUnbind only has $popin, and
23785
+ // `disarm` is a no-op on the null a trigger-less popin resolves to.
23786
+ loadingState.disarm( document.getElementById($popin.openTrigger) );
22346
23787
 
22347
23788
  // removing from FormValidator instance
22348
23789
  if ($validatorInstance) {
@@ -22455,6 +23896,7 @@ define('gina/popin', [ 'require', 'lib/domain', 'lib/merge', 'utils/events' ], f
22455
23896
  } else {
22456
23897
  $popinTrigger.removeAttribute('disabled', true);
22457
23898
  }
23899
+ loadingState.disarm($popinTrigger);
22458
23900
  // a11y: return focus to the trigger that opened the popin.
22459
23901
  if ( typeof($popinTrigger.focus) == 'function' ) {
22460
23902
  $popinTrigger.focus();
@@ -25939,7 +27381,8 @@ require([
25939
27381
  "lib/form-validator",
25940
27382
  "lib/collection",
25941
27383
  "lib/domain",
25942
- "lib/routing"
27384
+ "lib/routing",
27385
+ "lib/loading-state"
25943
27386
  ], function () {
25944
27387
  // Boot the popin handler at page load so the declarative `data-gina-dialog` API is
25945
27388
  // active WITHOUT bundle code calling `new gina.popin()`. Constructing the handler