lumen-framework 3.1.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. package/README.md +75 -109
  2. package/bin/lumen +23 -41
  3. package/dist/cli.cjs +856 -479
  4. package/dist/cli.cjs.map +4 -4
  5. package/dist/index.js +3273 -1719
  6. package/dist/index.js.map +4 -4
  7. package/dist/index.mjs +3251 -1697
  8. package/dist/index.mjs.map +4 -4
  9. package/dist/testing.js +280 -0
  10. package/dist/testing.js.map +7 -0
  11. package/dist/testing.mjs +252 -0
  12. package/dist/testing.mjs.map +7 -0
  13. package/dist/types/errors/links-only-error.d.ts +13 -0
  14. package/dist/types/errors/reserved-field-name-error.d.ts +13 -0
  15. package/dist/types/errors/unknown-attribute-error.d.ts +13 -0
  16. package/dist/types/index.d.ts +14 -0
  17. package/dist/types/interfaces.d.ts +1 -1
  18. package/dist/types/packages/application/index.d.ts +38 -45
  19. package/dist/types/packages/application/initialize.d.ts +3 -5
  20. package/dist/types/packages/application/interfaces.d.ts +13 -5
  21. package/dist/types/packages/application/utils/create-controller.d.ts +14 -4
  22. package/dist/types/packages/application/utils/create-serializer.d.ts +2 -2
  23. package/dist/types/packages/application/utils/normalize-port.d.ts +1 -3
  24. package/dist/types/packages/application/utils/resolve-visibility.d.ts +15 -0
  25. package/dist/types/packages/application/utils/restrict-open-namespaces.d.ts +19 -0
  26. package/dist/types/packages/application/utils/validate-attributes.d.ts +17 -0
  27. package/dist/types/packages/application/utils/validate-links-only.d.ts +19 -0
  28. package/dist/types/packages/application/utils/validate-namespaced-serializers.d.ts +3 -3
  29. package/dist/types/packages/application/utils/validate-reserved-names.d.ts +12 -0
  30. package/dist/types/packages/application/utils/warn-query-param-names.d.ts +10 -0
  31. package/dist/types/packages/cli/commands/dbcreate.d.ts +1 -1
  32. package/dist/types/packages/cli/commands/dbdrop.d.ts +1 -1
  33. package/dist/types/packages/cli/commands/destroy.d.ts +3 -2
  34. package/dist/types/packages/cli/commands/generate.d.ts +5 -5
  35. package/dist/types/packages/cli/commands/index.d.ts +0 -1
  36. package/dist/types/packages/cli/generator/index.d.ts +6 -6
  37. package/dist/types/packages/cli/generator/interfaces.d.ts +3 -3
  38. package/dist/types/packages/cli/generator/utils/create-generator.d.ts +2 -2
  39. package/dist/types/packages/cli/generator/utils/generate-type.d.ts +9 -9
  40. package/dist/types/packages/cli/generator/utils/migration-conflict.d.ts +4 -4
  41. package/dist/types/packages/cli/templates/pnpm-workspace.d.ts +15 -0
  42. package/dist/types/packages/cli/utils/create-spinner.d.ts +14 -0
  43. package/dist/types/packages/cli/utils/print-statements.d.ts +12 -0
  44. package/dist/types/packages/cli/utils/server-database.d.ts +27 -0
  45. package/dist/types/packages/compiler/interfaces.d.ts +1 -1
  46. package/dist/types/packages/config/interfaces.d.ts +10 -4
  47. package/dist/types/packages/controller/constants.d.ts +7 -2
  48. package/dist/types/packages/controller/errors/related-record-not-found-error.d.ts +4 -4
  49. package/dist/types/packages/controller/index.d.ts +364 -473
  50. package/dist/types/packages/controller/interfaces.d.ts +21 -6
  51. package/dist/types/packages/controller/utils/find-many.d.ts +2 -5
  52. package/dist/types/packages/controller/utils/find-one.d.ts +2 -5
  53. package/dist/types/packages/controller/utils/params-to-query.d.ts +8 -10
  54. package/dist/types/packages/controller/utils/resolve-relationships.d.ts +1 -3
  55. package/dist/types/packages/controller/utils/validate-relationships.d.ts +5 -3
  56. package/dist/types/packages/controller/visibility/errors.d.ts +21 -0
  57. package/dist/types/packages/controller/visibility/index.d.ts +51 -0
  58. package/dist/types/packages/database/attribute/index.d.ts +4 -6
  59. package/dist/types/packages/database/attribute/interfaces.d.ts +1 -1
  60. package/dist/types/packages/database/attribute/utils/create-attribute.d.ts +3 -5
  61. package/dist/types/packages/database/attribute/utils/create-getter.d.ts +2 -2
  62. package/dist/types/packages/database/attribute/utils/create-setter.d.ts +3 -5
  63. package/dist/types/packages/database/constants.d.ts +1 -0
  64. package/dist/types/packages/database/errors/index.d.ts +1 -0
  65. package/dist/types/packages/database/errors/invalid-driver-error.d.ts +1 -3
  66. package/dist/types/packages/database/errors/migrations-pending-error.d.ts +1 -3
  67. package/dist/types/packages/database/errors/model-missing-error.d.ts +1 -3
  68. package/dist/types/packages/database/errors/relationship-config-error.d.ts +10 -0
  69. package/dist/types/packages/database/errors/unique-constraint-error.d.ts +1 -1
  70. package/dist/types/packages/database/index.d.ts +9 -6
  71. package/dist/types/packages/database/initialize.d.ts +3 -5
  72. package/dist/types/packages/database/interfaces.d.ts +115 -25
  73. package/dist/types/packages/database/migration/index.d.ts +5 -7
  74. package/dist/types/packages/database/migration/interfaces.d.ts +2 -4
  75. package/dist/types/packages/database/migration/utils/generate-timestamp.d.ts +9 -1
  76. package/dist/types/packages/database/model/index.d.ts +348 -759
  77. package/dist/types/packages/database/model/initialize-class.d.ts +7 -1
  78. package/dist/types/packages/database/model/interfaces.d.ts +30 -12
  79. package/dist/types/packages/database/model/utils/attribute.d.ts +14 -0
  80. package/dist/types/packages/database/model/utils/get-columns.d.ts +1 -3
  81. package/dist/types/packages/database/model/utils/persistence.d.ts +5 -14
  82. package/dist/types/packages/database/model/utils/process-write-error.d.ts +2 -2
  83. package/dist/types/packages/database/model/utils/run-hooks.d.ts +7 -3
  84. package/dist/types/packages/database/model/utils/validate.d.ts +4 -1
  85. package/dist/types/packages/database/query/errors/record-not-found-error.d.ts +1 -1
  86. package/dist/types/packages/database/query/index.d.ts +179 -3
  87. package/dist/types/packages/database/query/runner/index.d.ts +1 -3
  88. package/dist/types/packages/database/query/runner/utils/build-results.d.ts +3 -4
  89. package/dist/types/packages/database/query/utils/format-select.d.ts +1 -3
  90. package/dist/types/packages/database/relationship/index.d.ts +7 -6
  91. package/dist/types/packages/database/relationship/interfaces.d.ts +16 -3
  92. package/dist/types/packages/database/relationship/utils/getters.d.ts +7 -13
  93. package/dist/types/packages/database/relationship/utils/inverse-setters.d.ts +5 -9
  94. package/dist/types/packages/database/relationship/utils/setters.d.ts +7 -13
  95. package/dist/types/packages/database/relationship/utils/unassociate.d.ts +1 -3
  96. package/dist/types/packages/database/relationship/utils/update-relationship.d.ts +6 -2
  97. package/dist/types/packages/database/transaction/index.d.ts +9 -10
  98. package/dist/types/packages/database/transaction/interfaces.d.ts +7 -1
  99. package/dist/types/packages/database/utils/connect.d.ts +16 -3
  100. package/dist/types/packages/database/utils/create-migrations.d.ts +1 -3
  101. package/dist/types/packages/database/utils/normalize-model-name.d.ts +1 -3
  102. package/dist/types/packages/database/utils/pending-migrations.d.ts +1 -3
  103. package/dist/types/packages/database/utils/primary-key-type.d.ts +10 -0
  104. package/dist/types/packages/database/utils/type-for-column.d.ts +3 -5
  105. package/dist/types/packages/database/utils/validate-relationships.d.ts +13 -0
  106. package/dist/types/packages/database/validation/errors/validation-error.d.ts +4 -4
  107. package/dist/types/packages/database/validation/index.d.ts +3 -5
  108. package/dist/types/packages/database/validation/interfaces.d.ts +1 -1
  109. package/dist/types/packages/freezeable/map/index.d.ts +1 -3
  110. package/dist/types/packages/freezeable/set/index.d.ts +1 -3
  111. package/dist/types/packages/freezeable/utils/freeze.d.ts +5 -15
  112. package/dist/types/packages/freezeable/utils/is-frozen.d.ts +1 -3
  113. package/dist/types/packages/fs/index.d.ts +7 -7
  114. package/dist/types/packages/fs/interfaces.d.ts +4 -4
  115. package/dist/types/packages/fs/utils/parse-path.d.ts +2 -2
  116. package/dist/types/packages/fs/watcher/interfaces.d.ts +1 -1
  117. package/dist/types/packages/jsonapi/errors/invalid-content-type-error.d.ts +3 -5
  118. package/dist/types/packages/jsonapi/errors/not-acceptable-error.d.ts +2 -4
  119. package/dist/types/packages/jsonapi/errors/unsupported-media-type-error.d.ts +2 -4
  120. package/dist/types/packages/jsonapi/index.d.ts +1 -1
  121. package/dist/types/packages/jsonapi/interfaces.d.ts +47 -36
  122. package/dist/types/packages/jsonapi/utils/has-media-type-params.d.ts +1 -1
  123. package/dist/types/packages/jsonapi/utils/is-jsonapi.d.ts +1 -1
  124. package/dist/types/packages/jsonapi/utils/media-type.d.ts +2 -2
  125. package/dist/types/packages/loader/builder/index.d.ts +3 -3
  126. package/dist/types/packages/loader/builder/interfaces.d.ts +7 -7
  127. package/dist/types/packages/loader/builder/utils/create-children-builder.d.ts +2 -2
  128. package/dist/types/packages/loader/builder/utils/create-parent-builder.d.ts +9 -2
  129. package/dist/types/packages/loader/builder/utils/sort-by-namespace.d.ts +2 -2
  130. package/dist/types/packages/loader/index.d.ts +1 -1
  131. package/dist/types/packages/loader/interfaces.d.ts +2 -2
  132. package/dist/types/packages/loader/resolver/index.d.ts +2 -2
  133. package/dist/types/packages/loader/resolver/utils/closest-ancestor.d.ts +2 -2
  134. package/dist/types/packages/loader/resolver/utils/closest-child.d.ts +2 -2
  135. package/dist/types/packages/logger/constants.d.ts +3 -3
  136. package/dist/types/packages/logger/errors/invalid-config-error.d.ts +5 -0
  137. package/dist/types/packages/logger/index.d.ts +65 -142
  138. package/dist/types/packages/logger/interfaces.d.ts +52 -10
  139. package/dist/types/packages/logger/request-logger/index.d.ts +3 -5
  140. package/dist/types/packages/logger/request-logger/interfaces.d.ts +5 -5
  141. package/dist/types/packages/logger/request-logger/templates.d.ts +5 -9
  142. package/dist/types/packages/logger/request-logger/utils/filter-params.d.ts +6 -1
  143. package/dist/types/packages/logger/request-logger/utils/log-json.d.ts +2 -4
  144. package/dist/types/packages/logger/request-logger/utils/log-text.d.ts +1 -3
  145. package/dist/types/packages/logger/request-logger/utils/params-for.d.ts +9 -0
  146. package/dist/types/packages/logger/utils/error-name.d.ts +7 -0
  147. package/dist/types/packages/logger/utils/line.d.ts +1 -3
  148. package/dist/types/packages/logger/writer/constants.d.ts +0 -1
  149. package/dist/types/packages/logger/writer/index.d.ts +6 -6
  150. package/dist/types/packages/logger/writer/interfaces.d.ts +2 -2
  151. package/dist/types/packages/logger/writer/utils/format-message.d.ts +2 -2
  152. package/dist/types/packages/lumenify/index.d.ts +20 -5
  153. package/dist/types/packages/lumenify/utils/create-response-proxy.d.ts +1 -1
  154. package/dist/types/packages/pm/cluster/index.d.ts +56 -7
  155. package/dist/types/packages/pm/cluster/interfaces.d.ts +2 -1
  156. package/dist/types/packages/pm/index.d.ts +2 -2
  157. package/dist/types/packages/router/definitions/context/index.d.ts +5 -7
  158. package/dist/types/packages/router/definitions/context/utils/create-definition-group.d.ts +3 -5
  159. package/dist/types/packages/router/definitions/context/utils/create-definition.d.ts +11 -6
  160. package/dist/types/packages/router/definitions/context/utils/normalize-resource-args.d.ts +4 -5
  161. package/dist/types/packages/router/definitions/index.d.ts +5 -9
  162. package/dist/types/packages/router/definitions/interfaces.d.ts +4 -4
  163. package/dist/types/packages/router/index.d.ts +23 -11
  164. package/dist/types/packages/router/interfaces.d.ts +4 -4
  165. package/dist/types/packages/router/namespace/index.d.ts +7 -9
  166. package/dist/types/packages/router/namespace/interfaces.d.ts +3 -3
  167. package/dist/types/packages/router/namespace/utils/normalize-name.d.ts +1 -3
  168. package/dist/types/packages/router/namespace/utils/normalize-path.d.ts +1 -3
  169. package/dist/types/packages/router/resource/index.d.ts +7 -8
  170. package/dist/types/packages/router/resource/interfaces.d.ts +12 -4
  171. package/dist/types/packages/router/resource/utils/normalize-only.d.ts +3 -5
  172. package/dist/types/packages/router/route/action/enhancers/resource.d.ts +1 -3
  173. package/dist/types/packages/router/route/action/enhancers/track-perf.d.ts +1 -3
  174. package/dist/types/packages/router/route/action/index.d.ts +7 -2
  175. package/dist/types/packages/router/route/action/interfaces.d.ts +4 -0
  176. package/dist/types/packages/router/route/action/utils/create-page-links.d.ts +21 -5
  177. package/dist/types/packages/router/route/action/utils/get-action-name.d.ts +1 -3
  178. package/dist/types/packages/router/route/action/utils/get-controller-name.d.ts +1 -3
  179. package/dist/types/packages/router/route/index.d.ts +13 -9
  180. package/dist/types/packages/router/route/interfaces.d.ts +7 -5
  181. package/dist/types/packages/router/route/params/errors/client-generated-id-error.d.ts +4 -4
  182. package/dist/types/packages/router/route/params/errors/forbidden-parameter-error.d.ts +4 -4
  183. package/dist/types/packages/router/route/params/errors/index.d.ts +1 -0
  184. package/dist/types/packages/router/route/params/errors/invalid-parameter-error.d.ts +4 -6
  185. package/dist/types/packages/router/route/params/errors/parameter-not-nullable-error.d.ts +4 -6
  186. package/dist/types/packages/router/route/params/errors/parameter-range-error.d.ts +9 -0
  187. package/dist/types/packages/router/route/params/errors/parameter-required-error.d.ts +4 -6
  188. package/dist/types/packages/router/route/params/errors/parameter-type-error.d.ts +4 -6
  189. package/dist/types/packages/router/route/params/errors/parameter-value-error.d.ts +4 -6
  190. package/dist/types/packages/router/route/params/errors/resource-mismatch-error.d.ts +4 -6
  191. package/dist/types/packages/router/route/params/index.d.ts +5 -9
  192. package/dist/types/packages/router/route/params/interfaces.d.ts +12 -8
  193. package/dist/types/packages/router/route/params/parameter/forbidden-parameter.d.ts +1 -1
  194. package/dist/types/packages/router/route/params/parameter/ignored-parameter.d.ts +14 -0
  195. package/dist/types/packages/router/route/params/parameter/index.d.ts +21 -5
  196. package/dist/types/packages/router/route/params/parameter/interfaces.d.ts +2 -2
  197. package/dist/types/packages/router/route/params/parameter/utils/validate-range.d.ts +3 -0
  198. package/dist/types/packages/router/route/params/parameter/utils/validate-value.d.ts +1 -3
  199. package/dist/types/packages/router/route/params/parameter-group/index.d.ts +3 -5
  200. package/dist/types/packages/router/route/params/parameter-group/utils/missing-params.d.ts +8 -0
  201. package/dist/types/packages/router/route/params/utils/get-data-params.d.ts +5 -1
  202. package/dist/types/packages/router/route/params/utils/get-default-collection-params.d.ts +1 -3
  203. package/dist/types/packages/router/route/params/utils/get-default-member-params.d.ts +5 -2
  204. package/dist/types/packages/router/route/params/utils/get-query-params.d.ts +3 -9
  205. package/dist/types/packages/router/route/params/utils/get-url-params.d.ts +1 -3
  206. package/dist/types/packages/router/route/params/utils/parse-column-value.d.ts +16 -0
  207. package/dist/types/packages/router/route/params/utils/validate-client-id.d.ts +1 -1
  208. package/dist/types/packages/router/route/params/utils/validate-resource-id.d.ts +1 -3
  209. package/dist/types/packages/router/route/params/utils/validate-type.d.ts +3 -5
  210. package/dist/types/packages/router/route/utils/get-dynamic-segments.d.ts +1 -3
  211. package/dist/types/packages/router/route/utils/get-static-path.d.ts +6 -2
  212. package/dist/types/packages/router/utils/create-replacer.d.ts +10 -2
  213. package/dist/types/packages/serializer/index.d.ts +243 -434
  214. package/dist/types/packages/serializer/interfaces.d.ts +19 -1
  215. package/dist/types/packages/serializer/utils/include-tree.d.ts +12 -3
  216. package/dist/types/packages/serializer/utils/load-linkage.d.ts +9 -3
  217. package/dist/types/packages/server/errors/error-list.d.ts +25 -0
  218. package/dist/types/packages/server/errors/method-not-allowed-error.d.ts +11 -0
  219. package/dist/types/packages/server/index.d.ts +9 -9
  220. package/dist/types/packages/server/interfaces.d.ts +59 -7
  221. package/dist/types/packages/server/request/constants.d.ts +2 -2
  222. package/dist/types/packages/server/request/index.d.ts +3 -5
  223. package/dist/types/packages/server/request/interfaces.d.ts +72 -8
  224. package/dist/types/packages/server/request/parser/errors/malformed-request-error.d.ts +3 -5
  225. package/dist/types/packages/server/request/parser/index.d.ts +6 -1
  226. package/dist/types/packages/server/request/parser/utils/format.d.ts +13 -11
  227. package/dist/types/packages/server/request/parser/utils/normalize-document.d.ts +13 -0
  228. package/dist/types/packages/server/request/parser/utils/parse-nested-object.d.ts +1 -3
  229. package/dist/types/packages/server/request/parser/utils/parse-read.d.ts +2 -4
  230. package/dist/types/packages/server/request/parser/utils/parse-write.d.ts +17 -1
  231. package/dist/types/packages/server/request/utils/get-domain.d.ts +1 -3
  232. package/dist/types/packages/server/responder/index.d.ts +1 -3
  233. package/dist/types/packages/server/responder/utils/content-type-for.d.ts +8 -0
  234. package/dist/types/packages/server/responder/utils/data-for.d.ts +3 -5
  235. package/dist/types/packages/server/responder/utils/normalize.d.ts +2 -3
  236. package/dist/types/packages/server/response/index.d.ts +3 -5
  237. package/dist/types/packages/server/response/interfaces.d.ts +14 -3
  238. package/dist/types/packages/server/utils/client-ip-for.d.ts +10 -0
  239. package/dist/types/packages/server/utils/create-server-error.d.ts +11 -3
  240. package/dist/types/packages/server/utils/request-id-for.d.ts +9 -0
  241. package/dist/types/packages/server/utils/set-cors-headers.d.ts +2 -2
  242. package/dist/types/packages/server/utils/source-for.d.ts +19 -3
  243. package/dist/types/packages/server/utils/status-for-error.d.ts +6 -0
  244. package/dist/types/packages/server/utils/validate-accept.d.ts +1 -1
  245. package/dist/types/packages/server/utils/validate-content-type.d.ts +8 -3
  246. package/dist/types/packages/testing/audit-visibility.d.ts +189 -0
  247. package/dist/types/packages/testing/index.d.ts +4 -0
  248. package/dist/types/packages/testing/start-app.d.ts +52 -0
  249. package/dist/types/packages/testing/utils/identifiers-in.d.ts +14 -0
  250. package/dist/types/testing.d.ts +9 -0
  251. package/dist/types/utils/chalk.d.ts +16 -0
  252. package/dist/types/utils/pick.d.ts +2 -2
  253. package/package.json +54 -26
  254. package/dist/types/packages/cli/commands/test.d.ts +0 -4
  255. package/dist/types/packages/logger/utils/sql.d.ts +0 -4
  256. package/dist/types/packages/router/route/params/parameter-group/utils/has-required-params.d.ts +0 -5
  257. package/dist/types/utils/create-query-string.d.ts +0 -6
  258. package/dist/types/utils/has-own-property.d.ts +0 -1
package/dist/cli.cjs CHANGED
@@ -40,20 +40,27 @@ __export(index_exports, {
40
40
  destroy: () => destroy2,
41
41
  generate: () => generate,
42
42
  repl: () => repl,
43
- serve: () => serve,
44
- test: () => test
43
+ serve: () => serve
45
44
  });
46
45
  module.exports = __toCommonJS(index_exports);
47
46
 
48
- // src/packages/cli/commands/build.ts
47
+ // src/packages/cli/utils/create-spinner.ts
49
48
  var import_ora = __toESM(require("ora"));
49
+ function createSpinner(text) {
50
+ const { isTTY, columns } = process.stderr;
51
+ return (0, import_ora.default)({
52
+ text,
53
+ spinner: "dots",
54
+ ...isTTY && !columns ? { isEnabled: false } : {}
55
+ });
56
+ }
50
57
 
51
58
  // src/constants.ts
52
59
  var import_os = require("os");
53
60
  var { env: ENV } = process;
54
61
  var CWD = process.cwd();
55
62
  var PID = process.pid;
56
- var PORT = parseInt(ENV.PORT, 10) || 4e3;
63
+ var PORT = parseInt(ENV.PORT ?? "", 10) || 4e3;
57
64
  var NODE_ENV = ENV.NODE_ENV || "development";
58
65
  var DATABASE_URL = ENV.DATABASE_URL;
59
66
  var LUMEN_CONSOLE = ENV.LUMEN_CONSOLE || false;
@@ -90,13 +97,9 @@ function exec(cmd, opts) {
90
97
  });
91
98
  }
92
99
 
93
- // src/utils/k.ts
94
- function K() {
95
- return this;
96
- }
97
-
98
100
  // src/utils/try-catch.ts
99
- async function tryCatch(fn, rescue = K) {
101
+ var noop = () => void 0;
102
+ async function tryCatch(fn, rescue = noop) {
100
103
  let result;
101
104
  try {
102
105
  result = await fn();
@@ -132,7 +135,7 @@ function freezeProps(target, makePublic, ...props) {
132
135
  (obj, key) => ({
133
136
  ...obj,
134
137
  [key]: {
135
- value: Reflect.get(target, key),
138
+ value: target[key],
136
139
  writable: false,
137
140
  enumerable: makePublic,
138
141
  configurable: false
@@ -378,7 +381,7 @@ function chain(source) {
378
381
  return source;
379
382
  },
380
383
  construct(constructor) {
381
- return chain(Reflect.construct(constructor, [source]));
384
+ return chain(new constructor(source));
382
385
  }
383
386
  };
384
387
  }
@@ -588,10 +591,9 @@ async function createManifest(dir, assets, { useStrict }) {
588
591
  await writeFile(file, useStrict ? "'use strict';\n\n" : "");
589
592
  await Promise.all(
590
593
  Array.from(assets).map(([key, value]) => {
591
- const write = Reflect.get(writer, key);
592
- if (write) {
593
- return write(value);
594
- } else if (!write && typeof value === "string") {
594
+ if (Object.hasOwn(writer, key)) {
595
+ return writer[key](value);
596
+ } else if (typeof value === "string") {
595
597
  return appendFile(file, createExportStatement(key, value));
596
598
  }
597
599
  return Promise.resolve();
@@ -706,11 +708,15 @@ async function compile(dir, env, opts = {}) {
706
708
  bundle: true,
707
709
  platform: "node",
708
710
  format: "cjs",
709
- target: "node20",
711
+ target: "node22",
710
712
  sourcemap: true,
711
713
  // Reproduces `rollup-plugin-lumen`: preserves each class's `.name` (Lumen keys
712
714
  // models/controllers/serializers off it) against esbuild's own renaming.
713
715
  keepNames: true,
716
+ // Strictness follows `useStrict` alone. Without this, esbuild would apply
717
+ // whatever tsconfig.json it finds above the app (its `strict` implies
718
+ // `alwaysStrict`, even for .js files), silently overriding `--use-weak`.
719
+ tsconfigRaw: { compilerOptions: { alwaysStrict: useStrict } },
714
720
  // Bare specifiers stay external (knex, inflection, the app's own deps);
715
721
  // relative paths and the two aliases below are bundled in. This matches the
716
722
  // old `is-external` split, with the framework itself pulled in via LUMEN_LOCAL.
@@ -727,10 +733,7 @@ async function compile(dir, env, opts = {}) {
727
733
 
728
734
  // src/packages/cli/commands/build.ts
729
735
  async function build2(useStrict = false) {
730
- const spinner = new import_ora.default({
731
- text: "Building your application...",
732
- spinner: "dots"
733
- });
736
+ const spinner = createSpinner("Building your application...");
734
737
  spinner.start();
735
738
  await compile(CWD, NODE_ENV, {
736
739
  useStrict
@@ -740,17 +743,24 @@ async function build2(useStrict = false) {
740
743
 
741
744
  // src/packages/cli/commands/create.ts
742
745
  var import_os3 = require("os");
743
- var import_ora2 = __toESM(require("ora"));
744
- var import_chalk10 = require("chalk");
746
+
747
+ // src/utils/chalk.ts
748
+ var import_chalk = require("chalk");
749
+ var chalk = new import_chalk.Chalk();
750
+ var chalk_default = chalk;
751
+
752
+ // src/packages/database/constants.ts
753
+ var SQLITE_DRIVER = "better-sqlite3";
754
+ var VALID_DRIVERS = ["pg", SQLITE_DRIVER, "mssql", "mysql", "mysql2"];
745
755
 
746
756
  // src/packages/cli/utils/driver-for.ts
747
757
  var drivers = /* @__PURE__ */ new Map([
748
758
  ["postgres", "pg"],
749
- ["sqlite", "sqlite3"],
759
+ ["sqlite", SQLITE_DRIVER],
750
760
  ["mysql", "mysql2"]
751
761
  ]);
752
762
  function driverFor(database = "sqlite") {
753
- return drivers.get(database) || "sqlite3";
763
+ return drivers.get(database) || SQLITE_DRIVER;
754
764
  }
755
765
 
756
766
  // src/packages/cli/templates/application.ts
@@ -778,6 +788,7 @@ var config_default = (name, env) => {
778
788
  level: ${isProdENV ? "'INFO'" : "'DEBUG'"},
779
789
  format: ${isProdENV ? "'json'" : "'text'"},
780
790
  enabled: ${(!isTestENV).toString()},
791
+ requestBody: ${(!isProdENV).toString()},
781
792
 
782
793
  filter: {
783
794
  params: []
@@ -800,23 +811,28 @@ function indent(amount = 1) {
800
811
  }
801
812
 
802
813
  // src/packages/cli/templates/database.ts
814
+ var SUFFIXES = {
815
+ development: "dev",
816
+ test: "test",
817
+ production: "prod"
818
+ };
803
819
  var database_default = (name, driver) => {
804
820
  const schemaName = underscore(name);
805
821
  let driverName = driver;
806
822
  let template2 = "export default {\n";
807
823
  let username;
808
824
  if (!driverName) {
809
- driverName = "sqlite3";
825
+ driverName = SQLITE_DRIVER;
810
826
  }
811
827
  if (driverName === "pg") {
812
828
  username = "postgres";
813
- } else if (driverName !== "pg" && driverName !== "sqlite3") {
829
+ } else if (driverName !== "pg" && driverName !== SQLITE_DRIVER) {
814
830
  username = "root";
815
831
  }
816
832
  ["development", "test", "production"].forEach((environment) => {
817
833
  template2 += `${indent(2)}${environment}: {
818
834
  `;
819
- if (driverName !== "sqlite3") {
835
+ if (driverName !== SQLITE_DRIVER) {
820
836
  template2 += `${indent(4)}pool: 5,
821
837
  `;
822
838
  }
@@ -826,23 +842,12 @@ var database_default = (name, driver) => {
826
842
  template2 += `${indent(4)}username: '${username}',
827
843
  `;
828
844
  }
829
- switch (environment) {
830
- case "development":
831
- template2 += `${indent(4)}database: '${schemaName}_dev'
845
+ if (driverName === SQLITE_DRIVER) {
846
+ template2 += `${indent(4)}database: '${schemaName}'
832
847
  `;
833
- break;
834
- case "test":
835
- template2 += `${indent(4)}database: '${schemaName}_test'
836
- `;
837
- break;
838
- case "production":
839
- template2 += `${indent(4)}database: '${schemaName}_prod'
840
- `;
841
- break;
842
- default:
843
- template2 += `${indent(4)}database: '${schemaName}_${environment}'
848
+ } else {
849
+ template2 += `${indent(4)}database: '${schemaName}_${SUFFIXES[environment]}'
844
850
  `;
845
- break;
846
851
  }
847
852
  template2 += `${indent(2)}}`;
848
853
  if (environment !== "production") {
@@ -861,24 +866,23 @@ var seed_default = () => template`
861
866
  `;
862
867
 
863
868
  // package.json
864
- var version = "3.1.1";
869
+ var version = "4.0.0";
865
870
 
866
871
  // src/packages/cli/templates/package-json.ts
867
872
  var LUMEN_VERSION = version;
868
873
  var DRIVER_DEPS = {
869
- sqlite3: { name: "sqlite3", version: "^5.1.7" },
870
- pg: { name: "pg", version: "^8.16.3" },
871
- mysql2: { name: "mysql2", version: "^3.15.3" }
874
+ [SQLITE_DRIVER]: { name: SQLITE_DRIVER, version: "^13.0.3" },
875
+ pg: { name: "pg", version: "^8.23.1" },
876
+ mysql2: { name: "mysql2", version: "^3.24.5" }
872
877
  };
873
878
  var package_json_default = (name, driver) => {
874
- const dbDriver = DRIVER_DEPS[driver] || DRIVER_DEPS.sqlite3;
879
+ const dbDriver = DRIVER_DEPS[driver] || DRIVER_DEPS[SQLITE_DRIVER];
875
880
  const pkg = {
876
881
  name,
877
882
  version: "0.0.1",
878
883
  description: "",
879
884
  scripts: {
880
885
  start: "lumen serve",
881
- test: "lumen test",
882
886
  lint: "eslint ."
883
887
  },
884
888
  author: "",
@@ -889,21 +893,32 @@ var package_json_default = (name, driver) => {
889
893
  [dbDriver.name]: dbDriver.version
890
894
  },
891
895
  devDependencies: {
892
- "@eslint/js": "^9.39.5",
893
- eslint: "^9.39.5",
894
- globals: "^17.7.0"
896
+ "@eslint/js": "^10.0.1",
897
+ eslint: "^10.12.0",
898
+ globals: "^17.13.0"
895
899
  },
896
900
  engines: {
897
- node: ">= 20"
901
+ node: ">= 22.14"
898
902
  }
899
903
  };
900
- if (dbDriver.name === "sqlite3") {
901
- pkg.pnpm = { onlyBuiltDependencies: ["sqlite3"] };
902
- }
903
904
  return `${JSON.stringify(pkg, null, 2)}
904
905
  `;
905
906
  };
906
907
 
908
+ // src/packages/cli/templates/pnpm-workspace.ts
909
+ var pnpm_workspace_default = (driver) => {
910
+ const builds = [
911
+ "esbuild",
912
+ ...driver === SQLITE_DRIVER ? [SQLITE_DRIVER] : []
913
+ ];
914
+ return [
915
+ "# Build scripts pnpm may skip: these packages work without them.",
916
+ "allowBuilds:",
917
+ ...builds.map((name) => ` ${name}: false`),
918
+ ""
919
+ ].join("\n");
920
+ };
921
+
907
922
  // src/packages/cli/templates/eslint-config.ts
908
923
  var eslint_config_default = () => `import js from '@eslint/js';
909
924
  import globals from 'globals';
@@ -936,13 +951,8 @@ var readme_default = (name) => template`
936
951
 
937
952
  * \`lumen serve\`
938
953
 
939
- ## Testing
940
-
941
- * \`lumen test\`
942
-
943
954
  ## Further Reading / Useful Links
944
955
  * [Lumen](https://github.com/nickschot/lux/)
945
- * [Chai](http://chaijs.com/) / [Mocha](http://mochajs.org/)
946
956
  `;
947
957
 
948
958
  // src/packages/cli/templates/license.ts
@@ -994,9 +1004,6 @@ var gitignore_default = () => template`
994
1004
  *.DS_Store
995
1005
  `;
996
1006
 
997
- // src/packages/cli/generator/index.ts
998
- var import_chalk9 = require("chalk");
999
-
1000
1007
  // src/packages/cli/utils/create-prompt.ts
1001
1008
  var import_readline = require("readline");
1002
1009
  var YES = /^y(es)?$/i;
@@ -1033,11 +1040,9 @@ __export(generate_type_exports, {
1033
1040
  util: () => util
1034
1041
  });
1035
1042
  var import_path12 = require("path");
1036
- var import_chalk8 = require("chalk");
1037
- var import_inflection20 = require("inflection");
1043
+ var import_inflection22 = require("inflection");
1038
1044
 
1039
1045
  // src/packages/cli/generator/constants.ts
1040
- var import_chalk = require("chalk");
1041
1046
  var NAMESPACED_RESOURCE_MESSAGE = template`
1042
1047
 
1043
1048
  🎉 It looks like the resource you generated is within a namespace!
@@ -1051,38 +1056,31 @@ var NAMESPACED_RESOURCE_MESSAGE = template`
1051
1056
  Example:
1052
1057
 
1053
1058
  export default function routes() {
1054
- ${(0, import_chalk.dim)("// this resource will be accessible at /users")}
1059
+ ${chalk_default.dim("// this resource will be accessible at /users")}
1055
1060
  this.resource('users');
1056
1061
 
1057
1062
  this.namespace('admin', function () {
1058
- ${(0, import_chalk.dim)("// this resource will be accessible at /admin/users")}
1063
+ ${chalk_default.dim("// this resource will be accessible at /admin/users")}
1059
1064
  this.resource('users');
1060
1065
  });
1061
1066
  }
1062
1067
 
1063
1068
  `;
1064
1069
 
1065
- // src/packages/database/errors/invalid-driver-error.ts
1066
- var import_chalk5 = require("chalk");
1067
-
1068
- // src/packages/database/constants.ts
1069
- var VALID_DRIVERS = [
1070
- "pg",
1071
- "sqlite3",
1072
- "mssql",
1073
- "mysql",
1074
- "mysql2",
1075
- "mariasql",
1076
- "strong-oracle",
1077
- "oracle"
1078
- ];
1070
+ // src/utils/k.ts
1071
+ function K() {
1072
+ return this;
1073
+ }
1079
1074
 
1080
1075
  // src/packages/logger/constants.ts
1081
1076
  var DEBUG = "DEBUG";
1082
1077
  var INFO = "INFO";
1083
1078
  var WARN = "WARN";
1084
1079
  var ERROR = "ERROR";
1085
- var FORMATS = new set_default(["text", "json"]);
1080
+ var FORMATS = new set_default([
1081
+ "text",
1082
+ "json"
1083
+ ]);
1086
1084
  FORMATS.freeze();
1087
1085
  var LEVELS = new map_default([
1088
1086
  [DEBUG, 0],
@@ -1092,9 +1090,26 @@ var LEVELS = new map_default([
1092
1090
  ]);
1093
1091
  LEVELS.freeze();
1094
1092
 
1093
+ // src/packages/logger/errors/invalid-config-error.ts
1094
+ var InvalidConfigError = class extends Error {
1095
+ constructor(key, value, allowed) {
1096
+ super(
1097
+ `Invalid logging.${key} ${JSON.stringify(value)}; expected one of: ${Array.from(allowed).join(", ")}.`
1098
+ );
1099
+ }
1100
+ };
1101
+ var invalid_config_error_default = InvalidConfigError;
1102
+
1095
1103
  // src/packages/logger/writer/index.ts
1096
1104
  var import_tty = require("tty");
1097
- var import_chalk2 = require("chalk");
1105
+
1106
+ // src/packages/logger/utils/error-name.ts
1107
+ function errorName(err) {
1108
+ if (!(err instanceof Error)) {
1109
+ return "Error";
1110
+ }
1111
+ return err.name === "Error" ? err.constructor.name : err.name;
1112
+ }
1098
1113
 
1099
1114
  // src/utils/entries.ts
1100
1115
  function entries(source) {
@@ -1113,11 +1128,12 @@ function omit(src, ...omitted) {
1113
1128
  }
1114
1129
 
1115
1130
  // src/packages/logger/writer/constants.ts
1116
- var import_ansi_regex = __toESM(require("ansi-regex"));
1117
- var ANSI = (0, import_ansi_regex.default)();
1118
1131
  var STDOUT = /^(DEBUG|INFO)$/;
1119
1132
  var STDERR = /^(WARN|ERROR)$/;
1120
1133
 
1134
+ // src/packages/logger/writer/utils/format-message.ts
1135
+ var import_util = require("util");
1136
+
1121
1137
  // src/utils/stringify.ts
1122
1138
  function stringify(value, spaces) {
1123
1139
  if (isObject(value) || Array.isArray(value)) {
@@ -1131,7 +1147,7 @@ function formatMessage(data, format) {
1131
1147
  if (data instanceof Error) {
1132
1148
  return data.stack;
1133
1149
  } else if (format === "json") {
1134
- return stringify(data).replace(ANSI, "");
1150
+ return (0, import_util.stripVTControlCharacters)(stringify(data));
1135
1151
  }
1136
1152
  return stringify(data, 2);
1137
1153
  }
@@ -1140,17 +1156,31 @@ function formatMessage(data, format) {
1140
1156
  function isMessageObject(value) {
1141
1157
  return Boolean(value) && typeof value === "object";
1142
1158
  }
1143
- function createWriter2(format) {
1159
+ function shortRequestId(context) {
1160
+ const id = context?.requestId;
1161
+ return typeof id === "string" && id ? `[${id.slice(0, 8)}]` : "";
1162
+ }
1163
+ function createWriter2(format, { timestamps = true } = {}) {
1144
1164
  return function write(data) {
1145
- const { level, ...etc } = data;
1146
- let { message, timestamp } = etc;
1165
+ const { level, context, timestamp, ...etc } = data;
1166
+ let { message } = etc;
1147
1167
  let output;
1148
1168
  if (format === "json") {
1149
- output = {};
1150
- if (isMessageObject(message) && message.message) {
1169
+ if (message instanceof Error) {
1151
1170
  output = {
1152
1171
  timestamp,
1153
1172
  level,
1173
+ ...context,
1174
+ message: message.message,
1175
+ name: errorName(message),
1176
+ ...omit(message, "message"),
1177
+ stack: message.stack
1178
+ };
1179
+ } else if (isMessageObject(message) && message.message) {
1180
+ output = {
1181
+ timestamp,
1182
+ level,
1183
+ ...context,
1154
1184
  message: message.message,
1155
1185
  ...omit(message, "message")
1156
1186
  };
@@ -1158,6 +1188,7 @@ function createWriter2(format) {
1158
1188
  output = {
1159
1189
  timestamp,
1160
1190
  level,
1191
+ ...context,
1161
1192
  message
1162
1193
  };
1163
1194
  }
@@ -1168,21 +1199,27 @@ function createWriter2(format) {
1168
1199
  columns = process.stdout.columns;
1169
1200
  }
1170
1201
  message = formatMessage(message, "text");
1202
+ let label = level.padEnd(5);
1171
1203
  switch (level) {
1172
1204
  case WARN:
1173
- timestamp = (0, import_chalk2.yellow)(`[${timestamp}]`);
1205
+ label = chalk_default.yellow(label);
1174
1206
  break;
1175
1207
  case ERROR:
1176
- timestamp = (0, import_chalk2.red)(`[${timestamp}]`);
1208
+ label = chalk_default.red(label);
1177
1209
  break;
1178
1210
  default:
1179
- timestamp = (0, import_chalk2.dim)(`[${timestamp}]`);
1211
+ label = chalk_default.dim(label);
1180
1212
  break;
1181
1213
  }
1182
- output = `${timestamp} ${message}
1183
-
1184
- ${(0, import_chalk2.dim)("-").repeat(columns)}
1185
- `;
1214
+ const prefix = [
1215
+ timestamps ? chalk_default.dim(`[${timestamp}]`) : "",
1216
+ label,
1217
+ shortRequestId(context)
1218
+ ].filter(Boolean).join(" ");
1219
+ output = columns ? `${prefix} ${message}
1220
+
1221
+ ${chalk_default.dim("-").repeat(columns)}
1222
+ ` : `${prefix} ${message}`;
1186
1223
  }
1187
1224
  if (STDOUT.test(level)) {
1188
1225
  process.stdout.write(`${output}
@@ -1194,12 +1231,6 @@ ${(0, import_chalk2.dim)("-").repeat(columns)}
1194
1231
  };
1195
1232
  }
1196
1233
 
1197
- // src/packages/logger/request-logger/utils/log-text.ts
1198
- var import_chalk4 = __toESM(require("chalk"));
1199
-
1200
- // src/packages/logger/request-logger/templates.ts
1201
- var import_chalk3 = require("chalk");
1202
-
1203
1234
  // src/packages/logger/utils/line.ts
1204
1235
  function line(strings, ...values) {
1205
1236
  return insertValues(strings, ...values).replace(/(\r\n|\n|\r|)/gm, "").replace(/\s+/g, " ").trim();
@@ -1214,6 +1245,13 @@ function pad(startTime, endTime, duration) {
1214
1245
  const maxLength = countDigits(endTime - startTime);
1215
1246
  return " ".repeat(maxLength - countDigits(duration)) + duration;
1216
1247
  }
1248
+ function handledBy(route) {
1249
+ if (!route) {
1250
+ return "";
1251
+ }
1252
+ const { controller: controller2, action } = route;
1253
+ return ` by ${chalk_default.yellow(controller2.constructor.name)}#${chalk_default.blue(action)}`;
1254
+ }
1217
1255
  var debugTemplate = ({
1218
1256
  path: path6,
1219
1257
  stats,
@@ -1227,23 +1265,23 @@ var debugTemplate = ({
1227
1265
  statusMessage,
1228
1266
  remoteAddress
1229
1267
  }) => `${line`
1230
- Processed ${(0, import_chalk3.cyan)(`${method}`)} "${path6}" from ${remoteAddress}
1231
- with ${Reflect.apply(colorStr, null, [`${statusCode}`])}
1232
- ${Reflect.apply(colorStr, null, [`${statusMessage}`])} by ${route ? `${(0, import_chalk3.yellow)(route.controller.constructor.name)}#${(0, import_chalk3.blue)(route.action)}` : null}
1268
+ Processed ${chalk_default.cyan(`${method}`)} "${path6}" from ${remoteAddress}
1269
+ with ${colorStr(`${statusCode}`)}
1270
+ ${colorStr(`${statusMessage}`)}${handledBy(route)}
1233
1271
  `}
1234
1272
 
1235
- ${(0, import_chalk3.magenta)("Params")}
1273
+ ${chalk_default.magenta("Params")}
1236
1274
 
1237
1275
  ${JSON.stringify(params, null, 2)}
1238
1276
 
1239
- ${(0, import_chalk3.magenta)("Stats")}
1277
+ ${chalk_default.magenta("Stats")}
1240
1278
 
1241
1279
  ${stats.map((stat2) => {
1242
1280
  const { type, duration, controller: controller2 } = stat2;
1243
1281
  let { name } = stat2;
1244
- name = (0, import_chalk3.blue)(name);
1282
+ name = chalk_default.blue(name);
1245
1283
  if (type === "action") {
1246
- name = `${(0, import_chalk3.yellow)(controller2)}#${name}`;
1284
+ name = `${chalk_default.yellow(controller2)}#${name}`;
1247
1285
  }
1248
1286
  return `${pad(startTime, endTime, duration)} ms ${name}`;
1249
1287
  }).join("\n")}
@@ -1264,29 +1302,68 @@ var infoTemplate = ({
1264
1302
  statusCode,
1265
1303
  statusMessage,
1266
1304
  remoteAddress
1267
- }) => line`
1268
- Processed ${(0, import_chalk3.cyan)(`${method}`)} "${path6}" ${(0, import_chalk3.magenta)("Params")} ${JSON.stringify(
1269
- params
1270
- )} from ${remoteAddress} in ${(endTime - startTime).toString()} ms with ${Reflect.apply(
1271
- colorStr,
1272
- null,
1273
- [`${statusCode}`]
1274
- )} ${Reflect.apply(colorStr, null, [`${statusMessage}`])} by ${route ? `${(0, import_chalk3.yellow)(route.controller.constructor.name)}#${(0, import_chalk3.blue)(route.action)}` : null}
1275
- `;
1305
+ }) => (
1306
+ // Built directly rather than with `line`, which would also collapse runs
1307
+ // of spaces inside param values.
1308
+ [
1309
+ chalk_default.cyan(method),
1310
+ path6,
1311
+ colorStr(`${statusCode} ${statusMessage}`),
1312
+ `in ${endTime - startTime} ms${handledBy(route)}`,
1313
+ remoteAddress ? `from ${remoteAddress}` : "",
1314
+ Object.keys(params).length ? JSON.stringify(params) : ""
1315
+ ].filter(Boolean).join(" ")
1316
+ );
1276
1317
 
1277
1318
  // src/packages/logger/request-logger/utils/filter-params.ts
1278
- function filterParams(params, ...filtered) {
1279
- return entries(params).map(([key, value]) => [
1280
- key,
1281
- filtered.indexOf(key) >= 0 ? "[FILTERED]" : value
1282
- ]).reduce(
1283
- (result, [key, value]) => ({
1284
- ...result,
1285
- [key]: value && typeof value === "object" && !Array.isArray(value) ? filterParams(value, ...filtered) : value
1286
- }),
1319
+ var FILTERED_PARAMS = [
1320
+ "password",
1321
+ "secret",
1322
+ "token"
1323
+ ];
1324
+ var FILTERED = "[FILTERED]";
1325
+ function isFiltered(key, filtered) {
1326
+ const name = key.toLowerCase();
1327
+ return filtered.some((item) => name.includes(item.toLowerCase()));
1328
+ }
1329
+ function filterValue(value, filtered) {
1330
+ if (Array.isArray(value)) {
1331
+ return value.map((item) => filterValue(item, filtered));
1332
+ }
1333
+ if (value && typeof value === "object") {
1334
+ return filterKeys(value, filtered);
1335
+ }
1336
+ return value;
1337
+ }
1338
+ function filterKeys(params, filtered) {
1339
+ return entries(params).reduce(
1340
+ (result, [key, value]) => {
1341
+ result[key] = isFiltered(key, filtered) ? FILTERED : filterValue(value, filtered);
1342
+ return result;
1343
+ },
1287
1344
  {}
1288
1345
  );
1289
1346
  }
1347
+ function filterParams(params, ...filtered) {
1348
+ return filterKeys(params, [...FILTERED_PARAMS, ...filtered]);
1349
+ }
1350
+
1351
+ // src/packages/logger/request-logger/utils/params-for.ts
1352
+ var DOCUMENT_MEMBERS = [
1353
+ "data",
1354
+ "meta",
1355
+ "included",
1356
+ "links",
1357
+ "jsonapi",
1358
+ "errors"
1359
+ ];
1360
+ function paramsFor(logger, req) {
1361
+ let params = req.params;
1362
+ if (!logger.requestBody) {
1363
+ params = omit(params, ...DOCUMENT_MEMBERS);
1364
+ }
1365
+ return filterParams(params, ...logger.filter.params);
1366
+ }
1290
1367
 
1291
1368
  // src/packages/logger/request-logger/utils/log-text.ts
1292
1369
  function logText(logger, {
@@ -1297,15 +1374,18 @@ function logText(logger, {
1297
1374
  res.once("finish", () => {
1298
1375
  const endTime = Date.now();
1299
1376
  const {
1377
+ id: requestId,
1378
+ ip: remoteAddress,
1300
1379
  route,
1301
1380
  method,
1302
- url: { path: path6 },
1303
- connection: { remoteAddress }
1381
+ // `pathname`, not `path`: the query string would bypass the param filter;
1382
+ // query params are logged, filtered, under `params`.
1383
+ url: { pathname: path6 }
1304
1384
  } = req;
1305
1385
  const { stats, statusCode, statusMessage } = res;
1306
- const params = filterParams(req.params, ...logger.filter.params);
1386
+ const params = paramsFor(logger, req);
1307
1387
  const statusColor = statusCode >= 200 && statusCode < 400 ? "green" : "red";
1308
- let colorStr = Reflect.get(import_chalk4.default, statusColor);
1388
+ let colorStr = chalk_default[statusColor];
1309
1389
  if (typeof colorStr === "undefined") {
1310
1390
  colorStr = (str) => str;
1311
1391
  }
@@ -1322,10 +1402,11 @@ function logText(logger, {
1322
1402
  statusMessage,
1323
1403
  remoteAddress
1324
1404
  };
1405
+ const context = { requestId };
1325
1406
  if (logger.level === DEBUG) {
1326
- logger.debug(debugTemplate(templateData));
1407
+ logger.debug(debugTemplate(templateData), context);
1327
1408
  } else {
1328
- logger.info(infoTemplate(templateData));
1409
+ logger.info(infoTemplate(templateData), context);
1329
1410
  }
1330
1411
  });
1331
1412
  }
@@ -1333,31 +1414,41 @@ function logText(logger, {
1333
1414
  // src/packages/logger/request-logger/utils/log-json.ts
1334
1415
  var MESSAGE = "Processed Request";
1335
1416
  function logJSON(logger, {
1417
+ startTime,
1336
1418
  request: req,
1337
1419
  response: res
1338
1420
  }) {
1339
1421
  res.once("finish", () => {
1340
1422
  const {
1423
+ id: requestId,
1424
+ ip: remoteAddress,
1425
+ route,
1341
1426
  method,
1342
1427
  headers,
1343
1428
  httpVersion,
1344
- url: { path: path6 },
1345
- connection: { remoteAddress }
1429
+ // `pathname`, not `path`: the query string would bypass the param filter;
1430
+ // query params are logged, filtered, under `params`.
1431
+ url: { pathname: path6 }
1346
1432
  } = req;
1347
1433
  const { statusCode: status } = res;
1348
1434
  const userAgent = headers.get("user-agent");
1349
1435
  const protocol = `HTTP/${httpVersion}`;
1350
- const params = filterParams(req.params, ...logger.filter.params);
1351
- logger.info({
1352
- message: MESSAGE,
1353
- method,
1354
- path: path6,
1355
- params,
1356
- status,
1357
- protocol,
1358
- userAgent,
1359
- remoteAddress
1360
- });
1436
+ logger.info(
1437
+ {
1438
+ message: MESSAGE,
1439
+ method,
1440
+ path: path6,
1441
+ status,
1442
+ durationMs: Date.now() - startTime,
1443
+ controller: route?.controller.constructor.name,
1444
+ action: route?.action,
1445
+ params: paramsFor(logger, req),
1446
+ protocol,
1447
+ userAgent,
1448
+ remoteAddress
1449
+ },
1450
+ { requestId }
1451
+ );
1361
1452
  });
1362
1453
  }
1363
1454
 
@@ -1380,24 +1471,28 @@ function createRequestLogger(logger) {
1380
1471
  };
1381
1472
  }
1382
1473
 
1383
- // src/packages/logger/utils/sql.ts
1384
- var PATTERN = /(?:,?`|'|").+(?:`|'|"),?/;
1385
- function sql(strings, ...values) {
1386
- return insertValues(strings, ...values).split(" ").map((part) => {
1387
- if (PATTERN.test(part)) {
1388
- return part;
1389
- }
1390
- return part.toUpperCase();
1391
- }).join(" ");
1392
- }
1393
-
1394
1474
  // src/packages/logger/index.ts
1395
1475
  var Logger = class {
1396
- constructor({ level, format, filter, enabled }) {
1476
+ constructor({
1477
+ level,
1478
+ format,
1479
+ filter,
1480
+ enabled,
1481
+ requestBody = false,
1482
+ timestamps = true
1483
+ }) {
1397
1484
  let write = K;
1398
1485
  let request = K;
1486
+ if (enabled) {
1487
+ if (!LEVELS.has(level)) {
1488
+ throw new invalid_config_error_default("level", level, LEVELS.keys());
1489
+ }
1490
+ if (!FORMATS.has(format)) {
1491
+ throw new invalid_config_error_default("format", format, FORMATS);
1492
+ }
1493
+ }
1399
1494
  if (!LUMEN_CONSOLE && enabled) {
1400
- write = createWriter2(format);
1495
+ write = createWriter2(format, { timestamps });
1401
1496
  request = createRequestLogger(this);
1402
1497
  }
1403
1498
  Object.defineProperties(this, {
@@ -1425,6 +1520,18 @@ var Logger = class {
1425
1520
  enumerable: true,
1426
1521
  configurable: false
1427
1522
  },
1523
+ requestBody: {
1524
+ value: Boolean(requestBody),
1525
+ writable: false,
1526
+ enumerable: true,
1527
+ configurable: false
1528
+ },
1529
+ timestamps: {
1530
+ value: Boolean(timestamps),
1531
+ writable: false,
1532
+ enumerable: true,
1533
+ configurable: false
1534
+ },
1428
1535
  request: {
1429
1536
  value: request,
1430
1537
  writable: false,
@@ -1434,13 +1541,14 @@ var Logger = class {
1434
1541
  });
1435
1542
  const levelNum = LEVELS.get(level) || 0;
1436
1543
  LEVELS.forEach((val, key) => {
1437
- Reflect.defineProperty(this, key.toLowerCase(), {
1544
+ Object.defineProperty(this, key.toLowerCase(), {
1438
1545
  writable: false,
1439
1546
  enumerable: false,
1440
1547
  configurable: false,
1441
- value: val >= levelNum ? (message) => {
1548
+ value: val >= levelNum ? (message, context) => {
1442
1549
  write({
1443
1550
  message,
1551
+ context,
1444
1552
  level: key,
1445
1553
  timestamp: this.getTimestamp()
1446
1554
  });
@@ -1449,9 +1557,8 @@ var Logger = class {
1449
1557
  });
1450
1558
  }
1451
1559
  /**
1452
- * @method getTimestamp
1453
- * @return {String} The current time as an ISO8601 string.
1454
- * @private
1560
+ * @returns The current time as an ISO8601 string.
1561
+ * @internal
1455
1562
  */
1456
1563
  getTimestamp() {
1457
1564
  return (/* @__PURE__ */ new Date()).toISOString();
@@ -1462,11 +1569,19 @@ var logger_default = Logger;
1462
1569
  // src/packages/database/errors/invalid-driver-error.ts
1463
1570
  var InvalidDriverError = class extends Error {
1464
1571
  constructor(driver) {
1465
- super(line`
1466
- Invalid database driver ${(0, import_chalk5.yellow)(driver)} in ./config/database.js.
1467
- Please use one of the following database drivers:
1468
- ${VALID_DRIVERS.map((str) => (0, import_chalk5.green)(str)).join(", ")}.
1469
- `);
1572
+ super(
1573
+ driver === "sqlite3" ? line`
1574
+ The database driver ${chalk_default.yellow("sqlite3")} is no longer
1575
+ supported: it is unmaintained. Set ${chalk_default.green(
1576
+ `driver: '${SQLITE_DRIVER}'`
1577
+ )} in ./config/database.js, and replace the sqlite3 dependency with
1578
+ ${chalk_default.green(SQLITE_DRIVER)}. The database files stay the same.
1579
+ ` : line`
1580
+ Invalid database driver ${chalk_default.yellow(driver)} in
1581
+ ./config/database.js. Please use one of the following database
1582
+ drivers: ${VALID_DRIVERS.map((str) => chalk_default.green(str)).join(", ")}.
1583
+ `
1584
+ );
1470
1585
  }
1471
1586
  };
1472
1587
  var invalid_driver_error_default = InvalidDriverError;
@@ -1480,30 +1595,30 @@ var ModelMissingError = class extends Error {
1480
1595
  var model_missing_error_default = ModelMissingError;
1481
1596
 
1482
1597
  // src/packages/database/errors/migrations-pending-error.ts
1483
- var import_chalk6 = require("chalk");
1484
1598
  var MigrationsPendingError = class extends Error {
1485
1599
  constructor(migrations = []) {
1486
- const pending = migrations.map((str) => (0, import_chalk6.yellow)(str.substr(0, str.length - 3))).join(", ");
1600
+ const pending = migrations.map((str) => chalk_default.yellow(str.substr(0, str.length - 3))).join(", ");
1487
1601
  super(line`
1488
1602
  The following migrations are pending ${pending}.
1489
- Please run ${(0, import_chalk6.green)("lumen db:migrate")} before starting your application.
1603
+ Please run ${chalk_default.green("lumen db:migrate")} before starting your application.
1490
1604
  `);
1491
1605
  }
1492
1606
  };
1493
1607
  var migrations_pending_error_default = MigrationsPendingError;
1494
1608
 
1495
1609
  // src/packages/server/utils/create-server-error.ts
1496
- function createServerError(Target, statusCode) {
1497
- const ServerError = class extends Target {
1610
+ function createServerError(Target, statusCode, { isPublic = false } = {}) {
1611
+ const StatusError = class extends Target {
1498
1612
  constructor(...args) {
1499
1613
  super(...args);
1500
1614
  this.statusCode = statusCode;
1615
+ this.isPublic = isPublic;
1501
1616
  }
1502
1617
  };
1503
- Reflect.defineProperty(ServerError, "name", {
1618
+ Object.defineProperty(StatusError, "name", {
1504
1619
  value: Target.name
1505
1620
  });
1506
- return ServerError;
1621
+ return StatusError;
1507
1622
  }
1508
1623
 
1509
1624
  // src/packages/database/errors/unique-constraint-error.ts
@@ -1519,38 +1634,63 @@ var ConfigMissingError = class extends Error {
1519
1634
  };
1520
1635
  var config_missing_error_default = ConfigMissingError;
1521
1636
 
1637
+ // src/packages/database/errors/relationship-config-error.ts
1638
+ var RelationshipConfigError = class extends TypeError {
1639
+ constructor(problems) {
1640
+ super(
1641
+ [
1642
+ "Invalid relationships. Each must name its `inverse`: the relationship on the related model that points back:",
1643
+ ...problems.sort().map((problem) => ` - ${problem}`)
1644
+ ].join("\n")
1645
+ );
1646
+ }
1647
+ };
1648
+ var relationship_config_error_default = RelationshipConfigError;
1649
+
1522
1650
  // src/packages/database/initialize.ts
1523
1651
  var import_cluster = __toESM(require("cluster"));
1524
1652
 
1525
1653
  // src/packages/database/utils/connect.ts
1526
1654
  var import_path10 = require("path");
1655
+ function connectionFor(path6, config, databaseUrl = DATABASE_URL) {
1656
+ const { host, socket, driver, database, username, password, port, ssl, url } = config;
1657
+ const connectionUrl = databaseUrl || url;
1658
+ if (connectionUrl) {
1659
+ if (driver === SQLITE_DRIVER || ssl === void 0) {
1660
+ return connectionUrl;
1661
+ }
1662
+ return driver === "pg" ? { connectionString: connectionUrl, ssl } : { uri: connectionUrl, ssl };
1663
+ }
1664
+ return {
1665
+ host,
1666
+ database,
1667
+ password,
1668
+ port,
1669
+ ssl,
1670
+ user: username,
1671
+ socketPath: socket,
1672
+ filename: driver === SQLITE_DRIVER ? (0, import_path10.join)(path6, "db", `${database || "default"}_${NODE_ENV}.sqlite`) : void 0
1673
+ };
1674
+ }
1527
1675
  function connect(path6, config) {
1528
1676
  let { pool } = config;
1529
- const { host, socket, driver, database, username, password, port, ssl, url } = config;
1677
+ const { driver } = config;
1530
1678
  if (VALID_DRIVERS.indexOf(driver) < 0) {
1531
1679
  throw new invalid_driver_error_default(driver);
1532
1680
  }
1533
- if (pool && typeof pool === "number") {
1681
+ const usingSQLite = driver === SQLITE_DRIVER;
1682
+ if (usingSQLite) {
1683
+ pool = { min: 1, max: 1 };
1684
+ } else if (pool && typeof pool === "number") {
1534
1685
  pool = {
1535
1686
  min: pool > 1 ? 2 : 1,
1536
1687
  max: pool
1537
1688
  };
1538
1689
  }
1539
1690
  const knex = require((0, import_path10.join)(path6, "node_modules", "knex"));
1540
- const usingSQLite = driver === "sqlite3";
1541
- const connection = DATABASE_URL || url || {
1542
- host,
1543
- database,
1544
- password,
1545
- port,
1546
- ssl,
1547
- user: username,
1548
- socketPath: socket,
1549
- filename: usingSQLite ? (0, import_path10.join)(path6, "db", `${database || "default"}_${NODE_ENV}.sqlite`) : void 0
1550
- };
1551
1691
  return knex({
1552
1692
  pool,
1553
- connection,
1693
+ connection: connectionFor(path6, config),
1554
1694
  debug: false,
1555
1695
  client: driver,
1556
1696
  useNullAsDefault: usingSQLite
@@ -1579,10 +1719,91 @@ async function pendingMigrations(appPath, table) {
1579
1719
  );
1580
1720
  }
1581
1721
 
1722
+ // src/packages/database/utils/validate-relationships.ts
1723
+ var import_inflection5 = require("inflection");
1724
+ function pairs(model2, opts, inverse) {
1725
+ const viaJoin = inverse.through === model2;
1726
+ if (inverse.model !== model2 && !viaJoin) {
1727
+ return `points at ${inverse.model.name}, not ${model2.name}`;
1728
+ }
1729
+ if (opts.through) {
1730
+ if (inverse.type !== "hasMany" || inverse.through !== opts.through) {
1731
+ return `is not a hasMany through ${opts.through.name}`;
1732
+ }
1733
+ } else if (opts.type === "belongsTo") {
1734
+ if (inverse.type === "belongsTo") {
1735
+ return "is a belongsTo too; one side must be a hasOne or hasMany";
1736
+ }
1737
+ } else if (!viaJoin && inverse.type !== "belongsTo") {
1738
+ return `is a ${inverse.type}; the other side of a ${opts.type} must be a belongsTo`;
1739
+ }
1740
+ return null;
1741
+ }
1742
+ function keyHolder(model2, opts) {
1743
+ if (opts.through) {
1744
+ return opts.through;
1745
+ }
1746
+ return opts.type === "belongsTo" ? model2 : opts.model;
1747
+ }
1748
+ function validateRelationships(models) {
1749
+ const problems = [];
1750
+ for (const model2 of models) {
1751
+ for (const name of model2.relationshipNames) {
1752
+ const opts = model2.relationshipFor(name);
1753
+ if (!opts) {
1754
+ continue;
1755
+ }
1756
+ const { type, inverse, model: related, foreignKey } = opts;
1757
+ const where = `${model2.name}.${type}.${name}`;
1758
+ if (!inverse) {
1759
+ problems.push(`${where} has no \`inverse\``);
1760
+ continue;
1761
+ }
1762
+ const inverseOpts = related.relationshipFor(inverse);
1763
+ if (!inverseOpts) {
1764
+ const names = related.relationshipNames.join(", ") || "none";
1765
+ problems.push(
1766
+ `${where} has inverse '${inverse}', but ${related.name} has no relationship '${inverse}' (it has: ${names})`
1767
+ );
1768
+ continue;
1769
+ }
1770
+ const mismatch = pairs(model2, opts, inverseOpts);
1771
+ if (mismatch) {
1772
+ problems.push(
1773
+ `${where} has inverse '${inverse}', but ${related.name}.${inverseOpts.type}.${inverse} ${mismatch}`
1774
+ );
1775
+ continue;
1776
+ }
1777
+ if (!opts.through && !inverseOpts.through && inverseOpts.foreignKey !== foreignKey) {
1778
+ problems.push(
1779
+ `${where} has foreign key \`${foreignKey}\`, but ${related.name}.${inverseOpts.type}.${inverse} has \`${inverseOpts.foreignKey}\`; both sides must name the same column`
1780
+ );
1781
+ continue;
1782
+ }
1783
+ const holder = keyHolder(model2, opts);
1784
+ const column = (0, import_inflection5.camelize)(foreignKey, true);
1785
+ if (!holder.attributeNames.includes(column)) {
1786
+ problems.push(
1787
+ `${where} needs the foreign key column \`${foreignKey}\` on \`${holder.tableName}\`, which has no such column`
1788
+ );
1789
+ }
1790
+ }
1791
+ }
1792
+ if (problems.length) {
1793
+ throw new relationship_config_error_default(problems);
1794
+ }
1795
+ }
1796
+
1582
1797
  // src/packages/database/initialize.ts
1583
1798
  async function initialize2(instance, opts) {
1584
- const { path: path6, models, logger, checkMigrations } = opts;
1585
- const config = Reflect.get(opts.config, NODE_ENV);
1799
+ const {
1800
+ path: path6,
1801
+ models,
1802
+ logger,
1803
+ checkMigrations,
1804
+ checkRelationships = true
1805
+ } = opts;
1806
+ const config = opts.config[NODE_ENV];
1586
1807
  if (!config) {
1587
1808
  throw new config_missing_error_default(NODE_ENV);
1588
1809
  }
@@ -1648,21 +1869,24 @@ async function initialize2(instance, opts) {
1648
1869
  (model2) => model2.initialize(instance, () => instance.connection(model2.tableName))
1649
1870
  )
1650
1871
  );
1872
+ if (checkRelationships) {
1873
+ validateRelationships(models.values());
1874
+ }
1651
1875
  return instance;
1652
1876
  }
1653
1877
 
1654
1878
  // src/packages/database/utils/normalize-model-name.ts
1655
- var import_inflection5 = require("inflection");
1656
- var normalize_model_name_default = compose(import_inflection5.singularize, import_inflection5.dasherize, underscore);
1879
+ var import_inflection6 = require("inflection");
1880
+ var normalize_model_name_default = compose(import_inflection6.singularize, import_inflection6.dasherize, underscore);
1657
1881
 
1658
1882
  // src/packages/database/model/index.ts
1659
- var import_inflection13 = require("inflection");
1883
+ var import_inflection15 = require("inflection");
1660
1884
 
1661
1885
  // src/packages/database/query/index.ts
1662
- var import_inflection8 = require("inflection");
1886
+ var import_inflection9 = require("inflection");
1663
1887
 
1664
1888
  // src/packages/database/query/utils/format-select.ts
1665
- var import_inflection6 = require("inflection");
1889
+ var import_inflection7 = require("inflection");
1666
1890
 
1667
1891
  // src/packages/database/query/errors/record-not-found-error.ts
1668
1892
  var RecordNotFoundError = class extends Error {
@@ -1672,24 +1896,27 @@ var RecordNotFoundError = class extends Error {
1672
1896
  );
1673
1897
  }
1674
1898
  };
1675
- var record_not_found_error_default = createServerError(RecordNotFoundError, 404);
1899
+ var record_not_found_error_default = createServerError(RecordNotFoundError, 404, { isPublic: true });
1676
1900
 
1677
1901
  // src/packages/database/query/runner/utils/build-results.ts
1678
- var import_inflection7 = require("inflection");
1902
+ var import_inflection8 = require("inflection");
1679
1903
 
1680
1904
  // src/packages/database/relationship/index.ts
1681
- var import_inflection10 = require("inflection");
1905
+ var import_inflection12 = require("inflection");
1682
1906
 
1683
1907
  // src/packages/database/relationship/utils/getters.ts
1684
- var import_inflection9 = require("inflection");
1908
+ var import_inflection10 = require("inflection");
1685
1909
 
1686
- // src/packages/database/model/initialize-class.ts
1910
+ // src/packages/database/relationship/utils/update-relationship.ts
1687
1911
  var import_inflection11 = require("inflection");
1688
1912
 
1913
+ // src/packages/database/model/initialize-class.ts
1914
+ var import_inflection13 = require("inflection");
1915
+
1689
1916
  // src/packages/server/utils/source-for.ts
1690
- var import_inflection12 = require("inflection");
1917
+ var import_inflection14 = require("inflection");
1691
1918
  function memberName(key) {
1692
- return (0, import_inflection12.dasherize)(underscore(key));
1919
+ return (0, import_inflection14.dasherize)(underscore(key));
1693
1920
  }
1694
1921
  function escapePointer(segment) {
1695
1922
  return segment.replace(/~/g, "~0").replace(/\//g, "~1");
@@ -1716,21 +1943,12 @@ var ValidationError = class extends Error {
1716
1943
  this.source = sourceFor(`data.attributes.${key}`);
1717
1944
  }
1718
1945
  };
1719
- var validation_error_default = createServerError(ValidationError, 422);
1946
+ var validation_error_default = createServerError(ValidationError, 422, { isPublic: true });
1720
1947
 
1721
1948
  // src/packages/database/migration/utils/generate-timestamp.ts
1722
- function formatInt(int) {
1723
- return (int / 10).toString().replace(".", "").substr(0, 2);
1724
- }
1725
- function* padding(char, amount) {
1726
- for (let i = 0; i < amount; i += 1) {
1727
- yield char;
1728
- }
1729
- }
1730
- function generateTimestamp() {
1731
- const now = /* @__PURE__ */ new Date();
1732
- const timestamp = now.toISOString().substr(0, 10).split("-").join("") + formatInt(now.getHours()) + formatInt(now.getMinutes()) + formatInt(now.getSeconds()) + formatInt(now.getMilliseconds());
1733
- return timestamp + Array.from(padding("0", 16 - timestamp.length)).join("");
1949
+ var pad2 = (int) => String(int).padStart(2, "0");
1950
+ function generateTimestamp(now = /* @__PURE__ */ new Date()) {
1951
+ return String(now.getUTCFullYear()) + pad2(now.getUTCMonth() + 1) + pad2(now.getUTCDate()) + pad2(now.getUTCHours()) + pad2(now.getUTCMinutes()) + pad2(now.getUTCSeconds()) + pad2(Math.floor(now.getUTCMilliseconds() / 10));
1734
1952
  }
1735
1953
 
1736
1954
  // src/packages/database/migration/index.ts
@@ -1761,11 +1979,11 @@ var database_default2 = Database;
1761
1979
 
1762
1980
  // src/packages/cli/templates/model.ts
1763
1981
  var import_os2 = require("os");
1764
- var import_inflection14 = require("inflection");
1982
+ var import_inflection16 = require("inflection");
1765
1983
  var VALID_ATTR = /^(\w|-)+:(\w|-)+$/;
1766
1984
  var RELATIONSHIP = /^belongs-to|has-(one|many)$/;
1767
1985
  var model_default2 = (name, attrs) => {
1768
- const normalized = chain(name).pipe(underscore).pipe(import_inflection14.classify).value();
1986
+ const normalized = chain(name).pipe(underscore).pipe(import_inflection16.classify).value();
1769
1987
  return template`
1770
1988
  import { Model } from 'lumen-framework';
1771
1989
 
@@ -1773,11 +1991,12 @@ var model_default2 = (name, attrs) => {
1773
1991
  ${entries(
1774
1992
  (attrs || []).filter((attr) => VALID_ATTR.test(attr)).map((attr) => attr.split(":")).filter(([, type]) => RELATIONSHIP.test(type)).reduce(
1775
1993
  (types, [related, type]) => {
1776
- const key = chain(type).pipe(underscore).pipe((str) => (0, import_inflection14.camelize)(str, true)).value();
1777
- const value = Reflect.get(types, key);
1994
+ const key = chain(type).pipe(underscore).pipe((str) => (0, import_inflection16.camelize)(str, true)).value();
1995
+ const value = types[key];
1778
1996
  if (value) {
1779
- const inverse = (0, import_inflection14.camelize)(normalized, true);
1780
- const relatedKey = chain(related).pipe(underscore).pipe((str) => (0, import_inflection14.camelize)(str, true)).value();
1997
+ const singular = (0, import_inflection16.camelize)(normalized, true);
1998
+ const inverse = key === "belongsTo" ? (0, import_inflection16.pluralize)(singular) : singular;
1999
+ const relatedKey = chain(related).pipe(underscore).pipe((str) => (0, import_inflection16.camelize)(str, true)).value();
1781
2000
  return {
1782
2001
  ...types,
1783
2002
  [key]: [
@@ -1812,11 +2031,11 @@ var model_default2 = (name, attrs) => {
1812
2031
  };
1813
2032
 
1814
2033
  // src/packages/cli/templates/serializer.ts
1815
- var import_inflection15 = require("inflection");
2034
+ var import_inflection17 = require("inflection");
1816
2035
  var serializer_default = (name, attrs) => {
1817
- let normalized = chain(name).pipe(underscore).pipe(import_inflection15.classify).value();
2036
+ let normalized = chain(name).pipe(underscore).pipe(import_inflection17.classify).value();
1818
2037
  if (!normalized.endsWith("Application")) {
1819
- normalized = (0, import_inflection15.pluralize)(normalized);
2038
+ normalized = (0, import_inflection17.pluralize)(normalized);
1820
2039
  }
1821
2040
  const body = entries(
1822
2041
  attrs.filter((attr) => /^(\w|-)+:(\w|-)+$/g.test(attr)).map((attr) => attr.split(":")).reduce(
@@ -1824,7 +2043,7 @@ var serializer_default = (name, attrs) => {
1824
2043
  const [, type] = parts;
1825
2044
  let [attr] = parts;
1826
2045
  let { hasOne, hasMany, attributes } = obj;
1827
- attr = `${indent(8)}'${(0, import_inflection15.camelize)(underscore(attr), true)}'`;
2046
+ attr = `${indent(8)}'${(0, import_inflection17.camelize)(underscore(attr), true)}'`;
1828
2047
  switch (type) {
1829
2048
  case "belongs-to":
1830
2049
  case "has-one":
@@ -1877,11 +2096,11 @@ ${indent(6)}];`;
1877
2096
  };
1878
2097
 
1879
2098
  // src/packages/cli/templates/controller.ts
1880
- var import_inflection16 = require("inflection");
2099
+ var import_inflection18 = require("inflection");
1881
2100
  var controller_default = (name, attrs) => {
1882
- let normalized = chain(name).pipe(underscore).pipe(import_inflection16.classify).value();
2101
+ let normalized = chain(name).pipe(underscore).pipe(import_inflection18.classify).value();
1883
2102
  if (!normalized.endsWith("Application")) {
1884
- normalized = (0, import_inflection16.pluralize)(normalized);
2103
+ normalized = (0, import_inflection18.pluralize)(normalized);
1885
2104
  }
1886
2105
  const body = entries(
1887
2106
  attrs.filter((attr) => /^(\w|-)+:(\w|-)+$/g.test(attr)).map((attr) => attr.split(":")[0]).reduce(
@@ -1889,7 +2108,7 @@ var controller_default = (name, attrs) => {
1889
2108
  ...obj,
1890
2109
  params: [
1891
2110
  ...obj.params,
1892
- `${indent(8)}'${(0, import_inflection16.camelize)(underscore(attr), true)}'`
2111
+ `${indent(8)}'${(0, import_inflection18.camelize)(underscore(attr), true)}'`
1893
2112
  ]
1894
2113
  }),
1895
2114
  { params: [] }
@@ -1931,10 +2150,10 @@ var empty_migration_default = () => template`
1931
2150
  `;
1932
2151
 
1933
2152
  // src/packages/cli/templates/model-migration.ts
1934
- var import_inflection17 = require("inflection");
2153
+ var import_inflection19 = require("inflection");
1935
2154
  var model_migration_default = (name, attrs) => {
1936
2155
  const indices = ["id"];
1937
- const table = chain(name).pipe((str) => str.substr(24)).pipe(underscore).pipe(import_inflection17.pluralize).value();
2156
+ const table = chain(name).pipe((str) => str.substr(24)).pipe(underscore).pipe(import_inflection19.pluralize).value();
1938
2157
  let body = "";
1939
2158
  if (Array.isArray(attrs)) {
1940
2159
  body = attrs.filter((attr) => /^(\w|-)+:(\w|-)+$/g.test(attr)).map((attr) => attr.split(":")).filter(([, type]) => !/^has-(one|many)$/g.test(type)).map((attr) => {
@@ -1975,9 +2194,9 @@ var model_migration_default = (name, attrs) => {
1975
2194
  };
1976
2195
 
1977
2196
  // src/packages/cli/templates/middleware.ts
1978
- var import_inflection18 = require("inflection");
2197
+ var import_inflection20 = require("inflection");
1979
2198
  var middleware_default = (name) => {
1980
- const normalized = chain(name).pipe(underscore).pipe((str) => (0, import_inflection18.camelize)(str, true)).value();
2199
+ const normalized = chain(name).pipe(underscore).pipe((str) => (0, import_inflection20.camelize)(str, true)).value();
1981
2200
  return template`
1982
2201
  export default function ${normalized}(/*request, response*/) {
1983
2202
 
@@ -1986,9 +2205,9 @@ var middleware_default = (name) => {
1986
2205
  };
1987
2206
 
1988
2207
  // src/packages/cli/templates/util.ts
1989
- var import_inflection19 = require("inflection");
2208
+ var import_inflection21 = require("inflection");
1990
2209
  var util_default = (name) => {
1991
- const normalized = chain(name).pipe(underscore).pipe((str) => (0, import_inflection19.camelize)(str, true)).value();
2210
+ const normalized = chain(name).pipe(underscore).pipe((str) => (0, import_inflection21.camelize)(str, true)).value();
1992
2211
  return template`
1993
2212
  export default function ${normalized}() {
1994
2213
 
@@ -2009,7 +2228,6 @@ function log(data) {
2009
2228
 
2010
2229
  // src/packages/cli/generator/utils/create-generator.ts
2011
2230
  var import_path11 = require("path");
2012
- var import_chalk7 = require("chalk");
2013
2231
  var FORWARD_SLASH = /\//g;
2014
2232
  function createGenerator({
2015
2233
  dir,
@@ -2019,18 +2237,18 @@ function createGenerator({
2019
2237
  return async ({ cwd, attrs, onConflict, ...opts }) => {
2020
2238
  const path6 = resolvePath(cwd, dir, `${opts.name}.js`);
2021
2239
  const name = opts.name.replace(FORWARD_SLASH, "-");
2022
- let action = (0, import_chalk7.green)("create");
2240
+ let action = chalk_default.green("create");
2023
2241
  await mkdirRec(path6.dir);
2024
2242
  if (await hasConflict(path6.absolute)) {
2025
2243
  const shouldContinue = await onConflict(path6.relative);
2026
2244
  if (shouldContinue && typeof shouldContinue === "string") {
2027
2245
  await rmrf_default((0, import_path11.join)(path6.dir, shouldContinue));
2028
- log(`${(0, import_chalk7.red)("remove")} ${(0, import_path11.join)(dir, shouldContinue)}`);
2246
+ log(`${chalk_default.red("remove")} ${(0, import_path11.join)(dir, shouldContinue)}`);
2029
2247
  } else if (shouldContinue && typeof shouldContinue === "boolean") {
2030
- action = (0, import_chalk7.yellow)("overwrite");
2248
+ action = chalk_default.yellow("overwrite");
2031
2249
  await rmrf_default(path6.absolute);
2032
2250
  } else {
2033
- log(`${(0, import_chalk7.yellow)("skip")} ${path6.relative}`);
2251
+ log(`${chalk_default.yellow("skip")} ${path6.relative}`);
2034
2252
  return;
2035
2253
  }
2036
2254
  }
@@ -2069,7 +2287,7 @@ async function controller(opts) {
2069
2287
  template: controller_default
2070
2288
  });
2071
2289
  if (!name.endsWith("application")) {
2072
- name = (0, import_inflection20.pluralize)(name);
2290
+ name = (0, import_inflection22.pluralize)(name);
2073
2291
  }
2074
2292
  await generate2({
2075
2293
  ...opts,
@@ -2100,7 +2318,7 @@ async function serializer(opts) {
2100
2318
  template: serializer_default
2101
2319
  });
2102
2320
  if (!name.endsWith("application")) {
2103
- name = (0, import_inflection20.pluralize)(name);
2321
+ name = (0, import_inflection22.pluralize)(name);
2104
2322
  }
2105
2323
  await generate2({
2106
2324
  ...opts,
@@ -2151,7 +2369,7 @@ function modelMigration(opts) {
2151
2369
  template: model_migration_default,
2152
2370
  hasConflict: detectConflict
2153
2371
  });
2154
- name = chain(name).pipe(import_path12.posix.basename).pipe(import_inflection20.pluralize).pipe((str) => `${generateTimestamp()}-create-${str}`).value();
2372
+ name = chain(name).pipe(import_path12.posix.basename).pipe(import_inflection22.pluralize).pipe((str) => `${generateTimestamp()}-create-${str}`).value();
2155
2373
  return generate2({
2156
2374
  ...opts,
2157
2375
  cwd,
@@ -2169,7 +2387,7 @@ async function model(opts) {
2169
2387
  template: model_default2
2170
2388
  });
2171
2389
  await modelMigration({ ...opts, name });
2172
- name = chain(name).pipe(import_path12.posix.basename).pipe(import_inflection20.singularize).value();
2390
+ name = chain(name).pipe(import_path12.posix.basename).pipe(import_inflection22.singularize).value();
2173
2391
  return generate2({
2174
2392
  ...opts,
2175
2393
  name
@@ -2219,20 +2437,20 @@ async function resource(opts) {
2219
2437
  `;
2220
2438
  }
2221
2439
  if (index + 1 === closeIndex) {
2222
- str += ` this.resource('${(0, import_inflection20.pluralize)(opts.name)}');
2440
+ str += ` this.resource('${(0, import_inflection22.pluralize)(opts.name)}');
2223
2441
  `;
2224
2442
  }
2225
2443
  return str;
2226
2444
  }, "")
2227
2445
  ).value();
2228
2446
  await writeFile(path6, routes);
2229
- log(`${(0, import_chalk8.green)("update")} app/routes.js`);
2447
+ log(`${chalk_default.green("update")} app/routes.js`);
2230
2448
  }
2231
2449
 
2232
2450
  // src/packages/cli/generator/utils/generator-for.ts
2233
2451
  function generatorFor(type) {
2234
2452
  const normalized = type.toLowerCase();
2235
- const generator = Reflect.get(generate_type_exports, normalized);
2453
+ const generator = generate_type_exports[normalized];
2236
2454
  if (!generator) {
2237
2455
  throw new Error(`Could not find a generator for '${type}'.`);
2238
2456
  }
@@ -2253,7 +2471,9 @@ async function runGenerator({
2253
2471
  type,
2254
2472
  name,
2255
2473
  attrs,
2256
- onConflict: (path6) => prompt.question(`${(0, import_chalk9.green)("?")} ${(0, import_chalk9.red)("Overwrite")} ${path6}? (Y/n)\r`)
2474
+ onConflict: (path6) => prompt.question(
2475
+ `${chalk_default.green("?")} ${chalk_default.red("Overwrite")} ${path6}? (Y/n)\r`
2476
+ )
2257
2477
  });
2258
2478
  prompt.close();
2259
2479
  }
@@ -2313,23 +2533,24 @@ async function create2(name, database) {
2313
2533
  writeFile(`${project}/LICENSE`, license_default()),
2314
2534
  writeFile(`${project}/package.json`, package_json_default(name, driver)),
2315
2535
  writeFile(`${project}/eslint.config.mjs`, eslint_config_default()),
2316
- writeFile(`${project}/.gitignore`, gitignore_default())
2536
+ writeFile(`${project}/.gitignore`, gitignore_default()),
2537
+ writeFile(`${project}/pnpm-workspace.yaml`, pnpm_workspace_default(driver))
2317
2538
  ]);
2318
2539
  const logOutput = template`
2319
- ${(0, import_chalk10.green)("create")} app/index.js
2320
- ${(0, import_chalk10.green)("create")} app/routes.js
2321
- ${(0, import_chalk10.green)("create")} bin/app.js
2322
- ${(0, import_chalk10.green)("create")} config/environments/development.js
2323
- ${(0, import_chalk10.green)("create")} config/environments/test.js
2324
- ${(0, import_chalk10.green)("create")} config/environments/production.js
2325
- ${(0, import_chalk10.green)("create")} config/database.js
2326
- ${(0, import_chalk10.green)("create")} db/migrate
2327
- ${(0, import_chalk10.green)("create")} db/seed.js
2328
- ${(0, import_chalk10.green)("create")} README.md
2329
- ${(0, import_chalk10.green)("create")} LICENSE
2330
- ${(0, import_chalk10.green)("create")} package.json
2331
- ${(0, import_chalk10.green)("create")} eslint.config.mjs
2332
- ${(0, import_chalk10.green)("create")} .gitignore
2540
+ ${chalk_default.green("create")} app/index.js
2541
+ ${chalk_default.green("create")} app/routes.js
2542
+ ${chalk_default.green("create")} config/environments/development.js
2543
+ ${chalk_default.green("create")} config/environments/test.js
2544
+ ${chalk_default.green("create")} config/environments/production.js
2545
+ ${chalk_default.green("create")} config/database.js
2546
+ ${chalk_default.green("create")} db/migrate
2547
+ ${chalk_default.green("create")} db/seed.js
2548
+ ${chalk_default.green("create")} README.md
2549
+ ${chalk_default.green("create")} LICENSE
2550
+ ${chalk_default.green("create")} package.json
2551
+ ${chalk_default.green("create")} eslint.config.mjs
2552
+ ${chalk_default.green("create")} .gitignore
2553
+ ${chalk_default.green("create")} pnpm-workspace.yaml
2333
2554
  `;
2334
2555
  process.stdout.write(logOutput.substr(0, logOutput.length - 1));
2335
2556
  process.stdout.write(import_os3.EOL);
@@ -2348,12 +2569,9 @@ async function create2(name, database) {
2348
2569
  await exec("git init && git add .", {
2349
2570
  cwd: project
2350
2571
  });
2351
- process.stdout.write(`${(0, import_chalk10.green)("initialize")} git`);
2572
+ process.stdout.write(`${chalk_default.green("initialize")} git`);
2352
2573
  process.stdout.write(import_os3.EOL);
2353
- const spinner = new import_ora2.default({
2354
- text: "Installing dependencies from npm...",
2355
- spinner: "dots"
2356
- });
2574
+ const spinner = createSpinner("Installing dependencies from npm...");
2357
2575
  spinner.start();
2358
2576
  await exec("npm install", {
2359
2577
  cwd: project
@@ -2362,7 +2580,7 @@ async function create2(name, database) {
2362
2580
  }
2363
2581
 
2364
2582
  // src/packages/cli/commands/dbcreate.ts
2365
- var import_os4 = require("os");
2583
+ var import_os5 = require("os");
2366
2584
 
2367
2585
  // src/utils/github.ts
2368
2586
  var GITHUB_URL = "https://github.com/nickschot/lux";
@@ -2410,12 +2628,14 @@ function createDefaultConfig() {
2410
2628
  server: {
2411
2629
  cors: {
2412
2630
  enabled: false
2413
- }
2631
+ },
2632
+ trustProxy: false
2414
2633
  },
2415
2634
  logging: {
2416
2635
  level: isProdENV ? "INFO" : "DEBUG",
2417
2636
  format: isProdENV ? "json" : "text",
2418
2637
  enabled: !isTestENV,
2638
+ requestBody: !isProdENV,
2419
2639
  filter: {
2420
2640
  params: []
2421
2641
  }
@@ -2424,14 +2644,11 @@ function createDefaultConfig() {
2424
2644
  }
2425
2645
 
2426
2646
  // src/utils/merge.ts
2427
- function hasOwnProperty2(target, key) {
2428
- return Reflect.apply(Object.prototype.hasOwnProperty, target, [key]);
2429
- }
2430
2647
  function merge(dest, source) {
2431
2648
  return entries(source).reduce(
2432
2649
  (result, [key, value]) => {
2433
- if (hasOwnProperty2(result, key) && isObject(value)) {
2434
- const currentValue = Reflect.get(result, key);
2650
+ if (Object.hasOwn(result, key) && isObject(value)) {
2651
+ const currentValue = result[key];
2435
2652
  if (isObject(currentValue)) {
2436
2653
  return {
2437
2654
  ...result,
@@ -2449,7 +2666,7 @@ function merge(dest, source) {
2449
2666
  }
2450
2667
 
2451
2668
  // src/packages/loader/utils/format-key.ts
2452
- var import_inflection21 = require("inflection");
2669
+ var import_inflection23 = require("inflection");
2453
2670
  var NAMESPACE_DELIMITER = /\$-/g;
2454
2671
  function formatKey(key, formatter) {
2455
2672
  return chain(key).pipe((str) => {
@@ -2457,7 +2674,7 @@ function formatKey(key, formatter) {
2457
2674
  return formatter(str);
2458
2675
  }
2459
2676
  return str;
2460
- }).pipe(underscore).pipe(import_inflection21.dasherize).pipe((str) => str.replace(NAMESPACE_DELIMITER, "/")).value();
2677
+ }).pipe(underscore).pipe(import_inflection23.dasherize).pipe((str) => str.replace(NAMESPACE_DELIMITER, "/")).value();
2461
2678
  }
2462
2679
 
2463
2680
  // src/packages/loader/utils/bundle-for.ts
@@ -2477,10 +2694,7 @@ function normalize(manifest) {
2477
2694
  break;
2478
2695
  case "Up":
2479
2696
  case "Down":
2480
- obj.migrations.set(
2481
- formatKey(key),
2482
- Reflect.construct(migration_default, [value])
2483
- );
2697
+ obj.migrations.set(formatKey(key), new migration_default(value));
2484
2698
  break;
2485
2699
  default:
2486
2700
  break;
@@ -2490,21 +2704,21 @@ function normalize(manifest) {
2490
2704
  case "Application":
2491
2705
  case "routes":
2492
2706
  case "seed":
2493
- Reflect.set(obj, formatKey(key), value);
2707
+ obj[formatKey(key)] = value;
2494
2708
  break;
2495
2709
  case "config":
2496
- Reflect.set(obj, "config", {
2710
+ obj.config = {
2497
2711
  ...merge(createDefaultConfig(), {
2498
2712
  ...obj.config,
2499
2713
  ...value
2500
2714
  })
2501
- });
2715
+ };
2502
2716
  break;
2503
2717
  case "database":
2504
- Reflect.set(obj, "config", {
2718
+ obj.config = {
2505
2719
  ...obj.config,
2506
2720
  database: value
2507
- });
2721
+ };
2508
2722
  break;
2509
2723
  default:
2510
2724
  obj.models.set(formatKey(key), value);
@@ -2523,10 +2737,8 @@ function normalize(manifest) {
2523
2737
  );
2524
2738
  }
2525
2739
  function bundleFor(path6) {
2526
- const manifest = Reflect.apply(require, null, [
2527
- (0, import_path13.join)(path6, "dist", "bundle")
2528
- ]);
2529
- return chain(manifest).pipe(normalize).pipe(entries).pipe((pairs) => new map_default(pairs)).value().freeze();
2740
+ const manifest = require((0, import_path13.join)(path6, "dist", "bundle"));
2741
+ return chain(manifest).pipe(normalize).pipe(entries).pipe((pairs2) => new map_default(pairs2)).value().freeze();
2530
2742
  }
2531
2743
 
2532
2744
  // src/packages/loader/index.ts
@@ -2540,57 +2752,87 @@ function createLoader(path6) {
2540
2752
  };
2541
2753
  }
2542
2754
 
2755
+ // src/packages/cli/utils/server-database.ts
2756
+ var import_os4 = require("os");
2757
+ function serverConfigFor(config) {
2758
+ return {
2759
+ ...config,
2760
+ pool: 1,
2761
+ database: config.driver === "pg" ? "postgres" : void 0
2762
+ };
2763
+ }
2764
+ function statementFor(connection, action, driver, database) {
2765
+ if (action === "create") {
2766
+ return connection.raw("CREATE DATABASE ??", [database]);
2767
+ }
2768
+ return connection.raw(
2769
+ driver === "pg" ? "DROP DATABASE IF EXISTS ?? WITH (FORCE)" : "DROP DATABASE IF EXISTS ??",
2770
+ [database]
2771
+ );
2772
+ }
2773
+ async function provision(path6, config, action) {
2774
+ const connection = connect(path6, serverConfigFor(config));
2775
+ try {
2776
+ const statement = statementFor(
2777
+ connection,
2778
+ action,
2779
+ config.driver,
2780
+ config.database
2781
+ );
2782
+ process.stdout.write(`${statement.toString()};${import_os4.EOL}`);
2783
+ await statement;
2784
+ } finally {
2785
+ await connection.destroy();
2786
+ }
2787
+ }
2788
+
2543
2789
  // src/packages/cli/commands/dbcreate.ts
2544
2790
  function dbcreate() {
2545
2791
  const load = createLoader(CWD);
2546
- const config = Reflect.get(load("config").database, NODE_ENV);
2792
+ const config = load("config").database[NODE_ENV];
2547
2793
  if (!config) {
2548
2794
  throw new database_config_missing_default(NODE_ENV);
2549
2795
  }
2550
- if (config.driver === "sqlite3") {
2796
+ if (config.driver === SQLITE_DRIVER) {
2551
2797
  return writeFile(`${CWD}/db/${config.database}_${NODE_ENV}.sqlite`, "");
2552
2798
  }
2553
2799
  if (DATABASE_URL || config.url) {
2554
2800
  process.stderr.write(CONNECTION_STRING_MESSAGE);
2555
- process.stderr.write(import_os4.EOL);
2801
+ process.stderr.write(import_os5.EOL);
2556
2802
  return Promise.resolve();
2557
2803
  }
2558
- const { schema } = connect(CWD, config);
2559
- const query = `CREATE DATABASE ${config.database}`;
2560
- const raw = schema.raw(query);
2561
- return raw.once("query", () => {
2562
- process.stdout.write(query);
2563
- process.stdout.write(import_os4.EOL);
2564
- });
2804
+ return provision(CWD, config, "create");
2565
2805
  }
2566
2806
 
2567
2807
  // src/packages/cli/commands/dbdrop.ts
2568
- var import_os5 = require("os");
2808
+ var import_os6 = require("os");
2569
2809
  function dbdrop() {
2570
2810
  const load = createLoader(CWD);
2571
- const config = Reflect.get(load("config").database, NODE_ENV);
2811
+ const config = load("config").database[NODE_ENV];
2572
2812
  if (!config) {
2573
2813
  throw new database_config_missing_default(NODE_ENV);
2574
2814
  }
2575
- if (config.driver === "sqlite3") {
2815
+ if (config.driver === SQLITE_DRIVER) {
2576
2816
  return rmrf_default(`${CWD}/db/${config.database}_${NODE_ENV}.sqlite`);
2577
2817
  }
2578
2818
  if (DATABASE_URL || config.url) {
2579
2819
  process.stderr.write(CONNECTION_STRING_MESSAGE);
2580
- process.stderr.write(import_os5.EOL);
2820
+ process.stderr.write(import_os6.EOL);
2581
2821
  return Promise.resolve();
2582
2822
  }
2583
- const { schema } = connect(CWD, config);
2584
- const query = `DROP DATABASE IF EXISTS ${config.database}`;
2585
- const raw = schema.raw(query);
2586
- return raw.once("query", () => {
2587
- process.stdout.write(query);
2588
- process.stdout.write(import_os5.EOL);
2823
+ return provision(CWD, config, "drop");
2824
+ }
2825
+
2826
+ // src/packages/cli/utils/print-statements.ts
2827
+ var import_os7 = require("os");
2828
+ function printStatements(builder, connection, write = (text) => process.stdout.write(text)) {
2829
+ builder.on("query", ({ sql, bindings }) => {
2830
+ write(`${connection.raw(sql, bindings ?? []).toString()};${import_os7.EOL}`);
2589
2831
  });
2832
+ return builder;
2590
2833
  }
2591
2834
 
2592
2835
  // src/packages/cli/commands/dbmigrate.ts
2593
- var import_os6 = require("os");
2594
2836
  async function dbmigrate() {
2595
2837
  const load = createLoader(CWD);
2596
2838
  const { database: config } = load("config");
@@ -2601,6 +2843,7 @@ async function dbmigrate() {
2601
2843
  models,
2602
2844
  path: CWD,
2603
2845
  checkMigrations: false,
2846
+ checkRelationships: false,
2604
2847
  logger: new logger_default({ enabled: false })
2605
2848
  });
2606
2849
  const pending = await pendingMigrations(CWD, () => connection("migrations"));
@@ -2610,11 +2853,7 @@ async function dbmigrate() {
2610
2853
  const key = name.replace(new RegExp(`${version2}-(.+)\\.js`), "$1");
2611
2854
  return [version2, migrations.get(`${key}-up`)];
2612
2855
  }).filter(([, migration2]) => Boolean(migration2)).reverse().map(([version2, migration2]) => () => {
2613
- const query = migration2.run(schema());
2614
- return query.on("query", () => {
2615
- process.stdout.write(sql`${query.toString()}`);
2616
- process.stdout.write(import_os6.EOL);
2617
- }).then(
2856
+ return printStatements(migration2.run(schema()), connection).then(
2618
2857
  () => connection("migrations").insert({
2619
2858
  version: version2
2620
2859
  })
@@ -2626,7 +2865,6 @@ async function dbmigrate() {
2626
2865
  }
2627
2866
 
2628
2867
  // src/packages/cli/commands/dbrollback.ts
2629
- var import_os7 = require("os");
2630
2868
  async function dbrollback() {
2631
2869
  const load = createLoader(CWD);
2632
2870
  const { database: config } = load("config");
@@ -2637,6 +2875,7 @@ async function dbrollback() {
2637
2875
  models,
2638
2876
  path: CWD,
2639
2877
  checkMigrations: false,
2878
+ checkRelationships: false,
2640
2879
  logger: new logger_default({ enabled: false })
2641
2880
  });
2642
2881
  const migrationFiles = await readdir(`${CWD}/db/migrate`);
@@ -2651,11 +2890,7 @@ async function dbrollback() {
2651
2890
  migration2 = target.replace(new RegExp(`${version2}-(.+)\\.js`), "$1");
2652
2891
  migration2 = migrations.get(`${migration2}-down`);
2653
2892
  if (migration2) {
2654
- const query = migration2.run(schema());
2655
- await query.on("query", () => {
2656
- process.stdout.write(sql`${query.toString()}`);
2657
- process.stdout.write(import_os7.EOL);
2658
- });
2893
+ await printStatements(migration2.run(schema()), connection);
2659
2894
  await connection("migrations").where({
2660
2895
  version: version2
2661
2896
  }).del();
@@ -2682,80 +2917,107 @@ function dbseed() {
2682
2917
 
2683
2918
  // src/packages/cli/commands/destroy.ts
2684
2919
  var import_os8 = require("os");
2685
- var import_chalk11 = require("chalk");
2686
- var import_inflection22 = require("inflection");
2687
- async function destroyType(type, name) {
2920
+ var import_path14 = require("path");
2921
+ var import_inflection24 = require("inflection");
2922
+ function log2(text) {
2923
+ process.stdout.write(text);
2924
+ process.stdout.write(import_os8.EOL);
2925
+ }
2926
+ async function destroyType(type, name, cwd = CWD) {
2688
2927
  const normalizedType = type.toLowerCase();
2689
2928
  let normalizedName = name;
2690
2929
  let path6;
2691
2930
  let migrations;
2692
2931
  switch (normalizedType) {
2693
2932
  case "model":
2694
- normalizedName = (0, import_inflection22.singularize)(normalizedName);
2695
- path6 = `app/${(0, import_inflection22.pluralize)(normalizedType)}/${normalizedName}.js`;
2933
+ normalizedName = (0, import_inflection24.singularize)(import_path14.posix.basename(normalizedName));
2934
+ path6 = `app/${(0, import_inflection24.pluralize)(normalizedType)}/${normalizedName}.js`;
2696
2935
  break;
2697
2936
  case "migration":
2698
- migrations = await readdir(`${CWD}/db/migrate`);
2937
+ migrations = await readdir(`${cwd}/db/migrate`);
2699
2938
  normalizedName = migrations.find(
2700
- (file) => `${normalizedName}.js` === file.substr(17)
2939
+ (file) => `${import_path14.posix.basename(normalizedName)}.js` === file.substring(17)
2701
2940
  );
2702
2941
  path6 = `db/migrate/${normalizedName}`;
2703
2942
  break;
2704
2943
  case "controller":
2705
2944
  case "serializer":
2706
- normalizedName = (0, import_inflection22.pluralize)(normalizedName);
2707
- path6 = `app/${(0, import_inflection22.pluralize)(normalizedType)}/${normalizedName}.js`;
2945
+ normalizedName = (0, import_inflection24.pluralize)(normalizedName);
2946
+ path6 = `app/${(0, import_inflection24.pluralize)(normalizedType)}/${normalizedName}.js`;
2708
2947
  break;
2709
2948
  case "middleware":
2710
2949
  path6 = `app/${normalizedType}/${normalizedName}.js`;
2711
2950
  break;
2712
2951
  case "util":
2713
- path6 = `app/${(0, import_inflection22.pluralize)(normalizedType)}/${normalizedName}.js`;
2952
+ path6 = `app/${(0, import_inflection24.pluralize)(normalizedType)}/${normalizedName}.js`;
2714
2953
  break;
2715
2954
  default:
2716
2955
  return;
2717
2956
  }
2718
- if (await exists(`${CWD}/${path6}`)) {
2719
- await rmrf_default(`${CWD}/${path6}`);
2720
- process.stdout.write(`${(0, import_chalk11.red)("remove")} ${path6}`);
2721
- process.stdout.write(import_os8.EOL);
2957
+ if (await exists(`${cwd}/${path6}`)) {
2958
+ await rmrf_default(`${cwd}/${path6}`);
2959
+ log2(`${chalk_default.red("remove")} ${path6}`);
2722
2960
  }
2723
2961
  }
2724
- async function destroy2({ type, name }) {
2962
+ async function otherControllersOf(name, cwd) {
2963
+ const dir = `${cwd}/app/controllers`;
2964
+ const own = `${(0, import_inflection24.pluralize)(name)}.js`;
2965
+ const file = import_path14.posix.basename(own);
2966
+ if (!await exists(dir)) {
2967
+ return [];
2968
+ }
2969
+ return (await readdirRec(dir)).map((path6) => path6.split("\\").join("/")).filter((path6) => import_path14.posix.basename(path6) === file && path6 !== own).map((path6) => `app/controllers/${path6}`);
2970
+ }
2971
+ async function destroy2({
2972
+ type,
2973
+ name,
2974
+ cwd = CWD
2975
+ }) {
2725
2976
  if (type === "resource") {
2726
- const routes = (await readFile(`${CWD}/app/routes.js`)).toString("utf8").split("\n").reduce((lines, line2) => {
2977
+ const model2 = (0, import_inflection24.singularize)(import_path14.posix.basename(name));
2978
+ const others = await otherControllersOf(name, cwd);
2979
+ await destroyType("controller", name, cwd);
2980
+ await destroyType("serializer", name, cwd);
2981
+ if (others.length) {
2982
+ log2(
2983
+ `${chalk_default.yellow("keep")} app/models/${model2}.js and its migration (used by ${others.join(", ")})`
2984
+ );
2985
+ } else {
2986
+ await destroyType("model", model2, cwd);
2987
+ await destroyType("migration", `create-${(0, import_inflection24.pluralize)(model2)}`, cwd);
2988
+ }
2989
+ if (import_path14.posix.dirname(name) === ".") {
2990
+ const path6 = `${cwd}/app/routes.js`;
2991
+ const before = (await readFile(path6)).toString("utf8");
2727
2992
  const pattern = new RegExp(
2728
- `\\s*this.resource\\(('|"|\`)${(0, import_inflection22.pluralize)(name)}('|"|\`)\\);?`
2993
+ `\\s*this.resource\\(('|"|\`)${(0, import_inflection24.pluralize)(name)}('|"|\`)\\);?`
2729
2994
  );
2730
- return pattern.test(line2) ? lines : [...lines, line2];
2731
- }, []).join("\n");
2732
- await Promise.all([
2733
- destroyType("model", name),
2734
- destroyType("migration", `create-${(0, import_inflection22.pluralize)(name)}`),
2735
- destroyType("serializer", name),
2736
- destroyType("controller", name)
2737
- ]);
2738
- await writeFile(`${CWD}/app/routes.js`, routes);
2739
- process.stdout.write(`${(0, import_chalk11.green)("update")} app/routes.js`);
2740
- process.stdout.write(import_os8.EOL);
2995
+ const after = before.split("\n").filter((line2) => !pattern.test(line2)).join("\n");
2996
+ if (after !== before) {
2997
+ await writeFile(path6, after);
2998
+ log2(`${chalk_default.green("update")} app/routes.js`);
2999
+ }
3000
+ }
2741
3001
  } else if (type === "model") {
2742
3002
  await Promise.all([
2743
- destroyType(type, name),
2744
- destroyType("migration", `create-${(0, import_inflection22.pluralize)(name)}`)
3003
+ destroyType(type, name, cwd),
3004
+ destroyType(
3005
+ "migration",
3006
+ `create-${(0, import_inflection24.pluralize)((0, import_inflection24.singularize)(import_path14.posix.basename(name)))}`,
3007
+ cwd
3008
+ )
2745
3009
  ]);
2746
3010
  } else {
2747
- await destroyType(type, name);
3011
+ await destroyType(type, name, cwd);
2748
3012
  }
2749
3013
  }
2750
3014
 
2751
3015
  // src/packages/cli/commands/repl.ts
2752
- var import_path14 = __toESM(require("path"));
3016
+ var import_path15 = __toESM(require("path"));
2753
3017
  var import_repl = require("repl");
2754
3018
  function repl() {
2755
3019
  return new Promise(async (resolve2) => {
2756
- const app = await Reflect.apply(require, null, [
2757
- import_path14.default.join(CWD, "dist", "boot")
2758
- ]);
3020
+ const app = await require(import_path15.default.join(CWD, "dist", "boot"));
2759
3021
  const instance = (0, import_repl.start)({
2760
3022
  prompt: "> "
2761
3023
  });
@@ -2790,15 +3052,11 @@ function repl() {
2790
3052
  });
2791
3053
  }
2792
3054
 
2793
- // src/packages/cli/commands/serve.ts
2794
- var import_chalk13 = require("chalk");
2795
-
2796
3055
  // src/packages/pm/cluster/index.ts
2797
3056
  var import_events2 = __toESM(require("events"));
2798
3057
  var import_os9 = __toESM(require("os"));
2799
3058
  var import_cluster2 = __toESM(require("cluster"));
2800
- var import_path15 = require("path");
2801
- var import_chalk12 = require("chalk");
3059
+ var import_path16 = require("path");
2802
3060
 
2803
3061
  // src/utils/range.ts
2804
3062
  function* range(start, end) {
@@ -2808,8 +3066,21 @@ function* range(start, end) {
2808
3066
  }
2809
3067
 
2810
3068
  // src/packages/pm/cluster/index.ts
3069
+ var BOOT_TIMEOUT = 3e4;
3070
+ var SHUTDOWN_TIMEOUT = 8e3;
3071
+ var WorkerBootError = class extends Error {
3072
+ constructor(pid, reason) {
3073
+ super(`Worker process ${pid} failed to start: ${reason}`);
3074
+ }
3075
+ };
2811
3076
  var Cluster = class extends import_events2.default {
2812
- constructor({ path: path6, port, logger, maxWorkers }) {
3077
+ constructor({
3078
+ path: path6,
3079
+ port,
3080
+ logger,
3081
+ maxWorkers,
3082
+ shutdownTimeout = SHUTDOWN_TIMEOUT
3083
+ }) {
2813
3084
  super();
2814
3085
  Object.defineProperties(this, {
2815
3086
  path: {
@@ -2841,128 +3112,219 @@ var Cluster = class extends import_events2.default {
2841
3112
  writable: false,
2842
3113
  enumerable: true,
2843
3114
  configurable: false
3115
+ },
3116
+ shutdownTimeout: {
3117
+ value: shutdownTimeout,
3118
+ writable: false,
3119
+ enumerable: true,
3120
+ configurable: false
3121
+ },
3122
+ retiring: {
3123
+ value: /* @__PURE__ */ new WeakSet(),
3124
+ writable: false,
3125
+ enumerable: false,
3126
+ configurable: false
3127
+ },
3128
+ stopping: {
3129
+ value: false,
3130
+ writable: true,
3131
+ enumerable: false,
3132
+ configurable: false
2844
3133
  }
2845
3134
  });
2846
3135
  import_cluster2.default.setupMaster({
2847
- exec: (0, import_path15.join)(path6, "dist", "boot.js")
3136
+ exec: (0, import_path16.join)(path6, "dist", "boot.js")
2848
3137
  });
2849
3138
  process.on("update", (changed) => {
2850
3139
  changed.forEach(({ name: filename }) => {
2851
- logger.info(`${(0, import_chalk12.green)("update")} ${filename}`);
3140
+ logger.info(`${chalk_default.green("update")} ${filename}`);
2852
3141
  });
2853
3142
  this.reload();
2854
3143
  });
2855
- this.forkAll().then(() => this.emit("ready"));
3144
+ this.forkAll().then(
3145
+ () => this.emit("ready"),
3146
+ (err) => {
3147
+ if (!this.stopping) {
3148
+ this.emit("error", err);
3149
+ }
3150
+ }
3151
+ );
2856
3152
  }
3153
+ /**
3154
+ * Fork a worker. Resolves with it once it is listening (or with `null`
3155
+ * when the cluster is full or stopping); rejects with a `WorkerBootError`
3156
+ * when it fails to start — it reports an error, exits, or does not listen
3157
+ * within `BOOT_TIMEOUT` twice in a row.
3158
+ */
2857
3159
  fork(retry = true) {
2858
- return new Promise((resolve2) => {
2859
- if (this.workers.size < this.maxWorkers) {
2860
- const worker = import_cluster2.default.fork({
2861
- NODE_ENV,
2862
- PORT: this.port
2863
- });
2864
- const timeout = setTimeout(() => {
2865
- this.logger.info(line`
2866
- Removing worker process: ${(0, import_chalk12.red)(`${worker.process.pid}`)}
2867
- `);
2868
- clearTimeout(timeout);
2869
- worker.removeAllListeners();
2870
- worker.kill();
2871
- this.workers.delete(worker);
2872
- resolve2(worker);
2873
- if (retry) {
2874
- this.fork(false);
2875
- }
2876
- }, 3e4);
2877
- const handleError = (err) => {
2878
- if (err) {
2879
- this.logger.error(err);
2880
- }
2881
- this.logger.info(line`
2882
- Removing worker process: ${(0, import_chalk12.red)(`${worker.process.pid}`)}
2883
- `);
3160
+ if (this.stopping || this.workers.size >= this.maxWorkers) {
3161
+ return Promise.resolve(null);
3162
+ }
3163
+ return new Promise((resolve2, reject) => {
3164
+ const worker = import_cluster2.default.fork({
3165
+ NODE_ENV,
3166
+ PORT: this.port
3167
+ });
3168
+ const { pid } = worker.process;
3169
+ let booted = false;
3170
+ const fail = (reason) => {
3171
+ clearTimeout(timeout);
3172
+ worker.removeAllListeners();
3173
+ worker.kill();
3174
+ this.logger.info(`Removing worker process: ${chalk_default.red(`${pid}`)}`);
3175
+ reject(new WorkerBootError(pid, reason));
3176
+ };
3177
+ const timeout = setTimeout(() => {
3178
+ if (retry) {
2884
3179
  clearTimeout(timeout);
2885
3180
  worker.removeAllListeners();
2886
3181
  worker.kill();
2887
- this.workers.delete(worker);
2888
- resolve2(worker);
2889
- };
2890
- worker.on("message", (msg) => {
2891
- let data = {};
2892
- let message = msg;
2893
- if (typeof message === "object") {
2894
- data = omit(message, "message");
2895
- message = message.message;
2896
- }
2897
- switch (message) {
2898
- case "ready":
2899
- this.logger.info(line`
2900
- Adding worker process: ${(0, import_chalk12.green)(`${worker.process.pid}`)}
2901
- `);
2902
- this.workers.add(worker);
2903
- clearTimeout(timeout);
2904
- worker.removeListener("error", handleError);
2905
- resolve2(worker);
2906
- break;
2907
- case "error":
2908
- handleError(data.error);
2909
- break;
2910
- default:
2911
- break;
2912
- }
2913
- });
2914
- worker.once("error", handleError);
2915
- worker.once("exit", (code) => {
2916
- const {
2917
- process: { pid }
2918
- } = worker;
2919
- if (typeof code === "number") {
3182
+ this.logger.info(`Removing worker process: ${chalk_default.red(`${pid}`)}`);
3183
+ this.fork(false).then(resolve2, reject);
3184
+ } else {
3185
+ fail(`not listening after ${BOOT_TIMEOUT / 1e3} s`);
3186
+ }
3187
+ }, BOOT_TIMEOUT);
3188
+ worker.on("message", (msg) => {
3189
+ let data = {};
3190
+ let message = msg;
3191
+ if (typeof message === "object") {
3192
+ data = omit(message, "message");
3193
+ message = message.message;
3194
+ }
3195
+ switch (message) {
3196
+ case "ready":
3197
+ booted = true;
3198
+ clearTimeout(timeout);
2920
3199
  this.logger.info(line`
2921
- Worker process: ${(0, import_chalk12.red)(`${pid}`)} exited with code ${code}
3200
+ Adding worker process: ${chalk_default.green(`${pid}`)}
2922
3201
  `);
2923
- }
2924
- this.logger.info(`Removing worker process: ${(0, import_chalk12.red)(`${pid}`)}`);
2925
- clearTimeout(timeout);
2926
- worker.removeAllListeners();
2927
- this.workers.delete(worker);
2928
- this.fork();
2929
- });
2930
- }
3202
+ this.workers.add(worker);
3203
+ resolve2(worker);
3204
+ break;
3205
+ case "error":
3206
+ if (data.error) {
3207
+ this.logger.error(data.error);
3208
+ }
3209
+ if (!booted) {
3210
+ fail("it reported an error while booting");
3211
+ }
3212
+ break;
3213
+ default:
3214
+ break;
3215
+ }
3216
+ });
3217
+ worker.once("error", (err) => {
3218
+ this.logger.error(err);
3219
+ if (!booted) {
3220
+ fail(err.message);
3221
+ }
3222
+ });
3223
+ worker.once("exit", (code, signal) => {
3224
+ clearTimeout(timeout);
3225
+ worker.removeAllListeners();
3226
+ this.workers.delete(worker);
3227
+ if (!booted) {
3228
+ reject(
3229
+ new WorkerBootError(
3230
+ pid,
3231
+ `it exited with ${code === null ? signal : `code ${code}`}`
3232
+ )
3233
+ );
3234
+ return;
3235
+ }
3236
+ if (this.retiring.has(worker)) {
3237
+ return;
3238
+ }
3239
+ this.logger.info(line`
3240
+ Worker process: ${chalk_default.red(`${pid}`)} exited with
3241
+ ${code === null ? `signal ${signal}` : `code ${code}`}
3242
+ `);
3243
+ this.logger.info(`Removing worker process: ${chalk_default.red(`${pid}`)}`);
3244
+ this.fork().catch((err) => this.replacementFailed(err));
3245
+ });
2931
3246
  });
2932
3247
  }
3248
+ /**
3249
+ * A replacement for a crashed worker did not start. With none left, the
3250
+ * application is down: stop, so the platform can restart it.
3251
+ */
3252
+ replacementFailed(err) {
3253
+ this.logger.error(err);
3254
+ if (!this.stopping && !this.workers.size) {
3255
+ this.emit("error", err);
3256
+ }
3257
+ }
3258
+ /**
3259
+ * Shut `worker` down gracefully: it stops accepting connections, finishes
3260
+ * the requests in flight, closes its database connections and exits. It is
3261
+ * killed if it has not exited after `shutdownTimeout`.
3262
+ */
2933
3263
  shutdown(worker) {
2934
3264
  return new Promise((resolve2) => {
2935
3265
  this.workers.delete(worker);
2936
- const timeout = setTimeout(() => worker.kill(), 5e3);
2937
- worker.once("disconnect", () => {
2938
- worker.kill();
2939
- });
2940
- worker.once("exit", () => {
3266
+ this.retiring.add(worker);
3267
+ if (worker.isDead()) {
2941
3268
  resolve2(worker);
3269
+ return;
3270
+ }
3271
+ const timeout = setTimeout(() => {
3272
+ this.logger.warn(line`
3273
+ Worker process: ${chalk_default.red(`${worker.process.pid}`)} did not exit
3274
+ within ${this.shutdownTimeout} ms; killing it.
3275
+ `);
3276
+ worker.kill("SIGKILL");
3277
+ }, this.shutdownTimeout);
3278
+ worker.once("exit", () => {
2942
3279
  clearTimeout(timeout);
3280
+ resolve2(worker);
2943
3281
  });
2944
- worker.send("shutdown");
2945
- worker.disconnect();
3282
+ if (worker.isConnected()) {
3283
+ worker.send("shutdown");
3284
+ } else {
3285
+ worker.kill();
3286
+ }
2946
3287
  });
2947
3288
  }
2948
- reload() {
2949
- if (this.workers.size) {
2950
- const groups = Array.from(this.workers).reduce((arr, item, idx, src) => {
2951
- if ((idx + 1) % 2) {
2952
- const group = src.slice(idx, idx + 2);
2953
- return [
2954
- ...arr,
2955
- () => Promise.all(group.map((worker) => this.shutdown(worker)))
2956
- ];
2957
- }
2958
- return arr;
2959
- }, []);
2960
- return composeAsync(...groups)();
3289
+ /**
3290
+ * Shut every worker down gracefully, booting ones included, and fork no
3291
+ * more. Resolves once all have exited.
3292
+ */
3293
+ async stop() {
3294
+ this.stopping = true;
3295
+ const workers = Object.values(import_cluster2.default.workers ?? {}).filter(
3296
+ (worker) => Boolean(worker)
3297
+ );
3298
+ await Promise.all(workers.map((worker) => this.shutdown(worker)));
3299
+ }
3300
+ /**
3301
+ * Replace every worker with one running the rebuilt application, two at a
3302
+ * time. A replacement that fails to start is logged, and the cluster keeps
3303
+ * watching: saving a fix reloads again.
3304
+ */
3305
+ async reload() {
3306
+ if (this.stopping) {
3307
+ return;
3308
+ }
3309
+ const workers = Array.from(this.workers);
3310
+ const failed = (err) => {
3311
+ this.logger.error(`${err.message}. Save a fix to reload.`);
3312
+ };
3313
+ if (!workers.length) {
3314
+ await this.forkAll().catch(failed);
3315
+ return;
3316
+ }
3317
+ for (let i = 0; i < workers.length; i += 2) {
3318
+ await Promise.all(
3319
+ workers.slice(i, i + 2).map(async (worker) => {
3320
+ await this.shutdown(worker);
3321
+ await this.fork().catch(failed);
3322
+ })
3323
+ );
2961
3324
  }
2962
- return this.fork();
2963
3325
  }
2964
3326
  forkAll() {
2965
- return Promise.race(
3327
+ return Promise.all(
2966
3328
  Array.from(range(1, this.maxWorkers)).map(() => this.fork())
2967
3329
  );
2968
3330
  }
@@ -2974,13 +3336,15 @@ function createCluster({
2974
3336
  path: path6,
2975
3337
  port,
2976
3338
  logger,
2977
- maxWorkers
3339
+ maxWorkers,
3340
+ shutdownTimeout
2978
3341
  }) {
2979
3342
  return new cluster_default({
2980
3343
  path: path6,
2981
3344
  port,
2982
3345
  logger,
2983
- maxWorkers
3346
+ maxWorkers,
3347
+ shutdownTimeout
2984
3348
  });
2985
3349
  }
2986
3350
 
@@ -2991,7 +3355,7 @@ async function serve({
2991
3355
  useStrict = false
2992
3356
  }) {
2993
3357
  const load = createLoader(CWD);
2994
- const { logging } = load("config");
3358
+ const { logging, server } = load("config");
2995
3359
  const logger = new logger_default(logging);
2996
3360
  if (hot) {
2997
3361
  const watcher = await watch(CWD);
@@ -3000,22 +3364,36 @@ async function serve({
3000
3364
  process.emit("update", changed);
3001
3365
  });
3002
3366
  }
3003
- createCluster({
3367
+ const pm = createCluster({
3004
3368
  logger,
3005
3369
  path: CWD,
3006
3370
  port: PORT,
3007
- maxWorkers: cluster3 ? void 0 : 1
3008
- }).once("ready", () => {
3009
- logger.info(`Lumen Server listening on port: ${(0, import_chalk13.cyan)(`${PORT}`)}`);
3371
+ maxWorkers: cluster3 ? void 0 : 1,
3372
+ shutdownTimeout: server?.shutdownTimeout
3373
+ });
3374
+ let stopping = false;
3375
+ const stop = async (code) => {
3376
+ if (stopping) {
3377
+ return;
3378
+ }
3379
+ stopping = true;
3380
+ await pm.stop();
3381
+ logger.info("Lumen Server stopped");
3382
+ process.exit(code);
3383
+ };
3384
+ pm.once("ready", () => {
3385
+ logger.info(`Lumen Server listening on port: ${chalk_default.cyan(`${PORT}`)}`);
3386
+ });
3387
+ pm.on("error", (err) => {
3388
+ logger.error(err.message);
3389
+ stop(1);
3390
+ });
3391
+ ["SIGTERM", "SIGINT"].forEach((signal) => {
3392
+ process.once(signal, () => {
3393
+ logger.info(`Received ${signal}; finishing requests in flight`);
3394
+ stop(0);
3395
+ });
3010
3396
  });
3011
- }
3012
-
3013
- // src/packages/cli/commands/test.ts
3014
- var import_os10 = require("os");
3015
- function test() {
3016
- process.stdout.write("Coming Soon!");
3017
- process.stdout.write(import_os10.EOL);
3018
- return Promise.resolve();
3019
3397
  }
3020
3398
  // Annotate the CommonJS export names for ESM import in node:
3021
3399
  0 && (module.exports = {
@@ -3029,7 +3407,6 @@ function test() {
3029
3407
  destroy,
3030
3408
  generate,
3031
3409
  repl,
3032
- serve,
3033
- test
3410
+ serve
3034
3411
  });
3035
3412
  //# sourceMappingURL=cli.cjs.map