@jaaq/jaaq-sdk-js 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,210 @@
1
+ # JAAQ TypeScript SDK (`jaaq-sdk-js`)
2
+
3
+ A lightweight, type-safe SDK for interacting with the **JAAQ API (v1)**.
4
+ It provides a simple client with typed resources generated from the backend OpenAPI specification.
5
+
6
+ ---
7
+
8
+ ## Project Structure
9
+
10
+ - src/
11
+ - core/ # HTTP client
12
+ - gen/ # Generated OpenAPI types
13
+ - resources/ # API resources (e.g., videos)
14
+ - index.ts
15
+ - test/
16
+ - handlers/
17
+ - mocks/
18
+ - setup.ts
19
+
20
+ ## Relative paths:
21
+
22
+ ```bash
23
+ "@src/*": ["src/*"],
24
+ "@core/*": ["src/core/*"],
25
+ "@resources/*": ["src/resources/*"],
26
+ "@gen/*": ["src/gen/*"],
27
+ "@tests/*": ["tests/*"]
28
+ ```
29
+
30
+ ## Installation
31
+
32
+ ```bash
33
+ pnpm install
34
+ ```
35
+
36
+ ## Build
37
+
38
+ ```bash
39
+ pnpm build
40
+ ```
41
+
42
+ ## Run tests
43
+
44
+ ```bash
45
+ pnpm test
46
+ ```
47
+
48
+ or
49
+
50
+ ```bash
51
+ pnpm test:watch
52
+ ```
53
+
54
+ ## Create types
55
+
56
+ Once we have the last openApi.json file from the Back-end
57
+
58
+ ```bash
59
+ pnpm openapi:gen
60
+ ```
61
+
62
+ And then:
63
+
64
+ ```bash
65
+ import type { paths, components } from '@gen/openapi';
66
+
67
+ type GetVideosResponse = components['schemas']['GetVideosResponse'];
68
+ type GetVideosParams = paths['/videos']['get']['parameters']['query'];
69
+
70
+ ```
71
+
72
+ ## Configuration options:
73
+
74
+ - apiKey (required) – Provided by backend.
75
+ - clientId (required) – Provided by consuming company.
76
+ - baseUrl (optional) – Defaults to https://api.jaaq.app/v1.
77
+ - fetch (optional) – Custom fetch implementation (for Node < 18).
78
+ - timeoutMs (optional) – Request timeout in ms.
79
+ - headers (optional) – Extra headers for all requests.
80
+ - apiKeyHeaderName (optional) – Defaults to x-api-key.
81
+
82
+ ## Linting
83
+
84
+ - ESLint (code rules) and Prettier (formatting).
85
+ - On save: use the Prettier editor extension (Format on Save).
86
+ - Pre-commit: lint-staged runs ESLint --fix on TS/JS and Prettier on staged files.
87
+ - Manual:
88
+ - `pnpm lint` / `pnpm lint:fix`
89
+ - `pnpm format` / `pnpm format:check`
90
+ - Commit messages: validated by commitlint (Husky commit-msg hook).
91
+ - PRs: CI runs `pnpm lint` and `pnpm format:check` on pull_request.
92
+
93
+ ## Usage:
94
+
95
+ #### install module:
96
+
97
+ ![Install module screenshot](./docs/install-sdk.png)
98
+
99
+ #### classic mode:
100
+
101
+ ```bash
102
+ // Import module
103
+ import { JaaqClient } from "jaaq-sdk-js";
104
+
105
+ const client = JaaqClient.init({
106
+ apiKey: '<YOUR_API_KEY>',
107
+ clientId: '<YOUR_CLIENT_ID>',
108
+ });
109
+ ```
110
+
111
+ #### JS mode:
112
+
113
+ ```bash
114
+ // Import module
115
+ import { createJaaqClient } from "jaaq-sdk-js";
116
+
117
+ const client = createJaaqClient({
118
+ apiKey: '<YOUR_API_KEY>',
119
+ clientId: '<YOUR_CLIENT_ID>',
120
+ });
121
+ ```
122
+
123
+ #### Access to the different resources:
124
+
125
+ ```bash
126
+ client.videos.getById('xxxxxx')
127
+ ```
128
+
129
+ or
130
+
131
+ ```bash
132
+ client.collections.list()
133
+ ```
134
+
135
+ #### Ex: Getting and displaying a video
136
+
137
+ ```bash
138
+ import { useEffect, useRef } from "react";
139
+ import { createJaaqClient } from "jaaq-sdk-js";
140
+
141
+ const API_KEY = import.meta.env.VITE_JAAQ_API_KEY;
142
+ const CLIENT_ID = import.meta.env.VITE_JAAQ_CLIENT_ID;
143
+
144
+ export default function VideoExample({ id }: { id: string }) {
145
+ const videoRef = useRef<HTMLVideoElement | null>(null);
146
+
147
+ useEffect(() => {
148
+ (async () => {
149
+ const client = createJaaqClient({
150
+ apiKey: API_KEY,
151
+ clientId: CLIENT_ID
152
+ });
153
+ const resp = await client.videos.getById(id);
154
+ const url = resp.video.videoUrl;
155
+ videoRef.current.src = url;
156
+ })();
157
+ }, [id]);
158
+
159
+ return (
160
+ <video
161
+ ref={videoRef}
162
+ autoPlay
163
+ controls
164
+ />;
165
+ )
166
+ }
167
+ ```
168
+
169
+ #### If extension is .m3u8
170
+
171
+ ```bash
172
+ import { useEffect, useRef } from "react";
173
+ import { createJaaqClient } from "jaaq-sdk-js";
174
+ import Hls from "hls.js";
175
+
176
+ const API_KEY = import.meta.env.VITE_JAAQ_API_KEY!;
177
+ const CLIENT_ID = import.meta.env.VITE_JAAQ_CLIENT_ID!;
178
+
179
+ export default function VideoExample({ id }: { id: string }) {
180
+ const videoRef = useRef<HTMLVideoElement | null>(null);
181
+
182
+ useEffect(() => {
183
+ let hls: Hls | undefined;
184
+
185
+ (async () => {
186
+ const client = createJaaqClient({ apiKey: API_KEY, clientId: CLIENT_ID });
187
+ const resp = await client.videos.getById(id);
188
+ const url = resp.video.videoUrl; // e.g. https://.../video.m3u8
189
+ const video = videoRef.current;
190
+ if (!video || !url) return;
191
+
192
+ if (Hls.isSupported()) {
193
+ hls = new Hls();
194
+ hls.loadSource(url);
195
+ hls.attachMedia(video);
196
+ }
197
+ })();
198
+
199
+ return () => hls?.destroy();
200
+ }, [id]);
201
+
202
+ return (
203
+ <video
204
+ ref={videoRef}
205
+ autoPlay
206
+ controls
207
+ />;
208
+ )
209
+ }
210
+ ```
package/dist/index.cjs ADDED
@@ -0,0 +1,134 @@
1
+ 'use strict';
2
+
3
+ async function parseJson(res) {
4
+ if (!res.ok) {
5
+ const text = await res.text().catch(() => '');
6
+ throw new Error(`HTTP ${res.status}: ${text || res.statusText}`);
7
+ }
8
+ if (res.status === 204)
9
+ return undefined;
10
+ return res.json();
11
+ }
12
+ /**
13
+ * Join base URL and path avoiding duplicate slashes.
14
+ * - Preserves absolute paths (http/https) as-is.
15
+ * - Trims trailing slashes from base and leading slashes from path.
16
+ */
17
+ function joinUrl(baseUrl, path) {
18
+ // return path if it is an absolute URL
19
+ if (/^https?:\/\//i.test(path))
20
+ return path;
21
+ const base = baseUrl.replace(/\/+$/, '');
22
+ const relative = path.replace(/^\/+/, '');
23
+ return `${base}/${relative}`;
24
+ }
25
+ function createHttpClient(config) {
26
+ const { baseUrl, apiKey, clientId, fetch: fetchImpl, headers, apiKeyHeaderName = 'x-api-key' } = config;
27
+ const f = fetchImpl ?? globalThis.fetch;
28
+ if (!f) {
29
+ throw new Error('No fetch implementation found. Provide one in SDKConfig.fetch for Node or custom environments.');
30
+ }
31
+ const baseHeaders = {
32
+ 'Content-Type': 'application/json',
33
+ [apiKeyHeaderName]: apiKey,
34
+ 'x-client-id': clientId,
35
+ ...headers,
36
+ };
37
+ const fullUrl = (path) => joinUrl(baseUrl, path);
38
+ const request = async (method, path, init, body) => {
39
+ const reqInit = {
40
+ method,
41
+ headers: {
42
+ ...baseHeaders,
43
+ ...init?.headers,
44
+ },
45
+ ...init,
46
+ };
47
+ if (body !== undefined)
48
+ reqInit.body = JSON.stringify(body);
49
+ const res = await f(fullUrl(path), reqInit);
50
+ return parseJson(res);
51
+ };
52
+ return {
53
+ get: (path, init) => request('GET', path, init),
54
+ post: (path, body, init) => request('POST', path, init, body),
55
+ put: (path, body, init) => request('PUT', path, init, body),
56
+ patch: (path, body, init) => request('PATCH', path, init, body),
57
+ delete: (path, init) => request('DELETE', path, init),
58
+ };
59
+ }
60
+
61
+ function createVideosResource(http) {
62
+ return {
63
+ async getById(id) {
64
+ const path = `b2b/videos/${encodeURIComponent(id)}`;
65
+ return http.get(path);
66
+ },
67
+ };
68
+ }
69
+
70
+ function createCollectionsResource(http) {
71
+ return {
72
+ async list() {
73
+ const path = 'b2b/collections';
74
+ return http.get(path);
75
+ },
76
+ async getById(id) {
77
+ const path = `b2b/collections/${encodeURIComponent(id)}`;
78
+ return http.get(path);
79
+ },
80
+ };
81
+ }
82
+
83
+ const BASE_URL = undefined;
84
+ class JaaqClient {
85
+ // Keep constructor private to enforce controlled instantiation.
86
+ constructor(http) {
87
+ this.videos = createVideosResource(http);
88
+ this.collections = createCollectionsResource(http);
89
+ }
90
+ /**
91
+ * Public "friend" constructor that remains inside the class boundary.
92
+ * This method is callable from the module-level build function
93
+ * without exposing `new JaaqClient(...)` to consumers.
94
+ */
95
+ static fromHttp(http) {
96
+ return new JaaqClient(http);
97
+ }
98
+ /**
99
+ * OO-friendly static initializer.
100
+ * Delegates to the single source of truth: `buildClient`.
101
+ */
102
+ static init(config) {
103
+ return buildClient(config);
104
+ }
105
+ }
106
+ /**
107
+ * Canonical factory (idiomatic for JS/TS).
108
+ * Delegates to the single source of truth: `buildClient`.
109
+ */
110
+ function createJaaqClient(config) {
111
+ return buildClient(config);
112
+ }
113
+ /**
114
+ * Single source of truth for constructing the client.
115
+ * Both `createJaaqClient` and `JaaqClient.init` call this function.
116
+ * The constructor stays private; instantiation is funneled via `JaaqClient.fromHttp`.
117
+ */
118
+ function buildClient(config) {
119
+ const { baseUrl = BASE_URL, apiKey, clientId, fetch: fetchImpl, headers, apiKeyHeaderName = 'x-api-key' } = config;
120
+ const http = createHttpClient({
121
+ baseUrl,
122
+ apiKey,
123
+ clientId,
124
+ apiKeyHeaderName,
125
+ fetch: fetchImpl ?? globalThis.fetch,
126
+ headers,
127
+ });
128
+ return JaaqClient.fromHttp(http);
129
+ }
130
+
131
+ exports.BASE_URL = BASE_URL;
132
+ exports.JaaqClient = JaaqClient;
133
+ exports.createJaaqClient = createJaaqClient;
134
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../src/core/httpClient.ts","../src/resources/videos.ts","../src/resources/collections.ts","../src/index.ts"],"sourcesContent":["export type FetchLike = typeof globalThis.fetch;\ntype RequestOptions = Parameters<typeof globalThis.fetch>[1];\ntype FetchResponse = Awaited<ReturnType<FetchLike>>;\n\nexport interface HttpClientConfig {\n baseUrl: string;\n apiKey: string;\n clientId: string;\n apiKeyHeaderName?: string;\n fetch?: FetchLike;\n timeoutMs?: number;\n headers?: Record<string, string>;\n}\n\nexport interface HttpClient {\n get<T>(_path: string, _init?: RequestOptions): Promise<T>;\n post<T>(_path: string, _body?: unknown, _init?: RequestOptions): Promise<T>;\n put<T>(_path: string, _body?: unknown, _init?: RequestOptions): Promise<T>;\n patch<T>(_path: string, _body?: unknown, _init?: RequestOptions): Promise<T>;\n delete<T>(_path: string, _init?: RequestOptions): Promise<T>;\n}\n\nasync function parseJson<T>(res: FetchResponse): Promise<T> {\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`HTTP ${res.status}: ${text || res.statusText}`);\n }\n if (res.status === 204) return undefined as unknown as T;\n return res.json() as Promise<T>;\n}\n\n/**\n * Join base URL and path avoiding duplicate slashes.\n * - Preserves absolute paths (http/https) as-is.\n * - Trims trailing slashes from base and leading slashes from path.\n */\nfunction joinUrl(baseUrl: string, path: string): string {\n // return path if it is an absolute URL\n if (/^https?:\\/\\//i.test(path)) return path;\n const base = baseUrl.replace(/\\/+$/, '');\n const relative = path.replace(/^\\/+/, '');\n return `${base}/${relative}`;\n}\n\nexport function createHttpClient(config: HttpClientConfig): HttpClient {\n const { baseUrl, apiKey, clientId, fetch: fetchImpl, headers, apiKeyHeaderName = 'x-api-key' } = config;\n\n const f: FetchLike | undefined = fetchImpl ?? (globalThis.fetch as FetchLike | undefined);\n if (!f) {\n throw new Error('No fetch implementation found. Provide one in SDKConfig.fetch for Node or custom environments.');\n }\n\n const baseHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n [apiKeyHeaderName]: apiKey,\n 'x-client-id': clientId,\n ...headers,\n };\n\n const fullUrl = (path: string) => joinUrl(baseUrl, path);\n\n const request = async <T>(method: string, path: string, init?: RequestOptions, body?: unknown) => {\n const reqInit: RequestOptions = {\n method,\n headers: {\n ...baseHeaders,\n ...(init?.headers as Record<string, string> | undefined),\n },\n ...init,\n };\n if (body !== undefined) reqInit.body = JSON.stringify(body);\n const res = await f(fullUrl(path), reqInit);\n return parseJson<T>(res);\n };\n\n return {\n get: <T>(path: string, init?: RequestOptions) => request<T>('GET', path, init),\n post: <T>(path: string, body?: unknown, init?: RequestOptions) => request<T>('POST', path, init, body),\n put: <T>(path: string, body?: unknown, init?: RequestOptions) => request<T>('PUT', path, init, body),\n patch: <T>(path: string, body?: unknown, init?: RequestOptions) => request<T>('PATCH', path, init, body),\n delete: <T>(path: string, init?: RequestOptions) => request<T>('DELETE', path, init),\n };\n}\n","import type { HttpClient } from '@core/httpClient';\nimport type { Video } from '@src/types/videos';\n\nexport interface VideosResource {\n getById(_id: string): Promise<Video>;\n}\n\nexport function createVideosResource(http: HttpClient): VideosResource {\n return {\n async getById(id: string) {\n const path = `b2b/videos/${encodeURIComponent(id)}`;\n return http.get<Video>(path);\n },\n };\n}\n","import type { HttpClient } from '@core/httpClient';\nimport type { Collection } from '@src/types/collection';\n\nexport interface CollectionsResource {\n list(): Promise<Collection[]>;\n getById(_id: string): Promise<Collection>;\n}\n\nexport function createCollectionsResource(http: HttpClient): CollectionsResource {\n return {\n async list() {\n const path = 'b2b/collections';\n return http.get<Collection[]>(path);\n },\n async getById(id: string) {\n const path = `b2b/collections/${encodeURIComponent(id)}`;\n return http.get<Collection>(path);\n },\n };\n}\n","import { createHttpClient, type HttpClient, type FetchLike } from '@core/httpClient';\nimport { createVideosResource, type VideosResource } from '@resources/videos';\nimport { createCollectionsResource, type CollectionsResource } from '@resources/collections';\n\n// Public type exports for consumers (barrel)\nexport * from '@src/types';\n\nexport const BASE_URL = process.env.JAAQ_API_URL as string;\n\nexport interface SDKConfig {\n baseUrl?: string; // Pending to approve\n apiKey: string; // Provided by backend per client\n clientId: string; // Provided by each consuming company at init\n fetch?: FetchLike; // Optional fetch injection (Node/custom envs) // Pending to approve\n timeoutMs?: number;\n headers?: Record<string, string>; // Pending to approve\n apiKeyHeaderName?: string;\n}\n\nexport class JaaqClient {\n public readonly videos: VideosResource;\n public readonly collections: CollectionsResource;\n\n // Keep constructor private to enforce controlled instantiation.\n private constructor(http: HttpClient) {\n this.videos = createVideosResource(http);\n this.collections = createCollectionsResource(http);\n }\n\n /**\n * Public \"friend\" constructor that remains inside the class boundary.\n * This method is callable from the module-level build function\n * without exposing `new JaaqClient(...)` to consumers.\n */\n static fromHttp(http: HttpClient): JaaqClient {\n return new JaaqClient(http);\n }\n\n /**\n * OO-friendly static initializer.\n * Delegates to the single source of truth: `buildClient`.\n */\n static init(config: SDKConfig): JaaqClient {\n return buildClient(config);\n }\n}\n\n/**\n * Canonical factory (idiomatic for JS/TS).\n * Delegates to the single source of truth: `buildClient`.\n */\nexport function createJaaqClient(config: SDKConfig): JaaqClient {\n return buildClient(config);\n}\n\n/**\n * Single source of truth for constructing the client.\n * Both `createJaaqClient` and `JaaqClient.init` call this function.\n * The constructor stays private; instantiation is funneled via `JaaqClient.fromHttp`.\n */\nfunction buildClient(config: SDKConfig): JaaqClient {\n const { baseUrl = BASE_URL, apiKey, clientId, fetch: fetchImpl, headers, apiKeyHeaderName = 'x-api-key' } = config;\n\n const http = createHttpClient({\n baseUrl,\n apiKey,\n clientId,\n apiKeyHeaderName,\n fetch: fetchImpl ?? (globalThis.fetch as FetchLike | undefined),\n headers,\n });\n\n return JaaqClient.fromHttp(http);\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ type FetchLike = typeof globalThis.fetch;
2
+ type RequestOptions = Parameters<typeof globalThis.fetch>[1];
3
+ interface HttpClient {
4
+ get<T>(_path: string, _init?: RequestOptions): Promise<T>;
5
+ post<T>(_path: string, _body?: unknown, _init?: RequestOptions): Promise<T>;
6
+ put<T>(_path: string, _body?: unknown, _init?: RequestOptions): Promise<T>;
7
+ patch<T>(_path: string, _body?: unknown, _init?: RequestOptions): Promise<T>;
8
+ delete<T>(_path: string, _init?: RequestOptions): Promise<T>;
9
+ }
10
+
11
+ interface Video {
12
+ id: string;
13
+ videoId: string;
14
+ questionId: string;
15
+ question: string;
16
+ status: number | null;
17
+ transcription: string | null;
18
+ createdAt: string;
19
+ modifiedAt: string;
20
+ videoUrl: string;
21
+ published: boolean;
22
+ tags: string[];
23
+ severity: number | null;
24
+ duration: number;
25
+ subTopics: string[];
26
+ description: string;
27
+ }
28
+
29
+ interface VideosResource {
30
+ getById(_id: string): Promise<Video>;
31
+ }
32
+
33
+ interface Collection {
34
+ id: string;
35
+ name: string;
36
+ description: string;
37
+ imageUrl: string;
38
+ videos: Video[];
39
+ }
40
+
41
+ interface CollectionsResource {
42
+ list(): Promise<Collection[]>;
43
+ getById(_id: string): Promise<Collection>;
44
+ }
45
+
46
+ type ISODateTimeString = string;
47
+ interface ApiError {
48
+ description: string;
49
+ error: string;
50
+ }
51
+
52
+ interface Subtopic {
53
+ id: string;
54
+ name: string;
55
+ createdAt: ISODateTimeString;
56
+ modifiedAt: ISODateTimeString;
57
+ modifiedBy: ISODateTimeString;
58
+ }
59
+
60
+ declare const BASE_URL: string;
61
+ interface SDKConfig {
62
+ baseUrl?: string;
63
+ apiKey: string;
64
+ clientId: string;
65
+ fetch?: FetchLike;
66
+ timeoutMs?: number;
67
+ headers?: Record<string, string>;
68
+ apiKeyHeaderName?: string;
69
+ }
70
+ declare class JaaqClient {
71
+ readonly videos: VideosResource;
72
+ readonly collections: CollectionsResource;
73
+ private constructor();
74
+ /**
75
+ * Public "friend" constructor that remains inside the class boundary.
76
+ * This method is callable from the module-level build function
77
+ * without exposing `new JaaqClient(...)` to consumers.
78
+ */
79
+ static fromHttp(http: HttpClient): JaaqClient;
80
+ /**
81
+ * OO-friendly static initializer.
82
+ * Delegates to the single source of truth: `buildClient`.
83
+ */
84
+ static init(config: SDKConfig): JaaqClient;
85
+ }
86
+ /**
87
+ * Canonical factory (idiomatic for JS/TS).
88
+ * Delegates to the single source of truth: `buildClient`.
89
+ */
90
+ declare function createJaaqClient(config: SDKConfig): JaaqClient;
91
+
92
+ export { BASE_URL, JaaqClient, createJaaqClient };
93
+ export type { ApiError, Collection, ISODateTimeString, SDKConfig, Subtopic, Video };
package/dist/index.mjs ADDED
@@ -0,0 +1,130 @@
1
+ async function parseJson(res) {
2
+ if (!res.ok) {
3
+ const text = await res.text().catch(() => '');
4
+ throw new Error(`HTTP ${res.status}: ${text || res.statusText}`);
5
+ }
6
+ if (res.status === 204)
7
+ return undefined;
8
+ return res.json();
9
+ }
10
+ /**
11
+ * Join base URL and path avoiding duplicate slashes.
12
+ * - Preserves absolute paths (http/https) as-is.
13
+ * - Trims trailing slashes from base and leading slashes from path.
14
+ */
15
+ function joinUrl(baseUrl, path) {
16
+ // return path if it is an absolute URL
17
+ if (/^https?:\/\//i.test(path))
18
+ return path;
19
+ const base = baseUrl.replace(/\/+$/, '');
20
+ const relative = path.replace(/^\/+/, '');
21
+ return `${base}/${relative}`;
22
+ }
23
+ function createHttpClient(config) {
24
+ const { baseUrl, apiKey, clientId, fetch: fetchImpl, headers, apiKeyHeaderName = 'x-api-key' } = config;
25
+ const f = fetchImpl ?? globalThis.fetch;
26
+ if (!f) {
27
+ throw new Error('No fetch implementation found. Provide one in SDKConfig.fetch for Node or custom environments.');
28
+ }
29
+ const baseHeaders = {
30
+ 'Content-Type': 'application/json',
31
+ [apiKeyHeaderName]: apiKey,
32
+ 'x-client-id': clientId,
33
+ ...headers,
34
+ };
35
+ const fullUrl = (path) => joinUrl(baseUrl, path);
36
+ const request = async (method, path, init, body) => {
37
+ const reqInit = {
38
+ method,
39
+ headers: {
40
+ ...baseHeaders,
41
+ ...init?.headers,
42
+ },
43
+ ...init,
44
+ };
45
+ if (body !== undefined)
46
+ reqInit.body = JSON.stringify(body);
47
+ const res = await f(fullUrl(path), reqInit);
48
+ return parseJson(res);
49
+ };
50
+ return {
51
+ get: (path, init) => request('GET', path, init),
52
+ post: (path, body, init) => request('POST', path, init, body),
53
+ put: (path, body, init) => request('PUT', path, init, body),
54
+ patch: (path, body, init) => request('PATCH', path, init, body),
55
+ delete: (path, init) => request('DELETE', path, init),
56
+ };
57
+ }
58
+
59
+ function createVideosResource(http) {
60
+ return {
61
+ async getById(id) {
62
+ const path = `b2b/videos/${encodeURIComponent(id)}`;
63
+ return http.get(path);
64
+ },
65
+ };
66
+ }
67
+
68
+ function createCollectionsResource(http) {
69
+ return {
70
+ async list() {
71
+ const path = 'b2b/collections';
72
+ return http.get(path);
73
+ },
74
+ async getById(id) {
75
+ const path = `b2b/collections/${encodeURIComponent(id)}`;
76
+ return http.get(path);
77
+ },
78
+ };
79
+ }
80
+
81
+ const BASE_URL = undefined;
82
+ class JaaqClient {
83
+ // Keep constructor private to enforce controlled instantiation.
84
+ constructor(http) {
85
+ this.videos = createVideosResource(http);
86
+ this.collections = createCollectionsResource(http);
87
+ }
88
+ /**
89
+ * Public "friend" constructor that remains inside the class boundary.
90
+ * This method is callable from the module-level build function
91
+ * without exposing `new JaaqClient(...)` to consumers.
92
+ */
93
+ static fromHttp(http) {
94
+ return new JaaqClient(http);
95
+ }
96
+ /**
97
+ * OO-friendly static initializer.
98
+ * Delegates to the single source of truth: `buildClient`.
99
+ */
100
+ static init(config) {
101
+ return buildClient(config);
102
+ }
103
+ }
104
+ /**
105
+ * Canonical factory (idiomatic for JS/TS).
106
+ * Delegates to the single source of truth: `buildClient`.
107
+ */
108
+ function createJaaqClient(config) {
109
+ return buildClient(config);
110
+ }
111
+ /**
112
+ * Single source of truth for constructing the client.
113
+ * Both `createJaaqClient` and `JaaqClient.init` call this function.
114
+ * The constructor stays private; instantiation is funneled via `JaaqClient.fromHttp`.
115
+ */
116
+ function buildClient(config) {
117
+ const { baseUrl = BASE_URL, apiKey, clientId, fetch: fetchImpl, headers, apiKeyHeaderName = 'x-api-key' } = config;
118
+ const http = createHttpClient({
119
+ baseUrl,
120
+ apiKey,
121
+ clientId,
122
+ apiKeyHeaderName,
123
+ fetch: fetchImpl ?? globalThis.fetch,
124
+ headers,
125
+ });
126
+ return JaaqClient.fromHttp(http);
127
+ }
128
+
129
+ export { BASE_URL, JaaqClient, createJaaqClient };
130
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","sources":["../src/core/httpClient.ts","../src/resources/videos.ts","../src/resources/collections.ts","../src/index.ts"],"sourcesContent":["export type FetchLike = typeof globalThis.fetch;\ntype RequestOptions = Parameters<typeof globalThis.fetch>[1];\ntype FetchResponse = Awaited<ReturnType<FetchLike>>;\n\nexport interface HttpClientConfig {\n baseUrl: string;\n apiKey: string;\n clientId: string;\n apiKeyHeaderName?: string;\n fetch?: FetchLike;\n timeoutMs?: number;\n headers?: Record<string, string>;\n}\n\nexport interface HttpClient {\n get<T>(_path: string, _init?: RequestOptions): Promise<T>;\n post<T>(_path: string, _body?: unknown, _init?: RequestOptions): Promise<T>;\n put<T>(_path: string, _body?: unknown, _init?: RequestOptions): Promise<T>;\n patch<T>(_path: string, _body?: unknown, _init?: RequestOptions): Promise<T>;\n delete<T>(_path: string, _init?: RequestOptions): Promise<T>;\n}\n\nasync function parseJson<T>(res: FetchResponse): Promise<T> {\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`HTTP ${res.status}: ${text || res.statusText}`);\n }\n if (res.status === 204) return undefined as unknown as T;\n return res.json() as Promise<T>;\n}\n\n/**\n * Join base URL and path avoiding duplicate slashes.\n * - Preserves absolute paths (http/https) as-is.\n * - Trims trailing slashes from base and leading slashes from path.\n */\nfunction joinUrl(baseUrl: string, path: string): string {\n // return path if it is an absolute URL\n if (/^https?:\\/\\//i.test(path)) return path;\n const base = baseUrl.replace(/\\/+$/, '');\n const relative = path.replace(/^\\/+/, '');\n return `${base}/${relative}`;\n}\n\nexport function createHttpClient(config: HttpClientConfig): HttpClient {\n const { baseUrl, apiKey, clientId, fetch: fetchImpl, headers, apiKeyHeaderName = 'x-api-key' } = config;\n\n const f: FetchLike | undefined = fetchImpl ?? (globalThis.fetch as FetchLike | undefined);\n if (!f) {\n throw new Error('No fetch implementation found. Provide one in SDKConfig.fetch for Node or custom environments.');\n }\n\n const baseHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n [apiKeyHeaderName]: apiKey,\n 'x-client-id': clientId,\n ...headers,\n };\n\n const fullUrl = (path: string) => joinUrl(baseUrl, path);\n\n const request = async <T>(method: string, path: string, init?: RequestOptions, body?: unknown) => {\n const reqInit: RequestOptions = {\n method,\n headers: {\n ...baseHeaders,\n ...(init?.headers as Record<string, string> | undefined),\n },\n ...init,\n };\n if (body !== undefined) reqInit.body = JSON.stringify(body);\n const res = await f(fullUrl(path), reqInit);\n return parseJson<T>(res);\n };\n\n return {\n get: <T>(path: string, init?: RequestOptions) => request<T>('GET', path, init),\n post: <T>(path: string, body?: unknown, init?: RequestOptions) => request<T>('POST', path, init, body),\n put: <T>(path: string, body?: unknown, init?: RequestOptions) => request<T>('PUT', path, init, body),\n patch: <T>(path: string, body?: unknown, init?: RequestOptions) => request<T>('PATCH', path, init, body),\n delete: <T>(path: string, init?: RequestOptions) => request<T>('DELETE', path, init),\n };\n}\n","import type { HttpClient } from '@core/httpClient';\nimport type { Video } from '@src/types/videos';\n\nexport interface VideosResource {\n getById(_id: string): Promise<Video>;\n}\n\nexport function createVideosResource(http: HttpClient): VideosResource {\n return {\n async getById(id: string) {\n const path = `b2b/videos/${encodeURIComponent(id)}`;\n return http.get<Video>(path);\n },\n };\n}\n","import type { HttpClient } from '@core/httpClient';\nimport type { Collection } from '@src/types/collection';\n\nexport interface CollectionsResource {\n list(): Promise<Collection[]>;\n getById(_id: string): Promise<Collection>;\n}\n\nexport function createCollectionsResource(http: HttpClient): CollectionsResource {\n return {\n async list() {\n const path = 'b2b/collections';\n return http.get<Collection[]>(path);\n },\n async getById(id: string) {\n const path = `b2b/collections/${encodeURIComponent(id)}`;\n return http.get<Collection>(path);\n },\n };\n}\n","import { createHttpClient, type HttpClient, type FetchLike } from '@core/httpClient';\nimport { createVideosResource, type VideosResource } from '@resources/videos';\nimport { createCollectionsResource, type CollectionsResource } from '@resources/collections';\n\n// Public type exports for consumers (barrel)\nexport * from '@src/types';\n\nexport const BASE_URL = process.env.JAAQ_API_URL as string;\n\nexport interface SDKConfig {\n baseUrl?: string; // Pending to approve\n apiKey: string; // Provided by backend per client\n clientId: string; // Provided by each consuming company at init\n fetch?: FetchLike; // Optional fetch injection (Node/custom envs) // Pending to approve\n timeoutMs?: number;\n headers?: Record<string, string>; // Pending to approve\n apiKeyHeaderName?: string;\n}\n\nexport class JaaqClient {\n public readonly videos: VideosResource;\n public readonly collections: CollectionsResource;\n\n // Keep constructor private to enforce controlled instantiation.\n private constructor(http: HttpClient) {\n this.videos = createVideosResource(http);\n this.collections = createCollectionsResource(http);\n }\n\n /**\n * Public \"friend\" constructor that remains inside the class boundary.\n * This method is callable from the module-level build function\n * without exposing `new JaaqClient(...)` to consumers.\n */\n static fromHttp(http: HttpClient): JaaqClient {\n return new JaaqClient(http);\n }\n\n /**\n * OO-friendly static initializer.\n * Delegates to the single source of truth: `buildClient`.\n */\n static init(config: SDKConfig): JaaqClient {\n return buildClient(config);\n }\n}\n\n/**\n * Canonical factory (idiomatic for JS/TS).\n * Delegates to the single source of truth: `buildClient`.\n */\nexport function createJaaqClient(config: SDKConfig): JaaqClient {\n return buildClient(config);\n}\n\n/**\n * Single source of truth for constructing the client.\n * Both `createJaaqClient` and `JaaqClient.init` call this function.\n * The constructor stays private; instantiation is funneled via `JaaqClient.fromHttp`.\n */\nfunction buildClient(config: SDKConfig): JaaqClient {\n const { baseUrl = BASE_URL, apiKey, clientId, fetch: fetchImpl, headers, apiKeyHeaderName = 'x-api-key' } = config;\n\n const http = createHttpClient({\n baseUrl,\n apiKey,\n clientId,\n apiKeyHeaderName,\n fetch: fetchImpl ?? (globalThis.fetch as FetchLike | undefined),\n headers,\n });\n\n return JaaqClient.fromHttp(http);\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).JaaqSDK={})}(this,function(e){"use strict";function t(e){const{baseUrl:t,apiKey:n,clientId:o,fetch:i,headers:s,apiKeyHeaderName:r="x-api-key"}=e,c=i??globalThis.fetch;if(!c)throw new Error("No fetch implementation found. Provide one in SDKConfig.fetch for Node or custom environments.");const a={"Content-Type":"application/json",[r]:n,"x-client-id":o,...s},f=async(e,n,o,i)=>{const s={method:e,headers:{...a,...o?.headers},...o};void 0!==i&&(s.body=JSON.stringify(i));const r=await c((e=>function(e,t){return/^https?:\/\//i.test(t)?t:`${e.replace(/\/+$/,"")}/${t.replace(/^\/+/,"")}`}(t,e))(n),s);return async function(e){if(!e.ok){const t=await e.text().catch(()=>"");throw new Error(`HTTP ${e.status}: ${t||e.statusText}`)}if(204!==e.status)return e.json()}(r)};return{get:(e,t)=>f("GET",e,t),post:(e,t,n)=>f("POST",e,n,t),put:(e,t,n)=>f("PUT",e,n,t),patch:(e,t,n)=>f("PATCH",e,n,t),delete:(e,t)=>f("DELETE",e,t)}}const n=void 0;class o{constructor(e){this.videos=function(e){return{async getById(t){const n=`b2b/videos/${encodeURIComponent(t)}`;return e.get(n)}}}(e),this.collections=function(e){return{list:async()=>e.get("b2b/collections"),async getById(t){const n=`b2b/collections/${encodeURIComponent(t)}`;return e.get(n)}}}(e)}static fromHttp(e){return new o(e)}static init(e){return i(e)}}function i(e){const{baseUrl:i=n,apiKey:s,clientId:r,fetch:c,headers:a,apiKeyHeaderName:f="x-api-key"}=e,d=t({baseUrl:i,apiKey:s,clientId:r,apiKeyHeaderName:f,fetch:c??globalThis.fetch,headers:a});return o.fromHttp(d)}e.BASE_URL=n,e.JaaqClient=o,e.createJaaqClient=function(e){return i(e)}});
2
+ //# sourceMappingURL=jaaq-sdk.min.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jaaq-sdk.min.js","sources":["../src/core/httpClient.ts","../src/index.ts","../src/resources/videos.ts","../src/resources/collections.ts"],"sourcesContent":["export type FetchLike = typeof globalThis.fetch;\ntype RequestOptions = Parameters<typeof globalThis.fetch>[1];\ntype FetchResponse = Awaited<ReturnType<FetchLike>>;\n\nexport interface HttpClientConfig {\n baseUrl: string;\n apiKey: string;\n clientId: string;\n apiKeyHeaderName?: string;\n fetch?: FetchLike;\n timeoutMs?: number;\n headers?: Record<string, string>;\n}\n\nexport interface HttpClient {\n get<T>(_path: string, _init?: RequestOptions): Promise<T>;\n post<T>(_path: string, _body?: unknown, _init?: RequestOptions): Promise<T>;\n put<T>(_path: string, _body?: unknown, _init?: RequestOptions): Promise<T>;\n patch<T>(_path: string, _body?: unknown, _init?: RequestOptions): Promise<T>;\n delete<T>(_path: string, _init?: RequestOptions): Promise<T>;\n}\n\nasync function parseJson<T>(res: FetchResponse): Promise<T> {\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new Error(`HTTP ${res.status}: ${text || res.statusText}`);\n }\n if (res.status === 204) return undefined as unknown as T;\n return res.json() as Promise<T>;\n}\n\n/**\n * Join base URL and path avoiding duplicate slashes.\n * - Preserves absolute paths (http/https) as-is.\n * - Trims trailing slashes from base and leading slashes from path.\n */\nfunction joinUrl(baseUrl: string, path: string): string {\n // return path if it is an absolute URL\n if (/^https?:\\/\\//i.test(path)) return path;\n const base = baseUrl.replace(/\\/+$/, '');\n const relative = path.replace(/^\\/+/, '');\n return `${base}/${relative}`;\n}\n\nexport function createHttpClient(config: HttpClientConfig): HttpClient {\n const { baseUrl, apiKey, clientId, fetch: fetchImpl, headers, apiKeyHeaderName = 'x-api-key' } = config;\n\n const f: FetchLike | undefined = fetchImpl ?? (globalThis.fetch as FetchLike | undefined);\n if (!f) {\n throw new Error('No fetch implementation found. Provide one in SDKConfig.fetch for Node or custom environments.');\n }\n\n const baseHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n [apiKeyHeaderName]: apiKey,\n 'x-client-id': clientId,\n ...headers,\n };\n\n const fullUrl = (path: string) => joinUrl(baseUrl, path);\n\n const request = async <T>(method: string, path: string, init?: RequestOptions, body?: unknown) => {\n const reqInit: RequestOptions = {\n method,\n headers: {\n ...baseHeaders,\n ...(init?.headers as Record<string, string> | undefined),\n },\n ...init,\n };\n if (body !== undefined) reqInit.body = JSON.stringify(body);\n const res = await f(fullUrl(path), reqInit);\n return parseJson<T>(res);\n };\n\n return {\n get: <T>(path: string, init?: RequestOptions) => request<T>('GET', path, init),\n post: <T>(path: string, body?: unknown, init?: RequestOptions) => request<T>('POST', path, init, body),\n put: <T>(path: string, body?: unknown, init?: RequestOptions) => request<T>('PUT', path, init, body),\n patch: <T>(path: string, body?: unknown, init?: RequestOptions) => request<T>('PATCH', path, init, body),\n delete: <T>(path: string, init?: RequestOptions) => request<T>('DELETE', path, init),\n };\n}\n","import { createHttpClient, type HttpClient, type FetchLike } from '@core/httpClient';\nimport { createVideosResource, type VideosResource } from '@resources/videos';\nimport { createCollectionsResource, type CollectionsResource } from '@resources/collections';\n\n// Public type exports for consumers (barrel)\nexport * from '@src/types';\n\nexport const BASE_URL = process.env.JAAQ_API_URL as string;\n\nexport interface SDKConfig {\n baseUrl?: string; // Pending to approve\n apiKey: string; // Provided by backend per client\n clientId: string; // Provided by each consuming company at init\n fetch?: FetchLike; // Optional fetch injection (Node/custom envs) // Pending to approve\n timeoutMs?: number;\n headers?: Record<string, string>; // Pending to approve\n apiKeyHeaderName?: string;\n}\n\nexport class JaaqClient {\n public readonly videos: VideosResource;\n public readonly collections: CollectionsResource;\n\n // Keep constructor private to enforce controlled instantiation.\n private constructor(http: HttpClient) {\n this.videos = createVideosResource(http);\n this.collections = createCollectionsResource(http);\n }\n\n /**\n * Public \"friend\" constructor that remains inside the class boundary.\n * This method is callable from the module-level build function\n * without exposing `new JaaqClient(...)` to consumers.\n */\n static fromHttp(http: HttpClient): JaaqClient {\n return new JaaqClient(http);\n }\n\n /**\n * OO-friendly static initializer.\n * Delegates to the single source of truth: `buildClient`.\n */\n static init(config: SDKConfig): JaaqClient {\n return buildClient(config);\n }\n}\n\n/**\n * Canonical factory (idiomatic for JS/TS).\n * Delegates to the single source of truth: `buildClient`.\n */\nexport function createJaaqClient(config: SDKConfig): JaaqClient {\n return buildClient(config);\n}\n\n/**\n * Single source of truth for constructing the client.\n * Both `createJaaqClient` and `JaaqClient.init` call this function.\n * The constructor stays private; instantiation is funneled via `JaaqClient.fromHttp`.\n */\nfunction buildClient(config: SDKConfig): JaaqClient {\n const { baseUrl = BASE_URL, apiKey, clientId, fetch: fetchImpl, headers, apiKeyHeaderName = 'x-api-key' } = config;\n\n const http = createHttpClient({\n baseUrl,\n apiKey,\n clientId,\n apiKeyHeaderName,\n fetch: fetchImpl ?? (globalThis.fetch as FetchLike | undefined),\n headers,\n });\n\n return JaaqClient.fromHttp(http);\n}\n","import type { HttpClient } from '@core/httpClient';\nimport type { Video } from '@src/types/videos';\n\nexport interface VideosResource {\n getById(_id: string): Promise<Video>;\n}\n\nexport function createVideosResource(http: HttpClient): VideosResource {\n return {\n async getById(id: string) {\n const path = `b2b/videos/${encodeURIComponent(id)}`;\n return http.get<Video>(path);\n },\n };\n}\n","import type { HttpClient } from '@core/httpClient';\nimport type { Collection } from '@src/types/collection';\n\nexport interface CollectionsResource {\n list(): Promise<Collection[]>;\n getById(_id: string): Promise<Collection>;\n}\n\nexport function createCollectionsResource(http: HttpClient): CollectionsResource {\n return {\n async list() {\n const path = 'b2b/collections';\n return http.get<Collection[]>(path);\n },\n async getById(id: string) {\n const path = `b2b/collections/${encodeURIComponent(id)}`;\n return http.get<Collection>(path);\n },\n };\n}\n"],"names":["createHttpClient","config","baseUrl","apiKey","clientId","fetch","fetchImpl","headers","apiKeyHeaderName","f","globalThis","Error","baseHeaders","request","async","method","path","init","body","reqInit","undefined","JSON","stringify","res","test","replace","joinUrl","fullUrl","ok","text","catch","status","statusText","json","parseJson","get","post","put","patch","delete","BASE_URL","JaaqClient","constructor","http","this","videos","getById","id","encodeURIComponent","createVideosResource","collections","createCollectionsResource","fromHttp","buildClient"],"mappings":"8OA4CM,SAAUA,EAAiBC,GAC/B,MAAMC,QAAEA,EAAOC,OAAEA,EAAMC,SAAEA,EAAUC,MAAOC,EAASC,QAAEA,EAAOC,iBAAEA,EAAmB,aAAgBP,EAE3FQ,EAA2BH,GAAcI,WAAWL,MAC1D,IAAKI,EACH,MAAM,IAAIE,MAAM,kGAGlB,MAAMC,EAAsC,CAC1C,eAAgB,mBAChBJ,CAACA,GAAmBL,EACpB,cAAeC,KACZG,GAKCM,EAAUC,MAAUC,EAAgBC,EAAcC,EAAuBC,KAC7E,MAAMC,EAA0B,CAC9BJ,SACAR,QAAS,IACJK,KACCK,GAAMV,YAETU,QAEQG,IAATF,IAAoBC,EAAQD,KAAOG,KAAKC,UAAUJ,IACtD,MAAMK,QAAYd,EAZJ,CAACO,GAvBnB,SAAiBd,EAAiBc,GAEhC,MAAI,gBAAgBQ,KAAKR,GAAcA,EAGhC,GAFMd,EAAQuB,QAAQ,OAAQ,OACpBT,EAAKS,QAAQ,OAAQ,KAExC,CAiBoCC,CAAQxB,EAASc,GAY7BW,CAAQX,GAAOG,GACnC,OAlDJL,eAA4BS,GAC1B,IAAKA,EAAIK,GAAI,CACX,MAAMC,QAAaN,EAAIM,OAAOC,MAAM,IAAM,IAC1C,MAAM,IAAInB,MAAM,QAAQY,EAAIQ,WAAWF,GAAQN,EAAIS,aACrD,CACA,GAAmB,MAAfT,EAAIQ,OACR,OAAOR,EAAIU,MACb,CA2CWC,CAAaX,IAGtB,MAAO,CACLY,IAAK,CAAInB,EAAcC,IAA0BJ,EAAW,MAAOG,EAAMC,GACzEmB,KAAM,CAAIpB,EAAcE,EAAgBD,IAA0BJ,EAAW,OAAQG,EAAMC,EAAMC,GACjGmB,IAAK,CAAIrB,EAAcE,EAAgBD,IAA0BJ,EAAW,MAAOG,EAAMC,EAAMC,GAC/FoB,MAAO,CAAItB,EAAcE,EAAgBD,IAA0BJ,EAAW,QAASG,EAAMC,EAAMC,GACnGqB,OAAQ,CAAIvB,EAAcC,IAA0BJ,EAAW,SAAUG,EAAMC,GAEnF,OC3EauB,OAAWpB,QAYXqB,EAKX,WAAAC,CAAoBC,GAClBC,KAAKC,OClBH,SAA+BF,GACnC,MAAO,CACL,aAAMG,CAAQC,GACZ,MAAM/B,EAAO,cAAcgC,mBAAmBD,KAC9C,OAAOJ,EAAKR,IAAWnB,EACzB,EAEJ,CDWkBiC,CAAqBN,GACnCC,KAAKM,YElBH,SAAoCP,GACxC,MAAO,CACL7B,KAAU,SAED6B,EAAKR,IADC,mBAGf,aAAMW,CAAQC,GACZ,MAAM/B,EAAO,mBAAmBgC,mBAAmBD,KACnD,OAAOJ,EAAKR,IAAgBnB,EAC9B,EAEJ,CFOuBmC,CAA0BR,EAC/C,CAOA,eAAOS,CAAST,GACd,OAAO,IAAIF,EAAWE,EACxB,CAMA,WAAO1B,CAAKhB,GACV,OAAOoD,EAAYpD,EACrB,EAgBF,SAASoD,EAAYpD,GACnB,MAAMC,QAAEA,EAAUsC,EAAQrC,OAAEA,EAAMC,SAAEA,EAAUC,MAAOC,EAASC,QAAEA,EAAOC,iBAAEA,EAAmB,aAAgBP,EAEtG0C,EAAO3C,EAAiB,CAC5BE,UACAC,SACAC,WACAI,mBACAH,MAAOC,GAAcI,WAAWL,MAChCE,YAGF,OAAOkC,EAAWW,SAAST,EAC7B,gDAtBM,SAA2B1C,GAC/B,OAAOoD,EAAYpD,EACrB"}
package/package.json ADDED
@@ -0,0 +1,102 @@
1
+ {
2
+ "name": "@jaaq/jaaq-sdk-js",
3
+ "version": "0.0.1",
4
+ "description": "Official JavaScript SDK for JAAQ API",
5
+ "main": "./dist/index.cjs",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "rollup -c",
10
+ "dev": "dotenvx run -f .env.development -- rollup -c --watch",
11
+ "build:dev": "dotenvx run -f .env.development -- rollup -c",
12
+ "build:staging": "dotenvx run -f .env.staging -- rollup -c",
13
+ "build:prod": "dotenvx run -f .env.production -- rollup -c",
14
+ "test": "vitest run --exclude=tests/build",
15
+ "test:watch": "vitest --exclude=tests/build",
16
+ "test:coverage": "vitest run --coverage --exclude=tests/build",
17
+ "test:coverage:watch": "vitest --coverage --exclude=tests/build",
18
+ "test:smoke:dev": "dotenvx run -f .env.development -- vitest run tests/build",
19
+ "test:smoke:staging": "dotenvx run -f .env.staging -- vitest run tests/build",
20
+ "test:smoke:prod": "dotenvx run -f .env.production -- vitest run tests/build",
21
+ "test:smoke:ci": "vitest run tests/build",
22
+ "check-paths-in-bundle": "grep -R -nE --exclude='*.map' '@core/|@resources/' dist || echo 'OK no aliases found'",
23
+ "openapi:gen": "openapi-typescript ./openapi.json -o ./src/gen/openapi.ts",
24
+ "audit:moderate": "pnpm audit --audit-level=moderate",
25
+ "lint": "eslint .",
26
+ "lint:fix": "eslint . --fix",
27
+ "format": "prettier --write .",
28
+ "format:check": "prettier --check .",
29
+ "prepare": "husky"
30
+ },
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.mjs",
35
+ "require": "./dist/index.cjs",
36
+ "default": "./dist/index.mjs"
37
+ }
38
+ },
39
+ "keywords": [
40
+ "jaaq",
41
+ "sdk",
42
+ "api",
43
+ "video",
44
+ "collections"
45
+ ],
46
+ "author": "JAAQ",
47
+ "license": "ISC",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "https://github.com/jaaq-eng/jaaq-sdk-js.git"
51
+ },
52
+ "bugs": {
53
+ "url": "https://github.com/jaaq-eng/jaaq-sdk-js/issues"
54
+ },
55
+ "homepage": "https://github.com/jaaq-eng/jaaq-sdk-js#readme",
56
+ "publishConfig": {
57
+ "access": "public"
58
+ },
59
+ "packageManager": "pnpm@10.17.1",
60
+ "lint-staged": {
61
+ "*.{ts,tsx,js}": [
62
+ "eslint --fix --max-warnings=0"
63
+ ],
64
+ "*.{json,md,yml,yaml,ts,tsx,js}": [
65
+ "prettier --write"
66
+ ]
67
+ },
68
+ "files": [
69
+ "dist",
70
+ "README.md",
71
+ "LICENSE"
72
+ ],
73
+ "devDependencies": {
74
+ "@commitlint/cli": "^20.1.0",
75
+ "@commitlint/config-conventional": "^20.0.0",
76
+ "@dotenvx/dotenvx": "^1.51.1",
77
+ "@eslint/js": "^9.39.1",
78
+ "@rollup/plugin-commonjs": "^29.0.0",
79
+ "@rollup/plugin-node-resolve": "^16.0.3",
80
+ "@rollup/plugin-replace": "^6.0.3",
81
+ "@rollup/plugin-terser": "^0.4.4",
82
+ "@rollup/plugin-typescript": "^12.3.0",
83
+ "@types/node": "^24.10.0",
84
+ "@typescript-eslint/eslint-plugin": "^8.46.3",
85
+ "@typescript-eslint/parser": "^8.46.3",
86
+ "eslint": "^9.39.1",
87
+ "eslint-config-prettier": "^10.1.8",
88
+ "globals": "^16.5.0",
89
+ "husky": "^9.1.7",
90
+ "lint-staged": "^16.2.6",
91
+ "msw": "^2.12.1",
92
+ "openapi-typescript": "^7.10.1",
93
+ "prettier": "^3.6.2",
94
+ "rollup": "^4.53.2",
95
+ "rollup-plugin-dts": "^6.2.3",
96
+ "tslib": "^2.8.1",
97
+ "typescript": "^5.9.3",
98
+ "vitest": "^4.0.8",
99
+ "@vitest/coverage-v8": "^4.0.8",
100
+ "rollup-plugin-tsconfig-paths": "^1.5.2"
101
+ }
102
+ }