@geekmidas/events 10.0.0-alpha.39 → 10.0.0-alpha.40

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.
@@ -36,6 +36,159 @@ export declare class SNSConnection implements EventConnection {
36
36
  get topicArn(): string;
37
37
  }
38
38
  //#endregion
39
+ //#region src/sns/push.d.ts
40
+ /**
41
+ * SNS delivering to an HTTP(S) endpoint, rather than to a queue that is polled.
42
+ *
43
+ * SNS POSTs each message to the subscribed URL as JSON. That is the whole
44
+ * transport: no queue in between, no poller, and the endpoint is handed the
45
+ * same envelope a Lambda subscription receives inside `Records[].Sns` — so the
46
+ * handler that runs deployed can be the one that runs here.
47
+ *
48
+ * The endpoint is public, so a message is trusted only once its signature
49
+ * checks out against a certificate SNS itself serves. Anything else that POSTs
50
+ * there is somebody's guess at the shape.
51
+ */
52
+ /** A message SNS POSTs to a subscribed HTTP endpoint. */
53
+ interface SnsHttpMessage {
54
+ Type: 'Notification' | 'SubscriptionConfirmation' | 'UnsubscribeConfirmation';
55
+ MessageId: string;
56
+ TopicArn: string;
57
+ Message: string;
58
+ Timestamp: string;
59
+ SignatureVersion: string;
60
+ Signature: string;
61
+ SigningCertURL: string;
62
+ Subject?: string;
63
+ /** Confirmation messages only. */
64
+ Token?: string;
65
+ /** Confirmation messages only: GET it to confirm. */
66
+ SubscribeURL?: string;
67
+ /** Notifications only. */
68
+ UnsubscribeURL?: string;
69
+ MessageAttributes?: Record<string, {
70
+ Type: string;
71
+ Value: string;
72
+ }>;
73
+ }
74
+ interface VerifySnsMessageOptions {
75
+ /**
76
+ * Fetch the PEM at a certificate URL. `fetch` by default; replaceable so a
77
+ * test signs with its own key instead of SNS's.
78
+ */
79
+ fetchCertificate?: (url: string) => Promise<string>;
80
+ }
81
+ /** The string SNS signed for this message. */
82
+ export declare function snsStringToSign(message: SnsHttpMessage): string;
83
+ /**
84
+ * Check that SNS sent this message: the certificate is SNS's own, and the
85
+ * signature over the message's signed fields verifies against it.
86
+ *
87
+ * Throws rather than returning false, so a caller cannot forget to look.
88
+ */
89
+ export declare function verifySnsMessage(message: SnsHttpMessage, options?: VerifySnsMessageOptions): Promise<void>;
90
+ /**
91
+ * Confirm a subscription by visiting the URL SNS sent. Until this happens SNS
92
+ * delivers nothing to the endpoint.
93
+ */
94
+ export declare function confirmSnsSubscription(message: SnsHttpMessage, request?: (url: string) => Promise<{
95
+ ok: boolean;
96
+ status: number;
97
+ }>): Promise<void>;
98
+ interface SubscribeHttpEndpointOptions {
99
+ /** The URL SNS POSTs to — `http:` or `https:`, which picks the protocol. */
100
+ endpoint: string;
101
+ /**
102
+ * The event types this endpoint takes. Becomes the subscription's filter
103
+ * policy on the `type` attribute `SNSPublisher` sets, so SNS sends nothing
104
+ * else — fan-out to every subscriber, each seeing only what it named.
105
+ */
106
+ events: readonly string[];
107
+ /**
108
+ * An SQS queue's ARN for what SNS gives up delivering. SNS retries an HTTP
109
+ * endpoint for a limited time and then drops the message, so without one an
110
+ * outage longer than that loses events.
111
+ */
112
+ deadLetterQueueArn?: string;
113
+ }
114
+ /**
115
+ * Subscribe an HTTP endpoint to the connection's topic, converging on one
116
+ * confirmed subscription with the filter asked for — so it is safe on every
117
+ * start:
118
+ *
119
+ * - **none yet** — subscribed; SNS sends a confirmation to the endpoint.
120
+ * - **confirmed** — its filter and dead-letter queue are brought up to date.
121
+ * Subscribing again with different attributes is an error on SNS, and the
122
+ * events a subscriber names change.
123
+ * - **stuck pending** — a confirmation that never landed (the endpoint was
124
+ * down, or answered wrong). Subscribing the same endpoint again is a no-op
125
+ * on an emulator, so it is dropped and subscribed afresh, which sends a new
126
+ * confirmation.
127
+ */
128
+ export declare function subscribeHttpEndpoint(connection: SNSConnection, options: SubscribeHttpEndpointOptions): Promise<string | undefined>;
129
+ /**
130
+ * A pushed notification as the Lambda event SNS would have invoked a function
131
+ * with, so it can be handed to the same adaptor.
132
+ */
133
+ export declare function toSnsEvent(message: SnsHttpMessage, subscriptionArn?: string): {
134
+ Records: {
135
+ EventSource: string;
136
+ EventVersion: string;
137
+ EventSubscriptionArn: string;
138
+ Sns: {
139
+ Type: "Notification" | "SubscriptionConfirmation" | "UnsubscribeConfirmation";
140
+ MessageId: string;
141
+ TopicArn: string;
142
+ Subject: string | null;
143
+ Message: string;
144
+ Timestamp: string;
145
+ SignatureVersion: string;
146
+ Signature: string;
147
+ SigningCertUrl: string;
148
+ UnsubscribeUrl: string;
149
+ MessageAttributes: Record<string, {
150
+ Type: string;
151
+ Value: string;
152
+ }>;
153
+ };
154
+ }[];
155
+ };
156
+ export declare class SnsCertificateUntrusted extends Error {
157
+ readonly url: string;
158
+ constructor(url: string);
159
+ }
160
+ export declare class SnsCertificateUnavailable extends Error {
161
+ readonly url: string;
162
+ readonly status: number;
163
+ constructor(url: string, status: number);
164
+ }
165
+ export declare class SnsSignatureVersionUnknown extends Error {
166
+ readonly version: string;
167
+ constructor(version: string);
168
+ }
169
+ export declare class SnsSignatureInvalid extends Error {
170
+ readonly messageId: string;
171
+ readonly topicArn: string;
172
+ constructor(messageId: string, topicArn: string);
173
+ }
174
+ export declare class SnsMessageTypeUnknown extends Error {
175
+ readonly type: string;
176
+ constructor(type: string);
177
+ }
178
+ export declare class SnsNotAConfirmation extends Error {
179
+ readonly type: string;
180
+ constructor(type: string);
181
+ }
182
+ export declare class SnsConfirmationFailed extends Error {
183
+ readonly topicArn: string;
184
+ readonly status: number;
185
+ constructor(topicArn: string, status: number);
186
+ }
187
+ export declare class SnsEndpointNotHttp extends Error {
188
+ readonly endpoint: string;
189
+ constructor(endpoint: string);
190
+ }
191
+ //#endregion
39
192
  //#region src/sns/SNSPublisher.d.ts
40
193
  type SNSPublisherOptions = {};
41
194
  export declare class SNSPublisher<TMessage extends PublishableMessage<string, any>> implements EventPublisher<TMessage> {
@@ -108,5 +261,5 @@ export declare class SnsQueueMissing extends Error {
108
261
  constructor(queueName: string | undefined);
109
262
  }
110
263
  //#endregion
111
- export { type SNSConnectionConfig, type SNSPublisherOptions, type SNSSubscriberOptions, type SnsAddress, snsUrl_d_exports as snsUrl };
264
+ export { type SNSConnectionConfig, type SNSPublisherOptions, type SNSSubscriberOptions, type SnsAddress, type SnsHttpMessage, type SubscribeHttpEndpointOptions, type VerifySnsMessageOptions, snsUrl_d_exports as snsUrl };
112
265
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/sns/SNSConnection.ts","../../src/sns/SNSPublisher.ts","../../src/sns/SNSSubscriber.ts"],"mappings":";;;;UAKiB;EAChB;EACA;EACA;EACA;IACC;IACA;IACA;;;qBAIW,yBAAyB;WAC5B,OAAI,mBAAA;UACL;UACA;UACA;EAER,YAAY,QAAQ;;;;;SAgBP,qBACZ,2BACE,QAAQ;EA8BL,WAAW;EAKX,SAAS;EAKf;;;;MAOI,aAAa;;;;MAOb;;;;KC1FO;qBAEC,aAAa,iBAAiB,4CAC/B,eAAe;UAGjB;EADT,YACS,YAAY,eACpB,WAAU;;;;;SAOE,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,aAAa;EAQ5C,QAAQ,UAAU,aAAa;UAWvB;EAkBR,SAAS;;;;UC5CC;EAChB;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;;qBAoBY,cAAc,iBAAiB,4CAChC,gBAAgB;UAUlB;UARD;UACA;UACA;UACA;UACA;UACA;EAER,YACS,YAAY,eACpB,UAAS;;;;;SAwBG,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,cAAc;EAoB7C,UACL,UAAU,oBACV,WAAW,SAAS,aAAa,gBAC/B;;;;EA2BG,QAAQ;UA6BA;UAwBA;UA4BA;;;;;;qBAgBF,wBAAwB;WACf;EAArB,YAAqB"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../../src/sns/SNSConnection.ts","../../src/sns/push.ts","../../src/sns/SNSPublisher.ts","../../src/sns/SNSSubscriber.ts"],"mappings":";;;;UAKiB;EAChB;EACA;EACA;EACA;IACC;IACA;IACA;;;qBAIW,yBAAyB;WAC5B,OAAI,mBAAA;UACL;UACA;UACA;EAER,YAAY,QAAQ;;;;;SAgBP,qBACZ,2BACE,QAAQ;EA8BL,WAAW;EAKX,SAAS;EAKf;;;;MAOI,aAAa;;;;MAOb;;;;;;;;;;;;;;;;;UCrEY;EAChB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAEA;;EAEA;;EAEA;EACA,oBAAoB;IAAiB;IAAc;;;UAwCnC;;;;;EAKhB,oBAAoB,gBAAgB;;;wBAwBrB,gBAAgB,SAAS;;;;;;;wBAgBnB,iBACrB,SAAS,gBACT,UAAS,0BACP;;;;;wBAsCmB,uBACrB,SAAS,gBACT,WAAU,gBAAgB;EAAU;EAAa;KAC/C;UAWc;;EAEhB;;;;;;EAMA;;;;;;EAMA;;;;;;;;;;;;;;;;wBAiBqB,sBACrB,YAAY,eACZ,SAAS,+BACP;;;;;wBAiFa,WAAW,SAAS,gBAAgB;;;;;;MAQ/C;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MAEA;MACA;MACA,mBAAiB;QAlRsB;QAAe;;;;;qBAyR9C,gCAAgC;WACvB;EAArB,YAAqB;;qBAUT,kCAAkC;WAEpC;WACA;EAFV,YACU,aACA;;qBAUE,mCAAmC;WAC1B;EAArB,YAAqB;;qBAST,4BAA4B;WAE9B;WACA;EAFV,YACU,mBACA;;qBAWE,8BAA8B;WACrB;EAArB,YAAqB;;qBAST,4BAA4B;WACnB;EAArB,YAAqB;;qBAST,8BAA8B;WAEhC;WACA;EAFV,YACU,kBACA;;qBAWE,2BAA2B;WAClB;EAArB,YAAqB;;;;KCjZV;qBAEC,aAAa,iBAAiB,4CAC/B,eAAe;UAGjB;EADT,YACS,YAAY,eACpB,WAAU;;;;;SAOE,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,aAAa;EAQ5C,QAAQ,UAAU,aAAa;UAWvB;EAkBR,SAAS;;;;UC5CC;EAChB;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;;qBAoBY,cAAc,iBAAiB,4CAChC,gBAAgB;UAUlB;UARD;UACA;UACA;UACA;UACA;UACA;EAER,YACS,YAAY,eACpB,UAAS;;;;;SAwBG,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,cAAc;EAoB7C,UACL,UAAU,oBACV,WAAW,SAAS,aAAa,gBAC/B;;;;EA2BG,QAAQ;UA6BA;UAwBA;UA4BA;;;;;;qBAgBF,wBAAwB;WACf;EAArB,YAAqB"}
@@ -36,6 +36,159 @@ export declare class SNSConnection implements EventConnection {
36
36
  get topicArn(): string;
37
37
  }
38
38
  //#endregion
39
+ //#region src/sns/push.d.ts
40
+ /**
41
+ * SNS delivering to an HTTP(S) endpoint, rather than to a queue that is polled.
42
+ *
43
+ * SNS POSTs each message to the subscribed URL as JSON. That is the whole
44
+ * transport: no queue in between, no poller, and the endpoint is handed the
45
+ * same envelope a Lambda subscription receives inside `Records[].Sns` — so the
46
+ * handler that runs deployed can be the one that runs here.
47
+ *
48
+ * The endpoint is public, so a message is trusted only once its signature
49
+ * checks out against a certificate SNS itself serves. Anything else that POSTs
50
+ * there is somebody's guess at the shape.
51
+ */
52
+ /** A message SNS POSTs to a subscribed HTTP endpoint. */
53
+ interface SnsHttpMessage {
54
+ Type: 'Notification' | 'SubscriptionConfirmation' | 'UnsubscribeConfirmation';
55
+ MessageId: string;
56
+ TopicArn: string;
57
+ Message: string;
58
+ Timestamp: string;
59
+ SignatureVersion: string;
60
+ Signature: string;
61
+ SigningCertURL: string;
62
+ Subject?: string;
63
+ /** Confirmation messages only. */
64
+ Token?: string;
65
+ /** Confirmation messages only: GET it to confirm. */
66
+ SubscribeURL?: string;
67
+ /** Notifications only. */
68
+ UnsubscribeURL?: string;
69
+ MessageAttributes?: Record<string, {
70
+ Type: string;
71
+ Value: string;
72
+ }>;
73
+ }
74
+ interface VerifySnsMessageOptions {
75
+ /**
76
+ * Fetch the PEM at a certificate URL. `fetch` by default; replaceable so a
77
+ * test signs with its own key instead of SNS's.
78
+ */
79
+ fetchCertificate?: (url: string) => Promise<string>;
80
+ }
81
+ /** The string SNS signed for this message. */
82
+ export declare function snsStringToSign(message: SnsHttpMessage): string;
83
+ /**
84
+ * Check that SNS sent this message: the certificate is SNS's own, and the
85
+ * signature over the message's signed fields verifies against it.
86
+ *
87
+ * Throws rather than returning false, so a caller cannot forget to look.
88
+ */
89
+ export declare function verifySnsMessage(message: SnsHttpMessage, options?: VerifySnsMessageOptions): Promise<void>;
90
+ /**
91
+ * Confirm a subscription by visiting the URL SNS sent. Until this happens SNS
92
+ * delivers nothing to the endpoint.
93
+ */
94
+ export declare function confirmSnsSubscription(message: SnsHttpMessage, request?: (url: string) => Promise<{
95
+ ok: boolean;
96
+ status: number;
97
+ }>): Promise<void>;
98
+ interface SubscribeHttpEndpointOptions {
99
+ /** The URL SNS POSTs to — `http:` or `https:`, which picks the protocol. */
100
+ endpoint: string;
101
+ /**
102
+ * The event types this endpoint takes. Becomes the subscription's filter
103
+ * policy on the `type` attribute `SNSPublisher` sets, so SNS sends nothing
104
+ * else — fan-out to every subscriber, each seeing only what it named.
105
+ */
106
+ events: readonly string[];
107
+ /**
108
+ * An SQS queue's ARN for what SNS gives up delivering. SNS retries an HTTP
109
+ * endpoint for a limited time and then drops the message, so without one an
110
+ * outage longer than that loses events.
111
+ */
112
+ deadLetterQueueArn?: string;
113
+ }
114
+ /**
115
+ * Subscribe an HTTP endpoint to the connection's topic, converging on one
116
+ * confirmed subscription with the filter asked for — so it is safe on every
117
+ * start:
118
+ *
119
+ * - **none yet** — subscribed; SNS sends a confirmation to the endpoint.
120
+ * - **confirmed** — its filter and dead-letter queue are brought up to date.
121
+ * Subscribing again with different attributes is an error on SNS, and the
122
+ * events a subscriber names change.
123
+ * - **stuck pending** — a confirmation that never landed (the endpoint was
124
+ * down, or answered wrong). Subscribing the same endpoint again is a no-op
125
+ * on an emulator, so it is dropped and subscribed afresh, which sends a new
126
+ * confirmation.
127
+ */
128
+ export declare function subscribeHttpEndpoint(connection: SNSConnection, options: SubscribeHttpEndpointOptions): Promise<string | undefined>;
129
+ /**
130
+ * A pushed notification as the Lambda event SNS would have invoked a function
131
+ * with, so it can be handed to the same adaptor.
132
+ */
133
+ export declare function toSnsEvent(message: SnsHttpMessage, subscriptionArn?: string): {
134
+ Records: {
135
+ EventSource: string;
136
+ EventVersion: string;
137
+ EventSubscriptionArn: string;
138
+ Sns: {
139
+ Type: "Notification" | "SubscriptionConfirmation" | "UnsubscribeConfirmation";
140
+ MessageId: string;
141
+ TopicArn: string;
142
+ Subject: string | null;
143
+ Message: string;
144
+ Timestamp: string;
145
+ SignatureVersion: string;
146
+ Signature: string;
147
+ SigningCertUrl: string;
148
+ UnsubscribeUrl: string;
149
+ MessageAttributes: Record<string, {
150
+ Type: string;
151
+ Value: string;
152
+ }>;
153
+ };
154
+ }[];
155
+ };
156
+ export declare class SnsCertificateUntrusted extends Error {
157
+ readonly url: string;
158
+ constructor(url: string);
159
+ }
160
+ export declare class SnsCertificateUnavailable extends Error {
161
+ readonly url: string;
162
+ readonly status: number;
163
+ constructor(url: string, status: number);
164
+ }
165
+ export declare class SnsSignatureVersionUnknown extends Error {
166
+ readonly version: string;
167
+ constructor(version: string);
168
+ }
169
+ export declare class SnsSignatureInvalid extends Error {
170
+ readonly messageId: string;
171
+ readonly topicArn: string;
172
+ constructor(messageId: string, topicArn: string);
173
+ }
174
+ export declare class SnsMessageTypeUnknown extends Error {
175
+ readonly type: string;
176
+ constructor(type: string);
177
+ }
178
+ export declare class SnsNotAConfirmation extends Error {
179
+ readonly type: string;
180
+ constructor(type: string);
181
+ }
182
+ export declare class SnsConfirmationFailed extends Error {
183
+ readonly topicArn: string;
184
+ readonly status: number;
185
+ constructor(topicArn: string, status: number);
186
+ }
187
+ export declare class SnsEndpointNotHttp extends Error {
188
+ readonly endpoint: string;
189
+ constructor(endpoint: string);
190
+ }
191
+ //#endregion
39
192
  //#region src/sns/SNSPublisher.d.ts
40
193
  type SNSPublisherOptions = {};
41
194
  export declare class SNSPublisher<TMessage extends PublishableMessage<string, any>> implements EventPublisher<TMessage> {
@@ -108,5 +261,5 @@ export declare class SnsQueueMissing extends Error {
108
261
  constructor(queueName: string | undefined);
109
262
  }
110
263
  //#endregion
111
- export { type SNSConnectionConfig, type SNSPublisherOptions, type SNSSubscriberOptions, type SnsAddress, snsUrl_d_exports as snsUrl };
264
+ export { type SNSConnectionConfig, type SNSPublisherOptions, type SNSSubscriberOptions, type SnsAddress, type SnsHttpMessage, type SubscribeHttpEndpointOptions, type VerifySnsMessageOptions, snsUrl_d_exports as snsUrl };
112
265
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/sns/SNSConnection.ts","../../src/sns/SNSPublisher.ts","../../src/sns/SNSSubscriber.ts"],"mappings":";;;;UAKiB;EAChB;EACA;EACA;EACA;IACC;IACA;IACA;;;qBAIW,yBAAyB;WAC5B,OAAI,mBAAA;UACL;UACA;UACA;EAER,YAAY,QAAQ;;;;;SAgBP,qBACZ,2BACE,QAAQ;EA8BL,WAAW;EAKX,SAAS;EAKf;;;;MAOI,aAAa;;;;MAOb;;;;KC1FO;qBAEC,aAAa,iBAAiB,4CAC/B,eAAe;UAGjB;EADT,YACS,YAAY,eACpB,WAAU;;;;;SAOE,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,aAAa;EAQ5C,QAAQ,UAAU,aAAa;UAWvB;EAkBR,SAAS;;;;UC5CC;EAChB;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;;qBAoBY,cAAc,iBAAiB,4CAChC,gBAAgB;UAUlB;UARD;UACA;UACA;UACA;UACA;UACA;EAER,YACS,YAAY,eACpB,UAAS;;;;;SAwBG,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,cAAc;EAoB7C,UACL,UAAU,oBACV,WAAW,SAAS,aAAa,gBAC/B;;;;EA2BG,QAAQ;UA6BA;UAwBA;UA4BA;;;;;;qBAgBF,wBAAwB;WACf;EAArB,YAAqB"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../../src/sns/SNSConnection.ts","../../src/sns/push.ts","../../src/sns/SNSPublisher.ts","../../src/sns/SNSSubscriber.ts"],"mappings":";;;;UAKiB;EAChB;EACA;EACA;EACA;IACC;IACA;IACA;;;qBAIW,yBAAyB;WAC5B,OAAI,mBAAA;UACL;UACA;UACA;EAER,YAAY,QAAQ;;;;;SAgBP,qBACZ,2BACE,QAAQ;EA8BL,WAAW;EAKX,SAAS;EAKf;;;;MAOI,aAAa;;;;MAOb;;;;;;;;;;;;;;;;;UCrEY;EAChB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;EAEA;;EAEA;;EAEA;EACA,oBAAoB;IAAiB;IAAc;;;UAwCnC;;;;;EAKhB,oBAAoB,gBAAgB;;;wBAwBrB,gBAAgB,SAAS;;;;;;;wBAgBnB,iBACrB,SAAS,gBACT,UAAS,0BACP;;;;;wBAsCmB,uBACrB,SAAS,gBACT,WAAU,gBAAgB;EAAU;EAAa;KAC/C;UAWc;;EAEhB;;;;;;EAMA;;;;;;EAMA;;;;;;;;;;;;;;;;wBAiBqB,sBACrB,YAAY,eACZ,SAAS,+BACP;;;;;wBAiFa,WAAW,SAAS,gBAAgB;;;;;;MAQ/C;MACA;MACA;MACA;MACA;MACA;MACA;MACA;MAEA;MACA;MACA,mBAAiB;QAlRsB;QAAe;;;;;qBAyR9C,gCAAgC;WACvB;EAArB,YAAqB;;qBAUT,kCAAkC;WAEpC;WACA;EAFV,YACU,aACA;;qBAUE,mCAAmC;WAC1B;EAArB,YAAqB;;qBAST,4BAA4B;WAE9B;WACA;EAFV,YACU,mBACA;;qBAWE,8BAA8B;WACrB;EAArB,YAAqB;;qBAST,4BAA4B;WACnB;EAArB,YAAqB;;qBAST,8BAA8B;WAEhC;WACA;EAFV,YACU,kBACA;;qBAWE,2BAA2B;WAClB;EAArB,YAAqB;;;;KCjZV;qBAEC,aAAa,iBAAiB,4CAC/B,eAAe;UAGjB;EADT,YACS,YAAY,eACpB,WAAU;;;;;SAOE,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,aAAa;EAQ5C,QAAQ,UAAU,aAAa;UAWvB;EAkBR,SAAS;;;;UC5CC;EAChB;EACA;EACA;EACA;EACA;;;;;;;;;;;;;;;;;;;qBAoBY,cAAc,iBAAiB,4CAChC,gBAAgB;UAUlB;UARD;UACA;UACA;UACA;UACA;UACA;EAER,YACS,YAAY,eACpB,UAAS;;;;;SAwBG,qBACZ,iBAAiB,iCAChB,2BAA2B,QAAQ,cAAc;EAoB7C,UACL,UAAU,oBACV,WAAW,SAAS,aAAa,gBAC/B;;;;EA2BG,QAAQ;UA6BA;UAwBA;UA4BA;;;;;;qBAgBF,wBAAwB;WACf;EAArB,YAAqB"}
@@ -2,7 +2,259 @@ import "../types-Bk5JM6MP.mjs";
2
2
  import { n as SQSConnection, t as SQSSubscriber } from "../SQSSubscriber-BXLE_1L3.mjs";
3
3
  import { parse, t as snsUrl_exports } from "./snsUrl.mjs";
4
4
  import { CreateQueueCommand, DeleteQueueCommand, GetQueueAttributesCommand, SQSClient, SetQueueAttributesCommand } from "@aws-sdk/client-sqs";
5
- import { PublishCommand, SNSClient, SubscribeCommand, UnsubscribeCommand } from "@aws-sdk/client-sns";
5
+ import { createVerify } from "node:crypto";
6
+ import { GetSubscriptionAttributesCommand, ListSubscriptionsByTopicCommand, PublishCommand, SNSClient, SetSubscriptionAttributesCommand, SubscribeCommand, UnsubscribeCommand } from "@aws-sdk/client-sns";
7
+ //#region src/sns/push.ts
8
+ /** The fields signed, in the order SNS signs them, per message type. */
9
+ const SIGNED_FIELDS = {
10
+ Notification: [
11
+ "Message",
12
+ "MessageId",
13
+ "Subject",
14
+ "Timestamp",
15
+ "TopicArn",
16
+ "Type"
17
+ ],
18
+ SubscriptionConfirmation: [
19
+ "Message",
20
+ "MessageId",
21
+ "SubscribeURL",
22
+ "Timestamp",
23
+ "Token",
24
+ "TopicArn",
25
+ "Type"
26
+ ],
27
+ UnsubscribeConfirmation: [
28
+ "Message",
29
+ "MessageId",
30
+ "SubscribeURL",
31
+ "Timestamp",
32
+ "Token",
33
+ "TopicArn",
34
+ "Type"
35
+ ]
36
+ };
37
+ /**
38
+ * Where SNS serves its signing certificates. Anywhere else is a certificate
39
+ * the sender chose, which would let the sender sign its own forgeries.
40
+ */
41
+ const SNS_CERTIFICATE_HOST = /^sns\.[a-z0-9-]+\.amazonaws\.com(\.cn)?$/;
42
+ const certificates = /* @__PURE__ */ new Map();
43
+ async function defaultFetchCertificate(url) {
44
+ const response = await fetch(url);
45
+ if (!response.ok) throw new SnsCertificateUnavailable(url, response.status);
46
+ return response.text();
47
+ }
48
+ /**
49
+ * A URL, or undefined for a value that is not one at all — what an emulator
50
+ * sends — so it is untrusted rather than a crash. (`URL.parse` is Node 22.1+.)
51
+ */
52
+ function parseUrl(value) {
53
+ try {
54
+ return new URL(String(value));
55
+ } catch {
56
+ return;
57
+ }
58
+ }
59
+ /** The string SNS signed for this message. */
60
+ function snsStringToSign(message) {
61
+ const fields = SIGNED_FIELDS[message.Type];
62
+ if (!fields) throw new SnsMessageTypeUnknown(String(message.Type));
63
+ return fields.filter((field) => message[field] !== void 0).map((field) => `${field}\n${message[field]}\n`).join("");
64
+ }
65
+ /**
66
+ * Check that SNS sent this message: the certificate is SNS's own, and the
67
+ * signature over the message's signed fields verifies against it.
68
+ *
69
+ * Throws rather than returning false, so a caller cannot forget to look.
70
+ */
71
+ async function verifySnsMessage(message, options = {}) {
72
+ const url = parseUrl(message.SigningCertURL);
73
+ if (url?.protocol !== "https:" || !SNS_CERTIFICATE_HOST.test(url.hostname)) throw new SnsCertificateUntrusted(message.SigningCertURL);
74
+ const algorithm = message.SignatureVersion === "1" ? "RSA-SHA1" : message.SignatureVersion === "2" ? "RSA-SHA256" : void 0;
75
+ if (!algorithm) throw new SnsSignatureVersionUnknown(message.SignatureVersion);
76
+ const fetchCertificate = options.fetchCertificate ?? defaultFetchCertificate;
77
+ let certificate = certificates.get(message.SigningCertURL);
78
+ if (!certificate || options.fetchCertificate) {
79
+ certificate = fetchCertificate(message.SigningCertURL);
80
+ if (!options.fetchCertificate) {
81
+ certificates.set(message.SigningCertURL, certificate);
82
+ certificate.catch(() => certificates.delete(message.SigningCertURL));
83
+ }
84
+ }
85
+ const verifier = createVerify(algorithm);
86
+ verifier.update(snsStringToSign(message), "utf8");
87
+ if (!verifier.verify(await certificate, message.Signature, "base64")) throw new SnsSignatureInvalid(message.MessageId, message.TopicArn);
88
+ }
89
+ /**
90
+ * Confirm a subscription by visiting the URL SNS sent. Until this happens SNS
91
+ * delivers nothing to the endpoint.
92
+ */
93
+ async function confirmSnsSubscription(message, request = fetch) {
94
+ if (message.Type !== "SubscriptionConfirmation" || !message.SubscribeURL) throw new SnsNotAConfirmation(message.Type);
95
+ const response = await request(message.SubscribeURL);
96
+ if (!response.ok) throw new SnsConfirmationFailed(message.TopicArn, response.status);
97
+ }
98
+ /**
99
+ * Subscribe an HTTP endpoint to the connection's topic, converging on one
100
+ * confirmed subscription with the filter asked for — so it is safe on every
101
+ * start:
102
+ *
103
+ * - **none yet** — subscribed; SNS sends a confirmation to the endpoint.
104
+ * - **confirmed** — its filter and dead-letter queue are brought up to date.
105
+ * Subscribing again with different attributes is an error on SNS, and the
106
+ * events a subscriber names change.
107
+ * - **stuck pending** — a confirmation that never landed (the endpoint was
108
+ * down, or answered wrong). Subscribing the same endpoint again is a no-op
109
+ * on an emulator, so it is dropped and subscribed afresh, which sends a new
110
+ * confirmation.
111
+ */
112
+ async function subscribeHttpEndpoint(connection, options) {
113
+ const protocol = new URL(options.endpoint).protocol.replace(":", "");
114
+ if (protocol !== "http" && protocol !== "https") throw new SnsEndpointNotHttp(options.endpoint);
115
+ const attributes = {
116
+ FilterPolicyScope: "MessageAttributes",
117
+ FilterPolicy: JSON.stringify({ type: [...options.events] })
118
+ };
119
+ if (options.deadLetterQueueArn) attributes.RedrivePolicy = JSON.stringify({ deadLetterTargetArn: options.deadLetterQueueArn });
120
+ const client = connection.snsClient;
121
+ const existing = await existingSubscription(connection, options.endpoint);
122
+ if (existing) {
123
+ const { Attributes = {} } = await client.send(new GetSubscriptionAttributesCommand({ SubscriptionArn: existing }));
124
+ if (Attributes.PendingConfirmation === "true") await client.send(new UnsubscribeCommand({ SubscriptionArn: existing }));
125
+ else {
126
+ for (const [name, value] of Object.entries(attributes)) {
127
+ if (Attributes[name] === value) continue;
128
+ await client.send(new SetSubscriptionAttributesCommand({
129
+ SubscriptionArn: existing,
130
+ AttributeName: name,
131
+ AttributeValue: value
132
+ }));
133
+ }
134
+ return existing;
135
+ }
136
+ }
137
+ const { SubscriptionArn } = await client.send(new SubscribeCommand({
138
+ TopicArn: connection.topicArn,
139
+ Protocol: protocol,
140
+ Endpoint: options.endpoint,
141
+ Attributes: attributes,
142
+ ReturnSubscriptionArn: true
143
+ }));
144
+ return SubscriptionArn;
145
+ }
146
+ /**
147
+ * The ARN of the endpoint's subscription to the topic, if it has one SNS will
148
+ * name. (Real SNS lists a pending one as `PendingConfirmation`, with no ARN to
149
+ * act on — and resends its confirmation on a new Subscribe anyway.)
150
+ */
151
+ async function existingSubscription(connection, endpoint) {
152
+ let NextToken;
153
+ do {
154
+ const page = await connection.snsClient.send(new ListSubscriptionsByTopicCommand({
155
+ TopicArn: connection.topicArn,
156
+ NextToken
157
+ }));
158
+ const match = page.Subscriptions?.find((s) => s.Endpoint === endpoint && s.SubscriptionArn?.startsWith("arn:"));
159
+ if (match) return match.SubscriptionArn;
160
+ NextToken = page.NextToken;
161
+ } while (NextToken);
162
+ }
163
+ /**
164
+ * A pushed notification as the Lambda event SNS would have invoked a function
165
+ * with, so it can be handed to the same adaptor.
166
+ */
167
+ function toSnsEvent(message, subscriptionArn = "") {
168
+ return { Records: [{
169
+ EventSource: "aws:sns",
170
+ EventVersion: "1.0",
171
+ EventSubscriptionArn: subscriptionArn,
172
+ Sns: {
173
+ Type: message.Type,
174
+ MessageId: message.MessageId,
175
+ TopicArn: message.TopicArn,
176
+ Subject: message.Subject ?? null,
177
+ Message: message.Message,
178
+ Timestamp: message.Timestamp,
179
+ SignatureVersion: message.SignatureVersion,
180
+ Signature: message.Signature,
181
+ SigningCertUrl: message.SigningCertURL,
182
+ UnsubscribeUrl: message.UnsubscribeURL ?? "",
183
+ MessageAttributes: message.MessageAttributes ?? {}
184
+ }
185
+ }] };
186
+ }
187
+ var SnsCertificateUntrusted = class extends Error {
188
+ url;
189
+ constructor(url) {
190
+ super(`The SNS message names a signing certificate at '${url}', which is not an https URL on sns.<region>.amazonaws.com. Only SNS can send to this endpoint; reject the request.`);
191
+ this.url = url;
192
+ this.name = "SnsCertificateUntrusted";
193
+ }
194
+ };
195
+ var SnsCertificateUnavailable = class extends Error {
196
+ url;
197
+ status;
198
+ constructor(url, status) {
199
+ super(`Fetching the SNS signing certificate at '${url}' answered ${status}. The message cannot be verified, so it is not handled; SNS will retry.`);
200
+ this.url = url;
201
+ this.status = status;
202
+ this.name = "SnsCertificateUnavailable";
203
+ }
204
+ };
205
+ var SnsSignatureVersionUnknown = class extends Error {
206
+ version;
207
+ constructor(version) {
208
+ super(`SNS signature version '${version}' is not one this endpoint verifies (it knows '1' and '2'). Reject the request.`);
209
+ this.version = version;
210
+ this.name = "SnsSignatureVersionUnknown";
211
+ }
212
+ };
213
+ var SnsSignatureInvalid = class extends Error {
214
+ messageId;
215
+ topicArn;
216
+ constructor(messageId, topicArn) {
217
+ super(`SNS message '${messageId}' from '${topicArn}' does not verify against its signing certificate. It was not sent by SNS, or was altered on the way; reject it.`);
218
+ this.messageId = messageId;
219
+ this.topicArn = topicArn;
220
+ this.name = "SnsSignatureInvalid";
221
+ }
222
+ };
223
+ var SnsMessageTypeUnknown = class extends Error {
224
+ type;
225
+ constructor(type) {
226
+ super(`'${type}' is not an SNS message type (Notification, SubscriptionConfirmation, UnsubscribeConfirmation). Reject it.`);
227
+ this.type = type;
228
+ this.name = "SnsMessageTypeUnknown";
229
+ }
230
+ };
231
+ var SnsNotAConfirmation = class extends Error {
232
+ type;
233
+ constructor(type) {
234
+ super(`Only a SubscriptionConfirmation carries a URL to confirm; this is a '${type}'.`);
235
+ this.type = type;
236
+ this.name = "SnsNotAConfirmation";
237
+ }
238
+ };
239
+ var SnsConfirmationFailed = class extends Error {
240
+ topicArn;
241
+ status;
242
+ constructor(topicArn, status) {
243
+ super(`Confirming the subscription to '${topicArn}' answered ${status}. SNS will deliver nothing until it is confirmed; it resends the confirmation, or subscribe the endpoint again.`);
244
+ this.topicArn = topicArn;
245
+ this.status = status;
246
+ this.name = "SnsConfirmationFailed";
247
+ }
248
+ };
249
+ var SnsEndpointNotHttp = class extends Error {
250
+ endpoint;
251
+ constructor(endpoint) {
252
+ super(`'${endpoint}' is not an http(s) URL. SNS pushes to an HTTP endpoint; subscribe a queue for anything else.`);
253
+ this.endpoint = endpoint;
254
+ this.name = "SnsEndpointNotHttp";
255
+ }
256
+ };
257
+ //#endregion
6
258
  //#region src/sns/SNSConnection.ts
7
259
  var SNSConnection = class SNSConnection {
8
260
  type = "sns";
@@ -245,6 +497,6 @@ var SnsQueueMissing = class extends Error {
245
497
  }
246
498
  };
247
499
  //#endregion
248
- export { SNSConnection, SNSPublisher, SNSSubscriber, SnsQueueMissing, snsUrl_exports as snsUrl };
500
+ export { SNSConnection, SNSPublisher, SNSSubscriber, SnsCertificateUnavailable, SnsCertificateUntrusted, SnsConfirmationFailed, SnsEndpointNotHttp, SnsMessageTypeUnknown, SnsNotAConfirmation, SnsQueueMissing, SnsSignatureInvalid, SnsSignatureVersionUnknown, confirmSnsSubscription, snsStringToSign, snsUrl_exports as snsUrl, subscribeHttpEndpoint, toSnsEvent, verifySnsMessage };
249
501
 
250
502
  //# sourceMappingURL=index.mjs.map