@thirdweb-dev/service-utils 0.0.0-dev-c3925f3-20230905155015 → 0.0.0-dev-92be883-20230913130717

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 (25) hide show
  1. package/cf-worker/dist/thirdweb-dev-service-utils-cf-worker.cjs.dev.js +15 -2
  2. package/cf-worker/dist/thirdweb-dev-service-utils-cf-worker.cjs.prod.js +15 -2
  3. package/cf-worker/dist/thirdweb-dev-service-utils-cf-worker.esm.js +14 -3
  4. package/dist/declarations/src/cf-worker/index.d.ts +6 -2
  5. package/dist/declarations/src/cf-worker/index.d.ts.map +1 -1
  6. package/dist/declarations/src/cf-worker/usage.d.ts +2 -2
  7. package/dist/declarations/src/core/api.d.ts +16 -1
  8. package/dist/declarations/src/core/api.d.ts.map +1 -1
  9. package/dist/declarations/src/core/rateLimit/index.d.ts +9 -0
  10. package/dist/declarations/src/core/rateLimit/index.d.ts.map +1 -0
  11. package/dist/declarations/src/core/rateLimit/types.d.ts +9 -0
  12. package/dist/declarations/src/core/rateLimit/types.d.ts.map +1 -0
  13. package/dist/declarations/src/core/usageLimit/index.d.ts +4 -0
  14. package/dist/declarations/src/core/usageLimit/index.d.ts.map +1 -0
  15. package/dist/declarations/src/core/usageLimit/types.d.ts +9 -0
  16. package/dist/declarations/src/core/usageLimit/types.d.ts.map +1 -0
  17. package/dist/declarations/src/node/index.d.ts +21 -2
  18. package/dist/declarations/src/node/index.d.ts.map +1 -1
  19. package/dist/{index-6e0ecc5f.cjs.prod.js → index-21ba31a2.cjs.prod.js} +91 -0
  20. package/dist/{index-cd4f96ef.cjs.dev.js → index-4e1ffe00.cjs.dev.js} +91 -0
  21. package/dist/{index-ffddf746.esm.js → index-524baaec.esm.js} +90 -1
  22. package/node/dist/thirdweb-dev-service-utils-node.cjs.dev.js +20 -1
  23. package/node/dist/thirdweb-dev-service-utils-node.cjs.prod.js +20 -1
  24. package/node/dist/thirdweb-dev-service-utils-node.esm.js +19 -1
  25. package/package.json +2 -1
@@ -2,10 +2,10 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var index = require('../../dist/index-cd4f96ef.cjs.dev.js');
6
- var services = require('../../dist/services-a3f36057.cjs.dev.js');
5
+ var index = require('../../dist/index-4e1ffe00.cjs.dev.js');
7
6
  var aws4fetch = require('aws4fetch');
8
7
  var zod = require('zod');
8
+ var services = require('../../dist/services-a3f36057.cjs.dev.js');
9
9
 
10
10
  // Initialize a singleton for aws usage.
11
11
  let _aws;
@@ -86,6 +86,8 @@ async function publishUsageEvents(usageEvents, config) {
86
86
  }
87
87
 
88
88
  const DEFAULT_CACHE_TTL_SECONDS = 60;
89
+ // must be > DEFAULT_RATE_LIMIT_WINDOW_SECONDS
90
+ const DEFAULT_RATE_LIMIT_CACHE_TTL_SECONDS = 60;
89
91
  async function authorizeWorker(authInput, serviceConfig) {
90
92
  let authData;
91
93
  try {
@@ -117,6 +119,14 @@ async function authorizeWorker(authInput, serviceConfig) {
117
119
  cacheTtlSeconds: serviceConfig.cacheTtlSeconds ?? DEFAULT_CACHE_TTL_SECONDS
118
120
  });
119
121
  }
122
+ async function rateLimitWorker(apiKeyMeta, serviceConfig) {
123
+ return await index.rateLimit(apiKeyMeta, serviceConfig, {
124
+ get: async bucketId => serviceConfig.kvStore.get(bucketId),
125
+ put: (bucketId, count) => serviceConfig.kvStore.put(bucketId, count, {
126
+ expirationTtl: DEFAULT_RATE_LIMIT_CACHE_TTL_SECONDS
127
+ })
128
+ });
129
+ }
120
130
  async function extractAuthorizationData(authInput) {
121
131
  const requestUrl = new URL(authInput.req.url);
122
132
  const headers = authInput.req.headers;
@@ -228,6 +238,8 @@ async function logHttpRequest(_ref) {
228
238
  console.log(`statusMessage=${statusMessage ?? res.statusText}`);
229
239
  }
230
240
 
241
+ exports.rateLimit = index.rateLimit;
242
+ exports.usageLimit = index.usageLimit;
231
243
  exports.SERVICES = services.SERVICES;
232
244
  exports.SERVICE_DEFINITIONS = services.SERVICE_DEFINITIONS;
233
245
  exports.SERVICE_NAMES = services.SERVICE_NAMES;
@@ -238,3 +250,4 @@ exports.extractAuthorizationData = extractAuthorizationData;
238
250
  exports.hashSecretKey = hashSecretKey;
239
251
  exports.logHttpRequest = logHttpRequest;
240
252
  exports.publishUsageEvents = publishUsageEvents;
253
+ exports.rateLimitWorker = rateLimitWorker;
@@ -2,10 +2,10 @@
2
2
 
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
- var index = require('../../dist/index-6e0ecc5f.cjs.prod.js');
6
- var services = require('../../dist/services-9e185105.cjs.prod.js');
5
+ var index = require('../../dist/index-21ba31a2.cjs.prod.js');
7
6
  var aws4fetch = require('aws4fetch');
8
7
  var zod = require('zod');
8
+ var services = require('../../dist/services-9e185105.cjs.prod.js');
9
9
 
10
10
  // Initialize a singleton for aws usage.
11
11
  let _aws;
@@ -86,6 +86,8 @@ async function publishUsageEvents(usageEvents, config) {
86
86
  }
87
87
 
88
88
  const DEFAULT_CACHE_TTL_SECONDS = 60;
89
+ // must be > DEFAULT_RATE_LIMIT_WINDOW_SECONDS
90
+ const DEFAULT_RATE_LIMIT_CACHE_TTL_SECONDS = 60;
89
91
  async function authorizeWorker(authInput, serviceConfig) {
90
92
  let authData;
91
93
  try {
@@ -117,6 +119,14 @@ async function authorizeWorker(authInput, serviceConfig) {
117
119
  cacheTtlSeconds: serviceConfig.cacheTtlSeconds ?? DEFAULT_CACHE_TTL_SECONDS
118
120
  });
119
121
  }
122
+ async function rateLimitWorker(apiKeyMeta, serviceConfig) {
123
+ return await index.rateLimit(apiKeyMeta, serviceConfig, {
124
+ get: async bucketId => serviceConfig.kvStore.get(bucketId),
125
+ put: (bucketId, count) => serviceConfig.kvStore.put(bucketId, count, {
126
+ expirationTtl: DEFAULT_RATE_LIMIT_CACHE_TTL_SECONDS
127
+ })
128
+ });
129
+ }
120
130
  async function extractAuthorizationData(authInput) {
121
131
  const requestUrl = new URL(authInput.req.url);
122
132
  const headers = authInput.req.headers;
@@ -228,6 +238,8 @@ async function logHttpRequest(_ref) {
228
238
  console.log(`statusMessage=${statusMessage ?? res.statusText}`);
229
239
  }
230
240
 
241
+ exports.rateLimit = index.rateLimit;
242
+ exports.usageLimit = index.usageLimit;
231
243
  exports.SERVICES = services.SERVICES;
232
244
  exports.SERVICE_DEFINITIONS = services.SERVICE_DEFINITIONS;
233
245
  exports.SERVICE_NAMES = services.SERVICE_NAMES;
@@ -238,3 +250,4 @@ exports.extractAuthorizationData = extractAuthorizationData;
238
250
  exports.hashSecretKey = hashSecretKey;
239
251
  exports.logHttpRequest = logHttpRequest;
240
252
  exports.publishUsageEvents = publishUsageEvents;
253
+ exports.rateLimitWorker = rateLimitWorker;
@@ -1,7 +1,8 @@
1
- import { a as authorize } from '../../dist/index-ffddf746.esm.js';
2
- export { b as SERVICES, S as SERVICE_DEFINITIONS, a as SERVICE_NAMES, g as getServiceByName } from '../../dist/services-86283509.esm.js';
1
+ import { a as authorize, r as rateLimit } from '../../dist/index-524baaec.esm.js';
2
+ export { r as rateLimit, u as usageLimit } from '../../dist/index-524baaec.esm.js';
3
3
  import { AwsClient } from 'aws4fetch';
4
4
  import { z } from 'zod';
5
+ export { b as SERVICES, S as SERVICE_DEFINITIONS, a as SERVICE_NAMES, g as getServiceByName } from '../../dist/services-86283509.esm.js';
5
6
 
6
7
  // Initialize a singleton for aws usage.
7
8
  let _aws;
@@ -82,6 +83,8 @@ async function publishUsageEvents(usageEvents, config) {
82
83
  }
83
84
 
84
85
  const DEFAULT_CACHE_TTL_SECONDS = 60;
86
+ // must be > DEFAULT_RATE_LIMIT_WINDOW_SECONDS
87
+ const DEFAULT_RATE_LIMIT_CACHE_TTL_SECONDS = 60;
85
88
  async function authorizeWorker(authInput, serviceConfig) {
86
89
  let authData;
87
90
  try {
@@ -113,6 +116,14 @@ async function authorizeWorker(authInput, serviceConfig) {
113
116
  cacheTtlSeconds: serviceConfig.cacheTtlSeconds ?? DEFAULT_CACHE_TTL_SECONDS
114
117
  });
115
118
  }
119
+ async function rateLimitWorker(apiKeyMeta, serviceConfig) {
120
+ return await rateLimit(apiKeyMeta, serviceConfig, {
121
+ get: async bucketId => serviceConfig.kvStore.get(bucketId),
122
+ put: (bucketId, count) => serviceConfig.kvStore.put(bucketId, count, {
123
+ expirationTtl: DEFAULT_RATE_LIMIT_CACHE_TTL_SECONDS
124
+ })
125
+ });
126
+ }
116
127
  async function extractAuthorizationData(authInput) {
117
128
  const requestUrl = new URL(authInput.req.url);
118
129
  const headers = authInput.req.headers;
@@ -224,4 +235,4 @@ async function logHttpRequest(_ref) {
224
235
  console.log(`statusMessage=${statusMessage ?? res.statusText}`);
225
236
  }
226
237
 
227
- export { authorizeWorker, deriveClientIdFromSecretKeyHash, extractAuthorizationData, hashSecretKey, logHttpRequest, publishUsageEvents };
238
+ export { authorizeWorker, deriveClientIdFromSecretKeyHash, extractAuthorizationData, hashSecretKey, logHttpRequest, publishUsageEvents, rateLimitWorker };
@@ -1,11 +1,14 @@
1
1
  import type { ExecutionContext, KVNamespace, Response } from "@cloudflare/workers-types";
2
- import type { CoreServiceConfig } from "../core/api";
2
+ import type { ApiKeyMetadata, CoreServiceConfig } from "../core/api";
3
3
  import type { Request } from "@cloudflare/workers-types";
4
4
  import type { AuthorizationInput } from "../core/authorize";
5
5
  import type { AuthorizationResult } from "../core/authorize/types";
6
+ import type { RateLimitResult } from "../core/rateLimit/types";
6
7
  import type { CoreAuthInput } from "../core/types";
7
- export * from "../core/services";
8
8
  export * from "./usage";
9
+ export * from "../core/services";
10
+ export * from "../core/rateLimit";
11
+ export * from "../core/usageLimit";
9
12
  type WorkerServiceConfig = CoreServiceConfig & {
10
13
  kvStore: KVNamespace;
11
14
  ctx: ExecutionContext;
@@ -15,6 +18,7 @@ type AuthInput = CoreAuthInput & {
15
18
  req: Request;
16
19
  };
17
20
  export declare function authorizeWorker(authInput: AuthInput, serviceConfig: WorkerServiceConfig): Promise<AuthorizationResult>;
21
+ export declare function rateLimitWorker(apiKeyMeta: ApiKeyMetadata, serviceConfig: WorkerServiceConfig): Promise<RateLimitResult>;
18
22
  export declare function extractAuthorizationData(authInput: AuthInput): Promise<AuthorizationInput>;
19
23
  export declare function hashSecretKey(secretKey: string): Promise<string>;
20
24
  export declare function deriveClientIdFromSecretKeyHash(secretKeyHash: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"../../../../src/cf-worker","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,WAAW,EACX,QAAQ,EACT,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAGV,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,cAAc,kBAAkB,CAAC;AACjC,cAAc,SAAS,CAAC;AAExB,KAAK,mBAAmB,GAAG,iBAAiB,GAAG;IAC7C,OAAO,EAAE,WAAW,CAAC;IACrB,GAAG,EAAE,gBAAgB,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAIF,KAAK,SAAS,GAAG,aAAa,GAAG;IAC/B,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,wBAAsB,eAAe,CACnC,SAAS,EAAE,SAAS,EACpB,aAAa,EAAE,mBAAmB,GACjC,OAAO,CAAC,mBAAmB,CAAC,CA0C9B;AAED,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,kBAAkB,CAAC,CA2E7B;AAED,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,mBAIpD;AAED,wBAAgB,+BAA+B,CAAC,aAAa,EAAE,MAAM,UAEpE;AAQD,wBAAsB,cAAc,CAAC,EACnC,MAAM,EACN,QAAQ,EACR,GAAG,EACH,GAAG,EACH,QAAQ,EACR,aAAa,GACd,EAAE,SAAS,GAAG;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,QAAQ,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CAChC,iBAoBA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"../../../../src/cf-worker","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,WAAW,EACX,QAAQ,EACT,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EACV,cAAc,EAEd,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAGrB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC/D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,cAAc,SAAS,CAAC;AACxB,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AAEnC,KAAK,mBAAmB,GAAG,iBAAiB,GAAG;IAC7C,OAAO,EAAE,WAAW,CAAC;IACrB,GAAG,EAAE,gBAAgB,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAMF,KAAK,SAAS,GAAG,aAAa,GAAG;IAC/B,GAAG,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,wBAAsB,eAAe,CACnC,SAAS,EAAE,SAAS,EACpB,aAAa,EAAE,mBAAmB,GACjC,OAAO,CAAC,mBAAmB,CAAC,CA0C9B;AAED,wBAAsB,eAAe,CACnC,UAAU,EAAE,cAAc,EAC1B,aAAa,EAAE,mBAAmB,GACjC,OAAO,CAAC,eAAe,CAAC,CAQ1B;AAED,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,kBAAkB,CAAC,CA2E7B;AAED,wBAAsB,aAAa,CAAC,SAAS,EAAE,MAAM,mBAIpD;AAED,wBAAgB,+BAA+B,CAAC,aAAa,EAAE,MAAM,UAEpE;AAQD,wBAAsB,cAAc,CAAC,EACnC,MAAM,EACN,QAAQ,EACR,GAAG,EACH,GAAG,EACH,QAAQ,EACR,aAAa,GACd,EAAE,SAAS,GAAG;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,QAAQ,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CAChC,iBAoBA"}
@@ -21,8 +21,8 @@ declare const usageEventSchema: z.ZodObject<{
21
21
  userOpHash: z.ZodOptional<z.ZodString>;
22
22
  }, "strip", z.ZodTypeAny, {
23
23
  source: "storage" | "rpc" | "bundler" | "relayer" | "wallet" | "paymaster";
24
- action: string;
25
24
  accountId: string;
25
+ action: string;
26
26
  apiKeyId?: string | undefined;
27
27
  creatorWalletAddress?: string | undefined;
28
28
  clientId?: string | undefined;
@@ -38,8 +38,8 @@ declare const usageEventSchema: z.ZodObject<{
38
38
  userOpHash?: string | undefined;
39
39
  }, {
40
40
  source: "storage" | "rpc" | "bundler" | "relayer" | "wallet" | "paymaster";
41
- action: string;
42
41
  accountId: string;
42
+ action: string;
43
43
  apiKeyId?: string | undefined;
44
44
  creatorWalletAddress?: string | undefined;
45
45
  clientId?: string | undefined;
@@ -1,6 +1,6 @@
1
1
  import type { ServiceName } from "./services";
2
2
  export type CoreServiceConfig = {
3
- enforceAuth: boolean;
3
+ enforceAuth?: boolean;
4
4
  apiUrl: string;
5
5
  serviceScope: ServiceName;
6
6
  serviceApiKey: string;
@@ -23,6 +23,20 @@ export type ApiKeyMetadata = {
23
23
  targetAddresses: string[];
24
24
  actions: string[];
25
25
  }[];
26
+ usage?: {
27
+ bundler?: {
28
+ chainId: number;
29
+ sumTransactionFee: number;
30
+ }[];
31
+ storage?: {
32
+ sumFileSizeBytes: number;
33
+ };
34
+ embeddedWallets?: {
35
+ countWalletAddresses: number;
36
+ };
37
+ };
38
+ limits: Partial<Record<ServiceName, number>>;
39
+ rateLimits: Partial<Record<ServiceName, number>>;
26
40
  };
27
41
  export type AccountMetadata = {
28
42
  id: string;
@@ -47,4 +61,5 @@ export type ApiAccountResponse = {
47
61
  };
48
62
  export declare function fetchKeyMetadataFromApi(clientId: string, config: CoreServiceConfig): Promise<ApiResponse>;
49
63
  export declare function fetchAccountFromApi(jwt: string, config: CoreServiceConfig, useWalletAuth: boolean): Promise<ApiAccountResponse>;
64
+ export declare function updateRateLimitedAt(apiKeyId: string, config: CoreServiceConfig): Promise<void>;
50
65
  //# sourceMappingURL=api.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"api.d.ts","sourceRoot":"../../../../src/core","sources":["api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,WAAW,EAAE,OAAO,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,WAAW,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,gBAAgB,CAAC;IAC9E,WAAW,EAAE,MAAM,GAAG,YAAY,CAAC;IACnC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,EAAE,CAAC;CACL,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAEF,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,WAAW,CAAC,CAiBtB;AAED,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,iBAAiB,EACzB,aAAa,EAAE,OAAO,GACrB,OAAO,CAAC,kBAAkB,CAAC,CAkB7B"}
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"../../../../src/core","sources":["api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,WAAW,CAAC;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,YAAY,GAAG,WAAW,GAAG,cAAc,GAAG,gBAAgB,CAAC;IAC9E,WAAW,EAAE,MAAM,GAAG,YAAY,CAAC;IACnC,oBAAoB,EAAE,MAAM,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE;QACR,IAAI,EAAE,MAAM,CAAC;QACb,eAAe,EAAE,MAAM,EAAE,CAAC;QAC1B,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,EAAE,CAAC;IACJ,KAAK,CAAC,EAAE;QACN,OAAO,CAAC,EAAE;YACR,OAAO,EAAE,MAAM,CAAC;YAChB,iBAAiB,EAAE,MAAM,CAAC;SAC3B,EAAE,CAAC;QACJ,OAAO,CAAC,EAAE;YACR,gBAAgB,EAAE,MAAM,CAAC;SAC1B,CAAC;QACF,eAAe,CAAC,EAAE;YAChB,oBAAoB,EAAE,MAAM,CAAC;SAC9B,CAAC;KACH,CAAC;IACF,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IAC7C,UAAU,EAAE,OAAO,CAAC,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,EAAE,MAAM,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,cAAc,GAAG,IAAI,CAAC;IAC5B,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IAC7B,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAEF,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,WAAW,CAAC,CAqBtB;AAED,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,iBAAiB,EACzB,aAAa,EAAE,OAAO,GACrB,OAAO,CAAC,kBAAkB,CAAC,CAwB7B;AAED,wBAAsB,mBAAmB,CACvC,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,IAAI,CAAC,CAgBf"}
@@ -0,0 +1,9 @@
1
+ import { ApiKeyMetadata, CoreServiceConfig } from "../api";
2
+ import { RateLimitResult } from "./types";
3
+ type CacheOptions = {
4
+ get: (bucketId: string) => Promise<string | null>;
5
+ put: (bucketId: string, count: string) => Promise<void> | void;
6
+ };
7
+ export declare function rateLimit(apiKeyMeta: ApiKeyMetadata, serviceConfig: CoreServiceConfig, cacheOptions: CacheOptions): Promise<RateLimitResult>;
8
+ export {};
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"../../../../../src/core/rateLimit","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAuB,MAAM,QAAQ,CAAC;AAEhF,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAI1C,KAAK,YAAY,GAAG;IAClB,GAAG,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAClD,GAAG,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAChE,CAAC;AAEF,wBAAsB,SAAS,CAC7B,UAAU,EAAE,cAAc,EAC1B,aAAa,EAAE,iBAAiB,EAChC,YAAY,EAAE,YAAY,GACzB,OAAO,CAAC,eAAe,CAAC,CAsC1B"}
@@ -0,0 +1,9 @@
1
+ export type RateLimitResult = {
2
+ rateLimited: false;
3
+ } | {
4
+ rateLimited: true;
5
+ status: number;
6
+ errorMessage: string;
7
+ errorCode: string;
8
+ };
9
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"../../../../../src/core/rateLimit","sources":["types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GACvB;IACE,WAAW,EAAE,KAAK,CAAC;CACpB,GACD;IACE,WAAW,EAAE,IAAI,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { ApiKeyMetadata, CoreServiceConfig } from "../api";
2
+ import { UsageLimitResult } from "./types";
3
+ export declare function usageLimit(apiKeyMeta: ApiKeyMetadata, serviceConfig: CoreServiceConfig): Promise<UsageLimitResult>;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"../../../../../src/core/usageLimit","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,QAAQ,CAAC;AAE3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE3C,wBAAsB,UAAU,CAC9B,UAAU,EAAE,cAAc,EAC1B,aAAa,EAAE,iBAAiB,GAC/B,OAAO,CAAC,gBAAgB,CAAC,CA4B3B"}
@@ -0,0 +1,9 @@
1
+ export type UsageLimitResult = {
2
+ usageLimited: false;
3
+ } | {
4
+ usageLimited: true;
5
+ status: number;
6
+ errorMessage: string;
7
+ errorCode: string;
8
+ };
9
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"../../../../../src/core/usageLimit","sources":["types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GACxB;IACE,YAAY,EAAE,KAAK,CAAC;CACrB,GACD;IACE,YAAY,EAAE,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC"}
@@ -1,15 +1,34 @@
1
1
  /// <reference types="node" />
2
+ import type { ServerResponse } from "http";
2
3
  import type { IncomingMessage } from "node:http";
3
- import type { AuthorizationInput } from "../core/authorize";
4
4
  import type { CoreServiceConfig } from "../core/api";
5
+ import type { AuthorizationInput } from "../core/authorize";
5
6
  import type { AuthorizationResult } from "../core/authorize/types";
6
7
  import type { CoreAuthInput } from "../core/types";
7
- import type { ServerResponse } from "http";
8
8
  export * from "../core/services";
9
+ export * from "../core/rateLimit";
10
+ export * from "../core/usageLimit";
9
11
  type NodeServiceConfig = CoreServiceConfig;
10
12
  export type AuthInput = CoreAuthInput & {
11
13
  req: IncomingMessage;
12
14
  };
15
+ /**
16
+ *
17
+ * @param {AuthInput['req']} authInput.req - The incoming request from which information will be pulled from. These information includes (checks are in order and terminates on first match):
18
+ * - clientId: Checks header `x-client-id`, search param `clientId`
19
+ * - bundleId: Checks header `x-bundle-id`, search param `bundleId`
20
+ * - secretKey: Checks header `x-secret-key`
21
+ * - origin (the requesting domain): Checks header `origin`, `referer`
22
+ * @param {AuthInput['clientId']} authInput.clientId - Overrides any clientId found on the `req` object
23
+ * @param {AuthInput['targetAddress']} authInput.targetAddress - Only used in smart wallets to determine if the request is authorized to interact with the target address.
24
+ * @param {NodeServiceConfig['enforceAuth']} serviceConfig - Always `true` unless you need to turn auth off. Tells the service whether or not to enforce auth.
25
+ * @param {NodeServiceConfig['apiUrl']} serviceConfig.apiUrl - The url of the api server to fetch information for verification. `https://api.thirdweb.com` for production and `https://api.staging.thirdweb.com` for staging
26
+ * @param {NodeServiceConfig['serviceApiKey']} serviceConfig.serviceApiKey - secret key to be used authenticate the caller of the api-server. Check the api-server's env variable for the keys.
27
+ * @param {NodeServiceConfig['serviceScope']} serviceConfig.serviceScope - The service that we are requesting authorization for. E.g. `relayer`, `rpc`, 'bundler', 'storage' etc.
28
+ * @param {NodeServiceConfig['serviceAction']} serviceConfig.serviceAction - Needed when the `serviceScope` is `storage`. Can be either `read` or `write`.
29
+ * @param {NodeServiceConfig['useWalletAuth']} serviceConfig.useWalletAuth - If true it pings the `wallet/me` or else, `account/me`. You most likely can leave this as false.
30
+ * @returns {AuthorizationResult} authorizationResult - contains if the request is authorized, and information about the account if it is authorized. Otherwise, it contains the error message and status code.
31
+ */
13
32
  export declare function authorizeNode(authInput: AuthInput, serviceConfig: NodeServiceConfig): Promise<AuthorizationResult>;
14
33
  export declare function extractAuthorizationData(authInput: AuthInput): AuthorizationInput;
15
34
  export declare function hashSecretKey(secretKey: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"../../../../src/node","sources":["index.ts"],"names":[],"mappings":";AAGA,OAAO,KAAK,EAAuB,eAAe,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAE3C,cAAc,kBAAkB,CAAC;AAEjC,KAAK,iBAAiB,GAAG,iBAAiB,CAAC;AAE3C,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG;IACtC,GAAG,EAAE,eAAe,CAAC;CACtB,CAAC;AAEF,wBAAsB,aAAa,CACjC,SAAS,EAAE,SAAS,EACpB,aAAa,EAAE,iBAAiB,GAC/B,OAAO,CAAC,mBAAmB,CAAC,CAsB9B;AAaD,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,SAAS,GACnB,kBAAkB,CA2FpB;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,UAE9C;AAED,wBAAgB,+BAA+B,CAAC,aAAa,EAAE,MAAM,UAEpE;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,QAAQ,EACR,GAAG,EACH,GAAG,EACH,QAAQ,EACR,aAAa,GACd,EAAE,SAAS,GAAG;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,cAAc,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CAChC,QAsBA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"../../../../src/node","sources":["index.ts"],"names":[],"mappings":";AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,MAAM,CAAC;AAC3C,OAAO,KAAK,EAAuB,eAAe,EAAE,MAAM,WAAW,CAAC;AACtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC;AAEnC,KAAK,iBAAiB,GAAG,iBAAiB,CAAC;AAE3C,MAAM,MAAM,SAAS,GAAG,aAAa,GAAG;IACtC,GAAG,EAAE,eAAe,CAAC;CACtB,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,aAAa,CACjC,SAAS,EAAE,SAAS,EACpB,aAAa,EAAE,iBAAiB,GAC/B,OAAO,CAAC,mBAAmB,CAAC,CAsB9B;AAaD,wBAAgB,wBAAwB,CACtC,SAAS,EAAE,SAAS,GACnB,kBAAkB,CA2FpB;AAED,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,UAE9C;AAED,wBAAgB,+BAA+B,CAAC,aAAa,EAAE,MAAM,UAEpE;AAED,wBAAgB,cAAc,CAAC,EAC7B,MAAM,EACN,QAAQ,EACR,GAAG,EACH,GAAG,EACH,QAAQ,EACR,aAAa,GACd,EAAE,SAAS,GAAG;IACb,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,cAAc,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,aAAa,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;CAChC,QAsBA"}
@@ -44,6 +44,25 @@ async function fetchAccountFromApi(jwt, config, useWalletAuth) {
44
44
  }
45
45
  return json;
46
46
  }
47
+ async function updateRateLimitedAt(apiKeyId, config) {
48
+ const {
49
+ apiUrl,
50
+ serviceScope: scope,
51
+ serviceApiKey
52
+ } = config;
53
+ const url = `${apiUrl}/usage/rateLimit`;
54
+ await fetch(url, {
55
+ method: "PUT",
56
+ headers: {
57
+ "x-service-api-key": serviceApiKey,
58
+ "content-type": "application/json"
59
+ },
60
+ body: JSON.stringify({
61
+ apiKeyId,
62
+ scope
63
+ })
64
+ });
65
+ }
47
66
 
48
67
  function authorizeClient(authOptions, apiKeyMeta) {
49
68
  const {
@@ -421,4 +440,76 @@ async function authorize(authData, serviceConfig, cacheOptions) {
421
440
  };
422
441
  }
423
442
 
443
+ const DEFAULT_RATE_LIMIT_WINDOW_SECONDS = 10;
444
+ async function rateLimit(apiKeyMeta, serviceConfig, cacheOptions) {
445
+ const {
446
+ id,
447
+ rateLimits,
448
+ accountId
449
+ } = apiKeyMeta;
450
+ const {
451
+ serviceScope
452
+ } = serviceConfig;
453
+ const limit = rateLimits[serviceScope];
454
+ if (limit === undefined) {
455
+ // No rate limit is provided. Assume the request is not rate limited.
456
+ return {
457
+ rateLimited: false
458
+ };
459
+ }
460
+
461
+ // Floors the current time to the nearest DEFAULT_RATE_LIMIT_WINDOW_SECONDS.
462
+ const bucketId = Math.floor(Date.now() / (1000 * DEFAULT_RATE_LIMIT_WINDOW_SECONDS)) * DEFAULT_RATE_LIMIT_WINDOW_SECONDS;
463
+ const key = [serviceScope, accountId, bucketId].join(":");
464
+ const value = parseInt((await cacheOptions.get(key)) || "0");
465
+ const current = value + 1;
466
+
467
+ // limit is in seconds, but we need in DEFAULT_RATE_LIMIT_WINDOW_SECONDS
468
+ if (current > limit * DEFAULT_RATE_LIMIT_WINDOW_SECONDS) {
469
+ // report rate limit hits
470
+ await updateRateLimitedAt(id, serviceConfig);
471
+ return {
472
+ rateLimited: true,
473
+ status: 429,
474
+ errorMessage: `You've exceeded your ${serviceScope} rate limit at ${limit} reqs/sec. To get higher rate limits, contact us at https://thirdweb.com/contact-us.`,
475
+ errorCode: "RATE_LIMIT_EXCEEDED"
476
+ };
477
+ } else {
478
+ await cacheOptions.put(key, current.toString());
479
+ }
480
+ return {
481
+ rateLimited: false
482
+ };
483
+ }
484
+
485
+ async function usageLimit(apiKeyMeta, serviceConfig) {
486
+ const {
487
+ limits,
488
+ usage
489
+ } = apiKeyMeta;
490
+ const {
491
+ serviceScope
492
+ } = serviceConfig;
493
+ const limit = limits[serviceScope];
494
+ if (!usage || !(serviceScope in usage) || limit === undefined) {
495
+ // No usage limit is provided. Assume the request is not limited.
496
+ return {
497
+ usageLimited: false
498
+ };
499
+ }
500
+ if (serviceScope === "storage" && (usage.storage?.sumFileSizeBytes || 0) > limit) {
501
+ return {
502
+ usageLimited: true,
503
+ status: 402,
504
+ errorMessage: `You've used all of your total usage limit for Storage Pinning. Please add your payment method at https://thirdweb.com/dashboard/settings/billing.`,
505
+ errorCode: "PAYMENT_METHOD_REQUIRED"
506
+ };
507
+ }
508
+ return {
509
+ usageLimited: false
510
+ };
511
+ }
512
+
424
513
  exports.authorize = authorize;
514
+ exports.rateLimit = rateLimit;
515
+ exports.usageLimit = usageLimit;
@@ -44,6 +44,25 @@ async function fetchAccountFromApi(jwt, config, useWalletAuth) {
44
44
  }
45
45
  return json;
46
46
  }
47
+ async function updateRateLimitedAt(apiKeyId, config) {
48
+ const {
49
+ apiUrl,
50
+ serviceScope: scope,
51
+ serviceApiKey
52
+ } = config;
53
+ const url = `${apiUrl}/usage/rateLimit`;
54
+ await fetch(url, {
55
+ method: "PUT",
56
+ headers: {
57
+ "x-service-api-key": serviceApiKey,
58
+ "content-type": "application/json"
59
+ },
60
+ body: JSON.stringify({
61
+ apiKeyId,
62
+ scope
63
+ })
64
+ });
65
+ }
47
66
 
48
67
  function authorizeClient(authOptions, apiKeyMeta) {
49
68
  const {
@@ -421,4 +440,76 @@ async function authorize(authData, serviceConfig, cacheOptions) {
421
440
  };
422
441
  }
423
442
 
443
+ const DEFAULT_RATE_LIMIT_WINDOW_SECONDS = 10;
444
+ async function rateLimit(apiKeyMeta, serviceConfig, cacheOptions) {
445
+ const {
446
+ id,
447
+ rateLimits,
448
+ accountId
449
+ } = apiKeyMeta;
450
+ const {
451
+ serviceScope
452
+ } = serviceConfig;
453
+ const limit = rateLimits[serviceScope];
454
+ if (limit === undefined) {
455
+ // No rate limit is provided. Assume the request is not rate limited.
456
+ return {
457
+ rateLimited: false
458
+ };
459
+ }
460
+
461
+ // Floors the current time to the nearest DEFAULT_RATE_LIMIT_WINDOW_SECONDS.
462
+ const bucketId = Math.floor(Date.now() / (1000 * DEFAULT_RATE_LIMIT_WINDOW_SECONDS)) * DEFAULT_RATE_LIMIT_WINDOW_SECONDS;
463
+ const key = [serviceScope, accountId, bucketId].join(":");
464
+ const value = parseInt((await cacheOptions.get(key)) || "0");
465
+ const current = value + 1;
466
+
467
+ // limit is in seconds, but we need in DEFAULT_RATE_LIMIT_WINDOW_SECONDS
468
+ if (current > limit * DEFAULT_RATE_LIMIT_WINDOW_SECONDS) {
469
+ // report rate limit hits
470
+ await updateRateLimitedAt(id, serviceConfig);
471
+ return {
472
+ rateLimited: true,
473
+ status: 429,
474
+ errorMessage: `You've exceeded your ${serviceScope} rate limit at ${limit} reqs/sec. To get higher rate limits, contact us at https://thirdweb.com/contact-us.`,
475
+ errorCode: "RATE_LIMIT_EXCEEDED"
476
+ };
477
+ } else {
478
+ await cacheOptions.put(key, current.toString());
479
+ }
480
+ return {
481
+ rateLimited: false
482
+ };
483
+ }
484
+
485
+ async function usageLimit(apiKeyMeta, serviceConfig) {
486
+ const {
487
+ limits,
488
+ usage
489
+ } = apiKeyMeta;
490
+ const {
491
+ serviceScope
492
+ } = serviceConfig;
493
+ const limit = limits[serviceScope];
494
+ if (!usage || !(serviceScope in usage) || limit === undefined) {
495
+ // No usage limit is provided. Assume the request is not limited.
496
+ return {
497
+ usageLimited: false
498
+ };
499
+ }
500
+ if (serviceScope === "storage" && (usage.storage?.sumFileSizeBytes || 0) > limit) {
501
+ return {
502
+ usageLimited: true,
503
+ status: 402,
504
+ errorMessage: `You've used all of your total usage limit for Storage Pinning. Please add your payment method at https://thirdweb.com/dashboard/settings/billing.`,
505
+ errorCode: "PAYMENT_METHOD_REQUIRED"
506
+ };
507
+ }
508
+ return {
509
+ usageLimited: false
510
+ };
511
+ }
512
+
424
513
  exports.authorize = authorize;
514
+ exports.rateLimit = rateLimit;
515
+ exports.usageLimit = usageLimit;
@@ -42,6 +42,25 @@ async function fetchAccountFromApi(jwt, config, useWalletAuth) {
42
42
  }
43
43
  return json;
44
44
  }
45
+ async function updateRateLimitedAt(apiKeyId, config) {
46
+ const {
47
+ apiUrl,
48
+ serviceScope: scope,
49
+ serviceApiKey
50
+ } = config;
51
+ const url = `${apiUrl}/usage/rateLimit`;
52
+ await fetch(url, {
53
+ method: "PUT",
54
+ headers: {
55
+ "x-service-api-key": serviceApiKey,
56
+ "content-type": "application/json"
57
+ },
58
+ body: JSON.stringify({
59
+ apiKeyId,
60
+ scope
61
+ })
62
+ });
63
+ }
45
64
 
46
65
  function authorizeClient(authOptions, apiKeyMeta) {
47
66
  const {
@@ -419,4 +438,74 @@ async function authorize(authData, serviceConfig, cacheOptions) {
419
438
  };
420
439
  }
421
440
 
422
- export { authorize as a };
441
+ const DEFAULT_RATE_LIMIT_WINDOW_SECONDS = 10;
442
+ async function rateLimit(apiKeyMeta, serviceConfig, cacheOptions) {
443
+ const {
444
+ id,
445
+ rateLimits,
446
+ accountId
447
+ } = apiKeyMeta;
448
+ const {
449
+ serviceScope
450
+ } = serviceConfig;
451
+ const limit = rateLimits[serviceScope];
452
+ if (limit === undefined) {
453
+ // No rate limit is provided. Assume the request is not rate limited.
454
+ return {
455
+ rateLimited: false
456
+ };
457
+ }
458
+
459
+ // Floors the current time to the nearest DEFAULT_RATE_LIMIT_WINDOW_SECONDS.
460
+ const bucketId = Math.floor(Date.now() / (1000 * DEFAULT_RATE_LIMIT_WINDOW_SECONDS)) * DEFAULT_RATE_LIMIT_WINDOW_SECONDS;
461
+ const key = [serviceScope, accountId, bucketId].join(":");
462
+ const value = parseInt((await cacheOptions.get(key)) || "0");
463
+ const current = value + 1;
464
+
465
+ // limit is in seconds, but we need in DEFAULT_RATE_LIMIT_WINDOW_SECONDS
466
+ if (current > limit * DEFAULT_RATE_LIMIT_WINDOW_SECONDS) {
467
+ // report rate limit hits
468
+ await updateRateLimitedAt(id, serviceConfig);
469
+ return {
470
+ rateLimited: true,
471
+ status: 429,
472
+ errorMessage: `You've exceeded your ${serviceScope} rate limit at ${limit} reqs/sec. To get higher rate limits, contact us at https://thirdweb.com/contact-us.`,
473
+ errorCode: "RATE_LIMIT_EXCEEDED"
474
+ };
475
+ } else {
476
+ await cacheOptions.put(key, current.toString());
477
+ }
478
+ return {
479
+ rateLimited: false
480
+ };
481
+ }
482
+
483
+ async function usageLimit(apiKeyMeta, serviceConfig) {
484
+ const {
485
+ limits,
486
+ usage
487
+ } = apiKeyMeta;
488
+ const {
489
+ serviceScope
490
+ } = serviceConfig;
491
+ const limit = limits[serviceScope];
492
+ if (!usage || !(serviceScope in usage) || limit === undefined) {
493
+ // No usage limit is provided. Assume the request is not limited.
494
+ return {
495
+ usageLimited: false
496
+ };
497
+ }
498
+ if (serviceScope === "storage" && (usage.storage?.sumFileSizeBytes || 0) > limit) {
499
+ return {
500
+ usageLimited: true,
501
+ status: 402,
502
+ errorMessage: `You've used all of your total usage limit for Storage Pinning. Please add your payment method at https://thirdweb.com/dashboard/settings/billing.`,
503
+ errorCode: "PAYMENT_METHOD_REQUIRED"
504
+ };
505
+ }
506
+ return {
507
+ usageLimited: false
508
+ };
509
+ }
510
+
511
+ export { authorize as a, rateLimit as r, usageLimit as u };
@@ -3,9 +3,26 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var node_crypto = require('node:crypto');
6
- var index = require('../../dist/index-cd4f96ef.cjs.dev.js');
6
+ var index = require('../../dist/index-4e1ffe00.cjs.dev.js');
7
7
  var services = require('../../dist/services-a3f36057.cjs.dev.js');
8
8
 
9
+ /**
10
+ *
11
+ * @param {AuthInput['req']} authInput.req - The incoming request from which information will be pulled from. These information includes (checks are in order and terminates on first match):
12
+ * - clientId: Checks header `x-client-id`, search param `clientId`
13
+ * - bundleId: Checks header `x-bundle-id`, search param `bundleId`
14
+ * - secretKey: Checks header `x-secret-key`
15
+ * - origin (the requesting domain): Checks header `origin`, `referer`
16
+ * @param {AuthInput['clientId']} authInput.clientId - Overrides any clientId found on the `req` object
17
+ * @param {AuthInput['targetAddress']} authInput.targetAddress - Only used in smart wallets to determine if the request is authorized to interact with the target address.
18
+ * @param {NodeServiceConfig['enforceAuth']} serviceConfig - Always `true` unless you need to turn auth off. Tells the service whether or not to enforce auth.
19
+ * @param {NodeServiceConfig['apiUrl']} serviceConfig.apiUrl - The url of the api server to fetch information for verification. `https://api.thirdweb.com` for production and `https://api.staging.thirdweb.com` for staging
20
+ * @param {NodeServiceConfig['serviceApiKey']} serviceConfig.serviceApiKey - secret key to be used authenticate the caller of the api-server. Check the api-server's env variable for the keys.
21
+ * @param {NodeServiceConfig['serviceScope']} serviceConfig.serviceScope - The service that we are requesting authorization for. E.g. `relayer`, `rpc`, 'bundler', 'storage' etc.
22
+ * @param {NodeServiceConfig['serviceAction']} serviceConfig.serviceAction - Needed when the `serviceScope` is `storage`. Can be either `read` or `write`.
23
+ * @param {NodeServiceConfig['useWalletAuth']} serviceConfig.useWalletAuth - If true it pings the `wallet/me` or else, `account/me`. You most likely can leave this as false.
24
+ * @returns {AuthorizationResult} authorizationResult - contains if the request is authorized, and information about the account if it is authorized. Otherwise, it contains the error message and status code.
25
+ */
9
26
  async function authorizeNode(authInput, serviceConfig) {
10
27
  let authData;
11
28
  try {
@@ -156,6 +173,8 @@ function logHttpRequest(_ref) {
156
173
  console.log(`statusMessage=${_statusMessage}`);
157
174
  }
158
175
 
176
+ exports.rateLimit = index.rateLimit;
177
+ exports.usageLimit = index.usageLimit;
159
178
  exports.SERVICES = services.SERVICES;
160
179
  exports.SERVICE_DEFINITIONS = services.SERVICE_DEFINITIONS;
161
180
  exports.SERVICE_NAMES = services.SERVICE_NAMES;
@@ -3,9 +3,26 @@
3
3
  Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var node_crypto = require('node:crypto');
6
- var index = require('../../dist/index-6e0ecc5f.cjs.prod.js');
6
+ var index = require('../../dist/index-21ba31a2.cjs.prod.js');
7
7
  var services = require('../../dist/services-9e185105.cjs.prod.js');
8
8
 
9
+ /**
10
+ *
11
+ * @param {AuthInput['req']} authInput.req - The incoming request from which information will be pulled from. These information includes (checks are in order and terminates on first match):
12
+ * - clientId: Checks header `x-client-id`, search param `clientId`
13
+ * - bundleId: Checks header `x-bundle-id`, search param `bundleId`
14
+ * - secretKey: Checks header `x-secret-key`
15
+ * - origin (the requesting domain): Checks header `origin`, `referer`
16
+ * @param {AuthInput['clientId']} authInput.clientId - Overrides any clientId found on the `req` object
17
+ * @param {AuthInput['targetAddress']} authInput.targetAddress - Only used in smart wallets to determine if the request is authorized to interact with the target address.
18
+ * @param {NodeServiceConfig['enforceAuth']} serviceConfig - Always `true` unless you need to turn auth off. Tells the service whether or not to enforce auth.
19
+ * @param {NodeServiceConfig['apiUrl']} serviceConfig.apiUrl - The url of the api server to fetch information for verification. `https://api.thirdweb.com` for production and `https://api.staging.thirdweb.com` for staging
20
+ * @param {NodeServiceConfig['serviceApiKey']} serviceConfig.serviceApiKey - secret key to be used authenticate the caller of the api-server. Check the api-server's env variable for the keys.
21
+ * @param {NodeServiceConfig['serviceScope']} serviceConfig.serviceScope - The service that we are requesting authorization for. E.g. `relayer`, `rpc`, 'bundler', 'storage' etc.
22
+ * @param {NodeServiceConfig['serviceAction']} serviceConfig.serviceAction - Needed when the `serviceScope` is `storage`. Can be either `read` or `write`.
23
+ * @param {NodeServiceConfig['useWalletAuth']} serviceConfig.useWalletAuth - If true it pings the `wallet/me` or else, `account/me`. You most likely can leave this as false.
24
+ * @returns {AuthorizationResult} authorizationResult - contains if the request is authorized, and information about the account if it is authorized. Otherwise, it contains the error message and status code.
25
+ */
9
26
  async function authorizeNode(authInput, serviceConfig) {
10
27
  let authData;
11
28
  try {
@@ -156,6 +173,8 @@ function logHttpRequest(_ref) {
156
173
  console.log(`statusMessage=${_statusMessage}`);
157
174
  }
158
175
 
176
+ exports.rateLimit = index.rateLimit;
177
+ exports.usageLimit = index.usageLimit;
159
178
  exports.SERVICES = services.SERVICES;
160
179
  exports.SERVICE_DEFINITIONS = services.SERVICE_DEFINITIONS;
161
180
  exports.SERVICE_NAMES = services.SERVICE_NAMES;
@@ -1,7 +1,25 @@
1
1
  import { createHash } from 'node:crypto';
2
- import { a as authorize } from '../../dist/index-ffddf746.esm.js';
2
+ import { a as authorize } from '../../dist/index-524baaec.esm.js';
3
+ export { r as rateLimit, u as usageLimit } from '../../dist/index-524baaec.esm.js';
3
4
  export { b as SERVICES, S as SERVICE_DEFINITIONS, a as SERVICE_NAMES, g as getServiceByName } from '../../dist/services-86283509.esm.js';
4
5
 
6
+ /**
7
+ *
8
+ * @param {AuthInput['req']} authInput.req - The incoming request from which information will be pulled from. These information includes (checks are in order and terminates on first match):
9
+ * - clientId: Checks header `x-client-id`, search param `clientId`
10
+ * - bundleId: Checks header `x-bundle-id`, search param `bundleId`
11
+ * - secretKey: Checks header `x-secret-key`
12
+ * - origin (the requesting domain): Checks header `origin`, `referer`
13
+ * @param {AuthInput['clientId']} authInput.clientId - Overrides any clientId found on the `req` object
14
+ * @param {AuthInput['targetAddress']} authInput.targetAddress - Only used in smart wallets to determine if the request is authorized to interact with the target address.
15
+ * @param {NodeServiceConfig['enforceAuth']} serviceConfig - Always `true` unless you need to turn auth off. Tells the service whether or not to enforce auth.
16
+ * @param {NodeServiceConfig['apiUrl']} serviceConfig.apiUrl - The url of the api server to fetch information for verification. `https://api.thirdweb.com` for production and `https://api.staging.thirdweb.com` for staging
17
+ * @param {NodeServiceConfig['serviceApiKey']} serviceConfig.serviceApiKey - secret key to be used authenticate the caller of the api-server. Check the api-server's env variable for the keys.
18
+ * @param {NodeServiceConfig['serviceScope']} serviceConfig.serviceScope - The service that we are requesting authorization for. E.g. `relayer`, `rpc`, 'bundler', 'storage' etc.
19
+ * @param {NodeServiceConfig['serviceAction']} serviceConfig.serviceAction - Needed when the `serviceScope` is `storage`. Can be either `read` or `write`.
20
+ * @param {NodeServiceConfig['useWalletAuth']} serviceConfig.useWalletAuth - If true it pings the `wallet/me` or else, `account/me`. You most likely can leave this as false.
21
+ * @returns {AuthorizationResult} authorizationResult - contains if the request is authorized, and information about the account if it is authorized. Otherwise, it contains the error message and status code.
22
+ */
5
23
  async function authorizeNode(authInput, serviceConfig) {
6
24
  let authData;
7
25
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thirdweb-dev/service-utils",
3
- "version": "0.0.0-dev-c3925f3-20230905155015",
3
+ "version": "0.0.0-dev-92be883-20230913130717",
4
4
  "main": "dist/thirdweb-dev-service-utils.cjs.js",
5
5
  "module": "dist/thirdweb-dev-service-utils.esm.js",
6
6
  "exports": {
@@ -52,6 +52,7 @@
52
52
  "eslint-config-thirdweb": "^0.1.6",
53
53
  "jest": "^29.6.2",
54
54
  "prettier": "^3.0.0",
55
+ "ts-jest": "^29.1.1",
55
56
  "typescript": "^5.1.6"
56
57
  },
57
58
  "dependencies": {