dynamodb-reactive 0.3.0 → 0.5.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.
Files changed (48) hide show
  1. package/README.md +121 -43
  2. package/dist/{client-Zb67giI5.d.ts → client-wZQ9XC9S.d.ts} +11 -0
  3. package/dist/client.cjs +53 -55
  4. package/dist/client.cjs.map +1 -1
  5. package/dist/client.d.cts +1 -1
  6. package/dist/client.d.ts +1 -1
  7. package/dist/client.js +53 -55
  8. package/dist/client.js.map +1 -1
  9. package/dist/cloudflare.cjs +461 -0
  10. package/dist/cloudflare.cjs.map +1 -0
  11. package/dist/cloudflare.d.cts +129 -0
  12. package/dist/cloudflare.d.ts +129 -0
  13. package/dist/cloudflare.js +456 -0
  14. package/dist/cloudflare.js.map +1 -0
  15. package/dist/cluster.cjs +66 -66
  16. package/dist/cluster.cjs.map +1 -1
  17. package/dist/cluster.d.cts +2 -78
  18. package/dist/cluster.d.ts +2 -78
  19. package/dist/cluster.js +66 -66
  20. package/dist/cluster.js.map +1 -1
  21. package/dist/core.cjs +1 -3
  22. package/dist/core.cjs.map +1 -1
  23. package/dist/core.js +1 -3
  24. package/dist/core.js.map +1 -1
  25. package/dist/hub.cjs +164 -0
  26. package/dist/hub.cjs.map +1 -0
  27. package/dist/hub.d.cts +91 -0
  28. package/dist/hub.d.ts +91 -0
  29. package/dist/hub.js +157 -0
  30. package/dist/hub.js.map +1 -0
  31. package/dist/index.cjs +1 -3
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.js +1 -3
  34. package/dist/index.js.map +1 -1
  35. package/dist/infra.cjs +19 -33
  36. package/dist/infra.cjs.map +1 -1
  37. package/dist/infra.js +19 -33
  38. package/dist/infra.js.map +1 -1
  39. package/dist/protocol-C4Syt_Bl.d.ts +78 -0
  40. package/dist/react.cjs +56 -65
  41. package/dist/react.cjs.map +1 -1
  42. package/dist/react.js +56 -65
  43. package/dist/react.js.map +1 -1
  44. package/dist/server.cjs +46 -139
  45. package/dist/server.cjs.map +1 -1
  46. package/dist/server.js +46 -139
  47. package/dist/server.js.map +1 -1
  48. package/package.json +42 -24
package/dist/server.cjs CHANGED
@@ -3,8 +3,8 @@
3
3
  var clientDynamodb = require('@aws-sdk/client-dynamodb');
4
4
  var libDynamodb = require('@aws-sdk/lib-dynamodb');
5
5
  var crypto = require('crypto');
6
- var zod = require('zod');
7
6
  var utilDynamodb = require('@aws-sdk/util-dynamodb');
7
+ var zod = require('zod');
8
8
  var util = require('util');
9
9
  var clientApigatewaymanagementapi = require('@aws-sdk/client-apigatewaymanagementapi');
10
10
  var jsonpatch = require('fast-json-patch');
@@ -158,11 +158,9 @@ function encodeStructuredValue(value, seen) {
158
158
  return `function:${String(value)}`;
159
159
  }
160
160
  if (ArrayBuffer.isView(value)) {
161
- return `binary:${Buffer.from(
162
- value.buffer,
163
- value.byteOffset,
164
- value.byteLength
165
- ).toString("base64url")}`;
161
+ return `binary:${Buffer.from(value.buffer, value.byteOffset, value.byteLength).toString(
162
+ "base64url"
163
+ )}`;
166
164
  }
167
165
  if (value instanceof ArrayBuffer) {
168
166
  return `binary:${Buffer.from(value).toString("base64url")}`;
@@ -174,9 +172,7 @@ function encodeStructuredValue(value, seen) {
174
172
  seen.add(value);
175
173
  let encoded;
176
174
  if (Array.isArray(value)) {
177
- encoded = `array:${JSON.stringify(
178
- value.map((item) => encodeStructuredValue(item, seen))
179
- )}`;
175
+ encoded = `array:${JSON.stringify(value.map((item) => encodeStructuredValue(item, seen)))}`;
180
176
  } else if (value instanceof Set) {
181
177
  encoded = `set:${JSON.stringify(
182
178
  [...value].map((item) => encodeStructuredValue(item, seen)).sort()
@@ -228,15 +224,13 @@ function extractFromCondition(tableName, condition) {
228
224
  return { dependencies, safe: dependencies.length === 1 };
229
225
  }
230
226
  case "logical": {
231
- if (condition.operator === "not" || !condition.conditions || condition.conditions.length === 0) {
227
+ if (condition.operator === "not" || // oxlint-disable-next-line typescript/no-unnecessary-condition -- Conditions can come from plain JavaScript callers.
228
+ !condition.conditions || condition.conditions.length === 0) {
232
229
  return { dependencies, safe: false };
233
230
  }
234
231
  let safe = true;
235
232
  for (const subCondition of condition.conditions) {
236
- const extracted = extractFromCondition(
237
- tableName,
238
- subCondition
239
- );
233
+ const extracted = extractFromCondition(tableName, subCondition);
240
234
  dependencies.push(...extracted.dependencies);
241
235
  safe &&= extracted.safe;
242
236
  }
@@ -261,11 +255,7 @@ function extractDependencies(operation) {
261
255
  return [...unique.values()];
262
256
  }
263
257
  function createDependencyKey(dependency) {
264
- const components = [
265
- dependency.tableName,
266
- dependency.fieldName,
267
- dependency.fieldValue
268
- ];
258
+ const components = [dependency.tableName, dependency.fieldName, dependency.fieldValue];
269
259
  const encoded = components.map(
270
260
  (component) => Buffer.from(component, "utf8").toString("base64url")
271
261
  );
@@ -317,9 +307,7 @@ function parseDependencyKey(key) {
317
307
  return null;
318
308
  }
319
309
  function extractAffectedKeys(tableName, item) {
320
- const keys = /* @__PURE__ */ new Set([
321
- createDependencyKey(wildcardDependency(tableName))
322
- ]);
310
+ const keys = /* @__PURE__ */ new Set([createDependencyKey(wildcardDependency(tableName))]);
323
311
  const visit = (fieldName, fieldValue) => {
324
312
  if (fieldValue !== null && fieldValue !== void 0) {
325
313
  const encodedValue = encodeDependencyValue(fieldValue);
@@ -333,10 +321,7 @@ function extractAffectedKeys(tableName, item) {
333
321
  );
334
322
  }
335
323
  if (typeof fieldValue === "string") {
336
- const maxPrefixLen = Math.min(
337
- fieldValue.length,
338
- MAX_TRACKED_PREFIX_LENGTH
339
- );
324
+ const maxPrefixLen = Math.min(fieldValue.length, MAX_TRACKED_PREFIX_LENGTH);
340
325
  for (let i = 1; i <= maxPrefixLen; i++) {
341
326
  keys.add(
342
327
  createDependencyKey({
@@ -693,6 +678,8 @@ var SystemTableNames = {
693
678
  dependencies: "ReactiveDependenciesV2",
694
679
  queries: "ReactiveConnectionQueriesV2"
695
680
  };
681
+
682
+ // ../server/src/query-metadata.ts
696
683
  var InvalidQueryMetadataError = class extends Error {
697
684
  constructor(message) {
698
685
  super(message);
@@ -745,9 +732,7 @@ function attributeValue(value) {
745
732
  )
746
733
  };
747
734
  }
748
- throw new InvalidQueryMetadataError(
749
- `Unsupported query value type: ${typeof value}`
750
- );
735
+ throw new InvalidQueryMetadataError(`Unsupported query value type: ${typeof value}`);
751
736
  }
752
737
  function quoteIdentifier(identifier) {
753
738
  if (!identifier) {
@@ -773,9 +758,7 @@ function compileCondition(condition, parameters) {
773
758
  };
774
759
  const operator = operators[condition.operator];
775
760
  if (!operator) {
776
- throw new InvalidQueryMetadataError(
777
- `Unsupported comparison operator: ${condition.operator}`
778
- );
761
+ throw new InvalidQueryMetadataError(`Unsupported comparison operator: ${condition.operator}`);
779
762
  }
780
763
  parameters.push(attributeValue(condition.value));
781
764
  return `${quotePath(condition.field)} ${operator} ?`;
@@ -792,7 +775,7 @@ function compileCondition(condition, parameters) {
792
775
  return `${name}(${quotePath(condition.field)}, ?)`;
793
776
  }
794
777
  throw new InvalidQueryMetadataError(
795
- `Unsupported function operator: ${condition.operator}`
778
+ `Unsupported function operator: ${String(condition.operator)}`
796
779
  );
797
780
  }
798
781
  if (condition.type === "logical" && condition.conditions?.length) {
@@ -812,9 +795,7 @@ function compileCondition(condition, parameters) {
812
795
  function parseQueryMetadata(metadata) {
813
796
  const parsed = QueryMetadataSchema.safeParse(metadata);
814
797
  if (!parsed.success) {
815
- throw new InvalidQueryMetadataError(
816
- `Invalid query metadata: ${parsed.error.message}`
817
- );
798
+ throw new InvalidQueryMetadataError(`Invalid query metadata: ${parsed.error.message}`);
818
799
  }
819
800
  return parsed.data;
820
801
  }
@@ -831,9 +812,7 @@ function compileQueryMetadata(metadataInput, nextToken) {
831
812
  }
832
813
  if (metadata.filterConditions.length) {
833
814
  predicates.push(
834
- ...metadata.filterConditions.map(
835
- (condition) => compileCondition(condition, parameters)
836
- )
815
+ ...metadata.filterConditions.map((condition) => compileCondition(condition, parameters))
837
816
  );
838
817
  }
839
818
  if (predicates.length) statement += ` WHERE ${predicates.join(" AND ")}`;
@@ -873,9 +852,7 @@ async function executeQueryMetadata(client, metadataInput) {
873
852
  new clientDynamodb.ExecuteStatementCommand(compileQueryMetadata(metadata, nextToken))
874
853
  );
875
854
  items.push(
876
- ...(response.Items ?? []).map(
877
- (item) => utilDynamodb.unmarshall(item)
878
- )
855
+ ...(response.Items ?? []).map((item) => utilDynamodb.unmarshall(item))
879
856
  );
880
857
  nextToken = response.NextToken;
881
858
  if (nextToken && seenTokens.has(nextToken)) {
@@ -921,10 +898,7 @@ function createClients(config) {
921
898
  function createDbContext(config, dependencyTracker) {
922
899
  const { client, docClient } = createClients(config);
923
900
  async function executeQuery(operation) {
924
- return executeQueryMetadata(
925
- client,
926
- operationToQueryMetadata(operation)
927
- );
901
+ return executeQueryMetadata(client, operationToQueryMetadata(operation));
928
902
  }
929
903
  return {
930
904
  query(table) {
@@ -975,10 +949,7 @@ function createDbContext(config, dependencyTracker) {
975
949
  });
976
950
  const expected = current;
977
951
  const fields = [
978
- .../* @__PURE__ */ new Set([
979
- ...table.getFieldNames().map(String),
980
- ...Object.keys(expected)
981
- ])
952
+ .../* @__PURE__ */ new Set([...table.getFieldNames().map(String), ...Object.keys(expected)])
982
953
  ].sort();
983
954
  const conditions = [];
984
955
  const expressionAttributeNames = {};
@@ -1003,9 +974,7 @@ function createDbContext(config, dependencyTracker) {
1003
974
  "utf8"
1004
975
  );
1005
976
  if (Buffer.byteLength(conditionExpression, "utf8") > MAX_CONDITION_EXPRESSION_BYTES || conditions.length > MAX_CONDITION_OPERATORS || variableBytes > MAX_EXPRESSION_VARIABLE_BYTES) {
1006
- throw new Error(
1007
- "Item is too complex to update with a safe compare-and-swap"
1008
- );
977
+ throw new Error("Item is too complex to update with a safe compare-and-swap");
1009
978
  }
1010
979
  const command = new libDynamodb.PutCommand({
1011
980
  TableName: table.tableName,
@@ -1070,26 +1039,11 @@ function validateKeyString(name, value, maxBytes) {
1070
1039
  }
1071
1040
  }
1072
1041
  function validateRequest(request) {
1073
- validateKeyString(
1074
- "connectionId",
1075
- request.connectionId,
1076
- DYNAMODB_PARTITION_KEY_MAX_BYTES2
1077
- );
1042
+ validateKeyString("connectionId", request.connectionId, DYNAMODB_PARTITION_KEY_MAX_BYTES2);
1078
1043
  if (request.type === "subscribe" || request.type === "unsubscribe") {
1079
- validateKeyString(
1080
- "subscriptionId",
1081
- request.subscriptionId,
1082
- DYNAMODB_SORT_KEY_MAX_BYTES
1083
- );
1084
- validateKeyString(
1085
- "registrationId",
1086
- request.registrationId,
1087
- DYNAMODB_SORT_KEY_MAX_BYTES
1088
- );
1089
- if (Buffer.byteLength(
1090
- `${request.connectionId}#${request.subscriptionId}`,
1091
- "utf8"
1092
- ) > DYNAMODB_SORT_KEY_MAX_BYTES) {
1044
+ validateKeyString("subscriptionId", request.subscriptionId, DYNAMODB_SORT_KEY_MAX_BYTES);
1045
+ validateKeyString("registrationId", request.registrationId, DYNAMODB_SORT_KEY_MAX_BYTES);
1046
+ if (Buffer.byteLength(`${request.connectionId}#${request.subscriptionId}`, "utf8") > DYNAMODB_SORT_KEY_MAX_BYTES) {
1093
1047
  throw requestError(
1094
1048
  "connectionId and subscriptionId exceed DynamoDB dependency key limits",
1095
1049
  400
@@ -1222,18 +1176,11 @@ function createReactiveHandler(config) {
1222
1176
  if (procedure.type !== "query") {
1223
1177
  throw requestError("Only query procedures can be subscribed to", 400);
1224
1178
  }
1225
- const resolverResult = await config.router.execute(
1226
- request.path,
1227
- context,
1228
- request.input
1229
- );
1179
+ const resolverResult = await config.router.execute(request.path, context, request.input);
1230
1180
  const operations = tracker.getOperations();
1231
1181
  const capturedResult = tracker.getCapturedResult();
1232
1182
  if (operations.length !== 1 || !capturedResult) {
1233
- throw requestError(
1234
- "A subscription must execute exactly one ctx.db query",
1235
- 400
1236
- );
1183
+ throw requestError("A subscription must execute exactly one ctx.db query", 400);
1237
1184
  }
1238
1185
  if (!Array.isArray(resolverResult) || resolverResult !== capturedResult.reference || !util.isDeepStrictEqual(resolverResult, capturedResult.snapshot)) {
1239
1186
  throw requestError(
@@ -1400,10 +1347,7 @@ function createReactiveHandler(config) {
1400
1347
  }
1401
1348
  let baseline;
1402
1349
  try {
1403
- baseline = await executeQueryMetadata(
1404
- ddbClient,
1405
- registrationEntry.queryMetadata
1406
- );
1350
+ baseline = await executeQueryMetadata(ddbClient, registrationEntry.queryMetadata);
1407
1351
  const active = {
1408
1352
  ...registrationEntry,
1409
1353
  status: "active",
@@ -1467,9 +1411,7 @@ function createReactiveHandler(config) {
1467
1411
  )
1468
1412
  ]);
1469
1413
  const query2 = queryResponse.Item;
1470
- const currentConnection = SystemSchemas.connections.safeParse(
1471
- connectionResponse.Item
1472
- );
1414
+ const currentConnection = SystemSchemas.connections.safeParse(connectionResponse.Item);
1473
1415
  if (query2?.status !== "active" || query2.generation !== request.generation || query2.registrationId !== request.registrationId || !currentConnection.success || currentConnection.data.status !== "open" || currentConnection.data.connectedAt !== connection.connectedAt) {
1474
1416
  return null;
1475
1417
  }
@@ -1569,11 +1511,7 @@ function createReactiveHandler(config) {
1569
1511
  };
1570
1512
  }
1571
1513
  async function registerConnection(connectionId, context) {
1572
- validateKeyString(
1573
- "connectionId",
1574
- connectionId,
1575
- DYNAMODB_PARTITION_KEY_MAX_BYTES2
1576
- );
1514
+ validateKeyString("connectionId", connectionId, DYNAMODB_PARTITION_KEY_MAX_BYTES2);
1577
1515
  const now = Date.now();
1578
1516
  await docClient.send(
1579
1517
  new libDynamodb.PutCommand({
@@ -1589,11 +1527,7 @@ function createReactiveHandler(config) {
1589
1527
  );
1590
1528
  }
1591
1529
  async function unregisterConnection(connectionId) {
1592
- validateKeyString(
1593
- "connectionId",
1594
- connectionId,
1595
- DYNAMODB_PARTITION_KEY_MAX_BYTES2
1596
- );
1530
+ validateKeyString("connectionId", connectionId, DYNAMODB_PARTITION_KEY_MAX_BYTES2);
1597
1531
  await docClient.send(
1598
1532
  new libDynamodb.UpdateCommand({
1599
1533
  TableName: connectionsTable,
@@ -1664,9 +1598,8 @@ function createReactiveHandler(config) {
1664
1598
  createCaller
1665
1599
  };
1666
1600
  }
1667
- var { compare } = jsonpatch__default.default;
1668
1601
  function generatePatches(oldValue, newValue) {
1669
- const operations = compare(
1602
+ const operations = jsonpatch__default.default.compare(
1670
1603
  oldValue,
1671
1604
  newValue
1672
1605
  );
@@ -1697,13 +1630,7 @@ function isConditionalFailure(error2) {
1697
1630
  return error2 instanceof Error && error2.name === "ConditionalCheckFailedException";
1698
1631
  }
1699
1632
  function createStreamUtils(config) {
1700
- const {
1701
- ddbClient,
1702
- docClient,
1703
- connectionsTable,
1704
- dependenciesTable,
1705
- queriesTable
1706
- } = config;
1633
+ const { ddbClient, docClient, connectionsTable, dependenciesTable, queriesTable } = config;
1707
1634
  let apiClient = config.apiClient;
1708
1635
  function getApiClient() {
1709
1636
  apiClient ??= new clientApigatewaymanagementapi.ApiGatewayManagementApiClient({
@@ -1879,10 +1806,7 @@ function createStreamUtils(config) {
1879
1806
  messageId: crypto.randomUUID(),
1880
1807
  patches
1881
1808
  };
1882
- const pendingDelivery = Buffer.byteLength(
1883
- JSON.stringify(deliveryMessage(query, patchDelivery)),
1884
- "utf8"
1885
- ) > MAX_PATCH_MESSAGE_BYTES ? {
1809
+ const pendingDelivery = Buffer.byteLength(JSON.stringify(deliveryMessage(query, patchDelivery)), "utf8") > MAX_PATCH_MESSAGE_BYTES ? {
1886
1810
  type: "resync",
1887
1811
  generation: query.generation,
1888
1812
  fromVersion,
@@ -2060,17 +1984,14 @@ function assertConnectionId(connectionId) {
2060
1984
  }
2061
1985
  }
2062
1986
  async function mapWithConcurrency(values, concurrency, mapper) {
2063
- const results = new Array(values.length);
1987
+ const results = Array.from({ length: values.length });
2064
1988
  let nextIndex = 0;
2065
- const workers = Array.from(
2066
- { length: Math.min(concurrency, values.length) },
2067
- async () => {
2068
- while (nextIndex < values.length) {
2069
- const index = nextIndex++;
2070
- results[index] = await mapper(values[index], index);
2071
- }
1989
+ const workers = Array.from({ length: Math.min(concurrency, values.length) }, async () => {
1990
+ while (nextIndex < values.length) {
1991
+ const index = nextIndex++;
1992
+ results[index] = await mapper(values[index], index);
2072
1993
  }
2073
- );
1994
+ });
2074
1995
  await Promise.all(workers);
2075
1996
  return results;
2076
1997
  }
@@ -2088,10 +2009,7 @@ function extractRecordAffectedKeys(record) {
2088
2009
  return affectedKeys;
2089
2010
  }
2090
2011
  function subscriptionKey(subscription) {
2091
- return JSON.stringify([
2092
- subscription.connectionId,
2093
- subscription.subscriptionId
2094
- ]);
2012
+ return JSON.stringify([subscription.connectionId, subscription.subscriptionId]);
2095
2013
  }
2096
2014
  function failureIdentifier(record, cause) {
2097
2015
  const itemIdentifier = record.dynamodb?.SequenceNumber;
@@ -2193,9 +2111,7 @@ function createConnectHandler(config) {
2193
2111
  connectedAt: now,
2194
2112
  ttl: Math.floor(now / 1e3) + DEFAULT_CONNECTION_LEASE_SECONDS
2195
2113
  };
2196
- await docClient.send(
2197
- new libDynamodb.PutCommand({ TableName: connectionsTable, Item: connectionEntry })
2198
- );
2114
+ await docClient.send(new libDynamodb.PutCommand({ TableName: connectionsTable, Item: connectionEntry }));
2199
2115
  debug("[stream] Connection established");
2200
2116
  return { statusCode: 200 };
2201
2117
  } catch (error2) {
@@ -2260,18 +2176,9 @@ function assertRequestKey(name, value, maxBytes) {
2260
2176
  return value;
2261
2177
  }
2262
2178
  function createLambdaHandlers(config = {}) {
2263
- const connectionsTable = requiredTableName(
2264
- config.connectionsTableName,
2265
- "CONNECTIONS_TABLE"
2266
- );
2267
- const dependenciesTable = requiredTableName(
2268
- config.dependenciesTableName,
2269
- "DEPENDENCIES_TABLE"
2270
- );
2271
- const queriesTable = requiredTableName(
2272
- config.queriesTableName,
2273
- "QUERIES_TABLE"
2274
- );
2179
+ const connectionsTable = requiredTableName(config.connectionsTableName, "CONNECTIONS_TABLE");
2180
+ const dependenciesTable = requiredTableName(config.dependenciesTableName, "DEPENDENCIES_TABLE");
2181
+ const queriesTable = requiredTableName(config.queriesTableName, "QUERIES_TABLE");
2275
2182
  const apiGatewayEndpoint = config.apiGatewayEndpoint ?? process.env.WEBSOCKET_ENDPOINT ?? "";
2276
2183
  const ddbClient = config.dbConfig?.client ?? new clientDynamodb.DynamoDBClient({
2277
2184
  region: config.dbConfig?.region ?? process.env.AWS_REGION,