@zdavison/matador 3.0.8 → 4.0.1

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 (40) hide show
  1. package/dist/core/fanout.d.ts +3 -2
  2. package/dist/core/fanout.d.ts.map +1 -1
  3. package/dist/core/fanout.js +4 -4
  4. package/dist/core/fanout.test.js +32 -24
  5. package/dist/core/matador.d.ts.map +1 -1
  6. package/dist/core/matador.js +16 -7
  7. package/dist/core/matador.test.js +32 -1
  8. package/dist/errors/index.d.ts +1 -1
  9. package/dist/errors/index.d.ts.map +1 -1
  10. package/dist/errors/index.js +2 -2
  11. package/dist/errors/matador-errors.d.ts +28 -0
  12. package/dist/errors/matador-errors.d.ts.map +1 -1
  13. package/dist/errors/matador-errors.js +41 -0
  14. package/dist/index.cjs +327 -80
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +3 -3
  17. package/dist/index.d.ts +3 -3
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +3 -3
  20. package/dist/index.js.map +1 -1
  21. package/dist/topology/builder.d.ts +28 -1
  22. package/dist/topology/builder.d.ts.map +1 -1
  23. package/dist/topology/builder.js +74 -0
  24. package/dist/topology/builder.test.js +188 -1
  25. package/dist/topology/index.d.ts +2 -2
  26. package/dist/topology/index.d.ts.map +1 -1
  27. package/dist/topology/index.js +1 -1
  28. package/dist/topology/types.d.ts +120 -6
  29. package/dist/topology/types.d.ts.map +1 -1
  30. package/dist/topology/types.js +50 -10
  31. package/dist/transport/local/local-transport.d.ts.map +1 -1
  32. package/dist/transport/local/local-transport.js +5 -2
  33. package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.d.ts +2 -0
  34. package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.d.ts.map +1 -0
  35. package/dist/transport/rabbitmq/rabbitmq-transport-reconnection.test.js +110 -0
  36. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts +24 -0
  37. package/dist/transport/rabbitmq/rabbitmq-transport.d.ts.map +1 -1
  38. package/dist/transport/rabbitmq/rabbitmq-transport.js +145 -68
  39. package/dist/transport/rabbitmq/rabbitmq-transport.test.js +130 -0
  40. package/package.json +6 -2
@@ -1,5 +1,5 @@
1
1
  import type { HasDescription } from '../errors/index.js';
2
- import type { DeadLetterConfig, QueueDefinition, RetryConfig, Topology } from './types.js';
2
+ import type { DeadLetterConfig, QueueDefinition, ResourcePrefix, RetryConfig, Topology, TopologyNaming } from './types.js';
3
3
  /**
4
4
  * Options for adding a queue.
5
5
  * Excludes 'name' as that is provided as the first argument to addQueue.
@@ -25,10 +25,37 @@ export declare class TopologyBuilder {
25
25
  private queues;
26
26
  private deadLetter;
27
27
  private retry;
28
+ private naming;
29
+ private prefix;
28
30
  /**
29
31
  * Sets the namespace prefix for all queues.
30
32
  */
31
33
  withNamespace(namespace: string): this;
34
+ /**
35
+ * Sets the prefix prepended to every default-derived broker resource name
36
+ * (e.g. `matador.{namespace}.{queue}`), so Matador-managed queues and
37
+ * exchanges are identifiable. Pass `null` to disable prefixing.
38
+ *
39
+ * Only applies to default names — a {@link withNaming} override fully owns
40
+ * its output and is never prefixed.
41
+ *
42
+ * Pass `null` or `undefined` to disable prefixing.
43
+ *
44
+ * @default 'matador'
45
+ * @example
46
+ * TopologyBuilder.create().withNamespace('myapp') // matador.myapp.events
47
+ * TopologyBuilder.create().withNamespace('myapp').withGlobalPrefix('acme') // acme.myapp.events
48
+ * TopologyBuilder.create().withNamespace('myapp').withGlobalPrefix(null) // myapp.events
49
+ */
50
+ withGlobalPrefix(prefix: ResourcePrefix): this;
51
+ /**
52
+ * Overrides how broker resource names are derived.
53
+ * Use during migrations to keep pre-existing queue/exchange names so a
54
+ * rolling deploy keeps routing through the resources already declared on
55
+ * the broker.
56
+ * @see TopologyNaming
57
+ */
58
+ withNaming(naming: TopologyNaming): this;
32
59
  /**
33
60
  * Adds a queue to the topology.
34
61
  * @param definition - A complete QueueDefinition object
@@ -1 +1 @@
1
- {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/topology/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,WAAW,EACX,QAAQ,EACT,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;AAWzD;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,KAAM,YAAW,cAAc;aASxD,MAAM,EAAE,SAAS,MAAM,EAAE;IAR3C,QAAQ,CAAC,WAAW,SAIuC;gBAGzD,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,SAAS,MAAM,EAAE;CAK5C;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B;;OAEG;IACH,MAAM,CAAC,MAAM,IAAI,eAAe;IAIhC,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,UAAU,CAGhB;IACF,OAAO,CAAC,KAAK,CAIX;IAEF;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKtC;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI;IAapD;;;OAGG;IACH,KAAK,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI;IACxC;;;;OAIG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI;IAWjD;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI;IAQvD;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI;IAS7C;;OAEG;IACH,YAAY,IAAI,IAAI;IAKpB;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAQzB;;OAEG;IACH,QAAQ,IAAI,SAAS,MAAM,EAAE;IAQ7B;;;OAGG;IACH,KAAK,IAAI,QAAQ;CAgBlB"}
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../../src/topology/builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,cAAc,EACd,WAAW,EACX,QAAQ,EACR,cAAc,EACf,MAAM,YAAY,CAAC;AAEpB;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;AAWzD;;GAEG;AACH,qBAAa,uBAAwB,SAAQ,KAAM,YAAW,cAAc;aASxD,MAAM,EAAE,SAAS,MAAM,EAAE;IAR3C,QAAQ,CAAC,WAAW,SAIuC;gBAGzD,OAAO,EAAE,MAAM,EACC,MAAM,EAAE,SAAS,MAAM,EAAE;CAK5C;AAED;;GAEG;AACH,qBAAa,eAAe;IAC1B;;OAEG;IACH,MAAM,CAAC,MAAM,IAAI,eAAe;IAIhC,OAAO,CAAC,SAAS,CAAM;IACvB,OAAO,CAAC,MAAM,CAAyB;IACvC,OAAO,CAAC,UAAU,CAGhB;IACF,OAAO,CAAC,KAAK,CAIX;IACF,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,MAAM,CAA6B;IAE3C;;OAEG;IACH,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI;IAKtC;;;;;;;;;;;;;;;OAeG;IACH,gBAAgB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAK9C;;;;;;OAMG;IACH,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI;IAKxC;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI;IAapD;;;OAGG;IACH,KAAK,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI;IACxC;;;;OAIG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,IAAI;IAWjD;;OAEG;IACH,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,IAAI;IAQvD;;OAEG;IACH,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,IAAI;IAS7C;;OAEG;IACH,YAAY,IAAI,IAAI;IAKpB;;OAEG;IACH,iBAAiB,IAAI,IAAI;IAQzB;;OAEG;IACH,QAAQ,IAAI,SAAS,MAAM,EAAE;IAU7B;;;OAGG;IACH,KAAK,IAAI,QAAQ;CAkBlB"}
@@ -40,6 +40,8 @@ export class TopologyBuilder {
40
40
  defaultDelayMs: 1000,
41
41
  maxDelayMs: 300000, // 5 minutes
42
42
  };
43
+ naming;
44
+ prefix = 'matador';
43
45
  /**
44
46
  * Sets the namespace prefix for all queues.
45
47
  */
@@ -47,6 +49,37 @@ export class TopologyBuilder {
47
49
  this.namespace = namespace;
48
50
  return this;
49
51
  }
52
+ /**
53
+ * Sets the prefix prepended to every default-derived broker resource name
54
+ * (e.g. `matador.{namespace}.{queue}`), so Matador-managed queues and
55
+ * exchanges are identifiable. Pass `null` to disable prefixing.
56
+ *
57
+ * Only applies to default names — a {@link withNaming} override fully owns
58
+ * its output and is never prefixed.
59
+ *
60
+ * Pass `null` or `undefined` to disable prefixing.
61
+ *
62
+ * @default 'matador'
63
+ * @example
64
+ * TopologyBuilder.create().withNamespace('myapp') // matador.myapp.events
65
+ * TopologyBuilder.create().withNamespace('myapp').withGlobalPrefix('acme') // acme.myapp.events
66
+ * TopologyBuilder.create().withNamespace('myapp').withGlobalPrefix(null) // myapp.events
67
+ */
68
+ withGlobalPrefix(prefix) {
69
+ this.prefix = prefix;
70
+ return this;
71
+ }
72
+ /**
73
+ * Overrides how broker resource names are derived.
74
+ * Use during migrations to keep pre-existing queue/exchange names so a
75
+ * rolling deploy keeps routing through the resources already declared on
76
+ * the broker.
77
+ * @see TopologyNaming
78
+ */
79
+ withNaming(naming) {
80
+ this.naming = naming;
81
+ return this;
82
+ }
50
83
  addQueue(nameOrDefinition, options = {}) {
51
84
  if (isQueueDefinition(nameOrDefinition)) {
52
85
  this.queues.push(nameOrDefinition);
@@ -106,8 +139,10 @@ export class TopologyBuilder {
106
139
  validate() {
107
140
  return [
108
141
  ...validateNamespace(this.namespace),
142
+ ...validatePrefix(this.prefix),
109
143
  ...validateQueues(this.queues),
110
144
  ...validateRetry(this.retry),
145
+ ...validateNaming(this.naming),
111
146
  ];
112
147
  }
113
148
  /**
@@ -124,6 +159,8 @@ export class TopologyBuilder {
124
159
  queues: [...this.queues],
125
160
  deadLetter: this.deadLetter,
126
161
  retry: this.retry,
162
+ naming: this.naming,
163
+ prefix: this.prefix,
127
164
  };
128
165
  }
129
166
  }
@@ -139,6 +176,21 @@ function validateNamespace(namespace) {
139
176
  }
140
177
  return [];
141
178
  }
179
+ function validatePrefix(prefix) {
180
+ // null/undefined explicitly disable prefixing.
181
+ if (prefix == null) {
182
+ return [];
183
+ }
184
+ if (prefix.trim() === '') {
185
+ return ['Prefix must be a non-empty string, or null to disable prefixing'];
186
+ }
187
+ if (!IDENTIFIER_PATTERN.test(prefix)) {
188
+ return [
189
+ 'Prefix must start with a letter and contain only alphanumeric characters, underscores, and hyphens',
190
+ ];
191
+ }
192
+ return [];
193
+ }
142
194
  function validateQueueName(queue, seen) {
143
195
  if (!queue.name || queue.name.trim() === '') {
144
196
  return ['Queue name cannot be empty'];
@@ -176,6 +228,28 @@ function validateQueues(queues) {
176
228
  }
177
229
  return issues;
178
230
  }
231
+ function validateNaming(naming) {
232
+ if (!naming)
233
+ return [];
234
+ const issues = [];
235
+ for (const field of [
236
+ 'queue',
237
+ 'mainExchange',
238
+ 'dlxExchange',
239
+ 'delayedExchange',
240
+ ]) {
241
+ const value = naming[field];
242
+ if (value !== undefined && typeof value !== 'function') {
243
+ issues.push(`Naming override "${field}" must be a function`);
244
+ }
245
+ }
246
+ if (naming.dlxExchangeType !== undefined &&
247
+ naming.dlxExchangeType !== 'direct' &&
248
+ naming.dlxExchangeType !== 'topic') {
249
+ issues.push(`Naming override "dlxExchangeType" must be 'direct' or 'topic'`);
250
+ }
251
+ return issues;
252
+ }
179
253
  function validateRetry(retry) {
180
254
  if (!retry.enabled)
181
255
  return [];
@@ -1,6 +1,7 @@
1
1
  import { describe, expect, it } from 'bun:test';
2
+ import { UnknownQueueReferenceError } from '../errors/matador-errors.js';
2
3
  import { TopologyBuilder, TopologyValidationError } from './builder.js';
3
- import { resolveQueueName } from './types.js';
4
+ import { findQueueDefinition, getDeadLetterQueueName, getQualifiedQueueName, getRetryQueueName, resolveQueueName, resolveTargetQueueName, } from './types.js';
4
5
  describe('TopologyBuilder', () => {
5
6
  describe('withNamespace', () => {
6
7
  it('should set the namespace', () => {
@@ -358,3 +359,189 @@ describe('resolveQueueName', () => {
358
359
  expect(resolveQueueName('myapp', queueDef)).toBe('matador.shared.id-platform');
359
360
  });
360
361
  });
362
+ describe('TopologyNaming', () => {
363
+ const v1Naming = {
364
+ queue: (ns, q) => `matador.${ns}.${q}`,
365
+ mainExchange: (ns) => `matador.${ns}`,
366
+ dlxExchange: (ns) => `matador.${ns}.dlx-undeliverable`,
367
+ dlxExchangeType: 'topic',
368
+ delayedExchange: (ns) => `matador.${ns}.delayed`,
369
+ };
370
+ describe('builder validation', () => {
371
+ it('should accept a valid naming configuration', () => {
372
+ const topology = TopologyBuilder.create()
373
+ .withNamespace('myapp')
374
+ .withNaming(v1Naming)
375
+ .addQueue('events')
376
+ .build();
377
+ expect(topology.naming?.queue?.('myapp', 'events')).toBe('matador.myapp.events');
378
+ expect(topology.naming?.mainExchange?.('myapp')).toBe('matador.myapp');
379
+ });
380
+ it('should accept partial naming overrides', () => {
381
+ const topology = TopologyBuilder.create()
382
+ .withNamespace('myapp')
383
+ .withNaming({ queue: (ns, q) => `legacy.matador.${ns}.${q}` })
384
+ .addQueue('events')
385
+ .build();
386
+ expect(topology.naming?.queue?.('myapp', 'events')).toBe('legacy.matador.myapp.events');
387
+ });
388
+ it('should reject a non-function naming override', () => {
389
+ expect(() => TopologyBuilder.create()
390
+ .withNamespace('myapp')
391
+ .withNaming({ queue: 'matador.' })
392
+ .addQueue('events')
393
+ .build()).toThrow(TopologyValidationError);
394
+ });
395
+ it('should reject an invalid dlxExchangeType', () => {
396
+ expect(() => TopologyBuilder.create()
397
+ .withNamespace('myapp')
398
+ .withNaming({
399
+ dlxExchangeType: 'fanout',
400
+ })
401
+ .addQueue('events')
402
+ .build()).toThrow(TopologyValidationError);
403
+ });
404
+ });
405
+ describe('name resolution with naming overrides', () => {
406
+ const topology = TopologyBuilder.create()
407
+ .withNamespace('myapp')
408
+ .withNaming({ queue: (ns, q) => `matador.${ns}.${q}` })
409
+ .addQueue('events')
410
+ .addQueue({
411
+ name: 'matador.shared.id-platform',
412
+ exact: true,
413
+ })
414
+ .build();
415
+ it('should build qualified queue names via the strategy', () => {
416
+ expect(getQualifiedQueueName('myapp', 'events', topology.naming)).toBe('matador.myapp.events');
417
+ });
418
+ it('should build resolved queue definitions via the strategy', () => {
419
+ const queueDef = { name: 'events' };
420
+ expect(resolveQueueName('myapp', queueDef, topology.naming)).toBe('matador.myapp.events');
421
+ });
422
+ it('should not transform exact queue definitions', () => {
423
+ const queueDef = { name: 'matador.shared.id-platform', exact: true };
424
+ expect(resolveQueueName('myapp', queueDef, topology.naming)).toBe('matador.shared.id-platform');
425
+ });
426
+ it('should resolve a target queue by exact name as-is', () => {
427
+ expect(resolveTargetQueueName(topology, 'matador.shared.id-platform')).toBe('matador.shared.id-platform');
428
+ });
429
+ it('should build non-exact target queues via the strategy', () => {
430
+ expect(resolveTargetQueueName(topology, 'events')).toBe('matador.myapp.events');
431
+ });
432
+ it('should derive retry and DLQ names from the strategy name', () => {
433
+ expect(getRetryQueueName('myapp', 'events', topology.naming)).toBe('matador.myapp.events.retry');
434
+ expect(getDeadLetterQueueName('myapp', 'events', 'unhandled', topology.naming)).toBe('matador.myapp.events.unhandled');
435
+ });
436
+ it('should find queue definitions by name', () => {
437
+ expect(findQueueDefinition(topology, 'events')?.name).toBe('events');
438
+ expect(findQueueDefinition(topology, 'matador.shared.id-platform')?.name).toBe('matador.shared.id-platform');
439
+ expect(findQueueDefinition(topology, 'missing')).toBeUndefined();
440
+ });
441
+ it('should throw for a queue reference not declared in the topology', () => {
442
+ // A targetQueue/consumeFrom name that was never added to the topology
443
+ // must fail loudly rather than be silently namespace-qualified and
444
+ // routed to a queue nobody consumes.
445
+ expect(() => resolveTargetQueueName(topology, 'ext-service.payments')).toThrow(UnknownQueueReferenceError);
446
+ });
447
+ });
448
+ });
449
+ describe('resource name prefix', () => {
450
+ describe('default', () => {
451
+ it('prefixes resource names with "matador" by default', () => {
452
+ const topology = TopologyBuilder.create()
453
+ .withNamespace('myapp')
454
+ .addQueue('events')
455
+ .build();
456
+ expect(topology.prefix).toBe('matador');
457
+ expect(resolveTargetQueueName(topology, 'events')).toBe('matador.myapp.events');
458
+ });
459
+ it('inherits the default prefix into retry and DLQ names', () => {
460
+ expect(getRetryQueueName('myapp', 'events', undefined, 'matador')).toBe('matador.myapp.events.retry');
461
+ expect(getDeadLetterQueueName('myapp', 'events', 'unhandled', undefined, 'matador')).toBe('matador.myapp.events.unhandled');
462
+ });
463
+ });
464
+ describe('withGlobalPrefix', () => {
465
+ it('uses a custom prefix string', () => {
466
+ const topology = TopologyBuilder.create()
467
+ .withNamespace('myapp')
468
+ .withGlobalPrefix('acme')
469
+ .addQueue('events')
470
+ .build();
471
+ expect(topology.prefix).toBe('acme');
472
+ expect(resolveTargetQueueName(topology, 'events')).toBe('acme.myapp.events');
473
+ });
474
+ it('disables prefixing when set to null', () => {
475
+ const topology = TopologyBuilder.create()
476
+ .withNamespace('myapp')
477
+ .withGlobalPrefix(null)
478
+ .addQueue('events')
479
+ .build();
480
+ expect(topology.prefix).toBeNull();
481
+ expect(resolveTargetQueueName(topology, 'events')).toBe('myapp.events');
482
+ });
483
+ it('disables prefixing when set to undefined', () => {
484
+ const topology = TopologyBuilder.create()
485
+ .withNamespace('myapp')
486
+ .withGlobalPrefix(undefined)
487
+ .addQueue('events')
488
+ .build();
489
+ expect(topology.prefix).toBeUndefined();
490
+ expect(resolveTargetQueueName(topology, 'events')).toBe('myapp.events');
491
+ });
492
+ });
493
+ describe('helper-level prefixing', () => {
494
+ it('applies a non-null prefix to the default qualified name', () => {
495
+ expect(getQualifiedQueueName('myapp', 'events', undefined, 'acme')).toBe('acme.myapp.events');
496
+ });
497
+ it('omits the prefix when null or undefined', () => {
498
+ expect(getQualifiedQueueName('myapp', 'events', undefined, null)).toBe('myapp.events');
499
+ expect(getQualifiedQueueName('myapp', 'events', undefined, undefined)).toBe('myapp.events');
500
+ });
501
+ });
502
+ describe('composition with withNaming', () => {
503
+ it('does not prefix names produced by a withNaming override', () => {
504
+ const topology = TopologyBuilder.create()
505
+ .withNamespace('myapp')
506
+ .withGlobalPrefix('matador')
507
+ .withNaming({ queue: (_ns, q) => `legacy-${q}` })
508
+ .addQueue('events')
509
+ .build();
510
+ // The override fully owns the name; the prefix is not prepended.
511
+ expect(resolveTargetQueueName(topology, 'events')).toBe('legacy-events');
512
+ });
513
+ });
514
+ describe('validation', () => {
515
+ it('rejects an empty-string prefix', () => {
516
+ expect(() => TopologyBuilder.create()
517
+ .withNamespace('myapp')
518
+ .withGlobalPrefix('')
519
+ .addQueue('events')
520
+ .build()).toThrow(TopologyValidationError);
521
+ });
522
+ it('rejects a prefix that is not a valid identifier', () => {
523
+ expect(() => TopologyBuilder.create()
524
+ .withNamespace('myapp')
525
+ .withGlobalPrefix('1bad')
526
+ .addQueue('events')
527
+ .build()).toThrow(TopologyValidationError);
528
+ expect(() => TopologyBuilder.create()
529
+ .withNamespace('myapp')
530
+ .withGlobalPrefix('has space')
531
+ .addQueue('events')
532
+ .build()).toThrow(TopologyValidationError);
533
+ });
534
+ it('accepts null (no prefix) and valid identifier prefixes', () => {
535
+ expect(() => TopologyBuilder.create()
536
+ .withNamespace('myapp')
537
+ .withGlobalPrefix(null)
538
+ .addQueue('events')
539
+ .build()).not.toThrow();
540
+ expect(() => TopologyBuilder.create()
541
+ .withNamespace('myapp')
542
+ .withGlobalPrefix('acme_co-1')
543
+ .addQueue('events')
544
+ .build()).not.toThrow();
545
+ });
546
+ });
547
+ });
@@ -1,5 +1,5 @@
1
- export type { DeadLetterConfig, DeadLetterQueueConfig, QueueDefinition, RabbitMQQueueDefinition, RabbitMQQueueOptions, RetryConfig, Topology, TransportQueueOptions, } from './types.js';
2
- export { getDeadLetterQueueName, getQualifiedQueueName, getRetryQueueName, resolveQueueName, } from './types.js';
1
+ export type { DeadLetterConfig, DeadLetterQueueConfig, QueueDefinition, RabbitMQQueueDefinition, RabbitMQQueueOptions, ResourcePrefix, RetryConfig, Topology, TopologyNaming, TransportQueueOptions, } from './types.js';
2
+ export { applyPrefix, findQueueDefinition, getDeadLetterQueueName, getQualifiedQueueName, getRetryQueueName, resolveQueueName, resolveTargetQueueName, } from './types.js';
3
3
  export type { QueueOptions } from './builder.js';
4
4
  export { TopologyBuilder, TopologyValidationError } from './builder.js';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/topology/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EACpB,WAAW,EACX,QAAQ,EACR,qBAAqB,GACtB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/topology/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,eAAe,EACf,uBAAuB,EACvB,oBAAoB,EACpB,cAAc,EACd,WAAW,EACX,QAAQ,EACR,cAAc,EACd,qBAAqB,GACtB,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,WAAW,EACX,mBAAmB,EACnB,sBAAsB,EACtB,qBAAqB,EACrB,iBAAiB,EACjB,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC"}
@@ -1,2 +1,2 @@
1
- export { getDeadLetterQueueName, getQualifiedQueueName, getRetryQueueName, resolveQueueName, } from './types.js';
1
+ export { applyPrefix, findQueueDefinition, getDeadLetterQueueName, getQualifiedQueueName, getRetryQueueName, resolveQueueName, resolveTargetQueueName, } from './types.js';
2
2
  export { TopologyBuilder, TopologyValidationError } from './builder.js';
@@ -1,3 +1,15 @@
1
+ /**
2
+ * A prefix prepended to every default-derived broker resource name so
3
+ * Matador-managed queues and exchanges are identifiable
4
+ * (e.g. `matador.{namespace}.{queue}`).
5
+ *
6
+ * A string is prepended as `${prefix}.`; `null` or `undefined` disables
7
+ * prefixing. Only applies to default names — a {@link TopologyNaming}
8
+ * override fully owns its output and is never prefixed.
9
+ *
10
+ * @example 'matador'
11
+ */
12
+ export type ResourcePrefix = string | null | undefined;
1
13
  /**
2
14
  * Transport-agnostic topology definition.
3
15
  * Matador owns the topology; transports translate and apply it.
@@ -11,6 +23,83 @@ export interface Topology {
11
23
  readonly deadLetter: DeadLetterConfig;
12
24
  /** Retry queue configuration */
13
25
  readonly retry: RetryConfig;
26
+ /**
27
+ * Optional overrides for how broker resource names are derived.
28
+ * Intended for migrations: lets an adopter keep pre-existing queue and
29
+ * exchange names (e.g. from Matador v1) so a rolling deploy keeps using
30
+ * the broker resources already in place.
31
+ */
32
+ readonly naming?: TopologyNaming | undefined;
33
+ /**
34
+ * Prefix prepended to every default-derived broker resource name so
35
+ * Matador-managed queues and exchanges are identifiable
36
+ * (e.g. `matador.{namespace}.{queue}`).
37
+ *
38
+ * `null` disables prefixing. Only applies to default names — a `naming`
39
+ * override fully owns its output and is never prefixed.
40
+ *
41
+ * Topologies built via {@link TopologyBuilder} default this to `'matador'`.
42
+ * When absent (e.g. a hand-built topology literal) no prefix is applied.
43
+ *
44
+ * @default 'matador'
45
+ */
46
+ readonly prefix?: ResourcePrefix;
47
+ }
48
+ /**
49
+ * Strategy for deriving broker resource names.
50
+ *
51
+ * Each builder receives the namespace as an explicit argument and returns the
52
+ * final name, so the namespace is always an input to the strategy — it is
53
+ * never applied separately on top of the result. Provide only the builders you
54
+ * want to override; the rest keep Matador's defaults:
55
+ * - work queue: `${namespace}.${queueName}`
56
+ * - main exchange: `${namespace}.exchange`
57
+ * - dead-letter exchange: `${namespace}.dlx`
58
+ * - delayed exchange: `${namespace}.delayed`
59
+ *
60
+ * Retry queues and DLQs have no builder of their own: they are derived from the
61
+ * work-queue name (`<queue>.retry`, `<queue>.unhandled`, `<queue>.undeliverable`),
62
+ * so overriding `queue` carries your prefix into them automatically.
63
+ *
64
+ * Intended for migrations: an adopter can keep pre-existing names (e.g. from
65
+ * Matador v1) so a rolling deploy keeps using the broker resources already in
66
+ * place instead of draining or dual-consuming.
67
+ *
68
+ * @example Keep Matador v1 names (`matador.{namespace}.{queue}`):
69
+ * ```typescript
70
+ * TopologyBuilder.create()
71
+ * .withNamespace('myapp')
72
+ * .withNaming({
73
+ * queue: (ns, q) => `matador.${ns}.${q}`,
74
+ * mainExchange: (ns) => `matador.${ns}`,
75
+ * dlxExchange: (ns) => `matador.${ns}.dlx-undeliverable`,
76
+ * dlxExchangeType: 'topic',
77
+ * delayedExchange: (ns) => `matador.${ns}.delayed`,
78
+ * })
79
+ * ```
80
+ */
81
+ export interface TopologyNaming {
82
+ /**
83
+ * Builds the qualified work-queue name from the namespace and queue name.
84
+ * Retry queues and DLQs are derived from the result automatically
85
+ * (`<queue>.retry`, `<queue>.unhandled`, `<queue>.undeliverable`).
86
+ * Default builds `${namespace}.${queueName}`.
87
+ */
88
+ readonly queue?: ((namespace: string, queueName: string) => string) | undefined;
89
+ /** Builds the main exchange name. Default builds `${namespace}.exchange`. */
90
+ readonly mainExchange?: ((namespace: string) => string) | undefined;
91
+ /** Builds the dead-letter exchange name. Default builds `${namespace}.dlx`. */
92
+ readonly dlxExchange?: ((namespace: string) => string) | undefined;
93
+ /**
94
+ * Exchange type used when asserting the dead-letter exchange.
95
+ * Useful when an existing DLX was declared as `topic` — asserting it as
96
+ * `direct` would fail with PRECONDITION_FAILED. Literal (non-wildcard)
97
+ * binding keys behave identically on a topic exchange.
98
+ * Default: `'direct'`.
99
+ */
100
+ readonly dlxExchangeType?: 'direct' | 'topic' | undefined;
101
+ /** Builds the delayed-message exchange name. Default builds `${namespace}.delayed`. */
102
+ readonly delayedExchange?: ((namespace: string) => string) | undefined;
14
103
  }
15
104
  /**
16
105
  * Individual queue definition.
@@ -109,20 +198,45 @@ export interface RetryConfig {
109
198
  readonly maxDelayMs: number;
110
199
  }
111
200
  /**
112
- * Gets the fully qualified queue name with namespace prefix.
201
+ * Prepends a resource-name prefix unless it is disabled.
202
+ * `null`/`undefined` mean no prefix; a string is prepended as `${prefix}.`.
203
+ */
204
+ export declare function applyPrefix(prefix: ResourcePrefix, name: string): string;
205
+ /**
206
+ * Gets the fully qualified work-queue name.
207
+ * An optional naming strategy may override how the name is derived; otherwise
208
+ * the default is `${namespace}.${queueName}`, optionally prefixed.
113
209
  */
114
- export declare function getQualifiedQueueName(namespace: string, queueName: string): string;
210
+ export declare function getQualifiedQueueName(namespace: string, queueName: string, naming?: TopologyNaming, prefix?: ResourcePrefix): string;
115
211
  /**
116
212
  * Gets the dead-letter queue name for a given queue.
117
213
  */
118
- export declare function getDeadLetterQueueName(namespace: string, queueName: string, dlqType: 'unhandled' | 'undeliverable'): string;
214
+ export declare function getDeadLetterQueueName(namespace: string, queueName: string, dlqType: 'unhandled' | 'undeliverable', naming?: TopologyNaming, prefix?: ResourcePrefix): string;
119
215
  /**
120
216
  * Gets the retry queue name for a given queue.
121
217
  */
122
- export declare function getRetryQueueName(namespace: string, queueName: string): string;
218
+ export declare function getRetryQueueName(namespace: string, queueName: string, naming?: TopologyNaming, prefix?: ResourcePrefix): string;
123
219
  /**
124
220
  * Resolves the actual queue name for a given queue definition.
125
- * When exact: true, returns name as-is. Otherwise, returns namespace.name.
221
+ * When exact: true, returns name as-is. Otherwise, returns the
222
+ * namespace-qualified name (honoring any naming overrides and prefix).
223
+ */
224
+ export declare function resolveQueueName(namespace: string, queueDef: QueueDefinition, naming?: TopologyNaming, prefix?: ResourcePrefix): string;
225
+ /**
226
+ * Finds the queue definition a local queue reference points at.
227
+ */
228
+ export declare function findQueueDefinition(topology: Topology, queueName: string): QueueDefinition | undefined;
229
+ /**
230
+ * Resolves a local queue reference (a subscriber's `targetQueue` or a
231
+ * `consumeFrom` entry) to the transport-level queue name.
232
+ *
233
+ * The reference must be declared in the topology via `.addQueue(...)`:
234
+ * an exact queue resolves to its name as-is; a Matador-owned queue is
235
+ * namespace-qualified (honoring any naming overrides). An undeclared
236
+ * reference throws {@link UnknownQueueReferenceError} rather than being
237
+ * silently qualified and routed to a queue nobody consumes.
238
+ *
239
+ * @throws UnknownQueueReferenceError if `queueName` is not in `topology.queues`.
126
240
  */
127
- export declare function resolveQueueName(namespace: string, queueDef: QueueDefinition): string;
241
+ export declare function resolveTargetQueueName(topology: Topology, queueName: string): string;
128
242
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/topology/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/topology/types.ts"],"names":[],"mappings":"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"}
@@ -1,28 +1,68 @@
1
+ import { UnknownQueueReferenceError } from '../errors/matador-errors.js';
1
2
  /**
2
- * Gets the fully qualified queue name with namespace prefix.
3
+ * Prepends a resource-name prefix unless it is disabled.
4
+ * `null`/`undefined` mean no prefix; a string is prepended as `${prefix}.`.
3
5
  */
4
- export function getQualifiedQueueName(namespace, queueName) {
5
- return `${namespace}.${queueName}`;
6
+ export function applyPrefix(prefix, name) {
7
+ return prefix == null ? name : `${prefix}.${name}`;
8
+ }
9
+ /**
10
+ * Gets the fully qualified work-queue name.
11
+ * An optional naming strategy may override how the name is derived; otherwise
12
+ * the default is `${namespace}.${queueName}`, optionally prefixed.
13
+ */
14
+ export function getQualifiedQueueName(namespace, queueName, naming, prefix) {
15
+ return (naming?.queue?.(namespace, queueName) ??
16
+ applyPrefix(prefix, `${namespace}.${queueName}`));
6
17
  }
7
18
  /**
8
19
  * Gets the dead-letter queue name for a given queue.
9
20
  */
10
- export function getDeadLetterQueueName(namespace, queueName, dlqType) {
11
- return `${namespace}.${queueName}.${dlqType}`;
21
+ export function getDeadLetterQueueName(namespace, queueName, dlqType, naming, prefix) {
22
+ return `${getQualifiedQueueName(namespace, queueName, naming, prefix)}.${dlqType}`;
12
23
  }
13
24
  /**
14
25
  * Gets the retry queue name for a given queue.
15
26
  */
16
- export function getRetryQueueName(namespace, queueName) {
17
- return `${namespace}.${queueName}.retry`;
27
+ export function getRetryQueueName(namespace, queueName, naming, prefix) {
28
+ return `${getQualifiedQueueName(namespace, queueName, naming, prefix)}.retry`;
18
29
  }
19
30
  /**
20
31
  * Resolves the actual queue name for a given queue definition.
21
- * When exact: true, returns name as-is. Otherwise, returns namespace.name.
32
+ * When exact: true, returns name as-is. Otherwise, returns the
33
+ * namespace-qualified name (honoring any naming overrides and prefix).
22
34
  */
23
- export function resolveQueueName(namespace, queueDef) {
35
+ export function resolveQueueName(namespace, queueDef, naming, prefix) {
24
36
  if (queueDef.exact) {
25
37
  return queueDef.name;
26
38
  }
27
- return `${namespace}.${queueDef.name}`;
39
+ return getQualifiedQueueName(namespace, queueDef.name, naming, prefix);
40
+ }
41
+ /**
42
+ * Finds the queue definition a local queue reference points at.
43
+ */
44
+ export function findQueueDefinition(topology, queueName) {
45
+ return topology.queues.find((q) => q.name === queueName);
46
+ }
47
+ /**
48
+ * Resolves a local queue reference (a subscriber's `targetQueue` or a
49
+ * `consumeFrom` entry) to the transport-level queue name.
50
+ *
51
+ * The reference must be declared in the topology via `.addQueue(...)`:
52
+ * an exact queue resolves to its name as-is; a Matador-owned queue is
53
+ * namespace-qualified (honoring any naming overrides). An undeclared
54
+ * reference throws {@link UnknownQueueReferenceError} rather than being
55
+ * silently qualified and routed to a queue nobody consumes.
56
+ *
57
+ * @throws UnknownQueueReferenceError if `queueName` is not in `topology.queues`.
58
+ */
59
+ export function resolveTargetQueueName(topology, queueName) {
60
+ const def = findQueueDefinition(topology, queueName);
61
+ if (def === undefined) {
62
+ throw new UnknownQueueReferenceError(queueName);
63
+ }
64
+ if (def.exact) {
65
+ return def.name;
66
+ }
67
+ return getQualifiedQueueName(topology.namespace, queueName, topology.naming, topology.prefix);
28
68
  }
@@ -1 +1 @@
1
- {"version":3,"file":"local-transport.d.ts","sourceRoot":"","sources":["../../../src/transport/local/local-transport.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACV,MAAM,iBAAiB,CAAC;AAiCzB;;;GAGG;AACH,qBAAa,cAAe,YAAW,SAAS;IAC9C,QAAQ,CAAC,IAAI,WAAW;IACxB,QAAQ,CAAC,YAAY,wBAAqB;IAE1C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsC;IAC7D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2C;IACzE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAwB;IAC1D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4C;IAC1E,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,MAAM,CAAC,EAAE,MAAM;IAIrB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBjC,WAAW,IAAI,OAAO;IAIhB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAiBhD,IAAI,CACR,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAe7B;;;OAGG;IACH,OAAO,CAAC,sBAAsB;YAkBhB,OAAO;YAgBP,oBAAoB;IA+B5B,SAAS,CACb,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,YAAY,CAAC;IAsClB,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhD,gBAAgB,CACpB,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC;IAQhB;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAMnC;;OAEG;IACH,YAAY,IAAI,SAAS,cAAc,EAAE;IAIzC;;OAEG;IACH,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,QAAQ,EAAE;IAMtD;;OAEG;IACH,KAAK,IAAI,IAAI;IAYb;;;OAGG;IACG,UAAU,CACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,QAAQ,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,cAAc,CAAA;KAAE,GAAG,IAAI,CAAC;IAmBlE,OAAO,CAAC,gBAAgB;CAQzB"}
1
+ {"version":3,"file":"local-transport.d.ts","sourceRoot":"","sources":["../../../src/transport/local/local-transport.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,MAAM,EAAiB,MAAM,sBAAsB,CAAC;AAClE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAExD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAChE,OAAO,KAAK,EACV,cAAc,EACd,cAAc,EACd,WAAW,EACX,gBAAgB,EAChB,YAAY,EACZ,SAAS,EACV,MAAM,iBAAiB,CAAC;AAiCzB;;;GAGG;AACH,qBAAa,cAAe,YAAW,SAAS;IAC9C,QAAQ,CAAC,IAAI,WAAW;IACxB,QAAQ,CAAC,YAAY,wBAAqB;IAE1C,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsC;IAC7D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2C;IACzE,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAwB;IAC1D,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA4C;IAC1E,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,MAAM,CAAC,EAAE,MAAM;IAIrB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAiBjC,WAAW,IAAI,OAAO;IAIhB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAsBhD,IAAI,CACR,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,QAAQ,EAClB,OAAO,CAAC,EAAE,WAAW,GACpB,OAAO,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAe7B;;;OAGG;IACH,OAAO,CAAC,sBAAsB;YAkBhB,OAAO;YAgBP,oBAAoB;IAoC5B,SAAS,CACb,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,cAAc,EACvB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,YAAY,CAAC;IAsClB,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAMhD,gBAAgB,CACpB,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC;IAQhB;;OAEG;IACH,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAMnC;;OAEG;IACH,YAAY,IAAI,SAAS,cAAc,EAAE;IAIzC;;OAEG;IACH,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,QAAQ,EAAE;IAMtD;;OAEG;IACH,KAAK,IAAI,IAAI;IAYb;;;OAGG;IACG,UAAU,CACd,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;QAAE,QAAQ,EAAE,QAAQ,CAAC;QAAC,OAAO,EAAE,cAAc,CAAA;KAAE,GAAG,IAAI,CAAC;IAmBlE,OAAO,CAAC,gBAAgB;CAQzB"}