@fairu/sdk 1.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.
Files changed (150) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/README.md +447 -0
  3. package/dist/FairuClient-BUObk5LJ.cjs +91 -0
  4. package/dist/FairuClient-BUObk5LJ.cjs.map +1 -0
  5. package/dist/FairuClient-CT-IPh8i.js +92 -0
  6. package/dist/FairuClient-CT-IPh8i.js.map +1 -0
  7. package/dist/FairuError-D8sSmRAa.js +214 -0
  8. package/dist/FairuError-D8sSmRAa.js.map +1 -0
  9. package/dist/FairuError-DWH_Nwk0.cjs +213 -0
  10. package/dist/FairuError-DWH_Nwk0.cjs.map +1 -0
  11. package/dist/FairuProvider-BRpRddCI.cjs +51 -0
  12. package/dist/FairuProvider-BRpRddCI.cjs.map +1 -0
  13. package/dist/FairuProvider-Tc0vFo5L.js +52 -0
  14. package/dist/FairuProvider-Tc0vFo5L.js.map +1 -0
  15. package/dist/FileProxyBuilder-D-jZpAtd.cjs +269 -0
  16. package/dist/FileProxyBuilder-D-jZpAtd.cjs.map +1 -0
  17. package/dist/FileProxyBuilder-vHw1zBpJ.js +270 -0
  18. package/dist/FileProxyBuilder-vHw1zBpJ.js.map +1 -0
  19. package/dist/FragmentBuilder-BinoxeVS.js +194 -0
  20. package/dist/FragmentBuilder-BinoxeVS.js.map +1 -0
  21. package/dist/FragmentBuilder-BuvIC0aT.cjs +193 -0
  22. package/dist/FragmentBuilder-BuvIC0aT.cjs.map +1 -0
  23. package/dist/UploadError-CHBJuChw.cjs +81 -0
  24. package/dist/UploadError-CHBJuChw.cjs.map +1 -0
  25. package/dist/UploadError-_gEcJqSS.js +82 -0
  26. package/dist/UploadError-_gEcJqSS.js.map +1 -0
  27. package/dist/client/FairuClient.d.ts +8 -0
  28. package/dist/client/FairuClient.d.ts.map +1 -0
  29. package/dist/client/FairuProvider.d.ts +78 -0
  30. package/dist/client/FairuProvider.d.ts.map +1 -0
  31. package/dist/client/cache.d.ts +6 -0
  32. package/dist/client/cache.d.ts.map +1 -0
  33. package/dist/client/config.d.ts +77 -0
  34. package/dist/client/config.d.ts.map +1 -0
  35. package/dist/client/index.d.ts +5 -0
  36. package/dist/client/index.d.ts.map +1 -0
  37. package/dist/errors/FairuError.d.ts +107 -0
  38. package/dist/errors/FairuError.d.ts.map +1 -0
  39. package/dist/errors/UploadError.d.ts +54 -0
  40. package/dist/errors/UploadError.d.ts.map +1 -0
  41. package/dist/errors/index.d.ts +3 -0
  42. package/dist/errors/index.d.ts.map +1 -0
  43. package/dist/fileproxy/FileProxyBuilder.d.ts +141 -0
  44. package/dist/fileproxy/FileProxyBuilder.d.ts.map +1 -0
  45. package/dist/fileproxy/index.d.ts +4 -0
  46. package/dist/fileproxy/index.d.ts.map +1 -0
  47. package/dist/fileproxy/types.d.ts +79 -0
  48. package/dist/fileproxy/types.d.ts.map +1 -0
  49. package/dist/fileproxy/useFileProxyUrl.d.ts +91 -0
  50. package/dist/fileproxy/useFileProxyUrl.d.ts.map +1 -0
  51. package/dist/fileproxy.cjs +10 -0
  52. package/dist/fileproxy.cjs.map +1 -0
  53. package/dist/fileproxy.d.ts +2 -0
  54. package/dist/fileproxy.js +10 -0
  55. package/dist/fileproxy.js.map +1 -0
  56. package/dist/fragments/FragmentBuilder.d.ts +107 -0
  57. package/dist/fragments/FragmentBuilder.d.ts.map +1 -0
  58. package/dist/fragments/FragmentRegistry.d.ts +93 -0
  59. package/dist/fragments/FragmentRegistry.d.ts.map +1 -0
  60. package/dist/fragments/index.d.ts +5 -0
  61. package/dist/fragments/index.d.ts.map +1 -0
  62. package/dist/fragments/predefined/assetFragments.d.ts +39 -0
  63. package/dist/fragments/predefined/assetFragments.d.ts.map +1 -0
  64. package/dist/fragments/predefined/copyrightFragments.d.ts +12 -0
  65. package/dist/fragments/predefined/copyrightFragments.d.ts.map +1 -0
  66. package/dist/fragments/predefined/folderFragments.d.ts +28 -0
  67. package/dist/fragments/predefined/folderFragments.d.ts.map +1 -0
  68. package/dist/fragments/predefined/galleryFragments.d.ts +24 -0
  69. package/dist/fragments/predefined/galleryFragments.d.ts.map +1 -0
  70. package/dist/fragments/predefined/index.d.ts +6 -0
  71. package/dist/fragments/predefined/index.d.ts.map +1 -0
  72. package/dist/fragments/predefined/licenseFragments.d.ts +12 -0
  73. package/dist/fragments/predefined/licenseFragments.d.ts.map +1 -0
  74. package/dist/fragments/types.d.ts +46 -0
  75. package/dist/fragments/types.d.ts.map +1 -0
  76. package/dist/fragments.cjs +403 -0
  77. package/dist/fragments.cjs.map +1 -0
  78. package/dist/fragments.d.ts +2 -0
  79. package/dist/fragments.js +403 -0
  80. package/dist/fragments.js.map +1 -0
  81. package/dist/generated/graphql.d.ts +3464 -0
  82. package/dist/generated/graphql.d.ts.map +1 -0
  83. package/dist/hooks/index.d.ts +3 -0
  84. package/dist/hooks/index.d.ts.map +1 -0
  85. package/dist/hooks/mutations/index.d.ts +5 -0
  86. package/dist/hooks/mutations/index.d.ts.map +1 -0
  87. package/dist/hooks/mutations/useAssetMutations.d.ts +66 -0
  88. package/dist/hooks/mutations/useAssetMutations.d.ts.map +1 -0
  89. package/dist/hooks/mutations/useFolderMutations.d.ts +69 -0
  90. package/dist/hooks/mutations/useFolderMutations.d.ts.map +1 -0
  91. package/dist/hooks/mutations/useGalleryMutations.d.ts +39 -0
  92. package/dist/hooks/mutations/useGalleryMutations.d.ts.map +1 -0
  93. package/dist/hooks/mutations/useUpdateAsset.d.ts +50 -0
  94. package/dist/hooks/mutations/useUpdateAsset.d.ts.map +1 -0
  95. package/dist/hooks/queries/index.d.ts +8 -0
  96. package/dist/hooks/queries/index.d.ts.map +1 -0
  97. package/dist/hooks/queries/useAsset.d.ts +46 -0
  98. package/dist/hooks/queries/useAsset.d.ts.map +1 -0
  99. package/dist/hooks/queries/useAssets.d.ts +58 -0
  100. package/dist/hooks/queries/useAssets.d.ts.map +1 -0
  101. package/dist/hooks/queries/useCopyright.d.ts +51 -0
  102. package/dist/hooks/queries/useCopyright.d.ts.map +1 -0
  103. package/dist/hooks/queries/useFolder.d.ts +93 -0
  104. package/dist/hooks/queries/useFolder.d.ts.map +1 -0
  105. package/dist/hooks/queries/useGallery.d.ts +101 -0
  106. package/dist/hooks/queries/useGallery.d.ts.map +1 -0
  107. package/dist/hooks/queries/useLicense.d.ts +63 -0
  108. package/dist/hooks/queries/useLicense.d.ts.map +1 -0
  109. package/dist/hooks/queries/useTenant.d.ts +89 -0
  110. package/dist/hooks/queries/useTenant.d.ts.map +1 -0
  111. package/dist/index.cjs +244 -0
  112. package/dist/index.cjs.map +1 -0
  113. package/dist/index.d.ts +18 -0
  114. package/dist/index.d.ts.map +1 -0
  115. package/dist/index.js +244 -0
  116. package/dist/index.js.map +1 -0
  117. package/dist/react.cjs +596 -0
  118. package/dist/react.cjs.map +1 -0
  119. package/dist/react.d.ts +22 -0
  120. package/dist/react.d.ts.map +1 -0
  121. package/dist/react.js +596 -0
  122. package/dist/react.js.map +1 -0
  123. package/dist/upload/index.d.ts +4 -0
  124. package/dist/upload/index.d.ts.map +1 -0
  125. package/dist/upload/types.d.ts +96 -0
  126. package/dist/upload/types.d.ts.map +1 -0
  127. package/dist/upload/useMultipartUpload.d.ts +67 -0
  128. package/dist/upload/useMultipartUpload.d.ts.map +1 -0
  129. package/dist/upload/useUpload.d.ts +64 -0
  130. package/dist/upload/useUpload.d.ts.map +1 -0
  131. package/dist/upload.cjs +6 -0
  132. package/dist/upload.cjs.map +1 -0
  133. package/dist/upload.d.ts +2 -0
  134. package/dist/upload.js +6 -0
  135. package/dist/upload.js.map +1 -0
  136. package/dist/useFileProxyUrl-BCcux6re.cjs +87 -0
  137. package/dist/useFileProxyUrl-BCcux6re.cjs.map +1 -0
  138. package/dist/useFileProxyUrl-D_S1R_7O.js +88 -0
  139. package/dist/useFileProxyUrl-D_S1R_7O.js.map +1 -0
  140. package/dist/useMultipartUpload-BKnDbl8h.cjs +423 -0
  141. package/dist/useMultipartUpload-BKnDbl8h.cjs.map +1 -0
  142. package/dist/useMultipartUpload-CPK_PgUU.js +424 -0
  143. package/dist/useMultipartUpload-CPK_PgUU.js.map +1 -0
  144. package/dist/vanilla.cjs +219 -0
  145. package/dist/vanilla.cjs.map +1 -0
  146. package/dist/vanilla.d.ts +94 -0
  147. package/dist/vanilla.d.ts.map +1 -0
  148. package/dist/vanilla.js +220 -0
  149. package/dist/vanilla.js.map +1 -0
  150. package/package.json +177 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # Changelog
2
+
3
+ ## [1.0.1](https://github.com/sushidev-team/fairu-sdk-typescript/compare/v1.0.0...v1.0.1) (2026-01-05)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * include generated GraphQL types in repository ([e917822](https://github.com/sushidev-team/fairu-sdk-typescript/commit/e917822299fa527ac82e3e5a1012dcb594e8f2cd))
9
+
10
+ ## 1.0.0 (2026-01-04)
11
+
12
+
13
+ ### Bug Fixes
14
+
15
+ * add codegen step to CI workflow ([2802899](https://github.com/sushidev-team/fairu-sdk-typescript/commit/28028995f4978284a879eb5bb034269a0d1cf9fa))
16
+ * add ESLint 9 flat config ([6870d77](https://github.com/sushidev-team/fairu-sdk-typescript/commit/6870d77ba4258665f2b7a46e7cb1a4ab092f222d))
package/README.md ADDED
@@ -0,0 +1,447 @@
1
+ # @fairu/sdk
2
+
3
+ TypeScript SDK for the Fairu GraphQL API with React hooks and vanilla client support.
4
+
5
+ ## Features
6
+
7
+ - **React Hooks** for all GraphQL operations (assets, folders, galleries, copyrights, licenses, users, roles, etc.)
8
+ - **Vanilla Client** for Node.js and non-React frameworks
9
+ - **Fragment System** with fluent builder API
10
+ - **Upload Hooks** with progress tracking (simple + multipart)
11
+ - **FileProxy URL Builder** for image transformations
12
+ - **Graphcache** for normalized caching
13
+ - **Full TypeScript support** with generated types from the GraphQL schema
14
+ - **Dynamic Base URL** configuration with environment variable support
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install @fairu/sdk urql graphql
20
+ ```
21
+
22
+ For React applications:
23
+ ```bash
24
+ npm install @fairu/sdk urql graphql react react-dom
25
+ ```
26
+
27
+ ## Quick Start
28
+
29
+ ### React
30
+
31
+ ```tsx
32
+ import { FairuProvider, useAssets, useUpload } from '@fairu/sdk/react';
33
+
34
+ function App() {
35
+ return (
36
+ <FairuProvider
37
+ url="https://fairu.app/graphql"
38
+ token={process.env.FAIRU_TOKEN}
39
+ >
40
+ <AssetGallery />
41
+ </FairuProvider>
42
+ );
43
+ }
44
+
45
+ function AssetGallery() {
46
+ const { assets, fetching, error, hasMore, total } = useAssets({
47
+ page: 1,
48
+ perPage: 20
49
+ });
50
+ const { upload, progress, status } = useUpload();
51
+
52
+ if (fetching) return <div>Loading...</div>;
53
+ if (error) return <div>Error: {error.message}</div>;
54
+
55
+ return (
56
+ <div>
57
+ <p>{total} assets found</p>
58
+ {assets.map((asset) => (
59
+ <img key={asset.id} src={asset.url} alt={asset.alt} />
60
+ ))}
61
+ </div>
62
+ );
63
+ }
64
+ ```
65
+
66
+ ### Vanilla TypeScript
67
+
68
+ ```typescript
69
+ import { createVanillaClient } from '@fairu/sdk/vanilla';
70
+
71
+ const client = createVanillaClient({
72
+ url: 'https://fairu.app/graphql',
73
+ token: 'your-token',
74
+ });
75
+
76
+ // Fetch an asset
77
+ const asset = await client.assets.find('asset-id');
78
+
79
+ // List assets with pagination
80
+ const { data, paginatorInfo } = await client.assets.list({
81
+ page: 1,
82
+ perPage: 20,
83
+ folderId: 'folder-id',
84
+ });
85
+
86
+ // Upload a file
87
+ const result = await client.upload.simple(file, {
88
+ folderId: 'folder-id',
89
+ alt: 'My image',
90
+ });
91
+ ```
92
+
93
+ ## Examples
94
+
95
+ Check out the complete example applications:
96
+
97
+ - **[Vite + React Example](./examples/vite-react/)** - Full-featured example with asset browser, gallery viewer, search, and file uploads
98
+ - **[Next.js App Router Example](./examples/nextjs-app/)** - Example using Next.js 15 with App Router and optimized images
99
+
100
+ ## API Reference
101
+
102
+ ### React Hooks
103
+
104
+ #### Asset Hooks
105
+
106
+ ```tsx
107
+ import { useAsset, useAssets, useSearch, useMultipleAssets } from '@fairu/sdk/react';
108
+
109
+ // Fetch single asset by ID
110
+ const { asset, fetching, error, refetch } = useAsset('asset-id');
111
+
112
+ // Fetch asset by path
113
+ const { asset } = useAssetByPath('/folder/image.jpg');
114
+
115
+ // Fetch paginated assets
116
+ const { assets, pagination, hasMore, total, refetch } = useAssets({
117
+ page: 1,
118
+ perPage: 20,
119
+ folder: 'folder-id',
120
+ });
121
+
122
+ // Search assets
123
+ const { assets, pagination, hasMore } = useSearch('search query', {
124
+ page: 1,
125
+ perPage: 20,
126
+ orderBy: 'created_at',
127
+ orderDirection: 'DESC',
128
+ });
129
+
130
+ // Fetch multiple assets by IDs
131
+ const { assets, fetching } = useMultipleAssets(['id1', 'id2', 'id3']);
132
+ ```
133
+
134
+ #### Folder Hooks
135
+
136
+ ```tsx
137
+ import { useFolder, useFolderByPath } from '@fairu/sdk/react';
138
+
139
+ // Browse folder contents (assets + subfolders)
140
+ const { entries, pagination, hasMore } = useFolder({
141
+ folder: 'folder-id',
142
+ page: 1,
143
+ perPage: 50,
144
+ search: 'optional search',
145
+ orderBy: 'name',
146
+ orderDirection: 'ASC',
147
+ });
148
+
149
+ // Get folder by path
150
+ const { folder } = useFolderByPath('/path/to/folder');
151
+ ```
152
+
153
+ #### Gallery Hooks
154
+
155
+ ```tsx
156
+ import { useGallery, useGalleries, useGalleryItems } from '@fairu/sdk/react';
157
+
158
+ // Fetch single gallery
159
+ const { gallery, fetching, error } = useGallery('gallery-id');
160
+
161
+ // Fetch galleries list
162
+ const { galleries, pagination, hasMore, total } = useGalleries({
163
+ tenants: ['tenant-id'],
164
+ page: 1,
165
+ perPage: 20,
166
+ search: 'optional search',
167
+ });
168
+
169
+ // Fetch gallery items with pagination
170
+ const { galleryName, items, pagination, hasMore } = useGalleryItems('gallery-id', {
171
+ page: 1,
172
+ perPage: 50,
173
+ });
174
+ ```
175
+
176
+ #### Additional Hooks
177
+
178
+ ```tsx
179
+ import {
180
+ useCopyright, useCopyrights,
181
+ useLicense, useLicenses,
182
+ useTenant, useHealthCheck, useSupportedDomains,
183
+ } from '@fairu/sdk/react';
184
+
185
+ // Copyrights
186
+ const { copyright } = useCopyright('copyright-id');
187
+ const { copyrights, pagination } = useCopyrights({ page: 1, perPage: 20 });
188
+
189
+ // Licenses
190
+ const { license } = useLicense('license-id');
191
+ const { licenses, pagination } = useLicenses({ page: 1, perPage: 20 });
192
+
193
+ // Tenant info
194
+ const { tenant, fetching } = useTenant();
195
+
196
+ // Health check
197
+ const { health } = useHealthCheck();
198
+
199
+ // Supported domains
200
+ const { domains } = useSupportedDomains();
201
+ ```
202
+
203
+ #### Mutation Hooks
204
+
205
+ ```tsx
206
+ import { useUpdateAsset, useDeleteAsset } from '@fairu/sdk/react';
207
+
208
+ // Update asset
209
+ const { updateAsset, fetching } = useUpdateAsset();
210
+ await updateAsset({
211
+ id: 'asset-id',
212
+ alt: 'New alt text',
213
+ caption: 'New caption',
214
+ description: 'New description',
215
+ });
216
+
217
+ // Delete asset
218
+ const { deleteAsset, fetching } = useDeleteAsset();
219
+ const success = await deleteAsset('asset-id');
220
+ ```
221
+
222
+ #### Upload Hooks
223
+
224
+ ```tsx
225
+ import { useUpload, useMultipartUpload } from '@fairu/sdk/react';
226
+
227
+ // Simple upload (for files < 50MB)
228
+ const { upload, progress, status, error, cancel, reset } = useUpload();
229
+
230
+ const result = await upload(file, {
231
+ folderId: 'folder-id',
232
+ alt: 'Description',
233
+ });
234
+
235
+ console.log('Upload progress:', progress.percentage, '%');
236
+ console.log('Status:', status); // 'idle' | 'uploading' | 'success' | 'error'
237
+
238
+ // Multipart upload (for large files)
239
+ const { upload, progress, partsCompleted, totalParts, cancel } = useMultipartUpload();
240
+
241
+ await upload(largeFile, {
242
+ folderId: 'folder-id',
243
+ chunkSize: 10 * 1024 * 1024, // 10MB chunks
244
+ concurrency: 3, // Upload 3 chunks in parallel
245
+ });
246
+ ```
247
+
248
+ ### FileProxy URL Builder
249
+
250
+ ```typescript
251
+ import { fileProxy, useFileProxyUrl, useResponsiveImageUrl } from '@fairu/sdk/fileproxy';
252
+
253
+ // Build URLs programmatically
254
+ const url = fileProxy('asset-id', 'image.jpg')
255
+ .width(800)
256
+ .height(600)
257
+ .quality(85)
258
+ .format('webp')
259
+ .fit('cover')
260
+ .focal(50, 30, 1.5)
261
+ .build();
262
+
263
+ // React hook for single image
264
+ function AssetImage({ asset }) {
265
+ const url = useFileProxyUrl(asset.id, asset.name, {
266
+ width: 800,
267
+ format: 'webp',
268
+ quality: 85,
269
+ focal: asset.focal_point,
270
+ });
271
+
272
+ return <img src={url} alt={asset.alt} />;
273
+ }
274
+
275
+ // Responsive images with srcSet
276
+ function ResponsiveImage({ asset }) {
277
+ const { src, srcSet, sizes } = useResponsiveImageUrl(
278
+ asset.id,
279
+ asset.name,
280
+ {
281
+ widths: [400, 800, 1200, 1600],
282
+ sizes: '(max-width: 600px) 100vw, 50vw',
283
+ format: 'webp',
284
+ quality: 80,
285
+ }
286
+ );
287
+
288
+ return <img src={src} srcSet={srcSet} sizes={sizes} alt={asset.alt} />;
289
+ }
290
+ ```
291
+
292
+ ### Fragment System
293
+
294
+ ```typescript
295
+ import { FragmentBuilder, fragments } from '@fairu/sdk/fragments';
296
+
297
+ // Use predefined fragments
298
+ const minimalFragment = fragments.asset('minimal');
299
+ const fullFragment = fragments.asset('full');
300
+
301
+ // Build custom fragments
302
+ const customFragment = FragmentBuilder.for('FairuAsset')
303
+ .name('MyAssetFragment')
304
+ .select(['id', 'name', 'url', 'width', 'height', 'blurhash'])
305
+ .with('copyrights', (f) => f.select(['id', 'name', 'email']))
306
+ .build();
307
+
308
+ // Use with hooks
309
+ const { asset } = useAsset('id', { fragment: customFragment });
310
+ ```
311
+
312
+ ### Error Handling
313
+
314
+ ```typescript
315
+ import { FairuError, ValidationError, AuthenticationError, GraphQLError } from '@fairu/sdk';
316
+
317
+ try {
318
+ await updateAsset({ id: 'invalid' });
319
+ } catch (error) {
320
+ if (error instanceof ValidationError) {
321
+ console.log('Validation errors:', error.validationErrors);
322
+ // { field: ['error message'] }
323
+ } else if (error instanceof AuthenticationError) {
324
+ console.log('Authentication failed - check your token');
325
+ } else if (error instanceof GraphQLError) {
326
+ console.log('GraphQL error:', error.message);
327
+ } else if (error instanceof FairuError) {
328
+ console.log('General error:', error.message, error.code);
329
+ }
330
+ }
331
+ ```
332
+
333
+ ## Configuration
334
+
335
+ ### React Provider
336
+
337
+ ```tsx
338
+ import { FairuProvider } from '@fairu/sdk/react';
339
+
340
+ <FairuProvider
341
+ // GraphQL endpoint (required)
342
+ url="https://fairu.app/graphql"
343
+
344
+ // API token - can be static string or getter function
345
+ token="your-api-token"
346
+ // OR: getToken={() => localStorage.getItem('token')}
347
+
348
+ // FileProxy base URL (optional, default: https://files.fairu.app)
349
+ fileProxyUrl="https://files.fairu.app"
350
+
351
+ // Cache configuration (optional)
352
+ cache={{
353
+ enabled: true,
354
+ }}
355
+
356
+ // Global error handler (optional)
357
+ onError={(error) => {
358
+ console.error('GraphQL Error:', error);
359
+ // Send to error tracking service, show notification, etc.
360
+ }}
361
+ >
362
+ <App />
363
+ </FairuProvider>
364
+ ```
365
+
366
+ ### Environment Variables
367
+
368
+ The SDK supports environment variables for configuration:
369
+
370
+ ```bash
371
+ # Vite
372
+ VITE_FAIRU_URL=https://fairu.app/graphql
373
+ VITE_FAIRU_TOKEN=your-token
374
+
375
+ # Next.js
376
+ NEXT_PUBLIC_FAIRU_URL=https://fairu.app/graphql
377
+ NEXT_PUBLIC_FAIRU_TOKEN=your-token
378
+
379
+ # Node.js
380
+ FAIRU_URL=https://fairu.app/graphql
381
+ FAIRU_TOKEN=your-token
382
+ ```
383
+
384
+ ## TypeScript
385
+
386
+ All types are exported from the package:
387
+
388
+ ```typescript
389
+ import type {
390
+ // Entity types
391
+ FairuAsset,
392
+ FairuFolder,
393
+ FairuGallery,
394
+ FairuCopyright,
395
+ FairuLicense,
396
+ FairuUser,
397
+ FairuRole,
398
+ FairuTenant,
399
+
400
+ // Input types
401
+ FairuFileDto,
402
+ FairuFolderDto,
403
+ FairuGalleryDto,
404
+
405
+ // Enum types
406
+ FairuLicenseType,
407
+ FairuSortingDirection,
408
+ FairuUploadType,
409
+
410
+ // Query/Hook result types
411
+ AssetData,
412
+ AssetListData,
413
+ GalleryData,
414
+ FolderEntryData,
415
+ } from '@fairu/sdk';
416
+ ```
417
+
418
+ ## Package Exports
419
+
420
+ ```typescript
421
+ // Main exports (types, errors, core utilities)
422
+ import { FairuError, ValidationError } from '@fairu/sdk';
423
+
424
+ // React hooks and provider
425
+ import {
426
+ FairuProvider,
427
+ useAsset,
428
+ useAssets,
429
+ useUpload
430
+ } from '@fairu/sdk/react';
431
+
432
+ // Vanilla client (no React dependency)
433
+ import { createVanillaClient } from '@fairu/sdk/vanilla';
434
+
435
+ // FileProxy utilities
436
+ import { fileProxy, FileProxyBuilder } from '@fairu/sdk/fileproxy';
437
+
438
+ // Fragment system
439
+ import { FragmentBuilder, fragments } from '@fairu/sdk/fragments';
440
+
441
+ // Upload utilities
442
+ import { UploadError } from '@fairu/sdk/upload';
443
+ ```
444
+
445
+ ## License
446
+
447
+ MIT
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ const core = require("@urql/core");
3
+ const wonka = require("wonka");
4
+ const FairuError = require("./FairuError-DWH_Nwk0.cjs");
5
+ const DEFAULT_CONFIG = {
6
+ url: "https://fairu.app/graphql",
7
+ fileProxyUrl: "https://files.fairu.app",
8
+ retry: {
9
+ maxAttempts: 3,
10
+ delayMs: 1e3,
11
+ retryIf: (error) => {
12
+ return error.message.includes("Network") || error.message.includes("fetch");
13
+ }
14
+ },
15
+ cache: {
16
+ enabled: true,
17
+ keys: {}
18
+ }
19
+ };
20
+ function createErrorExchange(onError) {
21
+ return core.mapExchange({
22
+ onResult: (result) => {
23
+ if (result.error && onError) {
24
+ const fairuError = FairuError.FairuError.fromCombinedError(result.error);
25
+ onError(fairuError);
26
+ }
27
+ return result;
28
+ }
29
+ });
30
+ }
31
+ function createAuthExchange(config) {
32
+ return ({ forward }) => {
33
+ return (operations$) => {
34
+ return wonka.pipe(
35
+ operations$,
36
+ wonka.map((operation) => {
37
+ let token = null;
38
+ if (config.token) {
39
+ token = config.token;
40
+ } else if (config.getToken) {
41
+ const result = config.getToken();
42
+ if (typeof result === "string" || result === null) {
43
+ token = result;
44
+ }
45
+ }
46
+ if (!token) {
47
+ return operation;
48
+ }
49
+ const fetchOptions = typeof operation.context.fetchOptions === "object" ? operation.context.fetchOptions : {};
50
+ return {
51
+ ...operation,
52
+ context: {
53
+ ...operation.context,
54
+ fetchOptions: {
55
+ ...fetchOptions,
56
+ headers: {
57
+ ...fetchOptions.headers,
58
+ Authorization: `Bearer ${token}`
59
+ }
60
+ }
61
+ }
62
+ };
63
+ }),
64
+ forward
65
+ );
66
+ };
67
+ };
68
+ }
69
+ function createFairuClient(config = {}) {
70
+ const url = config.url ?? DEFAULT_CONFIG.url;
71
+ const cacheConfig = { ...DEFAULT_CONFIG.cache, ...config.cache };
72
+ const exchanges = [];
73
+ if (config.token || config.getToken) {
74
+ exchanges.push(createAuthExchange(config));
75
+ }
76
+ if (cacheConfig.enabled) {
77
+ exchanges.push(core.cacheExchange);
78
+ }
79
+ if (config.onError) {
80
+ exchanges.push(createErrorExchange(config.onError));
81
+ }
82
+ exchanges.push(core.fetchExchange);
83
+ return new core.Client({
84
+ url,
85
+ exchanges,
86
+ requestPolicy: "cache-first"
87
+ });
88
+ }
89
+ exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
90
+ exports.createFairuClient = createFairuClient;
91
+ //# sourceMappingURL=FairuClient-BUObk5LJ.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FairuClient-BUObk5LJ.cjs","sources":["../src/client/config.ts","../src/client/FairuClient.ts"],"sourcesContent":["/**\n * Configuration options for the Fairu client.\n */\nexport interface FairuClientConfig {\n /**\n * The GraphQL API URL.\n * @default 'https://fairu.app/graphql'\n */\n url?: string;\n\n /**\n * Static API token for authentication.\n */\n token?: string;\n\n /**\n * Function to dynamically get the API token.\n * Useful for token refresh or async token retrieval.\n */\n getToken?: () => string | null | Promise<string | null>;\n\n /**\n * Cache configuration options.\n */\n cache?: CacheConfig;\n\n /**\n * Retry configuration for failed requests.\n */\n retry?: RetryConfig;\n\n /**\n * Callback for handling errors globally.\n */\n onError?: (error: Error) => void;\n\n /**\n * FileProxy base URL for image transformations.\n * @default 'https://files.fairu.app'\n */\n fileProxyUrl?: string;\n}\n\n/**\n * Cache configuration options.\n */\nexport interface CacheConfig {\n /**\n * Whether caching is enabled.\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Custom entity keys for cache normalization.\n */\n keys?: Record<string, (data: unknown) => string | null>;\n}\n\n/**\n * Retry configuration options.\n */\nexport interface RetryConfig {\n /**\n * Maximum number of retry attempts.\n * @default 3\n */\n maxAttempts?: number;\n\n /**\n * Delay between retries in milliseconds.\n * @default 1000\n */\n delayMs?: number;\n\n /**\n * Function to determine if an error should trigger a retry.\n */\n retryIf?: (error: Error) => boolean;\n}\n\n/**\n * Default configuration values.\n */\nexport const DEFAULT_CONFIG: Required<\n Pick<FairuClientConfig, 'url' | 'fileProxyUrl'>\n> & {\n retry: Required<RetryConfig>;\n cache: Required<CacheConfig>;\n} = {\n url: 'https://fairu.app/graphql',\n fileProxyUrl: 'https://files.fairu.app',\n retry: {\n maxAttempts: 3,\n delayMs: 1000,\n retryIf: (error: Error) => {\n // Only retry network errors by default\n return error.message.includes('Network') || error.message.includes('fetch');\n },\n },\n cache: {\n enabled: true,\n keys: {},\n },\n};\n","import {\n Client,\n fetchExchange,\n cacheExchange,\n type Exchange,\n type OperationResult,\n type Operation,\n mapExchange,\n} from '@urql/core';\nimport { map, pipe } from 'wonka';\nimport { DEFAULT_CONFIG, type FairuClientConfig } from './config';\nimport { FairuError } from '../errors';\n\n/**\n * Create an error handling exchange.\n */\nfunction createErrorExchange(onError?: (error: Error) => void): Exchange {\n return mapExchange({\n onResult: (result: OperationResult) => {\n if (result.error && onError) {\n const fairuError = FairuError.fromCombinedError(result.error);\n onError(fairuError);\n }\n return result;\n },\n });\n}\n\n/**\n * Create an authentication exchange that adds Bearer token to requests.\n */\nfunction createAuthExchange(config: FairuClientConfig): Exchange {\n return ({ forward }) => {\n return (operations$) => {\n return pipe(\n operations$,\n map((operation: Operation) => {\n let token: string | null = null;\n\n if (config.token) {\n token = config.token;\n } else if (config.getToken) {\n const result = config.getToken();\n if (typeof result === 'string' || result === null) {\n token = result;\n }\n }\n\n if (!token) {\n return operation;\n }\n\n const fetchOptions =\n typeof operation.context.fetchOptions === 'object'\n ? operation.context.fetchOptions\n : {};\n\n return {\n ...operation,\n context: {\n ...operation.context,\n fetchOptions: {\n ...fetchOptions,\n headers: {\n ...(fetchOptions as Record<string, unknown>).headers as Record<string, string>,\n Authorization: `Bearer ${token}`,\n },\n },\n },\n };\n }),\n forward\n );\n };\n };\n}\n\n/**\n * Create a urql client configured for the Fairu GraphQL API.\n */\nexport function createFairuClient(config: FairuClientConfig = {}): Client {\n const url = config.url ?? DEFAULT_CONFIG.url;\n const cacheConfig = { ...DEFAULT_CONFIG.cache, ...config.cache };\n\n const exchanges: Exchange[] = [];\n\n // Auth exchange (adds Bearer token)\n if (config.token || config.getToken) {\n exchanges.push(createAuthExchange(config));\n }\n\n // Cache exchange (if enabled)\n if (cacheConfig.enabled) {\n exchanges.push(cacheExchange);\n }\n\n // Error exchange\n if (config.onError) {\n exchanges.push(createErrorExchange(config.onError));\n }\n\n // Fetch exchange (must be last)\n exchanges.push(fetchExchange);\n\n return new Client({\n url,\n exchanges,\n requestPolicy: 'cache-first',\n });\n}\n\nexport type { Client };\n"],"names":["mapExchange","FairuError","pipe","map","cacheExchange","fetchExchange","Client"],"mappings":";;;;AAoFO,MAAM,iBAKT;AAAA,EACF,KAAK;AAAA,EACL,cAAc;AAAA,EACd,OAAO;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,SAAS,CAAC,UAAiB;AAEzB,aAAO,MAAM,QAAQ,SAAS,SAAS,KAAK,MAAM,QAAQ,SAAS,OAAO;AAAA,IAC5E;AAAA,EAAA;AAAA,EAEF,OAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM,CAAA;AAAA,EAAC;AAEX;ACxFA,SAAS,oBAAoB,SAA4C;AACvE,SAAOA,iBAAY;AAAA,IACjB,UAAU,CAAC,WAA4B;AACrC,UAAI,OAAO,SAAS,SAAS;AAC3B,cAAM,aAAaC,WAAAA,WAAW,kBAAkB,OAAO,KAAK;AAC5D,gBAAQ,UAAU;AAAA,MACpB;AACA,aAAO;AAAA,IACT;AAAA,EAAA,CACD;AACH;AAKA,SAAS,mBAAmB,QAAqC;AAC/D,SAAO,CAAC,EAAE,cAAc;AACtB,WAAO,CAAC,gBAAgB;AACtB,aAAOC,MAAAA;AAAAA,QACL;AAAA,QACAC,MAAAA,IAAI,CAAC,cAAyB;AAC5B,cAAI,QAAuB;AAE3B,cAAI,OAAO,OAAO;AAChB,oBAAQ,OAAO;AAAA,UACjB,WAAW,OAAO,UAAU;AAC1B,kBAAM,SAAS,OAAO,SAAA;AACtB,gBAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AACjD,sBAAQ;AAAA,YACV;AAAA,UACF;AAEA,cAAI,CAAC,OAAO;AACV,mBAAO;AAAA,UACT;AAEA,gBAAM,eACJ,OAAO,UAAU,QAAQ,iBAAiB,WACtC,UAAU,QAAQ,eAClB,CAAA;AAEN,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,SAAS;AAAA,cACP,GAAG,UAAU;AAAA,cACb,cAAc;AAAA,gBACZ,GAAG;AAAA,gBACH,SAAS;AAAA,kBACP,GAAI,aAAyC;AAAA,kBAC7C,eAAe,UAAU,KAAK;AAAA,gBAAA;AAAA,cAChC;AAAA,YACF;AAAA,UACF;AAAA,QAEJ,CAAC;AAAA,QACD;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AACF;AAKO,SAAS,kBAAkB,SAA4B,IAAY;AACxE,QAAM,MAAM,OAAO,OAAO,eAAe;AACzC,QAAM,cAAc,EAAE,GAAG,eAAe,OAAO,GAAG,OAAO,MAAA;AAEzD,QAAM,YAAwB,CAAA;AAG9B,MAAI,OAAO,SAAS,OAAO,UAAU;AACnC,cAAU,KAAK,mBAAmB,MAAM,CAAC;AAAA,EAC3C;AAGA,MAAI,YAAY,SAAS;AACvB,cAAU,KAAKC,kBAAa;AAAA,EAC9B;AAGA,MAAI,OAAO,SAAS;AAClB,cAAU,KAAK,oBAAoB,OAAO,OAAO,CAAC;AAAA,EACpD;AAGA,YAAU,KAAKC,kBAAa;AAE5B,SAAO,IAAIC,KAAAA,OAAO;AAAA,IAChB;AAAA,IACA;AAAA,IACA,eAAe;AAAA,EAAA,CAChB;AACH;;;"}
@@ -0,0 +1,92 @@
1
+ import { fetchExchange, Client, mapExchange, cacheExchange } from "@urql/core";
2
+ import { pipe, map } from "wonka";
3
+ import { F as FairuError } from "./FairuError-D8sSmRAa.js";
4
+ const DEFAULT_CONFIG = {
5
+ url: "https://fairu.app/graphql",
6
+ fileProxyUrl: "https://files.fairu.app",
7
+ retry: {
8
+ maxAttempts: 3,
9
+ delayMs: 1e3,
10
+ retryIf: (error) => {
11
+ return error.message.includes("Network") || error.message.includes("fetch");
12
+ }
13
+ },
14
+ cache: {
15
+ enabled: true,
16
+ keys: {}
17
+ }
18
+ };
19
+ function createErrorExchange(onError) {
20
+ return mapExchange({
21
+ onResult: (result) => {
22
+ if (result.error && onError) {
23
+ const fairuError = FairuError.fromCombinedError(result.error);
24
+ onError(fairuError);
25
+ }
26
+ return result;
27
+ }
28
+ });
29
+ }
30
+ function createAuthExchange(config) {
31
+ return ({ forward }) => {
32
+ return (operations$) => {
33
+ return pipe(
34
+ operations$,
35
+ map((operation) => {
36
+ let token = null;
37
+ if (config.token) {
38
+ token = config.token;
39
+ } else if (config.getToken) {
40
+ const result = config.getToken();
41
+ if (typeof result === "string" || result === null) {
42
+ token = result;
43
+ }
44
+ }
45
+ if (!token) {
46
+ return operation;
47
+ }
48
+ const fetchOptions = typeof operation.context.fetchOptions === "object" ? operation.context.fetchOptions : {};
49
+ return {
50
+ ...operation,
51
+ context: {
52
+ ...operation.context,
53
+ fetchOptions: {
54
+ ...fetchOptions,
55
+ headers: {
56
+ ...fetchOptions.headers,
57
+ Authorization: `Bearer ${token}`
58
+ }
59
+ }
60
+ }
61
+ };
62
+ }),
63
+ forward
64
+ );
65
+ };
66
+ };
67
+ }
68
+ function createFairuClient(config = {}) {
69
+ const url = config.url ?? DEFAULT_CONFIG.url;
70
+ const cacheConfig = { ...DEFAULT_CONFIG.cache, ...config.cache };
71
+ const exchanges = [];
72
+ if (config.token || config.getToken) {
73
+ exchanges.push(createAuthExchange(config));
74
+ }
75
+ if (cacheConfig.enabled) {
76
+ exchanges.push(cacheExchange);
77
+ }
78
+ if (config.onError) {
79
+ exchanges.push(createErrorExchange(config.onError));
80
+ }
81
+ exchanges.push(fetchExchange);
82
+ return new Client({
83
+ url,
84
+ exchanges,
85
+ requestPolicy: "cache-first"
86
+ });
87
+ }
88
+ export {
89
+ DEFAULT_CONFIG as D,
90
+ createFairuClient as c
91
+ };
92
+ //# sourceMappingURL=FairuClient-CT-IPh8i.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FairuClient-CT-IPh8i.js","sources":["../src/client/config.ts","../src/client/FairuClient.ts"],"sourcesContent":["/**\n * Configuration options for the Fairu client.\n */\nexport interface FairuClientConfig {\n /**\n * The GraphQL API URL.\n * @default 'https://fairu.app/graphql'\n */\n url?: string;\n\n /**\n * Static API token for authentication.\n */\n token?: string;\n\n /**\n * Function to dynamically get the API token.\n * Useful for token refresh or async token retrieval.\n */\n getToken?: () => string | null | Promise<string | null>;\n\n /**\n * Cache configuration options.\n */\n cache?: CacheConfig;\n\n /**\n * Retry configuration for failed requests.\n */\n retry?: RetryConfig;\n\n /**\n * Callback for handling errors globally.\n */\n onError?: (error: Error) => void;\n\n /**\n * FileProxy base URL for image transformations.\n * @default 'https://files.fairu.app'\n */\n fileProxyUrl?: string;\n}\n\n/**\n * Cache configuration options.\n */\nexport interface CacheConfig {\n /**\n * Whether caching is enabled.\n * @default true\n */\n enabled?: boolean;\n\n /**\n * Custom entity keys for cache normalization.\n */\n keys?: Record<string, (data: unknown) => string | null>;\n}\n\n/**\n * Retry configuration options.\n */\nexport interface RetryConfig {\n /**\n * Maximum number of retry attempts.\n * @default 3\n */\n maxAttempts?: number;\n\n /**\n * Delay between retries in milliseconds.\n * @default 1000\n */\n delayMs?: number;\n\n /**\n * Function to determine if an error should trigger a retry.\n */\n retryIf?: (error: Error) => boolean;\n}\n\n/**\n * Default configuration values.\n */\nexport const DEFAULT_CONFIG: Required<\n Pick<FairuClientConfig, 'url' | 'fileProxyUrl'>\n> & {\n retry: Required<RetryConfig>;\n cache: Required<CacheConfig>;\n} = {\n url: 'https://fairu.app/graphql',\n fileProxyUrl: 'https://files.fairu.app',\n retry: {\n maxAttempts: 3,\n delayMs: 1000,\n retryIf: (error: Error) => {\n // Only retry network errors by default\n return error.message.includes('Network') || error.message.includes('fetch');\n },\n },\n cache: {\n enabled: true,\n keys: {},\n },\n};\n","import {\n Client,\n fetchExchange,\n cacheExchange,\n type Exchange,\n type OperationResult,\n type Operation,\n mapExchange,\n} from '@urql/core';\nimport { map, pipe } from 'wonka';\nimport { DEFAULT_CONFIG, type FairuClientConfig } from './config';\nimport { FairuError } from '../errors';\n\n/**\n * Create an error handling exchange.\n */\nfunction createErrorExchange(onError?: (error: Error) => void): Exchange {\n return mapExchange({\n onResult: (result: OperationResult) => {\n if (result.error && onError) {\n const fairuError = FairuError.fromCombinedError(result.error);\n onError(fairuError);\n }\n return result;\n },\n });\n}\n\n/**\n * Create an authentication exchange that adds Bearer token to requests.\n */\nfunction createAuthExchange(config: FairuClientConfig): Exchange {\n return ({ forward }) => {\n return (operations$) => {\n return pipe(\n operations$,\n map((operation: Operation) => {\n let token: string | null = null;\n\n if (config.token) {\n token = config.token;\n } else if (config.getToken) {\n const result = config.getToken();\n if (typeof result === 'string' || result === null) {\n token = result;\n }\n }\n\n if (!token) {\n return operation;\n }\n\n const fetchOptions =\n typeof operation.context.fetchOptions === 'object'\n ? operation.context.fetchOptions\n : {};\n\n return {\n ...operation,\n context: {\n ...operation.context,\n fetchOptions: {\n ...fetchOptions,\n headers: {\n ...(fetchOptions as Record<string, unknown>).headers as Record<string, string>,\n Authorization: `Bearer ${token}`,\n },\n },\n },\n };\n }),\n forward\n );\n };\n };\n}\n\n/**\n * Create a urql client configured for the Fairu GraphQL API.\n */\nexport function createFairuClient(config: FairuClientConfig = {}): Client {\n const url = config.url ?? DEFAULT_CONFIG.url;\n const cacheConfig = { ...DEFAULT_CONFIG.cache, ...config.cache };\n\n const exchanges: Exchange[] = [];\n\n // Auth exchange (adds Bearer token)\n if (config.token || config.getToken) {\n exchanges.push(createAuthExchange(config));\n }\n\n // Cache exchange (if enabled)\n if (cacheConfig.enabled) {\n exchanges.push(cacheExchange);\n }\n\n // Error exchange\n if (config.onError) {\n exchanges.push(createErrorExchange(config.onError));\n }\n\n // Fetch exchange (must be last)\n exchanges.push(fetchExchange);\n\n return new Client({\n url,\n exchanges,\n requestPolicy: 'cache-first',\n });\n}\n\nexport type { Client };\n"],"names":[],"mappings":";;;AAoFO,MAAM,iBAKT;AAAA,EACF,KAAK;AAAA,EACL,cAAc;AAAA,EACd,OAAO;AAAA,IACL,aAAa;AAAA,IACb,SAAS;AAAA,IACT,SAAS,CAAC,UAAiB;AAEzB,aAAO,MAAM,QAAQ,SAAS,SAAS,KAAK,MAAM,QAAQ,SAAS,OAAO;AAAA,IAC5E;AAAA,EAAA;AAAA,EAEF,OAAO;AAAA,IACL,SAAS;AAAA,IACT,MAAM,CAAA;AAAA,EAAC;AAEX;ACxFA,SAAS,oBAAoB,SAA4C;AACvE,SAAO,YAAY;AAAA,IACjB,UAAU,CAAC,WAA4B;AACrC,UAAI,OAAO,SAAS,SAAS;AAC3B,cAAM,aAAa,WAAW,kBAAkB,OAAO,KAAK;AAC5D,gBAAQ,UAAU;AAAA,MACpB;AACA,aAAO;AAAA,IACT;AAAA,EAAA,CACD;AACH;AAKA,SAAS,mBAAmB,QAAqC;AAC/D,SAAO,CAAC,EAAE,cAAc;AACtB,WAAO,CAAC,gBAAgB;AACtB,aAAO;AAAA,QACL;AAAA,QACA,IAAI,CAAC,cAAyB;AAC5B,cAAI,QAAuB;AAE3B,cAAI,OAAO,OAAO;AAChB,oBAAQ,OAAO;AAAA,UACjB,WAAW,OAAO,UAAU;AAC1B,kBAAM,SAAS,OAAO,SAAA;AACtB,gBAAI,OAAO,WAAW,YAAY,WAAW,MAAM;AACjD,sBAAQ;AAAA,YACV;AAAA,UACF;AAEA,cAAI,CAAC,OAAO;AACV,mBAAO;AAAA,UACT;AAEA,gBAAM,eACJ,OAAO,UAAU,QAAQ,iBAAiB,WACtC,UAAU,QAAQ,eAClB,CAAA;AAEN,iBAAO;AAAA,YACL,GAAG;AAAA,YACH,SAAS;AAAA,cACP,GAAG,UAAU;AAAA,cACb,cAAc;AAAA,gBACZ,GAAG;AAAA,gBACH,SAAS;AAAA,kBACP,GAAI,aAAyC;AAAA,kBAC7C,eAAe,UAAU,KAAK;AAAA,gBAAA;AAAA,cAChC;AAAA,YACF;AAAA,UACF;AAAA,QAEJ,CAAC;AAAA,QACD;AAAA,MAAA;AAAA,IAEJ;AAAA,EACF;AACF;AAKO,SAAS,kBAAkB,SAA4B,IAAY;AACxE,QAAM,MAAM,OAAO,OAAO,eAAe;AACzC,QAAM,cAAc,EAAE,GAAG,eAAe,OAAO,GAAG,OAAO,MAAA;AAEzD,QAAM,YAAwB,CAAA;AAG9B,MAAI,OAAO,SAAS,OAAO,UAAU;AACnC,cAAU,KAAK,mBAAmB,MAAM,CAAC;AAAA,EAC3C;AAGA,MAAI,YAAY,SAAS;AACvB,cAAU,KAAK,aAAa;AAAA,EAC9B;AAGA,MAAI,OAAO,SAAS;AAClB,cAAU,KAAK,oBAAoB,OAAO,OAAO,CAAC;AAAA,EACpD;AAGA,YAAU,KAAK,aAAa;AAE5B,SAAO,IAAI,OAAO;AAAA,IAChB;AAAA,IACA;AAAA,IACA,eAAe;AAAA,EAAA,CAChB;AACH;"}