@suiteportal/connector 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.
@@ -0,0 +1,212 @@
1
+ /** Configuration for connecting to a NetSuite account. */
2
+ interface NetSuiteConfig {
3
+ /** NetSuite account ID (e.g. "1234567" or "1234567_SB1" for sandbox). */
4
+ accountId: string;
5
+ /** OAuth 1.0a consumer key (integration record). */
6
+ consumerKey: string;
7
+ /** OAuth 1.0a consumer secret. */
8
+ consumerSecret: string;
9
+ /** OAuth 1.0a token ID. */
10
+ tokenId: string;
11
+ /** OAuth 1.0a token secret. */
12
+ tokenSecret: string;
13
+ /** Request timeout in milliseconds. Default: 30000. */
14
+ timeout?: number;
15
+ /** Max concurrent requests. Default: 5. */
16
+ concurrency?: number;
17
+ /** Max retry attempts for retryable failures. Default: 3. */
18
+ maxRetries?: number;
19
+ /** Override the base URL (mainly for testing). */
20
+ baseUrl?: string;
21
+ }
22
+ /** A single row returned by SuiteQL — column names as keys. */
23
+ type SuiteQLRow = Record<string, unknown>;
24
+ /** Result of a SuiteQL query execution. */
25
+ interface SuiteQLResult<T = SuiteQLRow> {
26
+ items: T[];
27
+ totalResults?: number;
28
+ hasMore: boolean;
29
+ }
30
+ /** Raw response shape from the SuiteQL REST endpoint. */
31
+ interface SuiteQLResponse {
32
+ items: SuiteQLRow[];
33
+ totalResults?: number;
34
+ count?: number;
35
+ hasMore: boolean;
36
+ offset: number;
37
+ links?: Array<{
38
+ rel: string;
39
+ href: string;
40
+ }>;
41
+ }
42
+ /** Parameters needed to build an OAuth 1.0a signature. */
43
+ interface OAuthParams {
44
+ consumerKey: string;
45
+ consumerSecret: string;
46
+ tokenId: string;
47
+ tokenSecret: string;
48
+ realm: string;
49
+ method: string;
50
+ url: string;
51
+ timestamp?: string;
52
+ nonce?: string;
53
+ }
54
+ /** Generic HTTP request options used internally. */
55
+ interface RequestOptions {
56
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
57
+ path: string;
58
+ body?: unknown;
59
+ headers?: Record<string, string>;
60
+ query?: Record<string, string>;
61
+ }
62
+ /** Generic HTTP response wrapper. */
63
+ interface NetSuiteResponse<T = unknown> {
64
+ status: number;
65
+ headers: Headers;
66
+ data: T;
67
+ }
68
+
69
+ interface ResolvedConfig extends Required<Omit<NetSuiteConfig, 'baseUrl'>> {
70
+ baseUrl: string;
71
+ }
72
+ /** Validate config and fill defaults. */
73
+ declare function resolveConfig(config: NetSuiteConfig): ResolvedConfig;
74
+ /**
75
+ * Derive the NetSuite REST API base URL from the account ID.
76
+ * Account IDs with underscores (sandbox) have underscores replaced with hyphens.
77
+ * Example: "1234567_SB1" → "https://1234567-sb1.suitetalk.api.netsuite.com"
78
+ */
79
+ declare function deriveBaseUrl(accountId: string): string;
80
+ /** Get the realm (account ID in uppercase, underscores preserved). */
81
+ declare function getRealm(accountId: string): string;
82
+
83
+ /**
84
+ * Core HTTP client for the NetSuite REST API.
85
+ * Handles OAuth signing, retries, rate limiting, and timeouts.
86
+ */
87
+ declare class NetSuiteClient {
88
+ private readonly config;
89
+ private readonly rateLimiter;
90
+ constructor(config: NetSuiteConfig);
91
+ /** Make an authenticated request to the NetSuite REST API. */
92
+ request<T = unknown>(options: RequestOptions): Promise<NetSuiteResponse<T>>;
93
+ /** Get the resolved base URL. */
94
+ get baseUrl(): string;
95
+ /** Get the resolved config (read-only). */
96
+ get resolvedConfig(): Readonly<ResolvedConfig>;
97
+ private executeRequest;
98
+ private buildUrl;
99
+ private handleErrorResponse;
100
+ }
101
+
102
+ /**
103
+ * Execute a SuiteQL query and return the first page of results.
104
+ */
105
+ declare function executeSuiteQL<T = SuiteQLRow>(client: NetSuiteClient, query: string, options?: {
106
+ limit?: number;
107
+ offset?: number;
108
+ }): Promise<SuiteQLResult<T>>;
109
+
110
+ interface PaginationOptions {
111
+ /** Page size per request. Default: 1000. */
112
+ pageSize?: number;
113
+ /** Maximum total rows to fetch. Default: unlimited. */
114
+ maxRows?: number;
115
+ }
116
+ /**
117
+ * Execute a SuiteQL query and automatically paginate through all results.
118
+ * Collects all pages into a single array.
119
+ */
120
+ declare function executeSuiteQLPaginated<T = SuiteQLRow>(client: NetSuiteClient, query: string, options?: PaginationOptions): Promise<T[]>;
121
+
122
+ /**
123
+ * Build the OAuth Authorization header value.
124
+ * Format: OAuth realm="...", oauth_consumer_key="...", ..., oauth_signature="..."
125
+ */
126
+ declare function buildAuthorizationHeader(params: OAuthParams): string;
127
+
128
+ /**
129
+ * RFC 5849 §3.6 percent-encoding.
130
+ * Encodes all characters except unreserved (ALPHA, DIGIT, '-', '.', '_', '~').
131
+ */
132
+ declare function percentEncode(str: string): string;
133
+ /** Generate a random nonce for OAuth requests. */
134
+ declare function generateNonce(): string;
135
+ /** Generate a Unix timestamp string. */
136
+ declare function generateTimestamp(): string;
137
+ /**
138
+ * Build the OAuth signature base string per RFC 5849 §3.4.1.
139
+ */
140
+ declare function buildSignatureBaseString(method: string, baseUrl: string, params: Array<[string, string]>): string;
141
+ /**
142
+ * Sign the base string with HMAC-SHA256.
143
+ * Signing key = percentEncode(consumerSecret) & percentEncode(tokenSecret)
144
+ */
145
+ declare function signHmacSha256(baseString: string, consumerSecret: string, tokenSecret: string): string;
146
+ /**
147
+ * Generate the full OAuth 1.0a signature for a request.
148
+ * Returns the signature string and the oauth params used (for header construction).
149
+ */
150
+ declare function generateOAuthSignature(params: OAuthParams): {
151
+ signature: string;
152
+ oauthParams: Record<string, string>;
153
+ };
154
+
155
+ /** Base error class for all NetSuite connector errors. */
156
+ declare class NetSuiteError extends Error {
157
+ readonly status?: number | undefined;
158
+ readonly code?: string | undefined;
159
+ readonly details?: unknown | undefined;
160
+ constructor(message: string, status?: number | undefined, code?: string | undefined, details?: unknown | undefined);
161
+ }
162
+ /** Authentication or authorization failure. */
163
+ declare class AuthError extends NetSuiteError {
164
+ constructor(message: string, status?: number, details?: unknown);
165
+ }
166
+ /** Rate limit (429) exceeded. */
167
+ declare class RateLimitError extends NetSuiteError {
168
+ readonly retryAfterMs: number;
169
+ constructor(retryAfterMs: number, details?: unknown);
170
+ }
171
+ /** Request timeout via AbortController. */
172
+ declare class TimeoutError extends NetSuiteError {
173
+ constructor(timeoutMs: number);
174
+ }
175
+ /** Check if an HTTP status code is retryable. */
176
+ declare function isRetryableStatus(status: number): boolean;
177
+
178
+ /**
179
+ * Async semaphore for concurrency governance.
180
+ * Limits the number of in-flight requests to prevent overwhelming NetSuite.
181
+ */
182
+ declare class RateLimiter {
183
+ private readonly maxConcurrent;
184
+ private active;
185
+ private queue;
186
+ constructor(maxConcurrent: number);
187
+ /** Acquire a slot. Resolves when a slot is available. */
188
+ acquire(): Promise<void>;
189
+ /** Release a slot, unblocking the next waiter if any. */
190
+ release(): void;
191
+ /** Run an async function within the concurrency limit. */
192
+ run<T>(fn: () => Promise<T>): Promise<T>;
193
+ /** Number of currently active tasks. */
194
+ get activeCount(): number;
195
+ /** Number of tasks waiting for a slot. */
196
+ get waitingCount(): number;
197
+ }
198
+
199
+ interface RetryOptions {
200
+ maxRetries: number;
201
+ /** Base delay in ms before first retry. Default: 500. */
202
+ baseDelay?: number;
203
+ /** Maximum delay in ms. Default: 30000. */
204
+ maxDelay?: number;
205
+ }
206
+ /**
207
+ * Execute a function with exponential backoff + jitter.
208
+ * Respects `Retry-After` from RateLimitError.
209
+ */
210
+ declare function withRetry<T>(fn: () => Promise<T>, options: RetryOptions): Promise<T>;
211
+
212
+ export { AuthError, NetSuiteClient, type NetSuiteConfig, NetSuiteError, type NetSuiteResponse, type OAuthParams, type PaginationOptions, RateLimitError, RateLimiter, type RequestOptions, type ResolvedConfig, type RetryOptions, type SuiteQLResponse, type SuiteQLResult, type SuiteQLRow, TimeoutError, buildAuthorizationHeader, buildSignatureBaseString, deriveBaseUrl, executeSuiteQL, executeSuiteQLPaginated, generateNonce, generateOAuthSignature, generateTimestamp, getRealm, isRetryableStatus, percentEncode, resolveConfig, signHmacSha256, withRetry };
@@ -0,0 +1,212 @@
1
+ /** Configuration for connecting to a NetSuite account. */
2
+ interface NetSuiteConfig {
3
+ /** NetSuite account ID (e.g. "1234567" or "1234567_SB1" for sandbox). */
4
+ accountId: string;
5
+ /** OAuth 1.0a consumer key (integration record). */
6
+ consumerKey: string;
7
+ /** OAuth 1.0a consumer secret. */
8
+ consumerSecret: string;
9
+ /** OAuth 1.0a token ID. */
10
+ tokenId: string;
11
+ /** OAuth 1.0a token secret. */
12
+ tokenSecret: string;
13
+ /** Request timeout in milliseconds. Default: 30000. */
14
+ timeout?: number;
15
+ /** Max concurrent requests. Default: 5. */
16
+ concurrency?: number;
17
+ /** Max retry attempts for retryable failures. Default: 3. */
18
+ maxRetries?: number;
19
+ /** Override the base URL (mainly for testing). */
20
+ baseUrl?: string;
21
+ }
22
+ /** A single row returned by SuiteQL — column names as keys. */
23
+ type SuiteQLRow = Record<string, unknown>;
24
+ /** Result of a SuiteQL query execution. */
25
+ interface SuiteQLResult<T = SuiteQLRow> {
26
+ items: T[];
27
+ totalResults?: number;
28
+ hasMore: boolean;
29
+ }
30
+ /** Raw response shape from the SuiteQL REST endpoint. */
31
+ interface SuiteQLResponse {
32
+ items: SuiteQLRow[];
33
+ totalResults?: number;
34
+ count?: number;
35
+ hasMore: boolean;
36
+ offset: number;
37
+ links?: Array<{
38
+ rel: string;
39
+ href: string;
40
+ }>;
41
+ }
42
+ /** Parameters needed to build an OAuth 1.0a signature. */
43
+ interface OAuthParams {
44
+ consumerKey: string;
45
+ consumerSecret: string;
46
+ tokenId: string;
47
+ tokenSecret: string;
48
+ realm: string;
49
+ method: string;
50
+ url: string;
51
+ timestamp?: string;
52
+ nonce?: string;
53
+ }
54
+ /** Generic HTTP request options used internally. */
55
+ interface RequestOptions {
56
+ method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
57
+ path: string;
58
+ body?: unknown;
59
+ headers?: Record<string, string>;
60
+ query?: Record<string, string>;
61
+ }
62
+ /** Generic HTTP response wrapper. */
63
+ interface NetSuiteResponse<T = unknown> {
64
+ status: number;
65
+ headers: Headers;
66
+ data: T;
67
+ }
68
+
69
+ interface ResolvedConfig extends Required<Omit<NetSuiteConfig, 'baseUrl'>> {
70
+ baseUrl: string;
71
+ }
72
+ /** Validate config and fill defaults. */
73
+ declare function resolveConfig(config: NetSuiteConfig): ResolvedConfig;
74
+ /**
75
+ * Derive the NetSuite REST API base URL from the account ID.
76
+ * Account IDs with underscores (sandbox) have underscores replaced with hyphens.
77
+ * Example: "1234567_SB1" → "https://1234567-sb1.suitetalk.api.netsuite.com"
78
+ */
79
+ declare function deriveBaseUrl(accountId: string): string;
80
+ /** Get the realm (account ID in uppercase, underscores preserved). */
81
+ declare function getRealm(accountId: string): string;
82
+
83
+ /**
84
+ * Core HTTP client for the NetSuite REST API.
85
+ * Handles OAuth signing, retries, rate limiting, and timeouts.
86
+ */
87
+ declare class NetSuiteClient {
88
+ private readonly config;
89
+ private readonly rateLimiter;
90
+ constructor(config: NetSuiteConfig);
91
+ /** Make an authenticated request to the NetSuite REST API. */
92
+ request<T = unknown>(options: RequestOptions): Promise<NetSuiteResponse<T>>;
93
+ /** Get the resolved base URL. */
94
+ get baseUrl(): string;
95
+ /** Get the resolved config (read-only). */
96
+ get resolvedConfig(): Readonly<ResolvedConfig>;
97
+ private executeRequest;
98
+ private buildUrl;
99
+ private handleErrorResponse;
100
+ }
101
+
102
+ /**
103
+ * Execute a SuiteQL query and return the first page of results.
104
+ */
105
+ declare function executeSuiteQL<T = SuiteQLRow>(client: NetSuiteClient, query: string, options?: {
106
+ limit?: number;
107
+ offset?: number;
108
+ }): Promise<SuiteQLResult<T>>;
109
+
110
+ interface PaginationOptions {
111
+ /** Page size per request. Default: 1000. */
112
+ pageSize?: number;
113
+ /** Maximum total rows to fetch. Default: unlimited. */
114
+ maxRows?: number;
115
+ }
116
+ /**
117
+ * Execute a SuiteQL query and automatically paginate through all results.
118
+ * Collects all pages into a single array.
119
+ */
120
+ declare function executeSuiteQLPaginated<T = SuiteQLRow>(client: NetSuiteClient, query: string, options?: PaginationOptions): Promise<T[]>;
121
+
122
+ /**
123
+ * Build the OAuth Authorization header value.
124
+ * Format: OAuth realm="...", oauth_consumer_key="...", ..., oauth_signature="..."
125
+ */
126
+ declare function buildAuthorizationHeader(params: OAuthParams): string;
127
+
128
+ /**
129
+ * RFC 5849 §3.6 percent-encoding.
130
+ * Encodes all characters except unreserved (ALPHA, DIGIT, '-', '.', '_', '~').
131
+ */
132
+ declare function percentEncode(str: string): string;
133
+ /** Generate a random nonce for OAuth requests. */
134
+ declare function generateNonce(): string;
135
+ /** Generate a Unix timestamp string. */
136
+ declare function generateTimestamp(): string;
137
+ /**
138
+ * Build the OAuth signature base string per RFC 5849 §3.4.1.
139
+ */
140
+ declare function buildSignatureBaseString(method: string, baseUrl: string, params: Array<[string, string]>): string;
141
+ /**
142
+ * Sign the base string with HMAC-SHA256.
143
+ * Signing key = percentEncode(consumerSecret) & percentEncode(tokenSecret)
144
+ */
145
+ declare function signHmacSha256(baseString: string, consumerSecret: string, tokenSecret: string): string;
146
+ /**
147
+ * Generate the full OAuth 1.0a signature for a request.
148
+ * Returns the signature string and the oauth params used (for header construction).
149
+ */
150
+ declare function generateOAuthSignature(params: OAuthParams): {
151
+ signature: string;
152
+ oauthParams: Record<string, string>;
153
+ };
154
+
155
+ /** Base error class for all NetSuite connector errors. */
156
+ declare class NetSuiteError extends Error {
157
+ readonly status?: number | undefined;
158
+ readonly code?: string | undefined;
159
+ readonly details?: unknown | undefined;
160
+ constructor(message: string, status?: number | undefined, code?: string | undefined, details?: unknown | undefined);
161
+ }
162
+ /** Authentication or authorization failure. */
163
+ declare class AuthError extends NetSuiteError {
164
+ constructor(message: string, status?: number, details?: unknown);
165
+ }
166
+ /** Rate limit (429) exceeded. */
167
+ declare class RateLimitError extends NetSuiteError {
168
+ readonly retryAfterMs: number;
169
+ constructor(retryAfterMs: number, details?: unknown);
170
+ }
171
+ /** Request timeout via AbortController. */
172
+ declare class TimeoutError extends NetSuiteError {
173
+ constructor(timeoutMs: number);
174
+ }
175
+ /** Check if an HTTP status code is retryable. */
176
+ declare function isRetryableStatus(status: number): boolean;
177
+
178
+ /**
179
+ * Async semaphore for concurrency governance.
180
+ * Limits the number of in-flight requests to prevent overwhelming NetSuite.
181
+ */
182
+ declare class RateLimiter {
183
+ private readonly maxConcurrent;
184
+ private active;
185
+ private queue;
186
+ constructor(maxConcurrent: number);
187
+ /** Acquire a slot. Resolves when a slot is available. */
188
+ acquire(): Promise<void>;
189
+ /** Release a slot, unblocking the next waiter if any. */
190
+ release(): void;
191
+ /** Run an async function within the concurrency limit. */
192
+ run<T>(fn: () => Promise<T>): Promise<T>;
193
+ /** Number of currently active tasks. */
194
+ get activeCount(): number;
195
+ /** Number of tasks waiting for a slot. */
196
+ get waitingCount(): number;
197
+ }
198
+
199
+ interface RetryOptions {
200
+ maxRetries: number;
201
+ /** Base delay in ms before first retry. Default: 500. */
202
+ baseDelay?: number;
203
+ /** Maximum delay in ms. Default: 30000. */
204
+ maxDelay?: number;
205
+ }
206
+ /**
207
+ * Execute a function with exponential backoff + jitter.
208
+ * Respects `Retry-After` from RateLimitError.
209
+ */
210
+ declare function withRetry<T>(fn: () => Promise<T>, options: RetryOptions): Promise<T>;
211
+
212
+ export { AuthError, NetSuiteClient, type NetSuiteConfig, NetSuiteError, type NetSuiteResponse, type OAuthParams, type PaginationOptions, RateLimitError, RateLimiter, type RequestOptions, type ResolvedConfig, type RetryOptions, type SuiteQLResponse, type SuiteQLResult, type SuiteQLRow, TimeoutError, buildAuthorizationHeader, buildSignatureBaseString, deriveBaseUrl, executeSuiteQL, executeSuiteQLPaginated, generateNonce, generateOAuthSignature, generateTimestamp, getRealm, isRetryableStatus, percentEncode, resolveConfig, signHmacSha256, withRetry };