@panoptic-it-solutions/zoho-projects-client 0.1.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 (50) hide show
  1. package/README.md +231 -0
  2. package/dist/auth/token-manager.d.ts +59 -0
  3. package/dist/auth/token-manager.d.ts.map +1 -0
  4. package/dist/auth/token-manager.js +120 -0
  5. package/dist/auth/token-manager.js.map +1 -0
  6. package/dist/client.d.ts +193 -0
  7. package/dist/client.d.ts.map +1 -0
  8. package/dist/client.js +357 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/errors.d.ts +61 -0
  11. package/dist/errors.d.ts.map +1 -0
  12. package/dist/errors.js +113 -0
  13. package/dist/errors.js.map +1 -0
  14. package/dist/index.d.ts +7 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +11 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/types/common.d.ts +173 -0
  19. package/dist/types/common.d.ts.map +1 -0
  20. package/dist/types/common.js +56 -0
  21. package/dist/types/common.js.map +1 -0
  22. package/dist/types/index.d.ts +6 -0
  23. package/dist/types/index.d.ts.map +1 -0
  24. package/dist/types/index.js +11 -0
  25. package/dist/types/index.js.map +1 -0
  26. package/dist/types/projects.d.ts +3671 -0
  27. package/dist/types/projects.d.ts.map +1 -0
  28. package/dist/types/projects.js +94 -0
  29. package/dist/types/projects.js.map +1 -0
  30. package/dist/types/tasks.d.ts +2612 -0
  31. package/dist/types/tasks.d.ts.map +1 -0
  32. package/dist/types/tasks.js +127 -0
  33. package/dist/types/tasks.js.map +1 -0
  34. package/dist/types/timelogs.d.ts +13623 -0
  35. package/dist/types/timelogs.d.ts.map +1 -0
  36. package/dist/types/timelogs.js +115 -0
  37. package/dist/types/timelogs.js.map +1 -0
  38. package/dist/types/users.d.ts +695 -0
  39. package/dist/types/users.d.ts.map +1 -0
  40. package/dist/types/users.js +65 -0
  41. package/dist/types/users.js.map +1 -0
  42. package/dist/utils/pagination.d.ts +59 -0
  43. package/dist/utils/pagination.d.ts.map +1 -0
  44. package/dist/utils/pagination.js +84 -0
  45. package/dist/utils/pagination.js.map +1 -0
  46. package/dist/utils/rate-limiter.d.ts +33 -0
  47. package/dist/utils/rate-limiter.d.ts.map +1 -0
  48. package/dist/utils/rate-limiter.js +92 -0
  49. package/dist/utils/rate-limiter.js.map +1 -0
  50. package/package.json +53 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"users.d.ts","sourceRoot":"","sources":["../../src/types/users.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB;;GAEG;AACH,eAAO,MAAM,cAAc,aAAa,CAAC;AAEzC,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAwCP,CAAC;AAEjB,MAAM,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,UAAU,CAAC,CAAC;AAE9C;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGjC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE;;GAEG;AACH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAE7B,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAE9D;;GAEG;AACH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAGxC,CAAC;AAEH,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,6BAA6B,CAAC,CAAC"}
@@ -0,0 +1,65 @@
1
+ import { z } from "zod";
2
+ import { ZohoPageInfoSchema } from "./common.js";
3
+ /**
4
+ * User role - Zoho has many role types including custom ones
5
+ */
6
+ export const UserRoleSchema = z.string();
7
+ /**
8
+ * User from Zoho Projects API
9
+ */
10
+ export const UserSchema = z.object({
11
+ // Identification
12
+ id: z.string(),
13
+ zpuid: z.string().optional(),
14
+ name: z.string(),
15
+ email: z.string(),
16
+ // Status
17
+ active: z.boolean().optional(),
18
+ // Role & Profile
19
+ role: UserRoleSchema.optional(),
20
+ role_name: z.string().optional(),
21
+ role_id: z.string().optional(),
22
+ profile_id: z.string().optional(),
23
+ profile_name: z.string().optional(),
24
+ profile_type: z.union([z.string(), z.number()]).optional(),
25
+ // Billing & Budget
26
+ rate: z.union([z.string(), z.number()]).nullable().optional(), // Hourly rate
27
+ cost_per_hour: z.union([z.string(), z.number()]).nullable().optional(),
28
+ user_budget: z.union([z.string(), z.number()]).nullable().optional(),
29
+ revenue_budget: z.union([z.string(), z.number()]).nullable().optional(),
30
+ budget_threshold: z.union([z.string(), z.number()]).nullable().optional(),
31
+ invoice: z.string().nullable().optional(),
32
+ currency_code: z.string().optional(),
33
+ // Client-specific fields
34
+ client_company_name: z.string().nullable().optional(),
35
+ client_company_id: z.string().nullable().optional(),
36
+ // Project associations
37
+ associated_projects: z.array(z.object({
38
+ id: z.string(),
39
+ name: z.string(),
40
+ })).optional(),
41
+ // Timestamps
42
+ added_time: z.string().optional(),
43
+ added_time_long: z.number().optional(),
44
+ }).passthrough(); // Allow extra fields we haven't documented
45
+ /**
46
+ * Response wrapper for listing portal users
47
+ */
48
+ export const UserListResponseSchema = z.object({
49
+ users: z.array(UserSchema),
50
+ page_info: ZohoPageInfoSchema.optional(),
51
+ });
52
+ /**
53
+ * Response wrapper for getting a single user
54
+ */
55
+ export const UserResponseSchema = z.object({
56
+ users: z.array(UserSchema),
57
+ });
58
+ /**
59
+ * Response wrapper for listing project users
60
+ */
61
+ export const ProjectUserListResponseSchema = z.object({
62
+ users: z.array(UserSchema),
63
+ page_info: ZohoPageInfoSchema.optional(),
64
+ });
65
+ //# sourceMappingURL=users.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"users.js","sourceRoot":"","sources":["../../src/types/users.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import type { ZohoPageInfo } from "../types/index.js";
2
+ /**
3
+ * Default page size for Zoho Projects API
4
+ * Maximum allowed is 200
5
+ */
6
+ export declare const DEFAULT_PAGE_SIZE = 100;
7
+ /**
8
+ * Maximum page size allowed by Zoho
9
+ */
10
+ export declare const MAX_PAGE_SIZE = 200;
11
+ /**
12
+ * Generic paginated response structure
13
+ */
14
+ export interface PaginatedResponse<T> {
15
+ data: T[];
16
+ pageInfo?: ZohoPageInfo;
17
+ }
18
+ /**
19
+ * Options for auto-pagination
20
+ */
21
+ export interface AutoPaginateOptions {
22
+ /** Maximum number of items to fetch (default: unlimited) */
23
+ maxItems?: number;
24
+ /** Number of items per page (default: 100, max: 200) */
25
+ pageSize?: number;
26
+ }
27
+ /**
28
+ * Create an async generator that automatically handles pagination
29
+ *
30
+ * Usage:
31
+ * ```ts
32
+ * for await (const project of autoPaginate((index) =>
33
+ * client.projects.list({ index, range: 100 })
34
+ * )) {
35
+ * console.log(project);
36
+ * }
37
+ * ```
38
+ *
39
+ * @param fetchPage Function that fetches a page given the index (0-based)
40
+ * @param options Pagination options
41
+ */
42
+ export declare function autoPaginate<T>(fetchPage: (index: number, range: number) => Promise<PaginatedResponse<T>>, options?: AutoPaginateOptions): AsyncGenerator<T, void, unknown>;
43
+ /**
44
+ * Collect all items from an async generator into an array
45
+ *
46
+ * Usage:
47
+ * ```ts
48
+ * const allProjects = await collectAll(autoPaginate(...));
49
+ * ```
50
+ */
51
+ export declare function collectAll<T>(generator: AsyncGenerator<T, void, unknown>): Promise<T[]>;
52
+ /**
53
+ * Helper to create pagination params for Zoho API
54
+ */
55
+ export declare function createPaginationParams(index?: number, range?: number): {
56
+ index: number;
57
+ range: number;
58
+ };
59
+ //# sourceMappingURL=pagination.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.d.ts","sourceRoot":"","sources":["../../src/utils/pagination.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEtD;;;GAGG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAC;AAErC;;GAEG;AACH,eAAO,MAAM,aAAa,MAAM,CAAC;AAEjC;;GAEG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC;IAClC,IAAI,EAAE,CAAC,EAAE,CAAC;IACV,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,4DAA4D;IAC5D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,wDAAwD;IACxD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAuB,YAAY,CAAC,CAAC,EACnC,SAAS,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,EAC1E,OAAO,GAAE,mBAAwB,GAChC,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,CAqClC;AAED;;;;;;;GAOG;AACH,wBAAsB,UAAU,CAAC,CAAC,EAChC,SAAS,EAAE,cAAc,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,GAC1C,OAAO,CAAC,CAAC,EAAE,CAAC,CAMd;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,GAAE,MAAU,EACjB,KAAK,GAAE,MAA0B,GAChC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAKlC"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Default page size for Zoho Projects API
3
+ * Maximum allowed is 200
4
+ */
5
+ export const DEFAULT_PAGE_SIZE = 100;
6
+ /**
7
+ * Maximum page size allowed by Zoho
8
+ */
9
+ export const MAX_PAGE_SIZE = 200;
10
+ /**
11
+ * Create an async generator that automatically handles pagination
12
+ *
13
+ * Usage:
14
+ * ```ts
15
+ * for await (const project of autoPaginate((index) =>
16
+ * client.projects.list({ index, range: 100 })
17
+ * )) {
18
+ * console.log(project);
19
+ * }
20
+ * ```
21
+ *
22
+ * @param fetchPage Function that fetches a page given the index (0-based)
23
+ * @param options Pagination options
24
+ */
25
+ export async function* autoPaginate(fetchPage, options = {}) {
26
+ const pageSize = Math.min(options.pageSize ?? DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE);
27
+ const maxItems = options.maxItems ?? Infinity;
28
+ let index = 0;
29
+ let itemsYielded = 0;
30
+ let hasMore = true;
31
+ while (hasMore && itemsYielded < maxItems) {
32
+ const response = await fetchPage(index, pageSize);
33
+ const items = response.data;
34
+ if (items.length === 0) {
35
+ // No more items
36
+ hasMore = false;
37
+ break;
38
+ }
39
+ for (const item of items) {
40
+ if (itemsYielded >= maxItems) {
41
+ return;
42
+ }
43
+ yield item;
44
+ itemsYielded++;
45
+ }
46
+ // Determine if there are more pages
47
+ if (response.pageInfo?.has_more_page === false) {
48
+ hasMore = false;
49
+ }
50
+ else if (items.length < pageSize) {
51
+ // Received fewer items than requested, likely last page
52
+ hasMore = false;
53
+ }
54
+ else {
55
+ // Move to next page
56
+ index += pageSize;
57
+ }
58
+ }
59
+ }
60
+ /**
61
+ * Collect all items from an async generator into an array
62
+ *
63
+ * Usage:
64
+ * ```ts
65
+ * const allProjects = await collectAll(autoPaginate(...));
66
+ * ```
67
+ */
68
+ export async function collectAll(generator) {
69
+ const items = [];
70
+ for await (const item of generator) {
71
+ items.push(item);
72
+ }
73
+ return items;
74
+ }
75
+ /**
76
+ * Helper to create pagination params for Zoho API
77
+ */
78
+ export function createPaginationParams(index = 0, range = DEFAULT_PAGE_SIZE) {
79
+ return {
80
+ index,
81
+ range: Math.min(range, MAX_PAGE_SIZE),
82
+ };
83
+ }
84
+ //# sourceMappingURL=pagination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pagination.js","sourceRoot":"","sources":["../../src/utils/pagination.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAErC;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,GAAG,CAAC;AAoBjC;;;;;;;;;;;;;;GAcG;AACH,MAAM,CAAC,KAAK,SAAS,CAAC,CAAC,YAAY,CACjC,SAA0E,EAC1E,UAA+B,EAAE;IAEjC,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,iBAAiB,EAAE,aAAa,CAAC,CAAC;IAChF,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,IAAI,QAAQ,CAAC;IAE9C,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,IAAI,OAAO,GAAG,IAAI,CAAC;IAEnB,OAAO,OAAO,IAAI,YAAY,GAAG,QAAQ,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC;QAE5B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,gBAAgB;YAChB,OAAO,GAAG,KAAK,CAAC;YAChB,MAAM;QACR,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,YAAY,IAAI,QAAQ,EAAE,CAAC;gBAC7B,OAAO;YACT,CAAC;YACD,MAAM,IAAI,CAAC;YACX,YAAY,EAAE,CAAC;QACjB,CAAC;QAED,oCAAoC;QACpC,IAAI,QAAQ,CAAC,QAAQ,EAAE,aAAa,KAAK,KAAK,EAAE,CAAC;YAC/C,OAAO,GAAG,KAAK,CAAC;QAClB,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,GAAG,QAAQ,EAAE,CAAC;YACnC,wDAAwD;YACxD,OAAO,GAAG,KAAK,CAAC;QAClB,CAAC;aAAM,CAAC;YACN,oBAAoB;YACpB,KAAK,IAAI,QAAQ,CAAC;QACpB,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,SAA2C;IAE3C,MAAM,KAAK,GAAQ,EAAE,CAAC;IACtB,IAAI,KAAK,EAAE,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QACnC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACpC,QAAgB,CAAC,EACjB,QAAgB,iBAAiB;IAEjC,OAAO;QACL,KAAK;QACL,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,aAAa,CAAC;KACtC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,33 @@
1
+ import Bottleneck from "bottleneck";
2
+ /**
3
+ * Configuration for rate limiting
4
+ */
5
+ export interface RateLimiterConfig {
6
+ /**
7
+ * Redis configuration for distributed rate limiting
8
+ * If not provided, uses in-memory limiting (single instance only)
9
+ */
10
+ redis?: {
11
+ url: string;
12
+ };
13
+ }
14
+ /**
15
+ * Create a rate limiter for Zoho Projects API
16
+ *
17
+ * Uses Bottleneck's reservoir pattern to enforce:
18
+ * - Max 90 requests per 2 minutes (with 10 request safety margin)
19
+ * - Min 1.3s between requests
20
+ * - Max 5 concurrent requests
21
+ *
22
+ * Handles 429 responses by pausing for 30-minute lockout
23
+ */
24
+ export declare function createRateLimiter(config?: RateLimiterConfig): Bottleneck;
25
+ /**
26
+ * Get rate limiter stats for monitoring
27
+ */
28
+ export declare function getRateLimiterStats(limiter: Bottleneck): {
29
+ running: number;
30
+ queued: number;
31
+ reservoir: number | null;
32
+ };
33
+ //# sourceMappingURL=rate-limiter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/utils/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AAEpC;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;;OAGG;IACH,KAAK,CAAC,EAAE;QACN,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;CACH;AAsBD;;;;;;;;;GASG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,UAAU,CAkDxE;AAaD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,UAAU,GAAG;IACxD,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAOA"}
@@ -0,0 +1,92 @@
1
+ import Bottleneck from "bottleneck";
2
+ /**
3
+ * Zoho Projects API rate limits:
4
+ * - 100 requests per 2 minutes (per user/org)
5
+ * - 429 response triggers 30-minute lockout
6
+ */
7
+ const ZOHO_RATE_LIMIT = {
8
+ /** Maximum requests in the time window */
9
+ maxRequests: 100,
10
+ /** Time window in milliseconds (2 minutes) */
11
+ windowMs: 2 * 60 * 1000,
12
+ /** Safety margin - we use 90 requests to avoid hitting the exact limit */
13
+ safeMaxRequests: 90,
14
+ /** Minimum time between requests in milliseconds (~1.3s for safety) */
15
+ minTime: 1300,
16
+ /** Maximum concurrent requests */
17
+ maxConcurrent: 5,
18
+ /** Lockout duration when rate limited (30 minutes) */
19
+ lockoutMs: 30 * 60 * 1000,
20
+ };
21
+ /**
22
+ * Create a rate limiter for Zoho Projects API
23
+ *
24
+ * Uses Bottleneck's reservoir pattern to enforce:
25
+ * - Max 90 requests per 2 minutes (with 10 request safety margin)
26
+ * - Min 1.3s between requests
27
+ * - Max 5 concurrent requests
28
+ *
29
+ * Handles 429 responses by pausing for 30-minute lockout
30
+ */
31
+ export function createRateLimiter(config) {
32
+ const limiterOptions = {
33
+ // Reservoir pattern for request budget
34
+ reservoir: ZOHO_RATE_LIMIT.safeMaxRequests,
35
+ reservoirRefreshAmount: ZOHO_RATE_LIMIT.safeMaxRequests,
36
+ reservoirRefreshInterval: ZOHO_RATE_LIMIT.windowMs,
37
+ // Concurrency controls
38
+ maxConcurrent: ZOHO_RATE_LIMIT.maxConcurrent,
39
+ minTime: ZOHO_RATE_LIMIT.minTime,
40
+ };
41
+ // Add Redis clustering if configured
42
+ if (config?.redis) {
43
+ const redisConnection = new Bottleneck.IORedisConnection({
44
+ client: createRedisClient(config.redis.url),
45
+ });
46
+ return new Bottleneck({
47
+ ...limiterOptions,
48
+ id: "zoho-projects-rate-limiter",
49
+ datastore: "ioredis",
50
+ clearDatastore: false,
51
+ connection: redisConnection,
52
+ });
53
+ }
54
+ const limiter = new Bottleneck(limiterOptions);
55
+ // Handle rate limit errors with 30-minute backoff
56
+ limiter.on("failed", async (error, jobInfo) => {
57
+ const axiosError = error;
58
+ if (axiosError.response?.status === 429) {
59
+ console.warn(`[ZohoProjectsClient] Rate limit exceeded, pausing for ${ZOHO_RATE_LIMIT.lockoutMs / 1000 / 60} minutes`);
60
+ // Return retry delay in ms - Bottleneck will retry after this delay
61
+ return ZOHO_RATE_LIMIT.lockoutMs;
62
+ }
63
+ // Don't retry other errors
64
+ return undefined;
65
+ });
66
+ limiter.on("retry", (message, jobInfo) => {
67
+ console.info(`[ZohoProjectsClient] Retrying request after rate limit cooldown`);
68
+ });
69
+ return limiter;
70
+ }
71
+ /**
72
+ * Create Redis client for distributed rate limiting
73
+ * Lazy import to avoid requiring ioredis when not using Redis
74
+ */
75
+ function createRedisClient(url) {
76
+ // Dynamic import to avoid bundling ioredis when not needed
77
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
78
+ const Redis = require("ioredis");
79
+ return new Redis(url);
80
+ }
81
+ /**
82
+ * Get rate limiter stats for monitoring
83
+ */
84
+ export function getRateLimiterStats(limiter) {
85
+ const counts = limiter.counts();
86
+ return {
87
+ running: counts.RUNNING,
88
+ queued: counts.QUEUED,
89
+ reservoir: null, // Bottleneck doesn't expose current reservoir directly
90
+ };
91
+ }
92
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/utils/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,UAAU,MAAM,YAAY,CAAC;AAepC;;;;GAIG;AACH,MAAM,eAAe,GAAG;IACtB,0CAA0C;IAC1C,WAAW,EAAE,GAAG;IAChB,8CAA8C;IAC9C,QAAQ,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI;IACvB,0EAA0E;IAC1E,eAAe,EAAE,EAAE;IACnB,uEAAuE;IACvE,OAAO,EAAE,IAAI;IACb,kCAAkC;IAClC,aAAa,EAAE,CAAC;IAChB,sDAAsD;IACtD,SAAS,EAAE,EAAE,GAAG,EAAE,GAAG,IAAI;CAC1B,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAA0B;IAC1D,MAAM,cAAc,GAAkC;QACpD,uCAAuC;QACvC,SAAS,EAAE,eAAe,CAAC,eAAe;QAC1C,sBAAsB,EAAE,eAAe,CAAC,eAAe;QACvD,wBAAwB,EAAE,eAAe,CAAC,QAAQ;QAElD,uBAAuB;QACvB,aAAa,EAAE,eAAe,CAAC,aAAa;QAC5C,OAAO,EAAE,eAAe,CAAC,OAAO;KACjC,CAAC;IAEF,qCAAqC;IACrC,IAAI,MAAM,EAAE,KAAK,EAAE,CAAC;QAClB,MAAM,eAAe,GAAG,IAAI,UAAU,CAAC,iBAAiB,CAAC;YACvD,MAAM,EAAE,iBAAiB,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC;SAC5C,CAAC,CAAC;QAEH,OAAO,IAAI,UAAU,CAAC;YACpB,GAAG,cAAc;YACjB,EAAE,EAAE,4BAA4B;YAChC,SAAS,EAAE,SAAS;YACpB,cAAc,EAAE,KAAK;YACrB,UAAU,EAAE,eAAe;SAC5B,CAAC,CAAC;IACL,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,UAAU,CAAC,cAAc,CAAC,CAAC;IAE/C,kDAAkD;IAClD,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE;QAC5C,MAAM,UAAU,GAAG,KAA2C,CAAC;QAC/D,IAAI,UAAU,CAAC,QAAQ,EAAE,MAAM,KAAK,GAAG,EAAE,CAAC;YACxC,OAAO,CAAC,IAAI,CACV,yDAAyD,eAAe,CAAC,SAAS,GAAG,IAAI,GAAG,EAAE,UAAU,CACzG,CAAC;YACF,oEAAoE;YACpE,OAAO,eAAe,CAAC,SAAS,CAAC;QACnC,CAAC;QACD,2BAA2B;QAC3B,OAAO,SAAS,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,EAAE;QACvC,OAAO,CAAC,IAAI,CACV,iEAAiE,CAClE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAC,GAAW;IACpC,2DAA2D;IAC3D,iEAAiE;IACjE,MAAM,KAAK,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACjC,OAAO,IAAI,KAAK,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAmB;IAKrD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAChC,OAAO;QACL,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,SAAS,EAAE,IAAI,EAAE,uDAAuD;KACzE,CAAC;AACJ,CAAC"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@panoptic-it-solutions/zoho-projects-client",
3
+ "version": "0.1.0",
4
+ "description": "TypeScript client for Zoho Projects V3 API with OAuth 2.0 and rate limiting",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "clean": "rm -rf dist",
20
+ "prepublishOnly": "npm run clean && npm run build"
21
+ },
22
+ "keywords": [
23
+ "zoho",
24
+ "zoho-projects",
25
+ "api-client",
26
+ "typescript"
27
+ ],
28
+ "author": "Panoptic IT Solutions",
29
+ "license": "MIT",
30
+ "repository": {
31
+ "type": "git",
32
+ "url": "git+https://github.com/panoptic-it-solutions/zoho-projects-client.git"
33
+ },
34
+ "bugs": {
35
+ "url": "https://github.com/panoptic-it-solutions/zoho-projects-client/issues"
36
+ },
37
+ "homepage": "https://github.com/panoptic-it-solutions/zoho-projects-client#readme",
38
+ "publishConfig": {
39
+ "access": "public"
40
+ },
41
+ "dependencies": {
42
+ "axios": "^1.7.0",
43
+ "bottleneck": "^2.19.5",
44
+ "zod": "^3.23.0"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^22.0.0",
48
+ "typescript": "^5.6.0"
49
+ },
50
+ "engines": {
51
+ "node": ">=18.0.0"
52
+ }
53
+ }