@warlock.js/notifications 4.15.0 → 5.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.
- package/CHANGELOG.md +12 -0
- package/cjs/channel_api-CH3DxhRU.cjs +7463 -0
- package/cjs/channel_api-CH3DxhRU.cjs.map +1 -0
- package/cjs/chunk-fl3r8ClO.cjs +41 -0
- package/cjs/index.cjs +34 -4
- package/cjs/index.cjs.map +1 -1
- package/cjs/rabbitmq-driver-CJtyxjUQ.cjs +3 -0
- package/cjs/src-Cus6pYPC.cjs +10981 -0
- package/cjs/src-Cus6pYPC.cjs.map +1 -0
- package/esm/_virtual/_rolldown/runtime.mjs +32 -0
- package/esm/herald/src/communicators/broker-registry.mjs +204 -0
- package/esm/herald/src/communicators/broker-registry.mjs.map +1 -0
- package/esm/herald/src/communicators/broker.mjs +110 -0
- package/esm/herald/src/communicators/broker.mjs.map +1 -0
- package/esm/herald/src/communicators/index.mjs +4 -0
- package/esm/herald/src/decorators/consumable.mjs +36 -0
- package/esm/herald/src/decorators/consumable.mjs.map +1 -0
- package/esm/herald/src/decorators/index.mjs +3 -0
- package/esm/herald/src/drivers/index.mjs +5 -0
- package/esm/herald/src/drivers/rabbitmq/index.mjs +4 -0
- package/esm/herald/src/drivers/rabbitmq/rabbitmq-channel.mjs +403 -0
- package/esm/herald/src/drivers/rabbitmq/rabbitmq-channel.mjs.map +1 -0
- package/esm/herald/src/drivers/rabbitmq/rabbitmq-driver.mjs +303 -0
- package/esm/herald/src/drivers/rabbitmq/rabbitmq-driver.mjs.map +1 -0
- package/esm/herald/src/index.mjs +16 -0
- package/esm/herald/src/message-managers/event-consumer.mjs +76 -0
- package/esm/herald/src/message-managers/event-consumer.mjs.map +1 -0
- package/esm/herald/src/message-managers/event-message.mjs +85 -0
- package/esm/herald/src/message-managers/event-message.mjs.map +1 -0
- package/esm/herald/src/message-managers/index.mjs +4 -0
- package/esm/herald/src/message-managers/prepare-consumer-subscription.mjs +42 -0
- package/esm/herald/src/message-managers/prepare-consumer-subscription.mjs.map +1 -0
- package/esm/herald/src/use-case-broadcast.mjs +30 -0
- package/esm/herald/src/use-case-broadcast.mjs.map +1 -0
- package/esm/herald/src/utils/connect-to-broker.mjs +168 -0
- package/esm/herald/src/utils/connect-to-broker.mjs.map +1 -0
- package/esm/herald/src/utils/index.mjs +3 -0
- package/esm/in-app/base-notifications-repository.d.mts +15 -1
- package/esm/in-app/base-notifications-repository.d.mts.map +1 -1
- package/esm/in-app/base-notifications-repository.mjs +33 -3
- package/esm/in-app/base-notifications-repository.mjs.map +1 -1
- package/esm/in-app/in-app.d.mts +3 -3
- package/esm/in-app/in-app.d.mts.map +1 -1
- package/esm/node_modules/@mongez/reinforcements/esm/Random/_internal.mjs +6 -0
- package/esm/node_modules/@mongez/reinforcements/esm/Random/_internal.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/Random/bool.mjs +3 -0
- package/esm/node_modules/@mongez/reinforcements/esm/Random/color.mjs +3 -0
- package/esm/node_modules/@mongez/reinforcements/esm/Random/date.mjs +3 -0
- package/esm/node_modules/@mongez/reinforcements/esm/Random/float.mjs +3 -0
- package/esm/node_modules/@mongez/reinforcements/esm/Random/id.mjs +3 -0
- package/esm/node_modules/@mongez/reinforcements/esm/Random/int.mjs +3 -0
- package/esm/node_modules/@mongez/reinforcements/esm/Random/nanoid.mjs +3 -0
- package/esm/node_modules/@mongez/reinforcements/esm/Random/pick.mjs +3 -0
- package/esm/node_modules/@mongez/reinforcements/esm/Random/random.mjs +16 -0
- package/esm/node_modules/@mongez/reinforcements/esm/Random/sample.mjs +3 -0
- package/esm/node_modules/@mongez/reinforcements/esm/Random/string.mjs +3 -0
- package/esm/node_modules/@mongez/reinforcements/esm/Random/token.mjs +3 -0
- package/esm/node_modules/@mongez/reinforcements/esm/Random/uuid.mjs +3 -0
- package/esm/node_modules/@mongez/reinforcements/esm/Random/weighted.mjs +3 -0
- package/esm/node_modules/@mongez/reinforcements/esm/async/retry.mjs +3 -0
- package/esm/node_modules/@mongez/reinforcements/esm/index.mjs +24 -0
- package/esm/node_modules/@mongez/reinforcements/esm/mixed/clone/clone.mjs +82 -0
- package/esm/node_modules/@mongez/reinforcements/esm/mixed/clone/clone.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/number/round.mjs +17 -0
- package/esm/node_modules/@mongez/reinforcements/esm/number/round.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/object/except.mjs +13 -0
- package/esm/node_modules/@mongez/reinforcements/esm/object/except.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/object/get.mjs +18 -0
- package/esm/node_modules/@mongez/reinforcements/esm/object/get.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/object/omit.mjs +17 -0
- package/esm/node_modules/@mongez/reinforcements/esm/object/omit.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/object/unset.mjs +30 -0
- package/esm/node_modules/@mongez/reinforcements/esm/object/unset.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/capitalize.mjs +19 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/capitalize.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/ltrim.mjs +18 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/ltrim.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/readMoreChars.mjs +14 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/readMoreChars.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/rtrim.mjs +18 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/rtrim.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/toCamelCase.mjs +22 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/toCamelCase.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/toKebabCase.mjs +17 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/toKebabCase.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/toSnakeCase.mjs +21 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/toSnakeCase.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/toStudlyCase.mjs +21 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/toStudlyCase.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/trim.mjs +20 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/trim.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/ucfirst.mjs +15 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/ucfirst.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/words.mjs +25 -0
- package/esm/node_modules/@mongez/reinforcements/esm/string/words.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/utils/escapeRegex.mjs +11 -0
- package/esm/node_modules/@mongez/reinforcements/esm/utils/escapeRegex.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/utils/lazy.mjs +100 -0
- package/esm/node_modules/@mongez/reinforcements/esm/utils/lazy.mjs.map +1 -0
- package/esm/node_modules/@mongez/reinforcements/esm/utils/tap.mjs +27 -0
- package/esm/node_modules/@mongez/reinforcements/esm/utils/tap.mjs.map +1 -0
- package/esm/node_modules/@mongez/supportive-is/esm/index.mjs +353 -0
- package/esm/node_modules/@mongez/supportive-is/esm/index.mjs.map +1 -0
- package/esm/node_modules/amqplib/channel_api.mjs +28 -0
- package/esm/node_modules/amqplib/channel_api.mjs.map +1 -0
- package/esm/node_modules/amqplib/lib/api_args.mjs +241 -0
- package/esm/node_modules/amqplib/lib/api_args.mjs.map +1 -0
- package/esm/node_modules/amqplib/lib/bitset.mjs +130 -0
- package/esm/node_modules/amqplib/lib/bitset.mjs.map +1 -0
- package/esm/node_modules/amqplib/lib/channel.mjs +302 -0
- package/esm/node_modules/amqplib/lib/channel.mjs.map +1 -0
- package/esm/node_modules/amqplib/lib/channel_model.mjs +206 -0
- package/esm/node_modules/amqplib/lib/channel_model.mjs.map +1 -0
- package/esm/node_modules/amqplib/lib/codec.mjs +273 -0
- package/esm/node_modules/amqplib/lib/codec.mjs.map +1 -0
- package/esm/node_modules/amqplib/lib/connect.mjs +143 -0
- package/esm/node_modules/amqplib/lib/connect.mjs.map +1 -0
- package/esm/node_modules/amqplib/lib/connection.mjs +416 -0
- package/esm/node_modules/amqplib/lib/connection.mjs.map +1 -0
- package/esm/node_modules/amqplib/lib/credentials.mjs +50 -0
- package/esm/node_modules/amqplib/lib/credentials.mjs.map +1 -0
- package/esm/node_modules/amqplib/lib/defs.mjs +4600 -0
- package/esm/node_modules/amqplib/lib/defs.mjs.map +1 -0
- package/esm/node_modules/amqplib/lib/error.mjs +29 -0
- package/esm/node_modules/amqplib/lib/error.mjs.map +1 -0
- package/esm/node_modules/amqplib/lib/format.mjs +28 -0
- package/esm/node_modules/amqplib/lib/format.mjs.map +1 -0
- package/esm/node_modules/amqplib/lib/frame.mjs +130 -0
- package/esm/node_modules/amqplib/lib/frame.mjs.map +1 -0
- package/esm/node_modules/amqplib/lib/heartbeat.mjs +40 -0
- package/esm/node_modules/amqplib/lib/heartbeat.mjs.map +1 -0
- package/esm/node_modules/amqplib/lib/mux.mjs +86 -0
- package/esm/node_modules/amqplib/lib/mux.mjs.map +1 -0
- package/esm/node_modules/amqplib/package.mjs +41 -0
- package/esm/node_modules/amqplib/package.mjs.map +1 -0
- package/esm/node_modules/buffer-more-ints/buffer-more-ints.mjs +360 -0
- package/esm/node_modules/buffer-more-ints/buffer-more-ints.mjs.map +1 -0
- package/esm/node_modules/dayjs/dayjs.min.mjs +305 -0
- package/esm/node_modules/dayjs/dayjs.min.mjs.map +1 -0
- package/esm/node_modules/querystringify/index.mjs +80 -0
- package/esm/node_modules/querystringify/index.mjs.map +1 -0
- package/esm/node_modules/requires-port/index.mjs +35 -0
- package/esm/node_modules/requires-port/index.mjs.map +1 -0
- package/esm/node_modules/url-parse/index.mjs +388 -0
- package/esm/node_modules/url-parse/index.mjs.map +1 -0
- package/esm/queue/load-herald.mjs +1 -1
- package/esm/seal/src/config.mjs +15 -0
- package/esm/seal/src/config.mjs.map +1 -0
- package/esm/seal/src/factory/index.mjs +4 -0
- package/esm/seal/src/factory/validate.mjs +34 -0
- package/esm/seal/src/factory/validate.mjs.map +1 -0
- package/esm/seal/src/factory/validators.mjs +132 -0
- package/esm/seal/src/factory/validators.mjs.map +1 -0
- package/esm/seal/src/helpers/date-helpers.mjs +24 -0
- package/esm/seal/src/helpers/date-helpers.mjs.map +1 -0
- package/esm/seal/src/helpers/get-field-value.mjs +46 -0
- package/esm/seal/src/helpers/get-field-value.mjs.map +1 -0
- package/esm/seal/src/helpers/index.mjs +6 -0
- package/esm/seal/src/helpers/is-empty-value.mjs +10 -0
- package/esm/seal/src/helpers/is-empty-value.mjs.map +1 -0
- package/esm/seal/src/helpers/path-helpers.mjs +12 -0
- package/esm/seal/src/helpers/path-helpers.mjs.map +1 -0
- package/esm/seal/src/helpers/validation-helpers.mjs +72 -0
- package/esm/seal/src/helpers/validation-helpers.mjs.map +1 -0
- package/esm/seal/src/index.mjs +87 -0
- package/esm/seal/src/mutators/array-mutators.mjs +32 -0
- package/esm/seal/src/mutators/array-mutators.mjs.map +1 -0
- package/esm/seal/src/mutators/date-mutators.mjs +94 -0
- package/esm/seal/src/mutators/date-mutators.mjs.map +1 -0
- package/esm/seal/src/mutators/index.mjs +8 -0
- package/esm/seal/src/mutators/number-mutators.mjs +52 -0
- package/esm/seal/src/mutators/number-mutators.mjs.map +1 -0
- package/esm/seal/src/mutators/object-mutators.mjs +30 -0
- package/esm/seal/src/mutators/object-mutators.mjs.map +1 -0
- package/esm/seal/src/mutators/scalar-mutators.mjs +9 -0
- package/esm/seal/src/mutators/scalar-mutators.mjs.map +1 -0
- package/esm/seal/src/mutators/string-mutators.mjs +186 -0
- package/esm/seal/src/mutators/string-mutators.mjs.map +1 -0
- package/esm/seal/src/rules/array/array-rules.mjs +41 -0
- package/esm/seal/src/rules/array/array-rules.mjs.map +1 -0
- package/esm/seal/src/rules/array/index.mjs +3 -0
- package/esm/seal/src/rules/color/color-rules.mjs +104 -0
- package/esm/seal/src/rules/color/color-rules.mjs.map +1 -0
- package/esm/seal/src/rules/color/index.mjs +3 -0
- package/esm/seal/src/rules/common/enum.mjs +77 -0
- package/esm/seal/src/rules/common/enum.mjs.map +1 -0
- package/esm/seal/src/rules/common/equals-field-rules.mjs +42 -0
- package/esm/seal/src/rules/common/equals-field-rules.mjs.map +1 -0
- package/esm/seal/src/rules/common/index.mjs +8 -0
- package/esm/seal/src/rules/common/instanceof.mjs +23 -0
- package/esm/seal/src/rules/common/instanceof.mjs.map +1 -0
- package/esm/seal/src/rules/common/literal.mjs +31 -0
- package/esm/seal/src/rules/common/literal.mjs.map +1 -0
- package/esm/seal/src/rules/common/type-rules.mjs +97 -0
- package/esm/seal/src/rules/common/type-rules.mjs.map +1 -0
- package/esm/seal/src/rules/common/unknown-key.mjs +25 -0
- package/esm/seal/src/rules/common/unknown-key.mjs.map +1 -0
- package/esm/seal/src/rules/conditional/forbidden-if-rules.mjs +116 -0
- package/esm/seal/src/rules/conditional/forbidden-if-rules.mjs.map +1 -0
- package/esm/seal/src/rules/conditional/index.mjs +12 -0
- package/esm/seal/src/rules/conditional/present-if-rules.mjs +97 -0
- package/esm/seal/src/rules/conditional/present-if-rules.mjs.map +1 -0
- package/esm/seal/src/rules/conditional/present-unless-rules.mjs +30 -0
- package/esm/seal/src/rules/conditional/present-unless-rules.mjs.map +1 -0
- package/esm/seal/src/rules/conditional/present-with-rules.mjs +76 -0
- package/esm/seal/src/rules/conditional/present-with-rules.mjs.map +1 -0
- package/esm/seal/src/rules/conditional/present-without-rules.mjs +76 -0
- package/esm/seal/src/rules/conditional/present-without-rules.mjs.map +1 -0
- package/esm/seal/src/rules/conditional/required-if-rules.mjs +204 -0
- package/esm/seal/src/rules/conditional/required-if-rules.mjs.map +1 -0
- package/esm/seal/src/rules/conditional/required-unless-rules.mjs +31 -0
- package/esm/seal/src/rules/conditional/required-unless-rules.mjs.map +1 -0
- package/esm/seal/src/rules/conditional/required-when-rule.mjs +32 -0
- package/esm/seal/src/rules/conditional/required-when-rule.mjs.map +1 -0
- package/esm/seal/src/rules/conditional/required-with-rules.mjs +67 -0
- package/esm/seal/src/rules/conditional/required-with-rules.mjs.map +1 -0
- package/esm/seal/src/rules/conditional/required-without-rules.mjs +67 -0
- package/esm/seal/src/rules/conditional/required-without-rules.mjs.map +1 -0
- package/esm/seal/src/rules/core/equal.mjs +22 -0
- package/esm/seal/src/rules/core/equal.mjs.map +1 -0
- package/esm/seal/src/rules/core/forbidden.mjs +20 -0
- package/esm/seal/src/rules/core/forbidden.mjs.map +1 -0
- package/esm/seal/src/rules/core/index.mjs +7 -0
- package/esm/seal/src/rules/core/required.mjs +36 -0
- package/esm/seal/src/rules/core/required.mjs.map +1 -0
- package/esm/seal/src/rules/core/union.mjs +30 -0
- package/esm/seal/src/rules/core/union.mjs.map +1 -0
- package/esm/seal/src/rules/core/when.mjs +37 -0
- package/esm/seal/src/rules/core/when.mjs.map +1 -0
- package/esm/seal/src/rules/date/date-comparison-rules.mjs +78 -0
- package/esm/seal/src/rules/date/date-comparison-rules.mjs.map +1 -0
- package/esm/seal/src/rules/date/date-day-rules.mjs +49 -0
- package/esm/seal/src/rules/date/date-day-rules.mjs.map +1 -0
- package/esm/seal/src/rules/date/date-field-comparison-rules.mjs +82 -0
- package/esm/seal/src/rules/date/date-field-comparison-rules.mjs.map +1 -0
- package/esm/seal/src/rules/date/date-period-rules.mjs +399 -0
- package/esm/seal/src/rules/date/date-period-rules.mjs.map +1 -0
- package/esm/seal/src/rules/date/date-relative-rules.mjs +55 -0
- package/esm/seal/src/rules/date/date-relative-rules.mjs.map +1 -0
- package/esm/seal/src/rules/date/date-special-rules.mjs +58 -0
- package/esm/seal/src/rules/date/date-special-rules.mjs.map +1 -0
- package/esm/seal/src/rules/date/date.mjs +235 -0
- package/esm/seal/src/rules/date/date.mjs.map +1 -0
- package/esm/seal/src/rules/date/index.mjs +9 -0
- package/esm/seal/src/rules/file/dimensions.mjs +3 -0
- package/esm/seal/src/rules/file/file-size.mjs +3 -0
- package/esm/seal/src/rules/file/index.mjs +4 -0
- package/esm/seal/src/rules/index.mjs +54 -0
- package/esm/seal/src/rules/length/index.mjs +3 -0
- package/esm/seal/src/rules/length/length-rules.mjs +98 -0
- package/esm/seal/src/rules/length/length-rules.mjs.map +1 -0
- package/esm/seal/src/rules/number/index.mjs +3 -0
- package/esm/seal/src/rules/number/number-rules.mjs +197 -0
- package/esm/seal/src/rules/number/number-rules.mjs.map +1 -0
- package/esm/seal/src/rules/scalar/accepted-rule.mjs +116 -0
- package/esm/seal/src/rules/scalar/accepted-rule.mjs.map +1 -0
- package/esm/seal/src/rules/scalar/declined-rule.mjs +116 -0
- package/esm/seal/src/rules/scalar/declined-rule.mjs.map +1 -0
- package/esm/seal/src/rules/scalar/index.mjs +4 -0
- package/esm/seal/src/rules/string/alpha.mjs +41 -0
- package/esm/seal/src/rules/string/alpha.mjs.map +1 -0
- package/esm/seal/src/rules/string/credit-card.mjs +32 -0
- package/esm/seal/src/rules/string/credit-card.mjs.map +1 -0
- package/esm/seal/src/rules/string/email.mjs +20 -0
- package/esm/seal/src/rules/string/email.mjs.map +1 -0
- package/esm/seal/src/rules/string/id-formats.mjs +104 -0
- package/esm/seal/src/rules/string/id-formats.mjs.map +1 -0
- package/esm/seal/src/rules/string/index.mjs +13 -0
- package/esm/seal/src/rules/string/ip.mjs +66 -0
- package/esm/seal/src/rules/string/ip.mjs.map +1 -0
- package/esm/seal/src/rules/string/matches.mjs +4 -0
- package/esm/seal/src/rules/string/pattern.mjs +20 -0
- package/esm/seal/src/rules/string/pattern.mjs.map +1 -0
- package/esm/seal/src/rules/string/string-comparison.mjs +56 -0
- package/esm/seal/src/rules/string/string-comparison.mjs.map +1 -0
- package/esm/seal/src/rules/string/strong-password-rule.mjs +31 -0
- package/esm/seal/src/rules/string/strong-password-rule.mjs.map +1 -0
- package/esm/seal/src/rules/string/url.mjs +23 -0
- package/esm/seal/src/rules/string/url.mjs.map +1 -0
- package/esm/seal/src/rules/string/without-whitespace.mjs +19 -0
- package/esm/seal/src/rules/string/without-whitespace.mjs.map +1 -0
- package/esm/seal/src/standard-schema/json-schema.mjs +81 -0
- package/esm/seal/src/standard-schema/json-schema.mjs.map +1 -0
- package/esm/seal/src/standard-schema/map-result.mjs +26 -0
- package/esm/seal/src/standard-schema/map-result.mjs.map +1 -0
- package/esm/seal/src/types/date-types.mjs +17 -0
- package/esm/seal/src/types/date-types.mjs.map +1 -0
- package/esm/seal/src/validators/any-validator.mjs +38 -0
- package/esm/seal/src/validators/any-validator.mjs.map +1 -0
- package/esm/seal/src/validators/array-validator.mjs +177 -0
- package/esm/seal/src/validators/array-validator.mjs.map +1 -0
- package/esm/seal/src/validators/base-validator.mjs +691 -0
- package/esm/seal/src/validators/base-validator.mjs.map +1 -0
- package/esm/seal/src/validators/boolean-validator.mjs +126 -0
- package/esm/seal/src/validators/boolean-validator.mjs.map +1 -0
- package/esm/seal/src/validators/computed-validator.mjs +118 -0
- package/esm/seal/src/validators/computed-validator.mjs.map +1 -0
- package/esm/seal/src/validators/date-validator.mjs +639 -0
- package/esm/seal/src/validators/date-validator.mjs.map +1 -0
- package/esm/seal/src/validators/discriminated-union-validator.mjs +129 -0
- package/esm/seal/src/validators/discriminated-union-validator.mjs.map +1 -0
- package/esm/seal/src/validators/float-validator.mjs +18 -0
- package/esm/seal/src/validators/float-validator.mjs.map +1 -0
- package/esm/seal/src/validators/index.mjs +28 -0
- package/esm/seal/src/validators/instanceof-validator.mjs +64 -0
- package/esm/seal/src/validators/instanceof-validator.mjs.map +1 -0
- package/esm/seal/src/validators/int-validator.mjs +30 -0
- package/esm/seal/src/validators/int-validator.mjs.map +1 -0
- package/esm/seal/src/validators/lazy-validator.mjs +87 -0
- package/esm/seal/src/validators/lazy-validator.mjs.map +1 -0
- package/esm/seal/src/validators/literal-validator.mjs +71 -0
- package/esm/seal/src/validators/literal-validator.mjs.map +1 -0
- package/esm/seal/src/validators/managed-validator.mjs +39 -0
- package/esm/seal/src/validators/managed-validator.mjs.map +1 -0
- package/esm/seal/src/validators/methods/equality-conditional-methods.mjs +129 -0
- package/esm/seal/src/validators/methods/equality-conditional-methods.mjs.map +1 -0
- package/esm/seal/src/validators/methods/forbidden-methods.mjs +131 -0
- package/esm/seal/src/validators/methods/forbidden-methods.mjs.map +1 -0
- package/esm/seal/src/validators/methods/present-methods.mjs +235 -0
- package/esm/seal/src/validators/methods/present-methods.mjs.map +1 -0
- package/esm/seal/src/validators/methods/required-methods.mjs +347 -0
- package/esm/seal/src/validators/methods/required-methods.mjs.map +1 -0
- package/esm/seal/src/validators/number-validator.mjs +310 -0
- package/esm/seal/src/validators/number-validator.mjs.map +1 -0
- package/esm/seal/src/validators/numeric-validator.mjs +27 -0
- package/esm/seal/src/validators/numeric-validator.mjs.map +1 -0
- package/esm/seal/src/validators/object-validator.mjs +442 -0
- package/esm/seal/src/validators/object-validator.mjs.map +1 -0
- package/esm/seal/src/validators/primitive-validator.mjs +89 -0
- package/esm/seal/src/validators/primitive-validator.mjs.map +1 -0
- package/esm/seal/src/validators/record-validator.mjs +112 -0
- package/esm/seal/src/validators/record-validator.mjs.map +1 -0
- package/esm/seal/src/validators/scalar-validator.mjs +172 -0
- package/esm/seal/src/validators/scalar-validator.mjs.map +1 -0
- package/esm/seal/src/validators/string-validator.mjs +459 -0
- package/esm/seal/src/validators/string-validator.mjs.map +1 -0
- package/esm/seal/src/validators/tuple-validator.mjs +134 -0
- package/esm/seal/src/validators/tuple-validator.mjs.map +1 -0
- package/esm/seal/src/validators/union-validator.mjs +66 -0
- package/esm/seal/src/validators/union-validator.mjs.map +1 -0
- package/llms-full.txt +5 -1
- package/package.json +5 -5
- package/skills/send-ad-hoc/SKILL.md +1 -1
- package/skills/use-in-app/SKILL.md +4 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import { __toESM } from "../../../../_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { prepareConsumerSubscription } from "../../message-managers/prepare-consumer-subscription.mjs";
|
|
3
|
+
import { RabbitMQChannel } from "./rabbitmq-channel.mjs";
|
|
4
|
+
import { EventEmitter } from "node:events";
|
|
5
|
+
|
|
6
|
+
//#region ../herald/src/drivers/rabbitmq/rabbitmq-driver.ts
|
|
7
|
+
/**
|
|
8
|
+
* The single amqplib load, shared by every caller.
|
|
9
|
+
*
|
|
10
|
+
* Memoized as a promise rather than as a resolved value so the loader stays
|
|
11
|
+
* idempotent: without it, two callers arriving before the first `import()`
|
|
12
|
+
* settles would each start their own load and the last writer would win, so a
|
|
13
|
+
* caller could end up observing a module instance it never awaited.
|
|
14
|
+
*
|
|
15
|
+
* Resolves to `undefined` when amqplib is not installed.
|
|
16
|
+
*/
|
|
17
|
+
let amqplibModulePromise;
|
|
18
|
+
/**
|
|
19
|
+
* Installation instructions for amqplib
|
|
20
|
+
*/
|
|
21
|
+
const AMQPLIB_INSTALL_INSTRUCTIONS = `
|
|
22
|
+
RabbitMQ driver requires the amqplib package.
|
|
23
|
+
Install it with:
|
|
24
|
+
|
|
25
|
+
npx warlock add herald --driver=rabbitmq
|
|
26
|
+
|
|
27
|
+
Or manually:
|
|
28
|
+
|
|
29
|
+
npm install amqplib
|
|
30
|
+
pnpm add amqplib
|
|
31
|
+
yarn add amqplib
|
|
32
|
+
`.trim();
|
|
33
|
+
/**
|
|
34
|
+
* Strip `user:password@` credentials from any `amqp(s)://` URL embedded in a
|
|
35
|
+
* string. The connection URL carries plaintext broker credentials, and
|
|
36
|
+
* amqplib/Node's URL parser commonly echoes the offending URL verbatim in a
|
|
37
|
+
* malformed-URL error (e.g. an unencoded `@`/`:` in the password) — applied
|
|
38
|
+
* to every error `connect()` surfaces so a credential never reaches whatever
|
|
39
|
+
* the host app does with a thrown connection error (console.error,
|
|
40
|
+
* structured logging, an error tracker).
|
|
41
|
+
*/
|
|
42
|
+
function redactAmqpCredentials(message) {
|
|
43
|
+
return message.replace(/(amqps?:\/\/)[^/@\s]+@/gi, "$1****:****@");
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Load amqplib, reusing the single shared load for every caller.
|
|
47
|
+
*
|
|
48
|
+
* @returns The amqplib module, or `undefined` when it is not installed.
|
|
49
|
+
*/
|
|
50
|
+
function loadAmqplibModule() {
|
|
51
|
+
if (!amqplibModulePromise) amqplibModulePromise = import("../../../../node_modules/amqplib/channel_api.mjs").then((m) => /* @__PURE__ */ __toESM(m.default, 1)).catch(() => void 0);
|
|
52
|
+
return amqplibModulePromise;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* RabbitMQ Driver
|
|
56
|
+
*
|
|
57
|
+
* Implementation of BrokerDriverContract for RabbitMQ/AMQP.
|
|
58
|
+
*
|
|
59
|
+
* **Important:** This driver requires the `amqplib` package to be installed.
|
|
60
|
+
* Install it with: `npx warlock add herald --driver=rabbitmq` or `npm install amqplib`
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const driver = new RabbitMQDriver({
|
|
65
|
+
* driver: "rabbitmq",
|
|
66
|
+
* host: "localhost",
|
|
67
|
+
* port: 5672,
|
|
68
|
+
* username: "guest",
|
|
69
|
+
* password: "guest",
|
|
70
|
+
* });
|
|
71
|
+
*
|
|
72
|
+
* await driver.connect();
|
|
73
|
+
* const channel = driver.channel("user.created");
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
var RabbitMQDriver = class {
|
|
77
|
+
name = "rabbitmq";
|
|
78
|
+
consumers = [];
|
|
79
|
+
options;
|
|
80
|
+
events = new EventEmitter();
|
|
81
|
+
channels = /* @__PURE__ */ new Map();
|
|
82
|
+
connection = null;
|
|
83
|
+
amqpChannel = null;
|
|
84
|
+
_isConnected = false;
|
|
85
|
+
/**
|
|
86
|
+
* Create a new RabbitMQ driver
|
|
87
|
+
*
|
|
88
|
+
* @param options - RabbitMQ connection options
|
|
89
|
+
*/
|
|
90
|
+
constructor(options) {
|
|
91
|
+
this.options = options;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Whether connected to RabbitMQ
|
|
95
|
+
*/
|
|
96
|
+
get isConnected() {
|
|
97
|
+
return this._isConnected;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Subscribe the given consumer class to the driver
|
|
101
|
+
*
|
|
102
|
+
* @param consumer - Consumer class to subscribe
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* driver.subscribe(UserUpdatedConsumer);
|
|
107
|
+
* ```
|
|
108
|
+
*/
|
|
109
|
+
subscribe(Consumer) {
|
|
110
|
+
if (this.isConnected) this.channel(Consumer.eventName).subscribe(prepareConsumerSubscription(Consumer, (error, eventName) => {
|
|
111
|
+
this.events.emit("error", error, eventName);
|
|
112
|
+
}), { consumerId: Consumer.consumerId });
|
|
113
|
+
else this.consumers.push(Consumer);
|
|
114
|
+
return () => {
|
|
115
|
+
this.unsubscribe(Consumer);
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Unsubscribe the given consumer
|
|
120
|
+
*/
|
|
121
|
+
unsubscribe(Consumer) {
|
|
122
|
+
if (this.isConnected) this.channel(Consumer.eventName).unsubscribeById(Consumer.consumerId);
|
|
123
|
+
const index = this.consumers.indexOf(Consumer);
|
|
124
|
+
if (index > -1) this.consumers.splice(index, 1);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Publish the given event message.
|
|
128
|
+
* Auto-creates the channel if it hasn't been accessed before.
|
|
129
|
+
*/
|
|
130
|
+
publish(event) {
|
|
131
|
+
this.channel(event.eventName).publish(event.serialize());
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Connect to RabbitMQ
|
|
135
|
+
*/
|
|
136
|
+
async connect() {
|
|
137
|
+
const amqplib = await loadAmqplibModule();
|
|
138
|
+
if (!amqplib) throw new Error(`amqplib is not installed.\n\n${AMQPLIB_INSTALL_INSTRUCTIONS}`);
|
|
139
|
+
try {
|
|
140
|
+
const url = this.buildConnectionUrl();
|
|
141
|
+
const connectOptions = {
|
|
142
|
+
heartbeat: this.options.heartbeat ?? 60,
|
|
143
|
+
timeout: this.options.connectionTimeout,
|
|
144
|
+
...this.options.clientOptions
|
|
145
|
+
};
|
|
146
|
+
this.connection = await amqplib.connect(url, connectOptions);
|
|
147
|
+
this.amqpChannel = await this.connection.createChannel();
|
|
148
|
+
if (this.options.prefetch) await this.amqpChannel.prefetch(this.options.prefetch);
|
|
149
|
+
this._isConnected = true;
|
|
150
|
+
this.events.emit("connected");
|
|
151
|
+
for (const consumer of this.consumers) this.subscribe(consumer);
|
|
152
|
+
this.consumers.length = 0;
|
|
153
|
+
this.connection.on("close", () => {
|
|
154
|
+
this._isConnected = false;
|
|
155
|
+
this.events.emit("disconnected");
|
|
156
|
+
if (this.options.reconnect !== false) this.handleReconnect();
|
|
157
|
+
});
|
|
158
|
+
this.connection.on("error", (error) => {
|
|
159
|
+
this.events.emit("error", error);
|
|
160
|
+
});
|
|
161
|
+
} catch (error) {
|
|
162
|
+
this._isConnected = false;
|
|
163
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
164
|
+
throw new Error(`Failed to connect to RabbitMQ: ${redactAmqpCredentials(message)}`);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* Build connection URL from options
|
|
169
|
+
*/
|
|
170
|
+
buildConnectionUrl() {
|
|
171
|
+
if (this.options.uri) return this.options.uri;
|
|
172
|
+
const protocol = "amqp";
|
|
173
|
+
const host = this.options.host ?? "localhost";
|
|
174
|
+
const port = this.options.port ?? 5672;
|
|
175
|
+
const vhost = this.options.vhost ?? "/";
|
|
176
|
+
return `${protocol}://${encodeURIComponent(this.options.username ?? "guest")}:${encodeURIComponent(this.options.password ?? "guest")}@${host}:${port}/${encodeURIComponent(vhost)}`;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Handle reconnection
|
|
180
|
+
*/
|
|
181
|
+
async handleReconnect() {
|
|
182
|
+
const delay = this.options.reconnectDelay ?? 5e3;
|
|
183
|
+
let attempt = 0;
|
|
184
|
+
const tryReconnect = async () => {
|
|
185
|
+
attempt++;
|
|
186
|
+
this.events.emit("reconnecting", attempt);
|
|
187
|
+
try {
|
|
188
|
+
await this.connect();
|
|
189
|
+
} catch {
|
|
190
|
+
setTimeout(tryReconnect, delay);
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
setTimeout(tryReconnect, delay);
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Disconnect from RabbitMQ
|
|
197
|
+
*/
|
|
198
|
+
async disconnect() {
|
|
199
|
+
if (this.amqpChannel) {
|
|
200
|
+
try {
|
|
201
|
+
await this.amqpChannel.close();
|
|
202
|
+
} catch {}
|
|
203
|
+
this.amqpChannel = null;
|
|
204
|
+
}
|
|
205
|
+
if (this.connection) {
|
|
206
|
+
try {
|
|
207
|
+
await this.connection.close();
|
|
208
|
+
} catch {}
|
|
209
|
+
this.connection = null;
|
|
210
|
+
}
|
|
211
|
+
this._isConnected = false;
|
|
212
|
+
this.events.emit("disconnected");
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Register event listener
|
|
216
|
+
*/
|
|
217
|
+
on(event, listener) {
|
|
218
|
+
this.events.on(event, listener);
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Remove event listener
|
|
222
|
+
*/
|
|
223
|
+
off(event, listener) {
|
|
224
|
+
this.events.off(event, listener);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Get or create a channel
|
|
228
|
+
*/
|
|
229
|
+
channel(name, options) {
|
|
230
|
+
const existing = this.channels.get(name);
|
|
231
|
+
if (existing) return existing;
|
|
232
|
+
const channel = new RabbitMQChannel(name, this.amqpChannel, options);
|
|
233
|
+
this.channels.set(name, channel);
|
|
234
|
+
return channel;
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Start consuming messages
|
|
238
|
+
*/
|
|
239
|
+
async startConsuming() {}
|
|
240
|
+
/**
|
|
241
|
+
* Stop consuming messages from all subscribed channels.
|
|
242
|
+
* Gracefully cancels all active consumers.
|
|
243
|
+
*/
|
|
244
|
+
async stopConsuming() {
|
|
245
|
+
const stops = Array.from(this.channels.values()).map((channel) => channel.stopConsuming());
|
|
246
|
+
await Promise.all(stops);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Health check
|
|
250
|
+
*/
|
|
251
|
+
async healthCheck() {
|
|
252
|
+
if (!this._isConnected || !this.connection) return {
|
|
253
|
+
healthy: false,
|
|
254
|
+
error: "Not connected to RabbitMQ"
|
|
255
|
+
};
|
|
256
|
+
const start = Date.now();
|
|
257
|
+
try {
|
|
258
|
+
await this.amqpChannel.checkQueue("amq.rabbitmq.reply-to").catch(() => {});
|
|
259
|
+
return {
|
|
260
|
+
healthy: true,
|
|
261
|
+
latency: Date.now() - start
|
|
262
|
+
};
|
|
263
|
+
} catch (error) {
|
|
264
|
+
return {
|
|
265
|
+
healthy: false,
|
|
266
|
+
error: error instanceof Error ? error.message : String(error),
|
|
267
|
+
latency: Date.now() - start
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Get all channel names
|
|
273
|
+
*/
|
|
274
|
+
getChannelNames() {
|
|
275
|
+
return Array.from(this.channels.keys());
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Close a specific channel
|
|
279
|
+
*/
|
|
280
|
+
async closeChannel(name) {
|
|
281
|
+
const channel = this.channels.get(name);
|
|
282
|
+
if (channel) {
|
|
283
|
+
await channel.delete();
|
|
284
|
+
this.channels.delete(name);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Get the raw AMQP channel (for advanced use)
|
|
289
|
+
*/
|
|
290
|
+
getRawChannel() {
|
|
291
|
+
return this.amqpChannel;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Get the raw connection (for advanced use)
|
|
295
|
+
*/
|
|
296
|
+
getRawConnection() {
|
|
297
|
+
return this.connection;
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
//#endregion
|
|
302
|
+
export { RabbitMQDriver };
|
|
303
|
+
//# sourceMappingURL=rabbitmq-driver.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rabbitmq-driver.mjs","names":[],"sources":["../../../../../../../../../../herald/src/drivers/rabbitmq/rabbitmq-driver.ts"],"sourcesContent":["import { EventEmitter } from \"node:events\";\r\nimport type { BrokerDriverContract, ChannelContract } from \"../../contracts\";\r\nimport { EventMessage } from \"../../message-managers/event-message\";\r\nimport { EventConsumerClass } from \"../../message-managers/types\";\r\nimport type {\r\n BrokerDriverType,\r\n BrokerEvent,\r\n BrokerEventListener,\r\n ChannelOptions,\r\n HealthCheckResult,\r\n RabbitMQConnectionOptions,\r\n} from \"../../types\";\r\nimport { prepareConsumerSubscription } from \"./../../message-managers/prepare-consumer-subscription\";\r\nimport { RabbitMQChannel } from \"./rabbitmq-channel\";\r\n\r\n// ============================================================\r\n// Lazy-loaded amqplib Module\r\n// ============================================================\r\n\r\n/**\r\n * Shape of the lazily-imported amqplib module\r\n */\r\ntype AmqplibModule = typeof import(\"amqplib\");\r\n\r\n/**\r\n * The single amqplib load, shared by every caller.\r\n *\r\n * Memoized as a promise rather than as a resolved value so the loader stays\r\n * idempotent: without it, two callers arriving before the first `import()`\r\n * settles would each start their own load and the last writer would win, so a\r\n * caller could end up observing a module instance it never awaited.\r\n *\r\n * Resolves to `undefined` when amqplib is not installed.\r\n */\r\nlet amqplibModulePromise: Promise<AmqplibModule | undefined> | undefined;\r\n\r\n/**\r\n * Installation instructions for amqplib\r\n */\r\nconst AMQPLIB_INSTALL_INSTRUCTIONS = `\r\nRabbitMQ driver requires the amqplib package.\r\nInstall it with:\r\n\r\n npx warlock add herald --driver=rabbitmq\r\n\r\nOr manually:\r\n\r\n npm install amqplib\r\n pnpm add amqplib\r\n yarn add amqplib\r\n`.trim();\r\n\r\n/**\r\n * Strip `user:password@` credentials from any `amqp(s)://` URL embedded in a\r\n * string. The connection URL carries plaintext broker credentials, and\r\n * amqplib/Node's URL parser commonly echoes the offending URL verbatim in a\r\n * malformed-URL error (e.g. an unencoded `@`/`:` in the password) — applied\r\n * to every error `connect()` surfaces so a credential never reaches whatever\r\n * the host app does with a thrown connection error (console.error,\r\n * structured logging, an error tracker).\r\n */\r\nfunction redactAmqpCredentials(message: string): string {\r\n return message.replace(/(amqps?:\\/\\/)[^/@\\s]+@/gi, \"$1****:****@\");\r\n}\r\n\r\n/**\r\n * Load amqplib, reusing the single shared load for every caller.\r\n *\r\n * @returns The amqplib module, or `undefined` when it is not installed.\r\n */\r\nfunction loadAmqplibModule(): Promise<AmqplibModule | undefined> {\r\n if (!amqplibModulePromise) {\r\n amqplibModulePromise = import(\"amqplib\").catch(() => undefined);\r\n }\r\n\r\n return amqplibModulePromise;\r\n}\r\n\r\n// ============================================================\r\n// RabbitMQ Driver\r\n// ============================================================\r\n\r\n/**\r\n * RabbitMQ Driver\r\n *\r\n * Implementation of BrokerDriverContract for RabbitMQ/AMQP.\r\n *\r\n * **Important:** This driver requires the `amqplib` package to be installed.\r\n * Install it with: `npx warlock add herald --driver=rabbitmq` or `npm install amqplib`\r\n *\r\n * @example\r\n * ```typescript\r\n * const driver = new RabbitMQDriver({\r\n * driver: \"rabbitmq\",\r\n * host: \"localhost\",\r\n * port: 5672,\r\n * username: \"guest\",\r\n * password: \"guest\",\r\n * });\r\n *\r\n * await driver.connect();\r\n * const channel = driver.channel(\"user.created\");\r\n * ```\r\n */\r\nexport class RabbitMQDriver implements BrokerDriverContract {\r\n public readonly name = \"rabbitmq\" as const;\r\n\r\n public readonly consumers: EventConsumerClass[] = [];\r\n\r\n private readonly options: RabbitMQConnectionOptions;\r\n private readonly events = new EventEmitter();\r\n private readonly channels = new Map<string, ChannelContract<any>>();\r\n\r\n private connection: any = null;\r\n private amqpChannel: any = null;\r\n private _isConnected = false;\r\n\r\n /**\r\n * Create a new RabbitMQ driver\r\n *\r\n * @param options - RabbitMQ connection options\r\n */\r\n public constructor(options: RabbitMQConnectionOptions) {\r\n this.options = options;\r\n }\r\n\r\n /**\r\n * Whether connected to RabbitMQ\r\n */\r\n public get isConnected(): boolean {\r\n return this._isConnected;\r\n }\r\n\r\n /**\r\n * Subscribe the given consumer class to the driver\r\n *\r\n * @param consumer - Consumer class to subscribe\r\n *\r\n * @example\r\n * ```typescript\r\n * driver.subscribe(UserUpdatedConsumer);\r\n * ```\r\n */\r\n public subscribe(Consumer: EventConsumerClass) {\r\n if (this.isConnected) {\r\n this.channel(Consumer.eventName).subscribe(\r\n prepareConsumerSubscription(Consumer, (error, eventName) => {\r\n this.events.emit(\"error\", error, eventName);\r\n }),\r\n {\r\n consumerId: Consumer.consumerId,\r\n },\r\n );\r\n } else {\r\n this.consumers.push(Consumer);\r\n }\r\n\r\n return () => {\r\n this.unsubscribe(Consumer);\r\n };\r\n }\r\n\r\n /**\r\n * Unsubscribe the given consumer\r\n */\r\n public unsubscribe(Consumer: EventConsumerClass): void {\r\n if (this.isConnected) {\r\n this.channel(Consumer.eventName).unsubscribeById(Consumer.consumerId);\r\n }\r\n const index = this.consumers.indexOf(Consumer);\r\n if (index > -1) {\r\n this.consumers.splice(index, 1);\r\n }\r\n }\r\n\r\n /**\r\n * Publish the given event message.\r\n * Auto-creates the channel if it hasn't been accessed before.\r\n */\r\n public publish<TPayload = Record<string, any>>(event: EventMessage<TPayload>): void {\r\n this.channel(event.eventName).publish(event.serialize());\r\n }\r\n\r\n /**\r\n * Connect to RabbitMQ\r\n */\r\n public async connect(): Promise<void> {\r\n const amqplib = await loadAmqplibModule();\r\n\r\n if (!amqplib) {\r\n throw new Error(`amqplib is not installed.\\n\\n${AMQPLIB_INSTALL_INSTRUCTIONS}`);\r\n }\r\n\r\n try {\r\n // Build connection URL\r\n const url = this.buildConnectionUrl();\r\n\r\n // Build connection options merging our options with native client options\r\n const connectOptions = {\r\n heartbeat: this.options.heartbeat ?? 60,\r\n timeout: this.options.connectionTimeout,\r\n // Merge native amqplib client options\r\n ...this.options.clientOptions,\r\n };\r\n\r\n // Connect using cached module\r\n this.connection = await amqplib.connect(url, connectOptions);\r\n\r\n // Create channel\r\n this.amqpChannel = await this.connection.createChannel();\r\n\r\n // Set prefetch if specified\r\n if (this.options.prefetch) {\r\n await this.amqpChannel.prefetch(this.options.prefetch);\r\n }\r\n\r\n this._isConnected = true;\r\n this.events.emit(\"connected\");\r\n\r\n for (const consumer of this.consumers) {\r\n this.subscribe(consumer);\r\n }\r\n\r\n this.consumers.length = 0;\r\n\r\n // Handle connection close\r\n this.connection.on(\"close\", () => {\r\n this._isConnected = false;\r\n this.events.emit(\"disconnected\");\r\n\r\n if (this.options.reconnect !== false) {\r\n this.handleReconnect();\r\n }\r\n });\r\n\r\n // Handle errors\r\n this.connection.on(\"error\", (error: Error) => {\r\n this.events.emit(\"error\", error);\r\n });\r\n } catch (error) {\r\n this._isConnected = false;\r\n const message = error instanceof Error ? error.message : String(error);\r\n throw new Error(`Failed to connect to RabbitMQ: ${redactAmqpCredentials(message)}`);\r\n }\r\n }\r\n\r\n /**\r\n * Build connection URL from options\r\n */\r\n private buildConnectionUrl(): string {\r\n if (this.options.uri) {\r\n return this.options.uri;\r\n }\r\n\r\n const protocol = \"amqp\";\r\n const host = this.options.host ?? \"localhost\";\r\n const port = this.options.port ?? 5672;\r\n const vhost = this.options.vhost ?? \"/\";\r\n // URI-encoded so a credential containing a reserved URL character\r\n // (`@`, `:`, `/`, whitespace — common in generated secrets) can't produce\r\n // a malformed URL whose parser error echoes the raw credential back.\r\n const username = encodeURIComponent(this.options.username ?? \"guest\");\r\n const password = encodeURIComponent(this.options.password ?? \"guest\");\r\n\r\n const encodedVhost = encodeURIComponent(vhost);\r\n\r\n return `${protocol}://${username}:${password}@${host}:${port}/${encodedVhost}`;\r\n }\r\n\r\n /**\r\n * Handle reconnection\r\n */\r\n private async handleReconnect(): Promise<void> {\r\n const delay = this.options.reconnectDelay ?? 5000;\r\n let attempt = 0;\r\n\r\n const tryReconnect = async () => {\r\n attempt++;\r\n this.events.emit(\"reconnecting\", attempt);\r\n\r\n try {\r\n await this.connect();\r\n } catch {\r\n setTimeout(tryReconnect, delay);\r\n }\r\n };\r\n\r\n setTimeout(tryReconnect, delay);\r\n }\r\n\r\n /**\r\n * Disconnect from RabbitMQ\r\n */\r\n public async disconnect(): Promise<void> {\r\n if (this.amqpChannel) {\r\n try {\r\n await this.amqpChannel.close();\r\n } catch {\r\n // Ignore close errors\r\n }\r\n this.amqpChannel = null;\r\n }\r\n\r\n if (this.connection) {\r\n try {\r\n await this.connection.close();\r\n } catch {\r\n // Ignore close errors\r\n }\r\n this.connection = null;\r\n }\r\n\r\n this._isConnected = false;\r\n this.events.emit(\"disconnected\");\r\n }\r\n\r\n /**\r\n * Register event listener\r\n */\r\n public on(event: BrokerEvent, listener: BrokerEventListener): void {\r\n this.events.on(event, listener as any);\r\n }\r\n\r\n /**\r\n * Remove event listener\r\n */\r\n public off(event: BrokerEvent, listener: BrokerEventListener): void {\r\n this.events.off(event, listener as any);\r\n }\r\n\r\n /**\r\n * Get or create a channel\r\n */\r\n public channel<TPayload = unknown>(\r\n name: string,\r\n options?: ChannelOptions<TPayload>,\r\n ): ChannelContract<TPayload> {\r\n // Check cache\r\n const existing = this.channels.get(name);\r\n if (existing) {\r\n return existing as ChannelContract<TPayload>;\r\n }\r\n\r\n // Create new channel\r\n const channel = new RabbitMQChannel<TPayload>(name, this.amqpChannel, options);\r\n\r\n this.channels.set(name, channel);\r\n return channel;\r\n }\r\n\r\n /**\r\n * Start consuming messages\r\n */\r\n public async startConsuming(): Promise<void> {\r\n // Channels start consuming when subscribe() is called\r\n // This method is for batch start if needed\r\n }\r\n\r\n /**\r\n * Stop consuming messages from all subscribed channels.\r\n * Gracefully cancels all active consumers.\r\n */\r\n public async stopConsuming(): Promise<void> {\r\n const stops = Array.from(this.channels.values()).map(channel =>\r\n (channel as RabbitMQChannel<any>).stopConsuming(),\r\n );\r\n await Promise.all(stops);\r\n }\r\n\r\n /**\r\n * Health check\r\n */\r\n public async healthCheck(): Promise<HealthCheckResult> {\r\n if (!this._isConnected || !this.connection) {\r\n return {\r\n healthy: false,\r\n error: \"Not connected to RabbitMQ\",\r\n };\r\n }\r\n\r\n const start = Date.now();\r\n\r\n try {\r\n // Simple check - verify channel is open\r\n await this.amqpChannel.checkQueue(\"amq.rabbitmq.reply-to\").catch(() => {\r\n // Queue might not exist, but if we get here, connection is alive\r\n });\r\n\r\n return {\r\n healthy: true,\r\n latency: Date.now() - start,\r\n };\r\n } catch (error) {\r\n return {\r\n healthy: false,\r\n error: error instanceof Error ? error.message : String(error),\r\n latency: Date.now() - start,\r\n };\r\n }\r\n }\r\n\r\n /**\r\n * Get all channel names\r\n */\r\n public getChannelNames(): string[] {\r\n return Array.from(this.channels.keys());\r\n }\r\n\r\n /**\r\n * Close a specific channel\r\n */\r\n public async closeChannel(name: string): Promise<void> {\r\n const channel = this.channels.get(name);\r\n if (channel) {\r\n await channel.delete();\r\n this.channels.delete(name);\r\n }\r\n }\r\n\r\n /**\r\n * Get the raw AMQP channel (for advanced use)\r\n */\r\n public getRawChannel(): any {\r\n return this.amqpChannel;\r\n }\r\n\r\n /**\r\n * Get the raw connection (for advanced use)\r\n */\r\n public getRawConnection(): any {\r\n return this.connection;\r\n }\r\n}\r\n"],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { RabbitMQChannel } from "./drivers/rabbitmq/rabbitmq-channel.mjs";
|
|
2
|
+
import { RabbitMQDriver } from "./drivers/rabbitmq/rabbitmq-driver.mjs";
|
|
3
|
+
import { Broker } from "./communicators/broker.mjs";
|
|
4
|
+
import { MissingBrokerError, brokerRegistry } from "./communicators/broker-registry.mjs";
|
|
5
|
+
import "./communicators/index.mjs";
|
|
6
|
+
import "./drivers/index.mjs";
|
|
7
|
+
import { connectToBroker, herald, heraldChannel, publishEvent, subscribeConsumer } from "./utils/connect-to-broker.mjs";
|
|
8
|
+
import "./utils/index.mjs";
|
|
9
|
+
import { Consumable, pendingSubscribers } from "./decorators/consumable.mjs";
|
|
10
|
+
import "./decorators/index.mjs";
|
|
11
|
+
import { EventConsumer, defineConsumer } from "./message-managers/event-consumer.mjs";
|
|
12
|
+
import { EventMessage, defineEvent } from "./message-managers/event-message.mjs";
|
|
13
|
+
import "./message-managers/index.mjs";
|
|
14
|
+
import { heraldBroadcast } from "./use-case-broadcast.mjs";
|
|
15
|
+
|
|
16
|
+
export { Broker, Consumable, EventConsumer, EventMessage, MissingBrokerError, RabbitMQChannel, RabbitMQDriver, brokerRegistry, connectToBroker, defineConsumer, defineEvent, herald, heraldBroadcast, heraldChannel, pendingSubscribers, publishEvent, subscribeConsumer };
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { v } from "../../../seal/src/factory/validators.mjs";
|
|
2
|
+
import "../../../seal/src/index.mjs";
|
|
3
|
+
import { Consumable } from "../decorators/consumable.mjs";
|
|
4
|
+
import "../decorators/index.mjs";
|
|
5
|
+
import { randomUUID } from "crypto";
|
|
6
|
+
|
|
7
|
+
//#region ../herald/src/message-managers/event-consumer.ts
|
|
8
|
+
/**
|
|
9
|
+
* This class is used to be part of the Herald Event Consumer Manager.
|
|
10
|
+
* It should be used to consume events from Either RabbitMQ or Kafka through Herald
|
|
11
|
+
*
|
|
12
|
+
* It's highly recommended using it instead of declaring manual channel namd and subscribing to event
|
|
13
|
+
*/
|
|
14
|
+
var EventConsumer = class {
|
|
15
|
+
/**
|
|
16
|
+
* Event name
|
|
17
|
+
*/
|
|
18
|
+
static eventName;
|
|
19
|
+
static _consumerId;
|
|
20
|
+
static get consumerId() {
|
|
21
|
+
if (!this._consumerId) this._consumerId = randomUUID();
|
|
22
|
+
return this._consumerId;
|
|
23
|
+
}
|
|
24
|
+
get eventName() {
|
|
25
|
+
return this.constructor.eventName;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Min version accepted to be consumed by this class
|
|
29
|
+
*/
|
|
30
|
+
static minVersion;
|
|
31
|
+
/**
|
|
32
|
+
* Max version accepted to be consumed by this class
|
|
33
|
+
*/
|
|
34
|
+
static maxVersion;
|
|
35
|
+
/**
|
|
36
|
+
* Payload validation to auto reject the received event before accessing it in the handle method
|
|
37
|
+
*/
|
|
38
|
+
schema;
|
|
39
|
+
/**
|
|
40
|
+
* Determine whether this is accepted version to be used by this consumer
|
|
41
|
+
*/
|
|
42
|
+
static isAcceptedVersion(version) {
|
|
43
|
+
if (this.minVersion && version < this.minVersion) return false;
|
|
44
|
+
if (this.maxVersion && version > this.maxVersion) return false;
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Validate the given data
|
|
49
|
+
*/
|
|
50
|
+
async validate(data) {
|
|
51
|
+
if (!this.schema) return;
|
|
52
|
+
return await v.validate(this.schema, data);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* A shorthand to define an event consumer without declaring an entire class
|
|
57
|
+
*/
|
|
58
|
+
function defineConsumer(eventName, options) {
|
|
59
|
+
const Class = class AnnouncedConsumer extends EventConsumer {
|
|
60
|
+
static eventName = eventName;
|
|
61
|
+
schema = options.schema;
|
|
62
|
+
async handle(payload, event) {
|
|
63
|
+
if (options.validate) {
|
|
64
|
+
const result = await options.validate(payload);
|
|
65
|
+
if (!result || !result.isValid) return;
|
|
66
|
+
}
|
|
67
|
+
return options.handle(payload, event);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
Consumable()(Class);
|
|
71
|
+
return Class;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
//#endregion
|
|
75
|
+
export { EventConsumer, defineConsumer };
|
|
76
|
+
//# sourceMappingURL=event-consumer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-consumer.mjs","names":[],"sources":["../../../../../../../../../herald/src/message-managers/event-consumer.ts"],"sourcesContent":["/**\n * This class is used to be part of the Herald Event Consumer Manager.\n * It should be used to consume events from Either RabbitMQ or Kafka through Herald\n *\n * It's highly recommended using it instead of declaring manual channel namd and subscribing to event\n */\nimport { v, ValidationResult, type ObjectValidator } from \"@warlock.js/seal\";\nimport { randomUUID } from \"crypto\";\nimport { Consumable } from \"../decorators\";\nimport { ConsumedEventMessage, EventConsumerClass } from \"./types\";\n\nexport abstract class EventConsumer<Payload = Record<string, any>> {\n /**\n * Event name\n */\n public static eventName: string;\n\n private static _consumerId?: string;\n\n public static get consumerId(): string {\n if (!this._consumerId) {\n this._consumerId = randomUUID();\n }\n return this._consumerId;\n }\n\n public get eventName() {\n return (this.constructor as typeof EventConsumer).eventName;\n }\n\n /**\n * Min version accepted to be consumed by this class\n */\n public static minVersion?: number;\n\n /**\n * Max version accepted to be consumed by this class\n */\n public static maxVersion?: number;\n\n /**\n * Payload validation to auto reject the received event before accessing it in the handle method\n */\n public schema?: ObjectValidator;\n\n /**\n * The method that will be called when the event is received\n */\n public abstract handle(payload: Payload, event: ConsumedEventMessage): Promise<void>;\n\n /**\n * Determine whether this is accepted version to be used by this consumer\n */\n public static isAcceptedVersion(version: number): boolean {\n if (this.minVersion && version < this.minVersion) return false;\n if (this.maxVersion && version > this.maxVersion) return false;\n\n return true;\n }\n\n /**\n * Validate the given data\n */\n public async validate(data: Payload): Promise<ValidationResult | void> {\n if (!this.schema) return;\n\n return await v.validate(this.schema, data);\n }\n}\n\n/**\n * Define Consumer options\n */\ntype ConsumerOptions<Payload> = {\n /**\n * Payload validation to auto reject the received event before accessing it in the handle method\n */\n schema?: ObjectValidator;\n /**\n * Handle data\n */\n handle: (payload: Payload, event: ConsumedEventMessage) => Promise<void>;\n /**\n * Validate the payload before executing `handle`\n */\n validate?: (payload: Payload) => Promise<ValidationResult | boolean>;\n};\n\n/**\n * A shorthand to define an event consumer without declaring an entire class\n */\nexport function defineConsumer<Payload = Record<string, any>>(\n eventName: string,\n options: ConsumerOptions<Payload>,\n): EventConsumerClass {\n const Class = class AnnouncedConsumer extends EventConsumer<Payload> {\n public static eventName = eventName;\n public schema = options.schema;\n\n public async handle(payload: Payload, event: ConsumedEventMessage) {\n if (options.validate) {\n const result = await options.validate(payload);\n if (!result || !(result as ValidationResult).isValid) return;\n }\n\n return options.handle(payload, event);\n }\n };\n\n Consumable()(Class as EventConsumerClass);\n\n return Class as EventConsumerClass;\n}\n"],"mappings":";;;;;;;;;;;;;AAWA,IAAsB,gBAAtB,MAAmE;;;;CAIjE,OAAc;CAEd,OAAe;CAEf,WAAkB,aAAqB;EACrC,IAAI,CAAC,KAAK,aACR,KAAK,cAAc,WAAW;EAEhC,OAAO,KAAK;CACd;CAEA,IAAW,YAAY;EACrB,OAAQ,KAAK,YAAqC;CACpD;;;;CAKA,OAAc;;;;CAKd,OAAc;;;;CAKd,AAAO;;;;CAUP,OAAc,kBAAkB,SAA0B;EACxD,IAAI,KAAK,cAAc,UAAU,KAAK,YAAY,OAAO;EACzD,IAAI,KAAK,cAAc,UAAU,KAAK,YAAY,OAAO;EAEzD,OAAO;CACT;;;;CAKA,MAAa,SAAS,MAAiD;EACrE,IAAI,CAAC,KAAK,QAAQ;EAElB,OAAO,MAAM,EAAE,SAAS,KAAK,QAAQ,IAAI;CAC3C;AACF;;;;AAuBA,SAAgB,eACd,WACA,SACoB;CACpB,MAAM,QAAQ,MAAM,0BAA0B,cAAuB;EACnE,OAAc,YAAY;EAC1B,AAAO,SAAS,QAAQ;EAExB,MAAa,OAAO,SAAkB,OAA6B;GACjE,IAAI,QAAQ,UAAU;IACpB,MAAM,SAAS,MAAM,QAAQ,SAAS,OAAO;IAC7C,IAAI,CAAC,UAAU,CAAE,OAA4B,SAAS;GACxD;GAEA,OAAO,QAAQ,OAAO,SAAS,KAAK;EACtC;CACF;CAEA,WAAW,CAAC,CAAC,KAA2B;CAExC,OAAO;AACT"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
2
|
+
|
|
3
|
+
//#region ../herald/src/message-managers/event-message.ts
|
|
4
|
+
var EventMessage = class {
|
|
5
|
+
data;
|
|
6
|
+
/**
|
|
7
|
+
* Event version
|
|
8
|
+
*/
|
|
9
|
+
version;
|
|
10
|
+
/**
|
|
11
|
+
* Additional metadata (if any)
|
|
12
|
+
*/
|
|
13
|
+
metadata;
|
|
14
|
+
/**
|
|
15
|
+
* Event Message id
|
|
16
|
+
*/
|
|
17
|
+
messageId;
|
|
18
|
+
/**
|
|
19
|
+
* Schema of payload that will be used to determine whether this event should be published
|
|
20
|
+
*/
|
|
21
|
+
schema;
|
|
22
|
+
/**
|
|
23
|
+
* Data that will be sent with the event (Payload)
|
|
24
|
+
*/
|
|
25
|
+
toJSON() {
|
|
26
|
+
if (!this.data) throw new Error(`no Data is defined for Event: ${this.eventName}`);
|
|
27
|
+
return this.data;
|
|
28
|
+
}
|
|
29
|
+
constructor(data) {
|
|
30
|
+
this.data = data;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Serialize the event to be ready for publishing.
|
|
34
|
+
* Delegates payload resolution to toJSON() — override toJSON() to customize.
|
|
35
|
+
*
|
|
36
|
+
* @throws Error if toJSON() throws (e.g. no data provided)
|
|
37
|
+
*/
|
|
38
|
+
serialize() {
|
|
39
|
+
return {
|
|
40
|
+
payload: this.toJSON(),
|
|
41
|
+
metadata: this.metadata,
|
|
42
|
+
messageId: this.messageId ?? randomUUID(),
|
|
43
|
+
eventName: this.eventName,
|
|
44
|
+
version: this.version,
|
|
45
|
+
occurredAt: /* @__PURE__ */ new Date(),
|
|
46
|
+
__through: "EventMessage"
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* A shorthand to define an event without declaring an entire class.
|
|
52
|
+
*
|
|
53
|
+
* This factory function creates an EventMessage subclass that transforms
|
|
54
|
+
* input data (IncomingData) into a different output format (OutgoingData).
|
|
55
|
+
*
|
|
56
|
+
* @template IncomingData - The type of data passed to the constructor
|
|
57
|
+
* @template OutgoingData - The type of data returned by toJSON()
|
|
58
|
+
*
|
|
59
|
+
* @example
|
|
60
|
+
* ```typescript
|
|
61
|
+
* const UserCreatedEvent = defineEvent<User, { id: number; name: string }>(
|
|
62
|
+
* "user.created",
|
|
63
|
+
* { toJSON: (user) => user.only(["id", "name"]) }
|
|
64
|
+
* );
|
|
65
|
+
*
|
|
66
|
+
* publishEvent(new UserCreatedEvent(user));
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
function defineEvent(eventName, options = {}) {
|
|
70
|
+
return class AnnouncedEvent extends EventMessage {
|
|
71
|
+
eventName = eventName;
|
|
72
|
+
schema = options.schema;
|
|
73
|
+
constructor(data) {
|
|
74
|
+
super(data);
|
|
75
|
+
}
|
|
76
|
+
toJSON() {
|
|
77
|
+
if (!options.toJSON) return this.data;
|
|
78
|
+
return options.toJSON(this.data);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
//#endregion
|
|
84
|
+
export { EventMessage, defineEvent };
|
|
85
|
+
//# sourceMappingURL=event-message.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"event-message.mjs","names":[],"sources":["../../../../../../../../../herald/src/message-managers/event-message.ts"],"sourcesContent":["/**\n * This class is used to be part of the Herald Event Message Manager.\n * It should be used to trigger events to Either RabbitMQ or Kafka through Herald\n *\n * It's highly recommended using it instead of declaring manual channel namd and publishing data\n */\nimport { GenericObject } from \"@mongez/reinforcements\";\nimport { type ObjectValidator } from \"@warlock.js/seal\";\nimport { randomUUID } from \"crypto\";\n\nexport abstract class EventMessage<TPayload = Record<string, any>> {\n /**\n * Event Name\n */\n public abstract eventName: string;\n\n /**\n * Event version\n */\n public version?: number;\n\n /**\n * Additional metadata (if any)\n */\n public metadata?: Record<string, any>;\n\n /**\n * Event Message id\n */\n public messageId?: string;\n\n /**\n * Schema of payload that will be used to determine whether this event should be published\n */\n public schema?: ObjectValidator;\n\n /**\n * Data that will be sent with the event (Payload)\n */\n public toJSON(): TPayload {\n if (!this.data) {\n throw new Error(`no Data is defined for Event: ${this.eventName}`);\n }\n\n return this.data as TPayload;\n }\n\n public constructor(protected data?: TPayload) {}\n\n /**\n * Serialize the event to be ready for publishing.\n * Delegates payload resolution to toJSON() — override toJSON() to customize.\n *\n * @throws Error if toJSON() throws (e.g. no data provided)\n */\n public serialize() {\n const payload = this.toJSON();\n\n return {\n payload,\n metadata: this.metadata,\n messageId: this.messageId ?? randomUUID(),\n eventName: this.eventName,\n version: this.version,\n occurredAt: new Date(),\n __through: \"EventMessage\",\n };\n }\n}\n\ntype EventOptions<T> = {\n /**\n * Shapen the data that will be used\n */\n toJSON?: (data: T) => GenericObject;\n /**\n * Validation schema\n */\n schema?: ObjectValidator;\n};\n\n/**\n * Represents an EventMessage class constructor.\n *\n * @template TIncoming - The type of data accepted by the constructor\n * @template TOutgoing - The type of data returned by toJSON() (defaults to TIncoming)\n */\ntype EventMessageClass<TIncoming = Record<string, any>, TOutgoing = TIncoming> = new (\n data?: TIncoming,\n) => EventMessage<TOutgoing>;\n\n/**\n * A shorthand to define an event without declaring an entire class.\n *\n * This factory function creates an EventMessage subclass that transforms\n * input data (IncomingData) into a different output format (OutgoingData).\n *\n * @template IncomingData - The type of data passed to the constructor\n * @template OutgoingData - The type of data returned by toJSON()\n *\n * @example\n * ```typescript\n * const UserCreatedEvent = defineEvent<User, { id: number; name: string }>(\n * \"user.created\",\n * { toJSON: (user) => user.only([\"id\", \"name\"]) }\n * );\n *\n * publishEvent(new UserCreatedEvent(user));\n * ```\n */\nexport function defineEvent<IncomingData = unknown, OutgoingData = unknown>(\n eventName: string,\n options: EventOptions<IncomingData> = {},\n): EventMessageClass<IncomingData, OutgoingData> {\n // We need to use `any` here to bridge the IncomingData -> OutgoingData transformation\n // The class accepts IncomingData in constructor but outputs OutgoingData via toJSON()\n return class AnnouncedEvent extends EventMessage<OutgoingData> {\n public eventName = eventName;\n public schema = options.schema;\n\n public constructor(data?: IncomingData) {\n super(data as any);\n }\n\n public toJSON(): OutgoingData {\n if (!options.toJSON) return this.data as OutgoingData;\n\n return options.toJSON(this.data as IncomingData) as OutgoingData;\n }\n };\n}\n"],"mappings":";;;AAUA,IAAsB,eAAtB,MAAmE;CAqCpC;;;;CA5B7B,AAAO;;;;CAKP,AAAO;;;;CAKP,AAAO;;;;CAKP,AAAO;;;;CAKP,AAAO,SAAmB;EACxB,IAAI,CAAC,KAAK,MACR,MAAM,IAAI,MAAM,iCAAiC,KAAK,WAAW;EAGnE,OAAO,KAAK;CACd;CAEA,AAAO,YAAY,AAAU,MAAiB;EAAjB;CAAkB;;;;;;;CAQ/C,AAAO,YAAY;EAGjB,OAAO;GACL,SAHc,KAAK,OAGb;GACN,UAAU,KAAK;GACf,WAAW,KAAK,aAAa,WAAW;GACxC,WAAW,KAAK;GAChB,SAAS,KAAK;GACd,4BAAY,IAAI,KAAK;GACrB,WAAW;EACb;CACF;AACF;;;;;;;;;;;;;;;;;;;;AA0CA,SAAgB,YACd,WACA,UAAsC,CAAC,GACQ;CAG/C,OAAO,MAAM,uBAAuB,aAA2B;EAC7D,AAAO,YAAY;EACnB,AAAO,SAAS,QAAQ;EAExB,AAAO,YAAY,MAAqB;GACtC,MAAM,IAAW;EACnB;EAEA,AAAO,SAAuB;GAC5B,IAAI,CAAC,QAAQ,QAAQ,OAAO,KAAK;GAEjC,OAAO,QAAQ,OAAO,KAAK,IAAoB;EACjD;CACF;AACF"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//#region ../herald/src/message-managers/prepare-consumer-subscription.ts
|
|
2
|
+
function prepareConsumerSubscription(Consumer, onError) {
|
|
3
|
+
const callback = async (message, ctx) => {
|
|
4
|
+
const envelope = message.payload;
|
|
5
|
+
let eventPayload = envelope.payload;
|
|
6
|
+
if (envelope.version) {
|
|
7
|
+
if (!Consumer.isAcceptedVersion(Number(envelope.version))) {
|
|
8
|
+
ctx.ack();
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
const consumer = new Consumer();
|
|
13
|
+
if (consumer.schema) {
|
|
14
|
+
const result = await consumer.validate(eventPayload);
|
|
15
|
+
if (!result || result.isValid === false) {
|
|
16
|
+
ctx.nack();
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
eventPayload = result.data;
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
await consumer.handle(eventPayload, {
|
|
23
|
+
payload: eventPayload,
|
|
24
|
+
eventName: Consumer.eventName,
|
|
25
|
+
messageId: message.metadata.messageId,
|
|
26
|
+
occurredAt: envelope.occurredAt ? new Date(envelope.occurredAt) : void 0,
|
|
27
|
+
metadata: envelope.metadata,
|
|
28
|
+
version: envelope.version,
|
|
29
|
+
message
|
|
30
|
+
});
|
|
31
|
+
ctx.ack();
|
|
32
|
+
} catch (error) {
|
|
33
|
+
await ctx.retry();
|
|
34
|
+
if (onError) onError(error, Consumer.eventName);
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
return callback;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
export { prepareConsumerSubscription };
|
|
42
|
+
//# sourceMappingURL=prepare-consumer-subscription.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prepare-consumer-subscription.mjs","names":[],"sources":["../../../../../../../../../herald/src/message-managers/prepare-consumer-subscription.ts"],"sourcesContent":["import type { MessageHandler } from \"./../types\";\r\nimport { EventConsumerClass } from \"./types\";\r\n\r\nexport function prepareConsumerSubscription(\r\n Consumer: EventConsumerClass,\r\n onError?: (error: unknown, consumerName: string) => void,\r\n) {\r\n const callback: MessageHandler<any> = async (message, ctx) => {\r\n const envelope = message.payload;\r\n let eventPayload = envelope.payload;\r\n\r\n if (envelope.version) {\r\n if (!Consumer.isAcceptedVersion(Number(envelope.version))) {\r\n ctx.ack(); // Acknowledge but don't process\r\n return;\r\n }\r\n }\r\n\r\n const consumer = new Consumer();\r\n\r\n if (consumer.schema) {\r\n const result = await consumer.validate(eventPayload);\r\n if (!result || result.isValid === false) {\r\n ctx.nack();\r\n return;\r\n }\r\n\r\n eventPayload = result.data;\r\n }\r\n try {\r\n await consumer.handle(eventPayload, {\r\n payload: eventPayload,\r\n eventName: Consumer.eventName,\r\n messageId: message.metadata.messageId!,\r\n occurredAt: envelope.occurredAt ? new Date(envelope.occurredAt) : undefined,\r\n metadata: envelope.metadata,\r\n version: envelope.version,\r\n message,\r\n });\r\n ctx.ack(); // Auto-ack on success?\r\n } catch (error) {\r\n // Bounded retry instead of an unconditional requeue: a message that\r\n // reliably throws (bad payload, a handler bug, a downstream outage)\r\n // would otherwise be nack+requeued forever, pinning the consumer in a\r\n // hot loop. `ctx.retry()` caps redelivery and dead-letters/drops (with\r\n // a loud log) once the cap is hit.\r\n await ctx.retry();\r\n if (onError) {\r\n onError(error, Consumer.eventName);\r\n }\r\n }\r\n };\r\n\r\n return callback;\r\n}\r\n"],"mappings":";AAGA,SAAgB,4BACd,UACA,SACA;CACA,MAAM,WAAgC,OAAO,SAAS,QAAQ;EAC5D,MAAM,WAAW,QAAQ;EACzB,IAAI,eAAe,SAAS;EAE5B,IAAI,SAAS,SACX;OAAI,CAAC,SAAS,kBAAkB,OAAO,SAAS,OAAO,CAAC,GAAG;IACzD,IAAI,IAAI;IACR;GACF;;EAGF,MAAM,WAAW,IAAI,SAAS;EAE9B,IAAI,SAAS,QAAQ;GACnB,MAAM,SAAS,MAAM,SAAS,SAAS,YAAY;GACnD,IAAI,CAAC,UAAU,OAAO,YAAY,OAAO;IACvC,IAAI,KAAK;IACT;GACF;GAEA,eAAe,OAAO;EACxB;EACA,IAAI;GACF,MAAM,SAAS,OAAO,cAAc;IAClC,SAAS;IACT,WAAW,SAAS;IACpB,WAAW,QAAQ,SAAS;IAC5B,YAAY,SAAS,aAAa,IAAI,KAAK,SAAS,UAAU,IAAI;IAClE,UAAU,SAAS;IACnB,SAAS,SAAS;IAClB;GACF,CAAC;GACD,IAAI,IAAI;EACV,SAAS,OAAO;GAMd,MAAM,IAAI,MAAM;GAChB,IAAI,SACF,QAAQ,OAAO,SAAS,SAAS;EAErC;CACF;CAEA,OAAO;AACT"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { herald } from "./utils/connect-to-broker.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../herald/src/use-case-broadcast.ts
|
|
4
|
+
/**
|
|
5
|
+
* Channel adapter that publishes use-case broadcast events onto a herald broker.
|
|
6
|
+
*
|
|
7
|
+
* Register it in the use-cases config so successful use cases fan out to the bus:
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* // src/config/use-cases.ts
|
|
11
|
+
* import { heraldBroadcast } from "@warlock.js/herald";
|
|
12
|
+
*
|
|
13
|
+
* export default {
|
|
14
|
+
* broadcast: {
|
|
15
|
+
* enabled: true,
|
|
16
|
+
* channels: [heraldBroadcast({ broker: "default" })],
|
|
17
|
+
* },
|
|
18
|
+
* } satisfies UseCaseConfigurations;
|
|
19
|
+
*
|
|
20
|
+
* @param options - Optional broker name (defaults to the default broker)
|
|
21
|
+
*/
|
|
22
|
+
function heraldBroadcast(options) {
|
|
23
|
+
return { async broadcast(event) {
|
|
24
|
+
await herald(options?.broker).channel(event.event).publish(event.payload);
|
|
25
|
+
} };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
//#endregion
|
|
29
|
+
export { heraldBroadcast };
|
|
30
|
+
//# sourceMappingURL=use-case-broadcast.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-case-broadcast.mjs","names":[],"sources":["../../../../../../../../herald/src/use-case-broadcast.ts"],"sourcesContent":["import { herald } from \"./utils/connect-to-broker\";\n\n/**\n * Minimal mirror of `@warlock.js/core`'s `UseCaseBroadcastEvent`.\n *\n * Kept local on purpose: this adapter is **structurally typed** so `@warlock.js/herald`\n * takes no dependency on `@warlock.js/core`. The shape only needs the fields the\n * adapter reads (`event` for the channel, `payload` for the body).\n */\nexport type UseCaseBroadcastEvent = {\n useCase: string;\n event: string;\n id: string;\n at: Date;\n payload: unknown;\n};\n\n/**\n * Channel adapter that publishes use-case broadcast events onto a herald broker.\n *\n * Register it in the use-cases config so successful use cases fan out to the bus:\n *\n * @example\n * // src/config/use-cases.ts\n * import { heraldBroadcast } from \"@warlock.js/herald\";\n *\n * export default {\n * broadcast: {\n * enabled: true,\n * channels: [heraldBroadcast({ broker: \"default\" })],\n * },\n * } satisfies UseCaseConfigurations;\n *\n * @param options - Optional broker name (defaults to the default broker)\n */\nexport function heraldBroadcast(options?: { broker?: string }) {\n return {\n async broadcast(event: UseCaseBroadcastEvent): Promise<void> {\n await herald(options?.broker).channel(event.event).publish(event.payload);\n },\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,gBAAgB,SAA+B;CAC7D,OAAO,EACL,MAAM,UAAU,OAA6C;EAC3D,MAAM,OAAO,SAAS,MAAM,CAAC,CAAC,QAAQ,MAAM,KAAK,CAAC,CAAC,QAAQ,MAAM,OAAO;CAC1E,EACF;AACF"}
|