@kywi-software/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +172 -0
  3. package/dist/__tests__/auth-client.test.d.ts +2 -0
  4. package/dist/__tests__/auth-client.test.d.ts.map +1 -0
  5. package/dist/__tests__/auth-client.test.js +97 -0
  6. package/dist/__tests__/auth-client.test.js.map +1 -0
  7. package/dist/__tests__/content-client.test.d.ts +2 -0
  8. package/dist/__tests__/content-client.test.d.ts.map +1 -0
  9. package/dist/__tests__/content-client.test.js +178 -0
  10. package/dist/__tests__/content-client.test.js.map +1 -0
  11. package/dist/__tests__/create-sdk.test.d.ts +2 -0
  12. package/dist/__tests__/create-sdk.test.d.ts.map +1 -0
  13. package/dist/__tests__/create-sdk.test.js +86 -0
  14. package/dist/__tests__/create-sdk.test.js.map +1 -0
  15. package/dist/__tests__/feed-client.test.d.ts +2 -0
  16. package/dist/__tests__/feed-client.test.d.ts.map +1 -0
  17. package/dist/__tests__/feed-client.test.js +81 -0
  18. package/dist/__tests__/feed-client.test.js.map +1 -0
  19. package/dist/__tests__/hooks.test.d.ts +2 -0
  20. package/dist/__tests__/hooks.test.d.ts.map +1 -0
  21. package/dist/__tests__/hooks.test.js +123 -0
  22. package/dist/__tests__/hooks.test.js.map +1 -0
  23. package/dist/__tests__/http-client.test.d.ts +2 -0
  24. package/dist/__tests__/http-client.test.d.ts.map +1 -0
  25. package/dist/__tests__/http-client.test.js +164 -0
  26. package/dist/__tests__/http-client.test.js.map +1 -0
  27. package/dist/__tests__/i18n-client.test.d.ts +2 -0
  28. package/dist/__tests__/i18n-client.test.d.ts.map +1 -0
  29. package/dist/__tests__/i18n-client.test.js +73 -0
  30. package/dist/__tests__/i18n-client.test.js.map +1 -0
  31. package/dist/__tests__/media-client.test.d.ts +2 -0
  32. package/dist/__tests__/media-client.test.d.ts.map +1 -0
  33. package/dist/__tests__/media-client.test.js +115 -0
  34. package/dist/__tests__/media-client.test.js.map +1 -0
  35. package/dist/__tests__/nav-client.test.d.ts +2 -0
  36. package/dist/__tests__/nav-client.test.d.ts.map +1 -0
  37. package/dist/__tests__/nav-client.test.js +81 -0
  38. package/dist/__tests__/nav-client.test.js.map +1 -0
  39. package/dist/__tests__/sdk-scope-additions.test.d.ts +2 -0
  40. package/dist/__tests__/sdk-scope-additions.test.d.ts.map +1 -0
  41. package/dist/__tests__/sdk-scope-additions.test.js +420 -0
  42. package/dist/__tests__/sdk-scope-additions.test.js.map +1 -0
  43. package/dist/__tests__/site-client.test.d.ts +2 -0
  44. package/dist/__tests__/site-client.test.d.ts.map +1 -0
  45. package/dist/__tests__/site-client.test.js +52 -0
  46. package/dist/__tests__/site-client.test.js.map +1 -0
  47. package/dist/__tests__/user-client.test.d.ts +2 -0
  48. package/dist/__tests__/user-client.test.d.ts.map +1 -0
  49. package/dist/__tests__/user-client.test.js +51 -0
  50. package/dist/__tests__/user-client.test.js.map +1 -0
  51. package/dist/auth-client.d.ts +10 -0
  52. package/dist/auth-client.d.ts.map +1 -0
  53. package/dist/auth-client.js +34 -0
  54. package/dist/auth-client.js.map +1 -0
  55. package/dist/category-client.d.ts +4 -0
  56. package/dist/category-client.d.ts.map +1 -0
  57. package/dist/category-client.js +36 -0
  58. package/dist/category-client.js.map +1 -0
  59. package/dist/content-client.d.ts +4 -0
  60. package/dist/content-client.d.ts.map +1 -0
  61. package/dist/content-client.js +89 -0
  62. package/dist/content-client.js.map +1 -0
  63. package/dist/create-sdk.d.ts +3 -0
  64. package/dist/create-sdk.d.ts.map +1 -0
  65. package/dist/create-sdk.js +43 -0
  66. package/dist/create-sdk.js.map +1 -0
  67. package/dist/feed-client.d.ts +4 -0
  68. package/dist/feed-client.d.ts.map +1 -0
  69. package/dist/feed-client.js +64 -0
  70. package/dist/feed-client.js.map +1 -0
  71. package/dist/form-client.d.ts +4 -0
  72. package/dist/form-client.d.ts.map +1 -0
  73. package/dist/form-client.js +17 -0
  74. package/dist/form-client.js.map +1 -0
  75. package/dist/hooks/context.d.ts +8 -0
  76. package/dist/hooks/context.d.ts.map +1 -0
  77. package/dist/hooks/context.js +15 -0
  78. package/dist/hooks/context.js.map +1 -0
  79. package/dist/hooks/index.d.ts +8 -0
  80. package/dist/hooks/index.d.ts.map +1 -0
  81. package/dist/hooks/index.js +7 -0
  82. package/dist/hooks/index.js.map +1 -0
  83. package/dist/hooks/use-content.d.ts +13 -0
  84. package/dist/hooks/use-content.d.ts.map +1 -0
  85. package/dist/hooks/use-content.js +16 -0
  86. package/dist/hooks/use-content.js.map +1 -0
  87. package/dist/hooks/use-feed.d.ts +5 -0
  88. package/dist/hooks/use-feed.d.ts.map +1 -0
  89. package/dist/hooks/use-feed.js +13 -0
  90. package/dist/hooks/use-feed.js.map +1 -0
  91. package/dist/hooks/use-nav.d.ts +5 -0
  92. package/dist/hooks/use-nav.d.ts.map +1 -0
  93. package/dist/hooks/use-nav.js +12 -0
  94. package/dist/hooks/use-nav.js.map +1 -0
  95. package/dist/hooks/use-sdk-query.d.ts +8 -0
  96. package/dist/hooks/use-sdk-query.d.ts.map +1 -0
  97. package/dist/hooks/use-sdk-query.js +32 -0
  98. package/dist/hooks/use-sdk-query.js.map +1 -0
  99. package/dist/hooks-client.d.ts +4 -0
  100. package/dist/hooks-client.d.ts.map +1 -0
  101. package/dist/hooks-client.js +20 -0
  102. package/dist/hooks-client.js.map +1 -0
  103. package/dist/http-client.d.ts +14 -0
  104. package/dist/http-client.d.ts.map +1 -0
  105. package/dist/http-client.js +78 -0
  106. package/dist/http-client.js.map +1 -0
  107. package/dist/i18n-client.d.ts +3 -0
  108. package/dist/i18n-client.d.ts.map +1 -0
  109. package/dist/i18n-client.js +26 -0
  110. package/dist/i18n-client.js.map +1 -0
  111. package/dist/index.d.ts +20 -0
  112. package/dist/index.d.ts.map +1 -0
  113. package/dist/index.js +21 -0
  114. package/dist/index.js.map +1 -0
  115. package/dist/layout-client.d.ts +4 -0
  116. package/dist/layout-client.d.ts.map +1 -0
  117. package/dist/layout-client.js +17 -0
  118. package/dist/layout-client.js.map +1 -0
  119. package/dist/media-client.d.ts +4 -0
  120. package/dist/media-client.d.ts.map +1 -0
  121. package/dist/media-client.js +36 -0
  122. package/dist/media-client.js.map +1 -0
  123. package/dist/nav-client.d.ts +4 -0
  124. package/dist/nav-client.d.ts.map +1 -0
  125. package/dist/nav-client.js +36 -0
  126. package/dist/nav-client.js.map +1 -0
  127. package/dist/search-client.d.ts +4 -0
  128. package/dist/search-client.d.ts.map +1 -0
  129. package/dist/search-client.js +17 -0
  130. package/dist/search-client.js.map +1 -0
  131. package/dist/site-client.d.ts +4 -0
  132. package/dist/site-client.d.ts.map +1 -0
  133. package/dist/site-client.js +25 -0
  134. package/dist/site-client.js.map +1 -0
  135. package/dist/tree-client.d.ts +4 -0
  136. package/dist/tree-client.d.ts.map +1 -0
  137. package/dist/tree-client.js +22 -0
  138. package/dist/tree-client.js.map +1 -0
  139. package/dist/types.d.ts +367 -0
  140. package/dist/types.d.ts.map +1 -0
  141. package/dist/types.js +13 -0
  142. package/dist/types.js.map +1 -0
  143. package/dist/user-client.d.ts +4 -0
  144. package/dist/user-client.d.ts.map +1 -0
  145. package/dist/user-client.js +49 -0
  146. package/dist/user-client.js.map +1 -0
  147. package/dist/version-client.d.ts +4 -0
  148. package/dist/version-client.d.ts.map +1 -0
  149. package/dist/version-client.js +16 -0
  150. package/dist/version-client.js.map +1 -0
  151. package/dist/workflow-client.d.ts +4 -0
  152. package/dist/workflow-client.d.ts.map +1 -0
  153. package/dist/workflow-client.js +17 -0
  154. package/dist/workflow-client.js.map +1 -0
  155. package/package.json +65 -0
@@ -0,0 +1,81 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { KywiHttpClient } from '../http-client.js';
3
+ import { createFeedClient } from '../feed-client.js';
4
+ const mockFetch = vi.fn();
5
+ vi.stubGlobal('fetch', mockFetch);
6
+ function ok(data, status = 200) {
7
+ return new Response(JSON.stringify(data), {
8
+ status,
9
+ headers: { 'Content-Type': 'application/json' },
10
+ });
11
+ }
12
+ const paginatedResponse = {
13
+ data: [{ id: '1', title: 'Post 1' }, { id: '2', title: 'Post 2' }],
14
+ meta: { total: 10, page: 1, pageSize: 2, totalPages: 5 },
15
+ };
16
+ describe('FeedClient', () => {
17
+ let client;
18
+ beforeEach(() => {
19
+ mockFetch.mockReset();
20
+ const http = new KywiHttpClient({ baseUrl: 'https://api.test/api/v1' });
21
+ client = createFeedClient(http, 'default-site');
22
+ });
23
+ // ── query ─────────────────────────────────────────────────────────────
24
+ it('query sends POST with defaultSiteId when no siteId in query', async () => {
25
+ mockFetch.mockResolvedValue(ok(paginatedResponse));
26
+ const result = await client.query({ contentType: 'post' });
27
+ const [url, init] = mockFetch.mock.calls[0];
28
+ expect(new URL(url).pathname).toBe('/api/v1/feeds/query');
29
+ expect(init.method).toBe('POST');
30
+ const body = JSON.parse(init.body);
31
+ expect(body.contentType).toBe('post');
32
+ expect(body.siteId).toBe('default-site');
33
+ expect(result).toEqual({
34
+ items: paginatedResponse.data,
35
+ total: 10,
36
+ page: 1,
37
+ pageSize: 2,
38
+ totalPages: 5,
39
+ });
40
+ });
41
+ it('query preserves siteId from query when provided', async () => {
42
+ mockFetch.mockResolvedValue(ok(paginatedResponse));
43
+ await client.query({ contentType: 'post', siteId: 'custom-site' });
44
+ const body = JSON.parse(mockFetch.mock.calls[0][1].body);
45
+ expect(body.siteId).toBe('custom-site');
46
+ });
47
+ it('query passes all optional params', async () => {
48
+ mockFetch.mockResolvedValue(ok(paginatedResponse));
49
+ await client.query({
50
+ contentType: 'post',
51
+ filters: { category: 'news' },
52
+ search: 'hello',
53
+ status: 'published',
54
+ sort: { field: 'createdAt', direction: 'desc' },
55
+ limit: 5,
56
+ page: 2,
57
+ });
58
+ const body = JSON.parse(mockFetch.mock.calls[0][1].body);
59
+ expect(body.filters).toEqual({ category: 'news' });
60
+ expect(body.search).toBe('hello');
61
+ expect(body.status).toBe('published');
62
+ expect(body.sort).toEqual({ field: 'createdAt', direction: 'desc' });
63
+ expect(body.limit).toBe(5);
64
+ expect(body.page).toBe(2);
65
+ });
66
+ // ── get ───────────────────────────────────────────────────────────────
67
+ it('get sends correct URL and parses response', async () => {
68
+ mockFetch.mockResolvedValue(ok(paginatedResponse));
69
+ const result = await client.get('latest-posts');
70
+ const url = new URL(mockFetch.mock.calls[0][0]);
71
+ expect(url.pathname).toBe('/api/v1/feeds/latest-posts');
72
+ expect(result).toEqual({
73
+ items: paginatedResponse.data,
74
+ total: 10,
75
+ page: 1,
76
+ pageSize: 2,
77
+ totalPages: 5,
78
+ });
79
+ });
80
+ });
81
+ //# sourceMappingURL=feed-client.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feed-client.test.js","sourceRoot":"","sources":["../../src/__tests__/feed-client.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hooks.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/hooks.test.tsx"],"names":[],"mappings":""}
@@ -0,0 +1,123 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { describe, it, expect, vi } from 'vitest';
3
+ import { renderToStaticMarkup } from 'react-dom/server';
4
+ import { KywiSdkProvider, useKywiSdk } from '../hooks/context.js';
5
+ function createMockSdk() {
6
+ return {
7
+ content: {
8
+ getBySlug: vi.fn().mockResolvedValue({ id: '1', title: 'Test' }),
9
+ getById: vi.fn().mockResolvedValue(null),
10
+ getChildren: vi.fn().mockResolvedValue([]),
11
+ list: vi.fn().mockResolvedValue({ rows: [], total: 0 }),
12
+ create: vi.fn().mockResolvedValue({ id: 'new' }),
13
+ update: vi.fn().mockResolvedValue(null),
14
+ delete: vi.fn().mockResolvedValue(true),
15
+ publish: vi.fn().mockResolvedValue(null),
16
+ unpublish: vi.fn().mockResolvedValue(null),
17
+ },
18
+ nav: {
19
+ getTree: vi.fn().mockResolvedValue([]),
20
+ getBreadcrumbs: vi.fn().mockResolvedValue([]),
21
+ getChildren: vi.fn().mockResolvedValue([]),
22
+ },
23
+ site: {
24
+ getConfig: vi.fn().mockResolvedValue(null),
25
+ list: vi.fn().mockResolvedValue([]),
26
+ updateSettings: vi.fn().mockResolvedValue({}),
27
+ },
28
+ user: {
29
+ getCurrent: vi.fn().mockResolvedValue(null),
30
+ getById: vi.fn().mockResolvedValue(null),
31
+ list: vi.fn().mockResolvedValue({ users: [], total: 0 }),
32
+ create: vi.fn().mockResolvedValue({}),
33
+ },
34
+ feeds: {
35
+ query: vi.fn().mockResolvedValue({ items: [], total: 0, page: 1, pageSize: 10, totalPages: 0 }),
36
+ get: vi.fn().mockResolvedValue({ items: [], total: 0, page: 1, pageSize: 10, totalPages: 0 }),
37
+ list: vi.fn().mockResolvedValue([]),
38
+ create: vi.fn().mockResolvedValue({}),
39
+ update: vi.fn().mockResolvedValue({}),
40
+ delete: vi.fn().mockResolvedValue(undefined),
41
+ },
42
+ auth: {
43
+ login: vi.fn(),
44
+ register: vi.fn(),
45
+ refresh: vi.fn(),
46
+ forgotPassword: vi.fn(),
47
+ resetPassword: vi.fn(),
48
+ logout: vi.fn(),
49
+ },
50
+ media: {
51
+ upload: vi.fn(),
52
+ list: vi.fn(),
53
+ getById: vi.fn(),
54
+ delete: vi.fn(),
55
+ },
56
+ layout: {
57
+ get: vi.fn(),
58
+ update: vi.fn(),
59
+ updateRegion: vi.fn(),
60
+ },
61
+ search: {
62
+ search: vi.fn(),
63
+ },
64
+ versions: {
65
+ list: vi.fn(),
66
+ get: vi.fn(),
67
+ restore: vi.fn(),
68
+ },
69
+ workflow: {
70
+ publish: vi.fn(),
71
+ unpublish: vi.fn(),
72
+ restore: vi.fn(),
73
+ permanentDelete: vi.fn(),
74
+ },
75
+ categories: {
76
+ list: vi.fn(),
77
+ getById: vi.fn(),
78
+ create: vi.fn(),
79
+ update: vi.fn(),
80
+ delete: vi.fn(),
81
+ tree: vi.fn(),
82
+ },
83
+ forms: {
84
+ submit: vi.fn(),
85
+ listSubmissions: vi.fn(),
86
+ getCsrfToken: vi.fn(),
87
+ },
88
+ tree: {
89
+ getChildren: vi.fn(),
90
+ getBreadcrumbs: vi.fn(),
91
+ move: vi.fn(),
92
+ reorder: vi.fn(),
93
+ },
94
+ hooks: {
95
+ listWebhooks: vi.fn(),
96
+ createWebhook: vi.fn(),
97
+ updateWebhook: vi.fn(),
98
+ deleteWebhook: vi.fn(),
99
+ },
100
+ };
101
+ }
102
+ describe('KywiSdkProvider', () => {
103
+ it('provides SDK context to children', () => {
104
+ const mockSdk = createMockSdk();
105
+ let captured = null;
106
+ function TestChild() {
107
+ captured = useKywiSdk();
108
+ return _jsx("div", { children: "test" });
109
+ }
110
+ renderToStaticMarkup(_jsx(KywiSdkProvider, { sdk: mockSdk, children: _jsx(TestChild, {}) }));
111
+ expect(captured).toBe(mockSdk);
112
+ });
113
+ it('useKywiSdk throws without provider', () => {
114
+ function BadChild() {
115
+ useKywiSdk();
116
+ return _jsx("div", { children: "test" });
117
+ }
118
+ expect(() => {
119
+ renderToStaticMarkup(_jsx(BadChild, {}));
120
+ }).toThrow('useKywiSdk must be used within a KywiSdkProvider');
121
+ });
122
+ });
123
+ //# sourceMappingURL=hooks.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.test.js","sourceRoot":"","sources":["../../src/__tests__/hooks.test.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=http-client.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/http-client.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,164 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { KywiHttpClient } from '../http-client.js';
3
+ import { KywiSdkError } from '../types.js';
4
+ function mockResponse(status, body, ok) {
5
+ return {
6
+ status,
7
+ ok: ok ?? (status >= 200 && status < 300),
8
+ json: vi.fn().mockResolvedValue(body),
9
+ text: vi.fn().mockResolvedValue(typeof body === 'string' ? body : JSON.stringify(body)),
10
+ headers: new Headers(),
11
+ redirected: false,
12
+ statusText: '',
13
+ type: 'basic',
14
+ url: '',
15
+ clone: vi.fn(),
16
+ body: null,
17
+ bodyUsed: false,
18
+ arrayBuffer: vi.fn(),
19
+ blob: vi.fn(),
20
+ formData: vi.fn(),
21
+ bytes: vi.fn(),
22
+ };
23
+ }
24
+ describe('KywiHttpClient', () => {
25
+ const fetchMock = vi.fn();
26
+ beforeEach(() => {
27
+ fetchMock.mockReset();
28
+ vi.stubGlobal('fetch', fetchMock);
29
+ });
30
+ // ── GET ──────────────────────────────────────────────────────────────────
31
+ it('GET sends correct URL and headers', async () => {
32
+ fetchMock.mockResolvedValue(mockResponse(200, { data: 'ok' }));
33
+ const client = new KywiHttpClient({ baseUrl: 'https://api.example.com/v1' });
34
+ await client.get('/items');
35
+ expect(fetchMock).toHaveBeenCalledOnce();
36
+ const [url, init] = fetchMock.mock.calls[0];
37
+ expect(url).toBe('https://api.example.com/v1/items');
38
+ expect(init.method).toBe('GET');
39
+ expect(init.headers).toMatchObject({
40
+ 'Content-Type': 'application/json',
41
+ 'Accept': 'application/json',
42
+ });
43
+ });
44
+ it('GET appends query params', async () => {
45
+ fetchMock.mockResolvedValue(mockResponse(200, { data: [] }));
46
+ const client = new KywiHttpClient({ baseUrl: 'https://api.example.com/v1' });
47
+ await client.get('/items', { page: '1', limit: '10' });
48
+ const [url] = fetchMock.mock.calls[0];
49
+ const parsed = new URL(url);
50
+ expect(parsed.searchParams.get('page')).toBe('1');
51
+ expect(parsed.searchParams.get('limit')).toBe('10');
52
+ });
53
+ // ── POST ─────────────────────────────────────────────────────────────────
54
+ it('POST sends JSON body', async () => {
55
+ fetchMock.mockResolvedValue(mockResponse(201, { id: '1' }, true));
56
+ const client = new KywiHttpClient({ baseUrl: 'https://api.example.com/v1' });
57
+ const payload = { title: 'Hello', slug: 'hello' };
58
+ await client.post('/items', payload);
59
+ const [, init] = fetchMock.mock.calls[0];
60
+ expect(init.method).toBe('POST');
61
+ expect(init.body).toBe(JSON.stringify(payload));
62
+ });
63
+ // ── PUT ──────────────────────────────────────────────────────────────────
64
+ it('PUT sends JSON body', async () => {
65
+ fetchMock.mockResolvedValue(mockResponse(200, { id: '1', title: 'Updated' }));
66
+ const client = new KywiHttpClient({ baseUrl: 'https://api.example.com/v1' });
67
+ const payload = { title: 'Updated' };
68
+ await client.put('/items/1', payload);
69
+ const [url, init] = fetchMock.mock.calls[0];
70
+ expect(url).toBe('https://api.example.com/v1/items/1');
71
+ expect(init.method).toBe('PUT');
72
+ expect(init.body).toBe(JSON.stringify(payload));
73
+ });
74
+ // ── DELETE ───────────────────────────────────────────────────────────────
75
+ it('DELETE sends correct method', async () => {
76
+ fetchMock.mockResolvedValue(mockResponse(200, { success: true }));
77
+ const client = new KywiHttpClient({ baseUrl: 'https://api.example.com/v1' });
78
+ await client.delete('/items/1');
79
+ const [url, init] = fetchMock.mock.calls[0];
80
+ expect(url).toBe('https://api.example.com/v1/items/1');
81
+ expect(init.method).toBe('DELETE');
82
+ });
83
+ // ── Auth: Bearer token ───────────────────────────────────────────────────
84
+ it('includes Bearer token from config', async () => {
85
+ fetchMock.mockResolvedValue(mockResponse(200, {}));
86
+ const client = new KywiHttpClient({
87
+ baseUrl: 'https://api.example.com/v1',
88
+ token: 'jwt-token-123',
89
+ });
90
+ await client.get('/me');
91
+ const [, init] = fetchMock.mock.calls[0];
92
+ expect(init.headers).toMatchObject({
93
+ Authorization: 'Bearer jwt-token-123',
94
+ });
95
+ });
96
+ // ── Auth: API key ────────────────────────────────────────────────────────
97
+ it('sends the API key via the Authorization: Bearer header', async () => {
98
+ // The Kywi server authenticates API keys through Authorization (kywi_ prefix),
99
+ // never x-api-key.
100
+ fetchMock.mockResolvedValue(mockResponse(200, {}));
101
+ const client = new KywiHttpClient({
102
+ baseUrl: 'https://api.example.com/v1',
103
+ apiKey: 'kywi_key-abc',
104
+ });
105
+ await client.get('/items');
106
+ const [, init] = fetchMock.mock.calls[0];
107
+ expect(init.headers).toMatchObject({
108
+ 'Authorization': 'Bearer kywi_key-abc',
109
+ });
110
+ });
111
+ // ── Error handling ───────────────────────────────────────────────────────
112
+ it('throws KywiSdkError on non-OK non-404 responses', async () => {
113
+ fetchMock.mockResolvedValue(mockResponse(500, 'Internal Server Error', false));
114
+ const client = new KywiHttpClient({ baseUrl: 'https://api.example.com/v1' });
115
+ await expect(client.get('/fail')).rejects.toThrow(KywiSdkError);
116
+ await expect(client.get('/fail')).rejects.toMatchObject({
117
+ status: 500,
118
+ body: 'Internal Server Error',
119
+ });
120
+ });
121
+ // ── 404 handling ─────────────────────────────────────────────────────────
122
+ it('returns parsed body for 404 responses without throwing', async () => {
123
+ const notFoundBody = { error: { code: 'NOT_FOUND', message: 'Not found' } };
124
+ fetchMock.mockResolvedValue(mockResponse(404, notFoundBody, false));
125
+ const client = new KywiHttpClient({ baseUrl: 'https://api.example.com/v1' });
126
+ const result = await client.get('/missing');
127
+ expect(result).toEqual(notFoundBody);
128
+ });
129
+ // ── Trailing slash stripping ─────────────────────────────────────────────
130
+ it('strips trailing slash from baseUrl', async () => {
131
+ fetchMock.mockResolvedValue(mockResponse(200, {}));
132
+ const client = new KywiHttpClient({ baseUrl: 'https://api.example.com/v1///' });
133
+ await client.get('/items');
134
+ const [url] = fetchMock.mock.calls[0];
135
+ expect(url).toBe('https://api.example.com/v1/items');
136
+ });
137
+ // ── setToken ─────────────────────────────────────────────────────────────
138
+ it('setToken updates Authorization header', async () => {
139
+ fetchMock.mockResolvedValue(mockResponse(200, {}));
140
+ const client = new KywiHttpClient({ baseUrl: 'https://api.example.com/v1' });
141
+ client.setToken('new-token');
142
+ await client.get('/me');
143
+ const [, init] = fetchMock.mock.calls[0];
144
+ expect(init.headers).toMatchObject({
145
+ Authorization: 'Bearer new-token',
146
+ });
147
+ });
148
+ // ── Custom headers ──────────────────────────────────────────────────────
149
+ it('includes custom headers from config', async () => {
150
+ fetchMock.mockResolvedValue(mockResponse(200, {}));
151
+ const client = new KywiHttpClient({
152
+ baseUrl: 'https://api.example.com/v1',
153
+ headers: { 'X-Custom-Header': 'custom-value' },
154
+ });
155
+ await client.get('/items');
156
+ const [, init] = fetchMock.mock.calls[0];
157
+ expect(init.headers).toMatchObject({
158
+ 'X-Custom-Header': 'custom-value',
159
+ 'Content-Type': 'application/json',
160
+ 'Accept': 'application/json',
161
+ });
162
+ });
163
+ });
164
+ //# sourceMappingURL=http-client.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.test.js","sourceRoot":"","sources":["../../src/__tests__/http-client.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=i18n-client.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n-client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/i18n-client.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,73 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { createI18nClient } from '../i18n-client.js';
3
+ describe('I18nClient', () => {
4
+ function makeBundles() {
5
+ const en = new Map([
6
+ ['greeting', 'Hello'],
7
+ ['farewell', 'Goodbye'],
8
+ ['welcome', 'Welcome, {{name}}!'],
9
+ ['multi', '{{a}} and {{b}}'],
10
+ ]);
11
+ const fr = new Map([
12
+ ['greeting', 'Bonjour'],
13
+ ['farewell', 'Au revoir'],
14
+ ['welcome', 'Bienvenue, {{name}} !'],
15
+ ]);
16
+ return new Map([['en', en], ['fr', fr]]);
17
+ }
18
+ // ── get: basic lookup ─────────────────────────────────────────────────
19
+ it('returns the value for the default locale', () => {
20
+ const client = createI18nClient(makeBundles(), 'en');
21
+ expect(client.get('greeting')).toBe('Hello');
22
+ });
23
+ it('returns the value for a requested locale', () => {
24
+ const client = createI18nClient(makeBundles(), 'en');
25
+ expect(client.get('greeting', 'fr')).toBe('Bonjour');
26
+ });
27
+ // ── get: fallback ─────────────────────────────────────────────────────
28
+ it('falls back to default locale when key missing in requested locale', () => {
29
+ const bundles = makeBundles();
30
+ // 'multi' key only exists in 'en'
31
+ const client = createI18nClient(bundles, 'en');
32
+ expect(client.get('multi', 'fr')).toBe('{{a}} and {{b}}');
33
+ });
34
+ it('returns the key as-is when not found in any bundle', () => {
35
+ const client = createI18nClient(makeBundles(), 'en');
36
+ expect(client.get('unknown.key')).toBe('unknown.key');
37
+ });
38
+ it('returns the key as-is for a completely unknown locale', () => {
39
+ const client = createI18nClient(makeBundles(), 'en');
40
+ expect(client.get('greeting', 'de')).toBe('Hello');
41
+ });
42
+ it('returns key when default locale also has no matching bundle', () => {
43
+ const client = createI18nClient(new Map(), 'en');
44
+ expect(client.get('greeting')).toBe('greeting');
45
+ });
46
+ // ── get: interpolation ────────────────────────────────────────────────
47
+ it('interpolates {{var}} tokens', () => {
48
+ const client = createI18nClient(makeBundles(), 'en');
49
+ expect(client.get('welcome', undefined, { name: 'Alice' })).toBe('Welcome, Alice!');
50
+ });
51
+ it('interpolates multiple tokens', () => {
52
+ const client = createI18nClient(makeBundles(), 'en');
53
+ expect(client.get('multi', undefined, { a: 'X', b: 'Y' })).toBe('X and Y');
54
+ });
55
+ it('interpolates with a specific locale', () => {
56
+ const client = createI18nClient(makeBundles(), 'en');
57
+ expect(client.get('welcome', 'fr', { name: 'Alice' })).toBe('Bienvenue, Alice !');
58
+ });
59
+ it('leaves unmatched tokens untouched', () => {
60
+ const client = createI18nClient(makeBundles(), 'en');
61
+ expect(client.get('welcome')).toBe('Welcome, {{name}}!');
62
+ });
63
+ // ── getLocale ─────────────────────────────────────────────────────────
64
+ it('returns the configured locale', () => {
65
+ const client = createI18nClient(makeBundles(), 'en');
66
+ expect(client.getLocale()).toBe('en');
67
+ });
68
+ it('returns locale even if bundles are empty', () => {
69
+ const client = createI18nClient(new Map(), 'fr');
70
+ expect(client.getLocale()).toBe('fr');
71
+ });
72
+ });
73
+ //# sourceMappingURL=i18n-client.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n-client.test.js","sourceRoot":"","sources":["../../src/__tests__/i18n-client.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=media-client.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-client.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/media-client.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,115 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { KywiHttpClient } from '../http-client.js';
3
+ import { createMediaClient } from '../media-client.js';
4
+ const mockFetch = vi.fn();
5
+ vi.stubGlobal('fetch', mockFetch);
6
+ function ok(data, status = 200) {
7
+ return new Response(JSON.stringify(data), {
8
+ status,
9
+ headers: { 'Content-Type': 'application/json' },
10
+ });
11
+ }
12
+ const sampleMediaItem = {
13
+ id: 'media-1',
14
+ filename: 'photo.jpg',
15
+ originalFilename: 'my-photo.jpg',
16
+ mimeType: 'image/jpeg',
17
+ fileSize: 204800,
18
+ width: 1920,
19
+ height: 1080,
20
+ altText: 'A photo',
21
+ caption: 'Nice photo',
22
+ storageKey: 'uploads/2026/photo.jpg',
23
+ createdAt: '2026-04-04T00:00:00.000Z',
24
+ };
25
+ describe('MediaClient', () => {
26
+ let client;
27
+ beforeEach(() => {
28
+ mockFetch.mockReset();
29
+ const http = new KywiHttpClient({ baseUrl: 'https://api.test/api/v1' });
30
+ client = createMediaClient(http);
31
+ });
32
+ // ── upload ────────────────────────────────────────────────────────────
33
+ it('upload sends POST /media with FormData and returns MediaItem', async () => {
34
+ mockFetch.mockResolvedValue(ok({ data: sampleMediaItem }));
35
+ const file = new File(['content'], 'photo.jpg', { type: 'image/jpeg' });
36
+ const result = await client.upload(file, 'photo.jpg', { alt: 'A photo', caption: 'Nice photo' });
37
+ const [url, init] = mockFetch.mock.calls[0];
38
+ expect(new URL(url).pathname).toBe('/api/v1/media');
39
+ expect(init.method).toBe('POST');
40
+ expect(init.body).toBeInstanceOf(FormData);
41
+ expect(result).toEqual(sampleMediaItem);
42
+ });
43
+ it('upload sends POST /media with Buffer converted to Blob', async () => {
44
+ mockFetch.mockResolvedValue(ok({ data: sampleMediaItem }));
45
+ const buf = Buffer.from('image data');
46
+ const result = await client.upload(buf, 'photo.jpg');
47
+ const [url, init] = mockFetch.mock.calls[0];
48
+ expect(new URL(url).pathname).toBe('/api/v1/media');
49
+ expect(init.method).toBe('POST');
50
+ expect(init.body).toBeInstanceOf(FormData);
51
+ expect(result).toEqual(sampleMediaItem);
52
+ });
53
+ it('upload omits optional fields when not provided', async () => {
54
+ mockFetch.mockResolvedValue(ok({ data: sampleMediaItem }));
55
+ const file = new File(['content'], 'photo.jpg', { type: 'image/jpeg' });
56
+ await client.upload(file, 'photo.jpg');
57
+ const [, init] = mockFetch.mock.calls[0];
58
+ const fd = init.body;
59
+ expect(fd.has('alt')).toBe(false);
60
+ expect(fd.has('caption')).toBe(false);
61
+ });
62
+ // ── list ──────────────────────────────────────────────────────────────
63
+ it('list sends GET /media and returns paginated response', async () => {
64
+ const paginatedResponse = {
65
+ data: [sampleMediaItem],
66
+ meta: { total: 1, page: 1, pageSize: 20, totalPages: 1 },
67
+ };
68
+ mockFetch.mockResolvedValue(ok(paginatedResponse));
69
+ const result = await client.list();
70
+ const [url, init] = mockFetch.mock.calls[0];
71
+ expect(new URL(url).pathname).toBe('/api/v1/media');
72
+ expect(init.method).toBe('GET');
73
+ expect(result).toEqual(paginatedResponse);
74
+ });
75
+ it('list passes page, limit, and mimeType params', async () => {
76
+ const paginatedResponse = {
77
+ data: [],
78
+ meta: { total: 0, page: 2, pageSize: 5, totalPages: 0 },
79
+ };
80
+ mockFetch.mockResolvedValue(ok(paginatedResponse));
81
+ await client.list({ page: 2, limit: 5, mimeType: 'image/jpeg' });
82
+ const url = new URL(mockFetch.mock.calls[0][0]);
83
+ expect(url.searchParams.get('page')).toBe('2');
84
+ expect(url.searchParams.get('limit')).toBe('5');
85
+ expect(url.searchParams.get('mimeType')).toBe('image/jpeg');
86
+ });
87
+ it('list sends no query params when called with no opts', async () => {
88
+ mockFetch.mockResolvedValue(ok({ data: [], meta: { total: 0, page: 1, pageSize: 20, totalPages: 0 } }));
89
+ await client.list();
90
+ const url = new URL(mockFetch.mock.calls[0][0]);
91
+ expect(url.search).toBe('');
92
+ });
93
+ // ── getById ───────────────────────────────────────────────────────────
94
+ it('getById sends GET /media/:id and returns MediaItem', async () => {
95
+ mockFetch.mockResolvedValue(ok({ data: sampleMediaItem }));
96
+ const result = await client.getById('media-1');
97
+ const [url, init] = mockFetch.mock.calls[0];
98
+ expect(new URL(url).pathname).toBe('/api/v1/media/media-1');
99
+ expect(init.method).toBe('GET');
100
+ expect(result).toEqual(sampleMediaItem);
101
+ });
102
+ // ── delete ────────────────────────────────────────────────────────────
103
+ it('delete sends DELETE /media/:id and resolves void', async () => {
104
+ mockFetch.mockResolvedValue(new Response(JSON.stringify({ data: { deleted: true } }), {
105
+ status: 200,
106
+ headers: { 'Content-Type': 'application/json' },
107
+ }));
108
+ const result = await client.delete('media-1');
109
+ const [url, init] = mockFetch.mock.calls[0];
110
+ expect(new URL(url).pathname).toBe('/api/v1/media/media-1');
111
+ expect(init.method).toBe('DELETE');
112
+ expect(result).toBeUndefined();
113
+ });
114
+ });
115
+ //# sourceMappingURL=media-client.test.js.map