@happyvertical/smrt-messages 0.60.10 → 0.60.12
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/AGENTS.md +10 -0
- package/README.md +34 -0
- package/agents/notification-outbox.md +48 -2
- package/dist/chunks/{AccountCollection-By48bdwt.js → AccountCollection-C3mH6s-q.js} +2 -2
- package/dist/chunks/{AccountCollection-By48bdwt.js.map → AccountCollection-C3mH6s-q.js.map} +1 -1
- package/dist/chunks/{Email-CMUK3aWU.js → Email-CvQfps_w.js} +3 -3
- package/dist/chunks/{Email-CMUK3aWU.js.map → Email-CvQfps_w.js.map} +1 -1
- package/dist/chunks/{EmailFolder-DLJvqNqI.js → EmailFolder-Bb0y9h7I.js} +2 -2
- package/dist/chunks/{EmailFolder-DLJvqNqI.js.map → EmailFolder-Bb0y9h7I.js.map} +1 -1
- package/dist/chunks/{Message-DXpqot5f.js → Message-Dn0WWplz.js} +4 -4
- package/dist/chunks/{Message-DXpqot5f.js.map → Message-Dn0WWplz.js.map} +1 -1
- package/dist/chunks/{MessageCollection-CMGaSGBc.js → MessageCollection-BHz_udee.js} +2 -2
- package/dist/chunks/{MessageCollection-CMGaSGBc.js.map → MessageCollection-BHz_udee.js.map} +1 -1
- package/dist/chunks/__smrt-register__-Dftmkczm.js +6 -0
- package/dist/chunks/__smrt-register__-Dftmkczm.js.map +1 -0
- package/dist/collections/NotificationOptOutCollection.d.ts +6 -0
- package/dist/collections/NotificationOptOutCollection.d.ts.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +264 -55
- package/dist/index.js.map +1 -1
- package/dist/manifest.json +330 -1
- package/dist/models/NotificationOptOut.d.ts +44 -0
- package/dist/models/NotificationOptOut.d.ts.map +1 -0
- package/dist/services/NotificationOptOutService.d.ts +80 -0
- package/dist/services/NotificationOptOutService.d.ts.map +1 -0
- package/dist/smrt-knowledge.json +104 -10
- package/dist/smrt-knowledge.okf.md +4 -2
- package/package.json +9 -9
- package/dist/chunks/__smrt-register__-CaGLtiEU.js +0 -6
- package/dist/chunks/__smrt-register__-CaGLtiEU.js.map +0 -1
package/dist/manifest.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": "1.0.0",
|
|
3
3
|
"timestamp": 0,
|
|
4
4
|
"packageName": "@happyvertical/smrt-messages",
|
|
5
|
-
"packageVersion": "0.60.
|
|
5
|
+
"packageVersion": "0.60.12",
|
|
6
6
|
"objects": {
|
|
7
7
|
"@happyvertical/smrt-messages:AccountCollection": {
|
|
8
8
|
"name": "accountcollection",
|
|
@@ -3759,6 +3759,136 @@
|
|
|
3759
3759
|
},
|
|
3760
3760
|
"importPath": "@happyvertical/smrt-messages"
|
|
3761
3761
|
},
|
|
3762
|
+
"@happyvertical/smrt-messages:NotificationOptOutCollection": {
|
|
3763
|
+
"name": "notificationoptoutcollection",
|
|
3764
|
+
"className": "NotificationOptOutCollection",
|
|
3765
|
+
"qualifiedName": "@happyvertical/smrt-messages:NotificationOptOutCollection",
|
|
3766
|
+
"collection": "notificationoptouts",
|
|
3767
|
+
"filePath": "/home/runner/work/smrt/smrt/packages/messages/src/collections/NotificationOptOutCollection.ts",
|
|
3768
|
+
"packageName": "@happyvertical/smrt-messages",
|
|
3769
|
+
"fields": {},
|
|
3770
|
+
"methods": {},
|
|
3771
|
+
"decoratorConfig": {
|
|
3772
|
+
"tableName": "notification_opt_outs",
|
|
3773
|
+
"api": false,
|
|
3774
|
+
"mcp": false,
|
|
3775
|
+
"cli": false
|
|
3776
|
+
},
|
|
3777
|
+
"extends": "SmrtCollection",
|
|
3778
|
+
"extendsTypeArg": "NotificationOptOut",
|
|
3779
|
+
"exportName": "NotificationOptOutCollection",
|
|
3780
|
+
"collectionExportName": "NotificationOptOutCollectionCollection",
|
|
3781
|
+
"collectionOf": "@happyvertical/smrt-messages:NotificationOptOut",
|
|
3782
|
+
"inheritedExposure": [
|
|
3783
|
+
"api",
|
|
3784
|
+
"mcp",
|
|
3785
|
+
"cli"
|
|
3786
|
+
],
|
|
3787
|
+
"schema": {
|
|
3788
|
+
"tableName": "notification_opt_outs",
|
|
3789
|
+
"ddl": "CREATE TABLE IF NOT EXISTS \"notification_opt_outs\" (\n \"_sync_origin_device_id\" TEXT,\n \"_sync_revision\" INTEGER,\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID NOT NULL,\n \"recipient_user_id\" UUID NOT NULL,\n \"channel\" TEXT NOT NULL DEFAULT 'email',\n \"scope\" TEXT NOT NULL,\n \"opted_out_at\" TIMESTAMP,\n \"updated_by\" TEXT\n);",
|
|
3790
|
+
"columns": {
|
|
3791
|
+
"_sync_origin_device_id": {
|
|
3792
|
+
"type": "TEXT"
|
|
3793
|
+
},
|
|
3794
|
+
"_sync_revision": {
|
|
3795
|
+
"type": "INTEGER"
|
|
3796
|
+
},
|
|
3797
|
+
"id": {
|
|
3798
|
+
"type": "UUID",
|
|
3799
|
+
"primaryKey": true,
|
|
3800
|
+
"referenceKind": "id",
|
|
3801
|
+
"notNull": true
|
|
3802
|
+
},
|
|
3803
|
+
"slug": {
|
|
3804
|
+
"type": "TEXT",
|
|
3805
|
+
"notNull": true
|
|
3806
|
+
},
|
|
3807
|
+
"context": {
|
|
3808
|
+
"type": "TEXT",
|
|
3809
|
+
"notNull": true,
|
|
3810
|
+
"default": ""
|
|
3811
|
+
},
|
|
3812
|
+
"created_at": {
|
|
3813
|
+
"type": "TIMESTAMP",
|
|
3814
|
+
"notNull": true,
|
|
3815
|
+
"default": "current_timestamp"
|
|
3816
|
+
},
|
|
3817
|
+
"updated_at": {
|
|
3818
|
+
"type": "TIMESTAMP",
|
|
3819
|
+
"notNull": true,
|
|
3820
|
+
"default": "current_timestamp"
|
|
3821
|
+
},
|
|
3822
|
+
"tenant_id": {
|
|
3823
|
+
"type": "UUID",
|
|
3824
|
+
"referenceKind": "tenantId",
|
|
3825
|
+
"notNull": true,
|
|
3826
|
+
"unique": false
|
|
3827
|
+
},
|
|
3828
|
+
"recipient_user_id": {
|
|
3829
|
+
"type": "UUID",
|
|
3830
|
+
"referenceKind": "crossPackageRef",
|
|
3831
|
+
"notNull": true,
|
|
3832
|
+
"unique": false
|
|
3833
|
+
},
|
|
3834
|
+
"channel": {
|
|
3835
|
+
"type": "TEXT",
|
|
3836
|
+
"notNull": true,
|
|
3837
|
+
"unique": false,
|
|
3838
|
+
"default": "email"
|
|
3839
|
+
},
|
|
3840
|
+
"scope": {
|
|
3841
|
+
"type": "TEXT",
|
|
3842
|
+
"notNull": true,
|
|
3843
|
+
"unique": false
|
|
3844
|
+
},
|
|
3845
|
+
"opted_out_at": {
|
|
3846
|
+
"type": "TIMESTAMP",
|
|
3847
|
+
"notNull": false,
|
|
3848
|
+
"unique": false
|
|
3849
|
+
},
|
|
3850
|
+
"updated_by": {
|
|
3851
|
+
"type": "TEXT",
|
|
3852
|
+
"notNull": false,
|
|
3853
|
+
"unique": false
|
|
3854
|
+
}
|
|
3855
|
+
},
|
|
3856
|
+
"indexes": [
|
|
3857
|
+
{
|
|
3858
|
+
"name": "notification_opt_outs_tenant_id_recipient_user_id_idx",
|
|
3859
|
+
"columns": [
|
|
3860
|
+
"tenant_id",
|
|
3861
|
+
"recipient_user_id",
|
|
3862
|
+
"channel",
|
|
3863
|
+
"scope"
|
|
3864
|
+
],
|
|
3865
|
+
"unique": true
|
|
3866
|
+
},
|
|
3867
|
+
{
|
|
3868
|
+
"name": "notification_opt_outs_slug_context_idx",
|
|
3869
|
+
"columns": [
|
|
3870
|
+
"slug",
|
|
3871
|
+
"context"
|
|
3872
|
+
]
|
|
3873
|
+
},
|
|
3874
|
+
{
|
|
3875
|
+
"name": "notification_opt_outs_tenant_id_created_at_idx",
|
|
3876
|
+
"columns": [
|
|
3877
|
+
"tenant_id",
|
|
3878
|
+
"created_at"
|
|
3879
|
+
]
|
|
3880
|
+
},
|
|
3881
|
+
{
|
|
3882
|
+
"name": "notification_opt_outs_recipient_user_id_idx",
|
|
3883
|
+
"columns": [
|
|
3884
|
+
"recipient_user_id"
|
|
3885
|
+
]
|
|
3886
|
+
}
|
|
3887
|
+
],
|
|
3888
|
+
"version": "a13e93c1"
|
|
3889
|
+
},
|
|
3890
|
+
"importPath": "@happyvertical/smrt-messages"
|
|
3891
|
+
},
|
|
3762
3892
|
"@happyvertical/smrt-messages:PersonaMessageRouteCollection": {
|
|
3763
3893
|
"name": "personamessageroutecollection",
|
|
3764
3894
|
"className": "PersonaMessageRouteCollection",
|
|
@@ -8975,6 +9105,205 @@
|
|
|
8975
9105
|
},
|
|
8976
9106
|
"importPath": "@happyvertical/smrt-messages"
|
|
8977
9107
|
},
|
|
9108
|
+
"@happyvertical/smrt-messages:NotificationOptOut": {
|
|
9109
|
+
"name": "notificationoptout",
|
|
9110
|
+
"className": "NotificationOptOut",
|
|
9111
|
+
"qualifiedName": "@happyvertical/smrt-messages:NotificationOptOut",
|
|
9112
|
+
"collection": "notificationoptouts",
|
|
9113
|
+
"description": "One person's opt-out of one notification channel for one scope, in one tenant: the preference store behind the outbox's isSuppressed hook.",
|
|
9114
|
+
"filePath": "/home/runner/work/smrt/smrt/packages/messages/src/models/NotificationOptOut.ts",
|
|
9115
|
+
"packageName": "@happyvertical/smrt-messages",
|
|
9116
|
+
"fields": {
|
|
9117
|
+
"tenantId": {
|
|
9118
|
+
"type": "text",
|
|
9119
|
+
"required": true,
|
|
9120
|
+
"_meta": {
|
|
9121
|
+
"sqlType": "UUID",
|
|
9122
|
+
"__tenancy": {
|
|
9123
|
+
"isTenantIdField": true,
|
|
9124
|
+
"autoFilter": true,
|
|
9125
|
+
"required": true,
|
|
9126
|
+
"autoPopulate": true,
|
|
9127
|
+
"nullable": false,
|
|
9128
|
+
"mode": "required",
|
|
9129
|
+
"field": "tenantId",
|
|
9130
|
+
"allowSuperAdminBypass": false
|
|
9131
|
+
}
|
|
9132
|
+
}
|
|
9133
|
+
},
|
|
9134
|
+
"recipientUserId": {
|
|
9135
|
+
"type": "crossPackageRef",
|
|
9136
|
+
"required": true,
|
|
9137
|
+
"related": "@happyvertical/smrt-users:User"
|
|
9138
|
+
},
|
|
9139
|
+
"channel": {
|
|
9140
|
+
"type": "text",
|
|
9141
|
+
"required": true,
|
|
9142
|
+
"default": "email",
|
|
9143
|
+
"_meta": {
|
|
9144
|
+
"required": true
|
|
9145
|
+
},
|
|
9146
|
+
"enum": [
|
|
9147
|
+
"email"
|
|
9148
|
+
]
|
|
9149
|
+
},
|
|
9150
|
+
"scope": {
|
|
9151
|
+
"type": "text",
|
|
9152
|
+
"required": true,
|
|
9153
|
+
"_meta": {
|
|
9154
|
+
"required": true
|
|
9155
|
+
}
|
|
9156
|
+
},
|
|
9157
|
+
"optedOutAt": {
|
|
9158
|
+
"type": "datetime",
|
|
9159
|
+
"required": false
|
|
9160
|
+
},
|
|
9161
|
+
"updatedBy": {
|
|
9162
|
+
"type": "text",
|
|
9163
|
+
"required": false
|
|
9164
|
+
}
|
|
9165
|
+
},
|
|
9166
|
+
"methods": {},
|
|
9167
|
+
"decoratorConfig": {
|
|
9168
|
+
"tableName": "notification_opt_outs",
|
|
9169
|
+
"api": false,
|
|
9170
|
+
"mcp": false,
|
|
9171
|
+
"cli": false,
|
|
9172
|
+
"conflictColumns": [
|
|
9173
|
+
"tenant_id",
|
|
9174
|
+
"recipient_user_id",
|
|
9175
|
+
"channel",
|
|
9176
|
+
"scope"
|
|
9177
|
+
],
|
|
9178
|
+
"tenantScoped": {
|
|
9179
|
+
"mode": "required"
|
|
9180
|
+
}
|
|
9181
|
+
},
|
|
9182
|
+
"extends": "SmrtObject",
|
|
9183
|
+
"exportName": "NotificationOptOut",
|
|
9184
|
+
"collectionExportName": "NotificationOptOutCollection",
|
|
9185
|
+
"validationRules": [
|
|
9186
|
+
{
|
|
9187
|
+
"field": "recipientUserId",
|
|
9188
|
+
"rule": "required",
|
|
9189
|
+
"fieldType": "crossPackageRef"
|
|
9190
|
+
},
|
|
9191
|
+
{
|
|
9192
|
+
"field": "channel",
|
|
9193
|
+
"rule": "required",
|
|
9194
|
+
"fieldType": "text"
|
|
9195
|
+
},
|
|
9196
|
+
{
|
|
9197
|
+
"field": "scope",
|
|
9198
|
+
"rule": "required",
|
|
9199
|
+
"fieldType": "text"
|
|
9200
|
+
}
|
|
9201
|
+
],
|
|
9202
|
+
"schema": {
|
|
9203
|
+
"tableName": "notification_opt_outs",
|
|
9204
|
+
"ddl": "CREATE TABLE IF NOT EXISTS \"notification_opt_outs\" (\n \"_sync_origin_device_id\" TEXT,\n \"_sync_revision\" INTEGER,\n \"id\" UUID PRIMARY KEY NOT NULL,\n \"slug\" TEXT NOT NULL,\n \"context\" TEXT NOT NULL DEFAULT '',\n \"created_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"updated_at\" TIMESTAMP NOT NULL DEFAULT current_timestamp,\n \"tenant_id\" UUID NOT NULL,\n \"recipient_user_id\" UUID NOT NULL,\n \"channel\" TEXT NOT NULL DEFAULT 'email',\n \"scope\" TEXT NOT NULL,\n \"opted_out_at\" TIMESTAMP,\n \"updated_by\" TEXT\n);",
|
|
9205
|
+
"columns": {
|
|
9206
|
+
"_sync_origin_device_id": {
|
|
9207
|
+
"type": "TEXT"
|
|
9208
|
+
},
|
|
9209
|
+
"_sync_revision": {
|
|
9210
|
+
"type": "INTEGER"
|
|
9211
|
+
},
|
|
9212
|
+
"id": {
|
|
9213
|
+
"type": "UUID",
|
|
9214
|
+
"primaryKey": true,
|
|
9215
|
+
"referenceKind": "id",
|
|
9216
|
+
"notNull": true
|
|
9217
|
+
},
|
|
9218
|
+
"slug": {
|
|
9219
|
+
"type": "TEXT",
|
|
9220
|
+
"notNull": true
|
|
9221
|
+
},
|
|
9222
|
+
"context": {
|
|
9223
|
+
"type": "TEXT",
|
|
9224
|
+
"notNull": true,
|
|
9225
|
+
"default": ""
|
|
9226
|
+
},
|
|
9227
|
+
"created_at": {
|
|
9228
|
+
"type": "TIMESTAMP",
|
|
9229
|
+
"notNull": true,
|
|
9230
|
+
"default": "current_timestamp"
|
|
9231
|
+
},
|
|
9232
|
+
"updated_at": {
|
|
9233
|
+
"type": "TIMESTAMP",
|
|
9234
|
+
"notNull": true,
|
|
9235
|
+
"default": "current_timestamp"
|
|
9236
|
+
},
|
|
9237
|
+
"tenant_id": {
|
|
9238
|
+
"type": "UUID",
|
|
9239
|
+
"referenceKind": "tenantId",
|
|
9240
|
+
"notNull": true,
|
|
9241
|
+
"unique": false
|
|
9242
|
+
},
|
|
9243
|
+
"recipient_user_id": {
|
|
9244
|
+
"type": "UUID",
|
|
9245
|
+
"referenceKind": "crossPackageRef",
|
|
9246
|
+
"notNull": true,
|
|
9247
|
+
"unique": false
|
|
9248
|
+
},
|
|
9249
|
+
"channel": {
|
|
9250
|
+
"type": "TEXT",
|
|
9251
|
+
"notNull": true,
|
|
9252
|
+
"unique": false,
|
|
9253
|
+
"default": "email"
|
|
9254
|
+
},
|
|
9255
|
+
"scope": {
|
|
9256
|
+
"type": "TEXT",
|
|
9257
|
+
"notNull": true,
|
|
9258
|
+
"unique": false
|
|
9259
|
+
},
|
|
9260
|
+
"opted_out_at": {
|
|
9261
|
+
"type": "TIMESTAMP",
|
|
9262
|
+
"notNull": false,
|
|
9263
|
+
"unique": false
|
|
9264
|
+
},
|
|
9265
|
+
"updated_by": {
|
|
9266
|
+
"type": "TEXT",
|
|
9267
|
+
"notNull": false,
|
|
9268
|
+
"unique": false
|
|
9269
|
+
}
|
|
9270
|
+
},
|
|
9271
|
+
"indexes": [
|
|
9272
|
+
{
|
|
9273
|
+
"name": "notification_opt_outs_tenant_id_recipient_user_id_idx",
|
|
9274
|
+
"columns": [
|
|
9275
|
+
"tenant_id",
|
|
9276
|
+
"recipient_user_id",
|
|
9277
|
+
"channel",
|
|
9278
|
+
"scope"
|
|
9279
|
+
],
|
|
9280
|
+
"unique": true
|
|
9281
|
+
},
|
|
9282
|
+
{
|
|
9283
|
+
"name": "notification_opt_outs_slug_context_idx",
|
|
9284
|
+
"columns": [
|
|
9285
|
+
"slug",
|
|
9286
|
+
"context"
|
|
9287
|
+
]
|
|
9288
|
+
},
|
|
9289
|
+
{
|
|
9290
|
+
"name": "notification_opt_outs_tenant_id_created_at_idx",
|
|
9291
|
+
"columns": [
|
|
9292
|
+
"tenant_id",
|
|
9293
|
+
"created_at"
|
|
9294
|
+
]
|
|
9295
|
+
},
|
|
9296
|
+
{
|
|
9297
|
+
"name": "notification_opt_outs_recipient_user_id_idx",
|
|
9298
|
+
"columns": [
|
|
9299
|
+
"recipient_user_id"
|
|
9300
|
+
]
|
|
9301
|
+
}
|
|
9302
|
+
],
|
|
9303
|
+
"version": "a13e93c1"
|
|
9304
|
+
},
|
|
9305
|
+
"importPath": "@happyvertical/smrt-messages"
|
|
9306
|
+
},
|
|
8978
9307
|
"@happyvertical/smrt-messages:PersonaMessageRoute": {
|
|
8979
9308
|
"name": "personamessageroute",
|
|
8980
9309
|
"className": "PersonaMessageRoute",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { SmrtObjectOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
import '../__smrt-register__.js';
|
|
3
|
+
import { SmrtObject } from '@happyvertical/smrt-core';
|
|
4
|
+
import type { NotificationDeliveryChannel } from './NotificationDelivery.js';
|
|
5
|
+
/** `*` (everything in the tenant) or a host key such as `project:<uuid>`. */
|
|
6
|
+
export declare const NOTIFICATION_OPT_OUT_SCOPE_PATTERN: RegExp;
|
|
7
|
+
/** The scope that matches every notification of the channel in the tenant. */
|
|
8
|
+
export declare const NOTIFICATION_OPT_OUT_ALL_SCOPE = "*";
|
|
9
|
+
export declare function isValidNotificationOptOutScope(scope: unknown): scope is string;
|
|
10
|
+
export interface NotificationOptOutOptions extends SmrtObjectOptions {
|
|
11
|
+
tenantId?: string;
|
|
12
|
+
recipientUserId?: string;
|
|
13
|
+
channel?: NotificationDeliveryChannel;
|
|
14
|
+
scope?: string;
|
|
15
|
+
optedOutAt?: Date | null;
|
|
16
|
+
updatedBy?: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* One person's opt-out of one notification channel for one scope, in one
|
|
20
|
+
* tenant: the preference store behind the outbox's `isSuppressed` hook.
|
|
21
|
+
*
|
|
22
|
+
* Identity is `(tenant, recipient, channel, scope)` (the conflict identity).
|
|
23
|
+
* `optedOutAt` is set while the person is opted out and cleared by an opt-in,
|
|
24
|
+
* so the row keeps its history. `scope` is a host-defined key validated by
|
|
25
|
+
* {@link NOTIFICATION_OPT_OUT_SCOPE_PATTERN}. Read and change rows through
|
|
26
|
+
* `NotificationOptOutService`; the generated REST/MCP/CLI are off because the
|
|
27
|
+
* service is the authorization boundary.
|
|
28
|
+
*/
|
|
29
|
+
export declare class NotificationOptOut extends SmrtObject {
|
|
30
|
+
tenantId: string;
|
|
31
|
+
/** The person who opted out (`smrt-users:User`, uuid). */
|
|
32
|
+
recipientUserId: string;
|
|
33
|
+
/** Channel opted out of; only `email` today. */
|
|
34
|
+
channel: NotificationDeliveryChannel;
|
|
35
|
+
/** `*` or a host key such as `project:<uuid>`. */
|
|
36
|
+
scope: string;
|
|
37
|
+
/** When the opt-out took effect; null once the person opted back in. */
|
|
38
|
+
optedOutAt: Date | null;
|
|
39
|
+
/** Who last changed the row: the person's user id, or `system` for host code. */
|
|
40
|
+
updatedBy: string;
|
|
41
|
+
constructor(options?: NotificationOptOutOptions);
|
|
42
|
+
get isOptedOut(): boolean;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=NotificationOptOut.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationOptOut.d.ts","sourceRoot":"","sources":["../../src/models/NotificationOptOut.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAGlE,OAAO,yBAAyB,CAAC;AACjC,OAAO,EAGL,UAAU,EAEX,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,2BAA2B,CAAC;AAE7E,6EAA6E;AAC7E,eAAO,MAAM,kCAAkC,QACI,CAAC;AAEpD,8EAA8E;AAC9E,eAAO,MAAM,8BAA8B,MAAM,CAAC;AAElD,wBAAgB,8BAA8B,CAC5C,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,MAAM,CAIjB;AAED,MAAM,WAAW,yBAA0B,SAAQ,iBAAiB;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,2BAA2B,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,qBAQa,kBAAmB,SAAQ,UAAU;IAEhD,QAAQ,SAAM;IAEd,0DAA0D;IAE1D,eAAe,SAAM;IAErB,gDAAgD;IAEhD,OAAO,EAAE,2BAA2B,CAAW;IAE/C,kDAAkD;IAElD,KAAK,SAAM;IAEX,wEAAwE;IACxE,UAAU,EAAE,IAAI,GAAG,IAAI,CAAQ;IAE/B,iFAAiF;IACjF,SAAS,SAAM;gBAEH,OAAO,GAAE,yBAA8B;IAWnD,IAAI,UAAU,IAAI,OAAO,CAExB;CACF"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type SmrtClassOptions } from '@happyvertical/smrt-core';
|
|
2
|
+
import { type NotificationDeliveryChannel } from '../models/NotificationDelivery.js';
|
|
3
|
+
import { NotificationOptOut } from '../models/NotificationOptOut.js';
|
|
4
|
+
import type { NotificationSuppressionRequest } from './NotificationOutboxService.js';
|
|
5
|
+
/** The signed-in person making a request, as the host authenticated them. */
|
|
6
|
+
export interface NotificationOptOutPrincipal {
|
|
7
|
+
userId: string;
|
|
8
|
+
/**
|
|
9
|
+
* The tenant the principal is authenticated in. Required and must equal the
|
|
10
|
+
* request's tenant: a principal never acts in another tenant. A host whose
|
|
11
|
+
* recipient is authenticated elsewhere (e.g. a member of a child tenant)
|
|
12
|
+
* authorizes the recipient itself and makes a trusted call without a
|
|
13
|
+
* principal instead.
|
|
14
|
+
*/
|
|
15
|
+
tenantId: string;
|
|
16
|
+
}
|
|
17
|
+
export interface NotificationOptOutKey {
|
|
18
|
+
tenantId: string;
|
|
19
|
+
recipientUserId: string;
|
|
20
|
+
channel: NotificationDeliveryChannel;
|
|
21
|
+
/** `*` or a host key such as `project:<uuid>`. */
|
|
22
|
+
scope: string;
|
|
23
|
+
}
|
|
24
|
+
export interface NotificationOptOutChange extends NotificationOptOutKey {
|
|
25
|
+
/**
|
|
26
|
+
* The signed-in person, authenticated in this tenant. When present only the
|
|
27
|
+
* recipient themself may change the row, and only in the principal's own
|
|
28
|
+
* tenant; when omitted the call is trusted host code that has already
|
|
29
|
+
* authenticated the recipient (e.g. a signed unsubscribe link, or a host
|
|
30
|
+
* route whose member belongs to a child tenant of the delivery tenant).
|
|
31
|
+
*/
|
|
32
|
+
principal?: NotificationOptOutPrincipal;
|
|
33
|
+
}
|
|
34
|
+
export interface NotificationOptOutListRequest {
|
|
35
|
+
tenantId: string;
|
|
36
|
+
recipientUserId: string;
|
|
37
|
+
principal: NotificationOptOutPrincipal;
|
|
38
|
+
}
|
|
39
|
+
export type NotificationOptOutDenialReason = 'principal' | 'tenant';
|
|
40
|
+
/** Thrown when a principal tries to read or change another person's opt-out. */
|
|
41
|
+
export declare class NotificationOptOutDeniedError extends Error {
|
|
42
|
+
readonly reason: NotificationOptOutDenialReason;
|
|
43
|
+
constructor(reason: NotificationOptOutDenialReason, message: string);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* The per-recipient opt-out ledger behind the outbox's `isSuppressed` hook.
|
|
47
|
+
*
|
|
48
|
+
* Writes are idempotent and, with a `principal`, limited to the recipient's
|
|
49
|
+
* own rows. {@link isOptedOut} is a trusted read for suppression: it runs
|
|
50
|
+
* where the recipient is not the caller (a manager enqueues, a worker
|
|
51
|
+
* dispatches) and is always scoped to the given tenant.
|
|
52
|
+
*/
|
|
53
|
+
export declare class NotificationOptOutService {
|
|
54
|
+
private readonly options;
|
|
55
|
+
constructor(options: {
|
|
56
|
+
db: SmrtClassOptions['db'];
|
|
57
|
+
});
|
|
58
|
+
private collection;
|
|
59
|
+
/** Opt the recipient out of `scope`; a repeat leaves the row untouched. */
|
|
60
|
+
optOut(change: NotificationOptOutChange): Promise<NotificationOptOut>;
|
|
61
|
+
/** Clear an opt-out; a repeat, or no row at all, is a no-op. */
|
|
62
|
+
optIn(change: NotificationOptOutChange): Promise<NotificationOptOut>;
|
|
63
|
+
/**
|
|
64
|
+
* Whether the recipient has opted out of `scope` (a `*` row matches every
|
|
65
|
+
* scope). Trusted read: no principal, always under the tenant's scope.
|
|
66
|
+
*/
|
|
67
|
+
isOptedOut(key: NotificationOptOutKey): Promise<boolean>;
|
|
68
|
+
/** The recipient's own active opt-outs, in one tenant. */
|
|
69
|
+
listForRecipient(request: NotificationOptOutListRequest): Promise<NotificationOptOut[]>;
|
|
70
|
+
private change;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Build an `isSuppressed` function for `NotificationOutboxOptions` from the
|
|
74
|
+
* ledger. `scopeOf` maps a notification to the scope a recipient can opt out
|
|
75
|
+
* of (e.g. `project:<uuid>` from its `sourceRef`), or null when the
|
|
76
|
+
* notification is not opt-out-able. A `*` opt-out suppresses any notification
|
|
77
|
+
* `scopeOf` maps to a scope; returning `'*'` checks only the tenant-wide row.
|
|
78
|
+
*/
|
|
79
|
+
export declare function optOutSuppression(service: Pick<NotificationOptOutService, 'isOptedOut'>, scopeOf: (request: NotificationSuppressionRequest) => string | null | Promise<string | null>): (request: NotificationSuppressionRequest) => Promise<boolean>;
|
|
80
|
+
//# sourceMappingURL=NotificationOptOutService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"NotificationOptOutService.d.ts","sourceRoot":"","sources":["../../src/services/NotificationOptOutService.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,gBAAgB,EACtB,MAAM,0BAA0B,CAAC;AAGlC,OAAO,EAEL,KAAK,2BAA2B,EACjC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAGL,kBAAkB,EACnB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,gCAAgC,CAAC;AAErF,6EAA6E;AAC7E,MAAM,WAAW,2BAA2B;IAC1C,MAAM,EAAE,MAAM,CAAC;IACf;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,2BAA2B,CAAC;IACrC,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,wBAAyB,SAAQ,qBAAqB;IACrE;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,2BAA2B,CAAC;CACzC;AAED,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,SAAS,EAAE,2BAA2B,CAAC;CACxC;AAED,MAAM,MAAM,8BAA8B,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEpE,gFAAgF;AAChF,qBAAa,6BAA8B,SAAQ,KAAK;IAEpD,QAAQ,CAAC,MAAM,EAAE,8BAA8B;gBAAtC,MAAM,EAAE,8BAA8B,EAC/C,OAAO,EAAE,MAAM;CAKlB;AA+CD;;;;;;;GAOG;AACH,qBAAa,yBAAyB;IACxB,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE;QAAE,EAAE,EAAE,gBAAgB,CAAC,IAAI,CAAC,CAAA;KAAE;IAEpE,OAAO,CAAC,UAAU;IAIlB,2EAA2E;IACrE,MAAM,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAI3E,gEAAgE;IAC1D,KAAK,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAI1E;;;OAGG;IACG,UAAU,CAAC,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC;IAc9D,0DAA0D;IACpD,gBAAgB,CACpB,OAAO,EAAE,6BAA6B,GACrC,OAAO,CAAC,kBAAkB,EAAE,CAAC;YAmBlB,MAAM;CA6DrB;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,IAAI,CAAC,yBAAyB,EAAE,YAAY,CAAC,EACtD,OAAO,EAAE,CACP,OAAO,EAAE,8BAA8B,KACpC,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,GAC1C,CAAC,OAAO,EAAE,8BAA8B,KAAK,OAAO,CAAC,OAAO,CAAC,CAW/D"}
|