gina 0.6.7 → 0.6.8

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 (583) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/README.md +27 -26
  3. package/ROADMAP.md +3 -2
  4. package/framework/v0.6.8/VERSION +1 -0
  5. package/framework/{v0.6.7 → v0.6.8}/core/config.js +10 -0
  6. package/framework/v0.6.8/core/connectors/couchbase/lib/storage-store.js +1074 -0
  7. package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.js +532 -22
  8. package/framework/v0.6.8/core/controller/controller.render-stream.js +270 -0
  9. package/framework/{v0.6.7 → v0.6.8}/core/gna.js +10 -2
  10. package/framework/{v0.6.7 → v0.6.8}/core/router.js +30 -10
  11. package/framework/{v0.6.7 → v0.6.8}/core/server.isaac.js +42 -7
  12. package/framework/{v0.6.7 → v0.6.8}/lib/cmd/storage/gc.js +6 -0
  13. package/framework/{v0.6.7 → v0.6.8}/lib/cmd/storage/stats.js +7 -0
  14. package/framework/{v0.6.7 → v0.6.8}/lib/cmd/storage/verify.js +6 -0
  15. package/framework/{v0.6.7 → v0.6.8}/lib/storage/src/local-cas.js +107 -9
  16. package/framework/v0.6.8/lib/storage/src/local-stream.js +1418 -0
  17. package/framework/{v0.6.7 → v0.6.8}/lib/storage/src/local.js +177 -16
  18. package/framework/{v0.6.7 → v0.6.8}/lib/storage/src/main.js +406 -25
  19. package/framework/{v0.6.7 → v0.6.8}/lib/storage/src/meta-store.js +13 -2
  20. package/framework/v0.6.8/lib/storage/src/s3.js +759 -0
  21. package/framework/{v0.6.7 → v0.6.8}/lib/storage/src/util.js +26 -1
  22. package/framework/{v0.6.7 → v0.6.8}/lib/storage-store.js +33 -23
  23. package/framework/{v0.6.7 → v0.6.8}/package.json +1 -1
  24. package/gna.js +4 -4
  25. package/llms.txt +22 -47
  26. package/package.json +3 -3
  27. package/schema/routing.json +4 -0
  28. package/schema/settings.json +84 -10
  29. package/script/check_no_local_leak.js +2 -1
  30. package/script/retry_lockfile_sync.js +2 -1
  31. package/script/soak/storage-couchbase/README.md +61 -0
  32. package/script/soak/storage-couchbase/probe-cas.js +77 -0
  33. package/script/soak/storage-couchbase/probe-store.js +101 -0
  34. package/types/index.d.ts +18 -0
  35. package/framework/v0.6.7/VERSION +0 -1
  36. package/framework/v0.6.7/core/controller/controller.render-stream.js +0 -169
  37. /package/framework/{v0.6.7 → v0.6.8}/AUTHORS +0 -0
  38. /package/framework/{v0.6.7 → v0.6.8}/LICENSE +0 -0
  39. /package/framework/{v0.6.7 → v0.6.8}/core/asset/html/nolayout.html +0 -0
  40. /package/framework/{v0.6.7 → v0.6.8}/core/asset/html/static.html +0 -0
  41. /package/framework/{v0.6.7 → v0.6.8}/core/asset/img/android-chrome-192x192.png +0 -0
  42. /package/framework/{v0.6.7 → v0.6.8}/core/asset/img/android-chrome-512x512.png +0 -0
  43. /package/framework/{v0.6.7 → v0.6.8}/core/asset/img/apple-touch-icon.png +0 -0
  44. /package/framework/{v0.6.7 → v0.6.8}/core/asset/img/favicon-16x16.png +0 -0
  45. /package/framework/{v0.6.7 → v0.6.8}/core/asset/img/favicon-32x32.png +0 -0
  46. /package/framework/{v0.6.7 → v0.6.8}/core/asset/img/favicon.ico +0 -0
  47. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/README.md +0 -0
  48. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/beemaster/beemaster.css +0 -0
  49. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/beemaster/beemaster.js +0 -0
  50. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/beemaster/index.html +0 -0
  51. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/css/gina.min.css +0 -0
  52. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/css/gina.min.css.br +0 -0
  53. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/css/gina.min.css.gz +0 -0
  54. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/html/statusbar.html +0 -0
  55. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/html/statusbar.html.br +0 -0
  56. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/html/statusbar.html.gz +0 -0
  57. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/inspector/have_heart_one-webfont.woff2 +0 -0
  58. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/inspector/index.html +0 -0
  59. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/inspector/inspector.css +0 -0
  60. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/inspector/inspector.js +0 -0
  61. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/inspector/logo.svg +0 -0
  62. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/js/gina.js +0 -0
  63. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/js/gina.min.js +0 -0
  64. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/js/gina.min.js.br +0 -0
  65. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/js/gina.min.js.gz +0 -0
  66. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js +0 -0
  67. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js.br +0 -0
  68. /package/framework/{v0.6.7 → v0.6.8}/core/asset/plugin/dist/vendor/gina/js/gina.onload.min.js.gz +0 -0
  69. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/ai/index.js +0 -0
  70. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/ai/lib/connector.js +0 -0
  71. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/index.js +0 -0
  72. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/lib/connector.js +0 -0
  73. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/lib/connector.v3.js +0 -0
  74. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/lib/connector.v4.js +0 -0
  75. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/lib/n1ql.js +0 -0
  76. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/lib/session-store.js +0 -0
  77. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/lib/session-store.v3.js +0 -0
  78. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/couchbase/lib/session-store.v4.js +0 -0
  79. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/duckdb/index.js +0 -0
  80. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/duckdb/lib/connector.js +0 -0
  81. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/mongodb/index.js +0 -0
  82. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/mongodb/lib/connector.js +0 -0
  83. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/mongodb/lib/job-store.js +0 -0
  84. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/mongodb/lib/pipeline-loader.js +0 -0
  85. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/mongodb/lib/session-store.js +0 -0
  86. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/mysql/index.js +0 -0
  87. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/mysql/lib/connector.js +0 -0
  88. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/param-redact.js +0 -0
  89. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/postgresql/index.js +0 -0
  90. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/postgresql/lib/connector.js +0 -0
  91. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/redis/index.js +0 -0
  92. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/redis/lib/connector.js +0 -0
  93. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/redis/lib/job-store.js +0 -0
  94. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/redis/lib/render-cache-store.js +0 -0
  95. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/redis/lib/session-store.js +0 -0
  96. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/scylladb/index.js +0 -0
  97. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/scylladb/lib/connector.js +0 -0
  98. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/scylladb/lib/session-store.js +0 -0
  99. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/sql-parser.js +0 -0
  100. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/sqlite/index.js +0 -0
  101. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/sqlite/lib/connector.js +0 -0
  102. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/sqlite/lib/job-store.js +0 -0
  103. /package/framework/{v0.6.7 → v0.6.8}/core/connectors/sqlite/lib/session-store.js +0 -0
  104. /package/framework/{v0.6.7 → v0.6.8}/core/content.encoding +0 -0
  105. /package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.framework.js +0 -0
  106. /package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.render-json.js +0 -0
  107. /package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.render-nunjucks-async.js +0 -0
  108. /package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.render-nunjucks.js +0 -0
  109. /package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.render-swig-async.js +0 -0
  110. /package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.render-swig.js +0 -0
  111. /package/framework/{v0.6.7 → v0.6.8}/core/controller/controller.render-v1.js +0 -0
  112. /package/framework/{v0.6.7 → v0.6.8}/core/controller/index.js +0 -0
  113. /package/framework/{v0.6.7 → v0.6.8}/core/controller/inspector-window-emit.js +0 -0
  114. /package/framework/{v0.6.7 → v0.6.8}/core/controller/release-banner.js +0 -0
  115. /package/framework/{v0.6.7 → v0.6.8}/core/dev/index.js +0 -0
  116. /package/framework/{v0.6.7 → v0.6.8}/core/dev/lib/class.js +0 -0
  117. /package/framework/{v0.6.7 → v0.6.8}/core/dev/lib/factory.js +0 -0
  118. /package/framework/{v0.6.7 → v0.6.8}/core/dev/lib/tools.js +0 -0
  119. /package/framework/{v0.6.7 → v0.6.8}/core/locales/README.md +0 -0
  120. /package/framework/{v0.6.7 → v0.6.8}/core/locales/currency.json +0 -0
  121. /package/framework/{v0.6.7 → v0.6.8}/core/locales/dist/language/en.json +0 -0
  122. /package/framework/{v0.6.7 → v0.6.8}/core/locales/dist/language/fr.json +0 -0
  123. /package/framework/{v0.6.7 → v0.6.8}/core/locales/dist/region/en.json +0 -0
  124. /package/framework/{v0.6.7 → v0.6.8}/core/locales/dist/region/fr.json +0 -0
  125. /package/framework/{v0.6.7 → v0.6.8}/core/locales/index.js +0 -0
  126. /package/framework/{v0.6.7 → v0.6.8}/core/mime.types +0 -0
  127. /package/framework/{v0.6.7 → v0.6.8}/core/model/entity.js +0 -0
  128. /package/framework/{v0.6.7 → v0.6.8}/core/model/index.js +0 -0
  129. /package/framework/{v0.6.7 → v0.6.8}/core/model/template/entityFactory.js +0 -0
  130. /package/framework/{v0.6.7 → v0.6.8}/core/model/template/index.js +0 -0
  131. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/README.md +0 -0
  132. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/index.js +0 -0
  133. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/csrf/README.md +0 -0
  134. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/csrf/package.json +0 -0
  135. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/csrf/src/main.js +0 -0
  136. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/README.md +0 -0
  137. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/coep/README.md +0 -0
  138. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/coep/package.json +0 -0
  139. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/coep/src/main.js +0 -0
  140. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/coop/README.md +0 -0
  141. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/coop/package.json +0 -0
  142. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/coop/src/main.js +0 -0
  143. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/corp/README.md +0 -0
  144. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/corp/package.json +0 -0
  145. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/corp/src/main.js +0 -0
  146. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/csp/README.md +0 -0
  147. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/csp/package.json +0 -0
  148. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/csp/src/main.js +0 -0
  149. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/hide-powered-by/README.md +0 -0
  150. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/hide-powered-by/package.json +0 -0
  151. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/hide-powered-by/src/main.js +0 -0
  152. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/hsts/README.md +0 -0
  153. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/hsts/package.json +0 -0
  154. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/hsts/src/main.js +0 -0
  155. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/origin-agent-cluster/README.md +0 -0
  156. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/origin-agent-cluster/package.json +0 -0
  157. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/origin-agent-cluster/src/main.js +0 -0
  158. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/package.json +0 -0
  159. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/referrer-policy/README.md +0 -0
  160. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/referrer-policy/package.json +0 -0
  161. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/referrer-policy/src/main.js +0 -0
  162. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/src/main.js +0 -0
  163. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-content-type-options/README.md +0 -0
  164. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-content-type-options/package.json +0 -0
  165. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-content-type-options/src/main.js +0 -0
  166. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-dns-prefetch-control/README.md +0 -0
  167. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-dns-prefetch-control/package.json +0 -0
  168. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-dns-prefetch-control/src/main.js +0 -0
  169. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-download-options/README.md +0 -0
  170. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-download-options/package.json +0 -0
  171. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-download-options/src/main.js +0 -0
  172. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-frame-options/README.md +0 -0
  173. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-frame-options/package.json +0 -0
  174. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-frame-options/src/main.js +0 -0
  175. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-permitted-cross-domain-policies/README.md +0 -0
  176. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-permitted-cross-domain-policies/package.json +0 -0
  177. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-permitted-cross-domain-policies/src/main.js +0 -0
  178. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-xss-protection/README.md +0 -0
  179. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-xss-protection/package.json +0 -0
  180. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/security-headers/x-xss-protection/src/main.js +0 -0
  181. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/session/README.md +0 -0
  182. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/session/package.json +0 -0
  183. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/session/src/main.js +0 -0
  184. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/storage/README.md +0 -0
  185. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/storage/build.json +0 -0
  186. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/storage/package.json +0 -0
  187. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/storage/src/main.js +0 -0
  188. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/validator/README.md +0 -0
  189. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/validator/build.json +0 -0
  190. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/validator/package.json +0 -0
  191. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/validator/src/form-validator.js +0 -0
  192. /package/framework/{v0.6.7 → v0.6.8}/core/plugins/lib/validator/src/main.js +0 -0
  193. /package/framework/{v0.6.7 → v0.6.8}/core/server.express.js +0 -0
  194. /package/framework/{v0.6.7 → v0.6.8}/core/server.js +0 -0
  195. /package/framework/{v0.6.7 → v0.6.8}/core/status.codes +0 -0
  196. /package/framework/{v0.6.7 → v0.6.8}/core/template/_gitignore +0 -0
  197. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/config/app.json +0 -0
  198. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/config/connectors.json +0 -0
  199. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/config/routing.json +0 -0
  200. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/config/settings.json +0 -0
  201. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/config/settings.server.json +0 -0
  202. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/config/templates.json +0 -0
  203. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/config/watchers.json +0 -0
  204. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/controllers/controller.content.js +0 -0
  205. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/controllers/controller.js +0 -0
  206. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/controllers/setup.js +0 -0
  207. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/index.js +0 -0
  208. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle/locales/en.json +0 -0
  209. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_namespace/controllers/controller.js +0 -0
  210. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/css/default.css +0 -0
  211. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/css/home.css +0 -0
  212. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/css/vendor/readme.md +0 -0
  213. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/favicon.ico +0 -0
  214. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/js/components/x-checklist.js +0 -0
  215. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/js/vendor/readme.md +0 -0
  216. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/manifest.webmanifest +0 -0
  217. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/readme.md +0 -0
  218. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_public/sw.js +0 -0
  219. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_templates/handlers/main.js +0 -0
  220. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_templates/html/content/homepage.html +0 -0
  221. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_templates/html/includes/error-msg-noscript.html +0 -0
  222. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_templates/html/includes/error-msg-outdated-browser.html +0 -0
  223. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_templates/html/includes/x-checklist.html +0 -0
  224. /package/framework/{v0.6.7 → v0.6.8}/core/template/boilerplate/bundle_templates/html/layouts/main.html +0 -0
  225. /package/framework/{v0.6.7 → v0.6.8}/core/template/command/gina.bat.tpl +0 -0
  226. /package/framework/{v0.6.7 → v0.6.8}/core/template/command/gina.tpl +0 -0
  227. /package/framework/{v0.6.7 → v0.6.8}/core/template/conf/env.json +0 -0
  228. /package/framework/{v0.6.7 → v0.6.8}/core/template/conf/manifest.json +0 -0
  229. /package/framework/{v0.6.7 → v0.6.8}/core/template/conf/package.json +0 -0
  230. /package/framework/{v0.6.7 → v0.6.8}/core/template/conf/settings.json +0 -0
  231. /package/framework/{v0.6.7 → v0.6.8}/core/template/conf/statics.json +0 -0
  232. /package/framework/{v0.6.7 → v0.6.8}/core/template/conf/templates.json +0 -0
  233. /package/framework/{v0.6.7 → v0.6.8}/core/template/error/client/json/401.json +0 -0
  234. /package/framework/{v0.6.7 → v0.6.8}/core/template/error/client/json/403.json +0 -0
  235. /package/framework/{v0.6.7 → v0.6.8}/core/template/error/client/json/404.json +0 -0
  236. /package/framework/{v0.6.7 → v0.6.8}/core/template/error/server/html/50x.html +0 -0
  237. /package/framework/{v0.6.7 → v0.6.8}/core/template/error/server/json/500.json +0 -0
  238. /package/framework/{v0.6.7 → v0.6.8}/core/template/error/server/json/503.json +0 -0
  239. /package/framework/{v0.6.7 → v0.6.8}/core/template/extensions/logger/config.json +0 -0
  240. /package/framework/{v0.6.7 → v0.6.8}/helpers/console.js +0 -0
  241. /package/framework/{v0.6.7 → v0.6.8}/helpers/context.js +0 -0
  242. /package/framework/{v0.6.7 → v0.6.8}/helpers/data/LICENSE +0 -0
  243. /package/framework/{v0.6.7 → v0.6.8}/helpers/data/README.md +0 -0
  244. /package/framework/{v0.6.7 → v0.6.8}/helpers/data/package.json +0 -0
  245. /package/framework/{v0.6.7 → v0.6.8}/helpers/data/src/main.js +0 -0
  246. /package/framework/{v0.6.7 → v0.6.8}/helpers/dateFormat.js +0 -0
  247. /package/framework/{v0.6.7 → v0.6.8}/helpers/index.js +0 -0
  248. /package/framework/{v0.6.7 → v0.6.8}/helpers/json/LICENSE +0 -0
  249. /package/framework/{v0.6.7 → v0.6.8}/helpers/json/README.md +0 -0
  250. /package/framework/{v0.6.7 → v0.6.8}/helpers/json/package.json +0 -0
  251. /package/framework/{v0.6.7 → v0.6.8}/helpers/json/src/main.js +0 -0
  252. /package/framework/{v0.6.7 → v0.6.8}/helpers/path.js +0 -0
  253. /package/framework/{v0.6.7 → v0.6.8}/helpers/plugins/README.md +0 -0
  254. /package/framework/{v0.6.7 → v0.6.8}/helpers/plugins/package.json +0 -0
  255. /package/framework/{v0.6.7 → v0.6.8}/helpers/plugins/src/api-error.js +0 -0
  256. /package/framework/{v0.6.7 → v0.6.8}/helpers/plugins/src/main.js +0 -0
  257. /package/framework/{v0.6.7 → v0.6.8}/helpers/prototypes.js +0 -0
  258. /package/framework/{v0.6.7 → v0.6.8}/helpers/task.js +0 -0
  259. /package/framework/{v0.6.7 → v0.6.8}/helpers/text.js +0 -0
  260. /package/framework/{v0.6.7 → v0.6.8}/lib/admin/package.json +0 -0
  261. /package/framework/{v0.6.7 → v0.6.8}/lib/admin/src/main.js +0 -0
  262. /package/framework/{v0.6.7 → v0.6.8}/lib/archiver/README.md +0 -0
  263. /package/framework/{v0.6.7 → v0.6.8}/lib/archiver/build.json +0 -0
  264. /package/framework/{v0.6.7 → v0.6.8}/lib/archiver/package.json +0 -0
  265. /package/framework/{v0.6.7 → v0.6.8}/lib/archiver/src/dep/jszip.min.js +0 -0
  266. /package/framework/{v0.6.7 → v0.6.8}/lib/archiver/src/main.js +0 -0
  267. /package/framework/{v0.6.7 → v0.6.8}/lib/async/package.json +0 -0
  268. /package/framework/{v0.6.7 → v0.6.8}/lib/async/src/main.js +0 -0
  269. /package/framework/{v0.6.7 → v0.6.8}/lib/audit/package.json +0 -0
  270. /package/framework/{v0.6.7 → v0.6.8}/lib/audit/src/main.js +0 -0
  271. /package/framework/{v0.6.7 → v0.6.8}/lib/audit-store.js +0 -0
  272. /package/framework/{v0.6.7 → v0.6.8}/lib/authn/package.json +0 -0
  273. /package/framework/{v0.6.7 → v0.6.8}/lib/authn/src/lockout.js +0 -0
  274. /package/framework/{v0.6.7 → v0.6.8}/lib/authn/src/main.js +0 -0
  275. /package/framework/{v0.6.7 → v0.6.8}/lib/authn/src/totp.js +0 -0
  276. /package/framework/{v0.6.7 → v0.6.8}/lib/authz-gate/package.json +0 -0
  277. /package/framework/{v0.6.7 → v0.6.8}/lib/authz-gate/src/main.js +0 -0
  278. /package/framework/{v0.6.7 → v0.6.8}/lib/cache/README.md +0 -0
  279. /package/framework/{v0.6.7 → v0.6.8}/lib/cache/build.json +0 -0
  280. /package/framework/{v0.6.7 → v0.6.8}/lib/cache/package.json +0 -0
  281. /package/framework/{v0.6.7 → v0.6.8}/lib/cache/src/main.js +0 -0
  282. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/aliases.json +0 -0
  283. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/audit/arguments.json +0 -0
  284. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/audit/help.txt +0 -0
  285. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/audit/verify.js +0 -0
  286. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/add.js +0 -0
  287. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/arguments.json +0 -0
  288. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/build.js +0 -0
  289. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/copy.js +0 -0
  290. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/cp.js +0 -0
  291. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/help.js +0 -0
  292. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/help.txt +0 -0
  293. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/inc/name-rewrite.js +0 -0
  294. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/list.js +0 -0
  295. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/man.js +0 -0
  296. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/mcp-start.js +0 -0
  297. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/mcp.js +0 -0
  298. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/oas.js +0 -0
  299. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/openapi.js +0 -0
  300. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/remove.js +0 -0
  301. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/rename.js +0 -0
  302. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/restart.js +0 -0
  303. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/rm.js +0 -0
  304. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/start.js +0 -0
  305. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/status.js +0 -0
  306. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/stop.js +0 -0
  307. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/bundle/types.js +0 -0
  308. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/cache/arguments.json +0 -0
  309. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/cache/clear.js +0 -0
  310. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/cache/help.txt +0 -0
  311. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/cache/stats.js +0 -0
  312. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/add.js +0 -0
  313. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/arguments.json +0 -0
  314. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/help.js +0 -0
  315. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/help.txt +0 -0
  316. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/infer.js +0 -0
  317. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/list.js +0 -0
  318. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/migrate.js +0 -0
  319. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/models.js +0 -0
  320. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/remove.js +0 -0
  321. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/rm.js +0 -0
  322. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/connector/test.js +0 -0
  323. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/container/arguments.json +0 -0
  324. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/container/help.js +0 -0
  325. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/container/help.txt +0 -0
  326. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/container/man.js +0 -0
  327. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/container/ps.js +0 -0
  328. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/container/stop.js +0 -0
  329. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/add.js +0 -0
  330. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/arguments.json +0 -0
  331. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/help.txt +0 -0
  332. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/inc/args.js +0 -0
  333. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/inc/namespace.js +0 -0
  334. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/inc/reference-rewrite.js +0 -0
  335. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/inc/reference-scan.js +0 -0
  336. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/inc/scaffold.js +0 -0
  337. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/remove.js +0 -0
  338. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/rename.js +0 -0
  339. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/controller/rm.js +0 -0
  340. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/add.js +0 -0
  341. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/get.js +0 -0
  342. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/help.js +0 -0
  343. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/help.txt +0 -0
  344. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/link-dev.js +0 -0
  345. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/list.js +0 -0
  346. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/remove.js +0 -0
  347. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/rm.js +0 -0
  348. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/set.js +0 -0
  349. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/unset.js +0 -0
  350. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/env/use.js +0 -0
  351. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/add.js +0 -0
  352. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/arguments.json +0 -0
  353. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/build.js +0 -0
  354. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/dot.js +0 -0
  355. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/get.js +0 -0
  356. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/help.js +0 -0
  357. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/help.txt +0 -0
  358. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/init.js +0 -0
  359. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/link-node-modules.js +0 -0
  360. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/link.js +0 -0
  361. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/list.js +0 -0
  362. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/man.js +0 -0
  363. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/msg.json +0 -0
  364. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/open.js +0 -0
  365. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/remove.js +0 -0
  366. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/reset.js +0 -0
  367. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/restart.js +0 -0
  368. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/set.js +0 -0
  369. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/start.js +0 -0
  370. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/status.js +0 -0
  371. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/stop.js +0 -0
  372. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/tail.js +0 -0
  373. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/update.js +0 -0
  374. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/framework/version.js +0 -0
  375. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/gina-dev.1.md +0 -0
  376. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/gina-framework.1.md +0 -0
  377. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/gina.1.md +0 -0
  378. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/helper.js +0 -0
  379. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/i18n/add.js +0 -0
  380. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/i18n/arguments.json +0 -0
  381. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/i18n/export.js +0 -0
  382. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/i18n/help.js +0 -0
  383. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/i18n/help.txt +0 -0
  384. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/i18n/import.js +0 -0
  385. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/i18n/scan.js +0 -0
  386. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/_host.js +0 -0
  387. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/arguments.json +0 -0
  388. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/build.js +0 -0
  389. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/help.js +0 -0
  390. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/help.txt +0 -0
  391. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/list.js +0 -0
  392. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/man.js +0 -0
  393. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/rm.js +0 -0
  394. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/image/run.js +0 -0
  395. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/index.js +0 -0
  396. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/inspector/help.js +0 -0
  397. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/inspector/help.txt +0 -0
  398. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/inspector/open.js +0 -0
  399. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/man-render.js +0 -0
  400. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/minion/arguments.json +0 -0
  401. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/minion/help.js +0 -0
  402. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/minion/help.txt +0 -0
  403. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/minion/kill.js +0 -0
  404. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/minion/list.js +0 -0
  405. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/msg.json +0 -0
  406. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/port/help.js +0 -0
  407. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/port/help.txt +0 -0
  408. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/port/inc/scan.js +0 -0
  409. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/port/list.js +0 -0
  410. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/port/reset.js +0 -0
  411. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/port/set.js +0 -0
  412. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/add.js +0 -0
  413. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/arguments.json +0 -0
  414. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/backup.js +0 -0
  415. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/build.js +0 -0
  416. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/help.js +0 -0
  417. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/help.txt +0 -0
  418. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/import.js +0 -0
  419. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/list.js +0 -0
  420. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/man.js +0 -0
  421. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/move.js +0 -0
  422. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/remove.js +0 -0
  423. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/rename.js +0 -0
  424. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/restart.js +0 -0
  425. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/restore.js +0 -0
  426. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/rm.js +0 -0
  427. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/start.js +0 -0
  428. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/status.js +0 -0
  429. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/project/stop.js +0 -0
  430. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/protocol/arguments.json +0 -0
  431. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/protocol/help.js +0 -0
  432. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/protocol/help.txt +0 -0
  433. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/protocol/list.js +0 -0
  434. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/protocol/remove.js +0 -0
  435. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/protocol/set.js +0 -0
  436. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/add.js +0 -0
  437. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/help.js +0 -0
  438. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/help.txt +0 -0
  439. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/link-local.js +0 -0
  440. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/link-production.js +0 -0
  441. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/list.js +0 -0
  442. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/remove.js +0 -0
  443. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/rm.js +0 -0
  444. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/scope/use.js +0 -0
  445. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/secrets/arguments.json +0 -0
  446. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/secrets/check.js +0 -0
  447. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/secrets/help.js +0 -0
  448. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/secrets/help.txt +0 -0
  449. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/secrets/scan.js +0 -0
  450. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/service/help.js +0 -0
  451. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/service/help.txt +0 -0
  452. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/service/list.js +0 -0
  453. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/service/man.js +0 -0
  454. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/service/start.js +0 -0
  455. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/storage/arguments.json +0 -0
  456. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/storage/help.txt +0 -0
  457. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd/view/add.js +0 -0
  458. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd-status-format/package.json +0 -0
  459. /package/framework/{v0.6.7 → v0.6.8}/lib/cmd-status-format/src/main.js +0 -0
  460. /package/framework/{v0.6.7 → v0.6.8}/lib/collection/README.md +0 -0
  461. /package/framework/{v0.6.7 → v0.6.8}/lib/collection/build.json +0 -0
  462. /package/framework/{v0.6.7 → v0.6.8}/lib/collection/package.json +0 -0
  463. /package/framework/{v0.6.7 → v0.6.8}/lib/collection/src/main.js +0 -0
  464. /package/framework/{v0.6.7 → v0.6.8}/lib/config.js +0 -0
  465. /package/framework/{v0.6.7 → v0.6.8}/lib/connector-config/package.json +0 -0
  466. /package/framework/{v0.6.7 → v0.6.8}/lib/connector-config/src/main.js +0 -0
  467. /package/framework/{v0.6.7 → v0.6.8}/lib/connector-error/package.json +0 -0
  468. /package/framework/{v0.6.7 → v0.6.8}/lib/connector-error/src/main.js +0 -0
  469. /package/framework/{v0.6.7 → v0.6.8}/lib/connector-registry/package.json +0 -0
  470. /package/framework/{v0.6.7 → v0.6.8}/lib/connector-registry/src/main.js +0 -0
  471. /package/framework/{v0.6.7 → v0.6.8}/lib/cron/README.md +0 -0
  472. /package/framework/{v0.6.7 → v0.6.8}/lib/cron/package.json +0 -0
  473. /package/framework/{v0.6.7 → v0.6.8}/lib/cron/src/main.js +0 -0
  474. /package/framework/{v0.6.7 → v0.6.8}/lib/domain/LICENSE +0 -0
  475. /package/framework/{v0.6.7 → v0.6.8}/lib/domain/README.md +0 -0
  476. /package/framework/{v0.6.7 → v0.6.8}/lib/domain/package.json +0 -0
  477. /package/framework/{v0.6.7 → v0.6.8}/lib/domain/src/main.js +0 -0
  478. /package/framework/{v0.6.7 → v0.6.8}/lib/dto/package.json +0 -0
  479. /package/framework/{v0.6.7 → v0.6.8}/lib/dto/src/main.js +0 -0
  480. /package/framework/{v0.6.7 → v0.6.8}/lib/dto-pipe/package.json +0 -0
  481. /package/framework/{v0.6.7 → v0.6.8}/lib/dto-pipe/src/main.js +0 -0
  482. /package/framework/{v0.6.7 → v0.6.8}/lib/dto-types/package.json +0 -0
  483. /package/framework/{v0.6.7 → v0.6.8}/lib/dto-types/src/main.js +0 -0
  484. /package/framework/{v0.6.7 → v0.6.8}/lib/generator/index.js +0 -0
  485. /package/framework/{v0.6.7 → v0.6.8}/lib/i18n/package.json +0 -0
  486. /package/framework/{v0.6.7 → v0.6.8}/lib/i18n/src/main.js +0 -0
  487. /package/framework/{v0.6.7 → v0.6.8}/lib/image-build/package.json +0 -0
  488. /package/framework/{v0.6.7 → v0.6.8}/lib/image-build/src/main.js +0 -0
  489. /package/framework/{v0.6.7 → v0.6.8}/lib/index.js +0 -0
  490. /package/framework/{v0.6.7 → v0.6.8}/lib/inherits/LICENSE +0 -0
  491. /package/framework/{v0.6.7 → v0.6.8}/lib/inherits/README.md +0 -0
  492. /package/framework/{v0.6.7 → v0.6.8}/lib/inherits/package.json +0 -0
  493. /package/framework/{v0.6.7 → v0.6.8}/lib/inherits/src/main.js +0 -0
  494. /package/framework/{v0.6.7 → v0.6.8}/lib/inspector-events/package.json +0 -0
  495. /package/framework/{v0.6.7 → v0.6.8}/lib/inspector-events/src/main.js +0 -0
  496. /package/framework/{v0.6.7 → v0.6.8}/lib/inspector-redact/package.json +0 -0
  497. /package/framework/{v0.6.7 → v0.6.8}/lib/inspector-redact/src/main.js +0 -0
  498. /package/framework/{v0.6.7 → v0.6.8}/lib/instrument/package.json +0 -0
  499. /package/framework/{v0.6.7 → v0.6.8}/lib/instrument/src/main.js +0 -0
  500. /package/framework/{v0.6.7 → v0.6.8}/lib/job/package.json +0 -0
  501. /package/framework/{v0.6.7 → v0.6.8}/lib/job/src/main.js +0 -0
  502. /package/framework/{v0.6.7 → v0.6.8}/lib/job-store.js +0 -0
  503. /package/framework/{v0.6.7 → v0.6.8}/lib/json-config-header/package.json +0 -0
  504. /package/framework/{v0.6.7 → v0.6.8}/lib/json-config-header/src/main.js +0 -0
  505. /package/framework/{v0.6.7 → v0.6.8}/lib/loading-state/package.json +0 -0
  506. /package/framework/{v0.6.7 → v0.6.8}/lib/loading-state/src/main.js +0 -0
  507. /package/framework/{v0.6.7 → v0.6.8}/lib/logger/README.md +0 -0
  508. /package/framework/{v0.6.7 → v0.6.8}/lib/logger/package.json +0 -0
  509. /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/containers/default/index.js +0 -0
  510. /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/containers/file/index.js +0 -0
  511. /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/containers/file/lib/logrotator/README.md +0 -0
  512. /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/containers/file/lib/logrotator/index.js +0 -0
  513. /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/containers/mq/index.js +0 -0
  514. /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/containers/mq/listener.js +0 -0
  515. /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/containers/mq/speaker.js +0 -0
  516. /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/helper.js +0 -0
  517. /package/framework/{v0.6.7 → v0.6.8}/lib/logger/src/main.js +0 -0
  518. /package/framework/{v0.6.7 → v0.6.8}/lib/math/index.js +0 -0
  519. /package/framework/{v0.6.7 → v0.6.8}/lib/mcp-dispatch/package.json +0 -0
  520. /package/framework/{v0.6.7 → v0.6.8}/lib/mcp-dispatch/src/main.js +0 -0
  521. /package/framework/{v0.6.7 → v0.6.8}/lib/mcp-http/package.json +0 -0
  522. /package/framework/{v0.6.7 → v0.6.8}/lib/mcp-http/src/main.js +0 -0
  523. /package/framework/{v0.6.7 → v0.6.8}/lib/mcp-server/package.json +0 -0
  524. /package/framework/{v0.6.7 → v0.6.8}/lib/mcp-server/src/main.js +0 -0
  525. /package/framework/{v0.6.7 → v0.6.8}/lib/merge/README.md +0 -0
  526. /package/framework/{v0.6.7 → v0.6.8}/lib/merge/package.json +0 -0
  527. /package/framework/{v0.6.7 → v0.6.8}/lib/merge/src/main.js +0 -0
  528. /package/framework/{v0.6.7 → v0.6.8}/lib/metrics/package.json +0 -0
  529. /package/framework/{v0.6.7 → v0.6.8}/lib/metrics/src/main.js +0 -0
  530. /package/framework/{v0.6.7 → v0.6.8}/lib/model.js +0 -0
  531. /package/framework/{v0.6.7 → v0.6.8}/lib/net-locality/package.json +0 -0
  532. /package/framework/{v0.6.7 → v0.6.8}/lib/net-locality/src/main.js +0 -0
  533. /package/framework/{v0.6.7 → v0.6.8}/lib/nunjucks-filters/README.md +0 -0
  534. /package/framework/{v0.6.7 → v0.6.8}/lib/nunjucks-filters/package.json +0 -0
  535. /package/framework/{v0.6.7 → v0.6.8}/lib/nunjucks-filters/src/main.js +0 -0
  536. /package/framework/{v0.6.7 → v0.6.8}/lib/nunjucks-resolver/package.json +0 -0
  537. /package/framework/{v0.6.7 → v0.6.8}/lib/nunjucks-resolver/src/main.js +0 -0
  538. /package/framework/{v0.6.7 → v0.6.8}/lib/proc.js +0 -0
  539. /package/framework/{v0.6.7 → v0.6.8}/lib/release-watch/package.json +0 -0
  540. /package/framework/{v0.6.7 → v0.6.8}/lib/release-watch/src/main.js +0 -0
  541. /package/framework/{v0.6.7 → v0.6.8}/lib/render-cache/package.json +0 -0
  542. /package/framework/{v0.6.7 → v0.6.8}/lib/render-cache/src/main.js +0 -0
  543. /package/framework/{v0.6.7 → v0.6.8}/lib/render-cache-store.js +0 -0
  544. /package/framework/{v0.6.7 → v0.6.8}/lib/routing/README.md +0 -0
  545. /package/framework/{v0.6.7 → v0.6.8}/lib/routing/build.json +0 -0
  546. /package/framework/{v0.6.7 → v0.6.8}/lib/routing/package.json +0 -0
  547. /package/framework/{v0.6.7 → v0.6.8}/lib/routing/src/main.js +0 -0
  548. /package/framework/{v0.6.7 → v0.6.8}/lib/routing/src/radix.js +0 -0
  549. /package/framework/{v0.6.7 → v0.6.8}/lib/routing-introspect/package.json +0 -0
  550. /package/framework/{v0.6.7 → v0.6.8}/lib/routing-introspect/src/main.js +0 -0
  551. /package/framework/{v0.6.7 → v0.6.8}/lib/secrets/package.json +0 -0
  552. /package/framework/{v0.6.7 → v0.6.8}/lib/secrets/src/backends/env.js +0 -0
  553. /package/framework/{v0.6.7 → v0.6.8}/lib/secrets/src/backends/file.js +0 -0
  554. /package/framework/{v0.6.7 → v0.6.8}/lib/secrets/src/env-file.js +0 -0
  555. /package/framework/{v0.6.7 → v0.6.8}/lib/secrets/src/main.js +0 -0
  556. /package/framework/{v0.6.7 → v0.6.8}/lib/session-store.js +0 -0
  557. /package/framework/{v0.6.7 → v0.6.8}/lib/shell.js +0 -0
  558. /package/framework/{v0.6.7 → v0.6.8}/lib/sqlite-driver.js +0 -0
  559. /package/framework/{v0.6.7 → v0.6.8}/lib/state.js +0 -0
  560. /package/framework/{v0.6.7 → v0.6.8}/lib/storage/package.json +0 -0
  561. /package/framework/{v0.6.7 → v0.6.8}/lib/swig-filters/README.md +0 -0
  562. /package/framework/{v0.6.7 → v0.6.8}/lib/swig-filters/package.json +0 -0
  563. /package/framework/{v0.6.7 → v0.6.8}/lib/swig-filters/src/main.js +0 -0
  564. /package/framework/{v0.6.7 → v0.6.8}/lib/swig-resolver/package.json +0 -0
  565. /package/framework/{v0.6.7 → v0.6.8}/lib/swig-resolver/src/main.js +0 -0
  566. /package/framework/{v0.6.7 → v0.6.8}/lib/template-loaders/package.json +0 -0
  567. /package/framework/{v0.6.7 → v0.6.8}/lib/template-loaders/src/loaders/http.js +0 -0
  568. /package/framework/{v0.6.7 → v0.6.8}/lib/template-loaders/src/loaders/memory.js +0 -0
  569. /package/framework/{v0.6.7 → v0.6.8}/lib/template-loaders/src/main.js +0 -0
  570. /package/framework/{v0.6.7 → v0.6.8}/lib/url/README.md +0 -0
  571. /package/framework/{v0.6.7 → v0.6.8}/lib/url/index.js +0 -0
  572. /package/framework/{v0.6.7 → v0.6.8}/lib/url/routing.json +0 -0
  573. /package/framework/{v0.6.7 → v0.6.8}/lib/uuid/package.json +0 -0
  574. /package/framework/{v0.6.7 → v0.6.8}/lib/uuid/src/main.js +0 -0
  575. /package/framework/{v0.6.7 → v0.6.8}/lib/validator.js +0 -0
  576. /package/framework/{v0.6.7 → v0.6.8}/lib/watcher/package.json +0 -0
  577. /package/framework/{v0.6.7 → v0.6.8}/lib/watcher/src/main.js +0 -0
  578. /package/framework/{v0.6.7 → v0.6.8}/lib/ws-framing/package.json +0 -0
  579. /package/framework/{v0.6.7 → v0.6.8}/lib/ws-framing/src/main.js +0 -0
  580. /package/framework/{v0.6.7 → v0.6.8}/lib/ws-query/package.json +0 -0
  581. /package/framework/{v0.6.7 → v0.6.8}/lib/ws-query/src/main.js +0 -0
  582. /package/framework/{v0.6.7 → v0.6.8}/lib/ws-session/package.json +0 -0
  583. /package/framework/{v0.6.7 → v0.6.8}/lib/ws-session/src/main.js +0 -0
@@ -0,0 +1,1418 @@
1
+ /*
2
+ * This file is part of the gina package.
3
+ * Copyright (c) 2009-2026 Rhinostone <contact@gina.io>
4
+ *
5
+ * For the full copyright and license information, please view the LICENSE
6
+ * file that was distributed with this source code.
7
+ */
8
+ 'use strict';
9
+
10
+ /**
11
+ * @module lib/storage/local-stream
12
+ * @description The `local` adapter crossed with the `stream` strategy — large
13
+ * sequential media on the local filesystem, with **resumable uploads**.
14
+ *
15
+ * **The key names an ASSET, not a file**: `assets/<ulid>/original<ext>`. The
16
+ * per-asset directory is the namespace future renditions land in (`preview`,
17
+ * `thumb`, a transcoded variant); `original` is the reserved base-rendition
18
+ * name. No rendition API ships in this slice — the layout reserves the room.
19
+ * Keys stay OPAQUE: callers store what `finalize()`/`put()` returns and never
20
+ * parse the directory out of it.
21
+ *
22
+ * **What per-asset colocation actually buys, stated honestly.** It is
23
+ * OPERATIONAL grouping — one `rm -rf` removes an asset and everything derived
24
+ * from it, one `rsync` moves a subtree. It is NOT physical locality: a
25
+ * directory does not place extents, the allocator does. And gina neither
26
+ * prevents nor can prevent fragmentation — Node exposes no `fallocate`
27
+ * binding, native addons are not shipped, and `ftruncate` produces a fully
28
+ * SPARSE file (measured: 256MB truncate ⇒ 0 blocks on disk), so it reserves
29
+ * nothing and gives neither anti-fragmentation nor an early ENOSPC.
30
+ * Contiguity is a filesystem/volume concern (XFS extent hints and the like).
31
+ * The same honesty the `cas` slice applied to `F_FULLFSYNC`.
32
+ *
33
+ * **Resumable uploads: the filesystem IS the manifest.** A session lives at
34
+ * `<root>/.uploads/<uploadId>/` and holds exactly three kinds of entry —
35
+ * `data` (the file being assembled in place, written POSITIONALLY so segments
36
+ * may arrive out of order), `meta.json` (written ONCE at create through
37
+ * temp+rename, so its state is binary: present-and-parseable, or absent), and
38
+ * one zero-byte `<offset>-<length>.ok` marker per DURABLE range. Nothing about
39
+ * a session touches the metadata store, and the seam gains ZERO new verbs.
40
+ *
41
+ * The argument for that home is FATE-SHARING, not seam economy: the manifest
42
+ * must share crash-fate and fsync-ordering with the bytes it describes.
43
+ * `fsync(data)` happens-before the marker is created, which is enforceable
44
+ * only while both live on the same medium. A store-backed manifest (SQLite
45
+ * WAL, redis, couchbase) can survive a crash the bytes did not, and a manifest
46
+ * claiming a range the bytes lost passes the coverage check and publishes
47
+ * ZEROS — reintroducing, through desync, exactly the hazard the coverage check
48
+ * exists to close.
49
+ *
50
+ * **The zero-fill hazard, and why coverage is an INTERVAL UNION.** A hole in a
51
+ * sparse file reads back as zeros, not as an error (measured). So a finalise
52
+ * that did not verify coverage would publish a plausible, silently zero-filled
53
+ * object. Verifying by SUMMING marker lengths is not enough either: markers
54
+ * `0–8MB` and `4–12MB` sum to 16MB while `12–16MB` is uncovered. Coverage is
55
+ * therefore computed by sorting the markers, merging overlapping and adjacent
56
+ * spans, and requiring exactly ONE span equal to `[0, expectedSize)`.
57
+ *
58
+ * **Durability (`fsync: true`, the stream default).** Per segment: positional
59
+ * write → `fsync(data)` → create the marker. Only the DATA fsync is
60
+ * load-bearing; the marker's own durability is deliberately not guaranteed,
61
+ * because a lost marker beside present bytes costs a re-send, which is the
62
+ * safe direction. Measured: a process killed with SIGKILL after an un-fsynced
63
+ * write still yields the full bytes to a later reader — the page cache carries
64
+ * them — so markers stay honest across PROCESS death without fsync; only power
65
+ * loss or a kernel panic opens the window. The cost is a RANGE, not the single
66
+ * flattering figure: ~12ms per 8MB segment is ~2% of network-paced ingest at
67
+ * 100 Mbps, ~19% at 1 Gbps, and fsync-DOMINANT at 10 Gbps. A LAN-ingest
68
+ * consumer sets `fsync: false` and accepts the documented window. Inherits the
69
+ * `cas` slice's per-platform honesty: the parent-directory flush is
70
+ * best-effort, and macOS honours `fsync` as the platform defines it.
71
+ *
72
+ * **Multi-process: documented, not enforced.** Parallel segment writes are
73
+ * safe when the ranges are DISJOINT — measured on a POSIX-coherent local
74
+ * filesystem: two uncoordinated processes interleaving 1MB stripes into one
75
+ * shared file produced byte-exact output. On relaxed-coherence network mounts
76
+ * (GlusterFS and friends) that has NOT been measured; route one session's
77
+ * segments to one process there. Nothing in this driver enforces affinity —
78
+ * cross-process discovery does not exist in this layer.
79
+ *
80
+ * **No tiering, no hashing.** A multi-gigabyte strategy has no business
81
+ * inlining its payload into a metadata row, and the write path deliberately
82
+ * does not hash. Setting `inlineThreshold` or `hash` on a `stream` driver
83
+ * therefore warns as an ignored key rather than silently doing nothing —
84
+ * `capabilities.inline` and `capabilities.dedup` are both `false`.
85
+ *
86
+ * @example
87
+ * // settings.json — a stream driver for large media
88
+ * // { "storage": { "drivers": { "media": {
89
+ * // "adapter": "local", "strategy": "stream",
90
+ * // "root": "/var/data/media", "maxObjectSize": "5GB", "chunkSize": "8MB"
91
+ * // } } } }
92
+ * var s = gina.storage('media');
93
+ * s.createUpload({ expectedSize: 4294967296, originalName: 'talk.mp4' },
94
+ * function (err, session) {
95
+ * if (err) { return next(err); }
96
+ * // session => { uploadId, chunkSize, expectedSize }
97
+ * });
98
+ */
99
+
100
+ var fs = require('fs');
101
+ var nodePath = require('path');
102
+
103
+ var util = require('./util');
104
+
105
+ /**
106
+ * Directory holding in-flight `put()` temp files, relative to the driver root.
107
+ * Same name as the other local strategies' — one temp namespace per root.
108
+ *
109
+ * @inner
110
+ * @constant
111
+ * @type {string}
112
+ */
113
+ var TMP_DIR = '.tmp';
114
+
115
+ /**
116
+ * Directory holding resumable upload sessions, relative to the driver root.
117
+ * A dot segment, so it is structurally uncollidable with any caller key — the
118
+ * key guards refuse dot segments, the same trick `.tmp` and the `.driver`
119
+ * stamp row already use.
120
+ *
121
+ * @inner
122
+ * @constant
123
+ * @type {string}
124
+ */
125
+ var UPLOADS_DIR = '.uploads';
126
+
127
+ /**
128
+ * Name of the file a session assembles bytes into, inside its own directory.
129
+ * Its mtime — not the directory's — is the session's liveness signal: it
130
+ * advances on every segment write, whereas a directory mtime only moves when
131
+ * entries are added or removed.
132
+ *
133
+ * @inner
134
+ * @constant
135
+ * @type {string}
136
+ */
137
+ var DATA_FILE = 'data';
138
+
139
+ /**
140
+ * Name of a session's manifest, inside its own directory. Written ONCE, at
141
+ * create, through temp+rename — never mutated, so no read-modify-write hazard
142
+ * exists and no crash can leave it half-written.
143
+ *
144
+ * @inner
145
+ * @constant
146
+ * @type {string}
147
+ */
148
+ var META_FILE = 'meta.json';
149
+
150
+ /**
151
+ * The reserved base-rendition file name inside an asset directory.
152
+ *
153
+ * @inner
154
+ * @constant
155
+ * @type {string}
156
+ */
157
+ var ORIGINAL = 'original';
158
+
159
+ /**
160
+ * A durable-range marker's file name grammar: `<offset>-<length>.ok`.
161
+ * Anything else in a session directory is ignored by the coverage scan.
162
+ *
163
+ * @inner
164
+ * @constant
165
+ * @type {RegExp}
166
+ */
167
+ var MARKER_RE = /^([0-9]+)-([0-9]+)\.ok$/;
168
+
169
+ /**
170
+ * A driver-minted upload id: a ULID in Crockford base32, which excludes I, L,
171
+ * O and U. Validated BEFORE any path join — an id reaches the filesystem as a
172
+ * directory name, so this is a security boundary, not a formatting nicety
173
+ * (`findByDigest`'s posture in the cas strategy).
174
+ *
175
+ * @inner
176
+ * @constant
177
+ * @type {RegExp}
178
+ */
179
+ var UPLOAD_ID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/;
180
+
181
+ /**
182
+ * A resumable upload session, as returned by `createUpload()`.
183
+ *
184
+ * @typedef {Object} StorageUploadSession
185
+ * @property {string} uploadId - Opaque, driver-minted session id. Hand it back to
186
+ * `writeSegment`/`statUpload`/`finalize`/`abortUpload`.
187
+ * @property {number} chunkSize - The driver's configured segment size, in bytes. Advisory:
188
+ * a client may send any segment size, but this is the size
189
+ * the write path is tuned for.
190
+ * @property {number} expectedSize - The declared total, echoed back.
191
+ */
192
+
193
+ /**
194
+ * One durable byte range of a session, as reported by `statUpload()`.
195
+ *
196
+ * @typedef {Object} StorageUploadSpan
197
+ * @property {number} offset - First byte of the span.
198
+ * @property {number} length - Span length in bytes; the span covers `[offset, offset+length)`.
199
+ */
200
+
201
+ /**
202
+ * The state of a resumable upload, as returned by `statUpload()` — the verb
203
+ * that makes resuming possible at all.
204
+ *
205
+ * @typedef {Object} StorageUploadState
206
+ * @property {number} expectedSize - The declared total, from `createUpload`.
207
+ * @property {?string} contentType - Client-supplied MIME type, verbatim.
208
+ * @property {?string} originalName - Client-supplied filename, verbatim.
209
+ * @property {number} createdAt - Epoch ms at session creation.
210
+ * @property {StorageUploadSpan[]} received - Durable ranges, merged and offset-ordered.
211
+ * @property {StorageUploadSpan[]} missing - The gaps within `[0, expectedSize)` — what a
212
+ * resuming client must still send. An HTTP
213
+ * protocol that reports only a contiguous
214
+ * offset (tus) reads `received[0]`.
215
+ * @property {boolean} complete - `true` when `missing` is empty, i.e. when
216
+ * `finalize()` would succeed.
217
+ */
218
+
219
+ /**
220
+ * Build a `local` filesystem driver with the `stream` (large sequential media,
221
+ * resumable) strategy.
222
+ *
223
+ * @param {string} name - Driver name, used only in error messages.
224
+ * @param {object} conf - Resolved driver configuration (defaults
225
+ * resolve in `start()`, never here — the
226
+ * `maxObjectSize` pattern).
227
+ * @param {string} conf.root - Absolute driver root. Validated at boot
228
+ * to be absolute and outside every
229
+ * web-served tree.
230
+ * @param {number} conf.maxObjectSize - Per-object byte ceiling, already parsed.
231
+ * @param {number} conf.chunkSize - Write-stream `highWaterMark`, in bytes.
232
+ * @param {boolean} conf.fsync - Whether segment writes and the finalise
233
+ * publish flush to disk (file hard, parent
234
+ * dir best-effort).
235
+ * @param {number} conf.sessionTtl - How long an untouched session survives
236
+ * before the sweep reclaims it, in ms.
237
+ * @param {number} conf.sessionSweepInterval - Sweep period in ms; `0` disables the
238
+ * periodic sweep (the build-time pass
239
+ * still runs).
240
+ * @param {StorageMetaStore} metaStore - Metadata backend (embedded SQLite by
241
+ * default). No refcount verbs required —
242
+ * `stream` uses the four required ones.
243
+ * @returns {StorageDriver} A ready driver (plus the five resumable verbs).
244
+ *
245
+ * @example
246
+ * var driver = createLocalStreamDriver('media', conf, metaStore);
247
+ */
248
+ module.exports = function createLocalStreamDriver(name, conf, metaStore) {
249
+
250
+ var root = conf.root;
251
+ var max = conf.maxObjectSize;
252
+ var fsyncOn = ( conf.fsync === true );
253
+ var chunkSize = ( typeof conf.chunkSize === 'number' && conf.chunkSize > 0 )
254
+ ? conf.chunkSize
255
+ : 0;
256
+ var sessionTtlMs = ( typeof conf.sessionTtl === 'number' && conf.sessionTtl > 0 )
257
+ ? conf.sessionTtl
258
+ : 0;
259
+ var sweepIntervalMs = ( typeof conf.sessionSweepInterval === 'number' && conf.sessionSweepInterval > 0 )
260
+ ? conf.sessionSweepInterval
261
+ : 0;
262
+
263
+ /**
264
+ * Key → confined absolute path. Shared, single-sourced guard sequence —
265
+ * see {@link module:lib/storage/util.makeResolvePath} for the three guards
266
+ * and why their ORDER is load-bearing.
267
+ *
268
+ * @inner
269
+ * @type {function(string): {path: ?string, error: ?Error}}
270
+ */
271
+ var resolvePath = util.makeResolvePath(name, root);
272
+
273
+ var tmpD = nodePath.join(root, TMP_DIR);
274
+ var uploadsD = nodePath.join(root, UPLOADS_DIR);
275
+
276
+ /**
277
+ * fsync a file by path — the HARD half of the durability contract: a
278
+ * failure here fails the operation that asked for durability.
279
+ *
280
+ * @inner
281
+ * @param {string} p - File path.
282
+ * @returns {void}
283
+ */
284
+ var fsyncFile = function(p) {
285
+ var fd = fs.openSync(p, 'r');
286
+ try {
287
+ fs.fsyncSync(fd);
288
+ } finally {
289
+ fs.closeSync(fd);
290
+ }
291
+ };
292
+
293
+ /**
294
+ * fsync a directory — the BEST-EFFORT half: platforms and filesystems that
295
+ * cannot fsync a directory (Windows; some network mounts) throw here and
296
+ * the publish proceeds, which the module header documents rather than
297
+ * silently implies.
298
+ *
299
+ * @inner
300
+ * @param {string} p - Directory path.
301
+ * @returns {void}
302
+ */
303
+ var fsyncDirBestEffort = function(p) {
304
+ try {
305
+ var fd = fs.openSync(p, 'r');
306
+ try {
307
+ fs.fsyncSync(fd);
308
+ } finally {
309
+ fs.closeSync(fd);
310
+ }
311
+ } catch (e) {
312
+ // platform cannot fsync a directory — best-effort by contract
313
+ }
314
+ };
315
+
316
+ /**
317
+ * Validate an upload id and resolve its session directory.
318
+ *
319
+ * The id is charset-validated BEFORE any path join, so a traversal
320
+ * attempt (`../`), a dot segment or a stray separator never reaches
321
+ * `path.join` at all — the same posture `findByDigest` takes with a
322
+ * caller-supplied digest.
323
+ *
324
+ * @inner
325
+ * @param {*} uploadId - Caller-supplied session id, any type tolerated.
326
+ * @returns {{dir: ?string, error: ?Error}} Exactly one side is set.
327
+ */
328
+ var resolveSession = function(uploadId) {
329
+ if ( typeof uploadId !== 'string' || !UPLOAD_ID_RE.test(uploadId) ) {
330
+ return { dir: null, error: new Error('[storage:' + name + '] not a valid upload id: ' + JSON.stringify(uploadId)) };
331
+ }
332
+ return { dir: nodePath.join(uploadsD, uploadId), error: null };
333
+ };
334
+
335
+ /**
336
+ * Read a session manifest.
337
+ *
338
+ * Fails LOUDLY on a missing or unparseable manifest rather than assuming
339
+ * defaults: because the manifest is written atomically, "absent" and
340
+ * "present and parseable" are the only two states a crash can leave, so
341
+ * anything else is corruption or a stale/abandoned id — and inventing an
342
+ * `expectedSize` for it would let the coverage check pass over nothing.
343
+ *
344
+ * @inner
345
+ * @param {string} dir - Session directory.
346
+ * @returns {{meta: ?object, error: ?Error}} Exactly one side is set.
347
+ */
348
+ var readManifest = function(dir) {
349
+ var raw;
350
+ try {
351
+ raw = fs.readFileSync(nodePath.join(dir, META_FILE), 'utf8');
352
+ } catch (e) {
353
+ return { meta: null, error: new Error('[storage:' + name + '] no such upload session (or its manifest is gone): ' + nodePath.basename(dir)) };
354
+ }
355
+ var parsed;
356
+ try {
357
+ parsed = JSON.parse(raw);
358
+ } catch (e) {
359
+ return { meta: null, error: new Error('[storage:' + name + '] the manifest for upload session `' + nodePath.basename(dir) + '` is unreadable (' + (e.message || e) + ') — the session cannot be resumed or finalised; abort it') };
360
+ }
361
+ if ( !parsed || typeof parsed !== 'object' || !Number.isInteger(parsed.expectedSize) || typeof parsed.key !== 'string' ) {
362
+ return { meta: null, error: new Error('[storage:' + name + '] the manifest for upload session `' + nodePath.basename(dir) + '` is malformed — the session cannot be resumed or finalised; abort it') };
363
+ }
364
+ return { meta: parsed, error: null };
365
+ };
366
+
367
+ /**
368
+ * Merge byte spans into the minimal offset-ordered set covering the same
369
+ * bytes. ADJACENT spans merge as well as overlapping ones (`8MB-16MB`
370
+ * following `0MB-8MB` is one contiguous range, not two), which is what
371
+ * makes "exactly one span equal to `[0, expectedSize)`" the completeness
372
+ * test.
373
+ *
374
+ * @inner
375
+ * @param {StorageUploadSpan[]} spans - Unordered, possibly overlapping spans.
376
+ * @returns {StorageUploadSpan[]} Merged, offset-ordered.
377
+ */
378
+ var mergeSpans = function(spans) {
379
+ var sorted = spans.slice().sort(function(a, b) { return a.offset - b.offset; });
380
+ var out = [];
381
+ for (var i = 0; i < sorted.length; i++) {
382
+ var s = sorted[i];
383
+ if ( s.length <= 0 ) { continue; }
384
+ var last = out.length ? out[out.length - 1] : null;
385
+ if ( last && s.offset <= last.offset + last.length ) {
386
+ var end = Math.max(last.offset + last.length, s.offset + s.length);
387
+ last.length = end - last.offset;
388
+ } else {
389
+ out.push({ offset: s.offset, length: s.length });
390
+ }
391
+ }
392
+ return out;
393
+ };
394
+
395
+ /**
396
+ * Read a session's durable ranges from its markers.
397
+ *
398
+ * The filesystem IS the manifest: one zero-byte `<offset>-<length>.ok`
399
+ * file per range whose bytes are durable. Entries that do not match the
400
+ * grammar (`data`, `meta.json`, anything an operator dropped in) are
401
+ * ignored.
402
+ *
403
+ * @inner
404
+ * @param {string} dir - Session directory.
405
+ * @returns {StorageUploadSpan[]} Merged, offset-ordered durable ranges.
406
+ */
407
+ var readSpans = function(dir) {
408
+ var entries;
409
+ try {
410
+ entries = fs.readdirSync(dir);
411
+ } catch (e) {
412
+ return [];
413
+ }
414
+ var spans = [];
415
+ for (var i = 0; i < entries.length; i++) {
416
+ var m = MARKER_RE.exec(entries[i]);
417
+ if ( !m ) { continue; }
418
+ spans.push({ offset: parseInt(m[1], 10), length: parseInt(m[2], 10) });
419
+ }
420
+ return mergeSpans(spans);
421
+ };
422
+
423
+ /**
424
+ * The gaps in `[0, expectedSize)` left by a set of merged spans — what a
425
+ * resuming client still has to send.
426
+ *
427
+ * @inner
428
+ * @param {StorageUploadSpan[]} spans - Merged, offset-ordered durable ranges.
429
+ * @param {number} expectedSize - The declared total.
430
+ * @returns {StorageUploadSpan[]} Missing ranges, offset-ordered.
431
+ */
432
+ var missingSpans = function(spans, expectedSize) {
433
+ var out = [];
434
+ var cursor = 0;
435
+ for (var i = 0; i < spans.length; i++) {
436
+ if ( spans[i].offset > cursor ) {
437
+ out.push({ offset: cursor, length: spans[i].offset - cursor });
438
+ }
439
+ cursor = Math.max(cursor, spans[i].offset + spans[i].length);
440
+ }
441
+ if ( cursor < expectedSize ) {
442
+ out.push({ offset: cursor, length: expectedSize - cursor });
443
+ }
444
+ return out;
445
+ };
446
+
447
+ /**
448
+ * Total durable bytes across a merged span set.
449
+ *
450
+ * @inner
451
+ * @param {StorageUploadSpan[]} spans - Merged spans.
452
+ * @returns {number} Byte count.
453
+ */
454
+ var coveredBytes = function(spans) {
455
+ var n = 0;
456
+ for (var i = 0; i < spans.length; i++) { n += spans[i].length; }
457
+ return n;
458
+ };
459
+
460
+ /**
461
+ * Remove a session directory and everything in it. Used by `abortUpload`,
462
+ * by a successful `finalize`, and by the sweep.
463
+ *
464
+ * @inner
465
+ * @param {string} dir - Session directory.
466
+ * @returns {void}
467
+ */
468
+ var dropSession = function(dir) {
469
+ try { fs.rmSync(dir, { recursive: true, force: true }); } catch (e) { /* raced or unreadable */ }
470
+ };
471
+
472
+ /**
473
+ * One reclamation pass: abandoned upload sessions, plus `put()` temp
474
+ * orphans, both age-gated at `sessionTtl`.
475
+ *
476
+ * Sessions get their own sweep rather than riding a shared one because
477
+ * they legitimately live for DAYS — a paused multi-gigabyte upload is not
478
+ * garbage — where a crashed `put()`'s temp is dead the moment its process
479
+ * is. Liveness is `mtime(data)`, which advances on every segment write;
480
+ * the session directory's own mtime only moves when entries are added or
481
+ * removed, so a long segment write would look idle by that measure. A
482
+ * session whose `data` is gone (a `finalize` that renamed the bytes away
483
+ * and then failed to write the row) falls back to the directory mtime, so
484
+ * it is reclaimable too.
485
+ *
486
+ * The temp half is the same age gate applied to `<root>/.tmp`: a crashed
487
+ * `put()` leaves its temp behind and no verb ever looks at it again. Every
488
+ * strategy now clears its own — `cas` and `sharded` at build (#B349 closed
489
+ * the sharded gap), `stream` here at build AND on the interval, because
490
+ * this pass has sessions to walk anyway. Each sweeps only its OWN root.
491
+ *
492
+ * Never throws: an unreadable directory is the next verb's problem to
493
+ * report properly, and a sweep that took the build down would be a worse
494
+ * failure than the leak it prevents.
495
+ *
496
+ * @inner
497
+ * @returns {{sessions: number, temps: number}} What this pass reclaimed.
498
+ */
499
+ var sweepAbandoned = function() {
500
+ var out = { sessions: 0, temps: 0 };
501
+ if ( sessionTtlMs <= 0 ) { return out; }
502
+ var cutoff = Date.now() - sessionTtlMs;
503
+
504
+ try {
505
+ if ( fs.existsSync(uploadsD) ) {
506
+ var sessions = fs.readdirSync(uploadsD);
507
+ for (var i = 0; i < sessions.length; i++) {
508
+ var dir = nodePath.join(uploadsD, sessions[i]);
509
+ try {
510
+ var liveness;
511
+ try {
512
+ liveness = fs.statSync(nodePath.join(dir, DATA_FILE)).mtimeMs;
513
+ } catch (e) {
514
+ liveness = fs.statSync(dir).mtimeMs;
515
+ }
516
+ if ( liveness < cutoff ) {
517
+ fs.rmSync(dir, { recursive: true, force: true });
518
+ out.sessions++;
519
+ }
520
+ } catch (e) { /* raced or unreadable — leave it */ }
521
+ }
522
+ }
523
+ } catch (e) { /* never blocks the build */ }
524
+
525
+ try {
526
+ if ( fs.existsSync(tmpD) ) {
527
+ var temps = fs.readdirSync(tmpD);
528
+ for (var t = 0; t < temps.length; t++) {
529
+ var tp = nodePath.join(tmpD, temps[t]);
530
+ try {
531
+ if ( fs.statSync(tp).mtimeMs < cutoff ) {
532
+ fs.unlinkSync(tp);
533
+ out.temps++;
534
+ }
535
+ } catch (e) { /* raced or unreadable — leave it */ }
536
+ }
537
+ }
538
+ } catch (e) { /* never blocks the build */ }
539
+
540
+ return out;
541
+ };
542
+
543
+ // Build-time pass: a bundle that crashed mid-upload has sessions and temps
544
+ // nothing else will ever remove, and a restart is the earliest moment we
545
+ // can be sure the previous process is gone.
546
+ sweepAbandoned();
547
+
548
+ /**
549
+ * The periodic sweep timer — self-contained and unref'd (the `lib/job`
550
+ * scheduling precedent; `lib/cron` is dormant), so it never holds the
551
+ * process open. Cleared by `close()`. The build-time pass alone would
552
+ * suffice for short-lived temps but not for sessions abandoned by a client
553
+ * that simply went away while the bundle kept running.
554
+ *
555
+ * @inner
556
+ * @type {?object}
557
+ */
558
+ var sweepTimer = null;
559
+ if ( sweepIntervalMs > 0 ) {
560
+ sweepTimer = setInterval(sweepAbandoned, sweepIntervalMs);
561
+ if ( sweepTimer.unref ) { sweepTimer.unref(); }
562
+ }
563
+
564
+ /**
565
+ * What this driver can do — hoisted to a closure var so `stats()` can
566
+ * include it in its report without reaching back into the returned
567
+ * literal. Documented on the `capabilities` property below.
568
+ *
569
+ * @inner
570
+ * @type {{offload: boolean, ranges: boolean, dedup: boolean, resumable: boolean, inline: boolean}}
571
+ */
572
+ var capabilities = {
573
+ offload : false,
574
+ ranges : true,
575
+ dedup : false,
576
+ resumable : true,
577
+ inline : false
578
+ };
579
+
580
+ return {
581
+
582
+ /**
583
+ * Store a readable stream of known-at-the-end size and return its
584
+ * opaque key — the one-shot path, for content whose total the caller
585
+ * does not know up front or does not need to resume.
586
+ *
587
+ * Streams to a temp file inside the driver root and publishes with
588
+ * `rename(2)`, so a reader never observes a partial object and a
589
+ * crashed write leaves nothing visible. The write stream's
590
+ * `highWaterMark` is the configured `chunkSize`, so the syscall size is
591
+ * the tuned one with no hand-rolled buffer and no extra copy — memory
592
+ * stays bounded by the stream machinery rather than by us. With
593
+ * `fsync` on, the temp is flushed BEFORE the rename publishes it and
594
+ * the parent directory best-effort after.
595
+ *
596
+ * No tiering branch: a `stream` driver never inlines. The reported
597
+ * size is measured from the published file on disk, never from the
598
+ * client and never from an in-flight counter (#B142).
599
+ *
600
+ * @param {object} stream - Any readable stream.
601
+ * @param {object} [meta] - Client-supplied, untrusted.
602
+ * @param {string} [meta.originalName] - Filename; stored verbatim in metadata, and
603
+ * the only source of the path extension.
604
+ * @param {string} [meta.contentType] - MIME type; stored verbatim.
605
+ * @param {function} fn - `fn(err, {@link StoragePutResult})`.
606
+ * @returns {void}
607
+ *
608
+ * @example
609
+ * driver.put(req, { originalName: 'talk.mp4' }, function (err, res) {
610
+ * if (err) { return next(err); }
611
+ * record.assetKey = res.key; // opaque — store it, never parse it
612
+ * });
613
+ */
614
+ put: function(stream, meta, fn) {
615
+ if ( typeof meta === 'function' ) { fn = meta; meta = {}; }
616
+ meta = meta || {};
617
+ if ( typeof fn !== 'function' ) {
618
+ throw new Error('[storage:' + name + '] put() requires a callback');
619
+ }
620
+ if ( !stream || typeof stream.pipe !== 'function' ) {
621
+ return fn(new Error('[storage:' + name + '] put() requires a readable stream'));
622
+ }
623
+
624
+ var now = new Date();
625
+ var id = util.ulid(now.getTime());
626
+ var ext = util.sanitiseExtension(meta.originalName);
627
+ var key = 'assets/' + id + '/' + ORIGINAL + ext;
628
+ var final = nodePath.join(root, key);
629
+ var tmp = nodePath.join(tmpD, id + '.tmp');
630
+
631
+ try {
632
+ fs.mkdirSync(nodePath.dirname(final), { recursive: true });
633
+ fs.mkdirSync(tmpD, { recursive: true });
634
+ } catch (mkErr) {
635
+ return fn(mkErr);
636
+ }
637
+
638
+ var settled = false;
639
+ var written = 0;
640
+
641
+ var ws = fs.createWriteStream(tmp, { highWaterMark: chunkSize || undefined });
642
+
643
+ /**
644
+ * Abandon the put: tear both streams down, remove the temp, report.
645
+ *
646
+ * #B358 — the unlink WAITS for the write stream to close, and the
647
+ * callback waits with it. `createWriteStream()` opens
648
+ * asynchronously, so a refusal raised from the 'data' handler
649
+ * (`maxObjectSize`) or an immediately failing source reaches here
650
+ * while the open is still in the threadpool: the temp does not
651
+ * exist YET at this instant but lands a tick later, on the worker
652
+ * thread, with no event-loop turn in between for a synchronous
653
+ * check to see. The previous `existsSync`-gated unlink therefore
654
+ * read false, skipped, and orphaned the temp — measured at ~30% on
655
+ * a 4096-byte refusal. Deferring to 'close' also fixes the contract
656
+ * it broke: a caller told the put failed must not be able to
657
+ * observe residue afterwards. `force` absorbs the open that never
658
+ * happened at all, so the no-temp case costs nothing.
659
+ *
660
+ * @inner
661
+ * @param {*} err - The failure to report; wrapped if not an Error.
662
+ * @returns {void}
663
+ */
664
+ var onError = function(err) {
665
+ if (settled) return;
666
+ settled = true;
667
+ try { stream.destroy(); } catch (_e) {}
668
+
669
+ var finish = function() {
670
+ try { fs.rmSync(tmp, { force: true }); } catch (_e) {}
671
+ fn(( err instanceof Error ) ? err : new Error(String(err)));
672
+ };
673
+
674
+ // Already closed (an autoDestroyed ws that errored) would never
675
+ // emit 'close' again — that arm must not wait for it.
676
+ if ( ws.closed || ws.destroyed ) { return finish(); }
677
+ ws.once('close', finish);
678
+ try { ws.destroy(); } catch (_e) { finish(); }
679
+ };
680
+
681
+ // #B143 — armed at creation, never in a later handler: Node does not
682
+ // replay 'error' for a listener attached after the fact, and an
683
+ // unlistened stream 'error' becomes an uncaughtException that takes
684
+ // the bundle down. Both streams need their own; `.pipe()` returns the
685
+ // DESTINATION, so a single chained listener would cover only one.
686
+ ws.on('error', onError);
687
+ stream.on('error', onError);
688
+
689
+ // 'close' also follows 'error' on an autoDestroyed stream, so the
690
+ // settled latch is what keeps a failed write from publishing.
691
+ ws.on('close', function() {
692
+ if (settled) return;
693
+
694
+ var size;
695
+ try {
696
+ size = fs.statSync(tmp).size;
697
+ if ( fsyncOn ) { fsyncFile(tmp); }
698
+ fs.renameSync(tmp, final);
699
+ if ( fsyncOn ) { fsyncDirBestEffort(nodePath.dirname(final)); }
700
+ } catch (err) {
701
+ return onError(err);
702
+ }
703
+
704
+ settled = true;
705
+ metaStore.set(key, {
706
+ originalName : ( typeof meta.originalName === 'string' ) ? meta.originalName : null,
707
+ contentType : ( typeof meta.contentType === 'string' ) ? meta.contentType : null,
708
+ size : size,
709
+ createdAt : now.getTime()
710
+ }, function(metaErr) {
711
+ if (metaErr) {
712
+ // The bytes are published but unindexed. Roll the object
713
+ // back rather than leave a file `stat()` will deny exists
714
+ // — a caller that got no key can never reference it.
715
+ try { fs.unlinkSync(final); } catch (_e) {}
716
+ return fn(metaErr);
717
+ }
718
+ fn(null, {
719
+ key : key,
720
+ size : size,
721
+ contentType : ( typeof meta.contentType === 'string' ) ? meta.contentType : null
722
+ });
723
+ });
724
+ });
725
+
726
+ stream.on('data', function(chunk) {
727
+ if (settled) return;
728
+ written += chunk.length;
729
+ if ( written > max ) {
730
+ return onError(new Error('[storage:' + name + '] object exceeds maxObjectSize (' + max + ' bytes) — refused'));
731
+ }
732
+ });
733
+
734
+ stream.pipe(ws);
735
+ },
736
+
737
+ /**
738
+ * Open a resumable upload session.
739
+ *
740
+ * **`expectedSize` is REQUIRED**, and that is the deliberate exclusion
741
+ * in this contract: without a declared total, `finalize()` cannot
742
+ * verify that the received ranges actually cover the object, and
743
+ * `statUpload()` cannot say what is MISSING — only what arrived. A
744
+ * caller that does not know the size uses `put()`. (The equivalent
745
+ * wire protocols agree in spirit: tus requires `Upload-Length` unless
746
+ * a server opts into the separate defer-length extension, and S3
747
+ * multipart simply cannot report missing parts because it never learns
748
+ * the total.)
749
+ *
750
+ * The declared size permits an early `maxObjectSize` refusal before
751
+ * any bytes move — **advisory only**: `expectedSize` is
752
+ * client-supplied, so the running byte counter in `writeSegment` stays
753
+ * the real enforcement.
754
+ *
755
+ * **The KEY is minted here**, not at finalise, and persisted in the
756
+ * manifest. Minting it at finalise would mean that a row write failing
757
+ * after the bytes are published leaves them at an address nobody
758
+ * holds — an unreachable orphan plus a session that can never finalise
759
+ * again, which is strictly worse than the row simply being retried.
760
+ *
761
+ * @param {object} meta - Session metadata.
762
+ * @param {number} meta.expectedSize - The object's total size in bytes. Required;
763
+ * a positive integer, at most `maxObjectSize`.
764
+ * @param {string} [meta.originalName] - Filename; stored verbatim, and the only
765
+ * source of the key's extension.
766
+ * @param {string} [meta.contentType] - MIME type; stored verbatim.
767
+ * @param {function} fn - `fn(err, {@link StorageUploadSession})`.
768
+ * @returns {void}
769
+ *
770
+ * @example
771
+ * driver.createUpload({ expectedSize: 4294967296, originalName: 'talk.mp4' },
772
+ * function (err, session) {
773
+ * if (err) { return next(err); }
774
+ * res.setHeader('x-upload-id', session.uploadId);
775
+ * });
776
+ */
777
+ createUpload: function(meta, fn) {
778
+ if ( typeof meta === 'function' ) { fn = meta; meta = {}; }
779
+ meta = meta || {};
780
+ if ( typeof fn !== 'function' ) {
781
+ throw new Error('[storage:' + name + '] createUpload() requires a callback');
782
+ }
783
+ if ( !Number.isInteger(meta.expectedSize) || meta.expectedSize <= 0 ) {
784
+ return fn(new Error('[storage:' + name + '] createUpload() needs `expectedSize`, a positive integer byte count (got: ' + JSON.stringify(meta.expectedSize) + ') — a resumable upload of unknown total size is not supported; use put() for that'));
785
+ }
786
+ if ( meta.expectedSize > max ) {
787
+ return fn(new Error('[storage:' + name + '] expectedSize ' + meta.expectedSize + ' exceeds maxObjectSize (' + max + ' bytes) — refused before any bytes were transferred'));
788
+ }
789
+
790
+ var now = Date.now();
791
+ var id = util.ulid(now);
792
+ var ext = util.sanitiseExtension(meta.originalName);
793
+ // ONE ulid names the session AND the asset it becomes: an
794
+ // operational convenience (the session dir and the asset dir share
795
+ // a name in the same root), never something a caller may rely on —
796
+ // both values stay opaque.
797
+ var manifest = {
798
+ key : 'assets/' + id + '/' + ORIGINAL + ext,
799
+ expectedSize : meta.expectedSize,
800
+ originalName : ( typeof meta.originalName === 'string' ) ? meta.originalName : null,
801
+ contentType : ( typeof meta.contentType === 'string' ) ? meta.contentType : null,
802
+ createdAt : now,
803
+ chunkSize : chunkSize
804
+ };
805
+ var dir = nodePath.join(uploadsD, id);
806
+ var metaTmp = nodePath.join(dir, META_FILE + '.tmp');
807
+
808
+ try {
809
+ fs.mkdirSync(dir, { recursive: true });
810
+ // The assembling file exists from the start, so `mtime(data)`
811
+ // is a valid liveness signal for the sweep from the first
812
+ // moment, and so a positional write can open it 'r+'.
813
+ fs.closeSync(fs.openSync(nodePath.join(dir, DATA_FILE), 'w'));
814
+ // temp+rename: a crash mid-write can only leave the manifest
815
+ // ABSENT, never truncated, which is what makes "missing or
816
+ // unparseable ⇒ fail loudly" a safe rule rather than a trap.
817
+ fs.writeFileSync(metaTmp, JSON.stringify(manifest));
818
+ if ( fsyncOn ) { fsyncFile(metaTmp); }
819
+ fs.renameSync(metaTmp, nodePath.join(dir, META_FILE));
820
+ } catch (err) {
821
+ dropSession(dir);
822
+ return fn(err);
823
+ }
824
+
825
+ fn(null, { uploadId: id, chunkSize: chunkSize, expectedSize: meta.expectedSize });
826
+ },
827
+
828
+ /**
829
+ * Write one segment of a resumable upload at `offset`.
830
+ *
831
+ * Segments may arrive OUT OF ORDER and in parallel — the bytes are
832
+ * written positionally into the session's `data` file, so no assembly
833
+ * pass is needed at finalise (measured: a segment-per-file layout costs
834
+ * ~1.9 s/GB of extra concatenation I/O at finalise; this costs ~4 ms).
835
+ * Re-sending a range that already landed is idempotent, and re-sends
836
+ * may overlap freely.
837
+ *
838
+ * A segment whose source ends early is NOT an error: the marker
839
+ * records the RECEIVED length only, so the coverage union stays exact
840
+ * and the client re-sends the remainder. A segment that would run past
841
+ * `expectedSize` IS an error — that is where a lying `expectedSize` is
842
+ * actually stopped, since the declared value is only advisory.
843
+ *
844
+ * Durability order is load-bearing: the data is fsynced BEFORE the
845
+ * marker is created, so a surviving marker implies durable bytes. The
846
+ * marker's own durability is deliberately not guaranteed — a lost
847
+ * marker beside present bytes costs a re-send, which is the safe
848
+ * direction.
849
+ *
850
+ * @param {string} uploadId - The session id from `createUpload`.
851
+ * @param {number} offset - Byte offset this segment starts at; integer >= 0.
852
+ * @param {object} stream - Any readable stream carrying the segment's bytes.
853
+ * @param {function} fn - `fn(err, {offset, length, received})` — `length` is what
854
+ * THIS call made durable, `received` the session's total
855
+ * covered bytes afterwards.
856
+ * @returns {void}
857
+ *
858
+ * @example
859
+ * driver.writeSegment(uploadId, 8388608, req, function (err, r) {
860
+ * if (err) { return next(err); }
861
+ * res.setHeader('x-received', r.received);
862
+ * });
863
+ */
864
+ writeSegment: function(uploadId, offset, stream, fn) {
865
+ if ( typeof fn !== 'function' ) {
866
+ throw new Error('[storage:' + name + '] writeSegment() requires a callback');
867
+ }
868
+ if ( !stream || typeof stream.pipe !== 'function' ) {
869
+ return fn(new Error('[storage:' + name + '] writeSegment() requires a readable stream'));
870
+ }
871
+ var s = resolveSession(uploadId);
872
+ if ( s.error ) { return fn(s.error); }
873
+ if ( !Number.isInteger(offset) || offset < 0 ) {
874
+ return fn(new Error('[storage:' + name + '] writeSegment(): offset must be an integer >= 0 (got: ' + JSON.stringify(offset) + ')'));
875
+ }
876
+ var man = readManifest(s.dir);
877
+ if ( man.error ) { return fn(man.error); }
878
+ if ( offset >= man.meta.expectedSize ) {
879
+ return fn(new Error('[storage:' + name + '] writeSegment(): offset ' + offset + ' is at or beyond the declared size (' + man.meta.expectedSize + ') for upload `' + uploadId + '`'));
880
+ }
881
+
882
+ var dataPath = nodePath.join(s.dir, DATA_FILE);
883
+ var remaining = man.meta.expectedSize - offset;
884
+ var settled = false;
885
+ var written = 0;
886
+
887
+ // 'r+' + `start` writes IN PLACE at the offset: no truncation, no
888
+ // append, and the holes a not-yet-written range leaves read back
889
+ // as zeros — which is exactly why finalise verifies coverage
890
+ // rather than trusting the file's size.
891
+ var ws = fs.createWriteStream(dataPath, {
892
+ flags : 'r+',
893
+ start : offset,
894
+ highWaterMark : chunkSize || undefined
895
+ });
896
+
897
+ var onError = function(err) {
898
+ if (settled) return;
899
+ settled = true;
900
+ try { stream.destroy(); } catch (_e) {}
901
+ try { ws.destroy(); } catch (_e) {}
902
+ // The session is NOT dropped: partial bytes with no marker are
903
+ // invisible to the coverage check, so the client simply
904
+ // re-sends this range.
905
+ fn(( err instanceof Error ) ? err : new Error(String(err)));
906
+ };
907
+
908
+ // #B143 — armed at creation on both streams.
909
+ ws.on('error', onError);
910
+ stream.on('error', onError);
911
+
912
+ ws.on('close', function() {
913
+ if (settled) return;
914
+ settled = true;
915
+ try {
916
+ if ( written > 0 ) {
917
+ // data first, marker second — the whole durability
918
+ // contract of a resumable upload is in this order.
919
+ if ( fsyncOn ) { fsyncFile(dataPath); }
920
+ fs.closeSync(fs.openSync(nodePath.join(s.dir, offset + '-' + written + '.ok'), 'w'));
921
+ }
922
+ } catch (err) {
923
+ return fn(err);
924
+ }
925
+ fn(null, {
926
+ offset : offset,
927
+ length : written,
928
+ received : coveredBytes(readSpans(s.dir))
929
+ });
930
+ });
931
+
932
+ stream.on('data', function(chunk) {
933
+ if (settled) return;
934
+ written += chunk.length;
935
+ if ( written > remaining ) {
936
+ return onError(new Error('[storage:' + name + '] writeSegment(): the segment at offset ' + offset + ' runs past the declared size (' + man.meta.expectedSize + ' bytes) for upload `' + uploadId + '` — refused'));
937
+ }
938
+ });
939
+
940
+ stream.pipe(ws);
941
+ },
942
+
943
+ /**
944
+ * The state of a resumable upload — what landed, what is missing, and
945
+ * whether `finalize()` would succeed.
946
+ *
947
+ * The resumable twin of `stat()`, and not optional: resuming is
948
+ * impossible unless a client can learn what already arrived. Read
949
+ * straight from the session's markers, so it is accurate across a
950
+ * process restart and needs no in-memory session table.
951
+ *
952
+ * @param {string} uploadId - The session id from `createUpload`.
953
+ * @param {function} fn - `fn(err, {@link StorageUploadState})`.
954
+ * @returns {void}
955
+ *
956
+ * @example
957
+ * driver.statUpload(uploadId, function (err, st) {
958
+ * if (err) { return self.throwError(404); }
959
+ * if (st.complete) { return driver.finalize(uploadId, done); }
960
+ * res.json({ missing: st.missing }); // tell the client what to re-send
961
+ * });
962
+ */
963
+ statUpload: function(uploadId, fn) {
964
+ if ( typeof fn !== 'function' ) {
965
+ throw new Error('[storage:' + name + '] statUpload() requires a callback');
966
+ }
967
+ var s = resolveSession(uploadId);
968
+ if ( s.error ) { return fn(s.error); }
969
+ var man = readManifest(s.dir);
970
+ if ( man.error ) { return fn(man.error); }
971
+
972
+ var spans = readSpans(s.dir);
973
+ var missing = missingSpans(spans, man.meta.expectedSize);
974
+ fn(null, {
975
+ expectedSize : man.meta.expectedSize,
976
+ contentType : man.meta.contentType,
977
+ originalName : man.meta.originalName,
978
+ createdAt : man.meta.createdAt,
979
+ received : spans,
980
+ missing : missing,
981
+ complete : ( missing.length === 0 )
982
+ });
983
+ },
984
+
985
+ /**
986
+ * Publish a completed resumable upload and return its opaque key.
987
+ *
988
+ * **Coverage is verified as an INTERVAL UNION** — the markers are
989
+ * merged and must form exactly one span equal to `[0, expectedSize)`.
990
+ * Summing their lengths would not do: markers `0–8MB` and `4–12MB` sum
991
+ * to 16MB with `12–16MB` uncovered, and because a hole reads back as
992
+ * ZEROS a sum-based check would publish a plausible, silently
993
+ * zero-filled object. A gap is a REAL error and the session is
994
+ * PRESERVED so the client can complete it — never a silent publish.
995
+ *
996
+ * **Bytes are published BEFORE the metadata row.** The inverse
997
+ * ordering would manufacture rows without files, which the `cas`
998
+ * strategy's own verify classifies as loss evidence and never
999
+ * auto-fixes. This ordering's residue is the benign kind: an object
1000
+ * whose row write failed is still readable by key (`get()` falls
1001
+ * through to the file), merely missing `originalName`/`contentType`.
1002
+ *
1003
+ * **Idempotent, with a heal path.** If the final path already exists
1004
+ * this call skips assembly, (re)writes the row and cleans the session
1005
+ * — so a `finalize` that published the bytes and then failed on the
1006
+ * row is completed by simply retrying it.
1007
+ *
1008
+ * `writeSegment` must not run concurrently with `finalize` on the same
1009
+ * session — that is a CLIENT contract, single-actor-per-finalise. The
1010
+ * pre-rename size check below is its cheap detection: bytes past the
1011
+ * declared size are truncated away, and bytes MISSING under a full set
1012
+ * of markers refuse the publish outright.
1013
+ *
1014
+ * @param {string} uploadId - The session id from `createUpload`.
1015
+ * @param {function} fn - `fn(err, {key, size, contentType})` — the same shape
1016
+ * `put()` returns.
1017
+ * @returns {void}
1018
+ *
1019
+ * @example
1020
+ * driver.finalize(uploadId, function (err, res) {
1021
+ * if (err) { return next(err); } // a coverage gap keeps the session alive
1022
+ * record.assetKey = res.key;
1023
+ * });
1024
+ */
1025
+ finalize: function(uploadId, fn) {
1026
+ if ( typeof fn !== 'function' ) {
1027
+ throw new Error('[storage:' + name + '] finalize() requires a callback');
1028
+ }
1029
+ var s = resolveSession(uploadId);
1030
+ if ( s.error ) { return fn(s.error); }
1031
+ var man = readManifest(s.dir);
1032
+ if ( man.error ) { return fn(man.error); }
1033
+
1034
+ var meta = man.meta;
1035
+ var key = meta.key;
1036
+ var r = resolvePath(key);
1037
+ if ( r.error ) { return fn(r.error); }
1038
+ var final = r.path;
1039
+ var dataPath = nodePath.join(s.dir, DATA_FILE);
1040
+
1041
+ /**
1042
+ * Write the metadata row and, on success, drop the session. On
1043
+ * failure the session is KEPT: the bytes are already published, so
1044
+ * a retry takes the heal path above and completes the row.
1045
+ *
1046
+ * @inner
1047
+ * @param {number} size - The published object's size in bytes.
1048
+ * @returns {void}
1049
+ */
1050
+ var indexAndFinish = function(size) {
1051
+ metaStore.set(key, {
1052
+ originalName : meta.originalName,
1053
+ contentType : meta.contentType,
1054
+ size : size,
1055
+ createdAt : ( typeof meta.createdAt === 'number' ) ? meta.createdAt : Date.now()
1056
+ }, function(metaErr) {
1057
+ if (metaErr) { return fn(metaErr); }
1058
+ dropSession(s.dir);
1059
+ fn(null, { key: key, size: size, contentType: meta.contentType });
1060
+ });
1061
+ };
1062
+
1063
+ // Heal path: the bytes are already where they belong, so this is a
1064
+ // retry of a finalise whose row write failed (or a duplicate call).
1065
+ if ( fs.existsSync(final) ) {
1066
+ var healSize;
1067
+ try {
1068
+ healSize = fs.statSync(final).size;
1069
+ } catch (err) {
1070
+ return fn(err);
1071
+ }
1072
+ return indexAndFinish(healSize);
1073
+ }
1074
+
1075
+ var spans = readSpans(s.dir);
1076
+ var missing = missingSpans(spans, meta.expectedSize);
1077
+ if ( missing.length ) {
1078
+ return fn(new Error('[storage:' + name + '] upload `' + uploadId + '` is incomplete — '
1079
+ + missing.length + ' range(s) missing, ' + coveredBytes(missing) + ' of '
1080
+ + meta.expectedSize + ' bytes (first gap at offset ' + missing[0].offset + ', '
1081
+ + missing[0].length + ' bytes). The session is preserved; send the missing ranges and finalise again.'));
1082
+ }
1083
+
1084
+ var size;
1085
+ try {
1086
+ size = fs.statSync(dataPath).size;
1087
+ if ( size < meta.expectedSize ) {
1088
+ // Markers claim coverage the bytes do not back — the one
1089
+ // shape that would publish zeros with a full marker set.
1090
+ // Refuse and preserve; nothing here can safely repair it.
1091
+ return fn(new Error('[storage:' + name + '] upload `' + uploadId + '` claims full coverage but its assembling file is only '
1092
+ + size + ' of ' + meta.expectedSize + ' bytes — refusing to publish a short object. The session is preserved.'));
1093
+ }
1094
+ if ( size > meta.expectedSize ) {
1095
+ // Unreachable through this API (every segment is capped),
1096
+ // so this is external tampering or a client-contract
1097
+ // violation. Coverage proves [0, expectedSize) is durable;
1098
+ // drop whatever is past the declared end.
1099
+ fs.truncateSync(dataPath, meta.expectedSize);
1100
+ size = meta.expectedSize;
1101
+ }
1102
+ if ( fsyncOn ) { fsyncFile(dataPath); }
1103
+ fs.mkdirSync(nodePath.dirname(final), { recursive: true });
1104
+ fs.renameSync(dataPath, final);
1105
+ if ( fsyncOn ) { fsyncDirBestEffort(nodePath.dirname(final)); }
1106
+ } catch (err) {
1107
+ return fn(err);
1108
+ }
1109
+
1110
+ indexAndFinish(size);
1111
+ },
1112
+
1113
+ /**
1114
+ * Discard a resumable upload and everything it accumulated.
1115
+ *
1116
+ * Idempotent: aborting an unknown or already-aborted session answers
1117
+ * `{aborted: false}` rather than erroring — the caller asked for the
1118
+ * session to be gone, and it is. An invalid id is still refused, since
1119
+ * that is a caller bug rather than a state.
1120
+ *
1121
+ * @param {string} uploadId - The session id from `createUpload`.
1122
+ * @param {function} [fn] - `fn(err, {aborted})`.
1123
+ * @returns {void}
1124
+ *
1125
+ * @example
1126
+ * driver.abortUpload(uploadId, function (err, r) {
1127
+ * // r.aborted === false when it was already gone
1128
+ * });
1129
+ */
1130
+ abortUpload: function(uploadId, fn) {
1131
+ if ( typeof fn !== 'function' ) { fn = function() {}; }
1132
+ var s = resolveSession(uploadId);
1133
+ if ( s.error ) { return fn(s.error); }
1134
+ var existed = fs.existsSync(s.dir);
1135
+ dropSession(s.dir);
1136
+ fn(null, { aborted: existed });
1137
+ },
1138
+
1139
+ /**
1140
+ * Open a readable stream for `key`.
1141
+ *
1142
+ * Errors (rather than yielding `null`) on an unknown key: a caller
1143
+ * asking for bytes has no use for a null stream, and the existence
1144
+ * question belongs to `stat()`.
1145
+ *
1146
+ * @param {string} key - The opaque storage key.
1147
+ * @param {function} fn - `fn(err, stream)`.
1148
+ * @returns {void}
1149
+ *
1150
+ * @example
1151
+ * driver.get(key, function (err, stream) {
1152
+ * if (err) { return self.throwError(404); }
1153
+ * stream.pipe(res);
1154
+ * });
1155
+ */
1156
+ get: function(key, fn) {
1157
+ if ( typeof fn !== 'function' ) {
1158
+ throw new Error('[storage:' + name + '] get() requires a callback');
1159
+ }
1160
+ var r = resolvePath(key);
1161
+ if ( r.error ) { return fn(r.error); }
1162
+ if ( !fs.existsSync(r.path) ) {
1163
+ return fn(util.codedError('STORAGE_NO_OBJECT', '[storage:' + name + '] no object for key `' + key + '`'));
1164
+ }
1165
+ var rs = fs.createReadStream(r.path, { highWaterMark: chunkSize || undefined });
1166
+ // Armed before the caller can touch it (#B143) — an fs error after
1167
+ // handoff is the caller's, but one raised during open is ours.
1168
+ var handed = false;
1169
+ rs.on('error', function(err) {
1170
+ if (handed) return;
1171
+ handed = true;
1172
+ fn(err);
1173
+ });
1174
+ rs.on('open', function() {
1175
+ if (handed) return;
1176
+ handed = true;
1177
+ fn(null, rs);
1178
+ });
1179
+ },
1180
+
1181
+ /**
1182
+ * Byte-range read — the driver half of HTTP Range serving, and the
1183
+ * verb this strategy exists to feed.
1184
+ *
1185
+ * `end` is INCLUSIVE, matching both the HTTP `Range` header and
1186
+ * `fs.createReadStream({start, end})`, so no arithmetic happens at the
1187
+ * boundary between them. An `end` past the last byte is CLAMPED rather
1188
+ * than refused (RFC 9110: a range whose start is satisfiable is
1189
+ * satisfiable); only a `start` at or beyond the object's size is
1190
+ * unsatisfiable, and that is the caller's 416.
1191
+ *
1192
+ * The framework consumes this verb through the controller's
1193
+ * `serveFromStorage()` facade, which answers 206/416 with
1194
+ * `Accept-Ranges`/`Content-Range` on both engines, gated on
1195
+ * `capabilities.ranges`.
1196
+ *
1197
+ * @param {string} key - The opaque storage key.
1198
+ * @param {number} start - First byte offset, inclusive; integer >= 0.
1199
+ * @param {number} end - Last byte offset, INCLUSIVE; integer >= start.
1200
+ * @param {function} fn - `fn(err, stream)`.
1201
+ * @returns {void}
1202
+ *
1203
+ * @example
1204
+ * // Serving `Range: bytes=0-1023`
1205
+ * driver.getRange(key, 0, 1023, function (err, stream) {
1206
+ * if (err) { return self.throwError(416); }
1207
+ * stream.pipe(res);
1208
+ * });
1209
+ */
1210
+ getRange: function(key, start, end, fn) {
1211
+ if ( typeof fn !== 'function' ) {
1212
+ throw new Error('[storage:' + name + '] getRange() requires a callback');
1213
+ }
1214
+ if ( !Number.isInteger(start) || !Number.isInteger(end) || start < 0 || end < start ) {
1215
+ return fn(util.codedError('STORAGE_INVALID_RANGE', '[storage:' + name + '] getRange(): invalid range [' + start + ', ' + end + '] — both bounds must be integers with 0 <= start <= end'));
1216
+ }
1217
+ var r = resolvePath(key);
1218
+ if ( r.error ) { return fn(r.error); }
1219
+ if ( !fs.existsSync(r.path) ) {
1220
+ return fn(util.codedError('STORAGE_NO_OBJECT', '[storage:' + name + '] no object for key `' + key + '`'));
1221
+ }
1222
+ var size = fs.statSync(r.path).size;
1223
+ if ( start >= size ) {
1224
+ return fn(util.codedError('STORAGE_RANGE_UNSATISFIABLE', '[storage:' + name + '] getRange(): start ' + start + ' is beyond the object size (' + size + ') for key `' + key + '`'));
1225
+ }
1226
+ var rs = fs.createReadStream(r.path, { start: start, end: Math.min(end, size - 1) });
1227
+ var handed = false;
1228
+ rs.on('error', function(err) {
1229
+ if (handed) return;
1230
+ handed = true;
1231
+ fn(err);
1232
+ });
1233
+ rs.on('open', function() {
1234
+ if (handed) return;
1235
+ handed = true;
1236
+ fn(null, rs);
1237
+ });
1238
+ },
1239
+
1240
+ /**
1241
+ * Metadata for `key`.
1242
+ *
1243
+ * @param {string} key - The opaque storage key.
1244
+ * @param {function} fn - `fn(err, meta|null)`; `null` when the key is unknown.
1245
+ * @returns {void}
1246
+ *
1247
+ * @example
1248
+ * driver.stat(key, function (err, meta) {
1249
+ * if (!meta) { return self.throwError(404); }
1250
+ * res.setHeader('Content-Type', meta.contentType);
1251
+ * });
1252
+ */
1253
+ stat: function(key, fn) {
1254
+ if ( typeof fn !== 'function' ) {
1255
+ throw new Error('[storage:' + name + '] stat() requires a callback');
1256
+ }
1257
+ var r = resolvePath(key);
1258
+ if ( r.error ) { return fn(r.error); }
1259
+ metaStore.get(key, function(err, m) {
1260
+ if (err) { return fn(err); }
1261
+ if (!m) { return fn(null, null); }
1262
+ fn(null, {
1263
+ originalName : m.originalName,
1264
+ contentType : m.contentType,
1265
+ size : m.size,
1266
+ createdAt : m.createdAt
1267
+ });
1268
+ });
1269
+ },
1270
+
1271
+ /**
1272
+ * Delete the object and its metadata row.
1273
+ *
1274
+ * The asset's own directory is removed too when nothing else is left
1275
+ * in it — which is the operational half of the per-asset layout, and
1276
+ * why a future rendition sitting beside `original` correctly keeps the
1277
+ * directory alive.
1278
+ *
1279
+ * @param {string} key - The opaque storage key.
1280
+ * @param {function} [fn] - `fn(err, existed)`.
1281
+ * @returns {void}
1282
+ */
1283
+ release: function(key, fn) {
1284
+ if ( typeof fn !== 'function' ) { fn = function() {}; }
1285
+ var r = resolvePath(key);
1286
+ if ( r.error ) { return fn(r.error); }
1287
+
1288
+ var existed = false;
1289
+ try {
1290
+ if ( fs.existsSync(r.path) ) {
1291
+ fs.unlinkSync(r.path);
1292
+ existed = true;
1293
+ }
1294
+ } catch (err) {
1295
+ // ENOENT means a concurrent release won the race, which is the
1296
+ // outcome the caller asked for; anything else is a real failure.
1297
+ if ( err.code !== 'ENOENT' ) { return fn(err); }
1298
+ }
1299
+ // Only succeeds while the asset directory is empty — a rendition
1300
+ // beside `original` keeps it, which is the point of the layout.
1301
+ try { fs.rmdirSync(nodePath.dirname(r.path)); } catch (e) { /* not empty, or gone */ }
1302
+
1303
+ metaStore.remove(key, function(metaErr, metaExisted) {
1304
+ if (metaErr) { return fn(metaErr); }
1305
+ fn(null, existed || !!metaExisted);
1306
+ });
1307
+ },
1308
+
1309
+ /**
1310
+ * How to serve `key` — always `{kind:'path'}` here: a `stream` driver
1311
+ * never inlines, and `'url'` arrives with the s3 adapter. Callers must
1312
+ * still branch on `kind` rather than assume a path is available,
1313
+ * because the driver behind a key is a configuration choice.
1314
+ *
1315
+ * @param {string} key - The opaque storage key.
1316
+ * @param {function} fn - `fn(err, {kind: 'path', path: string})`.
1317
+ * @returns {void}
1318
+ *
1319
+ * @example
1320
+ * driver.resolve(key, function (err, r) {
1321
+ * if (err) { return next(err); }
1322
+ * if (r.kind === 'path') { return res.sendFile(r.path); }
1323
+ * });
1324
+ */
1325
+ resolve: function(key, opts, fn) {
1326
+ // optional middle `opts` — offload response overrides, consumed
1327
+ // by offload-capable adapters (`s3`); accepted and IGNORED here
1328
+ // so one facade call site serves every driver.
1329
+ if ( typeof opts === 'function' ) { fn = opts; }
1330
+ if ( typeof fn !== 'function' ) {
1331
+ throw new Error('[storage:' + name + '] resolve() requires a callback');
1332
+ }
1333
+ var r = resolvePath(key);
1334
+ if ( r.error ) { return fn(r.error); }
1335
+ if ( !fs.existsSync(r.path) ) {
1336
+ return fn(util.codedError('STORAGE_NO_OBJECT', '[storage:' + name + '] no object for key `' + key + '`'));
1337
+ }
1338
+ fn(null, { kind: 'path', path: r.path });
1339
+ },
1340
+
1341
+ /**
1342
+ * Driver-level statistics for the operator surface (`storage:stats`
1343
+ * and `GET /_gina/storage/stats`): the driver's identity — strategy,
1344
+ * root, capabilities — plus the metadata store's aggregate counts.
1345
+ *
1346
+ * The store half is OPTIONAL seam surface: a store lacking `stats()`
1347
+ * reports `store: null` ("store reports no stats") rather than
1348
+ * erroring, so a connector-backed driver degrades instead of failing
1349
+ * the whole stats sweep. In-flight upload sessions are NOT counted —
1350
+ * they hold no rows, and reporting them is deferred with the rest of
1351
+ * the stream maintenance surface.
1352
+ *
1353
+ * @param {function} fn - `fn(err, {name, strategy, root, capabilities, store})` —
1354
+ * `store` is `{objects, refcounted, zeroRefPending, inline,
1355
+ * bytes}`, or `null` when the store reports no stats.
1356
+ * @returns {void}
1357
+ *
1358
+ * @example
1359
+ * driver.stats(function (err, s) {
1360
+ * if (err) { return next(err); }
1361
+ * // s.store.objects, s.store.bytes …
1362
+ * });
1363
+ */
1364
+ stats: function(fn) {
1365
+ if ( typeof fn !== 'function' ) {
1366
+ throw new Error('[storage:' + name + '] stats() requires a callback');
1367
+ }
1368
+ var view = {
1369
+ name : name,
1370
+ strategy : conf.strategy,
1371
+ root : root,
1372
+ capabilities : capabilities,
1373
+ store : null
1374
+ };
1375
+ if ( typeof metaStore.stats !== 'function' ) {
1376
+ return fn(null, view);
1377
+ }
1378
+ metaStore.stats(function(err, s) {
1379
+ if (err) { return fn(err); }
1380
+ view.store = s;
1381
+ fn(null, view);
1382
+ });
1383
+ },
1384
+
1385
+ /**
1386
+ * What this driver can do. `resumable` is the stream flag — it is what
1387
+ * gates the five session verbs. `inline` and `dedup` are `false` BY
1388
+ * DESIGN here, not pending: a multi-gigabyte strategy does not inline,
1389
+ * and the write path deliberately does not hash.
1390
+ *
1391
+ * `offload` is `false` and measured, not assumed: no X-Accel /
1392
+ * X-Sendfile handling exists anywhere in either engine today, so a
1393
+ * caller must stream bytes itself.
1394
+ *
1395
+ * @type {{offload: boolean, ranges: boolean, dedup: boolean, resumable: boolean, inline: boolean}}
1396
+ */
1397
+ capabilities: capabilities,
1398
+
1399
+ /**
1400
+ * Stop the session sweep timer and release the metadata handle.
1401
+ * Teardown and tests only — the runtime never calls it.
1402
+ *
1403
+ * @returns {void}
1404
+ */
1405
+ close: function() {
1406
+ if ( sweepTimer ) {
1407
+ clearInterval(sweepTimer);
1408
+ sweepTimer = null;
1409
+ }
1410
+ try { metaStore.close(); } catch (e) { /* already closed */ }
1411
+ },
1412
+
1413
+ // test seam — the sweep has no operator door in this slice (the
1414
+ // `storage:gc` family stays cas-only), so this is what drives it
1415
+ // deterministically instead of waiting on the interval.
1416
+ _sweepAbandoned: sweepAbandoned
1417
+ };
1418
+ };