@warlock.js/notifications 5.30.0 → 5.32.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 +22 -1
- package/cjs/index.cjs +2 -2
- package/cjs/index.cjs.map +1 -1
- package/esm/channels/database-channel.d.mts +2 -3
- package/esm/channels/database-channel.d.mts.map +1 -1
- package/esm/channels/index.d.mts +2 -1
- package/esm/channels/index.mjs +1 -1
- package/esm/channels/mail-channel.d.mts +6 -4
- package/esm/channels/mail-channel.d.mts.map +1 -1
- package/esm/config.d.mts +13 -9
- package/esm/config.d.mts.map +1 -1
- package/esm/config.mjs.map +1 -1
- package/esm/contracts/channel.contract.d.mts +1 -3
- package/esm/contracts/channel.contract.d.mts.map +1 -1
- package/esm/contracts/index.d.mts +2 -1
- package/esm/contracts/notification.contract.d.mts +1 -3
- package/esm/contracts/notification.contract.d.mts.map +1 -1
- package/esm/contracts/preference-provider.contract.d.mts +1 -3
- package/esm/contracts/preference-provider.contract.d.mts.map +1 -1
- package/esm/contracts/queue-dispatcher.contract.d.mts +1 -3
- package/esm/contracts/queue-dispatcher.contract.d.mts.map +1 -1
- package/esm/contracts/rate-limiter.contract.d.mts +1 -3
- package/esm/contracts/rate-limiter.contract.d.mts.map +1 -1
- package/esm/dispatch/define-channel.d.mts +2 -2
- package/esm/dispatch/define-channel.d.mts.map +1 -1
- package/esm/dispatch/define-notification.d.mts +5 -6
- package/esm/dispatch/define-notification.d.mts.map +1 -1
- package/esm/dispatch/define-notification.mjs.map +1 -1
- package/esm/dispatch/index.d.mts +2 -1
- package/esm/dispatch/index.mjs +1 -1
- package/esm/dispatch/notifications-event-bus.d.mts +2 -3
- package/esm/dispatch/notifications-event-bus.d.mts.map +1 -1
- package/esm/dispatch/notify.d.mts +3 -5
- package/esm/dispatch/notify.d.mts.map +1 -1
- package/esm/dispatch/notify.mjs.map +1 -1
- package/esm/errors.d.mts +5 -6
- package/esm/errors.d.mts.map +1 -1
- package/esm/in-app/base-notifications-repository.d.mts +6 -6
- package/esm/in-app/base-notifications-repository.d.mts.map +1 -1
- package/esm/in-app/base-notifications-repository.mjs +1 -1
- package/esm/in-app/base-notifications-repository.mjs.map +1 -1
- package/esm/in-app/column-map.d.mts +11 -8
- package/esm/in-app/column-map.d.mts.map +1 -1
- package/esm/in-app/database-notification.d.mts +2 -3
- package/esm/in-app/database-notification.d.mts.map +1 -1
- package/esm/in-app/in-app.d.mts +6 -5
- package/esm/in-app/in-app.d.mts.map +1 -1
- package/esm/in-app/index.d.mts +2 -1
- package/esm/in-app/index.mjs +1 -1
- package/esm/index.d.mts +6 -0
- package/esm/migration/index.d.mts +2 -1
- package/esm/migration/index.mjs +1 -1
- package/esm/migration/notification-columns.d.mts +6 -7
- package/esm/migration/notification-columns.d.mts.map +1 -1
- package/esm/migration/notification-columns.mjs.map +1 -1
- package/esm/queue/bullmq-queue.d.mts +9 -7
- package/esm/queue/bullmq-queue.d.mts.map +1 -1
- package/esm/queue/bullmq-queue.mjs.map +1 -1
- package/esm/queue/herald-queue.d.mts +6 -4
- package/esm/queue/herald-queue.d.mts.map +1 -1
- package/esm/queue/index.d.mts +2 -1
- package/esm/queue/index.mjs +1 -1
- package/esm/queue/load-herald.d.mts +1 -2
- package/esm/queue/load-herald.d.mts.map +1 -1
- package/esm/queue/load-herald.mjs +1 -1
- package/esm/queue/load-queue.d.mts +2 -3
- package/esm/queue/load-queue.d.mts.map +1 -1
- package/esm/queue/notifications-worker.d.mts +5 -4
- package/esm/queue/notifications-worker.d.mts.map +1 -1
- package/esm/queue/notifications-worker.mjs.map +1 -1
- package/esm/queue/queue-package-not-installed.error.d.mts +1 -2
- package/esm/queue/queue-package-not-installed.error.d.mts.map +1 -1
- package/esm/types.d.mts +22 -18
- package/esm/types.d.mts.map +1 -1
- package/llms-full.txt +89 -81
- package/package.json +10 -9
- package/skills/configure-notifications/SKILL.md +7 -7
- package/skills/define-channel/SKILL.md +9 -7
- package/skills/define-notification/SKILL.md +22 -25
- package/skills/index.md +4 -1
- package/skills/notifications-basics/SKILL.md +12 -12
- package/skills/observe-notifications/SKILL.md +14 -10
- package/skills/send-ad-hoc/SKILL.md +9 -9
- package/skills/use-in-app/SKILL.md +6 -4
- package/skills/write-notification-migration/SKILL.md +10 -7
- package/cjs/channel_api-C5ZZDkgQ.cjs +0 -7463
- package/cjs/channel_api-C5ZZDkgQ.cjs.map +0 -1
- package/cjs/chunk-fl3r8ClO.cjs +0 -41
- package/cjs/rabbitmq-driver-BFoWr21L.cjs +0 -3
- package/cjs/src-eu2jxS21.cjs +0 -11190
- package/cjs/src-eu2jxS21.cjs.map +0 -1
- package/esm/_virtual/_rolldown/runtime.mjs +0 -32
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/_internal.mjs +0 -6
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/_internal.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/bool.mjs +0 -3
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/color.mjs +0 -3
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/date.mjs +0 -3
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/float.mjs +0 -3
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/id.mjs +0 -3
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/int.mjs +0 -3
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/nanoid.mjs +0 -3
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/pick.mjs +0 -3
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/random.mjs +0 -16
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/sample.mjs +0 -3
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/string.mjs +0 -3
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/token.mjs +0 -3
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/uuid.mjs +0 -3
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/Random/weighted.mjs +0 -3
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/async/retry.mjs +0 -3
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/index.mjs +0 -24
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/mixed/clone/clone.mjs +0 -82
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/mixed/clone/clone.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/number/round.mjs +0 -17
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/number/round.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/object/except.mjs +0 -13
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/object/except.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/object/get.mjs +0 -18
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/object/get.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/object/omit.mjs +0 -17
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/object/omit.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/object/unset.mjs +0 -30
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/object/unset.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/capitalize.mjs +0 -19
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/capitalize.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/ltrim.mjs +0 -18
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/ltrim.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/readMoreChars.mjs +0 -14
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/readMoreChars.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/rtrim.mjs +0 -18
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/rtrim.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/toCamelCase.mjs +0 -22
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/toCamelCase.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/toKebabCase.mjs +0 -17
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/toKebabCase.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/toSnakeCase.mjs +0 -21
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/toSnakeCase.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/toStudlyCase.mjs +0 -21
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/toStudlyCase.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/trim.mjs +0 -20
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/trim.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/ucfirst.mjs +0 -15
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/ucfirst.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/words.mjs +0 -25
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/string/words.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/utils/escapeRegex.mjs +0 -11
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/utils/escapeRegex.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/utils/lazy.mjs +0 -100
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/utils/lazy.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/utils/tap.mjs +0 -27
- package/esm/node_modules/.pnpm/@mongez_reinforcements@4.0.2/node_modules/@mongez/reinforcements/esm/utils/tap.mjs.map +0 -1
- package/esm/node_modules/.pnpm/@mongez_supportive-is@3.0.0/node_modules/@mongez/supportive-is/esm/index.mjs +0 -358
- package/esm/node_modules/.pnpm/@mongez_supportive-is@3.0.0/node_modules/@mongez/supportive-is/esm/index.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/channel_api.mjs +0 -28
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/channel_api.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/api_args.mjs +0 -241
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/api_args.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/bitset.mjs +0 -130
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/bitset.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/channel.mjs +0 -302
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/channel.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/channel_model.mjs +0 -206
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/channel_model.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/codec.mjs +0 -273
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/codec.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/connect.mjs +0 -143
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/connect.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/connection.mjs +0 -416
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/connection.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/credentials.mjs +0 -50
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/credentials.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/defs.mjs +0 -4600
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/defs.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/error.mjs +0 -29
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/error.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/format.mjs +0 -28
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/format.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/frame.mjs +0 -130
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/frame.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/heartbeat.mjs +0 -40
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/heartbeat.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/mux.mjs +0 -86
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/lib/mux.mjs.map +0 -1
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/package.mjs +0 -41
- package/esm/node_modules/.pnpm/amqplib@0.10.9/node_modules/amqplib/package.mjs.map +0 -1
- package/esm/node_modules/.pnpm/buffer-more-ints@1.0.0/node_modules/buffer-more-ints/buffer-more-ints.mjs +0 -360
- package/esm/node_modules/.pnpm/buffer-more-ints@1.0.0/node_modules/buffer-more-ints/buffer-more-ints.mjs.map +0 -1
- package/esm/node_modules/.pnpm/dayjs@1.11.23/node_modules/dayjs/dayjs.min.mjs +0 -305
- package/esm/node_modules/.pnpm/dayjs@1.11.23/node_modules/dayjs/dayjs.min.mjs.map +0 -1
- package/esm/node_modules/.pnpm/querystringify@2.2.0/node_modules/querystringify/index.mjs +0 -80
- package/esm/node_modules/.pnpm/querystringify@2.2.0/node_modules/querystringify/index.mjs.map +0 -1
- package/esm/node_modules/.pnpm/requires-port@1.0.0/node_modules/requires-port/index.mjs +0 -35
- package/esm/node_modules/.pnpm/requires-port@1.0.0/node_modules/requires-port/index.mjs.map +0 -1
- package/esm/node_modules/.pnpm/url-parse@1.5.10/node_modules/url-parse/index.mjs +0 -388
- package/esm/node_modules/.pnpm/url-parse@1.5.10/node_modules/url-parse/index.mjs.map +0 -1
- package/esm/packages/herald/src/communicators/broker-registry.mjs +0 -204
- package/esm/packages/herald/src/communicators/broker-registry.mjs.map +0 -1
- package/esm/packages/herald/src/communicators/broker.mjs +0 -110
- package/esm/packages/herald/src/communicators/broker.mjs.map +0 -1
- package/esm/packages/herald/src/communicators/index.mjs +0 -4
- package/esm/packages/herald/src/decorators/consumable.mjs +0 -36
- package/esm/packages/herald/src/decorators/consumable.mjs.map +0 -1
- package/esm/packages/herald/src/decorators/index.mjs +0 -3
- package/esm/packages/herald/src/drivers/index.mjs +0 -5
- package/esm/packages/herald/src/drivers/rabbitmq/index.mjs +0 -4
- package/esm/packages/herald/src/drivers/rabbitmq/rabbitmq-channel.mjs +0 -403
- package/esm/packages/herald/src/drivers/rabbitmq/rabbitmq-channel.mjs.map +0 -1
- package/esm/packages/herald/src/drivers/rabbitmq/rabbitmq-driver.mjs +0 -303
- package/esm/packages/herald/src/drivers/rabbitmq/rabbitmq-driver.mjs.map +0 -1
- package/esm/packages/herald/src/index.mjs +0 -16
- package/esm/packages/herald/src/message-managers/event-consumer.mjs +0 -76
- package/esm/packages/herald/src/message-managers/event-consumer.mjs.map +0 -1
- package/esm/packages/herald/src/message-managers/event-message.mjs +0 -85
- package/esm/packages/herald/src/message-managers/event-message.mjs.map +0 -1
- package/esm/packages/herald/src/message-managers/index.mjs +0 -4
- package/esm/packages/herald/src/message-managers/prepare-consumer-subscription.mjs +0 -42
- package/esm/packages/herald/src/message-managers/prepare-consumer-subscription.mjs.map +0 -1
- package/esm/packages/herald/src/use-case-broadcast.mjs +0 -30
- package/esm/packages/herald/src/use-case-broadcast.mjs.map +0 -1
- package/esm/packages/herald/src/utils/connect-to-broker.mjs +0 -168
- package/esm/packages/herald/src/utils/connect-to-broker.mjs.map +0 -1
- package/esm/packages/herald/src/utils/index.mjs +0 -3
- package/esm/packages/seal/src/config.mjs +0 -15
- package/esm/packages/seal/src/config.mjs.map +0 -1
- package/esm/packages/seal/src/factory/index.mjs +0 -4
- package/esm/packages/seal/src/factory/validate.mjs +0 -42
- package/esm/packages/seal/src/factory/validate.mjs.map +0 -1
- package/esm/packages/seal/src/factory/validators.mjs +0 -133
- package/esm/packages/seal/src/factory/validators.mjs.map +0 -1
- package/esm/packages/seal/src/helpers/date-helpers.mjs +0 -24
- package/esm/packages/seal/src/helpers/date-helpers.mjs.map +0 -1
- package/esm/packages/seal/src/helpers/get-field-value.mjs +0 -46
- package/esm/packages/seal/src/helpers/get-field-value.mjs.map +0 -1
- package/esm/packages/seal/src/helpers/index.mjs +0 -6
- package/esm/packages/seal/src/helpers/is-empty-value.mjs +0 -10
- package/esm/packages/seal/src/helpers/is-empty-value.mjs.map +0 -1
- package/esm/packages/seal/src/helpers/path-helpers.mjs +0 -12
- package/esm/packages/seal/src/helpers/path-helpers.mjs.map +0 -1
- package/esm/packages/seal/src/helpers/validation-helpers.mjs +0 -78
- package/esm/packages/seal/src/helpers/validation-helpers.mjs.map +0 -1
- package/esm/packages/seal/src/index.mjs +0 -88
- package/esm/packages/seal/src/mutators/array-mutators.mjs +0 -32
- package/esm/packages/seal/src/mutators/array-mutators.mjs.map +0 -1
- package/esm/packages/seal/src/mutators/boolean-mutators.mjs +0 -40
- package/esm/packages/seal/src/mutators/boolean-mutators.mjs.map +0 -1
- package/esm/packages/seal/src/mutators/date-mutators.mjs +0 -94
- package/esm/packages/seal/src/mutators/date-mutators.mjs.map +0 -1
- package/esm/packages/seal/src/mutators/index.mjs +0 -9
- package/esm/packages/seal/src/mutators/number-mutators.mjs +0 -52
- package/esm/packages/seal/src/mutators/number-mutators.mjs.map +0 -1
- package/esm/packages/seal/src/mutators/object-mutators.mjs +0 -30
- package/esm/packages/seal/src/mutators/object-mutators.mjs.map +0 -1
- package/esm/packages/seal/src/mutators/scalar-mutators.mjs +0 -9
- package/esm/packages/seal/src/mutators/scalar-mutators.mjs.map +0 -1
- package/esm/packages/seal/src/mutators/string-mutators.mjs +0 -186
- package/esm/packages/seal/src/mutators/string-mutators.mjs.map +0 -1
- package/esm/packages/seal/src/object.mjs +0 -13
- package/esm/packages/seal/src/object.mjs.map +0 -1
- package/esm/packages/seal/src/rules/array/array-rules.mjs +0 -41
- package/esm/packages/seal/src/rules/array/array-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/array/index.mjs +0 -3
- package/esm/packages/seal/src/rules/color/color-rules.mjs +0 -104
- package/esm/packages/seal/src/rules/color/color-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/color/index.mjs +0 -3
- package/esm/packages/seal/src/rules/common/enum.mjs +0 -77
- package/esm/packages/seal/src/rules/common/enum.mjs.map +0 -1
- package/esm/packages/seal/src/rules/common/equals-field-rules.mjs +0 -42
- package/esm/packages/seal/src/rules/common/equals-field-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/common/index.mjs +0 -8
- package/esm/packages/seal/src/rules/common/instanceof.mjs +0 -23
- package/esm/packages/seal/src/rules/common/instanceof.mjs.map +0 -1
- package/esm/packages/seal/src/rules/common/literal.mjs +0 -31
- package/esm/packages/seal/src/rules/common/literal.mjs.map +0 -1
- package/esm/packages/seal/src/rules/common/type-rules.mjs +0 -97
- package/esm/packages/seal/src/rules/common/type-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/common/unknown-key.mjs +0 -25
- package/esm/packages/seal/src/rules/common/unknown-key.mjs.map +0 -1
- package/esm/packages/seal/src/rules/conditional/forbidden-if-rules.mjs +0 -116
- package/esm/packages/seal/src/rules/conditional/forbidden-if-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/conditional/index.mjs +0 -12
- package/esm/packages/seal/src/rules/conditional/present-if-rules.mjs +0 -97
- package/esm/packages/seal/src/rules/conditional/present-if-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/conditional/present-unless-rules.mjs +0 -30
- package/esm/packages/seal/src/rules/conditional/present-unless-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/conditional/present-with-rules.mjs +0 -76
- package/esm/packages/seal/src/rules/conditional/present-with-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/conditional/present-without-rules.mjs +0 -76
- package/esm/packages/seal/src/rules/conditional/present-without-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/conditional/required-if-rules.mjs +0 -204
- package/esm/packages/seal/src/rules/conditional/required-if-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/conditional/required-unless-rules.mjs +0 -31
- package/esm/packages/seal/src/rules/conditional/required-unless-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/conditional/required-when-rule.mjs +0 -32
- package/esm/packages/seal/src/rules/conditional/required-when-rule.mjs.map +0 -1
- package/esm/packages/seal/src/rules/conditional/required-with-rules.mjs +0 -67
- package/esm/packages/seal/src/rules/conditional/required-with-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/conditional/required-without-rules.mjs +0 -67
- package/esm/packages/seal/src/rules/conditional/required-without-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/core/equal.mjs +0 -22
- package/esm/packages/seal/src/rules/core/equal.mjs.map +0 -1
- package/esm/packages/seal/src/rules/core/forbidden.mjs +0 -20
- package/esm/packages/seal/src/rules/core/forbidden.mjs.map +0 -1
- package/esm/packages/seal/src/rules/core/index.mjs +0 -7
- package/esm/packages/seal/src/rules/core/required.mjs +0 -36
- package/esm/packages/seal/src/rules/core/required.mjs.map +0 -1
- package/esm/packages/seal/src/rules/core/union.mjs +0 -32
- package/esm/packages/seal/src/rules/core/union.mjs.map +0 -1
- package/esm/packages/seal/src/rules/core/when.mjs +0 -37
- package/esm/packages/seal/src/rules/core/when.mjs.map +0 -1
- package/esm/packages/seal/src/rules/date/date-comparison-rules.mjs +0 -78
- package/esm/packages/seal/src/rules/date/date-comparison-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/date/date-day-rules.mjs +0 -49
- package/esm/packages/seal/src/rules/date/date-day-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/date/date-field-comparison-rules.mjs +0 -82
- package/esm/packages/seal/src/rules/date/date-field-comparison-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/date/date-period-rules.mjs +0 -401
- package/esm/packages/seal/src/rules/date/date-period-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/date/date-relative-rules.mjs +0 -55
- package/esm/packages/seal/src/rules/date/date-relative-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/date/date-special-rules.mjs +0 -58
- package/esm/packages/seal/src/rules/date/date-special-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/date/date.mjs +0 -235
- package/esm/packages/seal/src/rules/date/date.mjs.map +0 -1
- package/esm/packages/seal/src/rules/date/index.mjs +0 -9
- package/esm/packages/seal/src/rules/file/dimensions.mjs +0 -3
- package/esm/packages/seal/src/rules/file/file-size.mjs +0 -3
- package/esm/packages/seal/src/rules/file/index.mjs +0 -4
- package/esm/packages/seal/src/rules/index.mjs +0 -54
- package/esm/packages/seal/src/rules/length/index.mjs +0 -3
- package/esm/packages/seal/src/rules/length/length-rules.mjs +0 -98
- package/esm/packages/seal/src/rules/length/length-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/number/index.mjs +0 -3
- package/esm/packages/seal/src/rules/number/number-rules.mjs +0 -197
- package/esm/packages/seal/src/rules/number/number-rules.mjs.map +0 -1
- package/esm/packages/seal/src/rules/scalar/accepted-rule.mjs +0 -123
- package/esm/packages/seal/src/rules/scalar/accepted-rule.mjs.map +0 -1
- package/esm/packages/seal/src/rules/scalar/declined-rule.mjs +0 -123
- package/esm/packages/seal/src/rules/scalar/declined-rule.mjs.map +0 -1
- package/esm/packages/seal/src/rules/scalar/index.mjs +0 -4
- package/esm/packages/seal/src/rules/string/alpha.mjs +0 -41
- package/esm/packages/seal/src/rules/string/alpha.mjs.map +0 -1
- package/esm/packages/seal/src/rules/string/credit-card.mjs +0 -32
- package/esm/packages/seal/src/rules/string/credit-card.mjs.map +0 -1
- package/esm/packages/seal/src/rules/string/email.mjs +0 -20
- package/esm/packages/seal/src/rules/string/email.mjs.map +0 -1
- package/esm/packages/seal/src/rules/string/id-formats.mjs +0 -104
- package/esm/packages/seal/src/rules/string/id-formats.mjs.map +0 -1
- package/esm/packages/seal/src/rules/string/index.mjs +0 -13
- package/esm/packages/seal/src/rules/string/ip.mjs +0 -66
- package/esm/packages/seal/src/rules/string/ip.mjs.map +0 -1
- package/esm/packages/seal/src/rules/string/matches.mjs +0 -4
- package/esm/packages/seal/src/rules/string/pattern.mjs +0 -20
- package/esm/packages/seal/src/rules/string/pattern.mjs.map +0 -1
- package/esm/packages/seal/src/rules/string/string-comparison.mjs +0 -56
- package/esm/packages/seal/src/rules/string/string-comparison.mjs.map +0 -1
- package/esm/packages/seal/src/rules/string/strong-password-rule.mjs +0 -31
- package/esm/packages/seal/src/rules/string/strong-password-rule.mjs.map +0 -1
- package/esm/packages/seal/src/rules/string/url.mjs +0 -23
- package/esm/packages/seal/src/rules/string/url.mjs.map +0 -1
- package/esm/packages/seal/src/rules/string/without-whitespace.mjs +0 -19
- package/esm/packages/seal/src/rules/string/without-whitespace.mjs.map +0 -1
- package/esm/packages/seal/src/standard-schema/json-schema.mjs +0 -81
- package/esm/packages/seal/src/standard-schema/json-schema.mjs.map +0 -1
- package/esm/packages/seal/src/standard-schema/map-result.mjs +0 -26
- package/esm/packages/seal/src/standard-schema/map-result.mjs.map +0 -1
- package/esm/packages/seal/src/string.mjs +0 -13
- package/esm/packages/seal/src/string.mjs.map +0 -1
- package/esm/packages/seal/src/types/date-types.mjs +0 -17
- package/esm/packages/seal/src/types/date-types.mjs.map +0 -1
- package/esm/packages/seal/src/validators/any-validator.mjs +0 -38
- package/esm/packages/seal/src/validators/any-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/array-validator.mjs +0 -186
- package/esm/packages/seal/src/validators/array-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/base-validator.mjs +0 -720
- package/esm/packages/seal/src/validators/base-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/boolean-validator.mjs +0 -175
- package/esm/packages/seal/src/validators/boolean-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/computed-validator.mjs +0 -118
- package/esm/packages/seal/src/validators/computed-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/date-validator.mjs +0 -640
- package/esm/packages/seal/src/validators/date-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/discriminated-union-validator.mjs +0 -138
- package/esm/packages/seal/src/validators/discriminated-union-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/float-validator.mjs +0 -18
- package/esm/packages/seal/src/validators/float-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/index.mjs +0 -28
- package/esm/packages/seal/src/validators/instanceof-validator.mjs +0 -64
- package/esm/packages/seal/src/validators/instanceof-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/int-validator.mjs +0 -30
- package/esm/packages/seal/src/validators/int-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/lazy-validator.mjs +0 -87
- package/esm/packages/seal/src/validators/lazy-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/literal-validator.mjs +0 -72
- package/esm/packages/seal/src/validators/literal-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/managed-validator.mjs +0 -39
- package/esm/packages/seal/src/validators/managed-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/methods/equality-conditional-methods.mjs +0 -129
- package/esm/packages/seal/src/validators/methods/equality-conditional-methods.mjs.map +0 -1
- package/esm/packages/seal/src/validators/methods/forbidden-methods.mjs +0 -131
- package/esm/packages/seal/src/validators/methods/forbidden-methods.mjs.map +0 -1
- package/esm/packages/seal/src/validators/methods/present-methods.mjs +0 -235
- package/esm/packages/seal/src/validators/methods/present-methods.mjs.map +0 -1
- package/esm/packages/seal/src/validators/methods/required-methods.mjs +0 -347
- package/esm/packages/seal/src/validators/methods/required-methods.mjs.map +0 -1
- package/esm/packages/seal/src/validators/number-validator.mjs +0 -331
- package/esm/packages/seal/src/validators/number-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/numeric-validator.mjs +0 -27
- package/esm/packages/seal/src/validators/numeric-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/object-validator.mjs +0 -445
- package/esm/packages/seal/src/validators/object-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/primitive-validator.mjs +0 -88
- package/esm/packages/seal/src/validators/primitive-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/record-validator.mjs +0 -112
- package/esm/packages/seal/src/validators/record-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/scalar-validator.mjs +0 -173
- package/esm/packages/seal/src/validators/scalar-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/string-validator.mjs +0 -459
- package/esm/packages/seal/src/validators/string-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/tuple-validator.mjs +0 -134
- package/esm/packages/seal/src/validators/tuple-validator.mjs.map +0 -1
- package/esm/packages/seal/src/validators/union-validator.mjs +0 -66
- package/esm/packages/seal/src/validators/union-validator.mjs.map +0 -1
package/esm/config.d.mts
CHANGED
|
@@ -3,6 +3,7 @@ import { Channel } from "./contracts/channel.contract.mjs";
|
|
|
3
3
|
import { PreferenceProvider } from "./contracts/preference-provider.contract.mjs";
|
|
4
4
|
import { QueueDispatcher } from "./contracts/queue-dispatcher.contract.mjs";
|
|
5
5
|
import { RateLimiter } from "./contracts/rate-limiter.contract.mjs";
|
|
6
|
+
import "./contracts/index.mjs";
|
|
6
7
|
//#region ../../packages/notifications/src/config.d.ts
|
|
7
8
|
/**
|
|
8
9
|
* Channel registry — each key is a registered channel name, each value the
|
|
@@ -12,16 +13,20 @@ import { RateLimiter } from "./contracts/rate-limiter.contract.mjs";
|
|
|
12
13
|
* (declaration-merged into `NotificationChannels`) is accepted with its real
|
|
13
14
|
* payload type rather than `any`.
|
|
14
15
|
*/
|
|
15
|
-
type ChannelMap = Partial<{ [C in ChannelName]: Channel<NotificationChannels[C]
|
|
16
|
+
export type ChannelMap = Partial<{ [C in ChannelName]: Channel<NotificationChannels[C]>; }>;
|
|
16
17
|
/**
|
|
17
18
|
* Runtime configuration the dispatcher reads on every send. `queue`,
|
|
18
19
|
* `preferences`, and `rateLimit` are all OPTIONAL slots — the package
|
|
19
20
|
* reserves their shape but ships no defaults.
|
|
20
21
|
*/
|
|
21
|
-
type NotificationConfig = {
|
|
22
|
-
/** Channel registry — keys correlate to `NotificationChannels` payloads. */
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
export type NotificationConfig = {
|
|
23
|
+
/** Channel registry — keys correlate to `NotificationChannels` payloads. */
|
|
24
|
+
channels: ChannelMap;
|
|
25
|
+
/** Async dispatcher backing `.queue()`. Phase 2 ships a herald impl. */
|
|
26
|
+
queue?: QueueDispatcher;
|
|
27
|
+
/** Pre-send gate: drops channels the recipient opted out of. */
|
|
28
|
+
preferences?: PreferenceProvider;
|
|
29
|
+
/** Pre-send gate: drops channels exceeding per-recipient budgets. */
|
|
25
30
|
rateLimit?: RateLimiter;
|
|
26
31
|
};
|
|
27
32
|
/**
|
|
@@ -30,17 +35,16 @@ type NotificationConfig = {
|
|
|
30
35
|
* `src/config/notifications.ts`. Subsequent calls REPLACE the active config
|
|
31
36
|
* (does not merge) — pass the complete config, not a partial.
|
|
32
37
|
*/
|
|
33
|
-
declare function setNotificationConfig(config: NotificationConfig): void;
|
|
38
|
+
export declare function setNotificationConfig(config: NotificationConfig): void;
|
|
34
39
|
/**
|
|
35
40
|
* Get the active notifications configuration. Throws if not yet configured —
|
|
36
41
|
* fail loudly at the first send rather than silently doing nothing.
|
|
37
42
|
*/
|
|
38
|
-
declare function getNotificationConfig(): NotificationConfig;
|
|
43
|
+
export declare function getNotificationConfig(): NotificationConfig;
|
|
39
44
|
/**
|
|
40
45
|
* Clear the active configuration. Intended for tests; not part of the runtime
|
|
41
46
|
* surface. Production code should never call this.
|
|
42
47
|
*/
|
|
43
|
-
declare function resetNotificationConfig(): void;
|
|
48
|
+
export declare function resetNotificationConfig(): void;
|
|
44
49
|
//#endregion
|
|
45
|
-
export { ChannelMap, NotificationConfig, getNotificationConfig, resetNotificationConfig, setNotificationConfig };
|
|
46
50
|
//# sourceMappingURL=config.d.mts.map
|
package/esm/config.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.mts","names":[],"sources":["../../../../../../../packages/notifications/src/config.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../../../../../../../packages/notifications/src/config.ts"],"mappings":";;;;;;;;;;;;;;;YAiCY,aAAa,WACtB,KAAK,cAAc,QAAQ,qBAAqB;;;;;;YAQvC;;EAEV,UAAU;;EAEV,QAAQ;;EAER,cAAc;;EAEd,YAAY;;;;;;;;wBAWE,sBAAsB,QAAQ;;;;;wBAQ9B,yBAAyB;;;;;wBAWzB"}
|
package/esm/config.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.mjs","names":[],"sources":["../../../../../../../packages/notifications/src/config.ts"],"sourcesContent":["/**\n * Notification configuration holder — the runtime singleton the dispatch core\n * reads from. In a Warlock app you do NOT call `setNotificationConfig`\n * directly: `src/config/notifications.ts` exports a `NotificationConfig` as its\n * default, and the notifications connector calls this at boot. Tests (and\n * non-Warlock embeddings) may call it directly.\n *\n * @example src/config/notifications.ts — declarative; the connector registers it\n * import { type NotificationConfig, mailChannel, inApp } from \"@warlock.js/notifications\";\n * import { Notification } from \"app/notifications/models/notification/notification.model\";\n *\n * const config: NotificationConfig = {\n * channels: {\n * mail: mailChannel({ from: \"no-reply@store.com\" }),\n * database: inApp.configure({ model: Notification }),\n * },\n * // preferences / rateLimit / queue all optional\n * };\n *\n * export default config;\n */\nimport type {
|
|
1
|
+
{"version":3,"file":"config.mjs","names":[],"sources":["../../../../../../../packages/notifications/src/config.ts"],"sourcesContent":["/**\n * Notification configuration holder — the runtime singleton the dispatch core\n * reads from. In a Warlock app you do NOT call `setNotificationConfig`\n * directly: `src/config/notifications.ts` exports a `NotificationConfig` as its\n * default, and the notifications connector calls this at boot. Tests (and\n * non-Warlock embeddings) may call it directly.\n *\n * @example src/config/notifications.ts — declarative; the connector registers it\n * import { type NotificationConfig, mailChannel, inApp } from \"@warlock.js/notifications\";\n * import { Notification } from \"app/notifications/models/notification/notification.model\";\n *\n * const config: NotificationConfig = {\n * channels: {\n * mail: mailChannel({ from: \"no-reply@store.com\" }),\n * database: inApp.configure({ model: Notification }),\n * },\n * // preferences / rateLimit / queue all optional\n * };\n *\n * export default config;\n */\nimport type { Channel, PreferenceProvider, QueueDispatcher, RateLimiter } from \"./contracts\";\nimport { NotificationsNotConfiguredError } from \"./errors\";\nimport type { ChannelName, NotificationChannels } from \"./types\";\n\n/**\n * Channel registry — each key is a registered channel name, each value the\n * channel whose payload type matches the registry. Typed as a partial map\n * over `ChannelName` so `setNotificationConfig` rejects a channel whose\n * payload doesn't line up with its registry entry, AND so a custom channel\n * (declaration-merged into `NotificationChannels`) is accepted with its real\n * payload type rather than `any`.\n */\nexport type ChannelMap = Partial<{\n [C in ChannelName]: Channel<NotificationChannels[C]>;\n}>;\n\n/**\n * Runtime configuration the dispatcher reads on every send. `queue`,\n * `preferences`, and `rateLimit` are all OPTIONAL slots — the package\n * reserves their shape but ships no defaults.\n */\nexport type NotificationConfig = {\n /** Channel registry — keys correlate to `NotificationChannels` payloads. */\n channels: ChannelMap;\n /** Async dispatcher backing `.queue()`. Phase 2 ships a herald impl. */\n queue?: QueueDispatcher;\n /** Pre-send gate: drops channels the recipient opted out of. */\n preferences?: PreferenceProvider;\n /** Pre-send gate: drops channels exceeding per-recipient budgets. */\n rateLimit?: RateLimiter;\n};\n\nlet activeConfig: NotificationConfig | undefined;\n\n/**\n * Set the active notifications configuration. In a Warlock app the\n * notifications connector calls this at boot with the default export of\n * `src/config/notifications.ts`. Subsequent calls REPLACE the active config\n * (does not merge) — pass the complete config, not a partial.\n */\nexport function setNotificationConfig(config: NotificationConfig): void {\n activeConfig = config;\n}\n\n/**\n * Get the active notifications configuration. Throws if not yet configured —\n * fail loudly at the first send rather than silently doing nothing.\n */\nexport function getNotificationConfig(): NotificationConfig {\n if (!activeConfig) {\n throw new NotificationsNotConfiguredError();\n }\n return activeConfig;\n}\n\n/**\n * Clear the active configuration. Intended for tests; not part of the runtime\n * surface. Production code should never call this.\n */\nexport function resetNotificationConfig(): void {\n activeConfig = undefined;\n}\n"],"mappings":";;;AAqDA,IAAI;;;;;;;AAQJ,SAAgB,sBAAsB,QAAkC;CACtE,eAAe;AACjB;;;;;AAMA,SAAgB,wBAA4C;CAC1D,IAAI,CAAC,cACH,MAAM,IAAI,gCAAgC;CAE5C,OAAO;AACT;;;;;AAMA,SAAgB,0BAAgC;CAC9C,eAAe;AACjB"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { Id, Notifiable, SendOptions } from "../types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/notifications/src/contracts/channel.contract.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* A channel = one `send` + how it resolves a route from a notifiable.
|
|
@@ -9,7 +8,7 @@ import { Id, Notifiable, SendOptions } from "../types.mjs";
|
|
|
9
8
|
* When `route` returns `undefined`, the dispatcher tries `notifiable.id` as
|
|
10
9
|
* a fallback.
|
|
11
10
|
*/
|
|
12
|
-
interface Channel<P> {
|
|
11
|
+
export interface Channel<P> {
|
|
13
12
|
/** Unique channel name — matches the key in `NotificationChannels`. */
|
|
14
13
|
readonly name: string;
|
|
15
14
|
/**
|
|
@@ -34,5 +33,4 @@ interface Channel<P> {
|
|
|
34
33
|
}): Promise<void>;
|
|
35
34
|
}
|
|
36
35
|
//#endregion
|
|
37
|
-
export { Channel };
|
|
38
36
|
//# sourceMappingURL=channel.contract.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.contract.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/contracts/channel.contract.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"channel.contract.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/contracts/channel.contract.ts"],"mappings":";;;;;;;;;;iBAUiB,QAAQ;;WAEd;;;;;;EAOT,OAAO,YAAY;IAAwB,IAAI;;;;;;EAM/C,KAAK;IACH,SAAS;IACT;MAAkB,IAAI;;IACtB,aAAa;IACb,SAAS;MACP"}
|
|
@@ -2,4 +2,5 @@ import { Channel } from "./channel.contract.mjs";
|
|
|
2
2
|
import { NotificationContract } from "./notification.contract.mjs";
|
|
3
3
|
import { PreferenceProvider } from "./preference-provider.contract.mjs";
|
|
4
4
|
import { QueueDispatcher } from "./queue-dispatcher.contract.mjs";
|
|
5
|
-
import { RateLimiter } from "./rate-limiter.contract.mjs";
|
|
5
|
+
import { RateLimiter } from "./rate-limiter.contract.mjs";
|
|
6
|
+
export { Channel, NotificationContract, PreferenceProvider, QueueDispatcher, RateLimiter };
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { Id } from "../types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/notifications/src/contracts/notification.contract.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* The stored in-app record contract the database channel + `inApp` depend on.
|
|
6
5
|
* Accessors are the STABLE surface; physical columns vary per app and are
|
|
7
6
|
* resolved from the model's `columnMap` (see `DatabaseNotification`).
|
|
8
7
|
*/
|
|
9
|
-
interface NotificationContract {
|
|
8
|
+
export interface NotificationContract {
|
|
10
9
|
readonly recipientId: Id;
|
|
11
10
|
readonly type: string;
|
|
12
11
|
readonly isRead: boolean;
|
|
@@ -14,5 +13,4 @@ interface NotificationContract {
|
|
|
14
13
|
markRead(): Promise<void>;
|
|
15
14
|
}
|
|
16
15
|
//#endregion
|
|
17
|
-
export { NotificationContract };
|
|
18
16
|
//# sourceMappingURL=notification.contract.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notification.contract.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/contracts/notification.contract.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"notification.contract.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/contracts/notification.contract.ts"],"mappings":";;;;;;;iBAOiB;WACN,aAAa;WACb;WACA;WACA,QAAQ;EACjB,YAAY"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ChannelName, Notifiable } from "../types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/notifications/src/contracts/preference-provider.contract.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Pre-send gate consulted BEFORE each channel dispatch. Returns the subset of
|
|
@@ -12,9 +11,8 @@ import { ChannelName, Notifiable } from "../types.mjs";
|
|
|
12
11
|
* App-owned (set in the `preferences` slot of `config/notifications.ts`). The
|
|
13
12
|
* package reserves the slot but ships no default implementation.
|
|
14
13
|
*/
|
|
15
|
-
interface PreferenceProvider {
|
|
14
|
+
export interface PreferenceProvider {
|
|
16
15
|
resolveChannels(notifiable: Notifiable, type: string, requestedChannels: ChannelName[]): Promise<ChannelName[]> | ChannelName[];
|
|
17
16
|
}
|
|
18
17
|
//#endregion
|
|
19
|
-
export { PreferenceProvider };
|
|
20
18
|
//# sourceMappingURL=preference-provider.contract.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preference-provider.contract.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/contracts/preference-provider.contract.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"preference-provider.contract.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/contracts/preference-provider.contract.ts"],"mappings":";;;;;;;;;;;;;iBAaiB;EACf,gBACE,YAAY,YACZ,cACA,mBAAmB,gBAClB,QAAQ,iBAAiB"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { SendOptions } from "../types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/notifications/src/contracts/queue-dispatcher.contract.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Async dispatcher contract — what `.queue(...)` hands rendered jobs to.
|
|
@@ -10,7 +9,7 @@ import { SendOptions } from "../types.mjs";
|
|
|
10
9
|
* worker only needs to look up the channel by name and call `channel.send`.
|
|
11
10
|
* No model re-hydration, no closure serialization.
|
|
12
11
|
*/
|
|
13
|
-
interface QueueDispatcher {
|
|
12
|
+
export interface QueueDispatcher {
|
|
14
13
|
dispatch(job: {
|
|
15
14
|
channel: string;
|
|
16
15
|
route: unknown;
|
|
@@ -19,5 +18,4 @@ interface QueueDispatcher {
|
|
|
19
18
|
}): Promise<void>;
|
|
20
19
|
}
|
|
21
20
|
//#endregion
|
|
22
|
-
export { QueueDispatcher };
|
|
23
21
|
//# sourceMappingURL=queue-dispatcher.contract.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue-dispatcher.contract.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/contracts/queue-dispatcher.contract.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"queue-dispatcher.contract.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/contracts/queue-dispatcher.contract.ts"],"mappings":";;;;;;;;;;;iBAWiB;EACf,SAAS;IACP;IACA;IACA;IACA,SAAS;MACP"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ChannelName, Notifiable } from "../types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/notifications/src/contracts/rate-limiter.contract.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Per-(recipient, channel, type) rate limit. Return `false` to drop the
|
|
@@ -15,9 +14,8 @@ import { ChannelName, Notifiable } from "../types.mjs";
|
|
|
15
14
|
* App-owned (set in the `rateLimit` slot of `config/notifications.ts`). The
|
|
16
15
|
* package reserves the slot but ships no default implementation.
|
|
17
16
|
*/
|
|
18
|
-
interface RateLimiter {
|
|
17
|
+
export interface RateLimiter {
|
|
19
18
|
allow(notifiable: Notifiable, channel: ChannelName, type: string): Promise<boolean> | boolean;
|
|
20
19
|
}
|
|
21
20
|
//#endregion
|
|
22
|
-
export { RateLimiter };
|
|
23
21
|
//# sourceMappingURL=rate-limiter.contract.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limiter.contract.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/contracts/rate-limiter.contract.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"rate-limiter.contract.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/contracts/rate-limiter.contract.ts"],"mappings":";;;;;;;;;;;;;;;;iBAgBiB;EACf,MAAM,YAAY,YAAY,SAAS,aAAa,eAAe"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Channel } from "../contracts/channel.contract.mjs";
|
|
2
|
+
import "../contracts/index.mjs";
|
|
2
3
|
//#region ../../packages/notifications/src/dispatch/define-channel.d.ts
|
|
3
4
|
/**
|
|
4
5
|
* Identity helper that narrows the channel type to its payload. Use it when
|
|
@@ -31,7 +32,6 @@ import { Channel } from "../contracts/channel.contract.mjs";
|
|
|
31
32
|
* interface NotificationChannels { discord: DiscordPayload }
|
|
32
33
|
* }
|
|
33
34
|
*/
|
|
34
|
-
declare function defineChannel<P>(channel: Channel<P>): Channel<P>;
|
|
35
|
+
export declare function defineChannel<P>(channel: Channel<P>): Channel<P>;
|
|
35
36
|
//#endregion
|
|
36
|
-
export { defineChannel };
|
|
37
37
|
//# sourceMappingURL=define-channel.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-channel.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/dispatch/define-channel.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"define-channel.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/dispatch/define-channel.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAiCgB,cAAc,GAAG,SAAS,QAAQ,KAAK,QAAQ"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { ChannelName, Notifiable, NotificationChannels, RenderContext, SendOptions } from "../types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/notifications/src/dispatch/define-notification.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Per-channel renderer return type. The `database` renderer may OMIT `type`
|
|
@@ -7,16 +6,16 @@ import { ChannelName, Notifiable, NotificationChannels, RenderContext, SendOptio
|
|
|
7
6
|
*/
|
|
8
7
|
type RendererReturn<C extends ChannelName> = C extends "database" ? Omit<NotificationChannels["database"], "type"> : NotificationChannels[C];
|
|
9
8
|
type Renderer<Data, C extends ChannelName> = (data: Data, to: Notifiable, ctx: RenderContext) => RendererReturn<C>;
|
|
10
|
-
type NotificationDef<Data> = {
|
|
9
|
+
export type NotificationDef<Data> = {
|
|
11
10
|
type: string;
|
|
12
11
|
via: ChannelName[] | ((data: Data, to: Notifiable) => ChannelName[]);
|
|
13
|
-
} & { [C in ChannelName]?: Renderer<Data, C
|
|
14
|
-
interface DefinedNotification<Data> {
|
|
12
|
+
} & { [C in ChannelName]?: Renderer<Data, C>; };
|
|
13
|
+
export interface DefinedNotification<Data> {
|
|
15
14
|
send(to: Notifiable | Notifiable[], data: Data, options?: SendOptions): Promise<void>;
|
|
16
15
|
queue(to: Notifiable | Notifiable[], data: Data, options?: SendOptions): Promise<void>;
|
|
17
16
|
only(...channels: ChannelName[]): DefinedNotification<Data>;
|
|
18
17
|
}
|
|
19
|
-
declare function defineNotification<Data>(def: NotificationDef<Data>): DefinedNotification<Data>;
|
|
18
|
+
export declare function defineNotification<Data>(def: NotificationDef<Data>): DefinedNotification<Data>;
|
|
20
19
|
//#endregion
|
|
21
|
-
export {
|
|
20
|
+
export {};
|
|
22
21
|
//# sourceMappingURL=define-notification.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-notification.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/dispatch/define-notification.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"define-notification.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/dispatch/define-notification.ts"],"mappings":";;;;;;KAiDK,eAAe,UAAU,eAAe,uBACzC,KAAK,4CACL,qBAAqB;KAEpB,SAAS,MAAM,UAAU,gBAC5B,MAAM,MACN,IAAI,YACJ,KAAK,kBACF,eAAe;YAER,gBAAgB;EAC1B;EACA,KAAK,kBAAkB,MAAM,MAAM,IAAI,eAAe;OAErD,KAAK,eAAe,SAAS,MAAM;iBAGrB,oBAAoB;EACnC,KAAK,IAAI,aAAa,cAAc,MAAM,MAAM,UAAU,cAAc;EACxE,MAAM,IAAI,aAAa,cAAc,MAAM,MAAM,UAAU,cAAc;EACzE,QAAQ,UAAU,gBAAgB,oBAAoB;;wBAYxC,mBAAmB,MAAM,KAAK,gBAAgB,QAAQ,oBAAoB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-notification.mjs","names":[],"sources":["../../../../../../../../packages/notifications/src/dispatch/define-notification.ts"],"sourcesContent":["/**\n * `defineNotification` — reusable, type-safe, multi-channel notification.\n *\n * Functional, not class-based: pass `via` + a renderer per channel + a\n * stable `type`. The returned object exposes `.send` / `.queue` / `.only`.\n *\n * Renderer signature is `(data, to, ctx)` where `ctx` carries `locale` and\n * `meta` from `SendOptions`. The database renderer's `type` is OPTIONAL — the\n * dispatcher injects `def.type`, keeping the type defined in ONE place.\n *\n * ## Error policy\n * - **Config errors** (missing renderer, no queue dispatcher, unknown channel)\n * are programmer mistakes → they REJECT `.send()`/`.queue()` (and a static\n * `via` with a missing renderer throws at definition time). Consistent with\n * `notify`, which rejects `ChannelNotFoundError`.\n * - **Transport errors** (a channel's `send` throwing) stay ISOLATED — emitted\n * as a `failed` event, never aborting sibling channels/recipients.\n * - **`via()` / preferences throws** for one recipient are logged and that\n * recipient is dropped (the rest of a fan-out still go).\n *\n * @example\n * export const orderShipped = defineNotification<{ order: Order }>({\n * type: \"order.shipped\",\n * via: [\"mail\", \"database\"],\n * mail: ({ order }, to) => ({ subject: `#${order.number} shipped`, html: \"…\" }),\n * database: ({ order }) => ({ title: \"Your order shipped\", payload: { id: order.id } }),\n * });\n * await orderShipped.send(user, { order });\n * await orderShipped.queue([buyer, salesRep], { order }, { delay: \"10m\" });\n * await orderShipped.only(\"mail\").send(user, { order });\n */\nimport { log } from \"@warlock.js/logger\";\nimport { getNotificationConfig } from \"../config\";\nimport {
|
|
1
|
+
{"version":3,"file":"define-notification.mjs","names":[],"sources":["../../../../../../../../packages/notifications/src/dispatch/define-notification.ts"],"sourcesContent":["/**\n * `defineNotification` — reusable, type-safe, multi-channel notification.\n *\n * Functional, not class-based: pass `via` + a renderer per channel + a\n * stable `type`. The returned object exposes `.send` / `.queue` / `.only`.\n *\n * Renderer signature is `(data, to, ctx)` where `ctx` carries `locale` and\n * `meta` from `SendOptions`. The database renderer's `type` is OPTIONAL — the\n * dispatcher injects `def.type`, keeping the type defined in ONE place.\n *\n * ## Error policy\n * - **Config errors** (missing renderer, no queue dispatcher, unknown channel)\n * are programmer mistakes → they REJECT `.send()`/`.queue()` (and a static\n * `via` with a missing renderer throws at definition time). Consistent with\n * `notify`, which rejects `ChannelNotFoundError`.\n * - **Transport errors** (a channel's `send` throwing) stay ISOLATED — emitted\n * as a `failed` event, never aborting sibling channels/recipients.\n * - **`via()` / preferences throws** for one recipient are logged and that\n * recipient is dropped (the rest of a fan-out still go).\n *\n * @example\n * export const orderShipped = defineNotification<{ order: Order }>({\n * type: \"order.shipped\",\n * via: [\"mail\", \"database\"],\n * mail: ({ order }, to) => ({ subject: `#${order.number} shipped`, html: \"…\" }),\n * database: ({ order }) => ({ title: \"Your order shipped\", payload: { id: order.id } }),\n * });\n * await orderShipped.send(user, { order });\n * await orderShipped.queue([buyer, salesRep], { order }, { delay: \"10m\" });\n * await orderShipped.only(\"mail\").send(user, { order });\n */\nimport { log } from \"@warlock.js/logger\";\nimport { getNotificationConfig } from \"../config\";\nimport { ChannelNotFoundError, MissingRendererError, NoQueueDispatcherError } from \"../errors\";\nimport type {\n ChannelName,\n Notifiable,\n NotificationChannels,\n RenderContext,\n SendOptions,\n} from \"../types\";\nimport { newDispatchId } from \"./dispatch-id\";\nimport { dispatchChannel, type DispatchMode } from \"./dispatcher\";\nimport { emit } from \"./notifications-event-bus\";\n\n/**\n * Per-channel renderer return type. The `database` renderer may OMIT `type`\n * — it inherits from `NotificationDef.type` at dispatch time.\n */\ntype RendererReturn<C extends ChannelName> = C extends \"database\"\n ? Omit<NotificationChannels[\"database\"], \"type\">\n : NotificationChannels[C];\n\ntype Renderer<Data, C extends ChannelName> = (\n data: Data,\n to: Notifiable,\n ctx: RenderContext,\n) => RendererReturn<C>;\n\nexport type NotificationDef<Data> = {\n type: string;\n via: ChannelName[] | ((data: Data, to: Notifiable) => ChannelName[]);\n} & {\n [C in ChannelName]?: Renderer<Data, C>;\n};\n\nexport interface DefinedNotification<Data> {\n send(to: Notifiable | Notifiable[], data: Data, options?: SendOptions): Promise<void>;\n queue(to: Notifiable | Notifiable[], data: Data, options?: SendOptions): Promise<void>;\n only(...channels: ChannelName[]): DefinedNotification<Data>;\n}\n\n/** Errors that represent a misconfiguration and must surface (reject), not isolate. */\nfunction isConfigError(error: unknown): boolean {\n return (\n error instanceof MissingRendererError ||\n error instanceof NoQueueDispatcherError ||\n error instanceof ChannelNotFoundError\n );\n}\n\nexport function defineNotification<Data>(def: NotificationDef<Data>): DefinedNotification<Data> {\n const make = (restrict?: ChannelName[]): DefinedNotification<Data> => {\n // Validate at definition time when the channel set is static — a missing\n // renderer is a programmer error, surfaced loudly at import.\n if (Array.isArray(def.via)) {\n const channels = restrict ? def.via.filter((c) => restrict.includes(c)) : def.via;\n for (const channel of channels) {\n if (!def[channel]) {\n throw new MissingRendererError(def.type, channel);\n }\n }\n }\n\n return {\n send(to, data, options) {\n return dispatchAll(def, to, data, options ?? {}, \"send\", restrict);\n },\n queue(to, data, options) {\n return dispatchAll(def, to, data, options ?? {}, \"queue\", restrict);\n },\n only(...channels) {\n return make(channels);\n },\n };\n };\n\n return make();\n}\n\n/**\n * Fan-out to all recipients. Each recipient dispatches independently; one\n * recipient's transport failure never aborts the others. CONFIG errors,\n * however, propagate — they reject the returned promise.\n */\nasync function dispatchAll<Data>(\n def: NotificationDef<Data>,\n to: Notifiable | Notifiable[],\n data: Data,\n options: SendOptions,\n mode: DispatchMode,\n restrict?: ChannelName[],\n): Promise<void> {\n const recipients = Array.isArray(to) ? to : [to];\n\n const results = await Promise.allSettled(\n recipients.map((recipient) =>\n dispatchToRecipient(def, recipient, data, options, mode, restrict),\n ),\n );\n\n rethrowConfigErrors(results);\n}\n\nasync function dispatchToRecipient<Data>(\n def: NotificationDef<Data>,\n to: Notifiable,\n data: Data,\n options: SendOptions,\n mode: DispatchMode,\n restrict?: ChannelName[],\n): Promise<void> {\n const config = getNotificationConfig();\n\n // Resolve channels (via + preferences). A throw here drops THIS recipient\n // with a log — it's a per-recipient data problem, not a config error.\n let channels: ChannelName[];\n try {\n channels = typeof def.via === \"function\" ? def.via(data, to) : [...def.via];\n\n if (restrict) {\n channels = channels.filter((c) => restrict.includes(c));\n }\n\n if (!options.force && config.preferences) {\n const allowed = await config.preferences.resolveChannels(to, def.type, channels);\n for (const channel of channels.filter((c) => !allowed.includes(c))) {\n await emit(\"skipped\", {\n dispatchId: newDispatchId(),\n channel,\n notifiable: to,\n reason: \"preference\",\n options,\n });\n }\n channels = channels.filter((c) => allowed.includes(c));\n }\n } catch (error) {\n log.error(\"notifications\", `resolve.${def.type}`, error as Error);\n return;\n }\n\n const renderCtx: RenderContext = { locale: options.locale, meta: options.meta };\n\n const results = await Promise.allSettled(\n channels.map((name) => {\n const renderer = def[name] as Renderer<Data, ChannelName> | undefined;\n if (!renderer) {\n // Dynamic via chose a channel with no renderer — surface it.\n return Promise.reject(new MissingRendererError(def.type, name));\n }\n\n let payload: unknown = renderer(data, to, renderCtx);\n\n // Default the database channel's `type` from `def.type` when omitted.\n if (name === \"database\" && payload && typeof payload === \"object\" && !(\"type\" in payload)) {\n payload = { ...(payload as object), type: def.type };\n }\n\n return dispatchChannel({\n channelName: name,\n payload,\n to,\n notificationType: def.type,\n mode,\n options,\n });\n }),\n );\n\n rethrowConfigErrors(results);\n}\n\n/** Collect config-class rejections and rethrow (single or AggregateError). */\nfunction rethrowConfigErrors(results: PromiseSettledResult<unknown>[]): void {\n const configErrors = results\n .filter((r): r is PromiseRejectedResult => r.status === \"rejected\" && isConfigError(r.reason))\n .map((r) => r.reason);\n\n if (configErrors.length === 1) {\n throw configErrors[0];\n }\n if (configErrors.length > 1) {\n throw new AggregateError(configErrors, \"Multiple notification configuration errors\");\n }\n}\n"],"mappings":"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"}
|
package/esm/dispatch/index.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineChannel } from "./define-channel.mjs";
|
|
2
2
|
import { DefinedNotification, NotificationDef, defineNotification } from "./define-notification.mjs";
|
|
3
3
|
import { Notify, notify } from "./notify.mjs";
|
|
4
|
-
import { notifications } from "./notifications-event-bus.mjs";
|
|
4
|
+
import { notifications } from "./notifications-event-bus.mjs";
|
|
5
|
+
export { DefinedNotification, NotificationDef, Notify, defineChannel, defineNotification, notifications, notify };
|
package/esm/dispatch/index.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { NotificationEvents } from "../types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/notifications/src/dispatch/notifications-event-bus.d.ts
|
|
4
3
|
type EventName = keyof NotificationEvents;
|
|
5
4
|
type Handler<E extends EventName> = (data: NotificationEvents[E]) => void | Promise<void>;
|
|
@@ -9,10 +8,10 @@ type Handler<E extends EventName> = (data: NotificationEvents[E]) => void | Prom
|
|
|
9
8
|
* `on(event, handler)` returns an unsubscribe function. `off` exists for
|
|
10
9
|
* symmetry with libraries that hold handler references for later removal.
|
|
11
10
|
*/
|
|
12
|
-
declare const notifications: {
|
|
11
|
+
export declare const notifications: {
|
|
13
12
|
on<E extends EventName>(event: E, handler: Handler<E>): () => void;
|
|
14
13
|
off<E extends EventName>(event: E, handler: Handler<E>): void;
|
|
15
14
|
};
|
|
16
15
|
//#endregion
|
|
17
|
-
export {
|
|
16
|
+
export {};
|
|
18
17
|
//# sourceMappingURL=notifications-event-bus.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notifications-event-bus.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/dispatch/notifications-event-bus.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"notifications-event-bus.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/dispatch/notifications-event-bus.ts"],"mappings":";;KAkBK,kBAAkB;KAClB,QAAQ,UAAU,cAAc,MAAM,mBAAmB,cAAc;;;;;;;qBAe/D;EACX,GAAG,UAAU,WAAS,OAAS,GAAC,SAAW,QAAQ;EAMnD,IAAI,UAAU,WAAS,OAAS,GAAC,SAAW,QAAQ"}
|
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import { ChannelName, Notifiable, NotificationChannels, SendOptions } from "../types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/notifications/src/dispatch/notify.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Public shape of `notify`. The mapped portion gives `notify.<channel>` per
|
|
6
5
|
* registered channel; `channel(name)` is the runtime escape.
|
|
7
6
|
*/
|
|
8
|
-
type Notify = {
|
|
7
|
+
export type Notify = {
|
|
9
8
|
channel(name: string): {
|
|
10
9
|
send(to: Notifiable | string, payload: unknown, options?: SendOptions): Promise<void>;
|
|
11
10
|
};
|
|
12
|
-
} & { [C in ChannelName]: (to: Notifiable | string, payload: NotificationChannels[C], options?: SendOptions) => Promise<void
|
|
11
|
+
} & { [C in ChannelName]: (to: Notifiable | string, payload: NotificationChannels[C], options?: SendOptions) => Promise<void>; };
|
|
13
12
|
/**
|
|
14
13
|
* Build the `notify` proxy. `channel(name)` returns a `{ send }` object;
|
|
15
14
|
* any other property access returns a one-shot dispatcher for that channel.
|
|
16
15
|
*/
|
|
17
|
-
declare const notify: Notify;
|
|
16
|
+
export declare const notify: Notify;
|
|
18
17
|
//#endregion
|
|
19
|
-
export { Notify, notify };
|
|
20
18
|
//# sourceMappingURL=notify.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notify.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/dispatch/notify.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"notify.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/dispatch/notify.ts"],"mappings":";;;;;;YA8BY;EACV,QAAQ;IACN,KAAK,IAAI,qBAAqB,kBAAkB,UAAU,cAAc;;OAGzE,KAAK,eACJ,IAAI,qBACJ,SAAS,qBAAqB,IAC9B,UAAU,gBACP;;;;;qBAwEM,QAAQ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notify.mjs","names":[],"sources":["../../../../../../../../packages/notifications/src/dispatch/notify.ts"],"sourcesContent":["/**\n * `notify` — ad-hoc, per-channel send facade.\n *\n * A Proxy keyed by the channel registry. `notify.<channel>(to, payload,\n * options?)` works for ANY channel name registered in\n * `NotificationChannels` — built-in or custom (via declaration merging).\n *\n * For multi-channel sends, use `defineNotification` — that's the reusable\n * pattern and the only mental model for \"send through several channels at\n * once\" (no inline multi-channel overload here on purpose).\n *\n * `notify.channel(name)` is the runtime escape when the channel name isn't a\n * compile-time literal.\n *\n * @example\n * await notify.mail(user, { subject: \"Welcome\", html: \"<p>Hi!</p>\" });\n * await notify.database(user, { type: \"welcome\", title: \"Welcome!\" });\n * await notify.mail(\"guest@example.com\", { subject: \"…\", html: \"…\" }); // raw target\n * await notify.channel(dynamicName).send(user, payload); // dynamic name\n */\nimport { getNotificationConfig } from \"../config\";\nimport type { ChannelName, Notifiable, NotificationChannels, SendOptions } from \"../types\";\nimport { newDispatchId } from \"./dispatch-id\";\nimport { dispatchChannel } from \"./dispatcher\";\nimport { emit } from \"./notifications-event-bus\";\n\n/**\n * Public shape of `notify`. The mapped portion gives `notify.<channel>` per\n * registered channel; `channel(name)` is the runtime escape.\n */\nexport type Notify = {\n channel(name: string): {\n send(
|
|
1
|
+
{"version":3,"file":"notify.mjs","names":[],"sources":["../../../../../../../../packages/notifications/src/dispatch/notify.ts"],"sourcesContent":["/**\n * `notify` — ad-hoc, per-channel send facade.\n *\n * A Proxy keyed by the channel registry. `notify.<channel>(to, payload,\n * options?)` works for ANY channel name registered in\n * `NotificationChannels` — built-in or custom (via declaration merging).\n *\n * For multi-channel sends, use `defineNotification` — that's the reusable\n * pattern and the only mental model for \"send through several channels at\n * once\" (no inline multi-channel overload here on purpose).\n *\n * `notify.channel(name)` is the runtime escape when the channel name isn't a\n * compile-time literal.\n *\n * @example\n * await notify.mail(user, { subject: \"Welcome\", html: \"<p>Hi!</p>\" });\n * await notify.database(user, { type: \"welcome\", title: \"Welcome!\" });\n * await notify.mail(\"guest@example.com\", { subject: \"…\", html: \"…\" }); // raw target\n * await notify.channel(dynamicName).send(user, payload); // dynamic name\n */\nimport { getNotificationConfig } from \"../config\";\nimport type { ChannelName, Notifiable, NotificationChannels, SendOptions } from \"../types\";\nimport { newDispatchId } from \"./dispatch-id\";\nimport { dispatchChannel } from \"./dispatcher\";\nimport { emit } from \"./notifications-event-bus\";\n\n/**\n * Public shape of `notify`. The mapped portion gives `notify.<channel>` per\n * registered channel; `channel(name)` is the runtime escape.\n */\nexport type Notify = {\n channel(name: string): {\n send(to: Notifiable | string, payload: unknown, options?: SendOptions): Promise<void>;\n };\n} & {\n [C in ChannelName]: (\n to: Notifiable | string,\n payload: NotificationChannels[C],\n options?: SendOptions,\n ) => Promise<void>;\n};\n\n/**\n * Per-call dispatch for `notify.<channel>`. Mirrors `defineNotification`'s\n * pipeline but without rendering: payload is already concrete.\n *\n * Preferences gate runs ONLY when a notification `type` is known — explicit\n * via `options.type`, or implicit from a database payload's `type` field.\n * Without a type we have nothing to gate on, and ad-hoc sends bypass\n * preferences silently (use `defineNotification` if you want guaranteed\n * gating).\n */\nasync function notifyImpl(\n channelName: string,\n to: Notifiable | string,\n payload: unknown,\n options: SendOptions = {},\n): Promise<void> {\n const isRawTarget = typeof to === \"string\";\n const notifiable: Notifiable | undefined = isRawTarget ? undefined : to;\n\n // Derive the type: explicit `options.type` wins, then the payload's\n // `type` field (database channel convention).\n let notificationType: string | undefined = options.type;\n if (\n notificationType === undefined &&\n payload &&\n typeof payload === \"object\" &&\n \"type\" in payload &&\n typeof (payload as { type: unknown }).type === \"string\"\n ) {\n notificationType = (payload as { type: string }).type;\n }\n\n // Preferences gate — only when notifiable + type are both available.\n if (notifiable && notificationType && !options.force) {\n const config = getNotificationConfig();\n\n if (config.preferences) {\n const allowed = await config.preferences.resolveChannels(notifiable, notificationType, [\n channelName as ChannelName,\n ]);\n\n if (!allowed.includes(channelName as ChannelName)) {\n await emit(\"skipped\", {\n dispatchId: newDispatchId(),\n channel: channelName,\n notifiable,\n reason: \"preference\",\n options,\n });\n return;\n }\n }\n }\n\n await dispatchChannel({\n channelName,\n payload,\n to: notifiable,\n rawRoute: isRawTarget ? to : undefined,\n notificationType,\n mode: \"send\",\n options,\n });\n}\n\n/**\n * Build the `notify` proxy. `channel(name)` returns a `{ send }` object;\n * any other property access returns a one-shot dispatcher for that channel.\n */\nexport const notify: Notify = new Proxy({} as Notify, {\n get(_target, prop) {\n if (typeof prop === \"symbol\") {\n return undefined;\n }\n\n // Don't masquerade as a thenable — awaiting or Promise-wrapping `notify`\n // must NOT invoke notify.then(...) as a channel send. Also keep common\n // serialization probes from being treated as channel names.\n if (prop === \"then\" || prop === \"catch\" || prop === \"finally\" || prop === \"toJSON\") {\n return undefined;\n }\n\n if (prop === \"channel\") {\n return (name: string) => ({\n send: (to: Notifiable | string, payload: unknown, options?: SendOptions) =>\n notifyImpl(name, to, payload, options),\n });\n }\n\n return (to: Notifiable | string, payload: unknown, options?: SendOptions) =>\n notifyImpl(prop, to, payload, options);\n },\n});\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoDA,eAAe,WACb,aACA,IACA,SACA,UAAuB,CAAC,GACT;CACf,MAAM,cAAc,OAAO,OAAO;CAClC,MAAM,aAAqC,cAAc,SAAY;CAIrE,IAAI,mBAAuC,QAAQ;CACnD,IACE,qBAAqB,UACrB,WACA,OAAO,YAAY,YACnB,UAAU,WACV,OAAQ,QAA8B,SAAS,UAE/C,mBAAoB,QAA6B;CAInD,IAAI,cAAc,oBAAoB,CAAC,QAAQ,OAAO;EACpD,MAAM,SAAS,sBAAsB;EAErC,IAAI,OAAO,aAKT;OAAI,EAAC,MAJiB,OAAO,YAAY,gBAAgB,YAAY,kBAAkB,CACrF,WACF,CAAC,EAEW,CAAC,SAAS,WAA0B,GAAG;IACjD,MAAM,KAAK,WAAW;KACpB,YAAY,cAAc;KAC1B,SAAS;KACT;KACA,QAAQ;KACR;IACF,CAAC;IACD;GACF;;CAEJ;CAEA,MAAM,gBAAgB;EACpB;EACA;EACA,IAAI;EACJ,UAAU,cAAc,KAAK;EAC7B;EACA,MAAM;EACN;CACF,CAAC;AACH;;;;;AAMA,MAAa,SAAiB,IAAI,MAAM,CAAC,GAAa,EACpD,IAAI,SAAS,MAAM;CACjB,IAAI,OAAO,SAAS,UAClB;CAMF,IAAI,SAAS,UAAU,SAAS,WAAW,SAAS,aAAa,SAAS,UACxE;CAGF,IAAI,SAAS,WACX,QAAQ,UAAkB,EACxB,OAAO,IAAyB,SAAkB,YAChD,WAAW,MAAM,IAAI,SAAS,OAAO,EACzC;CAGF,QAAQ,IAAyB,SAAkB,YACjD,WAAW,MAAM,IAAI,SAAS,OAAO;AACzC,EACF,CAAC"}
|
package/esm/errors.d.mts
CHANGED
|
@@ -7,16 +7,16 @@
|
|
|
7
7
|
* Each captures a clean V8 stack (drops the Error-constructor frame) to match
|
|
8
8
|
* the cascade convention.
|
|
9
9
|
*/
|
|
10
|
-
declare class NotificationsNotConfiguredError extends Error {
|
|
10
|
+
export declare class NotificationsNotConfiguredError extends Error {
|
|
11
11
|
constructor();
|
|
12
12
|
}
|
|
13
|
-
declare class ChannelNotFoundError extends Error {
|
|
13
|
+
export declare class ChannelNotFoundError extends Error {
|
|
14
14
|
constructor(channelName: string);
|
|
15
15
|
}
|
|
16
|
-
declare class NoQueueDispatcherError extends Error {
|
|
16
|
+
export declare class NoQueueDispatcherError extends Error {
|
|
17
17
|
constructor(channelName: string);
|
|
18
18
|
}
|
|
19
|
-
declare class MissingRendererError extends Error {
|
|
19
|
+
export declare class MissingRendererError extends Error {
|
|
20
20
|
constructor(notificationType: string, channelName: string);
|
|
21
21
|
}
|
|
22
22
|
/**
|
|
@@ -24,9 +24,8 @@ declare class MissingRendererError extends Error {
|
|
|
24
24
|
* a recipient (e.g. a mail recipient with no email). Distinct from "the
|
|
25
25
|
* channel declares no resolver" — that case falls back to `{ id }`.
|
|
26
26
|
*/
|
|
27
|
-
declare class UnresolvableRouteError extends Error {
|
|
27
|
+
export declare class UnresolvableRouteError extends Error {
|
|
28
28
|
constructor(channelName: string, recipientId?: unknown);
|
|
29
29
|
}
|
|
30
30
|
//#endregion
|
|
31
|
-
export { ChannelNotFoundError, MissingRendererError, NoQueueDispatcherError, NotificationsNotConfiguredError, UnresolvableRouteError };
|
|
32
31
|
//# sourceMappingURL=errors.d.mts.map
|
package/esm/errors.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.mts","names":[],"sources":["../../../../../../../packages/notifications/src/errors.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"errors.d.mts","names":[],"sources":["../../../../../../../packages/notifications/src/errors.ts"],"mappings":";;;;;;;;;qBAiBa,wCAAwC;EACnD;;qBAUW,6BAA6B;EACxC,YAAmB;;qBAUR,+BAA+B;EAC1C,YAAmB;;qBAUR,6BAA6B;EACxC,YAAmB,0BAA0B;;;;;;;qBAelC,+BAA+B;EAC1C,YAAmB,qBAAqB"}
|
|
@@ -1,24 +1,25 @@
|
|
|
1
1
|
import { DatabasePayload, Id } from "../types.mjs";
|
|
2
2
|
import { NotificationContract } from "../contracts/notification.contract.mjs";
|
|
3
|
+
import "../contracts/index.mjs";
|
|
3
4
|
import { NotificationColumnMapHost } from "./column-map.mjs";
|
|
4
5
|
import { DatabaseNotification } from "./database-notification.mjs";
|
|
5
6
|
import { FilterRules, RepositoryManager } from "@warlock.js/core";
|
|
6
|
-
|
|
7
7
|
//#region ../../packages/notifications/src/in-app/base-notifications-repository.d.ts
|
|
8
8
|
/**
|
|
9
9
|
* Filter shape — logical keys the repo understands. `filterBy` maps each to a
|
|
10
10
|
* physical column (or a predicate) derived from the model's `columnMap`.
|
|
11
11
|
*/
|
|
12
|
-
type NotificationsFilter = {
|
|
12
|
+
export type NotificationsFilter = {
|
|
13
13
|
id?: Id;
|
|
14
|
-
type?: string;
|
|
14
|
+
type?: string;
|
|
15
|
+
/** Mode-agnostic unread filter — resolves to the model's read-state column. */
|
|
15
16
|
unread?: boolean;
|
|
16
17
|
recipientId?: Id;
|
|
17
18
|
idempotencyKey?: string;
|
|
18
19
|
};
|
|
19
20
|
/** Class form of a `DatabaseNotification` subclass — what `configure()` accepts. */
|
|
20
|
-
type NotificationModelClass = (new (...args: any[]) => DatabaseNotification) & NotificationColumnMapHost;
|
|
21
|
-
declare class BaseNotificationsRepository<TModel extends NotificationContract = NotificationContract> extends RepositoryManager<TModel, NotificationsFilter> {
|
|
21
|
+
export type NotificationModelClass = (new (...args: any[]) => DatabaseNotification) & NotificationColumnMapHost;
|
|
22
|
+
export declare class BaseNotificationsRepository<TModel extends NotificationContract = NotificationContract> extends RepositoryManager<TModel, NotificationsFilter> {
|
|
22
23
|
/** Resolved physical columns for the bound model — the single source. */
|
|
23
24
|
private readonly columns;
|
|
24
25
|
filterBy: FilterRules;
|
|
@@ -92,5 +93,4 @@ declare class BaseNotificationsRepository<TModel extends NotificationContract =
|
|
|
92
93
|
private firstByIdempotencyKey;
|
|
93
94
|
}
|
|
94
95
|
//#endregion
|
|
95
|
-
export { BaseNotificationsRepository, NotificationModelClass, NotificationsFilter };
|
|
96
96
|
//# sourceMappingURL=base-notifications-repository.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-notifications-repository.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/in-app/base-notifications-repository.ts"],"mappings":";;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"base-notifications-repository.d.mts","names":[],"sources":["../../../../../../../../packages/notifications/src/in-app/base-notifications-repository.ts"],"mappings":";;;;;;;;;;;YA2CY;EACV,KAAK;EACL;;EAEA;EACA,cAAc;EACd;;;YAIU,kCAAkC,gBAAgB,wBAC5D;qBAEW,4BACX,eAAe,uBAAuB,8BAC9B,kBAAkB,QAAQ;;mBAEjB;EAEV,UAAU;EAEjB,YAAmB,QAAQ;;MAYhB;;;;;;UASH;;;;;;;;YA2CE,MAAM,KAAK,0BAA0B;;;;;;;;;;;;UAwBvC;;;;;;;;;;;;EAoCK,UAAU,aAAa,IAAI,OAAO,iBAAiB,WAAW,KAAK,QAAQ;;;;;;EAwCjF,cACL,cAAc,MACd,OAAO,iBACP,WAAW,KACV,QAAQ;;;;;;;EAYJ,SAAS,aAAa,IAAI,KAAK,KAAK;;;;;EAwBpC,WAAW,aAAa,IAAI,KAAK,KAAK;;EAmBtC,QAAQ,aAAa,IAAI,IAAI,KAAK,QAAQ;;;;;EAQ1C,UAAU,aAAa,IAAI,KAAK,KAAK;;UAKpC"}
|
|
@@ -98,7 +98,7 @@ var BaseNotificationsRepository = class extends RepositoryManager {
|
|
|
98
98
|
*/
|
|
99
99
|
stripServerOwnedKeys(input) {
|
|
100
100
|
const { recipient, tenant, readAt, isRead } = this.columns;
|
|
101
|
-
const serverOwned = new Set([
|
|
101
|
+
const serverOwned = /* @__PURE__ */ new Set([
|
|
102
102
|
"id",
|
|
103
103
|
"recipientId",
|
|
104
104
|
"tenant",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-notifications-repository.mjs","names":[],"sources":["../../../../../../../../packages/notifications/src/in-app/base-notifications-repository.ts"],"sourcesContent":["/**\n * Shipped behavior repository for the in-app store.\n *\n * Concrete (NOT abstract) so `inApp.configure({ model })` can\n * `new BaseNotificationsRepository(model)` without subclassing. The dev only\n * extends this when they need extra query methods.\n *\n * ## The single source of truth: the model's `columnMap`\n *\n * The model declares its physical columns ONCE via `static columnMap`. The\n * constructor resolves that map and derives BOTH sides from it:\n *\n * - the READ path — `filterBy` maps logical keys (`recipientId`, `unread`,\n * `type`, …) onto the model's columns; `RepositoryManager` applies it.\n * - the WRITE / UPDATE / DELETE path — `createFor` / `markRead` / … translate\n * logical keys through the same `filterBy` via `toRow`.\n *\n * Because both come from one `columnMap`, reads and writes can never target\n * different columns. `unread` is mode-agnostic: it filters `is_read = false`\n * when the model has that flag, otherwise `read_at IS NULL`.\n *\n * @example default (SQL snake_case — no subclass needed)\n * inApp.configure({ model: Notification })\n *\n * @example custom columns — declare them on the model, not here\n * class Notification extends DatabaseNotification {\n * public static columnMap = { recipient: \"audience_id\", isRead: \"seen\" };\n * }\n */\nimport { type FilterRules, RepositoryManager } from \"@warlock.js/core\";\nimport type { NotificationContract } from \"../contracts\";\nimport type { DatabasePayload, Id } from \"../types\";\nimport {\n type NotificationColumnMapHost,\n type ResolvedNotificationColumnMap,\n resolveColumnMap,\n} from \"./column-map\";\nimport type { DatabaseNotification } from \"./database-notification\";\n\n/**\n * Filter shape — logical keys the repo understands. `filterBy` maps each to a\n * physical column (or a predicate) derived from the model's `columnMap`.\n */\nexport type NotificationsFilter = {\n id?: Id;\n type?: string;\n /** Mode-agnostic unread filter — resolves to the model's read-state column. */\n unread?: boolean;\n recipientId?: Id;\n idempotencyKey?: string;\n};\n\n/** Class form of a `DatabaseNotification` subclass — what `configure()` accepts. */\nexport type NotificationModelClass = (new (...args: any[]) => DatabaseNotification) &\n NotificationColumnMapHost;\n\nexport class BaseNotificationsRepository<\n TModel extends NotificationContract = NotificationContract,\n> extends RepositoryManager<TModel, NotificationsFilter> {\n /** Resolved physical columns for the bound model — the single source. */\n private readonly columns: ResolvedNotificationColumnMap;\n\n public filterBy: FilterRules;\n\n public constructor(model?: NotificationModelClass) {\n super();\n\n if (model) {\n this.source = model;\n }\n\n this.columns = resolveColumnMap(model?.columnMap);\n this.filterBy = this.buildFilterBy();\n }\n\n /** The tenant column for this model, or `undefined` for single-tenant. */\n public get tenantColumn(): string | undefined {\n return this.columns.tenant;\n }\n\n /**\n * Build `filterBy` from the resolved column map. `unread` is the\n * mode-agnostic read filter; `isRead` / `readAt` / `tenant` are exposed for\n * direct filtering only when the model declares those columns.\n */\n private buildFilterBy(): FilterRules {\n const { recipient, tenant, readAt, isRead } = this.columns;\n\n const rules: FilterRules = {\n id: \"=\",\n type: \"=\",\n recipientId: [\"=\", recipient],\n idempotencyKey: [\"=\", \"idempotency_key\"],\n unread: (value, query) => {\n if (value !== true && value !== \"true\") {\n return;\n }\n\n if (isRead) {\n query.where(isRead, false);\n } else if (readAt) {\n query.whereNull(readAt);\n }\n },\n };\n\n if (isRead) {\n rules.isRead = [\"=\", isRead];\n }\n\n if (readAt) {\n rules.readAt = [\"=\", readAt];\n }\n\n if (tenant) {\n rules.tenant = [\"=\", tenant];\n }\n\n return rules;\n }\n\n /**\n * Translate an object keyed by LOGICAL names into one keyed by PHYSICAL\n * columns, using `filterBy` as the lookup. Keys with no `[op, column]` tuple\n * (e.g. `title`, `body`, `payload`, `type`, `id`) pass through unchanged —\n * they're already physical. Used for every write / update / delete payload +\n * filter (the paths `RepositoryManager` does NOT map).\n */\n protected toRow(obj: Record<string, unknown>): Record<string, unknown> {\n const rules = this.filterBy as Record<string, unknown>;\n const row: Record<string, unknown> = {};\n\n for (const [logical, value] of Object.entries(obj)) {\n const rule = rules[logical];\n const column = Array.isArray(rule) ? (rule[1] as string) : logical;\n row[column] = value;\n }\n\n return row;\n }\n\n /**\n * Runtime backstop for the compile-time `DatabasePayload` `Omit`: untyped\n * callers (`notify.channel(name).send`, payloads assembled from request\n * JSON) can smuggle server-owned keys past the type system, and spreading\n * them into the row would let a payload redirect the notification into\n * another recipient's inbox (recipient spoofing), cross tenants, pre-set\n * read-state, or clobber the primary key. Drops BOTH the logical names and\n * the model's resolved physical columns — `toRow` passes unmapped keys\n * through verbatim, so a payload carrying `user_id` would otherwise reach\n * the recipient column directly.\n */\n private stripServerOwnedKeys(input: DatabasePayload): Record<string, unknown> {\n const { recipient, tenant, readAt, isRead } = this.columns;\n const serverOwned = new Set<string | undefined>([\n \"id\",\n \"recipientId\",\n \"tenant\",\n \"readAt\",\n \"isRead\",\n recipient,\n tenant,\n readAt,\n isRead,\n ]);\n\n const safe: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(input)) {\n if (!serverOwned.has(key)) {\n safe[key] = value;\n }\n }\n\n return safe;\n }\n\n /**\n * Create one row for the recipient. New rows start unread; the tenant column\n * (when the model declares one) is written from `tenantId`, which the\n * database channel reads off the recipient. `recipientId` and the other\n * server-owned keys always come from the trusted arguments — matching keys\n * in `input` are stripped, never merged.\n *\n * Idempotency: when `input.idempotencyKey` is set, find-or-create — return\n * the existing row instead of inserting a duplicate. A unique index on the\n * key is the backstop for the rare insert race (we re-fetch on conflict).\n */\n public async createFor(\n recipientId: Id,\n input: DatabasePayload,\n tenantId?: Id,\n ): Promise<TModel> {\n const { idempotencyKey } = input;\n\n if (idempotencyKey) {\n const existing = await this.firstByIdempotencyKey(recipientId, idempotencyKey);\n if (existing) {\n return existing;\n }\n }\n\n const row: Record<string, unknown> = { ...this.stripServerOwnedKeys(input), recipientId };\n\n if (this.columns.isRead) {\n row.isRead = false;\n }\n\n if (this.columns.tenant && tenantId !== undefined) {\n row.tenant = tenantId;\n }\n\n try {\n return await this.create(this.toRow(row));\n } catch (error) {\n // Lost the insert race to a concurrent send with the same key → re-fetch.\n if (idempotencyKey) {\n const existing = await this.firstByIdempotencyKey(recipientId, idempotencyKey);\n if (existing) {\n return existing;\n }\n }\n\n throw error;\n }\n }\n\n /**\n * Bulk-create one row per recipient sharing the same input + tenant. Reserved\n * for the Phase-2 `Channel.sendMany` fan-out hook; not on the per-recipient\n * send path yet.\n */\n public createManyFor(\n recipientIds: Id[],\n input: DatabasePayload,\n tenantId?: Id,\n ): Promise<TModel[]> {\n return Promise.all(\n recipientIds.map((recipientId) => this.createFor(recipientId, input, tenantId)),\n );\n }\n\n /**\n * Mark rows read — recipient-scoped. `id` omitted → all unread rows for the\n * recipient; `id` given → that one row, still scoped to the recipient. Scoped\n * to unread so a re-mark never overwrites an earlier `read_at` timestamp.\n * Sets whichever read-state column(s) the model declares.\n */\n public markRead(recipientId: Id, id?: Id): Promise<number> {\n const { isRead, readAt } = this.columns;\n const unreadScope = isRead ? { isRead: false } : { readAt: null };\n\n const data: Record<string, unknown> = {};\n\n if (isRead) {\n data.isRead = true;\n }\n\n if (readAt) {\n data.readAt = new Date();\n }\n\n return this.updateMany(\n this.toRow({ recipientId, ...unreadScope, ...(id !== undefined ? { id } : {}) }),\n this.toRow(data),\n );\n }\n\n /**\n * Inverse of `markRead`, same recipient-scoping. No unread scope needed —\n * clearing the read-state of an already-unread row is a no-op.\n */\n public markUnread(recipientId: Id, id?: Id): Promise<number> {\n const { isRead, readAt } = this.columns;\n const data: Record<string, unknown> = {};\n\n if (isRead) {\n data.isRead = false;\n }\n\n if (readAt) {\n data.readAt = null;\n }\n\n return this.updateMany(\n this.toRow({ recipientId, ...(id !== undefined ? { id } : {}) }),\n this.toRow(data),\n );\n }\n\n /** Find one row for a recipient (read path — `filterBy` maps the keys). */\n public findFor(recipientId: Id, id: Id): Promise<TModel | null> {\n return this.first({ recipientId, id } as NotificationsFilter);\n }\n\n /**\n * Delete rows — recipient-scoped. `id` omitted → clear all for the\n * recipient; `id` given → that one row.\n */\n public deleteFor(recipientId: Id, id?: Id): Promise<number> {\n return this.deleteMany(this.toRow({ recipientId, ...(id !== undefined ? { id } : {}) }));\n }\n\n /** Read path — `filterBy` maps `recipientId`/`idempotencyKey` to columns. */\n private firstByIdempotencyKey(recipientId: Id, idempotencyKey: string): Promise<TModel | null> {\n return this.first({ recipientId, idempotencyKey } as NotificationsFilter);\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"base-notifications-repository.mjs","names":[],"sources":["../../../../../../../../packages/notifications/src/in-app/base-notifications-repository.ts"],"sourcesContent":["/**\n * Shipped behavior repository for the in-app store.\n *\n * Concrete (NOT abstract) so `inApp.configure({ model })` can\n * `new BaseNotificationsRepository(model)` without subclassing. The dev only\n * extends this when they need extra query methods.\n *\n * ## The single source of truth: the model's `columnMap`\n *\n * The model declares its physical columns ONCE via `static columnMap`. The\n * constructor resolves that map and derives BOTH sides from it:\n *\n * - the READ path — `filterBy` maps logical keys (`recipientId`, `unread`,\n * `type`, …) onto the model's columns; `RepositoryManager` applies it.\n * - the WRITE / UPDATE / DELETE path — `createFor` / `markRead` / … translate\n * logical keys through the same `filterBy` via `toRow`.\n *\n * Because both come from one `columnMap`, reads and writes can never target\n * different columns. `unread` is mode-agnostic: it filters `is_read = false`\n * when the model has that flag, otherwise `read_at IS NULL`.\n *\n * @example default (SQL snake_case — no subclass needed)\n * inApp.configure({ model: Notification })\n *\n * @example custom columns — declare them on the model, not here\n * class Notification extends DatabaseNotification {\n * public static columnMap = { recipient: \"audience_id\", isRead: \"seen\" };\n * }\n */\nimport { type FilterRules, RepositoryManager } from \"@warlock.js/core\";\nimport type { NotificationContract } from \"../contracts\";\nimport type { DatabasePayload, Id } from \"../types\";\nimport {\n type NotificationColumnMapHost,\n type ResolvedNotificationColumnMap,\n resolveColumnMap,\n} from \"./column-map\";\nimport type { DatabaseNotification } from \"./database-notification\";\n\n/**\n * Filter shape — logical keys the repo understands. `filterBy` maps each to a\n * physical column (or a predicate) derived from the model's `columnMap`.\n */\nexport type NotificationsFilter = {\n id?: Id;\n type?: string;\n /** Mode-agnostic unread filter — resolves to the model's read-state column. */\n unread?: boolean;\n recipientId?: Id;\n idempotencyKey?: string;\n};\n\n/** Class form of a `DatabaseNotification` subclass — what `configure()` accepts. */\nexport type NotificationModelClass = (new (...args: any[]) => DatabaseNotification) &\n NotificationColumnMapHost;\n\nexport class BaseNotificationsRepository<\n TModel extends NotificationContract = NotificationContract,\n> extends RepositoryManager<TModel, NotificationsFilter> {\n /** Resolved physical columns for the bound model — the single source. */\n private readonly columns: ResolvedNotificationColumnMap;\n\n public filterBy: FilterRules;\n\n public constructor(model?: NotificationModelClass) {\n super();\n\n if (model) {\n this.source = model;\n }\n\n this.columns = resolveColumnMap(model?.columnMap);\n this.filterBy = this.buildFilterBy();\n }\n\n /** The tenant column for this model, or `undefined` for single-tenant. */\n public get tenantColumn(): string | undefined {\n return this.columns.tenant;\n }\n\n /**\n * Build `filterBy` from the resolved column map. `unread` is the\n * mode-agnostic read filter; `isRead` / `readAt` / `tenant` are exposed for\n * direct filtering only when the model declares those columns.\n */\n private buildFilterBy(): FilterRules {\n const { recipient, tenant, readAt, isRead } = this.columns;\n\n const rules: FilterRules = {\n id: \"=\",\n type: \"=\",\n recipientId: [\"=\", recipient],\n idempotencyKey: [\"=\", \"idempotency_key\"],\n unread: (value, query) => {\n if (value !== true && value !== \"true\") {\n return;\n }\n\n if (isRead) {\n query.where(isRead, false);\n } else if (readAt) {\n query.whereNull(readAt);\n }\n },\n };\n\n if (isRead) {\n rules.isRead = [\"=\", isRead];\n }\n\n if (readAt) {\n rules.readAt = [\"=\", readAt];\n }\n\n if (tenant) {\n rules.tenant = [\"=\", tenant];\n }\n\n return rules;\n }\n\n /**\n * Translate an object keyed by LOGICAL names into one keyed by PHYSICAL\n * columns, using `filterBy` as the lookup. Keys with no `[op, column]` tuple\n * (e.g. `title`, `body`, `payload`, `type`, `id`) pass through unchanged —\n * they're already physical. Used for every write / update / delete payload +\n * filter (the paths `RepositoryManager` does NOT map).\n */\n protected toRow(obj: Record<string, unknown>): Record<string, unknown> {\n const rules = this.filterBy as Record<string, unknown>;\n const row: Record<string, unknown> = {};\n\n for (const [logical, value] of Object.entries(obj)) {\n const rule = rules[logical];\n const column = Array.isArray(rule) ? (rule[1] as string) : logical;\n row[column] = value;\n }\n\n return row;\n }\n\n /**\n * Runtime backstop for the compile-time `DatabasePayload` `Omit`: untyped\n * callers (`notify.channel(name).send`, payloads assembled from request\n * JSON) can smuggle server-owned keys past the type system, and spreading\n * them into the row would let a payload redirect the notification into\n * another recipient's inbox (recipient spoofing), cross tenants, pre-set\n * read-state, or clobber the primary key. Drops BOTH the logical names and\n * the model's resolved physical columns — `toRow` passes unmapped keys\n * through verbatim, so a payload carrying `user_id` would otherwise reach\n * the recipient column directly.\n */\n private stripServerOwnedKeys(input: DatabasePayload): Record<string, unknown> {\n const { recipient, tenant, readAt, isRead } = this.columns;\n const serverOwned = new Set<string | undefined>([\n \"id\",\n \"recipientId\",\n \"tenant\",\n \"readAt\",\n \"isRead\",\n recipient,\n tenant,\n readAt,\n isRead,\n ]);\n\n const safe: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(input)) {\n if (!serverOwned.has(key)) {\n safe[key] = value;\n }\n }\n\n return safe;\n }\n\n /**\n * Create one row for the recipient. New rows start unread; the tenant column\n * (when the model declares one) is written from `tenantId`, which the\n * database channel reads off the recipient. `recipientId` and the other\n * server-owned keys always come from the trusted arguments — matching keys\n * in `input` are stripped, never merged.\n *\n * Idempotency: when `input.idempotencyKey` is set, find-or-create — return\n * the existing row instead of inserting a duplicate. A unique index on the\n * key is the backstop for the rare insert race (we re-fetch on conflict).\n */\n public async createFor(recipientId: Id, input: DatabasePayload, tenantId?: Id): Promise<TModel> {\n const { idempotencyKey } = input;\n\n if (idempotencyKey) {\n const existing = await this.firstByIdempotencyKey(recipientId, idempotencyKey);\n if (existing) {\n return existing;\n }\n }\n\n const row: Record<string, unknown> = { ...this.stripServerOwnedKeys(input), recipientId };\n\n if (this.columns.isRead) {\n row.isRead = false;\n }\n\n if (this.columns.tenant && tenantId !== undefined) {\n row.tenant = tenantId;\n }\n\n try {\n return await this.create(this.toRow(row));\n } catch (error) {\n // Lost the insert race to a concurrent send with the same key → re-fetch.\n if (idempotencyKey) {\n const existing = await this.firstByIdempotencyKey(recipientId, idempotencyKey);\n if (existing) {\n return existing;\n }\n }\n\n throw error;\n }\n }\n\n /**\n * Bulk-create one row per recipient sharing the same input + tenant. Reserved\n * for the Phase-2 `Channel.sendMany` fan-out hook; not on the per-recipient\n * send path yet.\n */\n public createManyFor(\n recipientIds: Id[],\n input: DatabasePayload,\n tenantId?: Id,\n ): Promise<TModel[]> {\n return Promise.all(\n recipientIds.map((recipientId) => this.createFor(recipientId, input, tenantId)),\n );\n }\n\n /**\n * Mark rows read — recipient-scoped. `id` omitted → all unread rows for the\n * recipient; `id` given → that one row, still scoped to the recipient. Scoped\n * to unread so a re-mark never overwrites an earlier `read_at` timestamp.\n * Sets whichever read-state column(s) the model declares.\n */\n public markRead(recipientId: Id, id?: Id): Promise<number> {\n const { isRead, readAt } = this.columns;\n const unreadScope = isRead ? { isRead: false } : { readAt: null };\n\n const data: Record<string, unknown> = {};\n\n if (isRead) {\n data.isRead = true;\n }\n\n if (readAt) {\n data.readAt = new Date();\n }\n\n return this.updateMany(\n this.toRow({ recipientId, ...unreadScope, ...(id !== undefined ? { id } : {}) }),\n this.toRow(data),\n );\n }\n\n /**\n * Inverse of `markRead`, same recipient-scoping. No unread scope needed —\n * clearing the read-state of an already-unread row is a no-op.\n */\n public markUnread(recipientId: Id, id?: Id): Promise<number> {\n const { isRead, readAt } = this.columns;\n const data: Record<string, unknown> = {};\n\n if (isRead) {\n data.isRead = false;\n }\n\n if (readAt) {\n data.readAt = null;\n }\n\n return this.updateMany(\n this.toRow({ recipientId, ...(id !== undefined ? { id } : {}) }),\n this.toRow(data),\n );\n }\n\n /** Find one row for a recipient (read path — `filterBy` maps the keys). */\n public findFor(recipientId: Id, id: Id): Promise<TModel | null> {\n return this.first({ recipientId, id } as NotificationsFilter);\n }\n\n /**\n * Delete rows — recipient-scoped. `id` omitted → clear all for the\n * recipient; `id` given → that one row.\n */\n public deleteFor(recipientId: Id, id?: Id): Promise<number> {\n return this.deleteMany(this.toRow({ recipientId, ...(id !== undefined ? { id } : {}) }));\n }\n\n /** Read path — `filterBy` maps `recipientId`/`idempotencyKey` to columns. */\n private firstByIdempotencyKey(recipientId: Id, idempotencyKey: string): Promise<TModel | null> {\n return this.first({ recipientId, idempotencyKey } as NotificationsFilter);\n }\n}\n"],"mappings":"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"}
|