@layered-loader/sqs 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +433 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/SqsGroupNotificationConsumer.d.ts +28 -0
- package/dist/lib/SqsGroupNotificationConsumer.js +107 -0
- package/dist/lib/SqsGroupNotificationConsumer.js.map +1 -0
- package/dist/lib/SqsGroupNotificationFactory.d.ts +22 -0
- package/dist/lib/SqsGroupNotificationFactory.js +40 -0
- package/dist/lib/SqsGroupNotificationFactory.js.map +1 -0
- package/dist/lib/SqsGroupNotificationPublisher.d.ts +38 -0
- package/dist/lib/SqsGroupNotificationPublisher.js +102 -0
- package/dist/lib/SqsGroupNotificationPublisher.js.map +1 -0
- package/dist/lib/SqsNotificationConsumer.d.ts +44 -0
- package/dist/lib/SqsNotificationConsumer.js +123 -0
- package/dist/lib/SqsNotificationConsumer.js.map +1 -0
- package/dist/lib/SqsNotificationFactory.d.ts +29 -0
- package/dist/lib/SqsNotificationFactory.js +40 -0
- package/dist/lib/SqsNotificationFactory.js.map +1 -0
- package/dist/lib/SqsNotificationPublisher.d.ts +39 -0
- package/dist/lib/SqsNotificationPublisher.js +109 -0
- package/dist/lib/SqsNotificationPublisher.js.map +1 -0
- package/dist/lib/channelNameResolver.d.ts +12 -0
- package/dist/lib/channelNameResolver.js +18 -0
- package/dist/lib/channelNameResolver.js.map +1 -0
- package/dist/lib/groupNotificationSchemas.d.ts +49 -0
- package/dist/lib/groupNotificationSchemas.js +31 -0
- package/dist/lib/groupNotificationSchemas.js.map +1 -0
- package/dist/lib/notificationSchemas.d.ts +66 -0
- package/dist/lib/notificationSchemas.js +40 -0
- package/dist/lib/notificationSchemas.js.map +1 -0
- package/dist/lib/triggers/AbstractSqsTrigger.d.ts +62 -0
- package/dist/lib/triggers/AbstractSqsTrigger.js +104 -0
- package/dist/lib/triggers/AbstractSqsTrigger.js.map +1 -0
- package/dist/lib/triggers/SqsGroupInvalidationTrigger.d.ts +39 -0
- package/dist/lib/triggers/SqsGroupInvalidationTrigger.js +46 -0
- package/dist/lib/triggers/SqsGroupInvalidationTrigger.js.map +1 -0
- package/dist/lib/triggers/SqsInvalidationTrigger.d.ts +56 -0
- package/dist/lib/triggers/SqsInvalidationTrigger.js +47 -0
- package/dist/lib/triggers/SqsInvalidationTrigger.js.map +1 -0
- package/dist/lib/triggers/dispatch.d.ts +19 -0
- package/dist/lib/triggers/dispatch.js +69 -0
- package/dist/lib/triggers/dispatch.js.map +1 -0
- package/dist/lib/triggers/types.d.ts +54 -0
- package/dist/lib/triggers/types.js +12 -0
- package/dist/lib/triggers/types.js.map +1 -0
- package/package.json +75 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { SqsGroupNotificationConsumer, } from './SqsGroupNotificationConsumer.js';
|
|
3
|
+
import { SqsGroupNotificationPublisher, } from './SqsGroupNotificationPublisher.js';
|
|
4
|
+
export function createGroupNotificationPair(config) {
|
|
5
|
+
const serverUuid = config.serverUuid ?? randomUUID();
|
|
6
|
+
const publisherConfig = config.publisher;
|
|
7
|
+
const consumerConfig = config.consumer;
|
|
8
|
+
const publisher = new SqsGroupNotificationPublisher(publisherConfig.creationConfig
|
|
9
|
+
? {
|
|
10
|
+
serverUuid,
|
|
11
|
+
channel: config.channel,
|
|
12
|
+
errorHandler: config.publisherErrorHandler,
|
|
13
|
+
dependencies: publisherConfig.dependencies,
|
|
14
|
+
creationConfig: publisherConfig.creationConfig,
|
|
15
|
+
}
|
|
16
|
+
: {
|
|
17
|
+
serverUuid,
|
|
18
|
+
channel: config.channel,
|
|
19
|
+
errorHandler: config.publisherErrorHandler,
|
|
20
|
+
dependencies: publisherConfig.dependencies,
|
|
21
|
+
locatorConfig: publisherConfig.locatorConfig,
|
|
22
|
+
});
|
|
23
|
+
const consumer = new SqsGroupNotificationConsumer(consumerConfig.creationConfig
|
|
24
|
+
? {
|
|
25
|
+
serverUuid,
|
|
26
|
+
errorHandler: config.consumerErrorHandler,
|
|
27
|
+
dependencies: consumerConfig.dependencies,
|
|
28
|
+
subscriptionConfig: consumerConfig.subscriptionConfig,
|
|
29
|
+
creationConfig: consumerConfig.creationConfig,
|
|
30
|
+
}
|
|
31
|
+
: {
|
|
32
|
+
serverUuid,
|
|
33
|
+
errorHandler: config.consumerErrorHandler,
|
|
34
|
+
dependencies: consumerConfig.dependencies,
|
|
35
|
+
subscriptionConfig: consumerConfig.subscriptionConfig,
|
|
36
|
+
locatorConfig: consumerConfig.locatorConfig,
|
|
37
|
+
});
|
|
38
|
+
return { publisher, consumer };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=SqsGroupNotificationFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SqsGroupNotificationFactory.js","sourceRoot":"","sources":["../../lib/SqsGroupNotificationFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGxC,OAAO,EACL,4BAA4B,GAE7B,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACL,6BAA6B,GAE9B,MAAM,oCAAoC,CAAA;AAiB3C,MAAM,UAAU,2BAA2B,CAAc,MAAkC;IAIzF,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,UAAU,EAAE,CAAA;IAEpD,MAAM,eAAe,GAAG,MAAM,CAAC,SAAS,CAAA;IACxC,MAAM,cAAc,GAAG,MAAM,CAAC,QAAQ,CAAA;IAEtC,MAAM,SAAS,GAAG,IAAI,6BAA6B,CACjD,eAAe,CAAC,cAAc;QAC5B,CAAC,CAAC;YACE,UAAU;YACV,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,YAAY,EAAE,MAAM,CAAC,qBAAqB;YAC1C,YAAY,EAAE,eAAe,CAAC,YAAY;YAC1C,cAAc,EAAE,eAAe,CAAC,cAAc;SAC/C;QACH,CAAC,CAAC;YACE,UAAU;YACV,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,YAAY,EAAE,MAAM,CAAC,qBAAqB;YAC1C,YAAY,EAAE,eAAe,CAAC,YAAY;YAC1C,aAAa,EAAE,eAAe,CAAC,aAAa;SAC7C,CACN,CAAA;IAED,MAAM,QAAQ,GAAG,IAAI,4BAA4B,CAC/C,cAAc,CAAC,cAAc;QAC3B,CAAC,CAAC;YACE,UAAU;YACV,YAAY,EAAE,MAAM,CAAC,oBAAoB;YACzC,YAAY,EAAE,cAAc,CAAC,YAAY;YACzC,kBAAkB,EAAE,cAAc,CAAC,kBAAkB;YACrD,cAAc,EAAE,cAAc,CAAC,cAAc;SAC9C;QACH,CAAC,CAAC;YACE,UAAU;YACV,YAAY,EAAE,MAAM,CAAC,oBAAoB;YACzC,YAAY,EAAE,cAAc,CAAC,YAAY;YACzC,kBAAkB,EAAE,cAAc,CAAC,kBAAkB;YACrD,aAAa,EAAE,cAAc,CAAC,aAAa;SAC5C,CACN,CAAA;IAED,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAA;AAChC,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type SNSCreationConfig, type SNSDependencies, type SNSTopicLocatorType } from '@message-queue-toolkit/sns';
|
|
2
|
+
import type { GroupNotificationPublisher, PublisherErrorHandler } from 'layered-loader';
|
|
3
|
+
export type SqsGroupNotificationPublisherConfig = {
|
|
4
|
+
creationConfig: SNSCreationConfig;
|
|
5
|
+
locatorConfig?: never;
|
|
6
|
+
} | {
|
|
7
|
+
creationConfig?: never;
|
|
8
|
+
locatorConfig: SNSTopicLocatorType;
|
|
9
|
+
};
|
|
10
|
+
export type SqsGroupNotificationPublisherParams = {
|
|
11
|
+
serverUuid: string;
|
|
12
|
+
errorHandler?: PublisherErrorHandler;
|
|
13
|
+
channel?: string;
|
|
14
|
+
dependencies: SNSDependencies;
|
|
15
|
+
} & SqsGroupNotificationPublisherConfig;
|
|
16
|
+
export declare class SqsGroupNotificationPublisher<LoadedValue> implements GroupNotificationPublisher<LoadedValue> {
|
|
17
|
+
readonly channel: string;
|
|
18
|
+
readonly errorHandler: PublisherErrorHandler;
|
|
19
|
+
private readonly serverUuid;
|
|
20
|
+
private readonly publisher;
|
|
21
|
+
private initPromise?;
|
|
22
|
+
private initialized;
|
|
23
|
+
constructor(params: SqsGroupNotificationPublisherParams);
|
|
24
|
+
subscribe(): Promise<void>;
|
|
25
|
+
private initializePublisher;
|
|
26
|
+
deleteFromGroup(key: string, group: string): Promise<unknown>;
|
|
27
|
+
deleteGroup(group: string): Promise<unknown>;
|
|
28
|
+
clear(): Promise<unknown>;
|
|
29
|
+
close(): Promise<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Returns the resolved topic ARN. Available only after `subscribe()` (or the
|
|
32
|
+
* first publish) has fully completed; returns `undefined` while init is in
|
|
33
|
+
* flight or before it has been triggered.
|
|
34
|
+
*/
|
|
35
|
+
get topicArn(): string | undefined;
|
|
36
|
+
private publishCommand;
|
|
37
|
+
private buildEnvelope;
|
|
38
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { AbstractSnsPublisher, } from '@message-queue-toolkit/sns';
|
|
3
|
+
import { resolveChannelName } from './channelNameResolver.js';
|
|
4
|
+
import { CLEAR_COMMAND, DELETE_FROM_GROUP_COMMAND, DELETE_GROUP_COMMAND, GROUP_NOTIFICATION_SCHEMAS, NOTIFICATION_ID_FIELD, NOTIFICATION_TIMESTAMP_FIELD, NOTIFICATION_TYPE_FIELD, } from './groupNotificationSchemas.js';
|
|
5
|
+
const DEFAULT_PUBLISHER_ERROR_HANDLER = (err, channel, logger) => {
|
|
6
|
+
logger.error(`Error while publishing notification to channel ${channel}: ${err.message}`);
|
|
7
|
+
};
|
|
8
|
+
class SnsGroupInvalidationPublisher extends AbstractSnsPublisher {
|
|
9
|
+
get publicTopicArn() {
|
|
10
|
+
return this.topicArn;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class SqsGroupNotificationPublisher {
|
|
14
|
+
channel;
|
|
15
|
+
errorHandler;
|
|
16
|
+
serverUuid;
|
|
17
|
+
publisher;
|
|
18
|
+
initPromise;
|
|
19
|
+
initialized = false;
|
|
20
|
+
constructor(params) {
|
|
21
|
+
this.serverUuid = params.serverUuid;
|
|
22
|
+
this.errorHandler = params.errorHandler ?? DEFAULT_PUBLISHER_ERROR_HANDLER;
|
|
23
|
+
this.channel = resolveChannelName(params);
|
|
24
|
+
// mqt's `messageSchemas` is `readonly ZodSchema<UnionType>[]`. Our schemas
|
|
25
|
+
// are narrower (one per command type) and `ZodSchema` is invariant in T,
|
|
26
|
+
// so the array of narrow schemas does not satisfy the broad type at the
|
|
27
|
+
// type level — even though mqt resolves them correctly at runtime via the
|
|
28
|
+
// configured `messageTypeResolver`.
|
|
29
|
+
const options = {
|
|
30
|
+
messageSchemas: GROUP_NOTIFICATION_SCHEMAS,
|
|
31
|
+
messageTypeResolver: { messageTypePath: NOTIFICATION_TYPE_FIELD },
|
|
32
|
+
messageIdField: NOTIFICATION_ID_FIELD,
|
|
33
|
+
messageTimestampField: NOTIFICATION_TIMESTAMP_FIELD,
|
|
34
|
+
...(params.creationConfig
|
|
35
|
+
? { creationConfig: params.creationConfig }
|
|
36
|
+
: { locatorConfig: params.locatorConfig }),
|
|
37
|
+
};
|
|
38
|
+
this.publisher = new SnsGroupInvalidationPublisher(params.dependencies, options);
|
|
39
|
+
}
|
|
40
|
+
async subscribe() {
|
|
41
|
+
if (this.initialized)
|
|
42
|
+
return;
|
|
43
|
+
if (!this.initPromise) {
|
|
44
|
+
this.initPromise = this.initializePublisher();
|
|
45
|
+
}
|
|
46
|
+
return this.initPromise;
|
|
47
|
+
}
|
|
48
|
+
async initializePublisher() {
|
|
49
|
+
try {
|
|
50
|
+
await this.publisher.init();
|
|
51
|
+
this.initialized = true;
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
this.initPromise = undefined;
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
deleteFromGroup(key, group) {
|
|
59
|
+
return this.publishCommand({
|
|
60
|
+
type: DELETE_FROM_GROUP_COMMAND,
|
|
61
|
+
key,
|
|
62
|
+
group,
|
|
63
|
+
...this.buildEnvelope(),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
deleteGroup(group) {
|
|
67
|
+
return this.publishCommand({
|
|
68
|
+
type: DELETE_GROUP_COMMAND,
|
|
69
|
+
group,
|
|
70
|
+
...this.buildEnvelope(),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
clear() {
|
|
74
|
+
return this.publishCommand({
|
|
75
|
+
type: CLEAR_COMMAND,
|
|
76
|
+
...this.buildEnvelope(),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
async close() {
|
|
80
|
+
await this.publisher.close();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Returns the resolved topic ARN. Available only after `subscribe()` (or the
|
|
84
|
+
* first publish) has fully completed; returns `undefined` while init is in
|
|
85
|
+
* flight or before it has been triggered.
|
|
86
|
+
*/
|
|
87
|
+
get topicArn() {
|
|
88
|
+
return this.initialized ? this.publisher.publicTopicArn : undefined;
|
|
89
|
+
}
|
|
90
|
+
async publishCommand(command) {
|
|
91
|
+
await this.subscribe();
|
|
92
|
+
await this.publisher.publish(command);
|
|
93
|
+
}
|
|
94
|
+
buildEnvelope() {
|
|
95
|
+
return {
|
|
96
|
+
id: randomUUID(),
|
|
97
|
+
timestamp: new Date().toISOString(),
|
|
98
|
+
originUuid: this.serverUuid,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=SqsGroupNotificationPublisher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SqsGroupNotificationPublisher.js","sourceRoot":"","sources":["../../lib/SqsGroupNotificationPublisher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACxC,OAAO,EACL,oBAAoB,GAKrB,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAC7D,OAAO,EACL,aAAa,EACb,yBAAyB,EACzB,oBAAoB,EACpB,0BAA0B,EAE1B,qBAAqB,EACrB,4BAA4B,EAC5B,uBAAuB,GACxB,MAAM,+BAA+B,CAAA;AAEtC,MAAM,+BAA+B,GAA0B,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE;IACtF,MAAM,CAAC,KAAK,CAAC,kDAAkD,OAAO,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;AAC3F,CAAC,CAAA;AAED,MAAM,6BAA8B,SAAQ,oBAE3C;IACC,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,QAAQ,CAAA;IACtB,CAAC;CACF;AAaD,MAAM,OAAO,6BAA6B;IAGxB,OAAO,CAAQ;IACf,YAAY,CAAuB;IAElC,UAAU,CAAQ;IAClB,SAAS,CAA+B;IACjD,WAAW,CAAgB;IAC3B,WAAW,GAAG,KAAK,CAAA;IAE3B,YAAY,MAA2C;QACrD,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAA;QACnC,IAAI,CAAC,YAAY,GAAG,MAAM,CAAC,YAAY,IAAI,+BAA+B,CAAA;QAC1E,IAAI,CAAC,OAAO,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAA;QAEzC,2EAA2E;QAC3E,yEAAyE;QACzE,wEAAwE;QACxE,0EAA0E;QAC1E,oCAAoC;QACpC,MAAM,OAAO,GAAG;YACd,cAAc,EAAE,0BAA0B;YAC1C,mBAAmB,EAAE,EAAE,eAAe,EAAE,uBAAuB,EAAE;YACjE,cAAc,EAAE,qBAAqB;YACrC,qBAAqB,EAAE,4BAA4B;YACnD,GAAG,CAAC,MAAM,CAAC,cAAc;gBACvB,CAAC,CAAC,EAAE,cAAc,EAAE,MAAM,CAAC,cAAc,EAAE;gBAC3C,CAAC,CAAC,EAAE,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,CAAC;SACe,CAAA;QAE7D,IAAI,CAAC,SAAS,GAAG,IAAI,6BAA6B,CAAC,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,CAAA;IAClF,CAAC;IAED,KAAK,CAAC,SAAS;QACb,IAAI,IAAI,CAAC,WAAW;YAAE,OAAM;QAE5B,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAA;QAC/C,CAAC;QACD,OAAO,IAAI,CAAC,WAAW,CAAA;IACzB,CAAC;IAEO,KAAK,CAAC,mBAAmB;QAC/B,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAA;YAC3B,IAAI,CAAC,WAAW,GAAG,IAAI,CAAA;QACzB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,WAAW,GAAG,SAAS,CAAA;YAC5B,MAAM,GAAG,CAAA;QACX,CAAC;IACH,CAAC;IAED,eAAe,CAAC,GAAW,EAAE,KAAa;QACxC,OAAO,IAAI,CAAC,cAAc,CAAC;YACzB,IAAI,EAAE,yBAAyB;YAC/B,GAAG;YACH,KAAK;YACL,GAAG,IAAI,CAAC,aAAa,EAAE;SACxB,CAAC,CAAA;IACJ,CAAC;IAED,WAAW,CAAC,KAAa;QACvB,OAAO,IAAI,CAAC,cAAc,CAAC;YACzB,IAAI,EAAE,oBAAoB;YAC1B,KAAK;YACL,GAAG,IAAI,CAAC,aAAa,EAAE;SACxB,CAAC,CAAA;IACJ,CAAC;IAED,KAAK;QACH,OAAO,IAAI,CAAC,cAAc,CAAC;YACzB,IAAI,EAAE,aAAa;YACnB,GAAG,IAAI,CAAC,aAAa,EAAE;SACxB,CAAC,CAAA;IACJ,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAA;IAC9B,CAAC;IAED;;;;OAIG;IACH,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAA;IACrE,CAAC;IAEO,KAAK,CAAC,cAAc,CAAC,OAAiC;QAC5D,MAAM,IAAI,CAAC,SAAS,EAAE,CAAA;QACtB,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IACvC,CAAC;IAEO,aAAa;QACnB,OAAO;YACL,EAAE,EAAE,UAAU,EAAE;YAChB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,UAAU,EAAE,IAAI,CAAC,UAAU;SAC5B,CAAA;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { SubscribeCommandInput } from '@aws-sdk/client-sns';
|
|
2
|
+
import { type SNSSQSConsumerDependencies, type SNSSQSCreationConfig, type SNSSQSQueueLocatorType } from '@message-queue-toolkit/sns';
|
|
3
|
+
import type { ConsumerErrorHandler } from 'layered-loader';
|
|
4
|
+
import { AbstractNotificationConsumer } from 'layered-loader';
|
|
5
|
+
/**
|
|
6
|
+
* Mirror of `SNSSubscriptionOptions` from `@message-queue-toolkit/sns/utils/snsSubscriber`,
|
|
7
|
+
* which is not re-exported from the package's main entry point.
|
|
8
|
+
*/
|
|
9
|
+
export type SqsSubscriptionOptions = Omit<SubscribeCommandInput, 'TopicArn' | 'Endpoint' | 'Protocol' | 'ReturnSubscriptionArn'> & {
|
|
10
|
+
updateAttributesIfExists: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type SqsNotificationConsumerConfig = {
|
|
13
|
+
creationConfig: SNSSQSCreationConfig;
|
|
14
|
+
locatorConfig?: never;
|
|
15
|
+
} | {
|
|
16
|
+
creationConfig?: never;
|
|
17
|
+
locatorConfig: SNSSQSQueueLocatorType;
|
|
18
|
+
};
|
|
19
|
+
export type SqsNotificationConsumerParams = {
|
|
20
|
+
serverUuid: string;
|
|
21
|
+
errorHandler?: ConsumerErrorHandler;
|
|
22
|
+
dependencies: SNSSQSConsumerDependencies;
|
|
23
|
+
subscriptionConfig?: SqsSubscriptionOptions;
|
|
24
|
+
} & SqsNotificationConsumerConfig;
|
|
25
|
+
export declare class SqsNotificationConsumer<LoadedValue> extends AbstractNotificationConsumer<LoadedValue> {
|
|
26
|
+
private readonly params;
|
|
27
|
+
private internalConsumer?;
|
|
28
|
+
private subscribePromise?;
|
|
29
|
+
constructor(params: SqsNotificationConsumerParams);
|
|
30
|
+
subscribe(): Promise<unknown>;
|
|
31
|
+
close(): Promise<void>;
|
|
32
|
+
/**
|
|
33
|
+
* Returns the resolved topic ARN. Available only after subscribe() has completed.
|
|
34
|
+
*/
|
|
35
|
+
get topicArn(): string | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Returns the resolved subscription ARN. Available only after subscribe() has completed.
|
|
38
|
+
*/
|
|
39
|
+
get subscriptionArn(): string | undefined;
|
|
40
|
+
/**
|
|
41
|
+
* Returns the resolved queue URL. Available only after subscribe() has completed.
|
|
42
|
+
*/
|
|
43
|
+
get queueUrl(): string | undefined;
|
|
44
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { MessageHandlerConfigBuilder } from '@message-queue-toolkit/core';
|
|
2
|
+
import { AbstractSnsSqsConsumer, } from '@message-queue-toolkit/sns';
|
|
3
|
+
import { AbstractNotificationConsumer } from 'layered-loader';
|
|
4
|
+
import { CLEAR_NOTIFICATION_SCHEMA, DELETE_MANY_NOTIFICATION_SCHEMA, DELETE_NOTIFICATION_SCHEMA, NOTIFICATION_ID_FIELD, NOTIFICATION_TIMESTAMP_FIELD, NOTIFICATION_TYPE_FIELD, SET_NOTIFICATION_SCHEMA, } from './notificationSchemas.js';
|
|
5
|
+
class SnsSqsInvalidationConsumer extends AbstractSnsSqsConsumer {
|
|
6
|
+
constructor(dependencies, options, context) {
|
|
7
|
+
super(dependencies, options, context);
|
|
8
|
+
}
|
|
9
|
+
get publicTopicArn() {
|
|
10
|
+
return this.topicArn;
|
|
11
|
+
}
|
|
12
|
+
get publicSubscriptionArn() {
|
|
13
|
+
return this.subscriptionArn;
|
|
14
|
+
}
|
|
15
|
+
get publicQueueUrl() {
|
|
16
|
+
return this.queueUrl;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export class SqsNotificationConsumer extends AbstractNotificationConsumer {
|
|
20
|
+
params;
|
|
21
|
+
internalConsumer;
|
|
22
|
+
subscribePromise;
|
|
23
|
+
constructor(params) {
|
|
24
|
+
super(params.serverUuid, params.errorHandler);
|
|
25
|
+
this.params = params;
|
|
26
|
+
}
|
|
27
|
+
async subscribe() {
|
|
28
|
+
if (this.internalConsumer) {
|
|
29
|
+
return this.internalConsumer;
|
|
30
|
+
}
|
|
31
|
+
if (this.subscribePromise) {
|
|
32
|
+
return this.subscribePromise;
|
|
33
|
+
}
|
|
34
|
+
if (!this.targetCache) {
|
|
35
|
+
throw new Error('targetCache must be set via setTargetCache() before subscribe() is called');
|
|
36
|
+
}
|
|
37
|
+
const handlers = new MessageHandlerConfigBuilder()
|
|
38
|
+
.addConfig(DELETE_NOTIFICATION_SCHEMA, async (message, ctx) => {
|
|
39
|
+
if (message.originUuid !== ctx.serverUuid) {
|
|
40
|
+
ctx.targetCache.delete(message.key);
|
|
41
|
+
}
|
|
42
|
+
return { result: 'success' };
|
|
43
|
+
})
|
|
44
|
+
.addConfig(DELETE_MANY_NOTIFICATION_SCHEMA, async (message, ctx) => {
|
|
45
|
+
if (message.originUuid !== ctx.serverUuid) {
|
|
46
|
+
ctx.targetCache.deleteMany(message.keys);
|
|
47
|
+
}
|
|
48
|
+
return { result: 'success' };
|
|
49
|
+
})
|
|
50
|
+
.addConfig(CLEAR_NOTIFICATION_SCHEMA, async (message, ctx) => {
|
|
51
|
+
if (message.originUuid !== ctx.serverUuid) {
|
|
52
|
+
ctx.targetCache.clear();
|
|
53
|
+
}
|
|
54
|
+
return { result: 'success' };
|
|
55
|
+
})
|
|
56
|
+
.addConfig(SET_NOTIFICATION_SCHEMA, async (message, ctx) => {
|
|
57
|
+
if (message.originUuid !== ctx.serverUuid) {
|
|
58
|
+
ctx.targetCache.set(message.key, message.value ?? null);
|
|
59
|
+
}
|
|
60
|
+
return { result: 'success' };
|
|
61
|
+
})
|
|
62
|
+
.build();
|
|
63
|
+
const options = {
|
|
64
|
+
handlers,
|
|
65
|
+
messageTypeResolver: { messageTypePath: NOTIFICATION_TYPE_FIELD },
|
|
66
|
+
messageIdField: NOTIFICATION_ID_FIELD,
|
|
67
|
+
messageTimestampField: NOTIFICATION_TIMESTAMP_FIELD,
|
|
68
|
+
subscriptionConfig: this.params.subscriptionConfig ?? { updateAttributesIfExists: false },
|
|
69
|
+
...(this.params.creationConfig
|
|
70
|
+
? { creationConfig: this.params.creationConfig }
|
|
71
|
+
: { locatorConfig: this.params.locatorConfig }),
|
|
72
|
+
};
|
|
73
|
+
const consumer = new SnsSqsInvalidationConsumer(this.params.dependencies, options, { serverUuid: this.serverUuid, targetCache: this.targetCache });
|
|
74
|
+
// Single-flight: concurrent subscribe() calls share one init/start; the
|
|
75
|
+
// internal consumer is only assigned once both succeed, so a partial
|
|
76
|
+
// failure leaves the instance in a re-tryable state.
|
|
77
|
+
this.subscribePromise = (async () => {
|
|
78
|
+
try {
|
|
79
|
+
await consumer.init();
|
|
80
|
+
await consumer.start();
|
|
81
|
+
this.internalConsumer = consumer;
|
|
82
|
+
return consumer;
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
await consumer.close().catch(() => undefined);
|
|
86
|
+
throw err;
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
this.subscribePromise = undefined;
|
|
90
|
+
}
|
|
91
|
+
})();
|
|
92
|
+
return this.subscribePromise;
|
|
93
|
+
}
|
|
94
|
+
async close() {
|
|
95
|
+
if (this.subscribePromise) {
|
|
96
|
+
await this.subscribePromise.catch(() => undefined);
|
|
97
|
+
}
|
|
98
|
+
if (!this.internalConsumer)
|
|
99
|
+
return;
|
|
100
|
+
const consumer = this.internalConsumer;
|
|
101
|
+
this.internalConsumer = undefined;
|
|
102
|
+
await consumer.close();
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Returns the resolved topic ARN. Available only after subscribe() has completed.
|
|
106
|
+
*/
|
|
107
|
+
get topicArn() {
|
|
108
|
+
return this.internalConsumer?.publicTopicArn;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Returns the resolved subscription ARN. Available only after subscribe() has completed.
|
|
112
|
+
*/
|
|
113
|
+
get subscriptionArn() {
|
|
114
|
+
return this.internalConsumer?.publicSubscriptionArn;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Returns the resolved queue URL. Available only after subscribe() has completed.
|
|
118
|
+
*/
|
|
119
|
+
get queueUrl() {
|
|
120
|
+
return this.internalConsumer?.publicQueueUrl;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=SqsNotificationConsumer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SqsNotificationConsumer.js","sourceRoot":"","sources":["../../lib/SqsNotificationConsumer.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAA;AACzE,OAAO,EACL,sBAAsB,GAKvB,MAAM,4BAA4B,CAAA;AAEnC,OAAO,EAAE,4BAA4B,EAAE,MAAM,gBAAgB,CAAA;AAC7D,OAAO,EACL,yBAAyB,EACzB,+BAA+B,EAC/B,0BAA0B,EAE1B,qBAAqB,EACrB,4BAA4B,EAC5B,uBAAuB,EACvB,uBAAuB,GACxB,MAAM,0BAA0B,CAAA;AA6BjC,MAAM,0BAAwC,SAAQ,sBAGrD;IACC,YACE,YAAwC,EACxC,OAA4F,EAC5F,OAAqC;QAErC,KAAK,CAAC,YAAY,EAAE,OAAO,EAAE,OAAO,CAAC,CAAA;IACvC,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,QAAQ,CAAA;IACtB,CAAC;IAED,IAAI,qBAAqB;QACvB,OAAO,IAAI,CAAC,eAAe,CAAA;IAC7B,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,QAAQ,CAAA;IACtB,CAAC;CACF;AAED,MAAM,OAAO,uBAAqC,SAAQ,4BAAyC;IAChF,MAAM,CAA+B;IAC9C,gBAAgB,CAA0C;IAC1D,gBAAgB,CAAmD;IAE3E,YAAY,MAAqC;QAC/C,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC,CAAA;QAC7C,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;IACtB,CAAC;IAED,KAAK,CAAC,SAAS;QACb,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC,gBAAgB,CAAA;QAC9B,CAAC;QACD,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC,gBAAgB,CAAA;QAC9B,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CACb,2EAA2E,CAC5E,CAAA;QACH,CAAC;QAED,MAAM,QAAQ,GAAG,IAAI,2BAA2B,EAG7C;aACA,SAAS,CAAC,0BAA0B,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE;YAC5D,IAAI,OAAO,CAAC,UAAU,KAAK,GAAG,CAAC,UAAU,EAAE,CAAC;gBAC1C,GAAG,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YACrC,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;QAC9B,CAAC,CAAC;aACD,SAAS,CAAC,+BAA+B,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE;YACjE,IAAI,OAAO,CAAC,UAAU,KAAK,GAAG,CAAC,UAAU,EAAE,CAAC;gBAC1C,GAAG,CAAC,WAAW,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAA;YAC1C,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;QAC9B,CAAC,CAAC;aACD,SAAS,CAAC,yBAAyB,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE;YAC3D,IAAI,OAAO,CAAC,UAAU,KAAK,GAAG,CAAC,UAAU,EAAE,CAAC;gBAC1C,GAAG,CAAC,WAAW,CAAC,KAAK,EAAE,CAAA;YACzB,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;QAC9B,CAAC,CAAC;aACD,SAAS,CAAC,uBAAuB,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE;YACzD,IAAI,OAAO,CAAC,UAAU,KAAK,GAAG,CAAC,UAAU,EAAE,CAAC;gBAC1C,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAG,OAAO,CAAC,KAA4B,IAAI,IAAI,CAAC,CAAA;YACjF,CAAC;YACD,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,CAAA;QAC9B,CAAC,CAAC;aACD,KAAK,EAAE,CAAA;QAEV,MAAM,OAAO,GAAG;YACd,QAAQ;YACR,mBAAmB,EAAE,EAAE,eAAe,EAAE,uBAAuB,EAAE;YACjE,cAAc,EAAE,qBAAqB;YACrC,qBAAqB,EAAE,4BAA4B;YACnD,kBAAkB,EAAE,IAAI,CAAC,MAAM,CAAC,kBAAkB,IAAI,EAAE,wBAAwB,EAAE,KAAK,EAAE;YACzF,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc;gBAC5B,CAAC,CAAC,EAAE,cAAc,EAAE,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE;gBAChD,CAAC,CAAC,EAAE,aAAa,EAAE,IAAI,CAAC,MAAM,CAAC,aAAa,EAAE,CAAC;SACqC,CAAA;QAExF,MAAM,QAAQ,GAAG,IAAI,0BAA0B,CAC7C,IAAI,CAAC,MAAM,CAAC,YAAY,EACxB,OAAO,EACP,EAAE,UAAU,EAAE,IAAI,CAAC,UAAU,EAAE,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,CAC/D,CAAA;QAED,wEAAwE;QACxE,qEAAqE;QACrE,qDAAqD;QACrD,IAAI,CAAC,gBAAgB,GAAG,CAAC,KAAK,IAAI,EAAE;YAClC,IAAI,CAAC;gBACH,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAA;gBACrB,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAA;gBACtB,IAAI,CAAC,gBAAgB,GAAG,QAAQ,CAAA;gBAChC,OAAO,QAAQ,CAAA;YACjB,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;gBAC7C,MAAM,GAAG,CAAA;YACX,CAAC;oBAAS,CAAC;gBACT,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAA;YACnC,CAAC;QACH,CAAC,CAAC,EAAE,CAAA;QACJ,OAAO,IAAI,CAAC,gBAAgB,CAAA;IAC9B,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAA;QACpD,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,gBAAgB;YAAE,OAAM;QAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAA;QACtC,IAAI,CAAC,gBAAgB,GAAG,SAAS,CAAA;QACjC,MAAM,QAAQ,CAAC,KAAK,EAAE,CAAA;IACxB,CAAC;IAED;;OAEG;IACH,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,gBAAgB,EAAE,cAAc,CAAA;IAC9C,CAAC;IAED;;OAEG;IACH,IAAI,eAAe;QACjB,OAAO,IAAI,CAAC,gBAAgB,EAAE,qBAAqB,CAAA;IACrD,CAAC;IAED;;OAEG;IACH,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,gBAAgB,EAAE,cAAc,CAAA;IAC9C,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { SNSDependencies, SNSSQSConsumerDependencies } from '@message-queue-toolkit/sns';
|
|
2
|
+
import type { ConsumerErrorHandler, PublisherErrorHandler } from 'layered-loader';
|
|
3
|
+
import { SqsNotificationConsumer, type SqsNotificationConsumerConfig, type SqsSubscriptionOptions } from './SqsNotificationConsumer.js';
|
|
4
|
+
import { SqsNotificationPublisher, type SqsNotificationPublisherConfig } from './SqsNotificationPublisher.js';
|
|
5
|
+
export type SqsNotificationConfig = {
|
|
6
|
+
/**
|
|
7
|
+
* Logical channel name used for error reporting.
|
|
8
|
+
* Defaults to the topic name/ARN derived from the publisher config.
|
|
9
|
+
*/
|
|
10
|
+
channel?: string;
|
|
11
|
+
/**
|
|
12
|
+
* Stable identifier for this process. When omitted, a random UUID is generated.
|
|
13
|
+
* Messages whose `originUuid` matches this value are treated as self-emitted and skipped.
|
|
14
|
+
*/
|
|
15
|
+
serverUuid?: string;
|
|
16
|
+
publisherErrorHandler?: PublisherErrorHandler;
|
|
17
|
+
consumerErrorHandler?: ConsumerErrorHandler;
|
|
18
|
+
publisher: {
|
|
19
|
+
dependencies: SNSDependencies;
|
|
20
|
+
} & SqsNotificationPublisherConfig;
|
|
21
|
+
consumer: {
|
|
22
|
+
dependencies: SNSSQSConsumerDependencies;
|
|
23
|
+
subscriptionConfig?: SqsSubscriptionOptions;
|
|
24
|
+
} & SqsNotificationConsumerConfig;
|
|
25
|
+
};
|
|
26
|
+
export declare function createNotificationPair<LoadedValue>(config: SqsNotificationConfig): {
|
|
27
|
+
publisher: SqsNotificationPublisher<LoadedValue>;
|
|
28
|
+
consumer: SqsNotificationConsumer<LoadedValue>;
|
|
29
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { SqsNotificationConsumer, } from './SqsNotificationConsumer.js';
|
|
3
|
+
import { SqsNotificationPublisher, } from './SqsNotificationPublisher.js';
|
|
4
|
+
export function createNotificationPair(config) {
|
|
5
|
+
const serverUuid = config.serverUuid ?? randomUUID();
|
|
6
|
+
const publisherConfig = config.publisher;
|
|
7
|
+
const consumerConfig = config.consumer;
|
|
8
|
+
const publisher = new SqsNotificationPublisher(publisherConfig.creationConfig
|
|
9
|
+
? {
|
|
10
|
+
serverUuid,
|
|
11
|
+
channel: config.channel,
|
|
12
|
+
errorHandler: config.publisherErrorHandler,
|
|
13
|
+
dependencies: publisherConfig.dependencies,
|
|
14
|
+
creationConfig: publisherConfig.creationConfig,
|
|
15
|
+
}
|
|
16
|
+
: {
|
|
17
|
+
serverUuid,
|
|
18
|
+
channel: config.channel,
|
|
19
|
+
errorHandler: config.publisherErrorHandler,
|
|
20
|
+
dependencies: publisherConfig.dependencies,
|
|
21
|
+
locatorConfig: publisherConfig.locatorConfig,
|
|
22
|
+
});
|
|
23
|
+
const consumer = new SqsNotificationConsumer(consumerConfig.creationConfig
|
|
24
|
+
? {
|
|
25
|
+
serverUuid,
|
|
26
|
+
errorHandler: config.consumerErrorHandler,
|
|
27
|
+
dependencies: consumerConfig.dependencies,
|
|
28
|
+
subscriptionConfig: consumerConfig.subscriptionConfig,
|
|
29
|
+
creationConfig: consumerConfig.creationConfig,
|
|
30
|
+
}
|
|
31
|
+
: {
|
|
32
|
+
serverUuid,
|
|
33
|
+
errorHandler: config.consumerErrorHandler,
|
|
34
|
+
dependencies: consumerConfig.dependencies,
|
|
35
|
+
subscriptionConfig: consumerConfig.subscriptionConfig,
|
|
36
|
+
locatorConfig: consumerConfig.locatorConfig,
|
|
37
|
+
});
|
|
38
|
+
return { publisher, consumer };
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=SqsNotificationFactory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SqsNotificationFactory.js","sourceRoot":"","sources":["../../lib/SqsNotificationFactory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAGxC,OAAO,EACL,uBAAuB,GAGxB,MAAM,8BAA8B,CAAA;AACrC,OAAO,EACL,wBAAwB,GAEzB,MAAM,+BAA+B,CAAA;AAwBtC,MAAM,UAAU,sBAAsB,CAAc,MAA6B;IAI/E,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,IAAI,UAAU,EAAE,CAAA;IAEpD,MAAM,eAAe,GAAG,MAAM,CAAC,SAAS,CAAA;IACxC,MAAM,cAAc,GAAG,MAAM,CAAC,QAAQ,CAAA;IAEtC,MAAM,SAAS,GAAG,IAAI,wBAAwB,CAC5C,eAAe,CAAC,cAAc;QAC5B,CAAC,CAAC;YACE,UAAU;YACV,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,YAAY,EAAE,MAAM,CAAC,qBAAqB;YAC1C,YAAY,EAAE,eAAe,CAAC,YAAY;YAC1C,cAAc,EAAE,eAAe,CAAC,cAAc;SAC/C;QACH,CAAC,CAAC;YACE,UAAU;YACV,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,YAAY,EAAE,MAAM,CAAC,qBAAqB;YAC1C,YAAY,EAAE,eAAe,CAAC,YAAY;YAC1C,aAAa,EAAE,eAAe,CAAC,aAAa;SAC7C,CACN,CAAA;IAED,MAAM,QAAQ,GAAG,IAAI,uBAAuB,CAC1C,cAAc,CAAC,cAAc;QAC3B,CAAC,CAAC;YACE,UAAU;YACV,YAAY,EAAE,MAAM,CAAC,oBAAoB;YACzC,YAAY,EAAE,cAAc,CAAC,YAAY;YACzC,kBAAkB,EAAE,cAAc,CAAC,kBAAkB;YACrD,cAAc,EAAE,cAAc,CAAC,cAAc;SAC9C;QACH,CAAC,CAAC;YACE,UAAU;YACV,YAAY,EAAE,MAAM,CAAC,oBAAoB;YACzC,YAAY,EAAE,cAAc,CAAC,YAAY;YACzC,kBAAkB,EAAE,cAAc,CAAC,kBAAkB;YACrD,aAAa,EAAE,cAAc,CAAC,aAAa;SAC5C,CACN,CAAA;IAED,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAA;AAChC,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { type SNSCreationConfig, type SNSDependencies, type SNSTopicLocatorType } from '@message-queue-toolkit/sns';
|
|
2
|
+
import type { NotificationPublisher, PublisherErrorHandler } from 'layered-loader';
|
|
3
|
+
export type SqsNotificationPublisherConfig = {
|
|
4
|
+
creationConfig: SNSCreationConfig;
|
|
5
|
+
locatorConfig?: never;
|
|
6
|
+
} | {
|
|
7
|
+
creationConfig?: never;
|
|
8
|
+
locatorConfig: SNSTopicLocatorType;
|
|
9
|
+
};
|
|
10
|
+
export type SqsNotificationPublisherParams = {
|
|
11
|
+
serverUuid: string;
|
|
12
|
+
errorHandler?: PublisherErrorHandler;
|
|
13
|
+
channel?: string;
|
|
14
|
+
dependencies: SNSDependencies;
|
|
15
|
+
} & SqsNotificationPublisherConfig;
|
|
16
|
+
export declare class SqsNotificationPublisher<LoadedValue> implements NotificationPublisher<LoadedValue> {
|
|
17
|
+
readonly channel: string;
|
|
18
|
+
readonly errorHandler: PublisherErrorHandler;
|
|
19
|
+
private readonly serverUuid;
|
|
20
|
+
private readonly publisher;
|
|
21
|
+
private initPromise?;
|
|
22
|
+
private initialized;
|
|
23
|
+
constructor(params: SqsNotificationPublisherParams);
|
|
24
|
+
subscribe(): Promise<void>;
|
|
25
|
+
private initializePublisher;
|
|
26
|
+
set(key: string, value: LoadedValue | null): Promise<unknown>;
|
|
27
|
+
delete(key: string): Promise<unknown>;
|
|
28
|
+
deleteMany(keys: string[]): Promise<unknown>;
|
|
29
|
+
clear(): Promise<unknown>;
|
|
30
|
+
close(): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Returns the resolved topic ARN. Available only after `subscribe()` (or the
|
|
33
|
+
* first publish) has fully completed; returns `undefined` while init is in
|
|
34
|
+
* flight or before it has been triggered.
|
|
35
|
+
*/
|
|
36
|
+
get topicArn(): string | undefined;
|
|
37
|
+
private publishCommand;
|
|
38
|
+
private buildEnvelope;
|
|
39
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { AbstractSnsPublisher, } from '@message-queue-toolkit/sns';
|
|
3
|
+
import { resolveChannelName } from './channelNameResolver.js';
|
|
4
|
+
import { CLEAR_COMMAND, DELETE_COMMAND, DELETE_MANY_COMMAND, NOTIFICATION_ID_FIELD, NOTIFICATION_SCHEMAS, NOTIFICATION_TIMESTAMP_FIELD, NOTIFICATION_TYPE_FIELD, SET_COMMAND, } from './notificationSchemas.js';
|
|
5
|
+
const DEFAULT_PUBLISHER_ERROR_HANDLER = (err, channel, logger) => {
|
|
6
|
+
logger.error(`Error while publishing notification to channel ${channel}: ${err.message}`);
|
|
7
|
+
};
|
|
8
|
+
class SnsInvalidationPublisher extends AbstractSnsPublisher {
|
|
9
|
+
get publicTopicArn() {
|
|
10
|
+
return this.topicArn;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export class SqsNotificationPublisher {
|
|
14
|
+
channel;
|
|
15
|
+
errorHandler;
|
|
16
|
+
serverUuid;
|
|
17
|
+
publisher;
|
|
18
|
+
initPromise;
|
|
19
|
+
initialized = false;
|
|
20
|
+
constructor(params) {
|
|
21
|
+
this.serverUuid = params.serverUuid;
|
|
22
|
+
this.errorHandler = params.errorHandler ?? DEFAULT_PUBLISHER_ERROR_HANDLER;
|
|
23
|
+
this.channel = resolveChannelName(params);
|
|
24
|
+
// mqt's `messageSchemas` is `readonly ZodSchema<UnionType>[]`. Our schemas
|
|
25
|
+
// are narrower (one per command type) and `ZodSchema` is invariant in T,
|
|
26
|
+
// so the array of narrow schemas does not satisfy the broad type at the
|
|
27
|
+
// type level — even though mqt resolves them correctly at runtime via the
|
|
28
|
+
// configured `messageTypeResolver`.
|
|
29
|
+
const options = {
|
|
30
|
+
messageSchemas: NOTIFICATION_SCHEMAS,
|
|
31
|
+
messageTypeResolver: { messageTypePath: NOTIFICATION_TYPE_FIELD },
|
|
32
|
+
messageIdField: NOTIFICATION_ID_FIELD,
|
|
33
|
+
messageTimestampField: NOTIFICATION_TIMESTAMP_FIELD,
|
|
34
|
+
...(params.creationConfig
|
|
35
|
+
? { creationConfig: params.creationConfig }
|
|
36
|
+
: { locatorConfig: params.locatorConfig }),
|
|
37
|
+
};
|
|
38
|
+
this.publisher = new SnsInvalidationPublisher(params.dependencies, options);
|
|
39
|
+
}
|
|
40
|
+
async subscribe() {
|
|
41
|
+
if (this.initialized)
|
|
42
|
+
return;
|
|
43
|
+
if (!this.initPromise) {
|
|
44
|
+
this.initPromise = this.initializePublisher();
|
|
45
|
+
}
|
|
46
|
+
return this.initPromise;
|
|
47
|
+
}
|
|
48
|
+
async initializePublisher() {
|
|
49
|
+
try {
|
|
50
|
+
await this.publisher.init();
|
|
51
|
+
this.initialized = true;
|
|
52
|
+
}
|
|
53
|
+
catch (err) {
|
|
54
|
+
this.initPromise = undefined;
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
set(key, value) {
|
|
59
|
+
return this.publishCommand({
|
|
60
|
+
type: SET_COMMAND,
|
|
61
|
+
key,
|
|
62
|
+
value,
|
|
63
|
+
...this.buildEnvelope(),
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
delete(key) {
|
|
67
|
+
return this.publishCommand({
|
|
68
|
+
type: DELETE_COMMAND,
|
|
69
|
+
key,
|
|
70
|
+
...this.buildEnvelope(),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
deleteMany(keys) {
|
|
74
|
+
return this.publishCommand({
|
|
75
|
+
type: DELETE_MANY_COMMAND,
|
|
76
|
+
keys,
|
|
77
|
+
...this.buildEnvelope(),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
clear() {
|
|
81
|
+
return this.publishCommand({
|
|
82
|
+
type: CLEAR_COMMAND,
|
|
83
|
+
...this.buildEnvelope(),
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
async close() {
|
|
87
|
+
await this.publisher.close();
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Returns the resolved topic ARN. Available only after `subscribe()` (or the
|
|
91
|
+
* first publish) has fully completed; returns `undefined` while init is in
|
|
92
|
+
* flight or before it has been triggered.
|
|
93
|
+
*/
|
|
94
|
+
get topicArn() {
|
|
95
|
+
return this.initialized ? this.publisher.publicTopicArn : undefined;
|
|
96
|
+
}
|
|
97
|
+
async publishCommand(command) {
|
|
98
|
+
await this.subscribe();
|
|
99
|
+
await this.publisher.publish(command);
|
|
100
|
+
}
|
|
101
|
+
buildEnvelope() {
|
|
102
|
+
return {
|
|
103
|
+
id: randomUUID(),
|
|
104
|
+
timestamp: new Date().toISOString(),
|
|
105
|
+
originUuid: this.serverUuid,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=SqsNotificationPublisher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SqsNotificationPublisher.js","sourceRoot":"","sources":["../../lib/SqsNotificationPublisher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { SNSCreationConfig, SNSTopicLocatorType } from '@message-queue-toolkit/sns';
|
|
2
|
+
export type ChannelNameSource = {
|
|
3
|
+
channel?: string;
|
|
4
|
+
creationConfig?: SNSCreationConfig;
|
|
5
|
+
locatorConfig?: SNSTopicLocatorType;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Derive a logical channel name (used in error messages and logs) from a
|
|
9
|
+
* publisher params object that has either an explicit `channel`, a
|
|
10
|
+
* `creationConfig.topic.Name`, or a `locatorConfig` with `topicName`/`topicArn`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function resolveChannelName(source: ChannelNameSource): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const DEFAULT_CHANNEL_NAME = 'sqs-notification-channel';
|
|
2
|
+
/**
|
|
3
|
+
* Derive a logical channel name (used in error messages and logs) from a
|
|
4
|
+
* publisher params object that has either an explicit `channel`, a
|
|
5
|
+
* `creationConfig.topic.Name`, or a `locatorConfig` with `topicName`/`topicArn`.
|
|
6
|
+
*/
|
|
7
|
+
export function resolveChannelName(source) {
|
|
8
|
+
if (source.channel)
|
|
9
|
+
return source.channel;
|
|
10
|
+
if (source.creationConfig?.topic?.Name)
|
|
11
|
+
return source.creationConfig.topic.Name;
|
|
12
|
+
if (source.locatorConfig?.topicName)
|
|
13
|
+
return source.locatorConfig.topicName;
|
|
14
|
+
if (source.locatorConfig?.topicArn)
|
|
15
|
+
return source.locatorConfig.topicArn;
|
|
16
|
+
return DEFAULT_CHANNEL_NAME;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=channelNameResolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"channelNameResolver.js","sourceRoot":"","sources":["../../lib/channelNameResolver.ts"],"names":[],"mappings":"AAQA,MAAM,oBAAoB,GAAG,0BAA0B,CAAA;AAEvD;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAyB;IAC1D,IAAI,MAAM,CAAC,OAAO;QAAE,OAAO,MAAM,CAAC,OAAO,CAAA;IACzC,IAAI,MAAM,CAAC,cAAc,EAAE,KAAK,EAAE,IAAI;QAAE,OAAO,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,IAAI,CAAA;IAC/E,IAAI,MAAM,CAAC,aAAa,EAAE,SAAS;QAAE,OAAO,MAAM,CAAC,aAAa,CAAC,SAAS,CAAA;IAC1E,IAAI,MAAM,CAAC,aAAa,EAAE,QAAQ;QAAE,OAAO,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAA;IACxE,OAAO,oBAAoB,CAAA;AAC7B,CAAC"}
|