@lsync/server 0.0.1 → 0.0.3

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/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
- import { n as webSocketAttachmentSchema, t as router } from "./router-C4eYfMV7.mjs";
2
- import { and, compileReadExpression, compileReadExpression as compileReadExpression$1, eq, field, gt, gte, inArray, lt, lte, matchesReadPredicate, matchesReadPredicate as matchesReadPredicate$1, ne, not, or, parseClientRpcRequest, readExpressionReferences, readExpressionReferences as readExpressionReferences$1, readExpressionRow, readExpressionRow as readExpressionRow$1, readRpcId, sendServerMessage, val } from "@lsync/transport";
1
+ import { n as webSocketAttachmentSchema, t as router } from "./router-CZnnFmXh.mjs";
2
+ import { ProtocolError, and, compileReadExpression, compileReadExpression as compileReadExpression$1, eq, field, gt, gte, inArray, lt, lte, matchesReadPredicate, matchesReadPredicate as matchesReadPredicate$1, ne, not, or, parseClientMessage, protocolErrorCodeSchema, readExpressionReferences, readExpressionReferences as readExpressionReferences$1, readExpressionRow, readExpressionRow as readExpressionRow$1, readMessageId, sendServerMessage, val } from "@lsync/transport";
3
3
  import sqlTag, { join, raw } from "sql-template-tag";
4
4
  import { z } from "zod";
5
5
  //#region src/access-expression.ts
@@ -242,7 +242,7 @@ function authorizeWriteBatch(batch, collections, auth, store) {
242
242
  }
243
243
  function authorizeApiCall(handler, name, input, auth) {
244
244
  if (!handler) return;
245
- const row = asRecord(input) ?? {};
245
+ const row = asRecord$1(input) ?? {};
246
246
  const decision = reduceAccessExpression(handler({
247
247
  auth,
248
248
  input: readExpressionRow$1()
@@ -345,16 +345,16 @@ function writeAccessHandler(access, type) {
345
345
  return access[type] ?? access.write ?? access.read;
346
346
  }
347
347
  function writeAccessRow(update, store) {
348
- if (update.type === "insert") return asRecord(update.value);
349
- const existing = asRecord(update.previousValue) ?? store.read(update.collection, update.key);
348
+ if (update.type === "insert") return asRecord$1(update.value);
349
+ const existing = asRecord$1(update.previousValue) ?? store.read(update.collection, update.key);
350
350
  if (update.type === "delete") return existing;
351
- const next = asRecord(update.value);
351
+ const next = asRecord$1(update.value);
352
352
  return existing && next ? {
353
353
  ...existing,
354
354
  ...next
355
355
  } : next ?? existing;
356
356
  }
357
- function asRecord(value) {
357
+ function asRecord$1(value) {
358
358
  return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
359
359
  }
360
360
  //#endregion
@@ -438,7 +438,7 @@ function selectRows(sql, table, options) {
438
438
  const where = [sqlTag`path = ${options.scope}`];
439
439
  where.push(...options.filters.map((filter) => filterSql(filter)));
440
440
  if (options.predicate) where.push(predicateSql(options.predicate));
441
- const order = options.orderBy.length > 0 ? join(options.orderBy.map((item) => orderBySql(item))) : sqlTag`key`;
441
+ const order = options.orderBy.length > 0 ? sqlTag`${join(options.orderBy.map((item) => orderBySql(item)))}, key` : sqlTag`key`;
442
442
  return execSql(sql, sqlTag`
443
443
  SELECT key, value FROM ${identifierSql(table)}
444
444
  WHERE ${join(where, " AND ")}
@@ -623,39 +623,9 @@ function isRecord(value) {
623
623
  }
624
624
  //#endregion
625
625
  //#region src/collection-builder.ts
626
- function collectionConfigsBuilder() {
627
- return new CollectionConfigsBuilder({});
628
- }
629
626
  function collectionBuilder() {
630
627
  return new CollectionBuilder({});
631
628
  }
632
- const Collections = {
633
- builder: collectionConfigsBuilder,
634
- collection: collectionBuilder
635
- };
636
- var CollectionConfigsBuilder = class CollectionConfigsBuilder {
637
- collections;
638
- constructor(collections) {
639
- this.collections = Object.freeze({ ...collections });
640
- }
641
- collection(name, configure) {
642
- const builder = configure(new CollectionBuilder({ path: rootPathForName(name) }));
643
- return new CollectionConfigsBuilder({
644
- ...this.collections,
645
- ...builder.entries()
646
- });
647
- }
648
- path(path, configure) {
649
- const builder = configure(new CollectionBuilder({ path }));
650
- return new CollectionConfigsBuilder({
651
- ...this.collections,
652
- ...builder.entries()
653
- });
654
- }
655
- build() {
656
- return { ...this.collections };
657
- }
658
- };
659
629
  var CollectionBuilder = class CollectionBuilder {
660
630
  state;
661
631
  constructor(state) {
@@ -769,26 +739,43 @@ function singularize(value) {
769
739
  //#endregion
770
740
  //#region src/definition-builder.ts
771
741
  function collectionShardOptionsFrom(definitions) {
772
- return new CollectionShardDefinitionBuilder(definitions.collections, {});
742
+ return new CollectionShardDefinitionBuilder(definitions.collections, {}, {});
773
743
  }
774
744
  var CollectionShardDefinitionBuilder = class CollectionShardDefinitionBuilder {
775
745
  definitions;
776
746
  overrides;
777
- constructor(definitions, overrides) {
747
+ options;
748
+ constructor(definitions, overrides, options) {
778
749
  this.definitions = definitions;
779
750
  this.overrides = overrides;
751
+ this.options = options;
752
+ }
753
+ limits(limits) {
754
+ return new CollectionShardDefinitionBuilder(this.definitions, this.overrides, {
755
+ ...this.options,
756
+ limits
757
+ });
758
+ }
759
+ rateLimit(rateLimit) {
760
+ return new CollectionShardDefinitionBuilder(this.definitions, this.overrides, {
761
+ ...this.options,
762
+ rateLimit
763
+ });
780
764
  }
781
765
  collection(path, configure) {
782
766
  const override = configure(new ServerCollectionBuilder({})).toOverride();
783
767
  return new CollectionShardDefinitionBuilder(this.definitions, {
784
768
  ...this.overrides,
785
769
  [path]: override
786
- });
770
+ }, this.options);
787
771
  }
788
772
  build() {
789
773
  const collections = {};
790
774
  for (const [name, definition] of Object.entries(this.definitions)) addDefinition(collections, definition, name, this.overrides);
791
- return { collections };
775
+ return {
776
+ ...this.options,
777
+ collections
778
+ };
792
779
  }
793
780
  };
794
781
  var ServerCollectionBuilder = class ServerCollectionBuilder {
@@ -874,22 +861,25 @@ function accessConfig(override) {
874
861
  }
875
862
  //#endregion
876
863
  //#region src/durable-object-rpc.ts
877
- function callRouter(caller, request) {
878
- if (request.method === "query" && request.params.path === "read") return caller.read(request.params.input.json);
879
- if (request.method === "query" && request.params.path === "changes") return caller.changes(request.params.input.json);
880
- if (request.method === "subscribe") return caller.subscribe(request.params.input.json);
881
- if (request.method === "unsubscribe") return caller.unsubscribe(request.params.input.json);
882
- if (request.method !== "mutation") throw new Error("Unsupported operation");
883
- if (request.params.path === "push") return caller.push(request.params.input.json);
884
- if (request.params.path === "api") return caller.api(request.params.input.json);
885
- throw new Error("Unsupported operation");
864
+ async function callRouter(caller, request) {
865
+ switch (request.type) {
866
+ case "read": return caller.read(request.input);
867
+ case "push": return caller.push(request.input);
868
+ case "changes": return caller.changes(request.input);
869
+ case "api": return caller.api(request.input);
870
+ case "subscribe": return caller.subscribe(request.input);
871
+ case "unsubscribe": return caller.unsubscribe(request.input);
872
+ }
873
+ }
874
+ function isResyncRequired(result) {
875
+ return typeof result === "object" && result !== null && result.type === "resyncRequired";
886
876
  }
887
877
  //#endregion
888
878
  //#region src/history-watermarks.ts
889
879
  const COLLECTION_WATERMARKS_TABLE = "_lsync_collection_watermarks";
890
880
  function ensureCollectionWatermarkTable(sql) {
891
881
  sql.exec(`
892
- CREATE TABLE IF NOT EXISTS ${quoteIdentifier$1(COLLECTION_WATERMARKS_TABLE)} (
882
+ CREATE TABLE IF NOT EXISTS ${quoteIdentifier$2(COLLECTION_WATERMARKS_TABLE)} (
893
883
  scope TEXT PRIMARY KEY,
894
884
  collection TEXT NOT NULL,
895
885
  latest_sequence INTEGER NOT NULL
@@ -898,7 +888,7 @@ function ensureCollectionWatermarkTable(sql) {
898
888
  }
899
889
  function recordCollectionWatermark(sql, collection, scope, sequence) {
900
890
  sql.exec(`
901
- INSERT INTO ${quoteIdentifier$1(COLLECTION_WATERMARKS_TABLE)}
891
+ INSERT INTO ${quoteIdentifier$2(COLLECTION_WATERMARKS_TABLE)}
902
892
  (scope, collection, latest_sequence)
903
893
  VALUES (?, ?, ?)
904
894
  ON CONFLICT(scope) DO UPDATE SET
@@ -908,21 +898,66 @@ function recordCollectionWatermark(sql, collection, scope, sequence) {
908
898
  }
909
899
  function readCollectionWatermark(sql, scope) {
910
900
  return sql.exec(`
911
- SELECT latest_sequence FROM ${quoteIdentifier$1(COLLECTION_WATERMARKS_TABLE)}
901
+ SELECT latest_sequence FROM ${quoteIdentifier$2(COLLECTION_WATERMARKS_TABLE)}
912
902
  WHERE scope = ?
913
903
  `, scope).toArray()[0]?.latest_sequence ?? 0;
914
904
  }
905
+ function quoteIdentifier$2(identifier) {
906
+ return `"${identifier.replaceAll(`"`, `""`)}"`;
907
+ }
908
+ //#endregion
909
+ //#region src/mutation-dedup.ts
910
+ const APPLIED_UPDATES_TABLE = "_lsync_applied_updates";
911
+ function deduplicateMutations(sql, batch) {
912
+ ensureAppliedUpdatesTable(sql);
913
+ const seen = /* @__PURE__ */ new Map();
914
+ return { updates: batch.updates.filter((update) => {
915
+ const payload = JSON.stringify(update);
916
+ const priorPayload = seen.get(update.id) ?? readAppliedPayload(sql, update.id);
917
+ if (priorPayload === void 0) {
918
+ seen.set(update.id, payload);
919
+ return true;
920
+ }
921
+ if (priorPayload !== payload) throw new Error(`Mutation id reused with a different payload: ${update.id}`);
922
+ return false;
923
+ }) };
924
+ }
925
+ function recordAppliedMutations(sql, updates) {
926
+ for (const update of updates) {
927
+ const { sequence, serverCreatedAt, ...original } = update;
928
+ sql.exec(`INSERT INTO ${quoteIdentifier$1(APPLIED_UPDATES_TABLE)} (update_id, payload, sequence) VALUES (?, ?, ?)`, update.id, JSON.stringify(original), sequence);
929
+ }
930
+ }
931
+ function ensureAppliedUpdatesTable(sql) {
932
+ sql.exec(`
933
+ CREATE TABLE IF NOT EXISTS ${quoteIdentifier$1(APPLIED_UPDATES_TABLE)} (
934
+ update_id TEXT PRIMARY KEY,
935
+ payload TEXT NOT NULL,
936
+ sequence INTEGER NOT NULL
937
+ )
938
+ `);
939
+ }
940
+ function readAppliedPayload(sql, updateId) {
941
+ return sql.exec(`SELECT payload FROM ${quoteIdentifier$1(APPLIED_UPDATES_TABLE)} WHERE update_id = ?`, updateId).toArray()[0]?.payload;
942
+ }
915
943
  function quoteIdentifier$1(identifier) {
916
944
  return `"${identifier.replaceAll(`"`, `""`)}"`;
917
945
  }
918
946
  //#endregion
919
947
  //#region src/history.ts
920
948
  const CHANGES_TABLE = "_lsync_changes";
949
+ const SQLITE_JSON_CONFLICT_POLICY = "server-sequence-last-write-wins";
921
950
  function persistSQLiteJsonBatchWithHistory(sql, batch, collections = {}, options = {}) {
922
951
  ensureSQLiteJsonTables(sql, collections);
923
952
  ensureChangesTable(sql);
924
- applySQLiteJsonBatch(sql, batch, collections);
925
- const updates = batch.updates.map((update) => appendChange(sql, update, collections));
953
+ const pending = deduplicateMutations(sql, batch);
954
+ if (pending.updates.length === 0) return {
955
+ updates: [],
956
+ watermark: readWatermark(sql)
957
+ };
958
+ applySQLiteJsonBatch(sql, pending, collections);
959
+ const updates = pending.updates.map((update) => appendChange(sql, update, collections));
960
+ recordAppliedMutations(sql, updates);
926
961
  const watermark = updates.at(-1)?.sequence ?? readWatermark(sql);
927
962
  pruneChanges(sql, watermark, options.maxChanges ?? 1e4);
928
963
  return {
@@ -952,11 +987,19 @@ function readSQLiteJsonChanges(sql, query, collections = {}) {
952
987
  };
953
988
  const limit = query.limit ?? 1e3;
954
989
  const rows = selectChanges(sql, requested, limit + 1);
990
+ const page = rows.slice(0, limit);
991
+ const cursors = Object.fromEntries(requested.map((item) => [item.collection, item.sequence]));
992
+ for (const row of page) {
993
+ const item = requested.find((candidate) => candidate.scope === row.scope);
994
+ if (item) cursors[item.collection] = row.sequence;
995
+ }
996
+ if (rows.length <= limit) for (const item of requested) cursors[item.collection] = watermark;
955
997
  return {
956
998
  type: "changes",
957
- updates: rows.slice(0, limit).map(rowToUpdate),
999
+ updates: page.map(rowToUpdate),
958
1000
  watermark,
959
- hasMore: rows.length > limit
1001
+ hasMore: rows.length > limit,
1002
+ cursors
960
1003
  };
961
1004
  }
962
1005
  function ensureChangesTable(sql) {
@@ -1003,7 +1046,7 @@ function selectChanges(sql, requested, limit) {
1003
1046
  const bindings = requested.flatMap((item) => [item.scope, item.sequence]);
1004
1047
  bindings.push(limit);
1005
1048
  return sql.exec(`
1006
- SELECT sequence, update_id, collection, "key", type, value, previous_value, client_id,
1049
+ SELECT sequence, update_id, collection, scope, "key", type, value, previous_value, client_id,
1007
1050
  client_created_at, server_created_at
1008
1051
  FROM ${quoteIdentifier(CHANGES_TABLE)}
1009
1052
  WHERE ${requested.map(() => "(scope = ? AND sequence > ?)").join(" OR ")}
@@ -1051,21 +1094,69 @@ function quoteIdentifier(identifier) {
1051
1094
  }
1052
1095
  //#endregion
1053
1096
  //#region src/messages.ts
1054
- function sendRpcResult(ws, id, result) {
1097
+ function sendResult(ws, id, payload) {
1055
1098
  sendServerMessage(ws, {
1099
+ version: 1,
1056
1100
  id,
1057
- result: {
1058
- type: "data",
1059
- data: { json: result }
1060
- }
1101
+ type: "result",
1102
+ payload
1061
1103
  });
1062
1104
  }
1063
- function sendRpcError(ws, id, error) {
1105
+ function sendProtocolError(ws, id, error) {
1064
1106
  sendServerMessage(ws, {
1107
+ version: 1,
1065
1108
  id,
1066
- error: { message: error instanceof Error ? error.message : "Unknown error" }
1109
+ type: "error",
1110
+ error: protocolErrorFrom(error)
1067
1111
  });
1068
1112
  }
1113
+ function protocolErrorFrom(error) {
1114
+ if (error instanceof ProtocolError) return {
1115
+ code: error.code,
1116
+ message: error.message,
1117
+ ...error.details !== void 0 ? { details: error.details } : {}
1118
+ };
1119
+ if (error instanceof z.ZodError) return {
1120
+ code: "VALIDATION_FAILED",
1121
+ message: "The request failed validation",
1122
+ details: error.issues
1123
+ };
1124
+ const message = error instanceof Error ? error.message : "Unknown protocol error";
1125
+ if (message.toLowerCase().includes("access denied")) return {
1126
+ code: "AUTH_DENIED",
1127
+ message
1128
+ };
1129
+ if (message.toLowerCase().includes("unknown collection")) return {
1130
+ code: "VALIDATION_FAILED",
1131
+ message
1132
+ };
1133
+ if (message.toLowerCase().includes("maximum subscriptions")) return {
1134
+ code: "CONFLICT",
1135
+ message
1136
+ };
1137
+ if (message.toLowerCase().includes("rate limit")) return {
1138
+ code: "RATE_LIMITED",
1139
+ message
1140
+ };
1141
+ if (message.toLowerCase().includes("unknown api path")) return {
1142
+ code: "NOT_FOUND",
1143
+ message
1144
+ };
1145
+ const candidate = asRecord(error);
1146
+ const code = protocolErrorCodeSchema.safeParse(candidate?.code);
1147
+ if (code.success) return {
1148
+ code: code.data,
1149
+ message,
1150
+ ...candidate?.details !== void 0 ? { details: candidate.details } : {}
1151
+ };
1152
+ return {
1153
+ code: "INTERNAL_ERROR",
1154
+ message
1155
+ };
1156
+ }
1157
+ function asRecord(value) {
1158
+ return typeof value === "object" && value !== null ? value : void 0;
1159
+ }
1069
1160
  //#endregion
1070
1161
  //#region src/socket-attachment.ts
1071
1162
  function webSocketAttachment(ws) {
@@ -1097,6 +1188,38 @@ function updateSocketSubscriptions(ws, collection, normalize, update) {
1097
1188
  };
1098
1189
  }
1099
1190
  //#endregion
1191
+ //#region src/safeguards.ts
1192
+ const DEFAULT_MAX_SUBSCRIPTIONS_PER_WEBSOCKET = 100;
1193
+ function assertSubscriptionAllowed(ws, collection, limits) {
1194
+ const attachment = webSocketAttachment(ws);
1195
+ if (!attachment) throw new Error("Missing WebSocket attachment");
1196
+ const maximum = limits?.maxSubscriptionsPerWebSocket ?? 100;
1197
+ if (maximum === false || attachment.subscriptions.includes(collection)) return;
1198
+ assertPositiveInteger(maximum, "maxSubscriptionsPerWebSocket");
1199
+ if (attachment.subscriptions.length >= maximum) throw new Error(`Maximum subscriptions per WebSocket exceeded (${maximum})`);
1200
+ }
1201
+ async function enforceRateLimit(ws, env, rateLimit) {
1202
+ if (!rateLimit) return;
1203
+ const attachment = webSocketAttachment(ws);
1204
+ if (!await rateLimit({
1205
+ auth: webSocketAuth(ws),
1206
+ clientId: attachment?.clientId ?? "unknown",
1207
+ env
1208
+ })) throw new Error("Rate limit exceeded");
1209
+ }
1210
+ function cloudflareRateLimiter(options = {}) {
1211
+ const bindingName = options.binding ?? "RATE_LIMITER";
1212
+ return async (context) => {
1213
+ const binding = context.env[bindingName];
1214
+ if (!binding?.limit) throw new Error(`Missing Cloudflare rate limit binding: ${bindingName}`);
1215
+ const key = options.key?.(context) ?? context.clientId;
1216
+ return (await binding.limit({ key })).success;
1217
+ };
1218
+ }
1219
+ function assertPositiveInteger(value, name) {
1220
+ if (!Number.isInteger(value) || value <= 0) throw new Error(`${name} must be a positive integer or false`);
1221
+ }
1222
+ //#endregion
1100
1223
  //#region src/access-invalidation.ts
1101
1224
  function invalidatesDependency(update, dependencies) {
1102
1225
  return dependencies.some((dependency) => {
@@ -1185,7 +1308,8 @@ var CollectionShardDurableObject = class {
1185
1308
  }
1186
1309
  async webSocketMessage(ws, message) {
1187
1310
  try {
1188
- const request = parseClientRpcRequest(message);
1311
+ await enforceRateLimit(ws, this.env, this.options.rateLimit);
1312
+ const request = parseClientMessage(message);
1189
1313
  const result = await callRouter(router.createCaller({
1190
1314
  shardId: this.shardId(),
1191
1315
  validate: (input) => this.validate(input),
@@ -1198,9 +1322,17 @@ var CollectionShardDurableObject = class {
1198
1322
  changes: (input) => this.readChanges(input, webSocketAuth(ws)),
1199
1323
  callApi: (input) => this.callApi(ws, input)
1200
1324
  }), request);
1201
- sendRpcResult(ws, request.id, result);
1325
+ if (request.type === "changes" && isResyncRequired(result)) {
1326
+ sendProtocolError(ws, request.id, {
1327
+ code: "RESYNC_REQUIRED",
1328
+ message: "The requested change history is no longer available",
1329
+ details: result
1330
+ });
1331
+ return;
1332
+ }
1333
+ sendResult(ws, request.id, result);
1202
1334
  } catch (error) {
1203
- sendRpcError(ws, readRpcId(message), error);
1335
+ sendProtocolError(ws, readMessageId(message), error);
1204
1336
  }
1205
1337
  }
1206
1338
  publish(batch) {
@@ -1212,17 +1344,16 @@ var CollectionShardDurableObject = class {
1212
1344
  };
1213
1345
  for (const socket of this.state.getWebSockets()) {
1214
1346
  const updates = this.subscribedUpdates(socket, batch);
1215
- const invalidations = this.subscribedInvalidations(socket, batch);
1347
+ const attachment = webSocketAttachment(socket);
1348
+ const invalidations = attachment ? subscribedInvalidations(attachment.subscriptions, batch, this.options.collections, webSocketAuth(socket), this.accessStore()) : [];
1216
1349
  if (updates.length === 0 && invalidations.length === 0) continue;
1217
1350
  sendServerMessage(socket, {
1218
- method: "subscription",
1219
- params: {
1220
- path: "updates",
1221
- input: { json: {
1222
- ...payload,
1223
- ...invalidations.length > 0 ? { invalidations } : {},
1224
- updates
1225
- } }
1351
+ version: 1,
1352
+ type: "updates",
1353
+ payload: {
1354
+ ...payload,
1355
+ ...invalidations.length > 0 ? { invalidations } : {},
1356
+ updates
1226
1357
  }
1227
1358
  });
1228
1359
  }
@@ -1253,6 +1384,7 @@ var CollectionShardDurableObject = class {
1253
1384
  };
1254
1385
  }
1255
1386
  subscribe(ws, input) {
1387
+ assertSubscriptionAllowed(ws, normalizeCollection(input.collection, this.options.collections), this.options.limits);
1256
1388
  return this.updateSubscriptions(ws, input.collection, (subscriptions, collection) => {
1257
1389
  subscriptions.add(collection);
1258
1390
  });
@@ -1284,8 +1416,9 @@ var CollectionShardDurableObject = class {
1284
1416
  return handler(args);
1285
1417
  }
1286
1418
  mutate(batch, auth = {}) {
1287
- this.validate(batch);
1288
- authorizeWriteBatch(batch, this.options.collections, auth, this.accessStore());
1419
+ const pending = deduplicateMutations(this.state.storage.sql, batch);
1420
+ this.validate(pending);
1421
+ authorizeWriteBatch(pending, this.options.collections, auth, this.accessStore());
1289
1422
  const persisted = this.persist(batch);
1290
1423
  this.publish(persisted);
1291
1424
  return {
@@ -1314,11 +1447,6 @@ var CollectionShardDurableObject = class {
1314
1447
  }] : [];
1315
1448
  });
1316
1449
  }
1317
- subscribedInvalidations(ws, batch) {
1318
- const attachment = webSocketAttachment(ws);
1319
- if (!attachment || attachment.subscriptions.length === 0) return [];
1320
- return subscribedInvalidations(attachment.subscriptions, batch, this.options.collections, webSocketAuth(ws), this.accessStore());
1321
- }
1322
1450
  accessStore() {
1323
1451
  return { read: (collection, key) => readSQLiteJsonRow(this.state.storage.sql, collection, key, this.options.collections) };
1324
1452
  }
@@ -1341,6 +1469,6 @@ function createWorkerHandler(options = {}) {
1341
1469
  } };
1342
1470
  }
1343
1471
  //#endregion
1344
- export { CollectionBuilder, CollectionConfigsBuilder, CollectionShardDefinitionBuilder, CollectionShardDurableObject, Collections, ServerCollectionBuilder, and, applySQLiteJsonBatch, authorizeApiCall, authorizeReadQuery, authorizeWriteBatch, collectionApiHandlers, collectionBuilder, collectionConfigsBuilder, collectionScope, compileReadExpression, createWorkerHandler, defaultCollectionApiPath, ensureSQLiteJsonTables, eq, field, gt, gte, inArray, isCollectionPattern, lt, lte, matchesReadPredicate, ne, not, or, persistSQLiteJsonBatchWithHistory, readAccessDecision, readExpressionReferences, readExpressionRow, readSQLiteJsonChanges, readSQLiteJsonRow, readSQLiteJsonRows, resolveCollection, router, sqliteJsonTable, val, validateBatch, visibleUpdateForAuth };
1472
+ export { CollectionShardDurableObject, DEFAULT_MAX_SUBSCRIPTIONS_PER_WEBSOCKET, SQLITE_JSON_CONFLICT_POLICY, and, applySQLiteJsonBatch, authorizeApiCall, authorizeReadQuery, authorizeWriteBatch, cloudflareRateLimiter, collectionApiHandlers, collectionScope, collectionShardOptionsFrom, compileReadExpression, createWorkerHandler, defaultCollectionApiPath, ensureSQLiteJsonTables, eq, field, gt, gte, inArray, isCollectionPattern, lt, lte, matchesReadPredicate, ne, not, or, persistSQLiteJsonBatchWithHistory, readAccessDecision, readExpressionReferences, readExpressionRow, readSQLiteJsonChanges, readSQLiteJsonRow, readSQLiteJsonRows, resolveCollection, router, sqliteJsonTable, val, validateBatch, visibleUpdateForAuth };
1345
1473
 
1346
1474
  //# sourceMappingURL=index.mjs.map