equipped 5.3.0 → 5.3.2

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 (191) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/cjs/cache/adapters/in-memory/index.cjs +155 -155
  3. package/dist/cjs/cache/adapters/in-memory/index.cjs.map +1 -1
  4. package/dist/cjs/cache/adapters/in-memory/index.min.cjs +3 -3
  5. package/dist/cjs/cache/adapters/in-memory/index.min.cjs.map +1 -1
  6. package/dist/cjs/dbs/adapters/mongodb/index.cjs.map +1 -1
  7. package/dist/cjs/dbs/adapters/mongodb/index.min.cjs +2 -2
  8. package/dist/cjs/dbs/adapters/mongodb/index.min.cjs.map +1 -1
  9. package/dist/cjs/dbs/index.cjs +181 -179
  10. package/dist/cjs/dbs/index.cjs.map +1 -1
  11. package/dist/cjs/dbs/index.min.cjs +3 -3
  12. package/dist/cjs/dbs/index.min.cjs.map +1 -1
  13. package/dist/cjs/events/adapters/kafka/index.cjs.map +1 -1
  14. package/dist/cjs/events/adapters/kafka/index.min.cjs +2 -2
  15. package/dist/cjs/events/adapters/kafka/index.min.cjs.map +1 -1
  16. package/dist/cjs/events/adapters/rabbitmq/index.cjs +179 -179
  17. package/dist/cjs/events/adapters/rabbitmq/index.cjs.map +1 -1
  18. package/dist/cjs/events/adapters/rabbitmq/index.min.cjs +3 -3
  19. package/dist/cjs/events/adapters/rabbitmq/index.min.cjs.map +1 -1
  20. package/dist/cjs/orm/adapters/mongodb/index.cjs +144 -144
  21. package/dist/cjs/orm/adapters/mongodb/index.cjs.map +1 -1
  22. package/dist/cjs/orm/adapters/mongodb/index.min.cjs +1 -1
  23. package/dist/cjs/orm/adapters/mongodb/index.min.cjs.map +1 -1
  24. package/dist/cjs/orm/adapters/postgresql/index.cjs +144 -215
  25. package/dist/cjs/orm/adapters/postgresql/index.cjs.map +1 -1
  26. package/dist/cjs/orm/adapters/postgresql/index.min.cjs +11 -11
  27. package/dist/cjs/orm/adapters/postgresql/index.min.cjs.map +1 -1
  28. package/dist/cjs/server/adapters/express/index.cjs +338 -226
  29. package/dist/cjs/server/adapters/express/index.cjs.map +1 -1
  30. package/dist/cjs/server/adapters/express/index.min.cjs +7 -7
  31. package/dist/cjs/server/adapters/express/index.min.cjs.map +1 -1
  32. package/dist/cjs/server/adapters/fastify/index.cjs +323 -211
  33. package/dist/cjs/server/adapters/fastify/index.cjs.map +1 -1
  34. package/dist/cjs/server/adapters/fastify/index.min.cjs +7 -7
  35. package/dist/cjs/server/adapters/fastify/index.min.cjs.map +1 -1
  36. package/dist/cjs/server/index.cjs +128 -16
  37. package/dist/cjs/server/index.cjs.map +1 -1
  38. package/dist/cjs/server/index.min.cjs +6 -6
  39. package/dist/cjs/server/index.min.cjs.map +1 -1
  40. package/dist/cjs/utilities/index.cjs +0 -5
  41. package/dist/cjs/utilities/index.cjs.map +1 -1
  42. package/dist/cjs/utilities/index.min.cjs +2 -2
  43. package/dist/cjs/utilities/index.min.cjs.map +1 -1
  44. package/dist/esm/cache/adapters/in-memory/index.min.mjs +1 -1
  45. package/dist/esm/cache/adapters/in-memory/index.min.mjs.map +1 -1
  46. package/dist/esm/cache/adapters/in-memory/index.mjs +6 -3
  47. package/dist/esm/cache/adapters/in-memory/index.mjs.map +1 -1
  48. package/dist/esm/cache/adapters/redis/index.min.mjs +1 -1
  49. package/dist/esm/cache/adapters/redis/index.mjs +5 -3
  50. package/dist/esm/cache/index.min.mjs +1 -1
  51. package/dist/esm/cache/index.mjs +2 -2
  52. package/dist/esm/{chunk-PNZNNUMN.mjs → chunk-26BOI6IL.mjs} +7 -5
  53. package/dist/esm/{chunk-PNZNNUMN.mjs.map → chunk-26BOI6IL.mjs.map} +1 -1
  54. package/dist/esm/{chunk-4JLDXF7T.min.mjs → chunk-AODOD22O.min.mjs} +2 -2
  55. package/dist/esm/chunk-APSDCRY5.min.mjs +32 -0
  56. package/dist/esm/chunk-APSDCRY5.min.mjs.map +1 -0
  57. package/dist/esm/{chunk-MDQKODYQ.mjs → chunk-CEYT5YBA.mjs} +204 -348
  58. package/dist/esm/chunk-CEYT5YBA.mjs.map +1 -0
  59. package/dist/esm/chunk-CU2HU5UE.min.mjs +2 -0
  60. package/dist/esm/chunk-CU2HU5UE.min.mjs.map +1 -0
  61. package/dist/esm/chunk-DAK7OKSH.min.mjs +2 -0
  62. package/dist/esm/{chunk-SGZNOYC3.min.mjs.map → chunk-DAK7OKSH.min.mjs.map} +1 -1
  63. package/dist/esm/{chunk-PCAUXBAG.min.mjs → chunk-DAUQA32Q.min.mjs} +2 -2
  64. package/dist/esm/{chunk-JH6EBXY4.mjs → chunk-FQYRVHQT.mjs} +2 -2
  65. package/dist/esm/chunk-NEF3UYFA.mjs +144 -0
  66. package/dist/esm/chunk-NEF3UYFA.mjs.map +1 -0
  67. package/dist/esm/chunk-NNFUYHQ7.mjs +135 -0
  68. package/dist/esm/chunk-NNFUYHQ7.mjs.map +1 -0
  69. package/dist/esm/{chunk-4MXYZS5R.mjs → chunk-PCEHFZ6G.mjs} +9 -5
  70. package/dist/esm/{chunk-4MXYZS5R.mjs.map → chunk-PCEHFZ6G.mjs.map} +1 -1
  71. package/dist/esm/{chunk-B5QQ32YY.min.mjs → chunk-QCCDJLBH.min.mjs} +2 -2
  72. package/dist/esm/{chunk-PQCBVQL6.mjs → chunk-RIXTQQUU.mjs} +2 -2
  73. package/dist/esm/{chunk-Z2TZ3KR7.mjs → chunk-RPPYCWXW.mjs} +2 -2
  74. package/dist/esm/{chunk-AKQBDYUO.mjs → chunk-T2O72JBW.mjs} +3 -3
  75. package/dist/esm/{chunk-TVTYDU2D.mjs → chunk-VQO4MSEC.mjs} +5 -5
  76. package/dist/esm/{chunk-ZGMFFK6Y.min.mjs → chunk-X7VNAGLR.min.mjs} +2 -2
  77. package/dist/esm/chunk-XTXAEBXY.min.mjs +2 -0
  78. package/dist/esm/{chunk-5VW3JAFR.min.mjs.map → chunk-XTXAEBXY.min.mjs.map} +1 -1
  79. package/dist/esm/{chunk-QHLKB47N.min.mjs → chunk-YDQW6ETS.min.mjs} +2 -2
  80. package/dist/esm/chunk-ZDBDR3IB.min.mjs +2 -0
  81. package/dist/esm/chunk-ZDBDR3IB.min.mjs.map +1 -0
  82. package/dist/esm/dbs/adapters/mongodb/index.min.mjs +1 -1
  83. package/dist/esm/dbs/adapters/mongodb/index.min.mjs.map +1 -1
  84. package/dist/esm/dbs/adapters/mongodb/index.mjs +8 -5
  85. package/dist/esm/dbs/adapters/mongodb/index.mjs.map +1 -1
  86. package/dist/esm/dbs/index.min.mjs +1 -1
  87. package/dist/esm/dbs/index.mjs +5 -3
  88. package/dist/esm/errors/index.min.mjs +1 -1
  89. package/dist/esm/errors/index.mjs +1 -1
  90. package/dist/esm/events/adapters/kafka/index.min.mjs +1 -1
  91. package/dist/esm/events/adapters/kafka/index.mjs +4 -2
  92. package/dist/esm/events/adapters/rabbitmq/index.min.mjs +1 -1
  93. package/dist/esm/events/adapters/rabbitmq/index.min.mjs.map +1 -1
  94. package/dist/esm/events/adapters/rabbitmq/index.mjs +7 -3
  95. package/dist/esm/events/adapters/rabbitmq/index.mjs.map +1 -1
  96. package/dist/esm/index.min.mjs +1 -1
  97. package/dist/esm/index.mjs +1 -1
  98. package/dist/esm/instance/index.min.mjs +1 -1
  99. package/dist/esm/instance/index.mjs +1 -1
  100. package/dist/esm/{introspection-32BRYKYY.mjs → introspection-FGDVYTPP.mjs} +3 -3
  101. package/dist/esm/introspection-UDWDS6DV.min.mjs +2 -0
  102. package/dist/esm/jobs/adapters/redis/index.min.mjs +1 -1
  103. package/dist/esm/jobs/adapters/redis/index.min.mjs.map +1 -1
  104. package/dist/esm/jobs/adapters/redis/index.mjs +10 -6
  105. package/dist/esm/jobs/adapters/redis/index.mjs.map +1 -1
  106. package/dist/esm/orm/adapters/in-memory/index.min.mjs +1 -1
  107. package/dist/esm/orm/adapters/in-memory/index.mjs +4 -3
  108. package/dist/esm/orm/adapters/json/index.min.mjs +1 -1
  109. package/dist/esm/orm/adapters/json/index.min.mjs.map +1 -1
  110. package/dist/esm/orm/adapters/json/index.mjs +6 -5
  111. package/dist/esm/orm/adapters/json/index.mjs.map +1 -1
  112. package/dist/esm/orm/adapters/mongodb/index.min.mjs +1 -1
  113. package/dist/esm/orm/adapters/mongodb/index.min.mjs.map +1 -1
  114. package/dist/esm/orm/adapters/mongodb/index.mjs +6 -4
  115. package/dist/esm/orm/adapters/mongodb/index.mjs.map +1 -1
  116. package/dist/esm/orm/adapters/postgresql/index.min.mjs +2 -2
  117. package/dist/esm/orm/adapters/postgresql/index.min.mjs.map +1 -1
  118. package/dist/esm/orm/adapters/postgresql/index.mjs +8 -5
  119. package/dist/esm/orm/adapters/postgresql/index.mjs.map +1 -1
  120. package/dist/esm/orm/index.min.mjs +1 -1
  121. package/dist/esm/orm/index.mjs +3 -3
  122. package/dist/esm/server/adapters/express/index.min.mjs +1 -1
  123. package/dist/esm/server/adapters/express/index.min.mjs.map +1 -1
  124. package/dist/esm/server/adapters/express/index.mjs +7 -3
  125. package/dist/esm/server/adapters/express/index.mjs.map +1 -1
  126. package/dist/esm/server/adapters/fastify/index.min.mjs +1 -1
  127. package/dist/esm/server/adapters/fastify/index.min.mjs.map +1 -1
  128. package/dist/esm/server/adapters/fastify/index.mjs +7 -3
  129. package/dist/esm/server/adapters/fastify/index.mjs.map +1 -1
  130. package/dist/esm/server/index.min.mjs +1 -1
  131. package/dist/esm/server/index.mjs +1 -1
  132. package/dist/esm/utilities/index.min.mjs +1 -1
  133. package/dist/esm/utilities/index.mjs +5 -4
  134. package/dist/esm/validations/index.min.mjs +1 -1
  135. package/dist/esm/validations/index.mjs +1 -1
  136. package/dist/types/{base-DVr0fHil.d.ts → base-qDndMBf8.d.ts} +9 -3
  137. package/dist/types/cache/adapters/in-memory/index.js +7 -4
  138. package/dist/types/cache/adapters/redis/index.js +5 -3
  139. package/dist/types/cache/index.js +2 -2
  140. package/dist/types/{chunk-NJ2FHI3U.js → chunk-35CDCTLR.js} +4 -4
  141. package/dist/types/{chunk-XGGGAEET.js → chunk-4YQA5GPT.js} +8 -4
  142. package/dist/types/{chunk-65EHKCDZ.js → chunk-AQIV47BY.js} +6 -4
  143. package/dist/types/{chunk-YTY3EFNB.js → chunk-BKDWUJRG.js} +203 -347
  144. package/dist/types/{chunk-JQ2FWX4W.js → chunk-DA2VYP7D.js} +1 -1
  145. package/dist/types/{chunk-3TKJGSCZ.js → chunk-GDMGPX4C.js} +1 -1
  146. package/dist/types/chunk-H5V6ADFD.js +134 -0
  147. package/dist/types/{chunk-3M4PBZAQ.js → chunk-OOKJEMQI.js} +2 -2
  148. package/dist/types/chunk-PPD4WDG6.js +143 -0
  149. package/dist/types/{chunk-OHFTQGQG.js → chunk-T7K7PO6G.js} +1 -1
  150. package/dist/types/dbs/adapters/mongodb/index.js +8 -5
  151. package/dist/types/dbs/index.js +5 -3
  152. package/dist/types/errors/index.js +1 -1
  153. package/dist/types/events/adapters/kafka/index.js +4 -2
  154. package/dist/types/events/adapters/rabbitmq/index.js +7 -3
  155. package/dist/types/index.js +1 -1
  156. package/dist/types/instance/index.js +1 -1
  157. package/dist/types/{introspection-VOIU3XE6.js → introspection-FSNS33CQ.js} +2 -2
  158. package/dist/types/jobs/adapters/redis/index.js +10 -6
  159. package/dist/types/orm/adapters/in-memory/index.js +4 -3
  160. package/dist/types/orm/adapters/json/index.js +6 -5
  161. package/dist/types/orm/adapters/mongodb/index.js +6 -4
  162. package/dist/types/orm/adapters/postgresql/index.js +8 -5
  163. package/dist/types/orm/index.js +3 -3
  164. package/dist/types/server/adapters/express/index.d.ts +1 -1
  165. package/dist/types/server/adapters/express/index.js +7 -3
  166. package/dist/types/server/adapters/fastify/index.d.ts +1 -1
  167. package/dist/types/server/adapters/fastify/index.js +7 -3
  168. package/dist/types/server/index.d.ts +2 -2
  169. package/dist/types/server/index.js +1 -1
  170. package/dist/types/utilities/index.d.ts +1 -2
  171. package/dist/types/utilities/index.js +5 -4
  172. package/dist/types/validations/index.js +1 -1
  173. package/package.json +1 -1
  174. package/dist/esm/chunk-5VW3JAFR.min.mjs +0 -2
  175. package/dist/esm/chunk-FLAPYQUT.min.mjs +0 -32
  176. package/dist/esm/chunk-FLAPYQUT.min.mjs.map +0 -1
  177. package/dist/esm/chunk-MDQKODYQ.mjs.map +0 -1
  178. package/dist/esm/chunk-SGZNOYC3.min.mjs +0 -2
  179. package/dist/esm/introspection-A72WMWE3.min.mjs +0 -2
  180. /package/dist/esm/{chunk-4JLDXF7T.min.mjs.map → chunk-AODOD22O.min.mjs.map} +0 -0
  181. /package/dist/esm/{chunk-PCAUXBAG.min.mjs.map → chunk-DAUQA32Q.min.mjs.map} +0 -0
  182. /package/dist/esm/{chunk-JH6EBXY4.mjs.map → chunk-FQYRVHQT.mjs.map} +0 -0
  183. /package/dist/esm/{chunk-B5QQ32YY.min.mjs.map → chunk-QCCDJLBH.min.mjs.map} +0 -0
  184. /package/dist/esm/{chunk-PQCBVQL6.mjs.map → chunk-RIXTQQUU.mjs.map} +0 -0
  185. /package/dist/esm/{chunk-Z2TZ3KR7.mjs.map → chunk-RPPYCWXW.mjs.map} +0 -0
  186. /package/dist/esm/{chunk-AKQBDYUO.mjs.map → chunk-T2O72JBW.mjs.map} +0 -0
  187. /package/dist/esm/{chunk-TVTYDU2D.mjs.map → chunk-VQO4MSEC.mjs.map} +0 -0
  188. /package/dist/esm/{chunk-ZGMFFK6Y.min.mjs.map → chunk-X7VNAGLR.min.mjs.map} +0 -0
  189. /package/dist/esm/{chunk-QHLKB47N.min.mjs.map → chunk-YDQW6ETS.min.mjs.map} +0 -0
  190. /package/dist/esm/{introspection-32BRYKYY.mjs.map → introspection-FGDVYTPP.mjs.map} +0 -0
  191. /package/dist/esm/{introspection-A72WMWE3.min.mjs.map → introspection-UDWDS6DV.min.mjs.map} +0 -0
@@ -46,7 +46,7 @@ var import_pino_http = require("pino-http");
46
46
  // src/instance/index.ts
47
47
  var import_pino = __toESM(require("pino"), 1);
48
48
  var import_ulid = require("ulid");
49
- var import_valleyed9 = require("valleyed");
49
+ var import_valleyed8 = require("valleyed");
50
50
 
51
51
  // src/errors/equippedError.ts
52
52
  var EquippedError = class extends Error {
@@ -73,7 +73,7 @@ var import_cors = require("@fastify/cors");
73
73
  var import_cors2 = require("cors");
74
74
  var import_socket = require("socket.io");
75
75
  var import_supertest = __toESM(require("supertest"), 1);
76
- var import_valleyed6 = require("valleyed");
76
+ var import_valleyed5 = require("valleyed");
77
77
 
78
78
  // src/events/adapters/base.ts
79
79
  var EventBus = class {
@@ -426,170 +426,6 @@ var openapi = (config) => {
426
426
  };
427
427
  };
428
428
 
429
- // src/utilities/configurable.ts
430
- var import_valleyed5 = require("valleyed");
431
- function configurable(pipeFn, base) {
432
- const pipe = pipeFn();
433
- import_valleyed5.v.compile(pipe);
434
- class Configurable extends base {
435
- constructor(config, ...baseArgs) {
436
- super(...baseArgs);
437
- this.config = config;
438
- }
439
- static create(input, ...args) {
440
- const r = import_valleyed5.v.validate(pipe, input);
441
- if (!r.valid) throw r.error;
442
- return new this(r.value, ...args);
443
- }
444
- }
445
- return Configurable;
446
- }
447
- if (void 0) {
448
- const { describe, test, expect, expectTypeOf } = void 0;
449
- const { v: v8 } = null;
450
- const testPipe = () => v8.object({
451
- host: v8.string(),
452
- port: v8.number()
453
- });
454
- class TestBase {
455
- baseValue;
456
- constructor() {
457
- this.baseValue = "base";
458
- }
459
- }
460
- class TestBaseWithArgs {
461
- label;
462
- constructor(label) {
463
- this.label = label;
464
- }
465
- }
466
- describe("configurable", () => {
467
- test("validation runs in static create before constructor body executes", () => {
468
- let constructorRan = false;
469
- const Wrapped = configurable(testPipe, TestBase);
470
- class MyClass extends Wrapped {
471
- constructor(config) {
472
- super(config);
473
- constructorRan = true;
474
- }
475
- }
476
- expect(() => MyClass.create({ host: 123, port: "bad" })).toThrow();
477
- expect(constructorRan).toBe(false);
478
- MyClass.create({ host: "localhost", port: 3e3 });
479
- expect(constructorRan).toBe(true);
480
- });
481
- test("constructor receives validated value", () => {
482
- const Wrapped = configurable(testPipe, TestBase);
483
- let receivedConfig;
484
- class MyClass extends Wrapped {
485
- constructor(config) {
486
- super(config);
487
- receivedConfig = this.config;
488
- }
489
- }
490
- MyClass.create({ host: "localhost", port: 3e3 });
491
- expect(receivedConfig).toEqual({ host: "localhost", port: 3e3 });
492
- });
493
- test("external new is a compile error", () => {
494
- const Wrapped = configurable(testPipe, TestBase);
495
- class MyClass extends Wrapped {
496
- constructor(config) {
497
- super(config);
498
- }
499
- }
500
- void (() => new MyClass({ host: "localhost", port: 3e3 }));
501
- });
502
- test("static Config resolves to PipeOutput<P> at the type level", () => {
503
- const Wrapped = configurable(testPipe, TestBase);
504
- class MyClass extends Wrapped {
505
- constructor(config) {
506
- super(config);
507
- }
508
- }
509
- expectTypeOf().toEqualTypeOf();
510
- expect(MyClass.create).toBeTypeOf("function");
511
- });
512
- test("ConstructorParameters<This>-based extras inference works for non-zero-arg leaf signatures", () => {
513
- const Wrapped = configurable(testPipe, TestBase);
514
- class MyClass extends Wrapped {
515
- extra;
516
- constructor(config, extra) {
517
- super(config);
518
- this.extra = extra;
519
- }
520
- }
521
- const instance = MyClass.create({ host: "localhost", port: 3e3 }, 42);
522
- expect(instance.extra).toBe(42);
523
- expectTypeOf(instance).toHaveProperty("extra");
524
- expectTypeOf(instance.extra).toEqualTypeOf();
525
- void (() => MyClass.create({ host: "localhost", port: 3e3 }, "not-a-number"));
526
- });
527
- test("base-args forwarding works for non-zero-arg bases", () => {
528
- const Wrapped = configurable(testPipe, TestBaseWithArgs);
529
- class MyClass extends Wrapped {
530
- constructor(config, label) {
531
- super(config, label);
532
- }
533
- }
534
- const instance = MyClass.create({ host: "localhost", port: 3e3 }, "test-label");
535
- expect(instance.label).toBe("test-label");
536
- });
537
- test("config is accessible as protected readonly on instances", () => {
538
- const Wrapped = configurable(testPipe, TestBase);
539
- class MyClass extends Wrapped {
540
- constructor(config) {
541
- super(config);
542
- }
543
- getHost() {
544
- return this.config.host;
545
- }
546
- }
547
- const instance = MyClass.create({ host: "localhost", port: 3e3 });
548
- expect(instance.getHost()).toBe("localhost");
549
- });
550
- test("accepts an abstract base class", () => {
551
- class AbstractBase {
552
- }
553
- const Wrapped = configurable(testPipe, AbstractBase);
554
- class Concrete extends Wrapped {
555
- constructor(config) {
556
- super(config);
557
- }
558
- greet() {
559
- return `hello from ${this.config.host}`;
560
- }
561
- }
562
- const instance = Concrete.create({ host: "localhost", port: 3e3 });
563
- expect(instance.greet()).toBe("hello from localhost");
564
- expectTypeOf(instance).toHaveProperty("config");
565
- });
566
- });
567
- }
568
-
569
- // src/utilities/json.ts
570
- var parseJSONValue = (data) => {
571
- try {
572
- if (data?.constructor?.name !== "String") return data;
573
- return JSON.parse(data);
574
- } catch {
575
- return data;
576
- }
577
- };
578
- function parseJSONObject(data) {
579
- return Object.fromEntries(Object.entries(data).map(([key, value]) => [key, parseJSONValue(value)]));
580
- }
581
-
582
- // src/utilities/media.ts
583
- var import_music_metadata = require("music-metadata");
584
- var getMediaDuration = async (buffer) => {
585
- try {
586
- const meta = await (0, import_music_metadata.parseBuffer)(buffer);
587
- return meta.format.duration ?? 0;
588
- } catch {
589
- return 0;
590
- }
591
- };
592
-
593
429
  // src/server/requests.ts
594
430
  var Request = class {
595
431
  ip;
@@ -618,8 +454,10 @@ var Request = class {
618
454
  this.params = params;
619
455
  this.cookies = cookies;
620
456
  this.headers = headers;
621
- this.query = parseJSONObject(query);
622
- this.body = Object.assign(parseJSONObject(body), files);
457
+ const hasFiles = Object.keys(files).length > 0;
458
+ const bodyFields = body && typeof body === "object" && !Array.isArray(body) ? body : {};
459
+ this.query = query;
460
+ this.body = hasFiles ? Object.assign({}, bodyFields, files) : body;
623
461
  }
624
462
  pipe(stream, opts = {}) {
625
463
  return new Response({ ...opts, piped: true, body: stream });
@@ -665,6 +503,83 @@ var Response = class {
665
503
  });
666
504
  }
667
505
  };
506
+ if (void 0) {
507
+ const { describe, expect, test } = void 0;
508
+ describe("Request", () => {
509
+ test("preserves adapter-provided query and body values without JSON normalization", () => {
510
+ const query = {
511
+ count: "10",
512
+ active: "true",
513
+ filter: '{"name":"test"}',
514
+ quoted: '"value"'
515
+ };
516
+ const body = {
517
+ count: "10",
518
+ active: "false",
519
+ payload: '{"nested":true}',
520
+ quoted: '"value"'
521
+ };
522
+ const request = new Request({
523
+ ip: void 0,
524
+ body,
525
+ cookies: {},
526
+ params: {},
527
+ query,
528
+ method: "post",
529
+ path: "/items",
530
+ headers: {},
531
+ files: {}
532
+ });
533
+ expect(request.query).toEqual(query);
534
+ expect(request.body).toEqual(body);
535
+ });
536
+ test("continues merging uploaded files into body fields without parsing string fields", () => {
537
+ const file = {
538
+ name: "avatar.png",
539
+ type: "image/png",
540
+ size: 4,
541
+ isTruncated: false,
542
+ data: Buffer.from("file"),
543
+ duration: 0
544
+ };
545
+ const body = { metadata: '{"alt":"avatar"}' };
546
+ const request = new Request({
547
+ ip: void 0,
548
+ body,
549
+ cookies: {},
550
+ params: {},
551
+ query: {},
552
+ method: "post",
553
+ path: "/upload",
554
+ headers: {},
555
+ files: { avatar: [file] }
556
+ });
557
+ expect(request.body).toEqual({ metadata: '{"alt":"avatar"}', avatar: [file] });
558
+ });
559
+ test("does not spread non-object body values when uploaded files are present", () => {
560
+ const file = {
561
+ name: "avatar.png",
562
+ type: "image/png",
563
+ size: 4,
564
+ isTruncated: false,
565
+ data: Buffer.from("file"),
566
+ duration: 0
567
+ };
568
+ const request = new Request({
569
+ ip: void 0,
570
+ body: "raw text body",
571
+ cookies: {},
572
+ params: {},
573
+ query: {},
574
+ method: "post",
575
+ path: "/upload",
576
+ headers: {},
577
+ files: { avatar: [file] }
578
+ });
579
+ expect(request.body).toEqual({ avatar: [file] });
580
+ });
581
+ });
582
+ }
668
583
 
669
584
  // src/server/requests-auth-methods/base.ts
670
585
  var import_node_http2 = require("http2");
@@ -795,50 +710,50 @@ var SocketEmitter = class {
795
710
  };
796
711
 
797
712
  // src/server/adapters/base.ts
798
- var serverConfigPipe = () => import_valleyed6.v.object({
799
- port: import_valleyed6.v.number(),
800
- cors: import_valleyed6.v.optional(
801
- import_valleyed6.v.object({
802
- origin: import_valleyed6.v.optional(import_valleyed6.v.or([import_valleyed6.v.array(import_valleyed6.v.string()), import_valleyed6.v.is(true)])),
803
- methods: import_valleyed6.v.optional(import_valleyed6.v.array(import_valleyed6.v.in(Object.values(Methods)))),
804
- credentials: import_valleyed6.v.optional(import_valleyed6.v.boolean())
713
+ var serverConfigPipe = () => import_valleyed5.v.object({
714
+ port: import_valleyed5.v.number(),
715
+ cors: import_valleyed5.v.optional(
716
+ import_valleyed5.v.object({
717
+ origin: import_valleyed5.v.optional(import_valleyed5.v.or([import_valleyed5.v.array(import_valleyed5.v.string()), import_valleyed5.v.is(true)])),
718
+ methods: import_valleyed5.v.optional(import_valleyed5.v.array(import_valleyed5.v.in(Object.values(Methods)))),
719
+ credentials: import_valleyed5.v.optional(import_valleyed5.v.boolean())
805
720
  })
806
721
  ),
807
- eventBus: import_valleyed6.v.optional(import_valleyed6.v.instanceOf(EventBus)),
808
- publicPath: import_valleyed6.v.optional(import_valleyed6.v.string()),
809
- healthPath: import_valleyed6.v.optional(import_valleyed6.v.string()),
810
- openapi: import_valleyed6.v.defaults(
811
- import_valleyed6.v.object({
812
- docsVersion: import_valleyed6.v.defaults(import_valleyed6.v.string(), "1.0.0"),
813
- docsBaseUrl: import_valleyed6.v.defaults(import_valleyed6.v.array(import_valleyed6.v.string()), ["/"]),
814
- docsPath: import_valleyed6.v.defaults(import_valleyed6.v.string(), "/__docs")
722
+ eventBus: import_valleyed5.v.optional(import_valleyed5.v.instanceOf(EventBus)),
723
+ publicPath: import_valleyed5.v.optional(import_valleyed5.v.string()),
724
+ healthPath: import_valleyed5.v.optional(import_valleyed5.v.string()),
725
+ openapi: import_valleyed5.v.defaults(
726
+ import_valleyed5.v.object({
727
+ docsVersion: import_valleyed5.v.defaults(import_valleyed5.v.string(), "1.0.0"),
728
+ docsBaseUrl: import_valleyed5.v.defaults(import_valleyed5.v.array(import_valleyed5.v.string()), ["/"]),
729
+ docsPath: import_valleyed5.v.defaults(import_valleyed5.v.string(), "/__docs")
815
730
  }),
816
731
  {}
817
732
  ),
818
- requests: import_valleyed6.v.defaults(
819
- import_valleyed6.v.object({
820
- log: import_valleyed6.v.defaults(import_valleyed6.v.boolean(), true),
821
- rateLimit: import_valleyed6.v.defaults(
822
- import_valleyed6.v.object({
823
- enabled: import_valleyed6.v.defaults(import_valleyed6.v.boolean(), false),
824
- periodInMs: import_valleyed6.v.defaults(import_valleyed6.v.number(), 60 * 60 * 1e3),
825
- limit: import_valleyed6.v.defaults(import_valleyed6.v.number(), 5e3)
733
+ requests: import_valleyed5.v.defaults(
734
+ import_valleyed5.v.object({
735
+ log: import_valleyed5.v.defaults(import_valleyed5.v.boolean(), true),
736
+ rateLimit: import_valleyed5.v.defaults(
737
+ import_valleyed5.v.object({
738
+ enabled: import_valleyed5.v.defaults(import_valleyed5.v.boolean(), false),
739
+ periodInMs: import_valleyed5.v.defaults(import_valleyed5.v.number(), 60 * 60 * 1e3),
740
+ limit: import_valleyed5.v.defaults(import_valleyed5.v.number(), 5e3)
826
741
  }),
827
742
  {}
828
743
  ),
829
- slowdown: import_valleyed6.v.defaults(
830
- import_valleyed6.v.object({
831
- enabled: import_valleyed6.v.defaults(import_valleyed6.v.boolean(), false),
832
- periodInMs: import_valleyed6.v.defaults(import_valleyed6.v.number(), 10 * 60 * 1e3),
833
- delayAfter: import_valleyed6.v.defaults(import_valleyed6.v.number(), 2e3),
834
- delayInMs: import_valleyed6.v.defaults(import_valleyed6.v.number(), 500)
744
+ slowdown: import_valleyed5.v.defaults(
745
+ import_valleyed5.v.object({
746
+ enabled: import_valleyed5.v.defaults(import_valleyed5.v.boolean(), false),
747
+ periodInMs: import_valleyed5.v.defaults(import_valleyed5.v.number(), 10 * 60 * 1e3),
748
+ delayAfter: import_valleyed5.v.defaults(import_valleyed5.v.number(), 2e3),
749
+ delayInMs: import_valleyed5.v.defaults(import_valleyed5.v.number(), 500)
835
750
  }),
836
751
  {}
837
752
  )
838
753
  }),
839
754
  {}
840
755
  ),
841
- socketsAuthMethods: import_valleyed6.v.defaults(import_valleyed6.v.array(import_valleyed6.v.instanceOf(BaseRequestAuthMethod)), [])
756
+ socketsAuthMethods: import_valleyed5.v.defaults(import_valleyed5.v.array(import_valleyed5.v.instanceOf(BaseRequestAuthMethod)), [])
842
757
  });
843
758
  var rawResponseHandledSymbol = /* @__PURE__ */ Symbol("rawResponseHandled");
844
759
  var rawResponseHandled = Object.freeze({ [rawResponseHandledSymbol]: true });
@@ -859,7 +774,7 @@ function writeRawHandlerError(response, error) {
859
774
  var errorsSchemas = Object.entries(StatusCodes).filter(([, value]) => value > 399).map(([key, value]) => ({
860
775
  status: value,
861
776
  contentType: "application/json",
862
- pipe: import_valleyed6.v.meta(import_valleyed6.v.array(import_valleyed6.v.object({ message: import_valleyed6.v.string(), field: import_valleyed6.v.optional(import_valleyed6.v.string()) })), {
777
+ pipe: import_valleyed5.v.meta(import_valleyed5.v.array(import_valleyed5.v.object({ message: import_valleyed5.v.string(), field: import_valleyed5.v.optional(import_valleyed5.v.string()) })), {
863
778
  $refId: `Errors.${key}Response`,
864
779
  description: `${key} Response`
865
780
  })
@@ -873,6 +788,7 @@ var Server = class {
873
788
  #queue = [];
874
789
  #routesByKey = /* @__PURE__ */ new Set();
875
790
  #started = false;
791
+ #beforeListenHandlers = [];
876
792
  #notFoundHandler = null;
877
793
  setup(httpServer, config) {
878
794
  this.#httpServer = httpServer;
@@ -928,6 +844,11 @@ var Server = class {
928
844
  this.#notFoundHandler = handler;
929
845
  return this;
930
846
  }
847
+ onBeforeListen(handler) {
848
+ if (this.#started) throw new EquippedError("Cannot add before-listen handler after server has started", {});
849
+ this.#beforeListenHandlers.push(handler);
850
+ return this;
851
+ }
931
852
  test() {
932
853
  return (0, import_supertest.default)(this.#httpServer);
933
854
  }
@@ -961,6 +882,7 @@ var Server = class {
961
882
  });
962
883
  return await this.handleResponse(res, response);
963
884
  });
885
+ for (const handler of this.#beforeListenHandlers) await handler({ httpServer: this.#httpServer, port: config.port });
964
886
  const started = await this.startServer(config.port);
965
887
  if (started) Instance.get().log.info(`${instance.id}(${app.name}) service listening on port ${config.port}`);
966
888
  return started;
@@ -1009,37 +931,37 @@ var Server = class {
1009
931
  { key: "responseCookies", type: "response" }
1010
932
  ];
1011
933
  defs.forEach((def) => {
1012
- const pipe = schema[def.key] ?? import_valleyed6.v.any();
934
+ const pipe = schema[def.key] ?? import_valleyed5.v.any();
1013
935
  if (def.skip) return;
1014
936
  if (def.type === "request") {
1015
937
  requestPipeDefs[def.key] = pipe;
1016
- jsonSchema.request[def.key] = import_valleyed6.v.schema(pipe);
938
+ jsonSchema.request[def.key] = import_valleyed5.v.schema(pipe);
1017
939
  }
1018
940
  if (def.type === "response") {
1019
941
  const pipeRecords = errorsSchemas.concat({ status: defaultStatusCode, contentType, pipe });
1020
- responsePipeDefs[def.key] = import_valleyed6.v.any().pipe((input) => {
942
+ responsePipeDefs[def.key] = import_valleyed5.v.any().pipe((input) => {
1021
943
  const p = pipeRecords.find((rec) => rec.status === status)?.pipe;
1022
- if (!p) throw import_valleyed6.PipeError.root(`schema not defined for status code: ${status}`, input);
1023
- const r = import_valleyed6.v.validate(p, input);
944
+ if (!p) throw import_valleyed5.PipeError.root(`schema not defined for status code: ${status}`, input);
945
+ const r = import_valleyed5.v.validate(p, input);
1024
946
  if (!r.valid) throw r.error;
1025
947
  return r.value;
1026
948
  });
1027
949
  jsonSchema.response[def.key] = pipeRecords.map((record) => ({
1028
950
  status: record.status,
1029
951
  contentType: record.contentType,
1030
- schema: import_valleyed6.v.schema(record.pipe)
952
+ schema: import_valleyed5.v.schema(record.pipe)
1031
953
  }));
1032
954
  }
1033
955
  });
1034
- const requestPipe = import_valleyed6.v.object(requestPipeDefs);
1035
- import_valleyed6.v.compile(requestPipe, { allErrors: true });
1036
- const responsePipe = import_valleyed6.v.object(responsePipeDefs);
1037
- import_valleyed6.v.compile(responsePipe, { allErrors: true });
956
+ const requestPipe = import_valleyed5.v.object(requestPipeDefs);
957
+ import_valleyed5.v.compile(requestPipe, { allErrors: true });
958
+ const responsePipe = import_valleyed5.v.object(responsePipeDefs);
959
+ import_valleyed5.v.compile(responsePipe, { allErrors: true });
1038
960
  const validateRequest = async (request) => {
1039
961
  if (!Object.keys(requestPipeDefs)) return request;
1040
962
  const validity = requestLocalStorage.run(
1041
963
  request,
1042
- () => import_valleyed6.v.validate(requestPipe, {
964
+ () => import_valleyed5.v.validate(requestPipe, {
1043
965
  params: request.params,
1044
966
  headers: request.headers,
1045
967
  query: request.query,
@@ -1061,7 +983,7 @@ var Server = class {
1061
983
  contentType = response.contentType;
1062
984
  const validity = responseLocalStorage.run(
1063
985
  response,
1064
- () => import_valleyed6.v.validate(responsePipe, {
986
+ () => import_valleyed5.v.validate(responsePipe, {
1065
987
  responseHeaders: response.headers,
1066
988
  responseCookies: Object.fromEntries(Object.entries(response.cookies).map(([key, val]) => [key, val.value])),
1067
989
  response: response.body
@@ -1112,6 +1034,7 @@ if (void 0) {
1112
1034
  const testInstanceState = globalThis;
1113
1035
  class TestServer extends Server {
1114
1036
  handledResponse = null;
1037
+ startedPorts = [];
1115
1038
  notFoundHandler = null;
1116
1039
  constructor() {
1117
1040
  super();
@@ -1156,7 +1079,8 @@ if (void 0) {
1156
1079
  registerNotFoundHandler(cb) {
1157
1080
  this.notFoundHandler = cb;
1158
1081
  }
1159
- async startServer() {
1082
+ async startServer(port) {
1083
+ this.startedPorts.push(port);
1160
1084
  return true;
1161
1085
  }
1162
1086
  async dispatchNotFound(path) {
@@ -1167,6 +1091,43 @@ if (void 0) {
1167
1091
  return { handledResponse: this.handledResponse, rawResponse };
1168
1092
  }
1169
1093
  }
1094
+ describe("Server before-listen handlers", () => {
1095
+ test("runs before-listen handlers before adapter listen in registration order", async () => {
1096
+ ensureTestInstance();
1097
+ const server = new TestServer();
1098
+ const calls = [];
1099
+ let receivedServer = null;
1100
+ server.onBeforeListen(({ httpServer, port }) => {
1101
+ calls.push(`first:${port}`);
1102
+ receivedServer = httpServer;
1103
+ expect(server.startedPorts).toEqual([]);
1104
+ }).onBeforeListen(({ httpServer, port }) => {
1105
+ calls.push(`second:${port}`);
1106
+ expect(httpServer).toBe(receivedServer);
1107
+ expect(server.startedPorts).toEqual([]);
1108
+ });
1109
+ await server.start();
1110
+ expect(calls).toEqual(["first:0", "second:0"]);
1111
+ expect(receivedServer).toBeInstanceOf(http.Server);
1112
+ expect(server.startedPorts).toEqual([0]);
1113
+ });
1114
+ test("rejects startup when a before-listen handler fails before adapter listen", async () => {
1115
+ ensureTestInstance();
1116
+ const server = new TestServer();
1117
+ server.onBeforeListen(async () => {
1118
+ throw new Error("before-listen failed");
1119
+ });
1120
+ await expect(server.start()).rejects.toThrow("before-listen failed");
1121
+ expect(server.startedPorts).toEqual([]);
1122
+ });
1123
+ test("rejects adding before-listen handlers after server startup", async () => {
1124
+ ensureTestInstance();
1125
+ const server = new TestServer();
1126
+ await server.start();
1127
+ expect(() => server.onBeforeListen(() => {
1128
+ })).toThrow("Cannot add before-listen handler after server has started");
1129
+ });
1130
+ });
1170
1131
  describe("Server not-found handler", () => {
1171
1132
  test("throws the default Equipped not-found error when no custom handler is set", async () => {
1172
1133
  ensureTestInstance();
@@ -1675,22 +1636,22 @@ if (void 0) {
1675
1636
  }
1676
1637
 
1677
1638
  // src/instance/settings.ts
1678
- var import_valleyed8 = require("valleyed");
1679
- var instanceSettingsPipe = () => import_valleyed8.v.object({
1680
- app: import_valleyed8.v.object({
1681
- name: import_valleyed8.v.string()
1639
+ var import_valleyed7 = require("valleyed");
1640
+ var instanceSettingsPipe = () => import_valleyed7.v.object({
1641
+ app: import_valleyed7.v.object({
1642
+ name: import_valleyed7.v.string()
1682
1643
  }),
1683
- log: import_valleyed8.v.defaults(
1684
- import_valleyed8.v.object({
1685
- level: import_valleyed8.v.defaults(import_valleyed8.v.in(["fatal", "error", "warn", "info", "debug", "trace", "silent"]), "info")
1644
+ log: import_valleyed7.v.defaults(
1645
+ import_valleyed7.v.object({
1646
+ level: import_valleyed7.v.defaults(import_valleyed7.v.in(["fatal", "error", "warn", "info", "debug", "trace", "silent"]), "info")
1686
1647
  }),
1687
1648
  {}
1688
1649
  ),
1689
- utils: import_valleyed8.v.defaults(
1690
- import_valleyed8.v.object({
1691
- hashSaltRounds: import_valleyed8.v.defaults(import_valleyed8.v.number(), 10),
1692
- paginationDefaultLimit: import_valleyed8.v.defaults(import_valleyed8.v.number(), 100),
1693
- maxFileUploadSizeInMb: import_valleyed8.v.defaults(import_valleyed8.v.number(), 500)
1650
+ utils: import_valleyed7.v.defaults(
1651
+ import_valleyed7.v.object({
1652
+ hashSaltRounds: import_valleyed7.v.defaults(import_valleyed7.v.number(), 10),
1653
+ paginationDefaultLimit: import_valleyed7.v.defaults(import_valleyed7.v.number(), 100),
1654
+ maxFileUploadSizeInMb: import_valleyed7.v.defaults(import_valleyed7.v.number(), 500)
1694
1655
  }),
1695
1656
  {}
1696
1657
  )
@@ -1740,7 +1701,7 @@ var Instance = class _Instance {
1740
1701
  }
1741
1702
  }
1742
1703
  static envs(envsPipe) {
1743
- const envValidity = import_valleyed9.v.validate(envsPipe, process.env);
1704
+ const envValidity = import_valleyed8.v.validate(envsPipe, process.env);
1744
1705
  if (!envValidity.valid) {
1745
1706
  _Instance.crash(
1746
1707
  new EquippedError(`Environment variables are not valid
@@ -1753,7 +1714,7 @@ ${envValidity.error.toString()}`, {
1753
1714
  }
1754
1715
  static create(settings) {
1755
1716
  if (_Instance.#instance) return _Instance.crash(new EquippedError("Instance has been initialized already", {}));
1756
- const settingsValidity = import_valleyed9.v.validate(instanceSettingsPipe(), settings);
1717
+ const settingsValidity = import_valleyed8.v.validate(instanceSettingsPipe(), settings);
1757
1718
  if (!settingsValidity.valid) {
1758
1719
  _Instance.crash(
1759
1720
  new EquippedError(`Settings are not valid
@@ -1807,6 +1768,157 @@ ${settingsValidity.error.toString()}`, {
1807
1768
  }
1808
1769
  };
1809
1770
 
1771
+ // src/utilities/configurable.ts
1772
+ var import_valleyed9 = require("valleyed");
1773
+ function configurable(pipeFn, base) {
1774
+ const pipe = pipeFn();
1775
+ import_valleyed9.v.compile(pipe);
1776
+ class Configurable extends base {
1777
+ constructor(config, ...baseArgs) {
1778
+ super(...baseArgs);
1779
+ this.config = config;
1780
+ }
1781
+ static create(input, ...args) {
1782
+ const r = import_valleyed9.v.validate(pipe, input);
1783
+ if (!r.valid) throw r.error;
1784
+ return new this(r.value, ...args);
1785
+ }
1786
+ }
1787
+ return Configurable;
1788
+ }
1789
+ if (void 0) {
1790
+ const { describe, test, expect, expectTypeOf } = void 0;
1791
+ const { v: v8 } = null;
1792
+ const testPipe = () => v8.object({
1793
+ host: v8.string(),
1794
+ port: v8.number()
1795
+ });
1796
+ class TestBase {
1797
+ baseValue;
1798
+ constructor() {
1799
+ this.baseValue = "base";
1800
+ }
1801
+ }
1802
+ class TestBaseWithArgs {
1803
+ label;
1804
+ constructor(label) {
1805
+ this.label = label;
1806
+ }
1807
+ }
1808
+ describe("configurable", () => {
1809
+ test("validation runs in static create before constructor body executes", () => {
1810
+ let constructorRan = false;
1811
+ const Wrapped = configurable(testPipe, TestBase);
1812
+ class MyClass extends Wrapped {
1813
+ constructor(config) {
1814
+ super(config);
1815
+ constructorRan = true;
1816
+ }
1817
+ }
1818
+ expect(() => MyClass.create({ host: 123, port: "bad" })).toThrow();
1819
+ expect(constructorRan).toBe(false);
1820
+ MyClass.create({ host: "localhost", port: 3e3 });
1821
+ expect(constructorRan).toBe(true);
1822
+ });
1823
+ test("constructor receives validated value", () => {
1824
+ const Wrapped = configurable(testPipe, TestBase);
1825
+ let receivedConfig;
1826
+ class MyClass extends Wrapped {
1827
+ constructor(config) {
1828
+ super(config);
1829
+ receivedConfig = this.config;
1830
+ }
1831
+ }
1832
+ MyClass.create({ host: "localhost", port: 3e3 });
1833
+ expect(receivedConfig).toEqual({ host: "localhost", port: 3e3 });
1834
+ });
1835
+ test("external new is a compile error", () => {
1836
+ const Wrapped = configurable(testPipe, TestBase);
1837
+ class MyClass extends Wrapped {
1838
+ constructor(config) {
1839
+ super(config);
1840
+ }
1841
+ }
1842
+ void (() => new MyClass({ host: "localhost", port: 3e3 }));
1843
+ });
1844
+ test("static Config resolves to PipeOutput<P> at the type level", () => {
1845
+ const Wrapped = configurable(testPipe, TestBase);
1846
+ class MyClass extends Wrapped {
1847
+ constructor(config) {
1848
+ super(config);
1849
+ }
1850
+ }
1851
+ expectTypeOf().toEqualTypeOf();
1852
+ expect(MyClass.create).toBeTypeOf("function");
1853
+ });
1854
+ test("ConstructorParameters<This>-based extras inference works for non-zero-arg leaf signatures", () => {
1855
+ const Wrapped = configurable(testPipe, TestBase);
1856
+ class MyClass extends Wrapped {
1857
+ extra;
1858
+ constructor(config, extra) {
1859
+ super(config);
1860
+ this.extra = extra;
1861
+ }
1862
+ }
1863
+ const instance = MyClass.create({ host: "localhost", port: 3e3 }, 42);
1864
+ expect(instance.extra).toBe(42);
1865
+ expectTypeOf(instance).toHaveProperty("extra");
1866
+ expectTypeOf(instance.extra).toEqualTypeOf();
1867
+ void (() => MyClass.create({ host: "localhost", port: 3e3 }, "not-a-number"));
1868
+ });
1869
+ test("base-args forwarding works for non-zero-arg bases", () => {
1870
+ const Wrapped = configurable(testPipe, TestBaseWithArgs);
1871
+ class MyClass extends Wrapped {
1872
+ constructor(config, label) {
1873
+ super(config, label);
1874
+ }
1875
+ }
1876
+ const instance = MyClass.create({ host: "localhost", port: 3e3 }, "test-label");
1877
+ expect(instance.label).toBe("test-label");
1878
+ });
1879
+ test("config is accessible as protected readonly on instances", () => {
1880
+ const Wrapped = configurable(testPipe, TestBase);
1881
+ class MyClass extends Wrapped {
1882
+ constructor(config) {
1883
+ super(config);
1884
+ }
1885
+ getHost() {
1886
+ return this.config.host;
1887
+ }
1888
+ }
1889
+ const instance = MyClass.create({ host: "localhost", port: 3e3 });
1890
+ expect(instance.getHost()).toBe("localhost");
1891
+ });
1892
+ test("accepts an abstract base class", () => {
1893
+ class AbstractBase {
1894
+ }
1895
+ const Wrapped = configurable(testPipe, AbstractBase);
1896
+ class Concrete extends Wrapped {
1897
+ constructor(config) {
1898
+ super(config);
1899
+ }
1900
+ greet() {
1901
+ return `hello from ${this.config.host}`;
1902
+ }
1903
+ }
1904
+ const instance = Concrete.create({ host: "localhost", port: 3e3 });
1905
+ expect(instance.greet()).toBe("hello from localhost");
1906
+ expectTypeOf(instance).toHaveProperty("config");
1907
+ });
1908
+ });
1909
+ }
1910
+
1911
+ // src/utilities/media.ts
1912
+ var import_music_metadata = require("music-metadata");
1913
+ var getMediaDuration = async (buffer) => {
1914
+ try {
1915
+ const meta = await (0, import_music_metadata.parseBuffer)(buffer);
1916
+ return meta.format.duration ?? 0;
1917
+ } catch {
1918
+ return 0;
1919
+ }
1920
+ };
1921
+
1810
1922
  // src/server/adapters/express/index.ts
1811
1923
  var ExpressServer = class extends configurable(serverConfigPipe, Server) {
1812
1924
  #app;