effortless-aws 0.28.0 → 0.30.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.
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createHandlerRuntime
3
- } from "../chunk-EZG3NX42.js";
3
+ } from "../chunk-ISJC6CHC.js";
4
4
 
5
5
  // src/runtime/wrap-fifo-queue.ts
6
6
  var parseMessages = (rawRecords, schema) => {
@@ -29,19 +29,22 @@ var parseMessages = (rawRecords, schema) => {
29
29
  return messages;
30
30
  };
31
31
  var wrapFifoQueue = (handler) => {
32
- if (!handler.onMessage && !handler.onBatch) {
33
- throw new Error("wrapFifoQueue requires a handler with onMessage or onBatch defined");
32
+ if (!handler.onMessage && !handler.onMessageBatch) {
33
+ throw new Error("wrapFifoQueue requires a handler with onMessage or onMessageBatch defined");
34
34
  }
35
35
  const rt = createHandlerRuntime(handler, "fifo-queue", handler.__spec.lambda?.logLevel ?? "info");
36
36
  const handleError = handler.onError ?? (({ error }) => console.error(`[effortless:${rt.handlerName}]`, error));
37
37
  return async (event) => {
38
38
  const startTime = Date.now();
39
39
  rt.patchConsole();
40
- let shared;
40
+ let ctxProps = {};
41
41
  try {
42
42
  const rawRecords = event.Records ?? [];
43
43
  const input = { messageCount: rawRecords.length };
44
- shared = await rt.commonArgs();
44
+ const common = await rt.commonArgs();
45
+ const ctx = common.ctx;
46
+ ctxProps = ctx && typeof ctx === "object" ? { ...ctx } : {};
47
+ const shared = { ...ctxProps };
45
48
  let messages;
46
49
  try {
47
50
  messages = parseMessages(rawRecords, handler.schema);
@@ -53,9 +56,14 @@ var wrapFifoQueue = (handler) => {
53
56
  };
54
57
  }
55
58
  const batchItemFailures = [];
56
- if (handler.onBatch) {
59
+ if (handler.onMessageBatch) {
57
60
  try {
58
- await handler.onBatch({ messages, ...shared });
61
+ const result = await handler.onMessageBatch({ messages, ...shared });
62
+ if (result?.failures) {
63
+ for (const id of result.failures) {
64
+ batchItemFailures.push({ itemIdentifier: id });
65
+ }
66
+ }
59
67
  } catch (error) {
60
68
  handleError({ error, ...shared });
61
69
  for (const message of messages) {
@@ -80,11 +88,11 @@ var wrapFifoQueue = (handler) => {
80
88
  }
81
89
  return { batchItemFailures };
82
90
  } finally {
83
- if (handler.onAfterInvoke && shared) {
91
+ if (handler.onCleanup) {
84
92
  try {
85
- await handler.onAfterInvoke(shared);
93
+ await handler.onCleanup(ctxProps);
86
94
  } catch (e) {
87
- console.error(`[effortless:${rt.handlerName}] onAfterInvoke error`, e);
95
+ console.error(`[effortless:${rt.handlerName}] onCleanup error`, e);
88
96
  }
89
97
  }
90
98
  rt.restoreConsole();
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createHandlerRuntime,
3
3
  createTableClient
4
- } from "../chunk-EZG3NX42.js";
4
+ } from "../chunk-ISJC6CHC.js";
5
5
 
6
6
  // src/runtime/wrap-table-stream.ts
7
7
  import { unmarshall } from "@aws-sdk/util-dynamodb";
@@ -36,20 +36,13 @@ var parseRecords = (rawRecords, schema) => {
36
36
  }
37
37
  return { records, sequenceNumbers };
38
38
  };
39
- var collectFailures = (records, sequenceNumbers) => {
40
- const failures = [];
41
- for (const record of records) {
42
- const seq = sequenceNumbers.get(record);
43
- if (seq) failures.push({ itemIdentifier: seq });
44
- }
45
- return failures;
46
- };
47
39
  var ENV_DEP_SELF = "EFF_DEP_SELF";
48
40
  var wrapTableStream = (handler) => {
49
- if (!handler.onRecord && !handler.onBatch) {
50
- throw new Error("wrapTableStream requires a handler with onRecord or onBatch defined");
41
+ if (!handler.onRecord && !handler.onRecordBatch) {
42
+ throw new Error("wrapTableStream requires a handler with onRecord or onRecordBatch defined");
51
43
  }
52
44
  const tagField = handler.__spec.tagField ?? "tag";
45
+ const concurrency = handler.__spec.concurrency ?? 1;
53
46
  let selfClient = null;
54
47
  const getSelfClient = () => {
55
48
  if (selfClient) return selfClient;
@@ -67,11 +60,14 @@ var wrapTableStream = (handler) => {
67
60
  return async (event) => {
68
61
  const startTime = Date.now();
69
62
  rt.patchConsole();
70
- let shared;
63
+ let ctxProps = {};
71
64
  try {
72
65
  const rawRecords = event.Records ?? [];
73
66
  const input = { recordCount: rawRecords.length };
74
- shared = { ...await rt.commonArgs(), table: getSelfClient() };
67
+ const common = await rt.commonArgs();
68
+ const ctx = common.ctx;
69
+ ctxProps = ctx && typeof ctx === "object" ? { ...ctx } : {};
70
+ const shared = { ...ctxProps };
75
71
  let records;
76
72
  let sequenceNumbers;
77
73
  try {
@@ -82,37 +78,47 @@ var wrapTableStream = (handler) => {
82
78
  return { batchItemFailures: rawRecords.map((r) => r.dynamodb?.SequenceNumber).filter((s) => !!s).map((seq) => ({ itemIdentifier: seq })) };
83
79
  }
84
80
  const batchItemFailures = [];
85
- if (handler.onBatch) {
81
+ const frozenBatch = Object.freeze(records);
82
+ if (handler.onRecordBatch) {
86
83
  try {
87
- await handler.onBatch({ records, ...shared });
84
+ const result = await handler.onRecordBatch({ records: frozenBatch, ...shared });
85
+ if (result?.failures) {
86
+ for (const seq of result.failures) {
87
+ batchItemFailures.push({ itemIdentifier: seq });
88
+ }
89
+ }
88
90
  } catch (error) {
89
91
  handleError({ error, ...shared });
90
- batchItemFailures.push(...collectFailures(records, sequenceNumbers));
91
- }
92
- } else {
93
- const results = [];
94
- const failures = [];
95
- const onRecord = handler.onRecord;
96
- for (const record of records) {
97
- try {
98
- const result = await onRecord({ record, ...shared });
99
- if (result !== void 0) results.push(result);
100
- } catch (error) {
101
- handleError({ error, ...shared });
102
- failures.push({ record, error });
92
+ for (const record of records) {
103
93
  const seq = sequenceNumbers.get(record);
104
94
  if (seq) batchItemFailures.push({ itemIdentifier: seq });
105
95
  }
106
96
  }
107
- if (handler.onBatchComplete) {
108
- try {
109
- await handler.onBatchComplete({ results, failures, ...shared });
110
- } catch (error) {
111
- handleError({ error, ...shared });
112
- for (const record of records) {
97
+ } else {
98
+ const onRecord = handler.onRecord;
99
+ if (concurrency <= 1) {
100
+ for (const record of records) {
101
+ try {
102
+ await onRecord({ record, batch: frozenBatch, ...shared });
103
+ } catch (error) {
104
+ handleError({ error, ...shared });
113
105
  const seq = sequenceNumbers.get(record);
114
- if (seq && !batchItemFailures.some((f) => f.itemIdentifier === seq)) {
115
- batchItemFailures.push({ itemIdentifier: seq });
106
+ if (seq) batchItemFailures.push({ itemIdentifier: seq });
107
+ }
108
+ }
109
+ } else {
110
+ for (let i = 0; i < records.length; i += concurrency) {
111
+ const chunk = records.slice(i, i + concurrency);
112
+ const results = await Promise.allSettled(
113
+ chunk.map((record) => onRecord({ record, batch: frozenBatch, ...shared }))
114
+ );
115
+ for (let j = 0; j < results.length; j++) {
116
+ const result = results[j];
117
+ const record = chunk[j];
118
+ if (result.status === "rejected") {
119
+ handleError({ error: result.reason, ...shared });
120
+ const seq = sequenceNumbers.get(record);
121
+ if (seq) batchItemFailures.push({ itemIdentifier: seq });
116
122
  }
117
123
  }
118
124
  }
@@ -125,11 +131,11 @@ var wrapTableStream = (handler) => {
125
131
  }
126
132
  return { batchItemFailures };
127
133
  } finally {
128
- if (handler.onAfterInvoke && shared) {
134
+ if (handler.onCleanup) {
129
135
  try {
130
- await handler.onAfterInvoke(shared);
136
+ await handler.onCleanup(ctxProps);
131
137
  } catch (e) {
132
- console.error(`[effortless:${rt.handlerName}] onAfterInvoke error`, e);
138
+ console.error(`[effortless:${rt.handlerName}] onCleanup error`, e);
133
139
  }
134
140
  }
135
141
  rt.restoreConsole();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "effortless-aws",
3
- "version": "0.28.0",
3
+ "version": "0.30.0",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "description": "Code-first AWS Lambda framework. Export handlers, deploy with one command.",
@@ -45,10 +45,13 @@
45
45
  "@types/node": "^22.19.9",
46
46
  "tsup": "^8.5.1",
47
47
  "type-fest": "^4.22.1",
48
- "typescript": "^5.9.3"
48
+ "typescript": "^5.9.3",
49
+ "vite-tsconfig-paths": "^6.1.1",
50
+ "vitest": "^4.0.18"
49
51
  },
50
52
  "scripts": {
51
53
  "build": "tsup",
52
- "typecheck": "tsc --noEmit"
54
+ "typecheck": "tsc --noEmit",
55
+ "test": "vitest run"
53
56
  }
54
57
  }