dynamodb-reactive 0.2.1 → 0.2.2

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/server.js CHANGED
@@ -2068,7 +2068,7 @@ async function mapWithConcurrency(values, concurrency, mapper) {
2068
2068
  await Promise.all(workers);
2069
2069
  return results;
2070
2070
  }
2071
- async function processRecord(record, utils) {
2071
+ function extractRecordAffectedKeys(record) {
2072
2072
  const tableName = extractTableName(record);
2073
2073
  if (!tableName || !record.dynamodb) {
2074
2074
  throw new Error("Malformed DynamoDB stream record");
@@ -2079,53 +2079,84 @@ async function processRecord(record, utils) {
2079
2079
  const item = unmarshall(image);
2080
2080
  for (const key of extractAffectedKeys(tableName, item)) affectedKeys.add(key);
2081
2081
  }
2082
- const subscriptions = /* @__PURE__ */ new Map();
2083
- const lookupResults = await mapWithConcurrency(
2084
- [...affectedKeys],
2085
- MAX_FANOUT_CONCURRENCY,
2086
- (key) => utils.findAffectedSubscriptions(key)
2087
- );
2088
- for (const result of lookupResults) {
2089
- for (const subscription of result) {
2090
- subscriptions.set(
2091
- `${subscription.connectionId}\0${subscription.subscriptionId}`,
2092
- subscription
2093
- );
2082
+ return affectedKeys;
2083
+ }
2084
+ function subscriptionKey(subscription) {
2085
+ return JSON.stringify([
2086
+ subscription.connectionId,
2087
+ subscription.subscriptionId
2088
+ ]);
2089
+ }
2090
+ function failureIdentifier(record, cause) {
2091
+ const itemIdentifier = record.dynamodb?.SequenceNumber;
2092
+ if (!itemIdentifier) {
2093
+ throw new Error("Failed DynamoDB stream record has no sequence number", {
2094
+ cause
2095
+ });
2096
+ }
2097
+ return itemIdentifier;
2098
+ }
2099
+ async function processStreamEvent(event, utils) {
2100
+ const failures = /* @__PURE__ */ new Map();
2101
+ const dependencies = /* @__PURE__ */ new Map();
2102
+ for (const [recordIndex, record] of event.Records.entries()) {
2103
+ try {
2104
+ for (const dependencyKey of extractRecordAffectedKeys(record)) {
2105
+ const recordIndexes = dependencies.get(dependencyKey) ?? /* @__PURE__ */ new Set();
2106
+ recordIndexes.add(recordIndex);
2107
+ dependencies.set(dependencyKey, recordIndexes);
2108
+ }
2109
+ } catch (error2) {
2110
+ error("[stream] Record dependency extraction failed:", error2);
2111
+ failures.set(recordIndex, error2);
2094
2112
  }
2095
2113
  }
2114
+ const subscriptions = /* @__PURE__ */ new Map();
2096
2115
  await mapWithConcurrency(
2097
- [...subscriptions.values()],
2116
+ [...dependencies],
2098
2117
  MAX_FANOUT_CONCURRENCY,
2099
- ({ connectionId, subscriptionId }) => utils.processSubscription(connectionId, subscriptionId)
2118
+ async ([dependencyKey, recordIndexes]) => {
2119
+ try {
2120
+ const affectedSubscriptions = await utils.findAffectedSubscriptions(dependencyKey);
2121
+ for (const subscription of affectedSubscriptions) {
2122
+ const key = subscriptionKey(subscription);
2123
+ const work = subscriptions.get(key) ?? {
2124
+ ...subscription,
2125
+ recordIndexes: /* @__PURE__ */ new Set()
2126
+ };
2127
+ for (const recordIndex of recordIndexes) {
2128
+ work.recordIndexes.add(recordIndex);
2129
+ }
2130
+ subscriptions.set(key, work);
2131
+ }
2132
+ } catch (error2) {
2133
+ error("[stream] Subscription dependency lookup failed:", error2);
2134
+ for (const recordIndex of recordIndexes) {
2135
+ failures.set(recordIndex, error2);
2136
+ }
2137
+ }
2138
+ }
2100
2139
  );
2101
- }
2102
- async function processStreamEvent(event, utils) {
2103
- const results = await mapWithConcurrency(
2104
- event.Records,
2140
+ await mapWithConcurrency(
2141
+ [...subscriptions.values()],
2105
2142
  MAX_FANOUT_CONCURRENCY,
2106
- async (record) => {
2143
+ async ({ connectionId, subscriptionId, recordIndexes }) => {
2107
2144
  try {
2108
- await processRecord(record, utils);
2109
- return null;
2145
+ await utils.processSubscription(connectionId, subscriptionId);
2110
2146
  } catch (error2) {
2111
- error("[stream] Record failed:", error2);
2112
- const itemIdentifier = record.dynamodb?.SequenceNumber;
2113
- if (!itemIdentifier) {
2114
- throw new Error(
2115
- "Failed DynamoDB stream record has no sequence number",
2116
- { cause: error2 }
2117
- );
2147
+ error("[stream] Subscription refresh failed:", error2);
2148
+ for (const recordIndex of recordIndexes) {
2149
+ failures.set(recordIndex, error2);
2118
2150
  }
2119
- return {
2120
- itemIdentifier
2121
- };
2122
2151
  }
2123
2152
  }
2124
2153
  );
2125
2154
  return {
2126
- batchItemFailures: results.filter(
2127
- (failure) => failure !== null
2128
- )
2155
+ batchItemFailures: event.Records.flatMap((record, recordIndex) => {
2156
+ if (!failures.has(recordIndex)) return [];
2157
+ const cause = failures.get(recordIndex);
2158
+ return [{ itemIdentifier: failureIdentifier(record, cause) }];
2159
+ })
2129
2160
  };
2130
2161
  }
2131
2162
  function clients(config) {