easy-ping 0.9.0 → 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 (102) hide show
  1. package/dist/adapters/drizzle.cjs +6 -4
  2. package/dist/adapters/drizzle.cjs.map +1 -1
  3. package/dist/adapters/drizzle.js +7 -5
  4. package/dist/adapters/drizzle.js.map +1 -1
  5. package/dist/adapters/mongodb.cjs.map +1 -1
  6. package/dist/adapters/mongodb.d.cts +1 -1
  7. package/dist/adapters/mongodb.d.ts +1 -1
  8. package/dist/adapters/mongodb.js +1 -1
  9. package/dist/adapters/mysql.cjs.map +1 -1
  10. package/dist/adapters/mysql.d.cts +1 -1
  11. package/dist/adapters/mysql.d.ts +1 -1
  12. package/dist/adapters/mysql.js +2 -2
  13. package/dist/adapters/postgres.cjs +3 -3
  14. package/dist/adapters/postgres.cjs.map +1 -1
  15. package/dist/adapters/postgres.d.cts +2 -2
  16. package/dist/adapters/postgres.d.ts +2 -2
  17. package/dist/adapters/postgres.js +5 -5
  18. package/dist/adapters/postgres.js.map +1 -1
  19. package/dist/adapters/sqlite.cjs.map +1 -1
  20. package/dist/adapters/sqlite.d.cts +1 -1
  21. package/dist/adapters/sqlite.d.ts +1 -1
  22. package/dist/adapters/sqlite.js +2 -2
  23. package/dist/{chunk-EROWLUL4.js → chunk-A4HX5YTG.js} +5 -3
  24. package/dist/chunk-A4HX5YTG.js.map +1 -0
  25. package/dist/{chunk-JOVIZXO6.js → chunk-CFLVAF2R.js} +20 -6
  26. package/dist/chunk-CFLVAF2R.js.map +1 -0
  27. package/dist/{chunk-2EYKPF5A.js → chunk-CMWOIYVQ.js} +3 -3
  28. package/dist/{chunk-2EYKPF5A.js.map → chunk-CMWOIYVQ.js.map} +1 -1
  29. package/dist/{chunk-E4IZ2CQC.js → chunk-JN3UCQBP.js} +2 -2
  30. package/dist/chunk-JN3UCQBP.js.map +1 -0
  31. package/dist/{chunk-3VOQOGDR.js → chunk-O2PBBL5X.js} +24 -6
  32. package/dist/chunk-O2PBBL5X.js.map +1 -0
  33. package/dist/index.cjs +74 -10
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +4 -2
  36. package/dist/index.d.ts +4 -2
  37. package/dist/index.js +39 -7
  38. package/dist/index.js.map +1 -1
  39. package/dist/node.cjs +14 -2
  40. package/dist/node.cjs.map +1 -1
  41. package/dist/node.js +15 -3
  42. package/dist/node.js.map +1 -1
  43. package/dist/{plugin-C4fikLl7.d.ts → plugin-Bp68CCnd.d.ts} +1 -0
  44. package/dist/{plugin-6S8tpH0n.d.cts → plugin-p4HS8yjS.d.cts} +1 -0
  45. package/dist/plugins/digests.cjs +1 -1
  46. package/dist/plugins/digests.cjs.map +1 -1
  47. package/dist/plugins/digests.d.cts +1 -1
  48. package/dist/plugins/digests.d.ts +1 -1
  49. package/dist/plugins/digests.js +1 -1
  50. package/dist/plugins/digests.js.map +1 -1
  51. package/dist/plugins/mobile-push.cjs +2 -1
  52. package/dist/plugins/mobile-push.cjs.map +1 -1
  53. package/dist/plugins/mobile-push.d.cts +1 -1
  54. package/dist/plugins/mobile-push.d.ts +1 -1
  55. package/dist/plugins/mobile-push.js +3 -2
  56. package/dist/plugins/mobile-push.js.map +1 -1
  57. package/dist/plugins/preferences-client.cjs.map +1 -1
  58. package/dist/plugins/preferences-client.d.cts +1 -1
  59. package/dist/plugins/preferences-client.d.ts +1 -1
  60. package/dist/plugins/preferences-client.js +1 -1
  61. package/dist/plugins/preferences.cjs.map +1 -1
  62. package/dist/plugins/preferences.d.cts +1 -1
  63. package/dist/plugins/preferences.d.ts +1 -1
  64. package/dist/plugins/preferences.js +1 -1
  65. package/dist/plugins/push.cjs.map +1 -1
  66. package/dist/plugins/push.d.cts +1 -1
  67. package/dist/plugins/push.d.ts +1 -1
  68. package/dist/plugins/push.js +1 -1
  69. package/dist/plugins/telegram.cjs +22 -1
  70. package/dist/plugins/telegram.cjs.map +1 -1
  71. package/dist/plugins/telegram.d.cts +1 -1
  72. package/dist/plugins/telegram.d.ts +1 -1
  73. package/dist/plugins/telegram.js +23 -2
  74. package/dist/plugins/telegram.js.map +1 -1
  75. package/dist/providers/expo-push.cjs +1 -1
  76. package/dist/providers/expo-push.cjs.map +1 -1
  77. package/dist/providers/expo-push.js +1 -1
  78. package/dist/providers/expo-push.js.map +1 -1
  79. package/dist/providers/web-push.cjs +3 -7
  80. package/dist/providers/web-push.cjs.map +1 -1
  81. package/dist/providers/web-push.d.cts +1 -1
  82. package/dist/providers/web-push.d.ts +1 -1
  83. package/dist/providers/web-push.js +3 -7
  84. package/dist/providers/web-push.js.map +1 -1
  85. package/dist/react-native.cjs +20 -2
  86. package/dist/react-native.cjs.map +1 -1
  87. package/dist/react-native.js +20 -2
  88. package/dist/react-native.js.map +1 -1
  89. package/dist/react.cjs +20 -2
  90. package/dist/react.cjs.map +1 -1
  91. package/dist/react.js +20 -2
  92. package/dist/react.js.map +1 -1
  93. package/dist/schema.cjs +3 -1
  94. package/dist/schema.cjs.map +1 -1
  95. package/dist/schema.d.cts +2 -1
  96. package/dist/schema.d.ts +2 -1
  97. package/dist/schema.js +1 -1
  98. package/package.json +1 -1
  99. package/dist/chunk-3VOQOGDR.js.map +0 -1
  100. package/dist/chunk-E4IZ2CQC.js.map +0 -1
  101. package/dist/chunk-EROWLUL4.js.map +0 -1
  102. package/dist/chunk-JOVIZXO6.js.map +0 -1
@@ -68,6 +68,22 @@ function createPluginStore(pluginId, schema, storage, prefix) {
68
68
  }
69
69
  return declaration;
70
70
  }
71
+ function writable(table) {
72
+ const declaration = resolve(table);
73
+ if (declaration.readOnly) {
74
+ throw new ConfigError(
75
+ `plugin "${pluginId}" tried to write "${table}", which it declares readOnly.`
76
+ );
77
+ }
78
+ return declaration;
79
+ }
80
+ function notEverything(table, where, action) {
81
+ if (Object.keys(where).length === 0) {
82
+ throw new ConfigError(
83
+ `plugin "${pluginId}" called ${action}("${table}", {}), which would touch every row. Pass a condition.`
84
+ );
85
+ }
86
+ }
71
87
  function checkFields(table, fields, context) {
72
88
  for (const field of fields) {
73
89
  const spec = Object.hasOwn(table.fields, field) ? table.fields[field] : void 0;
@@ -126,7 +142,7 @@ function createPluginStore(pluginId, schema, storage, prefix) {
126
142
  });
127
143
  },
128
144
  async insert(table, rows) {
129
- const declaration = resolve(table);
145
+ const declaration = writable(table);
130
146
  for (const row of rows) checkRow(declaration, row, "insert");
131
147
  return storage.insertRows(
132
148
  qualified(declaration),
@@ -134,7 +150,7 @@ function createPluginStore(pluginId, schema, storage, prefix) {
134
150
  );
135
151
  },
136
152
  async upsert(table, rows, options) {
137
- const declaration = resolve(table);
153
+ const declaration = writable(table);
138
154
  for (const row of rows) checkRow(declaration, row, "upsert");
139
155
  checkFields(declaration, options.onConflict, "onConflict");
140
156
  return storage.insertRows(
@@ -144,19 +160,21 @@ function createPluginStore(pluginId, schema, storage, prefix) {
144
160
  );
145
161
  },
146
162
  async update(table, where, set) {
147
- const declaration = resolve(table);
163
+ const declaration = writable(table);
148
164
  checkWhere(declaration, where);
165
+ notEverything(table, where, "update");
149
166
  checkRow(declaration, set, "update");
150
167
  return storage.updateRows(qualified(declaration), where, serialize(declaration, set));
151
168
  },
152
169
  async remove(table, where) {
153
- const declaration = resolve(table);
170
+ const declaration = writable(table);
154
171
  checkWhere(declaration, where);
172
+ notEverything(table, where, "remove");
155
173
  return storage.deleteRows(qualified(declaration), where);
156
174
  }
157
175
  };
158
176
  }
159
177
 
160
178
  export { ConfigError, EasyPingError, ValidationError, consoleLogger, createPluginStore, isOperator };
161
- //# sourceMappingURL=chunk-3VOQOGDR.js.map
162
- //# sourceMappingURL=chunk-3VOQOGDR.js.map
179
+ //# sourceMappingURL=chunk-O2PBBL5X.js.map
180
+ //# sourceMappingURL=chunk-O2PBBL5X.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/errors.ts","../src/core/store.ts"],"names":[],"mappings":"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","file":"chunk-O2PBBL5X.js","sourcesContent":["export class EasyPingError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"EasyPingError\";\n }\n}\n\n/** Thrown when a payload fails its Standard Schema. Developer error. */\nexport class ValidationError extends EasyPingError {\n readonly issues: readonly { message: string; path?: string }[];\n\n constructor(type: string, issues: readonly { message: string; path?: string }[]) {\n super(`Payload for \"${type}\" failed validation: ${issues.map((i) => i.message).join(\"; \")}`);\n this.name = \"ValidationError\";\n this.issues = issues;\n }\n}\n\n/** Thrown at startup for a malformed config. Never at send time. */\nexport class ConfigError extends EasyPingError {\n constructor(message: string) {\n super(message);\n this.name = \"ConfigError\";\n }\n}\n\nexport type Logger = {\n warn: (message: string, meta?: Record<string, unknown>) => void;\n error: (message: string, meta?: Record<string, unknown>) => void;\n};\n\nexport const consoleLogger: Logger = {\n warn: (message, meta) => console.warn(`[easy-ping] ${message}`, meta ?? \"\"),\n error: (message, meta) => console.error(`[easy-ping] ${message}`, meta ?? \"\"),\n};\n","import { toSnakeCase } from \"../schema/declaration\";\nimport { ConfigError } from \"./errors\";\nimport type { FieldDeclaration, SchemaDeclaration, TableDeclaration } from \"./plugin\";\n\nexport type Scalar = string | number | boolean | Date | null;\n\n/** Operators are tagged objects so a bare value is always equality; json columns cannot be filtered. */\nexport type WhereCondition =\n | Scalar\n | { in: readonly (string | number)[] }\n | { lt: Date | number }\n | { lte: Date | number }\n | { gt: Date | number }\n | { gte: Date | number }\n | { not: Scalar };\n\nexport type WhereClause = Record<string, WhereCondition>;\n\nexport type QueryOptions = {\n limit?: number;\n orderBy?: { field: string; direction: \"asc\" | \"desc\" };\n};\n\nexport type UpsertOptions = { onConflict: readonly string[] };\n\n/** Table access for plugins, scoped to the tables the plugin declared. */\nexport type PluginStore = {\n find<T = Record<string, unknown>>(\n table: string,\n where?: WhereClause,\n options?: QueryOptions,\n ): Promise<T[]>;\n insert(table: string, rows: readonly Record<string, unknown>[]): Promise<number>;\n upsert(\n table: string,\n rows: readonly Record<string, unknown>[],\n options: UpsertOptions,\n ): Promise<number>;\n update(table: string, where: WhereClause, set: Record<string, unknown>): Promise<number>;\n remove(table: string, where: WhereClause): Promise<number>;\n};\n\nexport type TableStorage = {\n readonly naming?: \"snake_case\" | \"preserve\";\n readonly serializesJson?: boolean;\n\n queryTable(\n table: string,\n where: WhereClause,\n options: QueryOptions,\n ): Promise<Record<string, unknown>[]>;\n insertRows(\n table: string,\n rows: readonly Record<string, unknown>[],\n onConflict?: readonly string[],\n ): Promise<number>;\n updateRows(table: string, where: WhereClause, set: Record<string, unknown>): Promise<number>;\n deleteRows(table: string, where: WhereClause): Promise<number>;\n};\n\nconst OPERATORS = [\"in\", \"lt\", \"lte\", \"gt\", \"gte\", \"not\"] as const;\n\nexport const isOperator = (value: unknown): boolean =>\n typeof value === \"object\" &&\n value !== null &&\n !(value instanceof Date) &&\n OPERATORS.some((op) => Object.hasOwn(value, op));\n\nconst isScalar = (value: unknown): value is Scalar =>\n value === null ||\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n typeof value === \"boolean\" ||\n value instanceof Date;\n\n/** A forwarded JSON body can carry `{ $ne: … }`; refuse anything that is not a scalar or a known operator. */\nfunction assertCondition(pluginId: string, field: string, condition: unknown): void {\n if (isScalar(condition)) return;\n\n if (isOperator(condition)) {\n const operator = condition as Record<string, unknown>;\n const keys = Object.keys(operator);\n const [op] = keys;\n if (keys.length !== 1 || op === undefined) {\n throw new ConfigError(`plugin \"${pluginId}\" gave field \"${field}\" more than one operator.`);\n }\n const payload = operator[op];\n const valid =\n op === \"in\"\n ? Array.isArray(payload) &&\n payload.every((item) => typeof item === \"string\" || typeof item === \"number\")\n : isScalar(payload);\n if (valid) return;\n }\n\n throw new ConfigError(\n `plugin \"${pluginId}\" passed a non-scalar value for field \"${field}\"; ` +\n \"validate request bodies before querying with them.\",\n );\n}\n\nfunction assertValue(pluginId: string, field: string, spec: FieldDeclaration, value: unknown) {\n if (value === undefined || isScalar(value)) return;\n if (spec.type === \"json\" && typeof value === \"object\") return;\n throw new ConfigError(\n `plugin \"${pluginId}\" passed a non-scalar value for ${spec.type} field \"${field}\".`,\n );\n}\n\n/** Every table and column is checked against the plugin's own declaration. */\nexport function createPluginStore(\n pluginId: string,\n schema: SchemaDeclaration | undefined,\n storage: TableStorage,\n prefix: string,\n): PluginStore {\n const byTableName = new Map<string, TableDeclaration>();\n for (const declaration of Object.values(schema ?? {})) {\n byTableName.set(declaration.tableName, declaration);\n }\n\n function resolve(table: string): TableDeclaration {\n const declaration = byTableName.get(table);\n if (!declaration) {\n throw new ConfigError(\n `plugin \"${pluginId}\" accessed table \"${table}\", which it does not declare in schema().`,\n );\n }\n return declaration;\n }\n\n function writable(table: string): TableDeclaration {\n const declaration = resolve(table);\n if (declaration.readOnly) {\n throw new ConfigError(\n `plugin \"${pluginId}\" tried to write \"${table}\", which it declares readOnly.`,\n );\n }\n return declaration;\n }\n\n function notEverything(table: string, where: WhereClause, action: string) {\n if (Object.keys(where).length === 0) {\n throw new ConfigError(\n `plugin \"${pluginId}\" called ${action}(\"${table}\", {}), which would touch every row. ` +\n \"Pass a condition.\",\n );\n }\n }\n\n function checkFields(table: TableDeclaration, fields: Iterable<string>, context: string) {\n for (const field of fields) {\n // hasOwn, not a lookup: `constructor` and `__proto__` are not columns.\n const spec = Object.hasOwn(table.fields, field) ? table.fields[field] : undefined;\n if (!spec) {\n throw new ConfigError(\n `plugin \"${pluginId}\" used unknown field \"${field}\" on \"${table.tableName}\" (${context}).`,\n );\n }\n if (context === \"where\" && spec.type === \"json\") {\n throw new ConfigError(\n `plugin \"${pluginId}\" cannot filter on json field \"${field}\"; ` +\n \"a bare object value is indistinguishable from an operator.\",\n );\n }\n }\n }\n\n function checkWhere(table: TableDeclaration, where: WhereClause) {\n checkFields(table, Object.keys(where), \"where\");\n for (const [field, condition] of Object.entries(where)) {\n assertCondition(pluginId, field, condition);\n }\n }\n\n function checkRow(table: TableDeclaration, row: Record<string, unknown>, context: string) {\n checkFields(table, Object.keys(row), context);\n for (const [field, value] of Object.entries(row)) {\n const spec = table.fields[field];\n if (spec) assertValue(pluginId, field, spec, value);\n }\n }\n\n const qualified = (table: TableDeclaration) => prefix + table.tableName;\n\n // A document store keeps the declared names; a SQL adapter wants columns.\n const column = (field: string) => (storage.naming === \"preserve\" ? field : toSnakeCase(field));\n\n // json must reach postgres-js as text.\n const serialize = (table: TableDeclaration, row: Record<string, unknown>) => {\n if (storage.serializesJson === false) return row;\n\n const out: Record<string, unknown> = {};\n for (const [field, value] of Object.entries(row)) {\n out[field] =\n table.fields[field]?.type === \"json\" && value !== null && value !== undefined\n ? JSON.stringify(value)\n : value;\n }\n return out;\n };\n\n return {\n async find(table, where = {}, options = {}) {\n const declaration = resolve(table);\n checkWhere(declaration, where);\n if (options.orderBy) checkFields(declaration, [options.orderBy.field], \"orderBy\");\n\n const rows = await storage.queryTable(qualified(declaration), where, options);\n\n // SELECT * returns snake_case; plugins read camelCase.\n return rows.map((row) => {\n const mapped: Record<string, unknown> = {};\n for (const field of Object.keys(declaration.fields)) {\n const value = row[column(field)];\n const type = declaration.fields[field]?.type;\n // Drivers differ on jsonb (parsed or text) and timestamptz (Date or text).\n if (type === \"json\" && typeof value === \"string\") mapped[field] = JSON.parse(value);\n else if (type === \"date\" && typeof value === \"string\") mapped[field] = new Date(value);\n // MySQL and SQLite have no boolean column; they hand back 0 and 1.\n else if (type === \"boolean\" && typeof value === \"number\") mapped[field] = value !== 0;\n else mapped[field] = value;\n }\n return mapped;\n }) as never;\n },\n\n async insert(table, rows) {\n const declaration = writable(table);\n for (const row of rows) checkRow(declaration, row, \"insert\");\n return storage.insertRows(\n qualified(declaration),\n rows.map((row) => serialize(declaration, row)),\n );\n },\n\n async upsert(table, rows, options) {\n const declaration = writable(table);\n for (const row of rows) checkRow(declaration, row, \"upsert\");\n checkFields(declaration, options.onConflict, \"onConflict\");\n return storage.insertRows(\n qualified(declaration),\n rows.map((row) => serialize(declaration, row)),\n options.onConflict,\n );\n },\n\n async update(table, where, set) {\n const declaration = writable(table);\n checkWhere(declaration, where);\n notEverything(table, where, \"update\");\n checkRow(declaration, set, \"update\");\n return storage.updateRows(qualified(declaration), where, serialize(declaration, set));\n },\n\n async remove(table, where) {\n const declaration = writable(table);\n checkWhere(declaration, where);\n notEverything(table, where, \"remove\");\n return storage.deleteRows(qualified(declaration), where);\n },\n };\n}\n"]}
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 } : {}