@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.
- package/dist/app.json +1 -1
- package/dist/app.stage.json +1 -1
- package/dist/bin.js +406 -278
- package/dist/handlers/bundle.js +0 -26
- package/dist/handlers/on-error-log.js +28 -71
- package/dist/handlers/on-failure.js +205 -0
- package/package.json +13 -13
- package/dist/prebuild/on-failure/HASH +0 -1
- package/dist/prebuild/on-failure/bundle.zip +0 -0
package/dist/handlers/bundle.js
CHANGED
|
@@ -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
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
111
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
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.
|
|
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/
|
|
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/
|
|
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/
|
|
118
|
-
"@awsless/validate": "^0.1.7",
|
|
118
|
+
"@awsless/size": "^0.0.2",
|
|
119
119
|
"@awsless/ssm": "^0.0.8",
|
|
120
|
-
"@awsless/
|
|
121
|
-
"@awsless/weak-cache": "^0.0.1",
|
|
120
|
+
"@awsless/validate": "^0.1.7",
|
|
122
121
|
"awsless": "^0.0.15-next.8",
|
|
123
|
-
"@awsless/
|
|
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
|