@atzentis/crossmedia-sdk 1.0.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.
- package/CHANGELOG.md +16 -0
- package/LICENSE +21 -0
- package/README.md +22 -0
- package/dist/index.d.ts +274 -0
- package/dist/index.js +256 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# @atzentis/crossmedia-sdk
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [`c9d8b81`](https://github.com/atzentis/crossmedia-sdk/commit/c9d8b81c4294d6d372492fd1486f9879b0f006e9) Thanks [@vassilibo](https://github.com/vassilibo)! - Initial public release of the Atzentis Crossmedia SDK.
|
|
8
|
+
|
|
9
|
+
Typed client for cross-network publishing (Instagram, TikTok, YouTube, Facebook, X, LinkedIn) and cross-network analytics.
|
|
10
|
+
|
|
11
|
+
- `@atzentis/crossmedia-sdk` — core HTTP client + typed error hierarchy
|
|
12
|
+
- `@atzentis/crossmedia-react` — provider + hooks
|
|
13
|
+
- `@atzentis/crossmedia-expo` — React Native / Expo surface
|
|
14
|
+
- `@atzentis/crossmedia-locales` — i18n locale packs
|
|
15
|
+
|
|
16
|
+
Consumed by Cyclus studio P11–P12 (greenfield, no local fallback).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Atzentis
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# @atzentis/crossmedia-sdk
|
|
2
|
+
|
|
3
|
+
TypeScript client for the Atzentis Crossmedia publishing API.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @atzentis/crossmedia-sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { CrossmediaClient } from "@atzentis/crossmedia-sdk";
|
|
15
|
+
|
|
16
|
+
const client = new CrossmediaClient({
|
|
17
|
+
baseUrl: "https://api.atzentis.ai",
|
|
18
|
+
apiKey: "atz_live_...",
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
See the [repository README](../../README.md) for development setup.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CrossmediaClient configuration.
|
|
5
|
+
* Customize the API base URL and behavior.
|
|
6
|
+
*/
|
|
7
|
+
type ClientConfig = {
|
|
8
|
+
/**
|
|
9
|
+
* API key for authenticated requests.
|
|
10
|
+
*/
|
|
11
|
+
readonly apiKey?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Tenant identifier (multi-tenant SDKs).
|
|
14
|
+
*/
|
|
15
|
+
readonly tenantId?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Base URL for API requests. Defaults to api.atzentis.ai.
|
|
18
|
+
*/
|
|
19
|
+
readonly baseUrl?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Request timeout in milliseconds. Defaults to 30000 (30 seconds).
|
|
22
|
+
*/
|
|
23
|
+
readonly timeoutMs?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Maximum number of automatic retries on network errors. Defaults to 3.
|
|
26
|
+
*/
|
|
27
|
+
readonly maxRetries?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Override the User-Agent string sent on every request. Defaults to
|
|
30
|
+
* `@atzentis/crossmedia/${VERSION}`.
|
|
31
|
+
*/
|
|
32
|
+
readonly userAgent?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Custom fetch implementation. Useful for testing (mock fetch) or
|
|
35
|
+
* Node.js environments before fetch became global.
|
|
36
|
+
*/
|
|
37
|
+
readonly fetch?: typeof fetch;
|
|
38
|
+
};
|
|
39
|
+
/** HTTP methods supported by HttpClient. */
|
|
40
|
+
type HttpMethod = "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
|
|
41
|
+
/** Per-request overrides passed to HttpClient methods. */
|
|
42
|
+
type RequestOptions = {
|
|
43
|
+
readonly headers?: Record<string, string>;
|
|
44
|
+
readonly query?: Record<string, unknown>;
|
|
45
|
+
readonly body?: unknown;
|
|
46
|
+
readonly signal?: AbortSignal;
|
|
47
|
+
readonly timeoutMs?: number;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* HTTP request context.
|
|
51
|
+
* Contains headers, method, and body for a request.
|
|
52
|
+
*/
|
|
53
|
+
type HttpRequest = {
|
|
54
|
+
readonly method: "GET" | "POST" | "PATCH" | "DELETE" | "PUT" | "HEAD" | "OPTIONS";
|
|
55
|
+
readonly url: string;
|
|
56
|
+
readonly headers: Record<string, string>;
|
|
57
|
+
readonly body?: unknown;
|
|
58
|
+
};
|
|
59
|
+
/**
|
|
60
|
+
* HTTP response context.
|
|
61
|
+
* Contains status, headers, and deserialized body.
|
|
62
|
+
*/
|
|
63
|
+
type HttpResponse<T = unknown> = {
|
|
64
|
+
readonly status: number;
|
|
65
|
+
readonly statusText: string;
|
|
66
|
+
readonly headers: Record<string, string>;
|
|
67
|
+
readonly data: T;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Request interceptor — transform or inspect outgoing requests.
|
|
72
|
+
*/
|
|
73
|
+
type RequestInterceptor = (init: RequestInit) => Promise<RequestInit | undefined> | RequestInit | undefined;
|
|
74
|
+
/**
|
|
75
|
+
* Response interceptor — inspect responses or handle errors.
|
|
76
|
+
*/
|
|
77
|
+
type ResponseInterceptor = (response: Response) => Promise<void> | void;
|
|
78
|
+
/**
|
|
79
|
+
* Interceptor manager — maintains a list of interceptors and provides removal.
|
|
80
|
+
*/
|
|
81
|
+
declare class InterceptorManager<T> {
|
|
82
|
+
readonly handlers: (T | null)[];
|
|
83
|
+
/**
|
|
84
|
+
* Add an interceptor and return a function to remove it.
|
|
85
|
+
*/
|
|
86
|
+
use(handler: T): () => void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface HttpClientConfig extends ClientConfig {
|
|
90
|
+
readonly userAgent: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* HttpClient — transport for @atzentis/crossmedia.
|
|
94
|
+
*
|
|
95
|
+
* Encapsulates fetch + retry + interceptors. Services depend on this class
|
|
96
|
+
* (not the public CrossmediaClient) so they remain testable in isolation.
|
|
97
|
+
* The top-level CrossmediaClient composes an HttpClient instance.
|
|
98
|
+
*/
|
|
99
|
+
declare class HttpClient {
|
|
100
|
+
readonly baseUrl: string;
|
|
101
|
+
private readonly timeoutMs;
|
|
102
|
+
private readonly maxRetries;
|
|
103
|
+
private readonly userAgent;
|
|
104
|
+
private readonly fetchImpl;
|
|
105
|
+
private readonly requestInterceptors;
|
|
106
|
+
private readonly responseInterceptors;
|
|
107
|
+
constructor(config: HttpClientConfig);
|
|
108
|
+
/** Add a request interceptor (e.g., for auth headers). */
|
|
109
|
+
interceptRequest(interceptor: RequestInterceptor): {
|
|
110
|
+
remove: () => void;
|
|
111
|
+
};
|
|
112
|
+
/** Add a response interceptor (e.g., for error mapping). */
|
|
113
|
+
interceptResponse(interceptor: ResponseInterceptor): {
|
|
114
|
+
remove: () => void;
|
|
115
|
+
};
|
|
116
|
+
get<T>(path: string, options?: RequestOptions): Promise<T>;
|
|
117
|
+
post<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T>;
|
|
118
|
+
patch<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T>;
|
|
119
|
+
put<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T>;
|
|
120
|
+
delete<T>(path: string, options?: RequestOptions): Promise<T>;
|
|
121
|
+
request<T>(method: HttpMethod, path: string, options?: RequestOptions): Promise<T>;
|
|
122
|
+
private withBody;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
declare const VERSION = "0.1.0";
|
|
126
|
+
/**
|
|
127
|
+
* Main entry point for @atzentis/crossmedia-sdk.
|
|
128
|
+
* Service properties are attached in later phases (P02–P05).
|
|
129
|
+
*/
|
|
130
|
+
declare class CrossmediaClient {
|
|
131
|
+
private readonly http;
|
|
132
|
+
constructor(config: ClientConfig);
|
|
133
|
+
/** Internal transport — used by service classes in P02+. */
|
|
134
|
+
getHttpClient(): HttpClient;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Retry configuration.
|
|
139
|
+
*/
|
|
140
|
+
interface RetryConfig {
|
|
141
|
+
readonly maxAttempts?: number;
|
|
142
|
+
readonly initialDelayMs?: number;
|
|
143
|
+
readonly maxDelayMs?: number;
|
|
144
|
+
readonly backoffMultiplier?: number;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Exponential backoff retry strategy.
|
|
148
|
+
* Retries on network errors or specific HTTP status codes (5xx, 429).
|
|
149
|
+
*/
|
|
150
|
+
declare function retryWithBackoff<T>(fn: () => Promise<T>, config?: RetryConfig): Promise<T>;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Base error class for all Crossmedia SDK errors.
|
|
154
|
+
* Extend this class to create domain-specific error types.
|
|
155
|
+
*/
|
|
156
|
+
declare class BaseError extends Error {
|
|
157
|
+
readonly name: string;
|
|
158
|
+
readonly code: string;
|
|
159
|
+
readonly statusCode?: number;
|
|
160
|
+
readonly details?: Record<string, unknown>;
|
|
161
|
+
constructor(message: string, code: string, statusCode?: number, details?: Record<string, unknown>);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Raised when authentication fails (invalid or missing credentials).
|
|
166
|
+
* Typically a 401 Unauthorized error.
|
|
167
|
+
*/
|
|
168
|
+
declare class AuthenticationError extends BaseError {
|
|
169
|
+
readonly name = "AuthenticationError";
|
|
170
|
+
constructor(message?: string);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Raised when a requested resource is not found.
|
|
175
|
+
* Typically a 404 Not Found error.
|
|
176
|
+
*/
|
|
177
|
+
declare class NotFoundError extends BaseError {
|
|
178
|
+
readonly name = "NotFoundError";
|
|
179
|
+
constructor(resourceType: string, resourceId: string);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Raised when user lacks permission for the requested operation.
|
|
184
|
+
* Typically a 403 Forbidden error.
|
|
185
|
+
*/
|
|
186
|
+
declare class PermissionError extends BaseError {
|
|
187
|
+
readonly name = "PermissionError";
|
|
188
|
+
constructor(message?: string);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Raised when rate limit is exceeded.
|
|
193
|
+
* Typically a 429 Too Many Requests error.
|
|
194
|
+
* Includes retry-after timing information.
|
|
195
|
+
*/
|
|
196
|
+
declare class RateLimitError extends BaseError {
|
|
197
|
+
readonly name = "RateLimitError";
|
|
198
|
+
readonly retryAfter?: number;
|
|
199
|
+
constructor(message?: string, retryAfter?: number);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Raised when request validation fails.
|
|
204
|
+
* Typically a 400 Bad Request error.
|
|
205
|
+
*/
|
|
206
|
+
declare class ValidationError extends BaseError {
|
|
207
|
+
readonly name = "ValidationError";
|
|
208
|
+
constructor(message?: string, details?: Record<string, unknown>);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/** Raised when a network connection cannot be established. */
|
|
212
|
+
declare class ConnectionError extends BaseError {
|
|
213
|
+
readonly name = "ConnectionError";
|
|
214
|
+
constructor(message?: string, details?: Record<string, unknown>);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Raised when a network request fails (connection timeout, DNS failure, etc.).
|
|
219
|
+
* Domain error — not mapped to an HTTP status code.
|
|
220
|
+
*/
|
|
221
|
+
declare class NetworkError extends BaseError {
|
|
222
|
+
readonly name = "NetworkError";
|
|
223
|
+
constructor(message?: string, details?: Record<string, unknown>);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Base class for Crossmedia SDK domain services.
|
|
228
|
+
* Each service receives HttpClient (not CrossmediaClient) for isolated testing.
|
|
229
|
+
*/
|
|
230
|
+
declare class BaseService {
|
|
231
|
+
protected readonly client: HttpClient;
|
|
232
|
+
protected readonly resource: string;
|
|
233
|
+
constructor(client: HttpClient, resource: string);
|
|
234
|
+
protected path(id?: string): string;
|
|
235
|
+
protected httpGet<T>(path: string): Promise<T>;
|
|
236
|
+
protected httpPost<T>(path: string, body?: unknown): Promise<T>;
|
|
237
|
+
protected httpDelete<T>(path: string): Promise<T>;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Paginated result wrapper.
|
|
242
|
+
* Indicates whether more results are available and provides next cursor.
|
|
243
|
+
*/
|
|
244
|
+
type PaginatedResult<T> = {
|
|
245
|
+
readonly items: T[];
|
|
246
|
+
readonly hasNextPage: boolean;
|
|
247
|
+
readonly nextCursor?: string;
|
|
248
|
+
};
|
|
249
|
+
/**
|
|
250
|
+
* Cursor-based pagination parameters.
|
|
251
|
+
* Use for requesting the next page of results.
|
|
252
|
+
*/
|
|
253
|
+
type CursorPageParams = {
|
|
254
|
+
readonly cursor?: string;
|
|
255
|
+
readonly limit?: number;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Pagination schema for list endpoints.
|
|
260
|
+
* Supports cursor-based pagination with configurable limit.
|
|
261
|
+
*/
|
|
262
|
+
declare const paginationSchema: z.ZodObject<{
|
|
263
|
+
cursor: z.ZodOptional<z.ZodEnum<["start", "middle", "end"]>>;
|
|
264
|
+
limit: z.ZodDefault<z.ZodNumber>;
|
|
265
|
+
}, "strip", z.ZodTypeAny, {
|
|
266
|
+
limit: number;
|
|
267
|
+
cursor?: "start" | "middle" | "end" | undefined;
|
|
268
|
+
}, {
|
|
269
|
+
cursor?: "start" | "middle" | "end" | undefined;
|
|
270
|
+
limit?: number | undefined;
|
|
271
|
+
}>;
|
|
272
|
+
type PaginationParams = z.infer<typeof paginationSchema>;
|
|
273
|
+
|
|
274
|
+
export { AuthenticationError, BaseError, BaseService, type ClientConfig, ConnectionError, CrossmediaClient, type CursorPageParams, HttpClient, type HttpClientConfig, type HttpMethod, type HttpRequest, type HttpResponse, InterceptorManager, NetworkError, NotFoundError, type PaginatedResult, type PaginationParams, PermissionError, RateLimitError, type RequestInterceptor, type RequestOptions, type ResponseInterceptor, type RetryConfig, VERSION, ValidationError, paginationSchema, retryWithBackoff };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// src/http/interceptors.ts
|
|
4
|
+
var InterceptorManager = class {
|
|
5
|
+
handlers = [];
|
|
6
|
+
/**
|
|
7
|
+
* Add an interceptor and return a function to remove it.
|
|
8
|
+
*/
|
|
9
|
+
use(handler) {
|
|
10
|
+
const id = this.handlers.length;
|
|
11
|
+
this.handlers.push(handler);
|
|
12
|
+
return () => {
|
|
13
|
+
this.handlers[id] = null;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// src/http/retry.ts
|
|
19
|
+
async function retryWithBackoff(fn, config = {}) {
|
|
20
|
+
const maxAttempts = config.maxAttempts ?? 3;
|
|
21
|
+
const initialDelayMs = config.initialDelayMs ?? 100;
|
|
22
|
+
const maxDelayMs = config.maxDelayMs ?? 5e3;
|
|
23
|
+
const backoffMultiplier = config.backoffMultiplier ?? 2;
|
|
24
|
+
let lastError;
|
|
25
|
+
let delayMs = initialDelayMs;
|
|
26
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
27
|
+
try {
|
|
28
|
+
return await fn();
|
|
29
|
+
} catch (error) {
|
|
30
|
+
lastError = error;
|
|
31
|
+
if (attempt === maxAttempts) {
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
await sleep(delayMs);
|
|
35
|
+
delayMs = Math.min(delayMs * backoffMultiplier, maxDelayMs);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
throw lastError;
|
|
39
|
+
}
|
|
40
|
+
function sleep(ms) {
|
|
41
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// src/http/client.ts
|
|
45
|
+
var DEFAULT_BASE_URL = "https://api.atzentis.ai";
|
|
46
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
47
|
+
var HttpClient = class {
|
|
48
|
+
baseUrl;
|
|
49
|
+
timeoutMs;
|
|
50
|
+
maxRetries;
|
|
51
|
+
userAgent;
|
|
52
|
+
fetchImpl;
|
|
53
|
+
requestInterceptors;
|
|
54
|
+
responseInterceptors;
|
|
55
|
+
constructor(config) {
|
|
56
|
+
this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
|
|
57
|
+
this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
58
|
+
this.maxRetries = config.maxRetries ?? 3;
|
|
59
|
+
this.userAgent = config.userAgent;
|
|
60
|
+
this.fetchImpl = config.fetch ?? globalThis.fetch?.bind(globalThis);
|
|
61
|
+
this.requestInterceptors = new InterceptorManager();
|
|
62
|
+
this.responseInterceptors = new InterceptorManager();
|
|
63
|
+
}
|
|
64
|
+
/** Add a request interceptor (e.g., for auth headers). */
|
|
65
|
+
interceptRequest(interceptor) {
|
|
66
|
+
const remove = this.requestInterceptors.use(interceptor);
|
|
67
|
+
return { remove };
|
|
68
|
+
}
|
|
69
|
+
/** Add a response interceptor (e.g., for error mapping). */
|
|
70
|
+
interceptResponse(interceptor) {
|
|
71
|
+
const remove = this.responseInterceptors.use(interceptor);
|
|
72
|
+
return { remove };
|
|
73
|
+
}
|
|
74
|
+
get(path, options) {
|
|
75
|
+
return this.request("GET", path, options);
|
|
76
|
+
}
|
|
77
|
+
post(path, body, options) {
|
|
78
|
+
return this.request("POST", path, this.withBody(body, options));
|
|
79
|
+
}
|
|
80
|
+
patch(path, body, options) {
|
|
81
|
+
return this.request("PATCH", path, this.withBody(body, options));
|
|
82
|
+
}
|
|
83
|
+
put(path, body, options) {
|
|
84
|
+
return this.request("PUT", path, this.withBody(body, options));
|
|
85
|
+
}
|
|
86
|
+
delete(path, options) {
|
|
87
|
+
return this.request("DELETE", path, options);
|
|
88
|
+
}
|
|
89
|
+
async request(method, path, options) {
|
|
90
|
+
return retryWithBackoff(
|
|
91
|
+
async () => {
|
|
92
|
+
const url = new URL(path, this.baseUrl).toString();
|
|
93
|
+
const headers = {
|
|
94
|
+
"content-type": "application/json",
|
|
95
|
+
accept: "application/json",
|
|
96
|
+
"user-agent": this.userAgent,
|
|
97
|
+
...options?.headers ?? {}
|
|
98
|
+
};
|
|
99
|
+
const controller = new AbortController();
|
|
100
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
101
|
+
const init = {
|
|
102
|
+
method,
|
|
103
|
+
headers,
|
|
104
|
+
...options?.body !== void 0 && {
|
|
105
|
+
body: typeof options.body === "string" ? options.body : JSON.stringify(options.body)
|
|
106
|
+
},
|
|
107
|
+
signal: options?.signal ?? controller.signal
|
|
108
|
+
};
|
|
109
|
+
let response;
|
|
110
|
+
try {
|
|
111
|
+
response = await this.fetchImpl(url, init);
|
|
112
|
+
} finally {
|
|
113
|
+
clearTimeout(timeoutId);
|
|
114
|
+
}
|
|
115
|
+
if (!response.ok) {
|
|
116
|
+
throw new Error(`HTTP ${response.status} ${response.statusText}`);
|
|
117
|
+
}
|
|
118
|
+
return await response.json();
|
|
119
|
+
},
|
|
120
|
+
{ maxAttempts: this.maxRetries }
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
withBody(body, options) {
|
|
124
|
+
if (body === void 0) return options ?? {};
|
|
125
|
+
return { ...options, body };
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
// src/client.ts
|
|
130
|
+
var VERSION = "0.1.0";
|
|
131
|
+
var CrossmediaClient = class {
|
|
132
|
+
http;
|
|
133
|
+
constructor(config) {
|
|
134
|
+
this.http = new HttpClient({
|
|
135
|
+
...config,
|
|
136
|
+
userAgent: `@atzentis/crossmedia-sdk/${VERSION}`
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/** Internal transport — used by service classes in P02+. */
|
|
140
|
+
getHttpClient() {
|
|
141
|
+
return this.http;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
// src/errors/base.ts
|
|
146
|
+
var BaseError = class extends Error {
|
|
147
|
+
name = "BaseError";
|
|
148
|
+
code;
|
|
149
|
+
statusCode;
|
|
150
|
+
details;
|
|
151
|
+
constructor(message, code, statusCode, details) {
|
|
152
|
+
super(message);
|
|
153
|
+
this.code = code;
|
|
154
|
+
if (statusCode !== void 0) {
|
|
155
|
+
this.statusCode = statusCode;
|
|
156
|
+
}
|
|
157
|
+
if (details !== void 0) {
|
|
158
|
+
this.details = details;
|
|
159
|
+
}
|
|
160
|
+
const errorWithCapture = Error;
|
|
161
|
+
errorWithCapture.captureStackTrace?.(this, this.constructor);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// src/errors/http/authentication.error.ts
|
|
166
|
+
var AuthenticationError = class extends BaseError {
|
|
167
|
+
name = "AuthenticationError";
|
|
168
|
+
constructor(message = "Authentication failed") {
|
|
169
|
+
super(message, "AUTHENTICATION_ERROR", 401);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
// src/errors/http/not-found.error.ts
|
|
174
|
+
var NotFoundError = class extends BaseError {
|
|
175
|
+
name = "NotFoundError";
|
|
176
|
+
constructor(resourceType, resourceId) {
|
|
177
|
+
const message = `${resourceType} with id "${resourceId}" not found`;
|
|
178
|
+
super(message, "NOT_FOUND_ERROR", 404);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// src/errors/http/permission.error.ts
|
|
183
|
+
var PermissionError = class extends BaseError {
|
|
184
|
+
name = "PermissionError";
|
|
185
|
+
constructor(message = "Permission denied") {
|
|
186
|
+
super(message, "PERMISSION_ERROR", 403);
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
// src/errors/http/rate-limit.error.ts
|
|
191
|
+
var RateLimitError = class extends BaseError {
|
|
192
|
+
name = "RateLimitError";
|
|
193
|
+
retryAfter;
|
|
194
|
+
// milliseconds
|
|
195
|
+
constructor(message = "Rate limit exceeded", retryAfter) {
|
|
196
|
+
super(message, "RATE_LIMIT_ERROR", 429);
|
|
197
|
+
if (retryAfter !== void 0) {
|
|
198
|
+
this.retryAfter = retryAfter;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
// src/errors/http/validation.error.ts
|
|
204
|
+
var ValidationError = class extends BaseError {
|
|
205
|
+
name = "ValidationError";
|
|
206
|
+
constructor(message = "Validation failed", details) {
|
|
207
|
+
super(message, "VALIDATION_ERROR", 400, details);
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// src/errors/domain/connection.error.ts
|
|
212
|
+
var ConnectionError = class extends BaseError {
|
|
213
|
+
name = "ConnectionError";
|
|
214
|
+
constructor(message = "Connection failed", details) {
|
|
215
|
+
super(message, "CONNECTION_ERROR", void 0, details);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
// src/errors/domain/network.error.ts
|
|
220
|
+
var NetworkError = class extends BaseError {
|
|
221
|
+
name = "NetworkError";
|
|
222
|
+
constructor(message = "Network request failed", details) {
|
|
223
|
+
super(message, "NETWORK_ERROR", void 0, details);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
// src/services/base.service.ts
|
|
228
|
+
var BaseService = class {
|
|
229
|
+
client;
|
|
230
|
+
resource;
|
|
231
|
+
constructor(client, resource) {
|
|
232
|
+
this.client = client;
|
|
233
|
+
this.resource = resource;
|
|
234
|
+
}
|
|
235
|
+
path(id) {
|
|
236
|
+
return id ? `/${this.resource}/${id}` : `/${this.resource}`;
|
|
237
|
+
}
|
|
238
|
+
httpGet(path) {
|
|
239
|
+
return this.client.get(path);
|
|
240
|
+
}
|
|
241
|
+
httpPost(path, body) {
|
|
242
|
+
return this.client.post(path, body);
|
|
243
|
+
}
|
|
244
|
+
httpDelete(path) {
|
|
245
|
+
return this.client.delete(path);
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
var PAGINATION_CURSORS = ["start", "middle", "end"];
|
|
249
|
+
var paginationSchema = z.object({
|
|
250
|
+
cursor: z.enum(PAGINATION_CURSORS).optional(),
|
|
251
|
+
limit: z.number().int().min(1).max(100).default(20)
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
export { AuthenticationError, BaseError, BaseService, ConnectionError, CrossmediaClient, HttpClient, InterceptorManager, NetworkError, NotFoundError, PermissionError, RateLimitError, VERSION, ValidationError, paginationSchema, retryWithBackoff };
|
|
255
|
+
//# sourceMappingURL=index.js.map
|
|
256
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/http/interceptors.ts","../src/http/retry.ts","../src/http/client.ts","../src/client.ts","../src/errors/base.ts","../src/errors/http/authentication.error.ts","../src/errors/http/not-found.error.ts","../src/errors/http/permission.error.ts","../src/errors/http/rate-limit.error.ts","../src/errors/http/validation.error.ts","../src/errors/domain/connection.error.ts","../src/errors/domain/network.error.ts","../src/services/base.service.ts","../src/schemas/shared.schema.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Request interceptor — transform or inspect outgoing requests.\n */\nexport type RequestInterceptor = (\n init: RequestInit,\n) => Promise<RequestInit | undefined> | RequestInit | undefined;\n\n/**\n * Response interceptor — inspect responses or handle errors.\n */\nexport type ResponseInterceptor = (response: Response) => Promise<void> | void;\n\n/**\n * Interceptor manager — maintains a list of interceptors and provides removal.\n */\nexport class InterceptorManager<T> {\n readonly handlers: (T | null)[] = [];\n\n /**\n * Add an interceptor and return a function to remove it.\n */\n use(handler: T): () => void {\n const id = this.handlers.length;\n this.handlers.push(handler);\n\n return () => {\n this.handlers[id] = null;\n };\n }\n}\n","/**\n * Retry configuration.\n */\nexport interface RetryConfig {\n readonly maxAttempts?: number;\n readonly initialDelayMs?: number;\n readonly maxDelayMs?: number;\n readonly backoffMultiplier?: number;\n}\n\n/**\n * Exponential backoff retry strategy.\n * Retries on network errors or specific HTTP status codes (5xx, 429).\n */\nexport async function retryWithBackoff<T>(\n fn: () => Promise<T>,\n config: RetryConfig = {},\n): Promise<T> {\n const maxAttempts = config.maxAttempts ?? 3;\n const initialDelayMs = config.initialDelayMs ?? 100;\n const maxDelayMs = config.maxDelayMs ?? 5000;\n const backoffMultiplier = config.backoffMultiplier ?? 2;\n\n let lastError: unknown;\n let delayMs = initialDelayMs;\n\n for (let attempt = 1; attempt <= maxAttempts; attempt++) {\n try {\n return await fn();\n } catch (error) {\n lastError = error;\n\n // Don't retry on the last attempt\n if (attempt === maxAttempts) {\n break;\n }\n\n // Wait before retrying\n await sleep(delayMs);\n delayMs = Math.min(delayMs * backoffMultiplier, maxDelayMs);\n }\n }\n\n throw lastError;\n}\n\n/**\n * Sleep for a given duration.\n */\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","import type { ClientConfig, HttpMethod, RequestOptions } from \"../types/http.types.js\";\nimport type { RequestInterceptor, ResponseInterceptor } from \"./interceptors.js\";\nimport { InterceptorManager } from \"./interceptors.js\";\nimport { retryWithBackoff } from \"./retry.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.atzentis.ai\";\nconst DEFAULT_TIMEOUT_MS = 30_000;\n\nexport interface HttpClientConfig extends ClientConfig {\n readonly userAgent: string;\n}\n\n/**\n * HttpClient — transport for @atzentis/crossmedia.\n *\n * Encapsulates fetch + retry + interceptors. Services depend on this class\n * (not the public CrossmediaClient) so they remain testable in isolation.\n * The top-level CrossmediaClient composes an HttpClient instance.\n */\nexport class HttpClient {\n readonly baseUrl: string;\n private readonly timeoutMs: number;\n private readonly maxRetries: number;\n private readonly userAgent: string;\n private readonly fetchImpl: typeof fetch;\n private readonly requestInterceptors: InterceptorManager<RequestInterceptor>;\n private readonly responseInterceptors: InterceptorManager<ResponseInterceptor>;\n\n constructor(config: HttpClientConfig) {\n this.baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;\n this.timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n this.maxRetries = config.maxRetries ?? 3;\n this.userAgent = config.userAgent;\n this.fetchImpl = config.fetch ?? globalThis.fetch?.bind(globalThis);\n this.requestInterceptors = new InterceptorManager();\n this.responseInterceptors = new InterceptorManager();\n }\n\n /** Add a request interceptor (e.g., for auth headers). */\n interceptRequest(interceptor: RequestInterceptor): { remove: () => void } {\n const remove = this.requestInterceptors.use(interceptor);\n return { remove };\n }\n\n /** Add a response interceptor (e.g., for error mapping). */\n interceptResponse(interceptor: ResponseInterceptor): { remove: () => void } {\n const remove = this.responseInterceptors.use(interceptor);\n return { remove };\n }\n\n get<T>(path: string, options?: RequestOptions): Promise<T> {\n return this.request<T>(\"GET\", path, options);\n }\n\n post<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T> {\n return this.request<T>(\"POST\", path, this.withBody(body, options));\n }\n\n patch<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T> {\n return this.request<T>(\"PATCH\", path, this.withBody(body, options));\n }\n\n put<T>(path: string, body?: unknown, options?: RequestOptions): Promise<T> {\n return this.request<T>(\"PUT\", path, this.withBody(body, options));\n }\n\n delete<T>(path: string, options?: RequestOptions): Promise<T> {\n return this.request<T>(\"DELETE\", path, options);\n }\n\n async request<T>(method: HttpMethod, path: string, options?: RequestOptions): Promise<T> {\n return retryWithBackoff(\n async () => {\n const url = new URL(path, this.baseUrl).toString();\n const headers: Record<string, string> = {\n \"content-type\": \"application/json\",\n accept: \"application/json\",\n \"user-agent\": this.userAgent,\n ...(options?.headers ?? {}),\n };\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeoutMs);\n const init: RequestInit = {\n method,\n headers,\n ...(options?.body !== undefined && {\n body: typeof options.body === \"string\" ? options.body : JSON.stringify(options.body),\n }),\n signal: options?.signal ?? controller.signal,\n };\n let response: Response;\n try {\n response = await this.fetchImpl(url, init);\n } finally {\n clearTimeout(timeoutId);\n }\n if (!response.ok) {\n throw new Error(`HTTP ${response.status} ${response.statusText}`);\n }\n return (await response.json()) as T;\n },\n { maxAttempts: this.maxRetries },\n );\n }\n\n private withBody(body: unknown, options: RequestOptions | undefined): RequestOptions {\n if (body === undefined) return options ?? {};\n return { ...options, body };\n }\n}\n","import { HttpClient } from \"./http/client.js\";\nimport type { ClientConfig } from \"./types/http.types.js\";\n\nexport const VERSION = \"0.1.0\";\n\n/**\n * Main entry point for @atzentis/crossmedia-sdk.\n * Service properties are attached in later phases (P02–P05).\n */\nexport class CrossmediaClient {\n private readonly http: HttpClient;\n\n constructor(config: ClientConfig) {\n this.http = new HttpClient({\n ...config,\n userAgent: `@atzentis/crossmedia-sdk/${VERSION}`,\n });\n }\n\n /** Internal transport — used by service classes in P02+. */\n getHttpClient(): HttpClient {\n return this.http;\n }\n}\n","/**\n * Base error class for all Crossmedia SDK errors.\n * Extend this class to create domain-specific error types.\n */\nexport class BaseError extends Error {\n readonly name: string = \"BaseError\";\n readonly code: string;\n readonly statusCode?: number;\n readonly details?: Record<string, unknown>;\n\n constructor(\n message: string,\n code: string,\n statusCode?: number,\n details?: Record<string, unknown>,\n ) {\n super(message);\n this.code = code;\n if (statusCode !== undefined) {\n this.statusCode = statusCode;\n }\n if (details !== undefined) {\n this.details = details;\n }\n\n const errorWithCapture = Error as typeof Error & {\n captureStackTrace?: (target: object, ctor: new (...args: never[]) => Error) => void;\n };\n errorWithCapture.captureStackTrace?.(this, this.constructor as new (...args: never[]) => Error);\n }\n}\n","import { BaseError } from \"../base.js\";\n\n/**\n * Raised when authentication fails (invalid or missing credentials).\n * Typically a 401 Unauthorized error.\n */\nexport class AuthenticationError extends BaseError {\n override readonly name = \"AuthenticationError\";\n\n constructor(message = \"Authentication failed\") {\n super(message, \"AUTHENTICATION_ERROR\", 401);\n }\n}\n","import { BaseError } from \"../base.js\";\n\n/**\n * Raised when a requested resource is not found.\n * Typically a 404 Not Found error.\n */\nexport class NotFoundError extends BaseError {\n override readonly name = \"NotFoundError\";\n\n constructor(resourceType: string, resourceId: string) {\n const message = `${resourceType} with id \"${resourceId}\" not found`;\n super(message, \"NOT_FOUND_ERROR\", 404);\n }\n}\n","import { BaseError } from \"../base.js\";\n\n/**\n * Raised when user lacks permission for the requested operation.\n * Typically a 403 Forbidden error.\n */\nexport class PermissionError extends BaseError {\n override readonly name = \"PermissionError\";\n\n constructor(message = \"Permission denied\") {\n super(message, \"PERMISSION_ERROR\", 403);\n }\n}\n","import { BaseError } from \"../base.js\";\n\n/**\n * Raised when rate limit is exceeded.\n * Typically a 429 Too Many Requests error.\n * Includes retry-after timing information.\n */\nexport class RateLimitError extends BaseError {\n override readonly name = \"RateLimitError\";\n readonly retryAfter?: number; // milliseconds\n\n constructor(message = \"Rate limit exceeded\", retryAfter?: number) {\n super(message, \"RATE_LIMIT_ERROR\", 429);\n if (retryAfter !== undefined) {\n this.retryAfter = retryAfter;\n }\n }\n}\n","import { BaseError } from \"../base.js\";\n\n/**\n * Raised when request validation fails.\n * Typically a 400 Bad Request error.\n */\nexport class ValidationError extends BaseError {\n override readonly name = \"ValidationError\";\n\n constructor(message = \"Validation failed\", details?: Record<string, unknown>) {\n super(message, \"VALIDATION_ERROR\", 400, details);\n }\n}\n","import { BaseError } from \"../base.js\";\n\n/** Raised when a network connection cannot be established. */\nexport class ConnectionError extends BaseError {\n override readonly name = \"ConnectionError\";\n\n constructor(message = \"Connection failed\", details?: Record<string, unknown>) {\n super(message, \"CONNECTION_ERROR\", undefined, details);\n }\n}\n","import { BaseError } from \"../base.js\";\n\n/**\n * Raised when a network request fails (connection timeout, DNS failure, etc.).\n * Domain error — not mapped to an HTTP status code.\n */\nexport class NetworkError extends BaseError {\n override readonly name = \"NetworkError\";\n\n constructor(message = \"Network request failed\", details?: Record<string, unknown>) {\n super(message, \"NETWORK_ERROR\", undefined, details);\n }\n}\n","import type { HttpClient } from \"../http/client.js\";\n\n/**\n * Base class for Crossmedia SDK domain services.\n * Each service receives HttpClient (not CrossmediaClient) for isolated testing.\n */\nexport class BaseService {\n protected readonly client: HttpClient;\n protected readonly resource: string;\n\n constructor(client: HttpClient, resource: string) {\n this.client = client;\n this.resource = resource;\n }\n\n protected path(id?: string): string {\n return id ? `/${this.resource}/${id}` : `/${this.resource}`;\n }\n\n protected httpGet<T>(path: string): Promise<T> {\n return this.client.get<T>(path);\n }\n\n protected httpPost<T>(path: string, body?: unknown): Promise<T> {\n return this.client.post<T>(path, body);\n }\n\n protected httpDelete<T>(path: string): Promise<T> {\n return this.client.delete<T>(path);\n }\n}\n","import { z } from \"zod\";\n\n/**\n * Cursor-based pagination constants.\n * Single source of truth for valid pagination cursor values.\n */\nexport const PAGINATION_CURSORS = [\"start\", \"middle\", \"end\"] as const;\nexport type PaginationCursor = (typeof PAGINATION_CURSORS)[number];\n\n/**\n * Pagination schema for list endpoints.\n * Supports cursor-based pagination with configurable limit.\n */\nexport const paginationSchema = z.object({\n cursor: z.enum(PAGINATION_CURSORS).optional(),\n limit: z.number().int().min(1).max(100).default(20),\n});\n\nexport type PaginationParams = z.infer<typeof paginationSchema>;\n\n/**\n * Standard sorting directions.\n */\nexport const SORT_DIRECTIONS = [\"asc\", \"desc\"] as const;\nexport type SortDirection = (typeof SORT_DIRECTIONS)[number];\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@atzentis/crossmedia-sdk",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Atzentis Crossmedia SDK — TypeScript client for api.atzentis.ai",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"atzentis",
|
|
7
|
+
"crossmedia",
|
|
8
|
+
"sdk",
|
|
9
|
+
"client",
|
|
10
|
+
"typescript"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"author": "Atzentis <dev@atzentis.com> (https://atzentis.com)",
|
|
14
|
+
"homepage": "https://github.com/atzentis/atzentis-crossmedia-sdk/tree/main/packages/core#readme",
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/atzentis/atzentis-crossmedia-sdk.git",
|
|
18
|
+
"directory": "packages/core"
|
|
19
|
+
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"import": "./dist/index.js",
|
|
26
|
+
"types": "./dist/index.d.ts"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"sideEffects": false,
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"README.md",
|
|
36
|
+
"LICENSE",
|
|
37
|
+
"CHANGELOG.md"
|
|
38
|
+
],
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"zod": "^3.23.0"
|
|
41
|
+
},
|
|
42
|
+
"peerDependenciesMeta": {
|
|
43
|
+
"zod": {
|
|
44
|
+
"optional": true
|
|
45
|
+
}
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@vitest/coverage-v8": "^2.1.0",
|
|
49
|
+
"tsup": "^8.3.0",
|
|
50
|
+
"typescript": "^5.7.0",
|
|
51
|
+
"vitest": "^2.1.0",
|
|
52
|
+
"zod": "^3.23.0"
|
|
53
|
+
},
|
|
54
|
+
"scripts": {
|
|
55
|
+
"build": "tsup",
|
|
56
|
+
"dev": "tsup --watch",
|
|
57
|
+
"test": "vitest run",
|
|
58
|
+
"test:coverage": "vitest run --coverage",
|
|
59
|
+
"test:watch": "vitest",
|
|
60
|
+
"type-check": "tsc --noEmit"
|
|
61
|
+
}
|
|
62
|
+
}
|