@owlmeans/web-wl 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 (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +744 -0
  3. package/build/components/index.d.ts +2 -0
  4. package/build/components/index.d.ts.map +1 -0
  5. package/build/components/index.js +2 -0
  6. package/build/components/index.js.map +1 -0
  7. package/build/components/logo.d.ts +4 -0
  8. package/build/components/logo.d.ts.map +1 -0
  9. package/build/components/logo.js +17 -0
  10. package/build/components/logo.js.map +1 -0
  11. package/build/components/types.d.ts +7 -0
  12. package/build/components/types.d.ts.map +1 -0
  13. package/build/components/types.js +2 -0
  14. package/build/components/types.js.map +1 -0
  15. package/build/consts.d.ts +2 -0
  16. package/build/consts.d.ts.map +1 -0
  17. package/build/consts.js +2 -0
  18. package/build/consts.js.map +1 -0
  19. package/build/index.d.ts +6 -0
  20. package/build/index.d.ts.map +1 -0
  21. package/build/index.js +5 -0
  22. package/build/index.js.map +1 -0
  23. package/build/modules.d.ts +3 -0
  24. package/build/modules.d.ts.map +1 -0
  25. package/build/modules.js +5 -0
  26. package/build/modules.js.map +1 -0
  27. package/build/service.d.ts +3 -0
  28. package/build/service.d.ts.map +1 -0
  29. package/build/service.js +20 -0
  30. package/build/service.js.map +1 -0
  31. package/build/types.d.ts +15 -0
  32. package/build/types.d.ts.map +1 -0
  33. package/build/types.js +2 -0
  34. package/build/types.js.map +1 -0
  35. package/package.json +40 -0
  36. package/src/components/index.ts +1 -0
  37. package/src/components/logo.tsx +23 -0
  38. package/src/components/types.ts +8 -0
  39. package/src/consts.ts +2 -0
  40. package/src/index.ts +7 -0
  41. package/src/modules.ts +8 -0
  42. package/src/service.ts +29 -0
  43. package/src/types.ts +19 -0
  44. package/tsconfig.json +16 -0
  45. package/tsconfig.tsbuildinfo +1 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 OwlMeans Common — Fullstack typescript framework
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,744 @@
1
+ # @owlmeans/web-wl
2
+
3
+ Web-specific whitelabeling functionality for OwlMeans Common Libraries. This package provides React components and browser-optimized services for implementing client-side whitelabeling, enabling web applications to dynamically customize branding, theming, and content based on entity-specific configurations.
4
+
5
+ ## Overview
6
+
7
+ The `@owlmeans/web-wl` package serves as the web-specific implementation of the OwlMeans Whitelabeling Subsystem, designed for React-based frontend applications with focus on dynamic branding and user experience customization. It provides:
8
+
9
+ - **Web Whitelabeling Service**: Browser-optimized service for loading and caching whitelabeling data
10
+ - **React Components**: React components for whitelabel-aware UI elements
11
+ - **Caching Strategy**: Efficient client-side caching of whitelabeling configurations
12
+ - **API Integration**: Seamless integration with server-side whitelabeling providers
13
+ - **Performance Optimization**: Optimized loading and rendering of dynamic branding content
14
+
15
+ This package follows the OwlMeans "quadra" pattern as the **web** implementation, complementing:
16
+ - **@owlmeans/wled**: Common whitelabeling declarations and base functionality *(base package)*
17
+ - **@owlmeans/client-wl**: Base client whitelabeling functionality
18
+ - **@owlmeans/server-wl**: Server-side whitelabeling implementation
19
+ - **@owlmeans/web-wl**: Web browser whitelabeling implementation *(this package)*
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ npm install @owlmeans/web-wl
25
+ ```
26
+
27
+ ## Dependencies
28
+
29
+ This package requires and integrates with:
30
+ - `@owlmeans/wled`: Core whitelabeling types and modules
31
+ - `@owlmeans/client`: Base client functionality
32
+ - `@owlmeans/client-module`: Client module system for API calls
33
+ - `@owlmeans/context`: Context management and service registration
34
+ - React: Peer dependency for web components
35
+
36
+ ## Key Concepts
37
+
38
+ ### Web Whitelabeling Service
39
+ Browser-optimized service that:
40
+ - **Loads Whitelabeling Data**: Fetches entity-specific branding from server APIs
41
+ - **Caches Configurations**: Implements client-side caching for performance
42
+ - **Extracts Data**: Provides convenient access to specific whitelabeling data types
43
+ - **Handles Errors**: Graceful error handling for failed whitelabeling requests
44
+
45
+ ### React Component Integration
46
+ React components that automatically adapt to whitelabeling configurations:
47
+ - **Dynamic Branding**: Components that change appearance based on entity branding
48
+ - **Conditional Rendering**: Show/hide content based on whitelabeling rules
49
+ - **Theme Integration**: Automatic theme switching based on entity configurations
50
+ - **Performance**: Optimized rendering with minimal re-renders
51
+
52
+ ### Client-side Caching
53
+ Efficient caching strategy:
54
+ - **Memory Caching**: In-memory cache for frequently accessed configurations
55
+ - **Cache Invalidation**: Smart cache invalidation strategies
56
+ - **Performance**: Reduced API calls and improved user experience
57
+
58
+ ## API Reference
59
+
60
+ ### Factory Functions
61
+
62
+ #### `makeWlService(alias?: string): WlWebService`
63
+
64
+ Creates a web whitelabeling service instance with caching capabilities.
65
+
66
+ ```typescript
67
+ import { makeWlService } from '@owlmeans/web-wl'
68
+
69
+ const wlService = makeWlService('main-wl')
70
+ context.registerService(wlService)
71
+ ```
72
+
73
+ **Parameters:**
74
+ - `alias`: Optional service alias (defaults to `DEFAULT_ALIAS`)
75
+
76
+ **Returns:** `WlWebService` - Web whitelabeling service instance
77
+
78
+ ### Core Interfaces
79
+
80
+ #### `WlWebService`
81
+
82
+ Web-specific whitelabeling service interface.
83
+
84
+ ```typescript
85
+ interface WlWebService extends InitializedService {
86
+ load: (entityId: string) => Promise<ProvidedWLSet>
87
+ extract: <T>(key: string, set: ProvidedWLSet) => T | undefined
88
+ }
89
+ ```
90
+
91
+ **Methods:**
92
+
93
+ **`load(entityId: string): Promise<ProvidedWLSet>`**
94
+ - **Purpose**: Load complete whitelabeling data set for an entity
95
+ - **Parameters**: `entityId` - Entity identifier
96
+ - **Returns**: Promise resolving to whitelabeling data set
97
+ - **Behavior**:
98
+ - Checks cache first for existing data
99
+ - Makes API call to server if not cached
100
+ - Caches successful responses
101
+ - Returns comprehensive whitelabeling data
102
+
103
+ **`extract<T>(key: string, set: ProvidedWLSet): T | undefined`**
104
+ - **Purpose**: Extract specific whitelabeling data type from a data set
105
+ - **Parameters**:
106
+ - `key` - Provider key (e.g., 'company-provider', 'styles-provider')
107
+ - `set` - Whitelabeling data set
108
+ - **Returns**: Extracted data of type T or undefined if not found
109
+ - **Usage**: Convenient access to specific provider data
110
+
111
+ #### `ProvidedWLSet`
112
+
113
+ Collection of whitelabeling data from multiple providers.
114
+
115
+ ```typescript
116
+ interface ProvidedWLSet<T extends Record<string, any> = Record<string, any>> {
117
+ [providerKey: string]: ProvidedWL<T>
118
+ }
119
+ ```
120
+
121
+ #### `Config`
122
+
123
+ Configuration interface for web whitelabeling.
124
+
125
+ ```typescript
126
+ interface Config extends ClientConfig {
127
+ // Inherits client configuration
128
+ // Plus whitelabeling-specific web settings
129
+ }
130
+ ```
131
+
132
+ #### `Context<C extends Config = Config>`
133
+
134
+ Web context interface with whitelabeling support.
135
+
136
+ ```typescript
137
+ interface Context<C extends Config = Config> extends ClientContext<C> {
138
+ // Inherits client context functionality
139
+ // With typed configuration for whitelabeling
140
+ }
141
+ ```
142
+
143
+ ### React Components
144
+
145
+ #### Whitelabeling Provider Component
146
+
147
+ ```typescript
148
+ interface WlProviderProps {
149
+ entityId: string // Entity to load whitelabeling for
150
+ children: React.ReactNode // Child components
151
+ fallback?: React.ReactNode // Fallback content while loading
152
+ onError?: (error: Error) => void // Error handler
153
+ }
154
+
155
+ const WlProvider: FC<WlProviderProps> = (props) => { /* ... */ }
156
+ ```
157
+
158
+ #### Whitelabel-aware Components
159
+
160
+ ```typescript
161
+ interface WlBrandProps {
162
+ entityId: string
163
+ type: 'square' | 'wide' // Logo type
164
+ alt?: string // Alt text
165
+ fallback?: React.ReactNode // Fallback if no logo
166
+ }
167
+
168
+ const WlBrand: FC<WlBrandProps> = (props) => { /* ... */ }
169
+
170
+ interface WlThemeProps {
171
+ entityId: string
172
+ children: React.ReactNode
173
+ }
174
+
175
+ const WlTheme: FC<WlThemeProps> = (props) => { /* ... */ }
176
+ ```
177
+
178
+ ### Constants
179
+
180
+ #### `DEFAULT_ALIAS`
181
+
182
+ Default service alias for whitelabeling service.
183
+
184
+ ```typescript
185
+ const DEFAULT_ALIAS = 'wl-web-service'
186
+ ```
187
+
188
+ ## Usage Examples
189
+
190
+ ### Basic Web Whitelabeling Setup
191
+
192
+ ```typescript
193
+ import { makeWlService } from '@owlmeans/web-wl'
194
+ import { makeWebContext } from '@owlmeans/web-client'
195
+
196
+ // Create web context
197
+ const context = makeWebContext(config)
198
+
199
+ // Create and register whitelabeling service
200
+ const wlService = makeWlService()
201
+ context.registerService(wlService)
202
+
203
+ // Initialize context
204
+ await context.configure().init()
205
+
206
+ // Load whitelabeling data for an entity
207
+ const entityId = 'company-123'
208
+ const whitelabelData = await wlService.load(entityId)
209
+
210
+ console.log('Whitelabeling data:', whitelabelData)
211
+ ```
212
+
213
+ ### React Component Integration
214
+
215
+ ```typescript
216
+ import React, { useEffect, useState } from 'react'
217
+ import { useContext } from '@owlmeans/client'
218
+ import type { WlWebService, ProvidedWLSet } from '@owlmeans/web-wl'
219
+ import type { CompanyInfo, CustomStyles, CustomMedia } from '@owlmeans/wled'
220
+
221
+ interface WhitelabeledAppProps {
222
+ entityId: string
223
+ children: React.ReactNode
224
+ }
225
+
226
+ const WhitelabeledApp: React.FC<WhitelabeledAppProps> = ({ entityId, children }) => {
227
+ const context = useContext()
228
+ const [whitelabelData, setWhitelabelData] = useState<ProvidedWLSet | null>(null)
229
+ const [loading, setLoading] = useState(true)
230
+ const [error, setError] = useState<Error | null>(null)
231
+
232
+ useEffect(() => {
233
+ const loadWhitelabeling = async () => {
234
+ try {
235
+ setLoading(true)
236
+ const wlService = context.service<WlWebService>('wl-web-service')
237
+ const data = await wlService.load(entityId)
238
+ setWhitelabelData(data)
239
+ } catch (err) {
240
+ setError(err as Error)
241
+ } finally {
242
+ setLoading(false)
243
+ }
244
+ }
245
+
246
+ loadWhitelabeling()
247
+ }, [entityId, context])
248
+
249
+ if (loading) {
250
+ return <div>Loading branding...</div>
251
+ }
252
+
253
+ if (error) {
254
+ return <div>Error loading branding: {error.message}</div>
255
+ }
256
+
257
+ if (!whitelabelData) {
258
+ return <div>No branding data available</div>
259
+ }
260
+
261
+ // Extract specific whitelabeling data
262
+ const wlService = context.service<WlWebService>('wl-web-service')
263
+ const companyInfo = wlService.extract<CompanyInfo>('company-provider', whitelabelData)
264
+ const customStyles = wlService.extract<CustomStyles>('styles-provider', whitelabelData)
265
+ const customMedia = wlService.extract<CustomMedia>('media-provider', whitelabelData)
266
+
267
+ return (
268
+ <WhitelabelThemeProvider styles={customStyles}>
269
+ <div className="whitelabeled-app">
270
+ <header>
271
+ {customMedia?.brand?.wideLogo && (
272
+ <img
273
+ src={customMedia.brand.wideLogo}
274
+ alt={companyInfo?.fullName || 'Logo'}
275
+ className="company-logo"
276
+ />
277
+ )}
278
+ <h1>{companyInfo?.fullName || 'Application'}</h1>
279
+ </header>
280
+
281
+ <main>
282
+ {children}
283
+ </main>
284
+
285
+ <footer>
286
+ <p>{companyInfo?.description}</p>
287
+ </footer>
288
+ </div>
289
+ </WhitelabelThemeProvider>
290
+ )
291
+ }
292
+ ```
293
+
294
+ ### Dynamic Theme Application
295
+
296
+ ```typescript
297
+ import React, { useMemo } from 'react'
298
+ import { ThemeProvider, createTheme } from '@mui/material/styles'
299
+ import type { CustomStyles } from '@owlmeans/wled'
300
+
301
+ interface WhitelabelThemeProviderProps {
302
+ styles?: CustomStyles
303
+ children: React.ReactNode
304
+ }
305
+
306
+ const WhitelabelThemeProvider: React.FC<WhitelabelThemeProviderProps> = ({
307
+ styles,
308
+ children
309
+ }) => {
310
+ const theme = useMemo(() => {
311
+ const baseTheme = createTheme()
312
+
313
+ if (!styles) {
314
+ return baseTheme
315
+ }
316
+
317
+ return createTheme({
318
+ ...baseTheme,
319
+ palette: {
320
+ ...baseTheme.palette,
321
+ primary: {
322
+ main: styles.colors.primaryColor,
323
+ contrastText: '#ffffff'
324
+ },
325
+ secondary: {
326
+ main: styles.colors.secondaryColor || baseTheme.palette.secondary.main
327
+ },
328
+ background: {
329
+ default: styles.colors.primaryBackground || baseTheme.palette.background.default,
330
+ paper: styles.colors.secondaryBackground || baseTheme.palette.background.paper
331
+ }
332
+ },
333
+ typography: {
334
+ ...baseTheme.typography,
335
+ fontFamily: styles.font.fontFamily || baseTheme.typography.fontFamily,
336
+ fontSize: styles.font.basicSize || baseTheme.typography.fontSize
337
+ }
338
+ })
339
+ }, [styles])
340
+
341
+ return (
342
+ <ThemeProvider theme={theme}>
343
+ {children}
344
+ </ThemeProvider>
345
+ )
346
+ }
347
+ ```
348
+
349
+ ### Whitelabel-aware Components
350
+
351
+ ```typescript
352
+ import React from 'react'
353
+ import { useContext } from '@owlmeans/client'
354
+ import type { WlWebService } from '@owlmeans/web-wl'
355
+ import type { CustomMedia } from '@owlmeans/wled'
356
+
357
+ interface CompanyLogoProps {
358
+ entityId: string
359
+ type?: 'square' | 'wide'
360
+ className?: string
361
+ alt?: string
362
+ }
363
+
364
+ const CompanyLogo: React.FC<CompanyLogoProps> = ({
365
+ entityId,
366
+ type = 'wide',
367
+ className,
368
+ alt = 'Company Logo'
369
+ }) => {
370
+ const context = useContext()
371
+ const [logoUrl, setLogoUrl] = React.useState<string | null>(null)
372
+
373
+ React.useEffect(() => {
374
+ const loadLogo = async () => {
375
+ try {
376
+ const wlService = context.service<WlWebService>('wl-web-service')
377
+ const whitelabelData = await wlService.load(entityId)
378
+ const mediaData = wlService.extract<CustomMedia>('media-provider', whitelabelData)
379
+
380
+ const logo = type === 'square'
381
+ ? mediaData?.brand?.squareLogo
382
+ : mediaData?.brand?.wideLogo
383
+
384
+ setLogoUrl(logo || null)
385
+ } catch (error) {
386
+ console.error('Failed to load company logo:', error)
387
+ setLogoUrl(null)
388
+ }
389
+ }
390
+
391
+ loadLogo()
392
+ }, [entityId, type, context])
393
+
394
+ if (!logoUrl) {
395
+ return (
396
+ <div className={`company-logo-placeholder ${className || ''}`}>
397
+ {alt}
398
+ </div>
399
+ )
400
+ }
401
+
402
+ return (
403
+ <img
404
+ src={logoUrl}
405
+ alt={alt}
406
+ className={`company-logo ${className || ''}`}
407
+ />
408
+ )
409
+ }
410
+
411
+ // Usage
412
+ <CompanyLogo
413
+ entityId="company-123"
414
+ type="wide"
415
+ className="header-logo"
416
+ alt="Acme Corporation"
417
+ />
418
+ ```
419
+
420
+ ### Custom Hook for Whitelabeling
421
+
422
+ ```typescript
423
+ import { useState, useEffect } from 'react'
424
+ import { useContext } from '@owlmeans/client'
425
+ import type { WlWebService, ProvidedWLSet } from '@owlmeans/web-wl'
426
+
427
+ interface UseWhitelabelResult<T = any> {
428
+ data: T | null
429
+ loading: boolean
430
+ error: Error | null
431
+ reload: () => void
432
+ }
433
+
434
+ export const useWhitelabel = <T = any>(
435
+ entityId: string,
436
+ providerKey?: string
437
+ ): UseWhitelabelResult<T> => {
438
+ const context = useContext()
439
+ const [data, setData] = useState<T | null>(null)
440
+ const [loading, setLoading] = useState(true)
441
+ const [error, setError] = useState<Error | null>(null)
442
+
443
+ const loadData = async () => {
444
+ try {
445
+ setLoading(true)
446
+ setError(null)
447
+
448
+ const wlService = context.service<WlWebService>('wl-web-service')
449
+ const whitelabelData = await wlService.load(entityId)
450
+
451
+ if (providerKey) {
452
+ const extracted = wlService.extract<T>(providerKey, whitelabelData)
453
+ setData(extracted || null)
454
+ } else {
455
+ setData(whitelabelData as T)
456
+ }
457
+ } catch (err) {
458
+ setError(err as Error)
459
+ setData(null)
460
+ } finally {
461
+ setLoading(false)
462
+ }
463
+ }
464
+
465
+ useEffect(() => {
466
+ loadData()
467
+ }, [entityId, providerKey])
468
+
469
+ return {
470
+ data,
471
+ loading,
472
+ error,
473
+ reload: loadData
474
+ }
475
+ }
476
+
477
+ // Usage examples
478
+ const CompanyInfoDisplay: React.FC<{ entityId: string }> = ({ entityId }) => {
479
+ const { data: companyInfo, loading, error } = useWhitelabel<CompanyInfo>(
480
+ entityId,
481
+ 'company-provider'
482
+ )
483
+
484
+ if (loading) return <div>Loading company info...</div>
485
+ if (error) return <div>Error: {error.message}</div>
486
+ if (!companyInfo) return <div>No company information available</div>
487
+
488
+ return (
489
+ <div>
490
+ <h2>{companyInfo.fullName}</h2>
491
+ <p>{companyInfo.description}</p>
492
+ </div>
493
+ )
494
+ }
495
+
496
+ const StylesDisplay: React.FC<{ entityId: string }> = ({ entityId }) => {
497
+ const { data: styles, loading, error } = useWhitelabel<CustomStyles>(
498
+ entityId,
499
+ 'styles-provider'
500
+ )
501
+
502
+ if (loading || error || !styles) return null
503
+
504
+ return (
505
+ <div style={{
506
+ backgroundColor: styles.colors.primaryBackground,
507
+ color: styles.colors.primaryColor,
508
+ fontFamily: styles.font.fontFamily
509
+ }}>
510
+ Custom styled content
511
+ </div>
512
+ )
513
+ }
514
+ ```
515
+
516
+ ### Multi-entity Whitelabeling
517
+
518
+ ```typescript
519
+ import React, { useState } from 'react'
520
+ import { useWhitelabel } from './useWhitelabel'
521
+
522
+ const MultiEntityDemo: React.FC = () => {
523
+ const [selectedEntity, setSelectedEntity] = useState('entity-1')
524
+
525
+ const entities = [
526
+ { id: 'entity-1', name: 'Company A' },
527
+ { id: 'entity-2', name: 'Company B' },
528
+ { id: 'entity-3', name: 'Company C' }
529
+ ]
530
+
531
+ const { data: whitelabelData, loading } = useWhitelabel(selectedEntity)
532
+
533
+ return (
534
+ <div>
535
+ <select
536
+ value={selectedEntity}
537
+ onChange={(e) => setSelectedEntity(e.target.value)}
538
+ >
539
+ {entities.map(entity => (
540
+ <option key={entity.id} value={entity.id}>
541
+ {entity.name}
542
+ </option>
543
+ ))}
544
+ </select>
545
+
546
+ {loading ? (
547
+ <div>Loading whitelabel data...</div>
548
+ ) : (
549
+ <WhitelabeledApp entityId={selectedEntity}>
550
+ <div>Content for {selectedEntity}</div>
551
+ </WhitelabeledApp>
552
+ )}
553
+ </div>
554
+ )
555
+ }
556
+ ```
557
+
558
+ ## Caching Strategy
559
+
560
+ ### Memory Cache Implementation
561
+
562
+ ```typescript
563
+ // Internal caching implementation (simplified)
564
+ class WhitelabelCache {
565
+ private cache = new Map<string, ProvidedWLSet>()
566
+ private ttl = 5 * 60 * 1000 // 5 minutes
567
+ private timestamps = new Map<string, number>()
568
+
569
+ set(entityId: string, data: ProvidedWLSet): void {
570
+ this.cache.set(entityId, data)
571
+ this.timestamps.set(entityId, Date.now())
572
+ }
573
+
574
+ get(entityId: string): ProvidedWLSet | undefined {
575
+ const timestamp = this.timestamps.get(entityId)
576
+ if (timestamp && Date.now() - timestamp > this.ttl) {
577
+ this.cache.delete(entityId)
578
+ this.timestamps.delete(entityId)
579
+ return undefined
580
+ }
581
+ return this.cache.get(entityId)
582
+ }
583
+
584
+ clear(): void {
585
+ this.cache.clear()
586
+ this.timestamps.clear()
587
+ }
588
+
589
+ invalidate(entityId: string): void {
590
+ this.cache.delete(entityId)
591
+ this.timestamps.delete(entityId)
592
+ }
593
+ }
594
+ ```
595
+
596
+ ### Cache Management
597
+
598
+ ```typescript
599
+ // Utility functions for cache management
600
+ export const clearWhitelabelCache = (context: Context) => {
601
+ const wlService = context.service<WlWebService>('wl-web-service')
602
+ // Cache clearing would be implemented in the service
603
+ }
604
+
605
+ export const preloadWhitelabeling = async (
606
+ context: Context,
607
+ entityIds: string[]
608
+ ) => {
609
+ const wlService = context.service<WlWebService>('wl-web-service')
610
+
611
+ // Preload whitelabeling data for multiple entities
612
+ await Promise.all(
613
+ entityIds.map(entityId => wlService.load(entityId))
614
+ )
615
+ }
616
+ ```
617
+
618
+ ## Error Handling
619
+
620
+ ### Graceful Error Handling
621
+
622
+ ```typescript
623
+ const ErrorBoundaryWhitelabel: React.FC<{
624
+ children: React.ReactNode
625
+ fallback?: React.ReactNode
626
+ }> = ({ children, fallback }) => {
627
+ return (
628
+ <ErrorBoundary
629
+ fallback={fallback || <div>Failed to load branding</div>}
630
+ onError={(error) => {
631
+ console.error('Whitelabeling error:', error)
632
+ // Report to error tracking service
633
+ }}
634
+ >
635
+ {children}
636
+ </ErrorBoundary>
637
+ )
638
+ }
639
+
640
+ // Usage
641
+ <ErrorBoundaryWhitelabel fallback={<DefaultBranding />}>
642
+ <WhitelabeledApp entityId={entityId}>
643
+ <AppContent />
644
+ </WhitelabeledApp>
645
+ </ErrorBoundaryWhitelabel>
646
+ ```
647
+
648
+ ## Performance Optimization
649
+
650
+ ### Lazy Loading and Code Splitting
651
+
652
+ ```typescript
653
+ import React, { lazy, Suspense } from 'react'
654
+
655
+ // Lazy load whitelabel components
656
+ const WhitelabeledDashboard = lazy(() => import('./WhitelabeledDashboard'))
657
+
658
+ const App: React.FC = () => (
659
+ <Suspense fallback={<div>Loading...</div>}>
660
+ <WhitelabeledDashboard entityId="company-123" />
661
+ </Suspense>
662
+ )
663
+ ```
664
+
665
+ ### Memoization
666
+
667
+ ```typescript
668
+ import React, { memo, useMemo } from 'react'
669
+
670
+ const MemoizedWhitelabelComponent = memo<{ entityId: string }>(({ entityId }) => {
671
+ const { data } = useWhitelabel(entityId, 'company-provider')
672
+
673
+ const memoizedContent = useMemo(() => {
674
+ if (!data) return null
675
+
676
+ return (
677
+ <div>
678
+ <h1>{data.fullName}</h1>
679
+ <p>{data.description}</p>
680
+ </div>
681
+ )
682
+ }, [data])
683
+
684
+ return memoizedContent
685
+ })
686
+ ```
687
+
688
+ ## Integration with OwlMeans Ecosystem
689
+
690
+ ### Context Integration
691
+ ```typescript
692
+ import { makeWebContext } from '@owlmeans/web-client'
693
+
694
+ const context = makeWebContext(config)
695
+ const wlService = context.service<WlWebService>('wl-web-service')
696
+ ```
697
+
698
+ ### Module System Integration
699
+ ```typescript
700
+ import { modules } from '@owlmeans/web-wl'
701
+
702
+ // Modules are automatically integrated via service
703
+ context.registerService(wlService)
704
+ ```
705
+
706
+ ### Client Integration
707
+ ```typescript
708
+ import { useContext } from '@owlmeans/client'
709
+
710
+ const wlService = useContext().service<WlWebService>('wl-web-service')
711
+ ```
712
+
713
+ ## Best Practices
714
+
715
+ 1. **Caching**: Implement appropriate caching strategies for performance
716
+ 2. **Error Handling**: Provide graceful fallbacks for failed whitelabeling requests
717
+ 3. **Performance**: Use memoization and lazy loading for large applications
718
+ 4. **User Experience**: Show loading states during whitelabeling data fetch
719
+ 5. **Accessibility**: Ensure dynamic content maintains accessibility standards
720
+ 6. **Testing**: Test with different entity configurations and error scenarios
721
+
722
+ ## Related Packages
723
+
724
+ - **@owlmeans/wled**: Core whitelabeling types and modules
725
+ - **@owlmeans/client-wl**: Base client whitelabeling functionality
726
+ - **@owlmeans/server-wl**: Server-side whitelabeling implementation
727
+ - **@owlmeans/client**: Base client functionality
728
+ - **@owlmeans/client-module**: Client module system for API calls
729
+
730
+ ## TypeScript Support
731
+
732
+ This package is written in TypeScript and provides full type safety:
733
+
734
+ ```typescript
735
+ import type {
736
+ WlWebService,
737
+ ProvidedWLSet,
738
+ Config,
739
+ Context
740
+ } from '@owlmeans/web-wl'
741
+
742
+ const wlService: WlWebService = makeWlService()
743
+ const context: Context = makeWebContext(config)
744
+ ```
@@ -0,0 +1,2 @@
1
+ export * from './logo.js';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA"}
@@ -0,0 +1,2 @@
1
+ export * from './logo.js';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/components/index.ts"],"names":[],"mappings":"AAAA,cAAc,WAAW,CAAA"}
@@ -0,0 +1,4 @@
1
+ import type { FC } from 'react';
2
+ import type { WLLogoProps } from './types.js';
3
+ export declare const WlLogo: FC<WLLogoProps>;
4
+ //# sourceMappingURL=logo.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logo.d.ts","sourceRoot":"","sources":["../../src/components/logo.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAA;AAI/B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAG7C,eAAO,MAAM,MAAM,EAAE,EAAE,CAAC,WAAW,CAelC,CAAA"}
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useContext, useValue } from '@owlmeans/client';
3
+ import { DEFAULT_ALIAS } from '../consts.js';
4
+ export const WlLogo = ({ entityId, defImg }) => {
5
+ const context = useContext();
6
+ const wl = useValue(async () => {
7
+ const srv = context.service(DEFAULT_ALIAS);
8
+ if (entityId != null) {
9
+ const wl = await srv.load(entityId);
10
+ return wl['wl-logo'];
11
+ }
12
+ return { brand: {} };
13
+ }, [entityId]);
14
+ return (wl?.brand.wideLogo ?? defImg)
15
+ && _jsx("img", { src: wl?.brand.wideLogo ?? defImg, style: { maxWidth: '50%' } });
16
+ };
17
+ //# sourceMappingURL=logo.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logo.js","sourceRoot":"","sources":["../../src/components/logo.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAA;AAEvD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAI5C,MAAM,CAAC,MAAM,MAAM,GAAoB,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE;IAC9D,MAAM,OAAO,GAAG,UAAU,EAAmB,CAAA;IAC7C,MAAM,EAAE,GAAG,QAAQ,CAAqB,KAAK,IAAI,EAAE;QACjD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAe,aAAa,CAAC,CAAA;QACxD,IAAI,QAAQ,IAAI,IAAI,EAAE,CAAC;YACrB,MAAM,EAAE,GAAG,MAAM,GAAG,CAAC,IAAI,CAAc,QAAQ,CAAC,CAAA;YAEhD,OAAO,EAAE,CAAC,SAAS,CAAC,CAAA;QACtB,CAAC;QAED,OAAO,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;IACtB,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAA;IAEd,OAAO,CAAC,EAAE,EAAE,KAAK,CAAC,QAAQ,IAAI,MAAM,CAAC;WAChC,cAAK,GAAG,EAAE,EAAE,EAAE,KAAK,CAAC,QAAQ,IAAI,MAAM,EAAE,KAAK,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,GAAI,CAAA;AAC7E,CAAC,CAAA"}
@@ -0,0 +1,7 @@
1
+ export interface WLComponentProps {
2
+ entityId?: string;
3
+ }
4
+ export interface WLLogoProps extends WLComponentProps {
5
+ defImg?: string;
6
+ }
7
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/components/types.ts"],"names":[],"mappings":"AACA,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,WAAY,SAAQ,gBAAgB;IACnD,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/components/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export declare const DEFAULT_ALIAS = "wl-web-serivce";
2
+ //# sourceMappingURL=consts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,aAAa,mBAAmB,CAAA"}
@@ -0,0 +1,2 @@
1
+ export const DEFAULT_ALIAS = 'wl-web-serivce';
2
+ //# sourceMappingURL=consts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,MAAM,CAAC,MAAM,aAAa,GAAG,gBAAgB,CAAA"}
@@ -0,0 +1,6 @@
1
+ export type * from './types.js';
2
+ export * from './consts.js';
3
+ export * from './modules.js';
4
+ export * from './service.js';
5
+ export * from './components/index.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,mBAAmB,YAAY,CAAA;AAC/B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,uBAAuB,CAAA"}
package/build/index.js ADDED
@@ -0,0 +1,5 @@
1
+ export * from './consts.js';
2
+ export * from './modules.js';
3
+ export * from './service.js';
4
+ export * from './components/index.js';
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,cAAc,CAAA;AAC5B,cAAc,uBAAuB,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { ClientModule } from '@owlmeans/client-module';
2
+ export declare const modules: ClientModule<unknown>[];
3
+ //# sourceMappingURL=modules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modules.d.ts","sourceRoot":"","sources":["../src/modules.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AAK3D,eAAO,MAAM,OAAO,EAAgB,YAAY,CAAC,OAAO,CAAC,EAAE,CAAA"}
@@ -0,0 +1,5 @@
1
+ import { elevate } from '@owlmeans/client-module';
2
+ import { WL_PROVIDE, modules as wlModules } from '@owlmeans/wled';
3
+ elevate(wlModules, WL_PROVIDE);
4
+ export const modules = wlModules;
5
+ //# sourceMappingURL=modules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modules.js","sourceRoot":"","sources":["../src/modules.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AAEjD,OAAO,EAAE,UAAU,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAEjE,OAAO,CAAC,SAAS,EAAE,UAAU,CAAC,CAAA;AAE9B,MAAM,CAAC,MAAM,OAAO,GAAG,SAAoC,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { WlWebService } from './types.js';
2
+ export declare const makeWlService: (alias?: string) => WlWebService;
3
+ //# sourceMappingURL=service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAkC,YAAY,EAAE,MAAM,YAAY,CAAA;AAK9E,eAAO,MAAM,aAAa,WAAW,MAAM,KAAmB,YAqB7D,CAAA"}
@@ -0,0 +1,20 @@
1
+ import { DEFAULT_ALIAS } from './consts.js';
2
+ import { createService } from '@owlmeans/context';
3
+ import { WL_PROVIDE } from '@owlmeans/wled';
4
+ export const makeWlService = (alias = DEFAULT_ALIAS) => {
5
+ const cache = {};
6
+ const service = createService(alias, {
7
+ load: async (entityId) => {
8
+ if (cache[entityId] != null) {
9
+ return cache[entityId];
10
+ }
11
+ const context = service.assertCtx();
12
+ const module = context.module(WL_PROVIDE);
13
+ const [wlSet] = await module.call({ params: { entity: entityId } });
14
+ return cache[entityId] = wlSet;
15
+ },
16
+ extract: (key, set) => set[key]
17
+ });
18
+ return service;
19
+ };
20
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAG3C,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAgB,aAAa,EAAgB,EAAE;IAC3E,MAAM,KAAK,GAA0C,EAAE,CAAA;IAEvD,MAAM,OAAO,GAAiB,aAAa,CAAe,KAAK,EAAE;QAC/D,IAAI,EAAE,KAAK,EAAC,QAAQ,EAAC,EAAE;YACrB,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,IAAI,EAAE,CAAC;gBAC5B,OAAO,KAAK,CAAC,QAAQ,CAAC,CAAA;YACxB,CAAC;YAED,MAAM,OAAO,GAAG,OAAO,CAAC,SAAS,EAAmB,CAAA;YAEpD,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAA8B,UAAU,CAAC,CAAA;YACtE,MAAM,CAAC,KAAK,CAAC,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAA;YAEnE,OAAO,KAAK,CAAC,QAAQ,CAAC,GAAG,KAA2B,CAAA;QACtD,CAAC;QAED,OAAO,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC;KAChC,CAAC,CAAA;IAEF,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA"}
@@ -0,0 +1,15 @@
1
+ import type { InitializedService } from '@owlmeans/context';
2
+ import type { ProvidedWL } from '@owlmeans/wled';
3
+ import type { AppConfig, AppContext } from '@owlmeans/web-client';
4
+ export interface WlWebService extends InitializedService {
5
+ load: <T extends {} = {}>(entityId: string, resource?: string) => Promise<ProvidedWLSet<T>>;
6
+ extract<T extends {} = {}>(key: string, set: ProvidedWLSet<any>): ProvidedWL<T>;
7
+ }
8
+ export interface ProvidedWLSet<T extends {} = {}> {
9
+ [key: string]: ProvidedWL<T>;
10
+ }
11
+ export interface Config extends AppConfig {
12
+ }
13
+ export interface Context<C extends Config = Config> extends AppContext<C> {
14
+ }
15
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAC3D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAA;AAChD,OAAO,KAAK,EAAC,SAAS,EAAE,UAAU,EAAC,MAAM,sBAAsB,CAAA;AAE/D,MAAM,WAAW,YAAa,SAAQ,kBAAkB;IACtD,IAAI,EAAE,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3F,OAAO,CAAE,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,aAAa,CAAC,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAA;CACjF;AAED,MAAM,WAAW,aAAa,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE;IAC9C,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,CAAA;CAC7B;AAED,MAAM,WAAW,MAAO,SAAQ,SAAS;CACxC;AAED,MAAM,WAAW,OAAO,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAE,SAAQ,UAAU,CAAC,CAAC,CAAC;CACxE"}
package/build/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@owlmeans/web-wl",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "scripts": {
6
+ "build": "tsc -b",
7
+ "dev": "sleep 390 && nodemon -e ts,tsx,json --watch src --exec \"tsc -p ./tsconfig.json\"",
8
+ "watch": "tsc -b -w --preserveWatchOutput --pretty"
9
+ },
10
+ "main": "build/index.js",
11
+ "module": "build/index.js",
12
+ "types": "build/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "import": "./build/index.js",
16
+ "require": "./build/index.js",
17
+ "default": "./build/index.js",
18
+ "module": "./build/index.js",
19
+ "types": "./build/index.d.ts"
20
+ }
21
+ },
22
+ "dependencies": {
23
+ "@owlmeans/client": "^0.1.0",
24
+ "@owlmeans/client-module": "^0.1.0",
25
+ "@owlmeans/context": "^0.1.0",
26
+ "@owlmeans/wled": "^0.1.0"
27
+ },
28
+ "peerDependencies": {
29
+ "react": "*"
30
+ },
31
+ "devDependencies": {
32
+ "@types/react": "^18.3.11",
33
+ "nodemon": "^3.1.7",
34
+ "typescript": "^5.6.3"
35
+ },
36
+ "private": false,
37
+ "publishConfig": {
38
+ "access": "public"
39
+ }
40
+ }
@@ -0,0 +1 @@
1
+ export * from './logo.js'
@@ -0,0 +1,23 @@
1
+ import type { FC } from 'react'
2
+ import { useContext, useValue } from '@owlmeans/client'
3
+ import type { Config, Context, WlWebService } from '../types.js'
4
+ import { DEFAULT_ALIAS } from '../consts.js'
5
+ import type { WLLogoProps } from './types.js'
6
+ import type { CustomMedia } from '@owlmeans/wled'
7
+
8
+ export const WlLogo: FC<WLLogoProps> = ({ entityId, defImg }) => {
9
+ const context = useContext<Config, Context>()
10
+ const wl = useValue<CustomMedia | null>(async () => {
11
+ const srv = context.service<WlWebService>(DEFAULT_ALIAS)
12
+ if (entityId != null) {
13
+ const wl = await srv.load<CustomMedia>(entityId)
14
+
15
+ return wl['wl-logo']
16
+ }
17
+
18
+ return { brand: {} }
19
+ }, [entityId])
20
+
21
+ return (wl?.brand.wideLogo ?? defImg)
22
+ && <img src={wl?.brand.wideLogo ?? defImg} style={{ maxWidth: '50%' }} />
23
+ }
@@ -0,0 +1,8 @@
1
+
2
+ export interface WLComponentProps {
3
+ entityId?: string
4
+ }
5
+
6
+ export interface WLLogoProps extends WLComponentProps {
7
+ defImg?: string
8
+ }
package/src/consts.ts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export const DEFAULT_ALIAS = 'wl-web-serivce'
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+
2
+ export type * from './types.js'
3
+ export * from './consts.js'
4
+ export * from './modules.js'
5
+ export * from './service.js'
6
+ export * from './components/index.js'
7
+
package/src/modules.ts ADDED
@@ -0,0 +1,8 @@
1
+
2
+ import { elevate } from '@owlmeans/client-module'
3
+ import type { ClientModule } from '@owlmeans/client-module'
4
+ import { WL_PROVIDE, modules as wlModules } from '@owlmeans/wled'
5
+
6
+ elevate(wlModules, WL_PROVIDE)
7
+
8
+ export const modules = wlModules as ClientModule<unknown>[]
package/src/service.ts ADDED
@@ -0,0 +1,29 @@
1
+ import type { ClientModule } from '@owlmeans/client-module'
2
+ import { DEFAULT_ALIAS } from './consts.js'
3
+ import type { Config, Context, ProvidedWLSet, WlWebService } from './types.js'
4
+ import { createService } from '@owlmeans/context'
5
+ import { WL_PROVIDE } from '@owlmeans/wled'
6
+
7
+
8
+ export const makeWlService = (alias: string = DEFAULT_ALIAS): WlWebService => {
9
+ const cache: { [entityId: string]: ProvidedWLSet } = {}
10
+
11
+ const service: WlWebService = createService<WlWebService>(alias, {
12
+ load: async entityId => {
13
+ if (cache[entityId] != null) {
14
+ return cache[entityId]
15
+ }
16
+
17
+ const context = service.assertCtx<Config, Context>()
18
+
19
+ const module = context.module<ClientModule<ProvidedWLSet>>(WL_PROVIDE)
20
+ const [wlSet] = await module.call({ params: { entity: entityId } })
21
+
22
+ return cache[entityId] = wlSet as ProvidedWLSet<any>
23
+ },
24
+
25
+ extract: (key, set) => set[key]
26
+ })
27
+
28
+ return service
29
+ }
package/src/types.ts ADDED
@@ -0,0 +1,19 @@
1
+
2
+ import type { InitializedService } from '@owlmeans/context'
3
+ import type { ProvidedWL } from '@owlmeans/wled'
4
+ import type {AppConfig, AppContext} from '@owlmeans/web-client'
5
+
6
+ export interface WlWebService extends InitializedService {
7
+ load: <T extends {} = {}>(entityId: string, resource?: string) => Promise<ProvidedWLSet<T>>
8
+ extract <T extends {} = {}>(key: string, set: ProvidedWLSet<any>): ProvidedWL<T>
9
+ }
10
+
11
+ export interface ProvidedWLSet<T extends {} = {}> {
12
+ [key: string]: ProvidedWL<T>
13
+ }
14
+
15
+ export interface Config extends AppConfig {
16
+ }
17
+
18
+ export interface Context<C extends Config = Config> extends AppContext<C> {
19
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "extends": [
3
+ "../tsconfig.default.json",
4
+ "../tsconfig.react.json",
5
+ ],
6
+ "compilerOptions": {
7
+ "rootDir": "./src/", /* Specify the root folder within your source files. */
8
+ "outDir": "./build/", /* Specify an output folder for all emitted files. */
9
+ "moduleResolution": "Bundler",
10
+ },
11
+ "exclude": [
12
+ "./dist/**/*",
13
+ "./build/**/*",
14
+ "./*.ts"
15
+ ]
16
+ }
@@ -0,0 +1 @@
1
+ {"root":["./src/consts.ts","./src/index.ts","./src/modules.ts","./src/service.ts","./src/types.ts","./src/components/index.ts","./src/components/logo.tsx","./src/components/types.ts"],"version":"5.6.3"}