easy-ping 0.8.1 → 0.10.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 (118) hide show
  1. package/README.md +17 -11
  2. package/dist/adapter-C3uFRpEN.d.cts +10 -0
  3. package/dist/adapter-DoCRPi9I.d.ts +10 -0
  4. package/dist/adapters/drizzle.cjs +6 -4
  5. package/dist/adapters/drizzle.cjs.map +1 -1
  6. package/dist/adapters/drizzle.js +7 -5
  7. package/dist/adapters/drizzle.js.map +1 -1
  8. package/dist/adapters/mongodb.cjs.map +1 -1
  9. package/dist/adapters/mongodb.d.cts +1 -1
  10. package/dist/adapters/mongodb.d.ts +1 -1
  11. package/dist/adapters/mongodb.js +1 -1
  12. package/dist/adapters/mysql.cjs +7 -4
  13. package/dist/adapters/mysql.cjs.map +1 -1
  14. package/dist/adapters/mysql.d.cts +4 -3
  15. package/dist/adapters/mysql.d.ts +4 -3
  16. package/dist/adapters/mysql.js +3 -3
  17. package/dist/adapters/postgres.cjs +3 -3
  18. package/dist/adapters/postgres.cjs.map +1 -1
  19. package/dist/adapters/postgres.d.cts +2 -2
  20. package/dist/adapters/postgres.d.ts +2 -2
  21. package/dist/adapters/postgres.js +7 -7
  22. package/dist/adapters/postgres.js.map +1 -1
  23. package/dist/adapters/sqlite.cjs +8 -5
  24. package/dist/adapters/sqlite.cjs.map +1 -1
  25. package/dist/adapters/sqlite.d.cts +4 -3
  26. package/dist/adapters/sqlite.d.ts +4 -3
  27. package/dist/adapters/sqlite.js +3 -3
  28. package/dist/chunk-A4HX5YTG.js +413 -0
  29. package/dist/chunk-A4HX5YTG.js.map +1 -0
  30. package/dist/{chunk-JOVIZXO6.js → chunk-CFLVAF2R.js} +20 -6
  31. package/dist/chunk-CFLVAF2R.js.map +1 -0
  32. package/dist/{chunk-2EYKPF5A.js → chunk-CMWOIYVQ.js} +3 -3
  33. package/dist/{chunk-2EYKPF5A.js.map → chunk-CMWOIYVQ.js.map} +1 -1
  34. package/dist/{chunk-HY47TQMY.js → chunk-HSB5T3LD.js} +11 -8
  35. package/dist/chunk-HSB5T3LD.js.map +1 -0
  36. package/dist/{chunk-E4IZ2CQC.js → chunk-JN3UCQBP.js} +2 -2
  37. package/dist/chunk-JN3UCQBP.js.map +1 -0
  38. package/dist/{chunk-3VOQOGDR.js → chunk-O2PBBL5X.js} +24 -6
  39. package/dist/chunk-O2PBBL5X.js.map +1 -0
  40. package/dist/{chunk-M4R77BJD.js → chunk-UM2L26PV.js} +10 -7
  41. package/dist/chunk-UM2L26PV.js.map +1 -0
  42. package/dist/dialect-CvBJSydR.d.cts +10 -0
  43. package/dist/dialect-CvBJSydR.d.ts +10 -0
  44. package/dist/index.cjs +74 -10
  45. package/dist/index.cjs.map +1 -1
  46. package/dist/index.d.cts +4 -2
  47. package/dist/index.d.ts +4 -2
  48. package/dist/index.js +39 -7
  49. package/dist/index.js.map +1 -1
  50. package/dist/node.cjs +14 -2
  51. package/dist/node.cjs.map +1 -1
  52. package/dist/node.js +15 -3
  53. package/dist/node.js.map +1 -1
  54. package/dist/{plugin-C4fikLl7.d.ts → plugin-Bp68CCnd.d.ts} +1 -0
  55. package/dist/{plugin-6S8tpH0n.d.cts → plugin-p4HS8yjS.d.cts} +1 -0
  56. package/dist/plugins/digests.cjs +19 -12
  57. package/dist/plugins/digests.cjs.map +1 -1
  58. package/dist/plugins/digests.d.cts +1 -1
  59. package/dist/plugins/digests.d.ts +1 -1
  60. package/dist/plugins/digests.js +5 -12
  61. package/dist/plugins/digests.js.map +1 -1
  62. package/dist/plugins/mobile-push.cjs +2 -1
  63. package/dist/plugins/mobile-push.cjs.map +1 -1
  64. package/dist/plugins/mobile-push.d.cts +1 -1
  65. package/dist/plugins/mobile-push.d.ts +1 -1
  66. package/dist/plugins/mobile-push.js +3 -2
  67. package/dist/plugins/mobile-push.js.map +1 -1
  68. package/dist/plugins/preferences-client.cjs.map +1 -1
  69. package/dist/plugins/preferences-client.d.cts +1 -1
  70. package/dist/plugins/preferences-client.d.ts +1 -1
  71. package/dist/plugins/preferences-client.js +1 -1
  72. package/dist/plugins/preferences.cjs.map +1 -1
  73. package/dist/plugins/preferences.d.cts +1 -1
  74. package/dist/plugins/preferences.d.ts +1 -1
  75. package/dist/plugins/preferences.js +1 -1
  76. package/dist/plugins/push.cjs.map +1 -1
  77. package/dist/plugins/push.d.cts +1 -1
  78. package/dist/plugins/push.d.ts +1 -1
  79. package/dist/plugins/push.js +1 -1
  80. package/dist/plugins/telegram.cjs +22 -1
  81. package/dist/plugins/telegram.cjs.map +1 -1
  82. package/dist/plugins/telegram.d.cts +1 -1
  83. package/dist/plugins/telegram.d.ts +1 -1
  84. package/dist/plugins/telegram.js +23 -2
  85. package/dist/plugins/telegram.js.map +1 -1
  86. package/dist/providers/expo-push.cjs +1 -1
  87. package/dist/providers/expo-push.cjs.map +1 -1
  88. package/dist/providers/expo-push.js +1 -1
  89. package/dist/providers/expo-push.js.map +1 -1
  90. package/dist/providers/web-push.cjs +3 -7
  91. package/dist/providers/web-push.cjs.map +1 -1
  92. package/dist/providers/web-push.d.cts +1 -1
  93. package/dist/providers/web-push.d.ts +1 -1
  94. package/dist/providers/web-push.js +3 -7
  95. package/dist/providers/web-push.js.map +1 -1
  96. package/dist/react-native.cjs +20 -2
  97. package/dist/react-native.cjs.map +1 -1
  98. package/dist/react-native.js +20 -2
  99. package/dist/react-native.js.map +1 -1
  100. package/dist/react.cjs +20 -2
  101. package/dist/react.cjs.map +1 -1
  102. package/dist/react.js +20 -2
  103. package/dist/react.js.map +1 -1
  104. package/dist/schema.cjs +336 -123
  105. package/dist/schema.cjs.map +1 -1
  106. package/dist/schema.d.cts +28 -2
  107. package/dist/schema.d.ts +28 -2
  108. package/dist/schema.js +3 -3
  109. package/package.json +1 -1
  110. package/dist/adapter-B7R4rXtJ.d.cts +0 -17
  111. package/dist/adapter-B7R4rXtJ.d.ts +0 -17
  112. package/dist/chunk-3VOQOGDR.js.map +0 -1
  113. package/dist/chunk-E4IZ2CQC.js.map +0 -1
  114. package/dist/chunk-HY47TQMY.js.map +0 -1
  115. package/dist/chunk-JOVIZXO6.js.map +0 -1
  116. package/dist/chunk-M4R77BJD.js.map +0 -1
  117. package/dist/chunk-XIDVLYNQ.js +0 -209
  118. package/dist/chunk-XIDVLYNQ.js.map +0 -1
@@ -0,0 +1,10 @@
1
+ type Row = Record<string, unknown>;
2
+ type SqlResult = {
3
+ rows: readonly Row[];
4
+ /** Rows an INSERT/UPDATE/DELETE touched; drivers without RETURNING report through this. */
5
+ affectedRows: number;
6
+ };
7
+ /** The whole driver contract for the MySQL and SQLite adapters. */
8
+ type SqlExec = (text: string, params: readonly unknown[]) => Promise<SqlResult>;
9
+
10
+ export type { SqlExec as S, SqlResult as a };
@@ -0,0 +1,10 @@
1
+ type Row = Record<string, unknown>;
2
+ type SqlResult = {
3
+ rows: readonly Row[];
4
+ /** Rows an INSERT/UPDATE/DELETE touched; drivers without RETURNING report through this. */
5
+ affectedRows: number;
6
+ };
7
+ /** The whole driver contract for the MySQL and SQLite adapters. */
8
+ type SqlExec = (text: string, params: readonly unknown[]) => Promise<SqlResult>;
9
+
10
+ export type { SqlExec as S, SqlResult as a };
package/dist/index.cjs CHANGED
@@ -209,6 +209,7 @@ function createScopedSigner(secret, pluginId, purposes, onViolation) {
209
209
  var STREAM_HIGH_WATER_MARK = 256;
210
210
  var MAX_BODY_BYTES = 64 * 1024;
211
211
  var MAX_READ_IDS = 200;
212
+ var SESSION_ERROR = /* @__PURE__ */ Symbol("session-error");
212
213
  var PRIVATE_HEADERS = {
213
214
  "cache-control": "private, no-store",
214
215
  "x-content-type-options": "nosniff"
@@ -308,13 +309,26 @@ function csrfCheck(request, url, trusted) {
308
309
  function createHandler(deps) {
309
310
  const trustedOrigins = deps.trustedOrigins ?? [];
310
311
  const maxBodyBytes = deps.maxBodyBytes ?? MAX_BODY_BYTES;
312
+ let warnedBadUserId = false;
311
313
  async function resolveUserId(request) {
314
+ let id;
312
315
  try {
313
- return await deps.session.getUserId(request);
316
+ id = await deps.session.getUserId(request);
314
317
  } catch (error) {
315
318
  deps.logger.error("session.getUserId threw", { error });
316
- return "error";
319
+ return SESSION_ERROR;
317
320
  }
321
+ if (id === null || id === void 0) return null;
322
+ if (typeof id !== "string" || id === "") {
323
+ if (!warnedBadUserId) {
324
+ warnedBadUserId = true;
325
+ deps.logger.warn(
326
+ `session.getUserId returned ${id === "" ? "an empty string" : typeof id}; treating the request as unauthenticated. Return a non-empty string or null.`
327
+ );
328
+ }
329
+ return null;
330
+ }
331
+ return id;
318
332
  }
319
333
  async function bearerMatches(request, secret) {
320
334
  if (!secret) return false;
@@ -337,7 +351,7 @@ function createHandler(deps) {
337
351
  if (rejected) return rejected;
338
352
  }
339
353
  const userId = await resolveUserId(request);
340
- if (userId === "error") return empty(500);
354
+ if (userId === SESSION_ERROR) return empty(500);
341
355
  if (userId === null) return empty(401);
342
356
  return harden(await route2.handler({ request, userId, claims: null, params: {} }));
343
357
  }
@@ -378,7 +392,7 @@ function createHandler(deps) {
378
392
  if (rejected) return rejected;
379
393
  }
380
394
  const userId = await resolveUserId(request);
381
- if (userId === "error") return empty(500);
395
+ if (userId === SESSION_ERROR) return empty(500);
382
396
  if (userId === null) return empty(401);
383
397
  if (request.method === "GET" && path === "/events") {
384
398
  if (!deps.events) return empty(404);
@@ -582,12 +596,23 @@ var ESCAPES = {
582
596
  var escapeHtml = (value) => String(value ?? "").replace(/[&<>"']/g, (char) => ESCAPES[char] ?? char);
583
597
 
584
598
  // src/core/rate-limit.ts
599
+ var UNKEYED = "\0unkeyed";
585
600
  var clientAddress = (request) => {
586
601
  const headers = request.headers;
587
602
  return headers.get("cf-connecting-ip") ?? headers.get("x-real-ip") ?? headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? null;
588
603
  };
589
604
  function createRateLimiter(config) {
590
- const keyOf = config.key ?? clientAddress;
605
+ let warned = false;
606
+ const keyOf = config.key ?? ((request) => {
607
+ const address = clientAddress(request);
608
+ if (address !== null) return address;
609
+ if (!warned) {
610
+ warned = true;
611
+ config.onUnkeyed?.();
612
+ }
613
+ return UNKEYED;
614
+ });
615
+ const maxKeys = Math.max(1, config.maxKeys ?? 1e4);
591
616
  const buckets = /* @__PURE__ */ new Map();
592
617
  let lastSweep = 0;
593
618
  return (request) => {
@@ -600,7 +625,12 @@ function createRateLimiter(config) {
600
625
  }
601
626
  let bucket = buckets.get(key);
602
627
  if (!bucket || bucket.resetAt <= now) {
628
+ if (!bucket && buckets.size >= maxKeys) {
629
+ const oldest = buckets.keys().next().value;
630
+ if (oldest !== void 0) buckets.delete(oldest);
631
+ }
603
632
  bucket = { count: 0, resetAt: now + config.windowMs };
633
+ buckets.delete(key);
604
634
  buckets.set(key, bucket);
605
635
  }
606
636
  bucket.count += 1;
@@ -1095,6 +1125,22 @@ function createPluginStore(pluginId, schema, storage, prefix) {
1095
1125
  }
1096
1126
  return declaration;
1097
1127
  }
1128
+ function writable(table) {
1129
+ const declaration = resolve(table);
1130
+ if (declaration.readOnly) {
1131
+ throw new ConfigError(
1132
+ `plugin "${pluginId}" tried to write "${table}", which it declares readOnly.`
1133
+ );
1134
+ }
1135
+ return declaration;
1136
+ }
1137
+ function notEverything(table, where, action) {
1138
+ if (Object.keys(where).length === 0) {
1139
+ throw new ConfigError(
1140
+ `plugin "${pluginId}" called ${action}("${table}", {}), which would touch every row. Pass a condition.`
1141
+ );
1142
+ }
1143
+ }
1098
1144
  function checkFields(table, fields, context) {
1099
1145
  for (const field of fields) {
1100
1146
  const spec = Object.hasOwn(table.fields, field) ? table.fields[field] : void 0;
@@ -1153,7 +1199,7 @@ function createPluginStore(pluginId, schema, storage, prefix) {
1153
1199
  });
1154
1200
  },
1155
1201
  async insert(table, rows) {
1156
- const declaration = resolve(table);
1202
+ const declaration = writable(table);
1157
1203
  for (const row of rows) checkRow(declaration, row, "insert");
1158
1204
  return storage.insertRows(
1159
1205
  qualified(declaration),
@@ -1161,7 +1207,7 @@ function createPluginStore(pluginId, schema, storage, prefix) {
1161
1207
  );
1162
1208
  },
1163
1209
  async upsert(table, rows, options) {
1164
- const declaration = resolve(table);
1210
+ const declaration = writable(table);
1165
1211
  for (const row of rows) checkRow(declaration, row, "upsert");
1166
1212
  checkFields(declaration, options.onConflict, "onConflict");
1167
1213
  return storage.insertRows(
@@ -1171,14 +1217,16 @@ function createPluginStore(pluginId, schema, storage, prefix) {
1171
1217
  );
1172
1218
  },
1173
1219
  async update(table, where, set) {
1174
- const declaration = resolve(table);
1220
+ const declaration = writable(table);
1175
1221
  checkWhere(declaration, where);
1222
+ notEverything(table, where, "update");
1176
1223
  checkRow(declaration, set, "update");
1177
1224
  return storage.updateRows(qualified(declaration), where, serialize(declaration, set));
1178
1225
  },
1179
1226
  async remove(table, where) {
1180
- const declaration = resolve(table);
1227
+ const declaration = writable(table);
1181
1228
  checkWhere(declaration, where);
1229
+ notEverything(table, where, "remove");
1182
1230
  return storage.deleteRows(qualified(declaration), where);
1183
1231
  }
1184
1232
  };
@@ -1257,7 +1305,18 @@ function validate(config, plugins) {
1257
1305
  }
1258
1306
  }
1259
1307
  }
1308
+ const core = new Set(CORE_ROUTES.map((route) => `${route.method} ${route.path}`));
1260
1309
  const routes = /* @__PURE__ */ new Set();
1310
+ for (const plugin of plugins) {
1311
+ for (const route of plugin.routes ?? []) {
1312
+ const signature = `${route.method} ${route.path}`;
1313
+ if (core.has(signature)) {
1314
+ throw new ConfigError(
1315
+ `plugin "${plugin.id}" registers "${signature}", which is a core route. Pick another path.`
1316
+ );
1317
+ }
1318
+ }
1319
+ }
1261
1320
  for (const plugin of plugins) {
1262
1321
  for (const route of plugin.routes ?? []) {
1263
1322
  const signature = `${route.method} ${route.path}`;
@@ -1374,7 +1433,12 @@ function easyPing(config) {
1374
1433
  trustedOrigins: config.trustedOrigins,
1375
1434
  onRequest: config.onRequest,
1376
1435
  maxBodyBytes: config.maxBodyBytes,
1377
- rateLimit: config.rateLimit ? createRateLimiter(config.rateLimit) : void 0,
1436
+ rateLimit: config.rateLimit ? createRateLimiter({
1437
+ onUnkeyed: () => logger.warn(
1438
+ "rateLimit: a request arrived with no cf-connecting-ip, x-real-ip or x-forwarded-for header, so every such request shares one bucket. Put the app behind a proxy that sets one of them, or pass rateLimit.key (a session or API key)."
1439
+ ),
1440
+ ...config.rateLimit
1441
+ }) : void 0,
1378
1442
  cronMaxSweeps: config.cron?.maxSweeps,
1379
1443
  ...config.cron?.secret ? { cronSecret: config.cron.secret } : {},
1380
1444
  ...config.machineSecret ?? config.cron?.secret ? { machineSecret: config.machineSecret ?? config.cron?.secret } : {}