gina 0.6.2 → 0.6.3

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 (570) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +41 -12
  3. package/ROADMAP.md +3 -2
  4. package/bin/cli +19 -2
  5. package/framework/v0.6.3/VERSION +1 -0
  6. package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/html/statusbar.html +6 -0
  7. package/framework/v0.6.3/core/asset/plugin/dist/vendor/gina/html/statusbar.html.br +0 -0
  8. package/framework/v0.6.3/core/asset/plugin/dist/vendor/gina/html/statusbar.html.gz +0 -0
  9. package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/inspector/index.html +1 -0
  10. package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/inspector/inspector.css +50 -1
  11. package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/inspector/inspector.js +457 -57
  12. package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/js/gina.js +1457 -58
  13. package/framework/v0.6.3/core/asset/plugin/dist/vendor/gina/js/gina.min.js +625 -0
  14. package/framework/v0.6.3/core/asset/plugin/dist/vendor/gina/js/gina.min.js.br +0 -0
  15. package/framework/v0.6.3/core/asset/plugin/dist/vendor/gina/js/gina.min.js.gz +0 -0
  16. package/framework/{v0.6.2 → v0.6.3}/core/connectors/couchbase/index.js +114 -0
  17. package/framework/{v0.6.2 → v0.6.3}/core/connectors/couchbase/lib/connector.js +14 -3
  18. package/framework/{v0.6.2 → v0.6.3}/core/connectors/couchbase/lib/session-store.js +14 -3
  19. package/framework/{v0.6.2 → v0.6.3}/core/connectors/couchbase/lib/session-store.v3.js +29 -2
  20. package/framework/{v0.6.2 → v0.6.3}/core/connectors/couchbase/lib/session-store.v4.js +29 -2
  21. package/framework/{v0.6.2 → v0.6.3}/core/connectors/mongodb/lib/session-store.js +24 -2
  22. package/framework/{v0.6.2 → v0.6.3}/core/connectors/redis/lib/session-store.js +33 -9
  23. package/framework/{v0.6.2 → v0.6.3}/core/connectors/scylladb/lib/session-store.js +25 -2
  24. package/framework/{v0.6.2 → v0.6.3}/core/connectors/sqlite/lib/session-store.js +26 -2
  25. package/framework/{v0.6.2 → v0.6.3}/core/controller/controller.js +201 -21
  26. package/framework/{v0.6.2 → v0.6.3}/core/controller/controller.render-nunjucks.js +8 -0
  27. package/framework/{v0.6.2 → v0.6.3}/core/controller/controller.render-swig.js +85 -4
  28. package/framework/{v0.6.2 → v0.6.3}/core/gna.js +23 -1
  29. package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/csrf/src/main.js +9 -5
  30. package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/hide-powered-by/README.md +20 -0
  31. package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/validator/src/form-validator.js +119 -43
  32. package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/validator/src/main.js +451 -9
  33. package/framework/{v0.6.2 → v0.6.3}/core/server.isaac.js +50 -54
  34. package/framework/{v0.6.2 → v0.6.3}/core/server.js +240 -3
  35. package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle/index.js +6 -4
  36. package/framework/{v0.6.2 → v0.6.3}/core/template/conf/settings.json +10 -0
  37. package/framework/{v0.6.2 → v0.6.3}/lib/cache/README.md +9 -1
  38. package/framework/{v0.6.2 → v0.6.3}/lib/cmd/audit/verify.js +11 -9
  39. package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/mcp-start.js +21 -12
  40. package/framework/{v0.6.2 → v0.6.3}/lib/cmd/connector/help.txt +1 -1
  41. package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/init.js +13 -1
  42. package/framework/{v0.6.2 → v0.6.3}/lib/dto/src/main.js +18 -11
  43. package/framework/{v0.6.2 → v0.6.3}/lib/logger/src/containers/file/index.js +29 -1
  44. package/framework/{v0.6.2 → v0.6.3}/lib/logger/src/containers/mq/speaker.js +30 -1
  45. package/framework/{v0.6.2 → v0.6.3}/lib/math/index.js +57 -6
  46. package/framework/{v0.6.2 → v0.6.3}/lib/merge/src/main.js +34 -5
  47. package/framework/{v0.6.2 → v0.6.3}/lib/render-cache/src/main.js +77 -4
  48. package/framework/{v0.6.2 → v0.6.3}/lib/routing/src/main.js +13 -1
  49. package/framework/{v0.6.2 → v0.6.3}/lib/routing-introspect/src/main.js +64 -2
  50. package/framework/v0.6.3/lib/secrets/src/backends/env.js +75 -0
  51. package/framework/{v0.6.2 → v0.6.3}/package.json +1 -1
  52. package/gna.js +5 -4
  53. package/llms.txt +19 -17
  54. package/package.json +5 -4
  55. package/schema/connectors.json +2 -2
  56. package/schema/routing.json +5 -0
  57. package/schema/settings.json +6 -1
  58. package/script/generate_gna_types.js +1 -1
  59. package/script/soak/couchbase-soak.js +1316 -0
  60. package/types/globals.d.ts +2 -0
  61. package/types/gna.d.ts +5 -0
  62. package/types/index.d.ts +1 -0
  63. package/utils/helper.js +38 -7
  64. package/framework/v0.6.2/VERSION +0 -1
  65. package/framework/v0.6.2/core/asset/plugin/dist/vendor/gina/html/statusbar.html.br +0 -0
  66. package/framework/v0.6.2/core/asset/plugin/dist/vendor/gina/html/statusbar.html.gz +0 -0
  67. package/framework/v0.6.2/core/asset/plugin/dist/vendor/gina/js/gina.min.js +0 -603
  68. package/framework/v0.6.2/core/asset/plugin/dist/vendor/gina/js/gina.min.js.br +0 -0
  69. package/framework/v0.6.2/core/asset/plugin/dist/vendor/gina/js/gina.min.js.gz +0 -0
  70. package/framework/v0.6.2/lib/secrets/src/backends/env.js +0 -60
  71. /package/framework/{v0.6.2 → v0.6.3}/AUTHORS +0 -0
  72. /package/framework/{v0.6.2 → v0.6.3}/LICENSE +0 -0
  73. /package/framework/{v0.6.2 → v0.6.3}/core/asset/html/nolayout.html +0 -0
  74. /package/framework/{v0.6.2 → v0.6.3}/core/asset/html/static.html +0 -0
  75. /package/framework/{v0.6.2 → v0.6.3}/core/asset/img/android-chrome-192x192.png +0 -0
  76. /package/framework/{v0.6.2 → v0.6.3}/core/asset/img/android-chrome-512x512.png +0 -0
  77. /package/framework/{v0.6.2 → v0.6.3}/core/asset/img/apple-touch-icon.png +0 -0
  78. /package/framework/{v0.6.2 → v0.6.3}/core/asset/img/favicon-16x16.png +0 -0
  79. /package/framework/{v0.6.2 → v0.6.3}/core/asset/img/favicon-32x32.png +0 -0
  80. /package/framework/{v0.6.2 → v0.6.3}/core/asset/img/favicon.ico +0 -0
  81. /package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/README.md +0 -0
  82. /package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/beemaster/beemaster.css +0 -0
  83. /package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/beemaster/beemaster.js +0 -0
  84. /package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/beemaster/index.html +0 -0
  85. /package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/css/gina.min.css +0 -0
  86. /package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/css/gina.min.css.br +0 -0
  87. /package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/css/gina.min.css.gz +0 -0
  88. /package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/inspector/have_heart_one-webfont.woff2 +0 -0
  89. /package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/inspector/logo.svg +0 -0
  90. /package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js +0 -0
  91. /package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js.br +0 -0
  92. /package/framework/{v0.6.2 → v0.6.3}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js.gz +0 -0
  93. /package/framework/{v0.6.2 → v0.6.3}/core/config.js +0 -0
  94. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/ai/index.js +0 -0
  95. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/ai/lib/connector.js +0 -0
  96. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/couchbase/lib/connector.v3.js +0 -0
  97. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/couchbase/lib/connector.v4.js +0 -0
  98. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/couchbase/lib/n1ql.js +0 -0
  99. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/duckdb/index.js +0 -0
  100. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/duckdb/lib/connector.js +0 -0
  101. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/mongodb/index.js +0 -0
  102. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/mongodb/lib/connector.js +0 -0
  103. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/mongodb/lib/job-store.js +0 -0
  104. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/mongodb/lib/pipeline-loader.js +0 -0
  105. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/mysql/index.js +0 -0
  106. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/mysql/lib/connector.js +0 -0
  107. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/postgresql/index.js +0 -0
  108. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/postgresql/lib/connector.js +0 -0
  109. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/redis/index.js +0 -0
  110. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/redis/lib/connector.js +0 -0
  111. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/redis/lib/job-store.js +0 -0
  112. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/redis/lib/render-cache-store.js +0 -0
  113. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/scylladb/index.js +0 -0
  114. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/scylladb/lib/connector.js +0 -0
  115. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/sql-parser.js +0 -0
  116. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/sqlite/index.js +0 -0
  117. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/sqlite/lib/connector.js +0 -0
  118. /package/framework/{v0.6.2 → v0.6.3}/core/connectors/sqlite/lib/job-store.js +0 -0
  119. /package/framework/{v0.6.2 → v0.6.3}/core/content.encoding +0 -0
  120. /package/framework/{v0.6.2 → v0.6.3}/core/controller/controller.framework.js +0 -0
  121. /package/framework/{v0.6.2 → v0.6.3}/core/controller/controller.render-json.js +0 -0
  122. /package/framework/{v0.6.2 → v0.6.3}/core/controller/controller.render-nunjucks-async.js +0 -0
  123. /package/framework/{v0.6.2 → v0.6.3}/core/controller/controller.render-stream.js +0 -0
  124. /package/framework/{v0.6.2 → v0.6.3}/core/controller/controller.render-swig-async.js +0 -0
  125. /package/framework/{v0.6.2 → v0.6.3}/core/controller/controller.render-v1.js +0 -0
  126. /package/framework/{v0.6.2 → v0.6.3}/core/controller/index.js +0 -0
  127. /package/framework/{v0.6.2 → v0.6.3}/core/controller/inspector-window-emit.js +0 -0
  128. /package/framework/{v0.6.2 → v0.6.3}/core/controller/release-banner.js +0 -0
  129. /package/framework/{v0.6.2 → v0.6.3}/core/dev/index.js +0 -0
  130. /package/framework/{v0.6.2 → v0.6.3}/core/dev/lib/class.js +0 -0
  131. /package/framework/{v0.6.2 → v0.6.3}/core/dev/lib/factory.js +0 -0
  132. /package/framework/{v0.6.2 → v0.6.3}/core/dev/lib/tools.js +0 -0
  133. /package/framework/{v0.6.2 → v0.6.3}/core/locales/README.md +0 -0
  134. /package/framework/{v0.6.2 → v0.6.3}/core/locales/currency.json +0 -0
  135. /package/framework/{v0.6.2 → v0.6.3}/core/locales/dist/language/en.json +0 -0
  136. /package/framework/{v0.6.2 → v0.6.3}/core/locales/dist/language/fr.json +0 -0
  137. /package/framework/{v0.6.2 → v0.6.3}/core/locales/dist/region/en.json +0 -0
  138. /package/framework/{v0.6.2 → v0.6.3}/core/locales/dist/region/fr.json +0 -0
  139. /package/framework/{v0.6.2 → v0.6.3}/core/locales/index.js +0 -0
  140. /package/framework/{v0.6.2 → v0.6.3}/core/mime.types +0 -0
  141. /package/framework/{v0.6.2 → v0.6.3}/core/model/entity.js +0 -0
  142. /package/framework/{v0.6.2 → v0.6.3}/core/model/index.js +0 -0
  143. /package/framework/{v0.6.2 → v0.6.3}/core/model/template/entityFactory.js +0 -0
  144. /package/framework/{v0.6.2 → v0.6.3}/core/model/template/index.js +0 -0
  145. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/README.md +0 -0
  146. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/index.js +0 -0
  147. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/csrf/README.md +0 -0
  148. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/csrf/package.json +0 -0
  149. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/README.md +0 -0
  150. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/coep/README.md +0 -0
  151. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/coep/package.json +0 -0
  152. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/coep/src/main.js +0 -0
  153. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/coop/README.md +0 -0
  154. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/coop/package.json +0 -0
  155. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/coop/src/main.js +0 -0
  156. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/corp/README.md +0 -0
  157. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/corp/package.json +0 -0
  158. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/corp/src/main.js +0 -0
  159. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/csp/README.md +0 -0
  160. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/csp/package.json +0 -0
  161. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/csp/src/main.js +0 -0
  162. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/hide-powered-by/package.json +0 -0
  163. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/hide-powered-by/src/main.js +0 -0
  164. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/hsts/README.md +0 -0
  165. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/hsts/package.json +0 -0
  166. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/hsts/src/main.js +0 -0
  167. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/origin-agent-cluster/README.md +0 -0
  168. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/origin-agent-cluster/package.json +0 -0
  169. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/origin-agent-cluster/src/main.js +0 -0
  170. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/package.json +0 -0
  171. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/referrer-policy/README.md +0 -0
  172. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/referrer-policy/package.json +0 -0
  173. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/referrer-policy/src/main.js +0 -0
  174. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/src/main.js +0 -0
  175. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-content-type-options/README.md +0 -0
  176. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-content-type-options/package.json +0 -0
  177. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-content-type-options/src/main.js +0 -0
  178. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-dns-prefetch-control/README.md +0 -0
  179. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-dns-prefetch-control/package.json +0 -0
  180. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-dns-prefetch-control/src/main.js +0 -0
  181. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-download-options/README.md +0 -0
  182. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-download-options/package.json +0 -0
  183. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-download-options/src/main.js +0 -0
  184. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-frame-options/README.md +0 -0
  185. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-frame-options/package.json +0 -0
  186. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-frame-options/src/main.js +0 -0
  187. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-permitted-cross-domain-policies/README.md +0 -0
  188. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-permitted-cross-domain-policies/package.json +0 -0
  189. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-permitted-cross-domain-policies/src/main.js +0 -0
  190. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-xss-protection/README.md +0 -0
  191. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-xss-protection/package.json +0 -0
  192. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/security-headers/x-xss-protection/src/main.js +0 -0
  193. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/session/README.md +0 -0
  194. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/session/package.json +0 -0
  195. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/session/src/main.js +0 -0
  196. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/storage/README.md +0 -0
  197. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/storage/build.json +0 -0
  198. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/storage/package.json +0 -0
  199. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/storage/src/main.js +0 -0
  200. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/validator/README.md +0 -0
  201. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/validator/build.json +0 -0
  202. /package/framework/{v0.6.2 → v0.6.3}/core/plugins/lib/validator/package.json +0 -0
  203. /package/framework/{v0.6.2 → v0.6.3}/core/router.js +0 -0
  204. /package/framework/{v0.6.2 → v0.6.3}/core/server.express.js +0 -0
  205. /package/framework/{v0.6.2 → v0.6.3}/core/status.codes +0 -0
  206. /package/framework/{v0.6.2 → v0.6.3}/core/template/_gitignore +0 -0
  207. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle/config/app.json +0 -0
  208. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle/config/connectors.json +0 -0
  209. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle/config/routing.json +0 -0
  210. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle/config/settings.json +0 -0
  211. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle/config/settings.server.json +0 -0
  212. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle/config/templates.json +0 -0
  213. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle/config/watchers.json +0 -0
  214. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle/controllers/controller.content.js +0 -0
  215. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle/controllers/controller.js +0 -0
  216. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle/controllers/setup.js +0 -0
  217. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle/locales/en.json +0 -0
  218. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_namespace/controllers/controller.js +0 -0
  219. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_public/css/default.css +0 -0
  220. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_public/css/home.css +0 -0
  221. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_public/css/vendor/readme.md +0 -0
  222. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_public/favicon.ico +0 -0
  223. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_public/js/components/x-checklist.js +0 -0
  224. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_public/js/vendor/readme.md +0 -0
  225. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_public/manifest.webmanifest +0 -0
  226. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_public/readme.md +0 -0
  227. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_public/sw.js +0 -0
  228. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_templates/handlers/main.js +0 -0
  229. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_templates/html/content/homepage.html +0 -0
  230. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_templates/html/includes/error-msg-noscript.html +0 -0
  231. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_templates/html/includes/error-msg-outdated-browser.html +0 -0
  232. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_templates/html/includes/x-checklist.html +0 -0
  233. /package/framework/{v0.6.2 → v0.6.3}/core/template/boilerplate/bundle_templates/html/layouts/main.html +0 -0
  234. /package/framework/{v0.6.2 → v0.6.3}/core/template/command/gina.bat.tpl +0 -0
  235. /package/framework/{v0.6.2 → v0.6.3}/core/template/command/gina.tpl +0 -0
  236. /package/framework/{v0.6.2 → v0.6.3}/core/template/conf/env.json +0 -0
  237. /package/framework/{v0.6.2 → v0.6.3}/core/template/conf/manifest.json +0 -0
  238. /package/framework/{v0.6.2 → v0.6.3}/core/template/conf/package.json +0 -0
  239. /package/framework/{v0.6.2 → v0.6.3}/core/template/conf/statics.json +0 -0
  240. /package/framework/{v0.6.2 → v0.6.3}/core/template/conf/templates.json +0 -0
  241. /package/framework/{v0.6.2 → v0.6.3}/core/template/error/client/json/401.json +0 -0
  242. /package/framework/{v0.6.2 → v0.6.3}/core/template/error/client/json/403.json +0 -0
  243. /package/framework/{v0.6.2 → v0.6.3}/core/template/error/client/json/404.json +0 -0
  244. /package/framework/{v0.6.2 → v0.6.3}/core/template/error/server/html/50x.html +0 -0
  245. /package/framework/{v0.6.2 → v0.6.3}/core/template/error/server/json/500.json +0 -0
  246. /package/framework/{v0.6.2 → v0.6.3}/core/template/error/server/json/503.json +0 -0
  247. /package/framework/{v0.6.2 → v0.6.3}/core/template/extensions/logger/config.json +0 -0
  248. /package/framework/{v0.6.2 → v0.6.3}/helpers/console.js +0 -0
  249. /package/framework/{v0.6.2 → v0.6.3}/helpers/context.js +0 -0
  250. /package/framework/{v0.6.2 → v0.6.3}/helpers/data/LICENSE +0 -0
  251. /package/framework/{v0.6.2 → v0.6.3}/helpers/data/README.md +0 -0
  252. /package/framework/{v0.6.2 → v0.6.3}/helpers/data/package.json +0 -0
  253. /package/framework/{v0.6.2 → v0.6.3}/helpers/data/src/main.js +0 -0
  254. /package/framework/{v0.6.2 → v0.6.3}/helpers/dateFormat.js +0 -0
  255. /package/framework/{v0.6.2 → v0.6.3}/helpers/index.js +0 -0
  256. /package/framework/{v0.6.2 → v0.6.3}/helpers/json/LICENSE +0 -0
  257. /package/framework/{v0.6.2 → v0.6.3}/helpers/json/README.md +0 -0
  258. /package/framework/{v0.6.2 → v0.6.3}/helpers/json/package.json +0 -0
  259. /package/framework/{v0.6.2 → v0.6.3}/helpers/json/src/main.js +0 -0
  260. /package/framework/{v0.6.2 → v0.6.3}/helpers/path.js +0 -0
  261. /package/framework/{v0.6.2 → v0.6.3}/helpers/plugins/README.md +0 -0
  262. /package/framework/{v0.6.2 → v0.6.3}/helpers/plugins/package.json +0 -0
  263. /package/framework/{v0.6.2 → v0.6.3}/helpers/plugins/src/api-error.js +0 -0
  264. /package/framework/{v0.6.2 → v0.6.3}/helpers/plugins/src/main.js +0 -0
  265. /package/framework/{v0.6.2 → v0.6.3}/helpers/prototypes.js +0 -0
  266. /package/framework/{v0.6.2 → v0.6.3}/helpers/task.js +0 -0
  267. /package/framework/{v0.6.2 → v0.6.3}/helpers/text.js +0 -0
  268. /package/framework/{v0.6.2 → v0.6.3}/lib/admin/package.json +0 -0
  269. /package/framework/{v0.6.2 → v0.6.3}/lib/admin/src/main.js +0 -0
  270. /package/framework/{v0.6.2 → v0.6.3}/lib/archiver/README.md +0 -0
  271. /package/framework/{v0.6.2 → v0.6.3}/lib/archiver/build.json +0 -0
  272. /package/framework/{v0.6.2 → v0.6.3}/lib/archiver/package.json +0 -0
  273. /package/framework/{v0.6.2 → v0.6.3}/lib/archiver/src/dep/jszip.min.js +0 -0
  274. /package/framework/{v0.6.2 → v0.6.3}/lib/archiver/src/main.js +0 -0
  275. /package/framework/{v0.6.2 → v0.6.3}/lib/async/package.json +0 -0
  276. /package/framework/{v0.6.2 → v0.6.3}/lib/async/src/main.js +0 -0
  277. /package/framework/{v0.6.2 → v0.6.3}/lib/audit/package.json +0 -0
  278. /package/framework/{v0.6.2 → v0.6.3}/lib/audit/src/main.js +0 -0
  279. /package/framework/{v0.6.2 → v0.6.3}/lib/audit-store.js +0 -0
  280. /package/framework/{v0.6.2 → v0.6.3}/lib/authn/package.json +0 -0
  281. /package/framework/{v0.6.2 → v0.6.3}/lib/authn/src/lockout.js +0 -0
  282. /package/framework/{v0.6.2 → v0.6.3}/lib/authn/src/main.js +0 -0
  283. /package/framework/{v0.6.2 → v0.6.3}/lib/authn/src/totp.js +0 -0
  284. /package/framework/{v0.6.2 → v0.6.3}/lib/authz-gate/package.json +0 -0
  285. /package/framework/{v0.6.2 → v0.6.3}/lib/authz-gate/src/main.js +0 -0
  286. /package/framework/{v0.6.2 → v0.6.3}/lib/cache/build.json +0 -0
  287. /package/framework/{v0.6.2 → v0.6.3}/lib/cache/package.json +0 -0
  288. /package/framework/{v0.6.2 → v0.6.3}/lib/cache/src/main.js +0 -0
  289. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/aliases.json +0 -0
  290. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/audit/arguments.json +0 -0
  291. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/audit/help.txt +0 -0
  292. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/add.js +0 -0
  293. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/arguments.json +0 -0
  294. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/build.js +0 -0
  295. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/copy.js +0 -0
  296. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/cp.js +0 -0
  297. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/help.js +0 -0
  298. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/help.txt +0 -0
  299. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/inc/name-rewrite.js +0 -0
  300. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/list.js +0 -0
  301. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/man.js +0 -0
  302. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/mcp.js +0 -0
  303. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/oas.js +0 -0
  304. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/openapi.js +0 -0
  305. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/remove.js +0 -0
  306. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/rename.js +0 -0
  307. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/restart.js +0 -0
  308. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/rm.js +0 -0
  309. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/start.js +0 -0
  310. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/status.js +0 -0
  311. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/stop.js +0 -0
  312. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/bundle/types.js +0 -0
  313. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/cache/arguments.json +0 -0
  314. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/cache/clear.js +0 -0
  315. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/cache/help.txt +0 -0
  316. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/cache/stats.js +0 -0
  317. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/connector/add.js +0 -0
  318. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/connector/arguments.json +0 -0
  319. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/connector/help.js +0 -0
  320. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/connector/infer.js +0 -0
  321. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/connector/list.js +0 -0
  322. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/connector/migrate.js +0 -0
  323. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/connector/models.js +0 -0
  324. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/connector/remove.js +0 -0
  325. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/connector/rm.js +0 -0
  326. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/connector/test.js +0 -0
  327. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/container/arguments.json +0 -0
  328. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/container/help.js +0 -0
  329. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/container/help.txt +0 -0
  330. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/container/man.js +0 -0
  331. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/container/ps.js +0 -0
  332. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/container/stop.js +0 -0
  333. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/controller/add.js +0 -0
  334. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/controller/arguments.json +0 -0
  335. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/controller/help.txt +0 -0
  336. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/controller/inc/args.js +0 -0
  337. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/controller/inc/namespace.js +0 -0
  338. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/controller/inc/reference-rewrite.js +0 -0
  339. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/controller/inc/reference-scan.js +0 -0
  340. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/controller/inc/scaffold.js +0 -0
  341. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/controller/remove.js +0 -0
  342. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/controller/rename.js +0 -0
  343. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/controller/rm.js +0 -0
  344. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/env/add.js +0 -0
  345. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/env/get.js +0 -0
  346. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/env/help.js +0 -0
  347. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/env/help.txt +0 -0
  348. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/env/link-dev.js +0 -0
  349. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/env/list.js +0 -0
  350. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/env/remove.js +0 -0
  351. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/env/rm.js +0 -0
  352. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/env/set.js +0 -0
  353. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/env/unset.js +0 -0
  354. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/env/use.js +0 -0
  355. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/add.js +0 -0
  356. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/arguments.json +0 -0
  357. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/build.js +0 -0
  358. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/dot.js +0 -0
  359. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/get.js +0 -0
  360. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/help.js +0 -0
  361. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/help.txt +0 -0
  362. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/link-node-modules.js +0 -0
  363. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/link.js +0 -0
  364. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/list.js +0 -0
  365. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/man.js +0 -0
  366. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/msg.json +0 -0
  367. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/open.js +0 -0
  368. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/remove.js +0 -0
  369. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/reset.js +0 -0
  370. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/restart.js +0 -0
  371. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/set.js +0 -0
  372. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/start.js +0 -0
  373. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/status.js +0 -0
  374. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/stop.js +0 -0
  375. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/tail.js +0 -0
  376. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/update.js +0 -0
  377. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/framework/version.js +0 -0
  378. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/gina-dev.1.md +0 -0
  379. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/gina-framework.1.md +0 -0
  380. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/gina.1.md +0 -0
  381. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/helper.js +0 -0
  382. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/i18n/add.js +0 -0
  383. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/i18n/arguments.json +0 -0
  384. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/i18n/export.js +0 -0
  385. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/i18n/help.js +0 -0
  386. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/i18n/help.txt +0 -0
  387. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/i18n/import.js +0 -0
  388. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/i18n/scan.js +0 -0
  389. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/image/_host.js +0 -0
  390. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/image/arguments.json +0 -0
  391. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/image/build.js +0 -0
  392. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/image/help.js +0 -0
  393. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/image/help.txt +0 -0
  394. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/image/list.js +0 -0
  395. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/image/man.js +0 -0
  396. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/image/rm.js +0 -0
  397. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/image/run.js +0 -0
  398. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/index.js +0 -0
  399. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/inspector/help.js +0 -0
  400. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/inspector/help.txt +0 -0
  401. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/inspector/open.js +0 -0
  402. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/man-render.js +0 -0
  403. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/minion/arguments.json +0 -0
  404. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/minion/help.js +0 -0
  405. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/minion/help.txt +0 -0
  406. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/minion/kill.js +0 -0
  407. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/minion/list.js +0 -0
  408. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/msg.json +0 -0
  409. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/port/help.js +0 -0
  410. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/port/help.txt +0 -0
  411. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/port/inc/scan.js +0 -0
  412. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/port/list.js +0 -0
  413. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/port/reset.js +0 -0
  414. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/port/set.js +0 -0
  415. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/add.js +0 -0
  416. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/arguments.json +0 -0
  417. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/backup.js +0 -0
  418. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/build.js +0 -0
  419. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/help.js +0 -0
  420. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/help.txt +0 -0
  421. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/import.js +0 -0
  422. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/list.js +0 -0
  423. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/man.js +0 -0
  424. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/move.js +0 -0
  425. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/remove.js +0 -0
  426. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/rename.js +0 -0
  427. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/restart.js +0 -0
  428. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/restore.js +0 -0
  429. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/rm.js +0 -0
  430. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/start.js +0 -0
  431. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/status.js +0 -0
  432. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/project/stop.js +0 -0
  433. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/protocol/arguments.json +0 -0
  434. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/protocol/help.js +0 -0
  435. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/protocol/help.txt +0 -0
  436. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/protocol/list.js +0 -0
  437. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/protocol/remove.js +0 -0
  438. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/protocol/set.js +0 -0
  439. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/scope/add.js +0 -0
  440. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/scope/help.js +0 -0
  441. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/scope/help.txt +0 -0
  442. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/scope/link-local.js +0 -0
  443. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/scope/link-production.js +0 -0
  444. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/scope/list.js +0 -0
  445. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/scope/remove.js +0 -0
  446. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/scope/rm.js +0 -0
  447. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/scope/use.js +0 -0
  448. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/secrets/arguments.json +0 -0
  449. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/secrets/check.js +0 -0
  450. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/secrets/help.js +0 -0
  451. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/secrets/help.txt +0 -0
  452. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/secrets/scan.js +0 -0
  453. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/service/help.js +0 -0
  454. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/service/help.txt +0 -0
  455. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/service/list.js +0 -0
  456. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/service/man.js +0 -0
  457. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/service/start.js +0 -0
  458. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd/view/add.js +0 -0
  459. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd-status-format/package.json +0 -0
  460. /package/framework/{v0.6.2 → v0.6.3}/lib/cmd-status-format/src/main.js +0 -0
  461. /package/framework/{v0.6.2 → v0.6.3}/lib/collection/README.md +0 -0
  462. /package/framework/{v0.6.2 → v0.6.3}/lib/collection/build.json +0 -0
  463. /package/framework/{v0.6.2 → v0.6.3}/lib/collection/package.json +0 -0
  464. /package/framework/{v0.6.2 → v0.6.3}/lib/collection/src/main.js +0 -0
  465. /package/framework/{v0.6.2 → v0.6.3}/lib/config.js +0 -0
  466. /package/framework/{v0.6.2 → v0.6.3}/lib/connector-config/package.json +0 -0
  467. /package/framework/{v0.6.2 → v0.6.3}/lib/connector-config/src/main.js +0 -0
  468. /package/framework/{v0.6.2 → v0.6.3}/lib/connector-error/package.json +0 -0
  469. /package/framework/{v0.6.2 → v0.6.3}/lib/connector-error/src/main.js +0 -0
  470. /package/framework/{v0.6.2 → v0.6.3}/lib/connector-registry/package.json +0 -0
  471. /package/framework/{v0.6.2 → v0.6.3}/lib/connector-registry/src/main.js +0 -0
  472. /package/framework/{v0.6.2 → v0.6.3}/lib/cron/README.md +0 -0
  473. /package/framework/{v0.6.2 → v0.6.3}/lib/cron/package.json +0 -0
  474. /package/framework/{v0.6.2 → v0.6.3}/lib/cron/src/main.js +0 -0
  475. /package/framework/{v0.6.2 → v0.6.3}/lib/domain/LICENSE +0 -0
  476. /package/framework/{v0.6.2 → v0.6.3}/lib/domain/README.md +0 -0
  477. /package/framework/{v0.6.2 → v0.6.3}/lib/domain/package.json +0 -0
  478. /package/framework/{v0.6.2 → v0.6.3}/lib/domain/src/main.js +0 -0
  479. /package/framework/{v0.6.2 → v0.6.3}/lib/dto/package.json +0 -0
  480. /package/framework/{v0.6.2 → v0.6.3}/lib/dto-pipe/package.json +0 -0
  481. /package/framework/{v0.6.2 → v0.6.3}/lib/dto-pipe/src/main.js +0 -0
  482. /package/framework/{v0.6.2 → v0.6.3}/lib/dto-types/package.json +0 -0
  483. /package/framework/{v0.6.2 → v0.6.3}/lib/dto-types/src/main.js +0 -0
  484. /package/framework/{v0.6.2 → v0.6.3}/lib/generator/index.js +0 -0
  485. /package/framework/{v0.6.2 → v0.6.3}/lib/i18n/package.json +0 -0
  486. /package/framework/{v0.6.2 → v0.6.3}/lib/i18n/src/main.js +0 -0
  487. /package/framework/{v0.6.2 → v0.6.3}/lib/image-build/package.json +0 -0
  488. /package/framework/{v0.6.2 → v0.6.3}/lib/image-build/src/main.js +0 -0
  489. /package/framework/{v0.6.2 → v0.6.3}/lib/index.js +0 -0
  490. /package/framework/{v0.6.2 → v0.6.3}/lib/inherits/LICENSE +0 -0
  491. /package/framework/{v0.6.2 → v0.6.3}/lib/inherits/README.md +0 -0
  492. /package/framework/{v0.6.2 → v0.6.3}/lib/inherits/package.json +0 -0
  493. /package/framework/{v0.6.2 → v0.6.3}/lib/inherits/src/main.js +0 -0
  494. /package/framework/{v0.6.2 → v0.6.3}/lib/inspector-events/package.json +0 -0
  495. /package/framework/{v0.6.2 → v0.6.3}/lib/inspector-events/src/main.js +0 -0
  496. /package/framework/{v0.6.2 → v0.6.3}/lib/inspector-redact/package.json +0 -0
  497. /package/framework/{v0.6.2 → v0.6.3}/lib/inspector-redact/src/main.js +0 -0
  498. /package/framework/{v0.6.2 → v0.6.3}/lib/instrument/package.json +0 -0
  499. /package/framework/{v0.6.2 → v0.6.3}/lib/instrument/src/main.js +0 -0
  500. /package/framework/{v0.6.2 → v0.6.3}/lib/job/package.json +0 -0
  501. /package/framework/{v0.6.2 → v0.6.3}/lib/job/src/main.js +0 -0
  502. /package/framework/{v0.6.2 → v0.6.3}/lib/job-store.js +0 -0
  503. /package/framework/{v0.6.2 → v0.6.3}/lib/json-config-header/package.json +0 -0
  504. /package/framework/{v0.6.2 → v0.6.3}/lib/json-config-header/src/main.js +0 -0
  505. /package/framework/{v0.6.2 → v0.6.3}/lib/logger/README.md +0 -0
  506. /package/framework/{v0.6.2 → v0.6.3}/lib/logger/package.json +0 -0
  507. /package/framework/{v0.6.2 → v0.6.3}/lib/logger/src/containers/default/index.js +0 -0
  508. /package/framework/{v0.6.2 → v0.6.3}/lib/logger/src/containers/file/lib/logrotator/README.md +0 -0
  509. /package/framework/{v0.6.2 → v0.6.3}/lib/logger/src/containers/file/lib/logrotator/index.js +0 -0
  510. /package/framework/{v0.6.2 → v0.6.3}/lib/logger/src/containers/mq/index.js +0 -0
  511. /package/framework/{v0.6.2 → v0.6.3}/lib/logger/src/containers/mq/listener.js +0 -0
  512. /package/framework/{v0.6.2 → v0.6.3}/lib/logger/src/helper.js +0 -0
  513. /package/framework/{v0.6.2 → v0.6.3}/lib/logger/src/main.js +0 -0
  514. /package/framework/{v0.6.2 → v0.6.3}/lib/mcp-dispatch/package.json +0 -0
  515. /package/framework/{v0.6.2 → v0.6.3}/lib/mcp-dispatch/src/main.js +0 -0
  516. /package/framework/{v0.6.2 → v0.6.3}/lib/mcp-http/package.json +0 -0
  517. /package/framework/{v0.6.2 → v0.6.3}/lib/mcp-http/src/main.js +0 -0
  518. /package/framework/{v0.6.2 → v0.6.3}/lib/mcp-server/package.json +0 -0
  519. /package/framework/{v0.6.2 → v0.6.3}/lib/mcp-server/src/main.js +0 -0
  520. /package/framework/{v0.6.2 → v0.6.3}/lib/merge/README.md +0 -0
  521. /package/framework/{v0.6.2 → v0.6.3}/lib/merge/package.json +0 -0
  522. /package/framework/{v0.6.2 → v0.6.3}/lib/metrics/package.json +0 -0
  523. /package/framework/{v0.6.2 → v0.6.3}/lib/metrics/src/main.js +0 -0
  524. /package/framework/{v0.6.2 → v0.6.3}/lib/model.js +0 -0
  525. /package/framework/{v0.6.2 → v0.6.3}/lib/net-locality/package.json +0 -0
  526. /package/framework/{v0.6.2 → v0.6.3}/lib/net-locality/src/main.js +0 -0
  527. /package/framework/{v0.6.2 → v0.6.3}/lib/nunjucks-filters/README.md +0 -0
  528. /package/framework/{v0.6.2 → v0.6.3}/lib/nunjucks-filters/package.json +0 -0
  529. /package/framework/{v0.6.2 → v0.6.3}/lib/nunjucks-filters/src/main.js +0 -0
  530. /package/framework/{v0.6.2 → v0.6.3}/lib/nunjucks-resolver/package.json +0 -0
  531. /package/framework/{v0.6.2 → v0.6.3}/lib/nunjucks-resolver/src/main.js +0 -0
  532. /package/framework/{v0.6.2 → v0.6.3}/lib/proc.js +0 -0
  533. /package/framework/{v0.6.2 → v0.6.3}/lib/release-watch/package.json +0 -0
  534. /package/framework/{v0.6.2 → v0.6.3}/lib/release-watch/src/main.js +0 -0
  535. /package/framework/{v0.6.2 → v0.6.3}/lib/render-cache/package.json +0 -0
  536. /package/framework/{v0.6.2 → v0.6.3}/lib/render-cache-store.js +0 -0
  537. /package/framework/{v0.6.2 → v0.6.3}/lib/routing/README.md +0 -0
  538. /package/framework/{v0.6.2 → v0.6.3}/lib/routing/build.json +0 -0
  539. /package/framework/{v0.6.2 → v0.6.3}/lib/routing/package.json +0 -0
  540. /package/framework/{v0.6.2 → v0.6.3}/lib/routing/src/radix.js +0 -0
  541. /package/framework/{v0.6.2 → v0.6.3}/lib/routing-introspect/package.json +0 -0
  542. /package/framework/{v0.6.2 → v0.6.3}/lib/secrets/package.json +0 -0
  543. /package/framework/{v0.6.2 → v0.6.3}/lib/secrets/src/main.js +0 -0
  544. /package/framework/{v0.6.2 → v0.6.3}/lib/session-store.js +0 -0
  545. /package/framework/{v0.6.2 → v0.6.3}/lib/shell.js +0 -0
  546. /package/framework/{v0.6.2 → v0.6.3}/lib/sqlite-driver.js +0 -0
  547. /package/framework/{v0.6.2 → v0.6.3}/lib/state.js +0 -0
  548. /package/framework/{v0.6.2 → v0.6.3}/lib/swig-filters/README.md +0 -0
  549. /package/framework/{v0.6.2 → v0.6.3}/lib/swig-filters/package.json +0 -0
  550. /package/framework/{v0.6.2 → v0.6.3}/lib/swig-filters/src/main.js +0 -0
  551. /package/framework/{v0.6.2 → v0.6.3}/lib/swig-resolver/package.json +0 -0
  552. /package/framework/{v0.6.2 → v0.6.3}/lib/swig-resolver/src/main.js +0 -0
  553. /package/framework/{v0.6.2 → v0.6.3}/lib/template-loaders/package.json +0 -0
  554. /package/framework/{v0.6.2 → v0.6.3}/lib/template-loaders/src/loaders/http.js +0 -0
  555. /package/framework/{v0.6.2 → v0.6.3}/lib/template-loaders/src/loaders/memory.js +0 -0
  556. /package/framework/{v0.6.2 → v0.6.3}/lib/template-loaders/src/main.js +0 -0
  557. /package/framework/{v0.6.2 → v0.6.3}/lib/url/README.md +0 -0
  558. /package/framework/{v0.6.2 → v0.6.3}/lib/url/index.js +0 -0
  559. /package/framework/{v0.6.2 → v0.6.3}/lib/url/routing.json +0 -0
  560. /package/framework/{v0.6.2 → v0.6.3}/lib/uuid/package.json +0 -0
  561. /package/framework/{v0.6.2 → v0.6.3}/lib/uuid/src/main.js +0 -0
  562. /package/framework/{v0.6.2 → v0.6.3}/lib/validator.js +0 -0
  563. /package/framework/{v0.6.2 → v0.6.3}/lib/watcher/package.json +0 -0
  564. /package/framework/{v0.6.2 → v0.6.3}/lib/watcher/src/main.js +0 -0
  565. /package/framework/{v0.6.2 → v0.6.3}/lib/ws-framing/package.json +0 -0
  566. /package/framework/{v0.6.2 → v0.6.3}/lib/ws-framing/src/main.js +0 -0
  567. /package/framework/{v0.6.2 → v0.6.3}/lib/ws-query/package.json +0 -0
  568. /package/framework/{v0.6.2 → v0.6.3}/lib/ws-query/src/main.js +0 -0
  569. /package/framework/{v0.6.2 → v0.6.3}/lib/ws-session/package.json +0 -0
  570. /package/framework/{v0.6.2 → v0.6.3}/lib/ws-session/src/main.js +0 -0
@@ -0,0 +1,1316 @@
1
+ 'use strict';
2
+ /**
3
+ * couchbase-soak.js — #CN12: connector-level SDK × Server soak harness
4
+ * (couchbase first).
5
+ *
6
+ * Screens a Couchbase Node SDK candidate (e.g. an SDK bump under evaluation)
7
+ * against a caller-chosen Couchbase Server BEFORE a consuming project adopts
8
+ * it: the harness scaffolds a throwaway gina project in FULL ISOLATION
9
+ * (self-bootstrapping gina home under os.tmpdir(), HOME override — the real
10
+ * ~/.gina and real projects are never touched), installs the CANDIDATE SDK
11
+ * into that project (the connector resolves `couchbase` from the project's
12
+ * own node_modules, so the install IS the version selector), builds and
13
+ * boots the bundle for the prod env, then drives the connector's three
14
+ * surfaces under sustained concurrent load for a fixed duration:
15
+ *
16
+ * query — N1QL entity methods through the connector's real dispatch
17
+ * (`adhoc: false`, positional params, `$scope` substitution),
18
+ * including a `request_plus` scan-consistency arm;
19
+ * kv — KV through the entity `getConnection()` collection handle, in
20
+ * BOTH forms consumers use: the 4-arg optional-callback form
21
+ * (`coll.insert(key, doc, opts, cb)`) and the promise form
22
+ * (insert → get → periodic remove cycle, expiry-bounded);
23
+ * session — the couchbase express-session store (set/get/touch/destroy
24
+ * churn through real HTTP requests with rotating cookie jars).
25
+ *
26
+ * PASS CRITERIA (the whole point — aimed at the silent-death class where a
27
+ * process under sustained SDK load exits cleanly with no JS stack):
28
+ * 1. the bundle process SURVIVES the full duration — ANY premature exit,
29
+ * explicitly INCLUDING a clean `exit 0`, is a FAILURE;
30
+ * 2. no unbounded RSS growth (least-squares slope over the tail half of
31
+ * the samples above --rss-slope MB/min AND total tail growth above
32
+ * --rss-floor MB fails);
33
+ * 3. no error-rate drift (tail-half error rate above --drift-factor × the
34
+ * whole-run rate, with at least --drift-min tail errors, fails);
35
+ * 4. every requested arm actually did work (an arm with zero successful
36
+ * operations fails — a dead arm is not a passing arm).
37
+ *
38
+ * Exit codes: 0 = PASS · 1 = FAIL (criteria) · 2 = harness/setup error.
39
+ *
40
+ * HONEST LIMITATION: a generic soak is a SCREEN, not proof. It exercises the
41
+ * connector's real code paths under load, but not your application's workload
42
+ * shape — run it as the FIRST filter on an SDK-bump candidate, with your own
43
+ * workload-shaped soak as the second gate. Recommended usage is two runs:
44
+ * first your current known-good SDK (baseline — also calibrates the RSS and
45
+ * drift thresholds on your hardware), then the candidate.
46
+ *
47
+ * REQUIREMENTS on the target (use a SCRATCH bucket, never a production one):
48
+ * - the bucket exists and the credentials can read/write it;
49
+ * - N1QL query service available; the harness issues
50
+ * `CREATE PRIMARY INDEX IF NOT EXISTS` on the bucket at setup (the query
51
+ * arm needs an index; USE KEYS and KV arms do not);
52
+ * - network reachability from this machine (preflighted; --skip-preflight
53
+ * to bypass);
54
+ * - `npm` on PATH (installs the candidate SDK + express-session into the
55
+ * throwaway project — network access required unless --sdk-path).
56
+ *
57
+ * Operator/consumer-invoked ONLY — never wired into the test suite or CI
58
+ * (CI has no cluster). Results land in ./tmp/couchbase-soak-<stamp>/
59
+ * (gitignored): samples.ndjson, boot.log, report.json, report.txt.
60
+ *
61
+ * Usage:
62
+ * node script/soak/couchbase-soak.js \
63
+ * --host=127.0.0.1 --database=soakbucket \
64
+ * --username=Administrator --password=secret \
65
+ * --sdk=4.6.1 --duration=15m
66
+ *
67
+ * Options (defaults):
68
+ * --host= (required) cluster host(s), comma-separated
69
+ * --database= (required) bucket name (also the models/ dir name)
70
+ * --username= (required) RBAC user
71
+ * --password= (required) RBAC password
72
+ * --sdk= couchbase npm version/range to install (XOR --sdk-path)
73
+ * --sdk-path= absolute path to an existing couchbase install dir
74
+ * (symlinked into the throwaway project instead of npm)
75
+ * --protocol=couchbase:// couchbases:// for TLS clusters
76
+ * --session-database= bucket for the session store entry (default: --database)
77
+ * --duration=15m soak length (30s / 15m / 1h forms)
78
+ * --concurrency=8 concurrent driver workers (round-robined over arms)
79
+ * --arms=query,kv,session subset selection
80
+ * --durability= `majority` adds DurabilityLevel.Majority to the
81
+ * callback-form KV insert (needs cluster support; off
82
+ * by default — topology-dependent)
83
+ * --kv-expiry=3600 expiry (s) stamped on soak KV documents
84
+ * --sample-interval=5 seconds between /soak/stats samples
85
+ * --rss-slope=5 FAIL threshold: tail RSS slope in MB/min
86
+ * --rss-floor=50 FAIL threshold: minimum tail RSS growth in MB
87
+ * --drift-factor=2 FAIL threshold: tail error rate vs whole-run rate
88
+ * --drift-min=20 FAIL threshold: minimum tail errors to call drift
89
+ * --setup-timeout=90 seconds allowed for the in-bundle setup route
90
+ * --start-port-from=9840 port scan base for the throwaway bundle
91
+ * --skip-preflight skip the TCP reachability check
92
+ * --keep keep the throwaway home + project for forensics
93
+ *
94
+ * @module script/soak/couchbase-soak
95
+ */
96
+
97
+ var fs = require('fs');
98
+ var os = require('os');
99
+ var path = require('path');
100
+ var net = require('net');
101
+ var http = require('http');
102
+ var { spawn, spawnSync } = require('child_process');
103
+
104
+ // ---------------------------------------------------------------------------
105
+ // Pure helpers (exported for the unit suite — no I/O, no process state)
106
+ // ---------------------------------------------------------------------------
107
+
108
+ /**
109
+ * Default option values + failure thresholds (exported so the unit suite and
110
+ * the docs can pin them).
111
+ * @constant
112
+ * @type {object}
113
+ */
114
+ var DEFAULTS = {
115
+ protocol : 'couchbase://',
116
+ duration : '15m',
117
+ concurrency : 8,
118
+ arms : ['query', 'kv', 'session'],
119
+ kvExpiry : 3600,
120
+ sampleInterval : 5,
121
+ rssSlope : 5, // MB/min over the tail half
122
+ rssFloor : 50, // MB minimum tail growth before slope can fail
123
+ driftFactor : 2, // tail error rate vs whole-run error rate
124
+ driftMin : 20, // minimum tail errors before drift can fail
125
+ setupTimeout : 90, // seconds
126
+ startPortFrom : 9840
127
+ };
128
+
129
+ var VALID_ARMS = ['query', 'kv', 'session'];
130
+
131
+ /**
132
+ * Parses a human duration (`90s`, `15m`, `2h`; a bare number means seconds)
133
+ * into milliseconds.
134
+ *
135
+ * @param {string|number} v
136
+ * @returns {number|null} milliseconds, or null when unparseable/non-positive
137
+ */
138
+ function parseDuration(v) {
139
+ if (v == null) { return null; }
140
+ var m = String(v).trim().match(/^(\d+(?:\.\d+)?)\s*(s|m|h)?$/i);
141
+ if (!m) { return null; }
142
+ var n = parseFloat(m[1]);
143
+ if (!(n > 0)) { return null; }
144
+ var unit = (m[2] || 's').toLowerCase();
145
+ var mult = (unit === 'h') ? 3600000 : (unit === 'm') ? 60000 : 1000;
146
+ return Math.round(n * mult);
147
+ }
148
+
149
+ /**
150
+ * Parses argv (`--name=value` / bare `--flag` forms) into a validated options
151
+ * object. Pure: no process access, no exit — callers decide what to do with
152
+ * `{ error }`.
153
+ *
154
+ * @param {string[]} argv - arguments AFTER the script path (process.argv.slice(2))
155
+ * @returns {{ok: boolean, error: (string|null), opts: (object|null)}}
156
+ */
157
+ function parseArgs(argv) {
158
+ var raw = {};
159
+ for (var i = 0; i < argv.length; i++) {
160
+ var a = argv[i];
161
+ if (a.indexOf('--') !== 0) { return { ok: false, error: 'unrecognized argument: ' + a, opts: null }; }
162
+ var eq = a.indexOf('=');
163
+ if (eq === -1) { raw[a.slice(2)] = true; }
164
+ else { raw[a.slice(2, eq)] = a.slice(eq + 1); }
165
+ }
166
+
167
+ function intOpt(name, dflt) {
168
+ if (raw[name] == null) { return dflt; }
169
+ var n = parseInt(raw[name], 10);
170
+ return isNaN(n) ? NaN : n;
171
+ }
172
+ function numOpt(name, dflt) {
173
+ if (raw[name] == null) { return dflt; }
174
+ var n = parseFloat(raw[name]);
175
+ return isNaN(n) ? NaN : n;
176
+ }
177
+
178
+ var required = ['host', 'database', 'username', 'password'];
179
+ for (var r = 0; r < required.length; r++) {
180
+ if (!raw[required[r]] || raw[required[r]] === true) {
181
+ return { ok: false, error: 'missing required option: --' + required[r], opts: null };
182
+ }
183
+ }
184
+ var hasSdk = !!raw.sdk && raw.sdk !== true;
185
+ var hasSdkPath = !!raw['sdk-path'] && raw['sdk-path'] !== true;
186
+ if (hasSdk === hasSdkPath) { // neither, or both
187
+ return { ok: false, error: 'exactly one of --sdk=<version> or --sdk-path=<dir> is required (the SDK candidate under test must be explicit — there is no default)', opts: null };
188
+ }
189
+
190
+ var durationMs = parseDuration(raw.duration || DEFAULTS.duration);
191
+ if (durationMs == null || durationMs < 10000) {
192
+ return { ok: false, error: 'invalid --duration (10s minimum; forms: 90s / 15m / 1h): ' + raw.duration, opts: null };
193
+ }
194
+
195
+ // raw.arms == null means "not given" (defaults apply); an explicit
196
+ // `--arms=` (empty) is a selection of zero arms and must error below.
197
+ var armsRaw = (raw.arms == null) ? DEFAULTS.arms.join(',') : String(raw.arms);
198
+ var arms = armsRaw.split(',').map(function (s) { return s.trim(); }).filter(Boolean);
199
+ var badArms = arms.filter(function (a) { return VALID_ARMS.indexOf(a) === -1; });
200
+ if (!arms.length || badArms.length) {
201
+ return { ok: false, error: 'invalid --arms (valid: ' + VALID_ARMS.join(',') + '): ' + (badArms.join(',') || '(empty)'), opts: null };
202
+ }
203
+
204
+ if (raw.durability != null && raw.durability !== 'majority') {
205
+ return { ok: false, error: 'invalid --durability (only `majority` is supported): ' + raw.durability, opts: null };
206
+ }
207
+
208
+ var opts = {
209
+ host : String(raw.host),
210
+ database : String(raw.database),
211
+ username : String(raw.username),
212
+ password : String(raw.password),
213
+ protocol : String(raw.protocol || DEFAULTS.protocol),
214
+ sdk : hasSdk ? String(raw.sdk) : null,
215
+ sdkPath : hasSdkPath ? String(raw['sdk-path']) : null,
216
+ sessionDatabase : String(raw['session-database'] || raw.database),
217
+ durationMs : durationMs,
218
+ concurrency : intOpt('concurrency', DEFAULTS.concurrency),
219
+ arms : arms,
220
+ durability : raw.durability === 'majority' ? 'majority' : null,
221
+ kvExpiry : intOpt('kv-expiry', DEFAULTS.kvExpiry),
222
+ sampleIntervalMs: numOpt('sample-interval', DEFAULTS.sampleInterval) * 1000,
223
+ thresholds : {
224
+ rssSlopeMBperMin : numOpt('rss-slope', DEFAULTS.rssSlope),
225
+ rssFloorMB : numOpt('rss-floor', DEFAULTS.rssFloor),
226
+ driftFactor : numOpt('drift-factor', DEFAULTS.driftFactor),
227
+ driftMinErrors : intOpt('drift-min', DEFAULTS.driftMin)
228
+ },
229
+ setupTimeoutMs : intOpt('setup-timeout', DEFAULTS.setupTimeout) * 1000,
230
+ startPortFrom : intOpt('start-port-from', DEFAULTS.startPortFrom),
231
+ skipPreflight : !!raw['skip-preflight'],
232
+ keep : !!raw.keep
233
+ };
234
+
235
+ var numeric = ['concurrency', 'kvExpiry', 'sampleIntervalMs', 'setupTimeoutMs', 'startPortFrom'];
236
+ for (var n = 0; n < numeric.length; n++) {
237
+ if (isNaN(opts[numeric[n]]) || opts[numeric[n]] <= 0) {
238
+ return { ok: false, error: 'invalid numeric option near --' + numeric[n], opts: null };
239
+ }
240
+ }
241
+ for (var t in opts.thresholds) {
242
+ if (isNaN(opts.thresholds[t]) || opts.thresholds[t] < 0) {
243
+ return { ok: false, error: 'invalid threshold option near ' + t, opts: null };
244
+ }
245
+ }
246
+ return { ok: true, error: null, opts: opts };
247
+ }
248
+
249
+ /**
250
+ * Least-squares slope of RSS over time, in MB per minute.
251
+ *
252
+ * @param {Array<{tMs: number, rssBytes: number}>} points
253
+ * @returns {number} slope in MB/min (0 for fewer than 2 points)
254
+ */
255
+ function lsqSlopeMBperMin(points) {
256
+ if (!points || points.length < 2) { return 0; }
257
+ var n = points.length, sx = 0, sy = 0, sxx = 0, sxy = 0;
258
+ for (var i = 0; i < n; i++) {
259
+ var x = points[i].tMs / 60000; // minutes
260
+ var y = points[i].rssBytes / (1024 * 1024); // MB
261
+ sx += x; sy += y; sxx += x * x; sxy += x * y;
262
+ }
263
+ var denom = n * sxx - sx * sx;
264
+ if (denom === 0) { return 0; }
265
+ return (n * sxy - sx * sy) / denom;
266
+ }
267
+
268
+ /**
269
+ * Evaluates a completed (or prematurely ended) soak run against the filed
270
+ * pass criteria. Pure — feed it the recorded inputs, get the verdict.
271
+ *
272
+ * Sample shape (counters are CUMULATIVE totals at sample time):
273
+ * { tMs, rssBytes, arms: { <arm>: { ok, errTransient, errPermanent, transport } } }
274
+ *
275
+ * @param {object} input
276
+ * @param {number} input.plannedMs - requested soak duration
277
+ * @param {?object} input.endedEarly - null, or {atMs, exit: {code, signal}}
278
+ * @param {Array} input.samples - chronological samples (shape above)
279
+ * @param {string[]} input.armsRequested - arms the run was asked to drive
280
+ * @param {object} input.thresholds - {rssSlopeMBperMin, rssFloorMB, driftFactor, driftMinErrors}
281
+ * @returns {{pass: boolean, failures: Array<{check: string, detail: string}>, warnings: string[], metrics: object}}
282
+ */
283
+ function evaluateRun(input) {
284
+ var failures = [];
285
+ var warnings = [];
286
+ var metrics = {};
287
+ var th = input.thresholds || {};
288
+ var samples = input.samples || [];
289
+
290
+ // 1 — liveness: the process must survive the full duration. A clean
291
+ // exit 0 before the end IS the failure class this harness exists for.
292
+ if (input.endedEarly) {
293
+ var ex = input.endedEarly.exit || {};
294
+ var how = (ex.code === 0)
295
+ ? 'a CLEAN exit 0 — this is the silent-death class, and it counts as FAILURE'
296
+ : (ex.signal ? 'signal ' + ex.signal : 'exit code ' + ex.code);
297
+ failures.push({
298
+ check : 'liveness',
299
+ detail : 'bundle process ended after ' + Math.round(input.endedEarly.atMs / 1000) + 's of a planned '
300
+ + Math.round(input.plannedMs / 1000) + 's soak (' + how + ')'
301
+ });
302
+ }
303
+
304
+ // 0 — evidence floor: a run with (almost) no samples proves nothing.
305
+ if (samples.length < 2) {
306
+ failures.push({ check: 'samples', detail: 'only ' + samples.length + ' sample(s) recorded — no basis for a verdict' });
307
+ return { pass: false, failures: failures, warnings: warnings, metrics: metrics };
308
+ }
309
+
310
+ var last = samples[samples.length - 1];
311
+ var tailStart = Math.floor(samples.length / 2);
312
+ var tail = samples.slice(tailStart);
313
+
314
+ // 2 — RSS growth over the tail half.
315
+ if (samples.length >= 6) {
316
+ var slope = lsqSlopeMBperMin(tail);
317
+ var growthMB = (tail[tail.length - 1].rssBytes - tail[0].rssBytes) / (1024 * 1024);
318
+ metrics.rssSlopeMBperMin = Math.round(slope * 100) / 100;
319
+ metrics.rssTailGrowthMB = Math.round(growthMB * 10) / 10;
320
+ metrics.rssLastMB = Math.round(last.rssBytes / (1024 * 1024));
321
+ if (slope > th.rssSlopeMBperMin && growthMB > th.rssFloorMB) {
322
+ failures.push({
323
+ check : 'rss',
324
+ detail : 'tail RSS slope ' + metrics.rssSlopeMBperMin + ' MB/min with ' + metrics.rssTailGrowthMB
325
+ + ' MB tail growth (thresholds: ' + th.rssSlopeMBperMin + ' MB/min AND ' + th.rssFloorMB + ' MB)'
326
+ });
327
+ }
328
+ } else {
329
+ warnings.push('rss check skipped — fewer than 6 samples (run longer or lower --sample-interval)');
330
+ }
331
+
332
+ // 3 + 4 — per-arm error drift and dead arms, from cumulative counters.
333
+ var armsRequested = input.armsRequested || [];
334
+ metrics.arms = {};
335
+ for (var a = 0; a < armsRequested.length; a++) {
336
+ var arm = armsRequested[a];
337
+ var endC = (last.arms || {})[arm];
338
+ var midC = (samples[tailStart].arms || {})[arm];
339
+ if (!endC) {
340
+ failures.push({ check: 'dead-arm', detail: 'arm `' + arm + '` reported no counters at all' });
341
+ continue;
342
+ }
343
+ var okTotal = endC.ok || 0;
344
+ var errTotal = (endC.errTransient || 0) + (endC.errPermanent || 0) + (endC.transport || 0);
345
+ var opsTotal = okTotal + errTotal;
346
+ if (okTotal === 0) {
347
+ failures.push({ check: 'dead-arm', detail: 'arm `' + arm + '` completed zero successful operations (' + errTotal + ' errors)' });
348
+ }
349
+ var tailErr = 0, tailOps = 0;
350
+ if (midC) {
351
+ var midErr = (midC.errTransient || 0) + (midC.errPermanent || 0) + (midC.transport || 0);
352
+ var midOps = (midC.ok || 0) + midErr;
353
+ tailErr = errTotal - midErr;
354
+ tailOps = opsTotal - midOps;
355
+ }
356
+ var overallRate = opsTotal ? errTotal / opsTotal : 0;
357
+ var tailRate = tailOps ? tailErr / tailOps : 0;
358
+ metrics.arms[arm] = {
359
+ ok: okTotal, errors: errTotal,
360
+ overallErrRate: Math.round(overallRate * 10000) / 10000,
361
+ tailErrRate: Math.round(tailRate * 10000) / 10000
362
+ };
363
+ if (tailErr >= th.driftMinErrors && tailRate > overallRate * th.driftFactor) {
364
+ failures.push({
365
+ check : 'error-drift',
366
+ detail : 'arm `' + arm + '`: tail error rate ' + metrics.arms[arm].tailErrRate
367
+ + ' vs whole-run ' + metrics.arms[arm].overallErrRate
368
+ + ' (' + tailErr + ' tail errors; thresholds: ×' + th.driftFactor + ', min ' + th.driftMinErrors + ')'
369
+ });
370
+ }
371
+ }
372
+
373
+ return { pass: failures.length === 0, failures: failures, warnings: warnings, metrics: metrics };
374
+ }
375
+
376
+ // ---------------------------------------------------------------------------
377
+ // Harness (I/O from here down — nothing below runs when require()d)
378
+ // ---------------------------------------------------------------------------
379
+
380
+ module.exports = {
381
+ DEFAULTS : DEFAULTS,
382
+ VALID_ARMS : VALID_ARMS,
383
+ parseDuration : parseDuration,
384
+ parseArgs : parseArgs,
385
+ lsqSlopeMBperMin : lsqSlopeMBperMin,
386
+ evaluateRun : evaluateRun
387
+ };
388
+
389
+ /** @param {string} msg */
390
+ function log(msg) { process.stdout.write('[couchbase-soak] ' + msg + '\n'); }
391
+
392
+ /** @param {number} ms @returns {Promise} */
393
+ function sleep(ms) { return new Promise(function (r) { setTimeout(r, ms); }); }
394
+
395
+ /**
396
+ * Builds the per-run context (paths, names, child env). Kept out of module
397
+ * scope so require()ing this file for its pure helpers touches nothing.
398
+ *
399
+ * @param {object} opts - parseArgs() output
400
+ * @returns {object} run context
401
+ */
402
+ function buildRunContext(opts) {
403
+ var STAMP = Date.now();
404
+ var GINA_ROOT = path.resolve(__dirname, '..', '..');
405
+ var HOME_BASE = process.env.GINA_SOAK_HOME_BASE
406
+ ? fs.realpathSync(process.env.GINA_SOAK_HOME_BASE)
407
+ : fs.realpathSync(os.tmpdir());
408
+ var FAKE_HOME = path.join(HOME_BASE, 'gina-soak-' + STAMP);
409
+ var ctx = {
410
+ stamp : STAMP,
411
+ proj : 'soak' + STAMP,
412
+ bundle : 'soak',
413
+ ginaRoot : GINA_ROOT,
414
+ cli : path.join(GINA_ROOT, 'bin', 'cli'),
415
+ container : path.join(GINA_ROOT, 'bin', 'gina-container'),
416
+ fakeHome : FAKE_HOME,
417
+ // neutral cwd: NOT the repo (stray `gina` self-symlink), NOT the fake
418
+ // home (dead prod scenes when cwd holds .gina/) — a sibling dir.
419
+ cwdDir : FAKE_HOME + '-cwd',
420
+ ginaHome : path.join(FAKE_HOME, '.gina'),
421
+ projDir : path.join(FAKE_HOME, 'proj'),
422
+ out : path.join(GINA_ROOT, 'tmp', 'couchbase-soak-' + STAMP)
423
+ };
424
+ ctx.src = path.join(ctx.projDir, 'src', ctx.bundle);
425
+ ctx.childEnv = Object.assign({}, process.env, {
426
+ HOME : FAKE_HOME,
427
+ GINA_LOG_STDOUT : 'true'
428
+ });
429
+ delete ctx.childEnv.GINA_HOMEDIR;
430
+ delete ctx.childEnv.NODE_OPTIONS;
431
+ return ctx;
432
+ }
433
+
434
+ /**
435
+ * Runs an offline gina CLI command against the isolated home. Exit status is
436
+ * NOT trusted (first-run home bootstrap exits non-zero on a benign MQ
437
+ * teardown) — callers verify on-disk state instead.
438
+ *
439
+ * @param {object} ctx
440
+ * @param {string[]} args
441
+ * @returns {{status: (number|null), stdout: string, stderr: string}}
442
+ */
443
+ function runCli(ctx, args) {
444
+ var r = spawnSync(process.execPath, [ctx.cli].concat(args), {
445
+ env: ctx.childEnv, cwd: ctx.cwdDir, encoding: 'utf8', timeout: 120000
446
+ });
447
+ return { status: r.status, stdout: r.stdout || '', stderr: r.stderr || '' };
448
+ }
449
+
450
+ /** @param {string} file @returns {object} */
451
+ function readJSON(file) { return JSON.parse(fs.readFileSync(file, 'utf8')); }
452
+
453
+ /**
454
+ * TCP-dials one host/port with a short timeout.
455
+ * @param {string} host
456
+ * @param {number} port
457
+ * @param {number} timeoutMs
458
+ * @returns {Promise<boolean>}
459
+ */
460
+ function dial(host, port, timeoutMs) {
461
+ return new Promise(function (resolve) {
462
+ var sock = new net.Socket();
463
+ var done = false;
464
+ function finish(v) { if (!done) { done = true; try { sock.destroy(); } catch (e) { /* ignore */ } resolve(v); } }
465
+ sock.setTimeout(timeoutMs);
466
+ sock.on('connect', function () { finish(true); });
467
+ sock.on('error', function () { finish(false); });
468
+ sock.on('timeout', function () { finish(false); });
469
+ sock.connect(port, host);
470
+ });
471
+ }
472
+
473
+ /**
474
+ * Preflight: the first configured host must accept TCP on the KV port (11210)
475
+ * or the management port (8091) — or on its explicit :port when one is given.
476
+ * Without this, an unreachable cluster leaves the connector in its infinite
477
+ * reconnect backoff and the boot never becomes ready.
478
+ *
479
+ * @param {object} opts
480
+ * @returns {Promise<?string>} null when reachable, else a human explanation
481
+ */
482
+ async function preflight(opts) {
483
+ var first = opts.host.split(',')[0].trim();
484
+ var m = first.match(/^(.*?):(\d+)$/);
485
+ if (m) {
486
+ return (await dial(m[1], parseInt(m[2], 10), 4000))
487
+ ? null
488
+ : first + ' did not accept TCP within 4s';
489
+ }
490
+ if (await dial(first, 11210, 4000)) { return null; }
491
+ if (await dial(first, 8091, 4000)) { return null; }
492
+ return first + ' accepted TCP on neither 11210 (KV) nor 8091 (mgmt) within 4s';
493
+ }
494
+
495
+ /**
496
+ * Scaffolds project + bundle into the isolated home (state-file-verified) and
497
+ * links <proj>/node_modules/gina explicitly (the CLI auto-link is unreliable
498
+ * under an isolated home, and npm installs prune it besides).
499
+ *
500
+ * @param {object} ctx
501
+ * @param {object} opts
502
+ * @returns {void} throws on failure
503
+ */
504
+ function scaffold(ctx, opts) {
505
+ fs.mkdirSync(ctx.projDir, { recursive: true });
506
+ fs.mkdirSync(ctx.cwdDir, { recursive: true });
507
+ runCli(ctx, ['project:add', '@' + ctx.proj, '--path=' + ctx.projDir]);
508
+ if (!fs.existsSync(path.join(ctx.ginaHome, 'projects.json')) ||
509
+ !readJSON(path.join(ctx.ginaHome, 'projects.json'))[ctx.proj]) {
510
+ throw new Error('project:add did not register @' + ctx.proj);
511
+ }
512
+ relinkGina(ctx);
513
+ runCli(ctx, ['bundle:add', ctx.bundle, '@' + ctx.proj, '--start-port-from=' + opts.startPortFrom]);
514
+ var key = ctx.bundle + '@' + ctx.proj;
515
+ if (!readJSON(path.join(ctx.ginaHome, 'ports.reverse.json'))[key]) {
516
+ throw new Error('bundle:add did not register ' + key);
517
+ }
518
+ log('scaffolded ' + key + ' in ' + ctx.fakeHome);
519
+ }
520
+
521
+ /**
522
+ * (Re)creates the <proj>/node_modules/gina symlink — required for the boot's
523
+ * require('gina'), and PRUNED by every npm install in the project (npm treats
524
+ * it as extraneous).
525
+ * @param {object} ctx
526
+ * @returns {void}
527
+ */
528
+ function relinkGina(ctx) {
529
+ var nmDir = path.join(ctx.projDir, 'node_modules');
530
+ fs.mkdirSync(nmDir, { recursive: true });
531
+ var link = path.join(nmDir, 'gina');
532
+ try { if (fs.lstatSync(link)) { return; } } catch (e) { /* absent — create */ }
533
+ fs.symlinkSync(ctx.ginaRoot, link);
534
+ }
535
+
536
+ /**
537
+ * Installs the SDK candidate + express-session into the throwaway project.
538
+ * The install is the SDK selector: the connector resolves `couchbase` from
539
+ * the project's node_modules AND derives the v3/v4 dispatch from the
540
+ * project package.json's `dependencies.couchbase`, so both must be real.
541
+ * With --sdk-path the install dir is symlinked and the dependency pin is
542
+ * written from that install's own package.json version.
543
+ *
544
+ * @param {object} ctx
545
+ * @param {object} opts
546
+ * @returns {void} throws on failure
547
+ */
548
+ function installDeps(ctx, opts) {
549
+ var pkgPath = path.join(ctx.projDir, 'package.json');
550
+ if (!fs.existsSync(pkgPath)) {
551
+ // never let npm walk up to an ancestor package.json (worst case: this repo)
552
+ throw new Error('scaffolded project has no package.json at ' + pkgPath + ' — refusing to npm install');
553
+ }
554
+ function npmi(args, label) {
555
+ log('npm install: ' + label + ' (this may take a while — native SDK)');
556
+ var r = spawnSync('npm', ['install'].concat(args, ['--no-audit', '--no-fund', '--loglevel=error']), {
557
+ cwd: ctx.projDir, encoding: 'utf8', timeout: 600000
558
+ });
559
+ if (r.status !== 0) {
560
+ throw new Error('npm install ' + label + ' failed (' + r.status + '):\n' + ((r.stderr || '') + (r.stdout || '')).slice(-2500));
561
+ }
562
+ }
563
+ if (opts.sdk) {
564
+ npmi(['couchbase@' + opts.sdk], 'couchbase@' + opts.sdk);
565
+ } else {
566
+ var sdkPkg = path.join(opts.sdkPath, 'package.json');
567
+ if (!fs.existsSync(sdkPkg)) { throw new Error('--sdk-path has no package.json: ' + opts.sdkPath); }
568
+ var v = readJSON(sdkPkg).version;
569
+ var nmDir = path.join(ctx.projDir, 'node_modules');
570
+ fs.mkdirSync(nmDir, { recursive: true });
571
+ fs.symlinkSync(fs.realpathSync(opts.sdkPath), path.join(nmDir, 'couchbase'));
572
+ var pkg = readJSON(pkgPath);
573
+ pkg.dependencies = pkg.dependencies || {};
574
+ pkg.dependencies.couchbase = v; // the v3/v4 resolver reads this pin
575
+ fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2));
576
+ log('symlinked SDK from ' + opts.sdkPath + ' (couchbase@' + v + ')');
577
+ }
578
+ npmi(['express-session'], 'express-session');
579
+ relinkGina(ctx); // npm pruned the extraneous symlink — restore it
580
+
581
+ var resolved = path.join(ctx.projDir, 'node_modules', 'couchbase', 'package.json');
582
+ if (!fs.existsSync(resolved)) { throw new Error('couchbase did not land in the project node_modules'); }
583
+ ctx.sdkVersion = readJSON(resolved).version;
584
+ log('SDK under test: couchbase@' + ctx.sdkVersion);
585
+ }
586
+
587
+ /**
588
+ * Writes the soak fixture into the bundle src: connectors.json (data entry
589
+ * named EXACTLY like the bucket + the literally-named `session` entry),
590
+ * entity + N1QL files, routes, the soak controller, and the express-session
591
+ * wire-in (the documented `lib.SessionStore` shape).
592
+ *
593
+ * @param {object} ctx
594
+ * @param {object} opts
595
+ * @returns {void}
596
+ */
597
+ function fixture(ctx, opts) {
598
+ var db = opts.database;
599
+ var confDir = path.join(ctx.src, 'config');
600
+
601
+ // -- connectors.json — entry key == database name so the model registry
602
+ // (models/<entry>/) and the connector's own walk (models/<database>/)
603
+ // agree on one directory whichever one resolves.
604
+ var connectors = {};
605
+ connectors[db] = {
606
+ connector : 'couchbase',
607
+ protocol : opts.protocol,
608
+ host : opts.host,
609
+ database : db,
610
+ username : opts.username,
611
+ password : opts.password
612
+ };
613
+ connectors.session = {
614
+ connector : 'couchbase',
615
+ protocol : opts.protocol,
616
+ host : opts.host,
617
+ database : opts.sessionDatabase,
618
+ username : opts.username,
619
+ password : opts.password
620
+ };
621
+ fs.writeFileSync(path.join(confDir, 'connectors.json'), JSON.stringify(connectors, null, 2));
622
+
623
+ // -- models/<db>/entities/soak.js + models/<db>/n1ql/soak/*.sql
624
+ var entDir = path.join(ctx.src, 'models', db, 'entities');
625
+ var n1qlDir = path.join(ctx.src, 'models', db, 'n1ql', 'soak');
626
+ fs.mkdirSync(entDir, { recursive: true });
627
+ fs.mkdirSync(n1qlDir, { recursive: true });
628
+
629
+ var durabilityLines = opts.durability === 'majority'
630
+ ? [
631
+ ' var couchbase = require(' + JSON.stringify(path.join(ctx.projDir, 'node_modules', 'couchbase')) + ');',
632
+ ' var KV_OPTS = { expiry: ' + opts.kvExpiry + ', durabilityLevel: couchbase.DurabilityLevel.Majority };'
633
+ ]
634
+ : [
635
+ ' var KV_OPTS = { expiry: ' + opts.kvExpiry + ' };'
636
+ ];
637
+
638
+ fs.writeFileSync(path.join(entDir, 'soak.js'), [
639
+ '/**',
640
+ ' * Soak entity — generated by script/soak/couchbase-soak.js.',
641
+ ' * Custom KV methods are INSTANCE methods (assigned in the constructor):',
642
+ ' * prototype-level user methods do not survive the connector\'s inherits().',
643
+ ' */',
644
+ 'function Soak(conn, caller) {',
645
+ ' var self = this;'
646
+ ].concat(durabilityLines, [
647
+ '',
648
+ ' /**',
649
+ ' * Callback-form KV insert — the 4-arg consumer shape:',
650
+ ' * coll.insert(key, doc, opts, cb) on the entity collection handle.',
651
+ ' * @param {string} key',
652
+ ' * @param {object} doc',
653
+ ' * @callback cb - (err, mutationResult)',
654
+ ' */',
655
+ ' this.kvCbInsert = function(key, doc, cb) {',
656
+ ' doc._collection = \'soak\';',
657
+ ' doc._scope = self._scope;',
658
+ ' var coll = self.getConnection();',
659
+ ' coll.insert(key, doc, KV_OPTS, cb);',
660
+ ' };',
661
+ '',
662
+ ' /**',
663
+ ' * Promise-form KV cycle: insert a fresh expiry-bounded doc, read it',
664
+ ' * back, remove every 4th (bounds bucket growth alongside expiry).',
665
+ ' * @param {string} key',
666
+ ' * @param {object} doc',
667
+ ' * @param {number} seq',
668
+ ' * @returns {Promise<object>} the read-back content',
669
+ ' */',
670
+ ' this.kvPromiseCycle = async function(key, doc, seq) {',
671
+ ' doc._collection = \'soak\';',
672
+ ' doc._scope = self._scope;',
673
+ ' var coll = self.getConnection();',
674
+ ' await coll.insert(key, doc, KV_OPTS);',
675
+ ' var got = await coll.get(key);',
676
+ ' if (seq % 4 === 0) { await coll.remove(key); }',
677
+ ' return got.value;',
678
+ ' };',
679
+ '}',
680
+ '',
681
+ 'module.exports = Soak;',
682
+ ''
683
+ ]).join('\n'));
684
+
685
+ var bt = '`'; // N1QL identifier backtick
686
+ fs.writeFileSync(path.join(n1qlDir, 'findByWorker.sql'), [
687
+ '/**',
688
+ ' * @param {string} $1 worker tag',
689
+ ' * @return {array}',
690
+ ' */',
691
+ 'SELECT t.worker, t.seq, t.bumped',
692
+ 'FROM ' + bt + db + bt + ' t',
693
+ 'WHERE t._collection = \'soak\'',
694
+ ' AND t._scope = $scope',
695
+ ' AND t.worker = $1',
696
+ 'LIMIT 20'
697
+ ].join('\n'));
698
+
699
+ fs.writeFileSync(path.join(n1qlDir, 'findByWorkerRp.sql'), [
700
+ '/**',
701
+ ' * @options { consistency: "request_plus" }',
702
+ ' * @param {string} $1 worker tag',
703
+ ' * @return {array}',
704
+ ' */',
705
+ 'SELECT t.worker, t.seq, t.bumped',
706
+ 'FROM ' + bt + db + bt + ' t',
707
+ 'WHERE t._collection = \'soak\'',
708
+ ' AND t._scope = $scope',
709
+ ' AND t.worker = $1',
710
+ 'LIMIT 20'
711
+ ].join('\n'));
712
+
713
+ fs.writeFileSync(path.join(n1qlDir, 'bumpByKey.sql'), [
714
+ '/**',
715
+ ' * @param {string} $1 document key',
716
+ ' * @param {string} $2 bump tag',
717
+ ' * @return {array}',
718
+ ' */',
719
+ 'UPDATE ' + bt + db + bt + ' t',
720
+ 'USE KEYS $1',
721
+ 'SET t.bumped = $2',
722
+ 'RETURNING t.seq'
723
+ ].join('\n'));
724
+
725
+ // -- routes: all param-less GETs/POST — the controller rotates its own
726
+ // worker tags/keys server-side, so no request-param API is involved.
727
+ fs.writeFileSync(path.join(confDir, 'routing.json'), JSON.stringify({
728
+ '$schema' : 'https://gina.io/schema/routing.json',
729
+ 'soak-setup' : { url: '/soak/setup', method: 'POST', param: { control: 'soakSetup' } },
730
+ 'soak-query' : { url: '/soak/query', method: 'GET', param: { control: 'soakQuery' } },
731
+ 'soak-query-rp' : { url: '/soak/query-rp', method: 'GET', param: { control: 'soakQueryRp' } },
732
+ 'soak-kv-cb' : { url: '/soak/kv-cb', method: 'GET', param: { control: 'soakKvCb' } },
733
+ 'soak-kv-p' : { url: '/soak/kv-p', method: 'GET', param: { control: 'soakKvPromise' } },
734
+ 'soak-session' : { url: '/soak/session', method: 'GET', param: { control: 'soakSession' } },
735
+ 'soak-session-bye' : { url: '/soak/session-destroy', method: 'GET', param: { control: 'soakSessionDestroy' } },
736
+ 'soak-stats' : { url: '/soak/stats', method: 'GET', param: { control: 'soakStats' } }
737
+ }, null, 2));
738
+
739
+ // -- the soak controller (replaces the scaffold default wholesale)
740
+ fs.writeFileSync(path.join(ctx.src, 'controllers', 'controller.js'), [
741
+ '/**',
742
+ ' * SoakController — generated by script/soak/couchbase-soak.js.',
743
+ ' * Counters live on `global.__soakStats` (prod bundle: load-once, but the',
744
+ ' * global guard keeps them restart-safe and hot-reload-safe alike).',
745
+ ' */',
746
+ 'var db = getModel(' + JSON.stringify(db) + ');',
747
+ '',
748
+ 'function SoakController() {',
749
+ ' var self = this;',
750
+ '',
751
+ ' var STATS = global.__soakStats = global.__soakStats || {',
752
+ ' startedAt : Date.now(),',
753
+ ' rr : 0,',
754
+ ' arms : {',
755
+ ' query : { ok: 0, errTransient: 0, errPermanent: 0 },',
756
+ ' kv : { ok: 0, errTransient: 0, errPermanent: 0 },',
757
+ ' session : { ok: 0, errTransient: 0, errPermanent: 0 }',
758
+ ' }',
759
+ ' };',
760
+ '',
761
+ ' /**',
762
+ ' * Books one arm outcome, classified via the #CE1 stamp when present.',
763
+ ' * @param {string} arm',
764
+ ' * @param {?Error} err',
765
+ ' * @returns {void}',
766
+ ' * @inner',
767
+ ' */',
768
+ ' function bump(arm, err) {',
769
+ ' var a = STATS.arms[arm];',
770
+ ' if (!err) { a.ok++; return; }',
771
+ ' if (err.isTransient === true) { a.errTransient++; } else { a.errPermanent++; }',
772
+ ' }',
773
+ '',
774
+ ' /** @returns {string} rotating worker tag @inner */',
775
+ ' function nextWorker() { return \'w\' + (STATS.rr++ % 8); }',
776
+ '',
777
+ ' this.soakSetup = function(req, res) {',
778
+ ' if (!db || !db.soak) {',
779
+ ' return self.renderJSON({ ok: false, error: \'model wiring incomplete: getModel(...)\' +',
780
+ ' (db ? \'.soak missing (entities did not load)\' : \' returned nothing\') });',
781
+ ' }',
782
+ ' var cluster = null;',
783
+ ' try { cluster = db.soak.getCluster(); }',
784
+ ' catch (e) { return self.renderJSON({ ok: false, error: \'getCluster(): \' + e.message }); }',
785
+ ' cluster.query(\'CREATE PRIMARY INDEX IF NOT EXISTS ON ' + bt + db + bt + '\', { adhoc: true })',
786
+ ' .then(function () {',
787
+ ' return db.soak.kvPromiseCycle(\'soak:setup:probe\', { worker: \'setup\', seq: 1 }, 1);',
788
+ ' })',
789
+ ' .then(function () { self.renderJSON({ ok: true, indexed: true }); })',
790
+ ' .catch(function (e) { self.renderJSON({ ok: false, error: String(e && e.message || e) }); });',
791
+ ' };',
792
+ '',
793
+ ' this.soakQuery = function(req, res) {',
794
+ ' db.soak.findByWorker(nextWorker()).onComplete(function (err, rows) {',
795
+ ' bump(\'query\', err);',
796
+ ' self.renderJSON({ ok: !err, n: rows ? rows.length : 0 });',
797
+ ' });',
798
+ ' };',
799
+ '',
800
+ ' this.soakQueryRp = function(req, res) {',
801
+ ' db.soak.findByWorkerRp(nextWorker()).onComplete(function (err, rows) {',
802
+ ' bump(\'query\', err);',
803
+ ' self.renderJSON({ ok: !err, n: rows ? rows.length : 0 });',
804
+ ' });',
805
+ ' };',
806
+ '',
807
+ ' this.soakKvCb = function(req, res) {',
808
+ ' var seq = STATS.rr++;',
809
+ ' var key = \'soak:cb:\' + process.pid + \':\' + seq;',
810
+ ' db.soak.kvCbInsert(key, { worker: nextWorker(), seq: seq, via: \'cb\' }, function (err) {',
811
+ ' bump(\'kv\', err);',
812
+ ' if (err) { return self.renderJSON({ ok: false, error: String(err.message || err) }); }',
813
+ ' // follow with a query-path mutation on the same key (UPDATE ... USE KEYS)',
814
+ ' db.soak.bumpByKey(key, \'y\').onComplete(function (uErr) {',
815
+ ' bump(\'query\', uErr);',
816
+ ' self.renderJSON({ ok: !uErr });',
817
+ ' });',
818
+ ' });',
819
+ ' };',
820
+ '',
821
+ ' this.soakKvPromise = function(req, res) {',
822
+ ' var seq = STATS.rr++;',
823
+ ' var key = \'soak:p:\' + process.pid + \':\' + seq;',
824
+ ' db.soak.kvPromiseCycle(key, { worker: nextWorker(), seq: seq, via: \'p\' }, seq)',
825
+ ' .then(function () { bump(\'kv\', null); self.renderJSON({ ok: true }); })',
826
+ ' .catch(function (err) { bump(\'kv\', err); self.renderJSON({ ok: false, error: String(err && err.message || err) }); });',
827
+ ' };',
828
+ '',
829
+ ' this.soakSession = function(req, res) {',
830
+ ' if (!req.session) { return self.renderJSON({ ok: false, error: \'no req.session — store wiring failed\' }); }',
831
+ ' req.session.n = (req.session.n || 0) + 1;',
832
+ ' bump(\'session\', null);',
833
+ ' self.renderJSON({ ok: true, n: req.session.n });',
834
+ ' };',
835
+ '',
836
+ ' this.soakSessionDestroy = function(req, res) {',
837
+ ' if (!req.session) { return self.renderJSON({ ok: false, error: \'no req.session\' }); }',
838
+ ' req.session.destroy(function (err) {',
839
+ ' bump(\'session\', err || null);',
840
+ ' self.renderJSON({ ok: !err });',
841
+ ' });',
842
+ ' };',
843
+ '',
844
+ ' this.soakStats = function(req, res) {',
845
+ ' self.renderJSON({ pid: process.pid, mem: process.memoryUsage(), stats: STATS });',
846
+ ' };',
847
+ '}',
848
+ '',
849
+ 'module.exports = SoakController;',
850
+ ''
851
+ ].join('\n'));
852
+
853
+ // -- bundle entry: the documented express-session wire-in (guide shape,
854
+ // #B167: the entry must be literally named `session`; the store takes
855
+ // the model layer's already-open bucket as options.db).
856
+ fs.writeFileSync(path.join(ctx.src, 'index.js'), [
857
+ '/**',
858
+ ' * Soak bundle entry — generated by script/soak/couchbase-soak.js.',
859
+ ' * Wires the couchbase express-session store per the documented shape.',
860
+ ' */',
861
+ 'var soakApp = require(\'gina\');',
862
+ 'var expressSession = require(\'express-session\');',
863
+ 'var SessionStore = soakApp.lib.SessionStore;',
864
+ '',
865
+ 'soakApp.onInitialize(function (event, app) {',
866
+ ' var CouchbaseStore = new SessionStore(expressSession);',
867
+ ' app.use(expressSession({',
868
+ ' secret : \'soak-\' + process.pid,',
869
+ ' resave : false,',
870
+ ' saveUninitialized : false,',
871
+ ' store : new CouchbaseStore({',
872
+ ' db : getModel(\'session\').getConnection(),',
873
+ ' prefix : \'soak:sess:\'',
874
+ ' }),',
875
+ ' cookie : { maxAge: 300000 }',
876
+ ' }));',
877
+ ' event.emit(\'complete\', app);',
878
+ '});',
879
+ '',
880
+ 'soakApp.onError(function (err, req, res, next) { next(err); });',
881
+ '',
882
+ 'soakApp.start();',
883
+ ''
884
+ ].join('\n'));
885
+
886
+ log('fixture applied (connectors + entity + n1ql + routes + session wire-in)');
887
+ }
888
+
889
+ /**
890
+ * Builds the prod release (fixture config is baked into the release tree).
891
+ * @param {object} ctx
892
+ * @returns {void} throws when no prod entry point was produced
893
+ */
894
+ function build(ctx) {
895
+ runCli(ctx, ['bundle:build', ctx.bundle, '@' + ctx.proj, '--env=prod', '--scope=local']);
896
+ var relRoot = path.join(ctx.projDir, 'releases', ctx.bundle, 'local', 'prod');
897
+ var built = fs.existsSync(relRoot) && fs.readdirSync(relRoot).some(function (v) {
898
+ return fs.existsSync(path.join(relRoot, v, 'index.js'));
899
+ });
900
+ if (!built) { throw new Error('bundle:build produced no prod release under ' + relRoot); }
901
+ log('prod release built');
902
+ }
903
+
904
+ /**
905
+ * @param {object} ctx
906
+ * @returns {number} the prod http/1.1 port from ports.reverse.json
907
+ */
908
+ function resolvePort(ctx) {
909
+ var key = ctx.bundle + '@' + ctx.proj;
910
+ var port = null;
911
+ try { port = readJSON(path.join(ctx.ginaHome, 'ports.reverse.json'))[key].prod['http/1.1'].http; } catch (e) { /* fall through */ }
912
+ if (!port) { throw new Error('could not resolve the prod http/1.1 port for ' + key); }
913
+ return port;
914
+ }
915
+
916
+ /**
917
+ * @param {object} ctx
918
+ * @param {number} port
919
+ * @param {number} timeoutMs
920
+ * @returns {Promise<boolean>}
921
+ */
922
+ function waitForPort(ctx, port, timeoutMs) {
923
+ var deadline = Date.now() + timeoutMs;
924
+ return new Promise(function (resolve) {
925
+ (function poll() {
926
+ dial('127.0.0.1', port, 800).then(function (up) {
927
+ if (up) { return resolve(true); }
928
+ if (Date.now() >= deadline) { return resolve(false); }
929
+ setTimeout(poll, 300);
930
+ });
931
+ })();
932
+ });
933
+ }
934
+
935
+ /**
936
+ * Boots the bundle (prod, daemonless) and records launcher exit + the bundle
937
+ * child's pid from the `[ FRAMEWORK ][ <pid> ]` mount line. Asserts via the
938
+ * launcher's own `App:` line that the boot really is the prod release.
939
+ *
940
+ * @param {object} ctx
941
+ * @param {number} port
942
+ * @returns {Promise<object>} boot handle {proc, logRef, exit, bundlePid, bootedAt}
943
+ */
944
+ async function boot(ctx, port) {
945
+ var env = Object.assign({}, ctx.childEnv, { NODE_ENV: 'prod' });
946
+ var handle = { logRef: { txt: '' }, exit: { code: null, signal: null, done: false, atMs: null }, bundlePid: null };
947
+ handle.bootedAt = Date.now();
948
+ handle.proc = spawn(process.execPath, [ctx.container, ctx.bundle, '@' + ctx.proj], {
949
+ env: env, cwd: ctx.cwdDir, stdio: ['ignore', 'pipe', 'pipe']
950
+ });
951
+ handle.proc.stdout.on('data', function (d) { handle.logRef.txt += d; });
952
+ handle.proc.stderr.on('data', function (d) { handle.logRef.txt += d; });
953
+ handle.proc.on('exit', function (code, signal) {
954
+ handle.exit = { code: code, signal: signal, done: true, atMs: Date.now() - handle.bootedAt };
955
+ });
956
+ var opened = await waitForPort(ctx, port, 45000);
957
+ if (!opened) {
958
+ var note = handle.exit.done
959
+ ? 'launcher EXITED (code ' + handle.exit.code + ', signal ' + handle.exit.signal + ') before binding'
960
+ : 'launcher still running at deadline — a stalled boot (cluster unreachable? credentials?)';
961
+ throw new Error('port ' + port + ' never opened. ' + note + '\n--- boot log tail:\n' + handle.logRef.txt.slice(-3000));
962
+ }
963
+ var pm = handle.logRef.txt.match(/\[ FRAMEWORK \]\[ (\d+) \]/);
964
+ handle.bundlePid = pm ? parseInt(pm[1], 10) : null;
965
+ var appLine = (handle.logRef.txt.match(/\[ gina-container \] App:.*$/m) || [''])[0];
966
+ if (appLine && appLine.indexOf('/releases/') === -1) {
967
+ throw new Error('boot is NOT the prod release (App: line = ' + appLine + ')');
968
+ }
969
+ log('booted (port ' + port + ', bundle pid ' + (handle.bundlePid || '?') + ')');
970
+ return handle;
971
+ }
972
+
973
+ /**
974
+ * One HTTP GET/POST against the bundle, with a hard timeout.
975
+ *
976
+ * @param {object} agent - shared keep-alive agent
977
+ * @param {number} port
978
+ * @param {string} reqPath
979
+ * @param {object} [o] - {method, cookies: string[], timeoutMs}
980
+ * @returns {Promise<{status: number, body: string, setCookie: string[]}>} rejects on transport error/timeout
981
+ */
982
+ function request(agent, port, reqPath, o) {
983
+ o = o || {};
984
+ return new Promise(function (resolve, reject) {
985
+ var req = http.request({
986
+ agent: agent, host: '127.0.0.1', port: port, path: reqPath,
987
+ method: o.method || 'GET',
988
+ headers: (o.cookies && o.cookies.length) ? { Cookie: o.cookies.join('; ') } : {}
989
+ }, function (res) {
990
+ var chunks = [];
991
+ res.on('data', function (c) { chunks.push(c); });
992
+ res.on('end', function () {
993
+ resolve({
994
+ status: res.statusCode,
995
+ body: Buffer.concat(chunks).toString(),
996
+ setCookie: res.headers['set-cookie'] || []
997
+ });
998
+ });
999
+ });
1000
+ req.setTimeout(o.timeoutMs || 30000, function () { req.destroy(new Error('request timeout')); });
1001
+ req.on('error', reject);
1002
+ req.end();
1003
+ });
1004
+ }
1005
+
1006
+ /**
1007
+ * Runs the in-bundle setup route (primary index + KV probe) and fails loudly
1008
+ * on anything but `{ok: true}`.
1009
+ *
1010
+ * @param {object} ctx
1011
+ * @param {object} opts
1012
+ * @param {object} agent
1013
+ * @param {number} port
1014
+ * @returns {Promise<void>}
1015
+ */
1016
+ async function setupTarget(ctx, opts, agent, port) {
1017
+ var webroot = '/' + ctx.bundle;
1018
+ var r = await request(agent, port, webroot + '/soak/setup', { method: 'POST', timeoutMs: opts.setupTimeoutMs });
1019
+ var payload = null;
1020
+ try { payload = JSON.parse(r.body); } catch (e) { /* handled below */ }
1021
+ if (!payload || payload.ok !== true) {
1022
+ throw new Error('in-bundle setup failed (HTTP ' + r.status + '): '
1023
+ + (payload && payload.error ? payload.error : r.body.slice(0, 500))
1024
+ + '\nCheck: bucket exists, credentials can write it, query service up, primary index creatable.');
1025
+ }
1026
+ log('setup ok (primary index ensured + KV probe round-tripped)');
1027
+ }
1028
+
1029
+ /**
1030
+ * Drives the load for the full duration and samples /soak/stats, composing
1031
+ * server counters + driver transport counters into evaluator-shaped samples.
1032
+ *
1033
+ * @param {object} ctx
1034
+ * @param {object} opts
1035
+ * @param {object} handle - boot handle
1036
+ * @param {number} port
1037
+ * @returns {Promise<{samples: Array, endedEarly: ?object}>}
1038
+ */
1039
+ async function drive(ctx, opts, handle, port) {
1040
+ var webroot = '/' + ctx.bundle;
1041
+ var agent = new http.Agent({ keepAlive: true, maxSockets: opts.concurrency + 4 });
1042
+ var stopFlag = { stop: false };
1043
+ var startMs = Date.now();
1044
+ var plannedEnd = startMs + opts.durationMs;
1045
+ var driver = { // transport-level outcomes per arm (server can't see these)
1046
+ query : { sent: 0, http200: 0, httpOther: 0, transport: 0 },
1047
+ kv : { sent: 0, http200: 0, httpOther: 0, transport: 0 },
1048
+ session : { sent: 0, http200: 0, httpOther: 0, transport: 0 }
1049
+ };
1050
+
1051
+ /**
1052
+ * @param {string} arm @param {number} idx @returns {Promise<void>} @inner
1053
+ */
1054
+ async function worker(arm, idx) {
1055
+ var seq = 0;
1056
+ var jar = [];
1057
+ while (!stopFlag.stop) {
1058
+ var reqPath;
1059
+ var o = {};
1060
+ if (arm === 'query') {
1061
+ reqPath = (seq % 3 === 2) ? '/soak/query-rp' : '/soak/query';
1062
+ } else if (arm === 'kv') {
1063
+ reqPath = (seq % 2 === 0) ? '/soak/kv-cb' : '/soak/kv-p';
1064
+ } else {
1065
+ if (seq > 0 && seq % 25 === 0) { jar = []; } // fresh identity
1066
+ reqPath = (seq > 0 && seq % 10 === 0) ? '/soak/session-destroy' : '/soak/session';
1067
+ o.cookies = jar;
1068
+ }
1069
+ driver[arm].sent++;
1070
+ try {
1071
+ var r = await request(agent, port, webroot + reqPath, o);
1072
+ if (arm === 'session' && r.setCookie.length) {
1073
+ jar = r.setCookie.map(function (c) { return c.split(';')[0]; });
1074
+ }
1075
+ if (r.status === 200) { driver[arm].http200++; } else { driver[arm].httpOther++; }
1076
+ } catch (e) {
1077
+ driver[arm].transport++;
1078
+ await sleep(250); // do not spin on a dead socket
1079
+ }
1080
+ seq++;
1081
+ }
1082
+ }
1083
+
1084
+ var samples = [];
1085
+ var ndjson = fs.createWriteStream(path.join(ctx.out, 'samples.ndjson'));
1086
+
1087
+ /** @returns {Promise<void>} one stats sample @inner */
1088
+ async function sampleOnce() {
1089
+ try {
1090
+ var r = await request(agent, port, webroot + '/soak/stats', { timeoutMs: 4000 });
1091
+ var p = JSON.parse(r.body);
1092
+ var s = { tMs: Date.now() - startMs, rssBytes: p.mem.rss, arms: {} };
1093
+ for (var a = 0; a < opts.arms.length; a++) {
1094
+ var arm = opts.arms[a];
1095
+ var srv = (p.stats.arms || {})[arm] || {};
1096
+ s.arms[arm] = {
1097
+ ok : srv.ok || 0,
1098
+ errTransient : srv.errTransient || 0,
1099
+ errPermanent : srv.errPermanent || 0,
1100
+ transport : driver[arm].transport + driver[arm].httpOther
1101
+ };
1102
+ }
1103
+ // session `ok` is driver-observed (store failures surface as request
1104
+ // failures, not server counters — count successes from the wire).
1105
+ if (s.arms.session) { s.arms.session.ok = driver.session.http200; }
1106
+ samples.push(s);
1107
+ ndjson.write(JSON.stringify(s) + '\n');
1108
+ } catch (e) { /* liveness is judged below, not by one missed sample */ }
1109
+ }
1110
+
1111
+ var workers = [];
1112
+ for (var w = 0; w < opts.concurrency; w++) {
1113
+ workers.push(worker(opts.arms[w % opts.arms.length], w));
1114
+ }
1115
+
1116
+ var endedEarly = null;
1117
+ while (Date.now() < plannedEnd) {
1118
+ await sleep(Math.min(opts.sampleIntervalMs, plannedEnd - Date.now()));
1119
+ await sampleOnce();
1120
+ var dead = handle.exit.done
1121
+ || (handle.bundlePid && !isAlive(handle.bundlePid));
1122
+ if (dead) {
1123
+ endedEarly = {
1124
+ atMs : handle.exit.done ? handle.exit.atMs : (Date.now() - handle.bootedAt),
1125
+ exit : handle.exit.done ? { code: handle.exit.code, signal: handle.exit.signal }
1126
+ : { code: null, signal: null }
1127
+ };
1128
+ log('bundle process ENDED EARLY — stopping the drive');
1129
+ break;
1130
+ }
1131
+ var elapsed = Math.round((Date.now() - startMs) / 1000);
1132
+ if (elapsed % 30 < opts.sampleIntervalMs / 1000) {
1133
+ log('t+' + elapsed + 's — ' + JSON.stringify(samples.length ? samples[samples.length - 1].arms : {}));
1134
+ }
1135
+ }
1136
+ stopFlag.stop = true;
1137
+ await Promise.race([Promise.all(workers), sleep(8000)]);
1138
+ await sampleOnce(); // final counters
1139
+ ndjson.end();
1140
+ agent.destroy();
1141
+ return { samples: samples, endedEarly: endedEarly, driver: driver };
1142
+ }
1143
+
1144
+ /**
1145
+ * @param {number} pid
1146
+ * @returns {boolean} whether the pid is signalable
1147
+ */
1148
+ function isAlive(pid) {
1149
+ try { process.kill(pid, 0); return true; } catch (e) { return false; }
1150
+ }
1151
+
1152
+ /**
1153
+ * SIGTERM-drains the boot; escalates to SIGKILL after a grace period — and
1154
+ * then ALSO signals the bundle pid (gina-container forwards SIGTERM only; a
1155
+ * SIGKILLed launcher orphans the bundle child).
1156
+ *
1157
+ * @param {object} handle
1158
+ * @returns {Promise<void>}
1159
+ */
1160
+ async function drain(handle) {
1161
+ try { handle.proc.kill('SIGTERM'); } catch (e) { /* already gone */ }
1162
+ var until = Date.now() + 12000;
1163
+ while (Date.now() < until && !handle.exit.done) { await sleep(200); }
1164
+ if (!handle.exit.done) {
1165
+ try { handle.proc.kill('SIGKILL'); } catch (e) { /* ignore */ }
1166
+ if (handle.bundlePid && isAlive(handle.bundlePid)) {
1167
+ try { process.kill(handle.bundlePid, 'SIGTERM'); } catch (e) { /* ignore */ }
1168
+ await sleep(1500);
1169
+ if (isAlive(handle.bundlePid)) {
1170
+ try { process.kill(handle.bundlePid, 'SIGKILL'); } catch (e) { /* ignore */ }
1171
+ }
1172
+ }
1173
+ }
1174
+ }
1175
+
1176
+ /**
1177
+ * Best-effort teardown: unregister + remove the throwaway home, hunt THIS
1178
+ * run's surviving processes, verify the real ~/.gina is untouched, and clear
1179
+ * stray CLI self-symlinks at the repo root (both the `gina` name and the
1180
+ * install-dir basename variant).
1181
+ *
1182
+ * @param {object} ctx
1183
+ * @param {object} opts
1184
+ * @returns {string[]} human-readable warnings (empty = clean)
1185
+ */
1186
+ function teardown(ctx, opts) {
1187
+ var warnings = [];
1188
+ if (!opts.keep) {
1189
+ try { runCli(ctx, ['project:rm', '@' + ctx.proj, '--force']); } catch (e) { /* best effort */ }
1190
+ try { fs.rmSync(ctx.fakeHome, { recursive: true, force: true }); } catch (e) { warnings.push('could not remove ' + ctx.fakeHome + ': ' + e.message); }
1191
+ try { fs.rmSync(ctx.cwdDir, { recursive: true, force: true }); } catch (e) { /* ignore */ }
1192
+ } else {
1193
+ warnings.push('--keep: left ' + ctx.fakeHome + ' and the @' + ctx.proj + ' registration in place');
1194
+ }
1195
+ try {
1196
+ var ps = spawnSync('ps', ['-eo', 'pid,command'], { encoding: 'utf8' }).stdout || '';
1197
+ ps.split('\n').forEach(function (line) {
1198
+ if (line.indexOf('@' + ctx.proj) === -1) { return; }
1199
+ var pid = parseInt(line.trim().split(/\s+/)[0], 10);
1200
+ if (pid && pid !== process.pid) {
1201
+ try { process.kill(pid, 'SIGTERM'); warnings.push('killed surviving process ' + pid); } catch (e) { /* gone */ }
1202
+ }
1203
+ });
1204
+ } catch (e) { /* ps unavailable */ }
1205
+ try {
1206
+ var realProjects = path.join(os.homedir(), '.gina', 'projects.json');
1207
+ if (fs.existsSync(realProjects) && readJSON(realProjects)[ctx.proj]) {
1208
+ warnings.push('LEAK: @' + ctx.proj + ' registered in the REAL ~/.gina/projects.json — remove it');
1209
+ }
1210
+ } catch (e) { /* ignore */ }
1211
+ try {
1212
+ var realDot = path.join(os.homedir(), '.' + ctx.proj);
1213
+ if (fs.existsSync(realDot)) {
1214
+ if (!fs.readdirSync(realDot).length) { fs.rmdirSync(realDot); }
1215
+ else { warnings.push('LEAK: non-empty ' + realDot + ' on the real home'); }
1216
+ }
1217
+ } catch (e) { /* ignore */ }
1218
+ [path.join(ctx.ginaRoot, 'gina'), path.join(ctx.ginaRoot, path.basename(ctx.ginaRoot))].forEach(function (stray) {
1219
+ try {
1220
+ if (fs.existsSync(stray) && fs.lstatSync(stray).isSymbolicLink()) {
1221
+ fs.unlinkSync(stray);
1222
+ warnings.push('removed a stray self-symlink at ' + stray);
1223
+ }
1224
+ } catch (e) { /* ignore */ }
1225
+ });
1226
+ return warnings;
1227
+ }
1228
+
1229
+ /**
1230
+ * Entry point: setup → soak → verdict → report.
1231
+ * @returns {Promise<void>} process.exit()s with 0/1/2
1232
+ */
1233
+ async function main() {
1234
+ var parsed = parseArgs(process.argv.slice(2));
1235
+ if (!parsed.ok) {
1236
+ process.stderr.write('couchbase-soak: ' + parsed.error + '\n(see the header of script/soak/couchbase-soak.js for usage)\n');
1237
+ process.exit(2);
1238
+ }
1239
+ var opts = parsed.opts;
1240
+ var ctx = buildRunContext(opts);
1241
+ fs.mkdirSync(ctx.out, { recursive: true });
1242
+ log('output dir: ' + ctx.out);
1243
+ log('arms: ' + opts.arms.join(',') + ' · duration: ' + Math.round(opts.durationMs / 1000) + 's · concurrency: ' + opts.concurrency);
1244
+
1245
+ if (!opts.skipPreflight) {
1246
+ var unreachable = await preflight(opts);
1247
+ if (unreachable) {
1248
+ process.stderr.write('couchbase-soak: preflight failed — ' + unreachable + ' (use --skip-preflight to bypass)\n');
1249
+ process.exit(2);
1250
+ }
1251
+ }
1252
+
1253
+ var handle = null;
1254
+ var result = null;
1255
+ var verdict = null;
1256
+ var report = { stamp: ctx.stamp, node: process.version, opts: Object.assign({}, opts, { password: '(redacted)' }) };
1257
+
1258
+ try {
1259
+ scaffold(ctx, opts);
1260
+ installDeps(ctx, opts);
1261
+ fixture(ctx, opts);
1262
+ build(ctx);
1263
+ var port = resolvePort(ctx);
1264
+ handle = await boot(ctx, port);
1265
+ var agent = new http.Agent({ keepAlive: true });
1266
+ await setupTarget(ctx, opts, agent, port);
1267
+ agent.destroy();
1268
+
1269
+ log('driving load for ' + Math.round(opts.durationMs / 1000) + 's ...');
1270
+ result = await drive(ctx, opts, handle, port);
1271
+ } catch (e) {
1272
+ if (handle) { await drain(handle); }
1273
+ if (handle && handle.logRef) {
1274
+ try { fs.writeFileSync(path.join(ctx.out, 'boot.log'), handle.logRef.txt); } catch (w) { /* ignore */ }
1275
+ }
1276
+ var warningsE = teardown(ctx, opts);
1277
+ warningsE.forEach(function (w) { log('teardown: ' + w); });
1278
+ process.stderr.write('couchbase-soak: SETUP/RUN error — ' + (e && e.stack || e) + '\n');
1279
+ process.exit(2);
1280
+ }
1281
+
1282
+ await drain(handle);
1283
+ try { fs.writeFileSync(path.join(ctx.out, 'boot.log'), handle.logRef.txt); } catch (w) { /* ignore */ }
1284
+
1285
+ verdict = evaluateRun({
1286
+ plannedMs : opts.durationMs,
1287
+ endedEarly : result.endedEarly,
1288
+ samples : result.samples,
1289
+ armsRequested : opts.arms,
1290
+ thresholds : opts.thresholds
1291
+ });
1292
+
1293
+ report.sdkVersion = ctx.sdkVersion;
1294
+ report.driver = result.driver;
1295
+ report.verdict = verdict;
1296
+ fs.writeFileSync(path.join(ctx.out, 'report.json'), JSON.stringify(report, null, 2));
1297
+
1298
+ var lines = [
1299
+ '# couchbase-soak ' + ctx.stamp,
1300
+ 'node ' + process.version + ' · couchbase@' + ctx.sdkVersion + ' · target ' + opts.protocol + opts.host + '/' + opts.database,
1301
+ 'planned ' + Math.round(opts.durationMs / 1000) + 's · arms ' + opts.arms.join(',') + ' · concurrency ' + opts.concurrency,
1302
+ ''
1303
+ ];
1304
+ verdict.warnings.forEach(function (w) { lines.push('WARN: ' + w); });
1305
+ verdict.failures.forEach(function (f) { lines.push('FAIL [' + f.check + ']: ' + f.detail); });
1306
+ lines.push('', 'metrics: ' + JSON.stringify(verdict.metrics), '', verdict.pass ? 'VERDICT: PASS' : 'VERDICT: FAIL');
1307
+ fs.writeFileSync(path.join(ctx.out, 'report.txt'), lines.join('\n'));
1308
+ lines.forEach(function (l) { if (l) { log(l); } });
1309
+
1310
+ var warnings = teardown(ctx, opts);
1311
+ warnings.forEach(function (w) { log('teardown: ' + w); });
1312
+ log('report: ' + path.join(ctx.out, 'report.txt'));
1313
+ process.exit(verdict.pass ? 0 : 1);
1314
+ }
1315
+
1316
+ if (require.main === module) { main(); }