@sudobility/ratelimit_service 1.0.1

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 (67) hide show
  1. package/CLAUDE.md +160 -0
  2. package/dist/helpers/EntitlementHelper.cjs +75 -0
  3. package/dist/helpers/EntitlementHelper.d.ts +52 -0
  4. package/dist/helpers/EntitlementHelper.d.ts.map +1 -0
  5. package/dist/helpers/EntitlementHelper.js +75 -0
  6. package/dist/helpers/EntitlementHelper.js.map +1 -0
  7. package/dist/helpers/RateLimitChecker.cjs +264 -0
  8. package/dist/helpers/RateLimitChecker.d.ts +90 -0
  9. package/dist/helpers/RateLimitChecker.d.ts.map +1 -0
  10. package/dist/helpers/RateLimitChecker.js +264 -0
  11. package/dist/helpers/RateLimitChecker.js.map +1 -0
  12. package/dist/helpers/RateLimitRouteHandler.cjs +191 -0
  13. package/dist/helpers/RateLimitRouteHandler.d.ts +70 -0
  14. package/dist/helpers/RateLimitRouteHandler.d.ts.map +1 -0
  15. package/dist/helpers/RateLimitRouteHandler.js +191 -0
  16. package/dist/helpers/RateLimitRouteHandler.js.map +1 -0
  17. package/dist/helpers/RevenueCatHelper.cjs +96 -0
  18. package/dist/helpers/RevenueCatHelper.d.ts +51 -0
  19. package/dist/helpers/RevenueCatHelper.d.ts.map +1 -0
  20. package/dist/helpers/RevenueCatHelper.js +96 -0
  21. package/dist/helpers/RevenueCatHelper.js.map +1 -0
  22. package/dist/helpers/index.cjs +10 -0
  23. package/dist/helpers/index.d.ts +4 -0
  24. package/dist/helpers/index.d.ts.map +1 -0
  25. package/dist/helpers/index.js +10 -0
  26. package/dist/helpers/index.js.map +1 -0
  27. package/dist/index.cjs +36 -0
  28. package/dist/index.d.ts +9 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +36 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/middleware/hono.cjs +94 -0
  33. package/dist/middleware/hono.d.ts +63 -0
  34. package/dist/middleware/hono.d.ts.map +1 -0
  35. package/dist/middleware/hono.js +94 -0
  36. package/dist/middleware/hono.js.map +1 -0
  37. package/dist/schema/rate-limits.cjs +136 -0
  38. package/dist/schema/rate-limits.d.ts +333 -0
  39. package/dist/schema/rate-limits.d.ts.map +1 -0
  40. package/dist/schema/rate-limits.js +136 -0
  41. package/dist/schema/rate-limits.js.map +1 -0
  42. package/dist/types/entitlements.cjs +9 -0
  43. package/dist/types/entitlements.d.ts +29 -0
  44. package/dist/types/entitlements.d.ts.map +1 -0
  45. package/dist/types/entitlements.js +9 -0
  46. package/dist/types/entitlements.js.map +1 -0
  47. package/dist/types/index.cjs +20 -0
  48. package/dist/types/index.d.ts +4 -0
  49. package/dist/types/index.d.ts.map +1 -0
  50. package/dist/types/index.js +20 -0
  51. package/dist/types/index.js.map +1 -0
  52. package/dist/types/rate-limits.cjs +3 -0
  53. package/dist/types/rate-limits.d.ts +34 -0
  54. package/dist/types/rate-limits.d.ts.map +1 -0
  55. package/dist/types/rate-limits.js +3 -0
  56. package/dist/types/rate-limits.js.map +1 -0
  57. package/dist/types/responses.cjs +13 -0
  58. package/dist/types/responses.d.ts +85 -0
  59. package/dist/types/responses.d.ts.map +1 -0
  60. package/dist/types/responses.js +13 -0
  61. package/dist/types/responses.js.map +1 -0
  62. package/dist/utils/time.cjs +180 -0
  63. package/dist/utils/time.d.ts +80 -0
  64. package/dist/utils/time.d.ts.map +1 -0
  65. package/dist/utils/time.js +180 -0
  66. package/dist/utils/time.js.map +1 -0
  67. package/package.json +79 -0
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRateLimitMiddleware = createRateLimitMiddleware;
4
+ const RevenueCatHelper_1 = require("../helpers/RevenueCatHelper");
5
+ const EntitlementHelper_1 = require("../helpers/EntitlementHelper");
6
+ const RateLimitChecker_1 = require("../helpers/RateLimitChecker");
7
+ const types_1 = require("../types");
8
+ /**
9
+ * Create a Hono middleware for rate limiting based on RevenueCat entitlements.
10
+ *
11
+ * This middleware:
12
+ * 1. Fetches user's subscription info from RevenueCat
13
+ * 2. Resolves rate limits based on entitlements
14
+ * 3. Checks and increments counters
15
+ * 4. Returns 429 if rate limit exceeded
16
+ * 5. Sets X-RateLimit-* headers
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { createRateLimitMiddleware } from "@sudobility/ratelimit_service/middleware/hono";
21
+ * import { db, rateLimitCounters } from "./db";
22
+ *
23
+ * const rateLimitMiddleware = createRateLimitMiddleware({
24
+ * revenueCatApiKey: process.env.REVENUECAT_API_KEY!,
25
+ * rateLimitsConfig: {
26
+ * none: { hourly: 5, daily: 20, monthly: 100 },
27
+ * pro: { hourly: undefined, daily: undefined, monthly: undefined },
28
+ * },
29
+ * db,
30
+ * rateLimitsTable: rateLimitCounters,
31
+ * getUserId: (c) => c.get("firebaseUser").uid,
32
+ * });
33
+ *
34
+ * app.use("/api/*", rateLimitMiddleware);
35
+ * ```
36
+ */
37
+ function createRateLimitMiddleware(config) {
38
+ const rcHelper = new RevenueCatHelper_1.RevenueCatHelper({ apiKey: config.revenueCatApiKey });
39
+ const entitlementHelper = new EntitlementHelper_1.EntitlementHelper(config.rateLimitsConfig);
40
+ const rateLimitChecker = new RateLimitChecker_1.RateLimitChecker({
41
+ db: config.db,
42
+ table: config.rateLimitsTable,
43
+ });
44
+ return async (c, next) => {
45
+ // Check if rate limiting should be skipped
46
+ if (config.shouldSkip) {
47
+ const skip = await config.shouldSkip(c);
48
+ if (skip) {
49
+ await next();
50
+ return;
51
+ }
52
+ }
53
+ // Get user ID
54
+ const userId = await config.getUserId(c);
55
+ // Get user's subscription info from RevenueCat
56
+ let entitlements;
57
+ let subscriptionStartedAt = null;
58
+ try {
59
+ const subscriptionInfo = await rcHelper.getSubscriptionInfo(userId);
60
+ entitlements = subscriptionInfo.entitlements;
61
+ subscriptionStartedAt = subscriptionInfo.subscriptionStartedAt;
62
+ }
63
+ catch (error) {
64
+ console.error("RevenueCat error, using 'none' entitlement:", error);
65
+ entitlements = [types_1.NONE_ENTITLEMENT];
66
+ }
67
+ // Get rate limits for user's entitlements
68
+ const limits = entitlementHelper.getRateLimits(entitlements);
69
+ // Check and increment rate limits
70
+ const result = await rateLimitChecker.checkAndIncrement(userId, limits, subscriptionStartedAt);
71
+ // Set rate limit headers
72
+ if (result.remaining.hourly !== undefined) {
73
+ c.header("X-RateLimit-Hourly-Remaining", result.remaining.hourly.toString());
74
+ }
75
+ if (result.remaining.daily !== undefined) {
76
+ c.header("X-RateLimit-Daily-Remaining", result.remaining.daily.toString());
77
+ }
78
+ if (result.remaining.monthly !== undefined) {
79
+ c.header("X-RateLimit-Monthly-Remaining", result.remaining.monthly.toString());
80
+ }
81
+ if (!result.allowed) {
82
+ return c.json({
83
+ success: false,
84
+ error: "Rate limit exceeded",
85
+ message: `You have exceeded your ${result.exceededLimit} request limit. Please try again later or upgrade your subscription.`,
86
+ remaining: result.remaining,
87
+ exceededLimit: result.exceededLimit,
88
+ timestamp: new Date().toISOString(),
89
+ }, 429);
90
+ }
91
+ await next();
92
+ };
93
+ }
94
+ //# sourceMappingURL=hono.js.map
@@ -0,0 +1,63 @@
1
+ import type { Context, Next } from "hono";
2
+ import type { PostgresJsDatabase } from "drizzle-orm/postgres-js";
3
+ import type { PgTable, TableConfig } from "drizzle-orm/pg-core";
4
+ import { type RateLimitsConfig } from "../types";
5
+ /**
6
+ * Configuration for the rate limit middleware factory.
7
+ */
8
+ export interface RateLimitMiddlewareConfig {
9
+ /** RevenueCat API key */
10
+ revenueCatApiKey: string;
11
+ /** Rate limits configuration */
12
+ rateLimitsConfig: RateLimitsConfig;
13
+ /** Drizzle database instance */
14
+ db: PostgresJsDatabase<any>;
15
+ /** The rate_limit_counters table from your schema */
16
+ rateLimitsTable: PgTable<TableConfig>;
17
+ /** Function to extract user ID from context */
18
+ getUserId: (c: Context) => string | Promise<string>;
19
+ /** Optional: Skip rate limiting for certain conditions (e.g., admin tokens) */
20
+ shouldSkip?: (c: Context) => boolean | Promise<boolean>;
21
+ }
22
+ /**
23
+ * Create a Hono middleware for rate limiting based on RevenueCat entitlements.
24
+ *
25
+ * This middleware:
26
+ * 1. Fetches user's subscription info from RevenueCat
27
+ * 2. Resolves rate limits based on entitlements
28
+ * 3. Checks and increments counters
29
+ * 4. Returns 429 if rate limit exceeded
30
+ * 5. Sets X-RateLimit-* headers
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * import { createRateLimitMiddleware } from "@sudobility/ratelimit_service/middleware/hono";
35
+ * import { db, rateLimitCounters } from "./db";
36
+ *
37
+ * const rateLimitMiddleware = createRateLimitMiddleware({
38
+ * revenueCatApiKey: process.env.REVENUECAT_API_KEY!,
39
+ * rateLimitsConfig: {
40
+ * none: { hourly: 5, daily: 20, monthly: 100 },
41
+ * pro: { hourly: undefined, daily: undefined, monthly: undefined },
42
+ * },
43
+ * db,
44
+ * rateLimitsTable: rateLimitCounters,
45
+ * getUserId: (c) => c.get("firebaseUser").uid,
46
+ * });
47
+ *
48
+ * app.use("/api/*", rateLimitMiddleware);
49
+ * ```
50
+ */
51
+ export declare function createRateLimitMiddleware(config: RateLimitMiddlewareConfig): (c: Context, next: Next) => Promise<(Response & import("hono").TypedResponse<{
52
+ success: false;
53
+ error: string;
54
+ message: string;
55
+ remaining: {
56
+ hourly?: number | undefined;
57
+ daily?: number | undefined;
58
+ monthly?: number | undefined;
59
+ };
60
+ exceededLimit: import("..").ExceededLimit | undefined;
61
+ timestamp: string;
62
+ }, 429, "json">) | undefined>;
63
+ //# sourceMappingURL=hono.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/middleware/hono.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAIhE,OAAO,EAAoB,KAAK,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAEnE;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,yBAAyB;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,gCAAgC;IAChC,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,gCAAgC;IAChC,EAAE,EAAE,kBAAkB,CAAC,GAAG,CAAC,CAAC;IAC5B,qDAAqD;IACrD,eAAe,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IACtC,+CAA+C;IAC/C,SAAS,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACzD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,yBAAyB,IAQ3D,GAAG,OAAO,EAAE,MAAM,IAAI;;;;;;;;;;;8BAuErC"}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRateLimitMiddleware = createRateLimitMiddleware;
4
+ const RevenueCatHelper_1 = require("../helpers/RevenueCatHelper");
5
+ const EntitlementHelper_1 = require("../helpers/EntitlementHelper");
6
+ const RateLimitChecker_1 = require("../helpers/RateLimitChecker");
7
+ const types_1 = require("../types");
8
+ /**
9
+ * Create a Hono middleware for rate limiting based on RevenueCat entitlements.
10
+ *
11
+ * This middleware:
12
+ * 1. Fetches user's subscription info from RevenueCat
13
+ * 2. Resolves rate limits based on entitlements
14
+ * 3. Checks and increments counters
15
+ * 4. Returns 429 if rate limit exceeded
16
+ * 5. Sets X-RateLimit-* headers
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { createRateLimitMiddleware } from "@sudobility/ratelimit_service/middleware/hono";
21
+ * import { db, rateLimitCounters } from "./db";
22
+ *
23
+ * const rateLimitMiddleware = createRateLimitMiddleware({
24
+ * revenueCatApiKey: process.env.REVENUECAT_API_KEY!,
25
+ * rateLimitsConfig: {
26
+ * none: { hourly: 5, daily: 20, monthly: 100 },
27
+ * pro: { hourly: undefined, daily: undefined, monthly: undefined },
28
+ * },
29
+ * db,
30
+ * rateLimitsTable: rateLimitCounters,
31
+ * getUserId: (c) => c.get("firebaseUser").uid,
32
+ * });
33
+ *
34
+ * app.use("/api/*", rateLimitMiddleware);
35
+ * ```
36
+ */
37
+ function createRateLimitMiddleware(config) {
38
+ const rcHelper = new RevenueCatHelper_1.RevenueCatHelper({ apiKey: config.revenueCatApiKey });
39
+ const entitlementHelper = new EntitlementHelper_1.EntitlementHelper(config.rateLimitsConfig);
40
+ const rateLimitChecker = new RateLimitChecker_1.RateLimitChecker({
41
+ db: config.db,
42
+ table: config.rateLimitsTable,
43
+ });
44
+ return async (c, next) => {
45
+ // Check if rate limiting should be skipped
46
+ if (config.shouldSkip) {
47
+ const skip = await config.shouldSkip(c);
48
+ if (skip) {
49
+ await next();
50
+ return;
51
+ }
52
+ }
53
+ // Get user ID
54
+ const userId = await config.getUserId(c);
55
+ // Get user's subscription info from RevenueCat
56
+ let entitlements;
57
+ let subscriptionStartedAt = null;
58
+ try {
59
+ const subscriptionInfo = await rcHelper.getSubscriptionInfo(userId);
60
+ entitlements = subscriptionInfo.entitlements;
61
+ subscriptionStartedAt = subscriptionInfo.subscriptionStartedAt;
62
+ }
63
+ catch (error) {
64
+ console.error("RevenueCat error, using 'none' entitlement:", error);
65
+ entitlements = [types_1.NONE_ENTITLEMENT];
66
+ }
67
+ // Get rate limits for user's entitlements
68
+ const limits = entitlementHelper.getRateLimits(entitlements);
69
+ // Check and increment rate limits
70
+ const result = await rateLimitChecker.checkAndIncrement(userId, limits, subscriptionStartedAt);
71
+ // Set rate limit headers
72
+ if (result.remaining.hourly !== undefined) {
73
+ c.header("X-RateLimit-Hourly-Remaining", result.remaining.hourly.toString());
74
+ }
75
+ if (result.remaining.daily !== undefined) {
76
+ c.header("X-RateLimit-Daily-Remaining", result.remaining.daily.toString());
77
+ }
78
+ if (result.remaining.monthly !== undefined) {
79
+ c.header("X-RateLimit-Monthly-Remaining", result.remaining.monthly.toString());
80
+ }
81
+ if (!result.allowed) {
82
+ return c.json({
83
+ success: false,
84
+ error: "Rate limit exceeded",
85
+ message: `You have exceeded your ${result.exceededLimit} request limit. Please try again later or upgrade your subscription.`,
86
+ remaining: result.remaining,
87
+ exceededLimit: result.exceededLimit,
88
+ timestamp: new Date().toISOString(),
89
+ }, 429);
90
+ }
91
+ await next();
92
+ };
93
+ }
94
+ //# sourceMappingURL=hono.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.js","sourceRoot":"","sources":["../../src/middleware/hono.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,136 @@
1
+ "use strict";
2
+ /**
3
+ * Drizzle schema and database initialization for rate limit tracking.
4
+ *
5
+ * Provides:
6
+ * - createRateLimitCountersTable: Factory for Drizzle table with custom schema
7
+ * - initRateLimitTable: SQL initialization for the table
8
+ * - Default rateLimitCounters table for public schema
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.rateLimitCounters = void 0;
12
+ exports.createRateLimitCountersTable = createRateLimitCountersTable;
13
+ exports.createRateLimitCountersTablePublic = createRateLimitCountersTablePublic;
14
+ exports.initRateLimitTable = initRateLimitTable;
15
+ const pg_core_1 = require("drizzle-orm/pg-core");
16
+ /**
17
+ * Create a rate limit counters table for a specific PostgreSQL schema.
18
+ *
19
+ * @param schema - The Drizzle pgSchema object (e.g., pgSchema("shapeshyft"))
20
+ * @param indexPrefix - Prefix for index names to avoid conflicts
21
+ * @returns Drizzle table definition
22
+ *
23
+ * @example
24
+ * ```typescript
25
+ * import { pgSchema } from "drizzle-orm/pg-core";
26
+ * import { createRateLimitCountersTable } from "@sudobility/ratelimit_service";
27
+ *
28
+ * const mySchema = pgSchema("myapp");
29
+ * export const rateLimitCounters = createRateLimitCountersTable(mySchema, "myapp");
30
+ * ```
31
+ */
32
+ function createRateLimitCountersTable(schema, indexPrefix) {
33
+ return schema.table("rate_limit_counters", {
34
+ id: (0, pg_core_1.uuid)("id").primaryKey().defaultRandom(),
35
+ user_id: (0, pg_core_1.varchar)("user_id", { length: 128 }).notNull(),
36
+ period_type: (0, pg_core_1.varchar)("period_type", { length: 16 }).notNull(),
37
+ period_start: (0, pg_core_1.timestamp)("period_start", { withTimezone: true }).notNull(),
38
+ request_count: (0, pg_core_1.integer)("request_count").notNull().default(0),
39
+ created_at: (0, pg_core_1.timestamp)("created_at", { withTimezone: true }).defaultNow(),
40
+ updated_at: (0, pg_core_1.timestamp)("updated_at", { withTimezone: true }).defaultNow(),
41
+ }, (table) => ({
42
+ userPeriodUniqueIdx: (0, pg_core_1.uniqueIndex)(`${indexPrefix}_rate_limit_user_period_idx`).on(table.user_id, table.period_type, table.period_start),
43
+ userTypeIdx: (0, pg_core_1.index)(`${indexPrefix}_rate_limit_user_type_idx`).on(table.user_id, table.period_type),
44
+ }));
45
+ }
46
+ /**
47
+ * Create a rate limit counters table for the public schema.
48
+ *
49
+ * @param indexPrefix - Prefix for index names
50
+ * @returns Drizzle table definition
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * import { createRateLimitCountersTablePublic } from "@sudobility/ratelimit_service";
55
+ *
56
+ * export const rateLimitCounters = createRateLimitCountersTablePublic("sudojo");
57
+ * ```
58
+ */
59
+ function createRateLimitCountersTablePublic(indexPrefix) {
60
+ return (0, pg_core_1.pgTable)("rate_limit_counters", {
61
+ id: (0, pg_core_1.uuid)("id").primaryKey().defaultRandom(),
62
+ user_id: (0, pg_core_1.varchar)("user_id", { length: 128 }).notNull(),
63
+ period_type: (0, pg_core_1.varchar)("period_type", { length: 16 }).notNull(),
64
+ period_start: (0, pg_core_1.timestamp)("period_start", { withTimezone: true }).notNull(),
65
+ request_count: (0, pg_core_1.integer)("request_count").notNull().default(0),
66
+ created_at: (0, pg_core_1.timestamp)("created_at", { withTimezone: true }).defaultNow(),
67
+ updated_at: (0, pg_core_1.timestamp)("updated_at", { withTimezone: true }).defaultNow(),
68
+ }, table => ({
69
+ userPeriodUniqueIdx: (0, pg_core_1.uniqueIndex)(`${indexPrefix}_rate_limit_user_period_idx`).on(table.user_id, table.period_type, table.period_start),
70
+ userTypeIdx: (0, pg_core_1.index)(`${indexPrefix}_rate_limit_user_type_idx`).on(table.user_id, table.period_type),
71
+ }));
72
+ }
73
+ /**
74
+ * Default rate limit counters table for public schema.
75
+ * Use createRateLimitCountersTable for custom schemas.
76
+ */
77
+ exports.rateLimitCounters = (0, pg_core_1.pgTable)("rate_limit_counters", {
78
+ id: (0, pg_core_1.uuid)("id").primaryKey().defaultRandom(),
79
+ user_id: (0, pg_core_1.varchar)("user_id", { length: 128 }).notNull(),
80
+ period_type: (0, pg_core_1.varchar)("period_type", { length: 16 }).notNull(),
81
+ period_start: (0, pg_core_1.timestamp)("period_start", { withTimezone: true }).notNull(),
82
+ request_count: (0, pg_core_1.integer)("request_count").notNull().default(0),
83
+ created_at: (0, pg_core_1.timestamp)("created_at", { withTimezone: true }).defaultNow(),
84
+ updated_at: (0, pg_core_1.timestamp)("updated_at", { withTimezone: true }).defaultNow(),
85
+ }, table => ({
86
+ userPeriodUniqueIdx: (0, pg_core_1.uniqueIndex)("rate_limit_counters_user_period_idx").on(table.user_id, table.period_type, table.period_start),
87
+ userTypeIdx: (0, pg_core_1.index)("rate_limit_counters_user_type_idx").on(table.user_id, table.period_type),
88
+ }));
89
+ /**
90
+ * Initialize the rate limit counters table in the database.
91
+ *
92
+ * @param client - postgres-js client instance
93
+ * @param schemaName - PostgreSQL schema name (e.g., "shapeshyft", "whisperly", or null for public)
94
+ * @param indexPrefix - Prefix for index names to avoid conflicts
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * import postgres from "postgres";
99
+ * import { initRateLimitTable } from "@sudobility/ratelimit_service";
100
+ *
101
+ * const client = postgres(connectionString);
102
+ *
103
+ * // For public schema
104
+ * await initRateLimitTable(client, null, "sudojo");
105
+ *
106
+ * // For custom schema
107
+ * await initRateLimitTable(client, "shapeshyft", "shapeshyft");
108
+ * ```
109
+ */
110
+ async function initRateLimitTable(client, schemaName, indexPrefix) {
111
+ const tableName = schemaName
112
+ ? `${schemaName}.rate_limit_counters`
113
+ : "rate_limit_counters";
114
+ const uniqueIdxName = `${indexPrefix}_rate_limit_user_period_idx`;
115
+ const typeIdxName = `${indexPrefix}_rate_limit_user_type_idx`;
116
+ await client.unsafe(`
117
+ CREATE TABLE IF NOT EXISTS ${tableName} (
118
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
119
+ user_id VARCHAR(128) NOT NULL,
120
+ period_type VARCHAR(16) NOT NULL,
121
+ period_start TIMESTAMPTZ NOT NULL,
122
+ request_count INTEGER NOT NULL DEFAULT 0,
123
+ created_at TIMESTAMPTZ DEFAULT NOW(),
124
+ updated_at TIMESTAMPTZ DEFAULT NOW()
125
+ )
126
+ `);
127
+ await client.unsafe(`
128
+ CREATE UNIQUE INDEX IF NOT EXISTS ${uniqueIdxName}
129
+ ON ${tableName} (user_id, period_type, period_start)
130
+ `);
131
+ await client.unsafe(`
132
+ CREATE INDEX IF NOT EXISTS ${typeIdxName}
133
+ ON ${tableName} (user_id, period_type)
134
+ `);
135
+ }
136
+ //# sourceMappingURL=rate-limits.js.map