@awsless/cli 0.0.46-next.34 → 0.0.46-next.35

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.
@@ -151,21 +151,6 @@ var imageHandler = (event) => {
151
151
  return;
152
152
  };
153
153
 
154
- // src/feature/bundle/server/resource/log.ts
155
- var logHandler = (event) => {
156
- const route = event?.["$awsless-route"];
157
- if (typeof route === "string") {
158
- if (routeType(route) === "on-error-log") {
159
- return asyncRoute(route, event.event);
160
- }
161
- return;
162
- }
163
- if (typeof event?.awslogs?.data === "string") {
164
- return asyncRoute("base:on-error-log:handler", event);
165
- }
166
- return;
167
- };
168
-
169
154
  // src/feature/bundle/server/resource/metric.ts
170
155
  var metricHandler = (event) => {
171
156
  if (typeof event?.["$awsless-route"] === "string" || typeof event?.headers?.["x-awsless-route"] === "string") {
@@ -181,15 +166,6 @@ var metricHandler = (event) => {
181
166
  return;
182
167
  };
183
168
 
184
- // src/feature/bundle/server/resource/on-failure.ts
185
- var onFailureHandler = (event) => {
186
- const route = event?.["$awsless-route"];
187
- if (typeof route === "string" && routeType(route) === "on-failure") {
188
- return asyncRoute(route, event.event);
189
- }
190
- return;
191
- };
192
-
193
169
  // src/feature/bundle/server/resource/pubsub.ts
194
170
  var eventTypes = ["connected", "disconnected", "subscribed", "unsubscribed"];
195
171
  var pubsubHandler = (event, routes) => {
@@ -398,8 +374,6 @@ var createBundle = (handlers) => {
398
374
  iconHandler,
399
375
  imageHandler,
400
376
  metricHandler,
401
- onFailureHandler,
402
- logHandler,
403
377
  queueHandler,
404
378
  pubsubHandler,
405
379
  topicHandler,
@@ -14,15 +14,6 @@ import {
14
14
  transform,
15
15
  uuid
16
16
  } from "@awsless/validate";
17
- import {
18
- define,
19
- getItem,
20
- number as dbNumber,
21
- object as dbObject,
22
- putItem,
23
- string as dbString
24
- } from "@awsless/dynamodb";
25
- import { getRouteEnv, internalInvoke } from "awsless";
26
17
  import { createHash } from "crypto";
27
18
  import * as zlib from "zlib";
28
19
  var RuntimeErrorSchema = object({
@@ -58,69 +49,35 @@ var EventSchema = object({
58
49
  timestamp: pipe(number(), transform((v) => new Date(v)))
59
50
  }))
60
51
  });
61
- var requestTable = define(getRouteEnv("TABLE"), {
62
- hash: "id",
63
- schema: dbObject({
64
- id: dbString(),
65
- ttl: dbNumber()
66
- })
67
- });
68
- var registerOwnRequest = async (requestId) => {
69
- try {
70
- await putItem(requestTable, {
71
- id: requestId,
72
- ttl: Math.floor(Date.now() / 1000) + 3600
73
- });
74
- } catch (error) {
75
- console.info("Failed to register the request marker", error);
76
- }
77
- };
78
- var isOwnRequest = async (requestId) => {
79
- try {
80
- const item = await getItem(requestTable, { id: requestId }, { consistentRead: true });
81
- return !!item;
82
- } catch (error) {
83
- console.info("Failed to check the request marker", error);
84
- return false;
85
- }
86
- };
87
- var handle_default = async (event, context) => {
88
- const consumerRoute = getRouteEnv("CONSUMER");
89
- if (!consumerRoute) {
90
- throw new Error("The CONSUMER route env is not set");
91
- }
92
- await registerOwnRequest(context.awsRequestId);
93
- const original = { error: console.error, warn: console.warn };
94
- console.error = console.info;
95
- console.warn = console.info;
96
- try {
97
- const payload = Buffer.from(event.awslogs.data, "base64");
98
- const unzipped = zlib.gunzipSync(new Uint8Array(payload));
99
- const result = safeParse(EventSchema, JSON.parse(unzipped.toString("utf-8")));
100
- if (!result.success) {
101
- console.info("Failed to parse log data", result.issues);
102
- return;
103
- }
104
- const origin = result.output.logGroup.split("/").pop();
105
- for (const logEvent of result.output.logEvents) {
106
- const error = parseError(logEvent.message, origin);
107
- if (!error || await isOwnRequest(error.requestId)) {
108
- continue;
52
+ var createHandler = (consumer) => {
53
+ return async (event, context) => {
54
+ try {
55
+ const payload = Buffer.from(event.awslogs.data, "base64");
56
+ const unzipped = zlib.gunzipSync(new Uint8Array(payload));
57
+ const result = safeParse(EventSchema, JSON.parse(unzipped.toString("utf-8")));
58
+ if (!result.success) {
59
+ console.info("Failed to parse log data", result.issues);
60
+ return;
61
+ }
62
+ const origin = result.output.logGroup.split("/").pop();
63
+ for (const logEvent of result.output.logEvents) {
64
+ const error = parseError(logEvent.message, origin);
65
+ if (!error) {
66
+ continue;
67
+ }
68
+ const invoke = Promise.resolve().then(() => {
69
+ return consumer({
70
+ ...error,
71
+ date: logEvent.timestamp
72
+ });
73
+ }).catch(() => {});
74
+ const deadline = Math.max(0, context.getRemainingTimeInMillis() - 3000);
75
+ await Promise.race([invoke, new Promise((resolve) => setTimeout(resolve, deadline))]);
109
76
  }
110
- const invoke = internalInvoke(consumerRoute, {
111
- ...error,
112
- date: logEvent.timestamp
113
- });
114
- invoke.catch(() => {});
115
- const deadline = Math.max(0, context.getRemainingTimeInMillis() - 3000);
116
- await Promise.race([invoke, new Promise((resolve) => setTimeout(resolve, deadline))]);
77
+ } catch (error) {
78
+ console.info("Failed to consume the error logs", error);
117
79
  }
118
- } catch (error) {
119
- console.info("Failed to consume the error logs", error);
120
- } finally {
121
- console.error = original.error;
122
- console.warn = original.warn;
123
- }
80
+ };
124
81
  };
125
82
  var parseError = (message, origin) => {
126
83
  let parsed;
@@ -179,5 +136,5 @@ var parseError = (message, origin) => {
179
136
  return;
180
137
  };
181
138
  export {
182
- handle_default as default
139
+ createHandler
183
140
  };
@@ -0,0 +1,205 @@
1
+ // src/feature/on-failure/server/handle.ts
2
+ import { parse as parse2, patch } from "@awsless/json";
3
+ import { deleteObject, getObject } from "@awsless/s3";
4
+
5
+ // src/feature/on-failure/server/util.ts
6
+ import { parse } from "@awsless/json";
7
+ var isDynamoDBFailureEvent = (event) => {
8
+ return "DDBStreamBatchInfo" in event;
9
+ };
10
+ var logicalResourceName = (physical) => {
11
+ const segments = physical.replace(/\.fifo$/, "").split("--");
12
+ if (segments.length === 4) {
13
+ return `${segments[1]}:${segments[2]}:${segments[3]}`;
14
+ }
15
+ if (segments.length === 3) {
16
+ return `${segments[1]}:${segments[2]}`;
17
+ }
18
+ return physical;
19
+ };
20
+ var getFailureSource = (payload) => {
21
+ const record = getDeliveryRecord(payload);
22
+ if (!record) {
23
+ return;
24
+ }
25
+ if (isTopicRecord(record)) {
26
+ return {
27
+ resource: record.Sns.TopicArn ? logicalResourceName(lastArnSegment(record.Sns.TopicArn)) : "topic",
28
+ event: parseEvent(record.Sns.Message)
29
+ };
30
+ }
31
+ if (isStreamRecord(record)) {
32
+ const table = record.eventSourceARN.split("/")[1];
33
+ return {
34
+ resource: table ? logicalResourceName(table) : "table-stream"
35
+ };
36
+ }
37
+ if (isQueueRecord(record)) {
38
+ return {
39
+ resource: logicalResourceName(lastArnSegment(record.eventSourceARN)),
40
+ event: parseEvent(record.body)
41
+ };
42
+ }
43
+ return;
44
+ };
45
+ var getDeliveryRecord = (payload) => {
46
+ if (!payload || typeof payload !== "object") {
47
+ return;
48
+ }
49
+ const records = payload.Records;
50
+ const record = Array.isArray(records) ? records[0] : undefined;
51
+ return record && typeof record === "object" ? record : undefined;
52
+ };
53
+ var isTopicRecord = (record) => {
54
+ return "Sns" in record && typeof record.Sns === "object" && record.Sns !== null;
55
+ };
56
+ var isStreamRecord = (record) => {
57
+ return "eventSource" in record && record.eventSource === "aws:dynamodb" && "eventSourceARN" in record;
58
+ };
59
+ var isQueueRecord = (record) => {
60
+ return "eventSource" in record && record.eventSource === "aws:sqs" && "eventSourceARN" in record;
61
+ };
62
+ var lastArnSegment = (arn) => {
63
+ return arn.split(":").at(-1);
64
+ };
65
+ var parseEvent = (value) => {
66
+ if (typeof value !== "string") {
67
+ return value;
68
+ }
69
+ try {
70
+ return parse(value);
71
+ } catch {
72
+ return value;
73
+ }
74
+ };
75
+
76
+ // src/feature/on-failure/server/handle.ts
77
+ var createHandler = (consumer) => {
78
+ return async (event) => {
79
+ if (!Array.isArray(event.Records)) {
80
+ throw new TypeError(`Unknown Event Type: ${JSON.stringify(event)}`);
81
+ }
82
+ await Promise.all(event.Records.map((record) => {
83
+ return unknownRecord(record, consumer);
84
+ }));
85
+ };
86
+ };
87
+ var unknownRecord = (record, consumer) => {
88
+ if (typeof record.eventSource === "string") {
89
+ if (record.eventSource.startsWith("aws:sqs")) {
90
+ return sqsRecord(record, consumer);
91
+ }
92
+ if (record.eventSource.startsWith("aws:s3")) {
93
+ return s3Record(record, consumer);
94
+ }
95
+ }
96
+ throw new TypeError(`Unknown Record Type: ${JSON.stringify(record)}`);
97
+ };
98
+ var sqsRecord = async (record, consumer) => {
99
+ const s3Records = parseS3Records(record.body);
100
+ if (s3Records) {
101
+ await Promise.all(s3Records.map((record2) => s3Record(record2, consumer)));
102
+ return;
103
+ }
104
+ const queueName = record.messageAttributes.queueName?.stringValue;
105
+ const body = parsePayload(record.body);
106
+ const payload = {
107
+ type: "queue",
108
+ id: record.messageId,
109
+ date: new Date(Number(record.attributes.SentTimestamp)),
110
+ payload: body,
111
+ source: queueName ? { resource: logicalResourceName(queueName), event: body } : undefined,
112
+ queue: {
113
+ name: queueName,
114
+ url: record.messageAttributes.queueUrl?.stringValue
115
+ }
116
+ };
117
+ await consumer(payload);
118
+ };
119
+ var parseS3Records = (body) => {
120
+ try {
121
+ const event = JSON.parse(body);
122
+ if (event?.Event === "s3:TestEvent") {
123
+ return [];
124
+ }
125
+ if (Array.isArray(event?.Records) && event.Records[0]?.eventSource === "aws:s3") {
126
+ return event.Records;
127
+ }
128
+ } catch {}
129
+ return;
130
+ };
131
+ var s3Record = async (record, consumer) => {
132
+ const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, " "));
133
+ const object = await getObject({
134
+ bucket: record.s3.bucket.name,
135
+ key
136
+ });
137
+ if (!object) {
138
+ return;
139
+ }
140
+ const json = await object.body.transformToString();
141
+ const unknownEvent = JSON.parse(json);
142
+ const payload = formatUnknownFailureEvent(unknownEvent);
143
+ await consumer(payload);
144
+ await deleteObject({
145
+ bucket: record.s3.bucket.name,
146
+ key
147
+ });
148
+ };
149
+ var formatUnknownFailureEvent = (event) => {
150
+ if (isDynamoDBFailureEvent(event)) {
151
+ return formatDynamoDBStreamFailureEvent(event);
152
+ }
153
+ return formatAsyncLambdaFailureEvent(event);
154
+ };
155
+ var formatAsyncLambdaFailureEvent = (event) => {
156
+ const payload = patchPayload(event.requestPayload);
157
+ const route = payload && typeof payload === "object" ? payload["$awsless-route"] : undefined;
158
+ return {
159
+ type: "async-lambda",
160
+ date: new Date(event.timestamp),
161
+ id: event.requestContext.requestId,
162
+ function: {
163
+ name: typeof route === "string" ? route : event.requestContext.functionArn.split(":")[6]
164
+ },
165
+ payload: typeof route === "string" ? payload.event ?? {} : payload,
166
+ source: typeof route === "string" ? { resource: route, event: payload.event ?? {} } : getFailureSource(payload),
167
+ error: {
168
+ type: event.responsePayload.errorType,
169
+ message: event.responsePayload.errorMessage,
170
+ stackTrace: event.responsePayload.stackTrace
171
+ }
172
+ };
173
+ };
174
+ var formatDynamoDBStreamFailureEvent = (event) => {
175
+ const payload = parsePayload(event.payload);
176
+ const streamArn = event.DDBStreamBatchInfo?.streamArn;
177
+ const table = streamArn?.split("/")[1];
178
+ return {
179
+ type: "dynamodb-stream",
180
+ date: new Date(event.timestamp),
181
+ id: event.requestContext.requestId,
182
+ function: {
183
+ name: event.requestContext.functionArn.split(":")[6]
184
+ },
185
+ payload,
186
+ source: table ? { resource: logicalResourceName(table) } : getFailureSource(payload)
187
+ };
188
+ };
189
+ var parsePayload = (payload) => {
190
+ try {
191
+ return parse2(payload);
192
+ } catch {
193
+ return payload;
194
+ }
195
+ };
196
+ var patchPayload = (payload) => {
197
+ try {
198
+ return patch(payload);
199
+ } catch {
200
+ return payload;
201
+ }
202
+ };
203
+ export {
204
+ createHandler
205
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@awsless/cli",
3
- "version": "0.0.46-next.34",
3
+ "version": "0.0.46-next.35",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -25,14 +25,14 @@
25
25
  "@awsless/ts-file-cache": "^0.0.16"
26
26
  },
27
27
  "peerDependencies": {
28
+ "@awsless/big-float": "^0.1.7",
28
29
  "@awsless/duration": "^0.0.4",
29
- "@awsless/json": "^0.0.11",
30
30
  "@awsless/dynamodb": "^0.3.22",
31
- "@awsless/lambda": "^0.0.47",
31
+ "@awsless/json": "^0.0.11",
32
32
  "@awsless/validate": "^0.1.7",
33
33
  "@awsless/s3": "^0.0.22",
34
- "@awsless/big-float": "^0.1.7",
35
34
  "@awsless/weak-cache": "^0.0.1",
35
+ "@awsless/lambda": "^0.0.47",
36
36
  "awsless": "^0.0.15-next.8"
37
37
  },
38
38
  "devDependencies": {
@@ -103,24 +103,24 @@
103
103
  "zod": "^3.24.2",
104
104
  "zod-to-json-schema": "^3.24.3",
105
105
  "@awsless/big-float": "^0.1.7",
106
+ "@awsless/clui": "^0.0.9",
106
107
  "@awsless/cloudwatch": "^0.0.1",
107
108
  "@awsless/duration": "^0.0.4",
108
- "@awsless/iot": "^0.0.5",
109
109
  "@awsless/dynamodb": "^0.3.22",
110
+ "@awsless/json": "^0.0.11",
111
+ "@awsless/iot": "^0.0.5",
110
112
  "@awsless/lambda": "^0.0.47",
113
+ "@awsless/open-search": "^0.0.24",
111
114
  "@awsless/s3": "^0.0.22",
112
- "@awsless/redis": "^0.1.13",
113
- "@awsless/clui": "^0.0.9",
114
- "@awsless/size": "^0.0.2",
115
+ "@awsless/scheduler": "^0.0.5",
115
116
  "@awsless/sns": "^0.0.11",
116
117
  "@awsless/sqs": "^0.0.24",
117
- "@awsless/json": "^0.0.11",
118
- "@awsless/validate": "^0.1.7",
118
+ "@awsless/size": "^0.0.2",
119
119
  "@awsless/ssm": "^0.0.8",
120
- "@awsless/scheduler": "^0.0.5",
121
- "@awsless/weak-cache": "^0.0.1",
120
+ "@awsless/validate": "^0.1.7",
122
121
  "awsless": "^0.0.15-next.8",
123
- "@awsless/open-search": "^0.0.24"
122
+ "@awsless/weak-cache": "^0.0.1",
123
+ "@awsless/redis": "^0.1.13"
124
124
  },
125
125
  "scripts": {
126
126
  "test": "bun cli/build-handlers.ts && pnpm vitest",
@@ -1 +0,0 @@
1
- 360568614ec11c8dd2efe5e05ccc2dde09fb5b28
Binary file