@kuckit/sdk-react 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,700 @@
1
+ import { Fragment, createContext, createElement, useContext } from "react";
2
+
3
+ //#region src/define-module.ts
4
+ /**
5
+ * Helper function to define a client module with type safety
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * export const kuckitClientModule = defineKuckitClientModule({
10
+ * id: 'acme.billing',
11
+ * displayName: 'Billing',
12
+ * version: '1.0.0',
13
+ *
14
+ * register(ctx) {
15
+ * ctx.registerComponent('BillingDashboard', BillingDashboard)
16
+ * ctx.registerComponent('InvoiceList', InvoiceList)
17
+ * },
18
+ * })
19
+ * ```
20
+ */
21
+ function defineKuckitClientModule(mod) {
22
+ return mod;
23
+ }
24
+
25
+ //#endregion
26
+ //#region src/registry.ts
27
+ /**
28
+ * Registry for module-provided routes
29
+ *
30
+ * Collects route definitions during module loading and provides
31
+ * methods to build a TanStack Router route tree.
32
+ */
33
+ var RouteRegistry = class {
34
+ routes = /* @__PURE__ */ new Map();
35
+ frozen = false;
36
+ /**
37
+ * Register a route definition
38
+ * @throws Error if registry is frozen or route ID already exists
39
+ */
40
+ add(route) {
41
+ if (this.frozen) throw new Error(`RouteRegistry is frozen. Cannot add route "${route.id}" after finalization.`);
42
+ if (this.routes.has(route.id)) throw new Error(`Route with ID "${route.id}" is already registered.`);
43
+ this.routes.set(route.id, route);
44
+ }
45
+ /**
46
+ * Get all registered routes
47
+ */
48
+ getAll() {
49
+ return Array.from(this.routes.values());
50
+ }
51
+ /**
52
+ * Get a route by ID
53
+ */
54
+ get(id) {
55
+ return this.routes.get(id);
56
+ }
57
+ /**
58
+ * Check if a route is registered
59
+ */
60
+ has(id) {
61
+ return this.routes.has(id);
62
+ }
63
+ /**
64
+ * Get routes grouped by parent ID
65
+ */
66
+ getByParent(parentId = "__root__") {
67
+ return this.getAll().filter((r) => (r.parentRouteId ?? "__root__") === parentId);
68
+ }
69
+ /**
70
+ * Freeze the registry to prevent further modifications
71
+ */
72
+ freeze() {
73
+ this.frozen = true;
74
+ }
75
+ /**
76
+ * Check if registry is frozen
77
+ */
78
+ isFrozen() {
79
+ return this.frozen;
80
+ }
81
+ /**
82
+ * Get the number of registered routes
83
+ */
84
+ get size() {
85
+ return this.routes.size;
86
+ }
87
+ /**
88
+ * Clear all routes (only works if not frozen)
89
+ */
90
+ clear() {
91
+ if (this.frozen) throw new Error("RouteRegistry is frozen. Cannot clear routes.");
92
+ this.routes.clear();
93
+ }
94
+ };
95
+ /**
96
+ * Registry for module-provided navigation items
97
+ *
98
+ * Collects nav items during module loading and provides
99
+ * methods to build navigation menus.
100
+ */
101
+ var NavRegistry = class {
102
+ items = /* @__PURE__ */ new Map();
103
+ frozen = false;
104
+ /**
105
+ * Register a navigation item
106
+ * @throws Error if registry is frozen or item ID already exists
107
+ */
108
+ add(item) {
109
+ if (this.frozen) throw new Error(`NavRegistry is frozen. Cannot add item "${item.id}" after finalization.`);
110
+ if (this.items.has(item.id)) throw new Error(`Navigation item with ID "${item.id}" is already registered.`);
111
+ this.items.set(item.id, item);
112
+ }
113
+ /**
114
+ * Get all registered navigation items, sorted by order
115
+ */
116
+ getAll() {
117
+ return Array.from(this.items.values()).sort((a, b) => (a.order ?? 100) - (b.order ?? 100));
118
+ }
119
+ /**
120
+ * Get a navigation item by ID
121
+ */
122
+ get(id) {
123
+ return this.items.get(id);
124
+ }
125
+ /**
126
+ * Check if a nav item is registered
127
+ */
128
+ has(id) {
129
+ return this.items.has(id);
130
+ }
131
+ /**
132
+ * Get top-level navigation items (no parent)
133
+ */
134
+ getTopLevel() {
135
+ return this.getAll().filter((item) => !item.parentId);
136
+ }
137
+ /**
138
+ * Get nav items that should appear in main navigation
139
+ * (items with showInMainNav === true)
140
+ */
141
+ getMainNavItems() {
142
+ return this.getAll().filter((item) => !item.parentId && item.showInMainNav === true);
143
+ }
144
+ /**
145
+ * Get module nav items (not in main nav)
146
+ * (items without showInMainNav or showInMainNav === false)
147
+ */
148
+ getModuleNavItems() {
149
+ return this.getAll().filter((item) => !item.parentId && item.showInMainNav !== true);
150
+ }
151
+ /**
152
+ * Get child navigation items for a parent
153
+ */
154
+ getChildren(parentId) {
155
+ return this.getAll().filter((item) => item.parentId === parentId);
156
+ }
157
+ /**
158
+ * Build a hierarchical navigation tree
159
+ */
160
+ buildTree() {
161
+ return this.getTopLevel().map((item) => this.buildTreeItem(item));
162
+ }
163
+ buildTreeItem(item) {
164
+ const children = this.getChildren(item.id);
165
+ return {
166
+ ...item,
167
+ children: children.length > 0 ? children.map((c) => this.buildTreeItem(c)) : void 0
168
+ };
169
+ }
170
+ /**
171
+ * Freeze the registry to prevent further modifications
172
+ */
173
+ freeze() {
174
+ this.frozen = true;
175
+ }
176
+ /**
177
+ * Check if registry is frozen
178
+ */
179
+ isFrozen() {
180
+ return this.frozen;
181
+ }
182
+ /**
183
+ * Get the number of registered items
184
+ */
185
+ get size() {
186
+ return this.items.size;
187
+ }
188
+ /**
189
+ * Clear all items (only works if not frozen)
190
+ */
191
+ clear() {
192
+ if (this.frozen) throw new Error("NavRegistry is frozen. Cannot clear items.");
193
+ this.items.clear();
194
+ }
195
+ };
196
+ /**
197
+ * Registry for module-provided slot components
198
+ *
199
+ * Collects components registered to named slots during module loading
200
+ * and provides methods to retrieve them for rendering.
201
+ */
202
+ var SlotRegistry = class {
203
+ slots = /* @__PURE__ */ new Map();
204
+ frozen = false;
205
+ /**
206
+ * Register a component into a named slot
207
+ * @throws Error if registry is frozen
208
+ */
209
+ add(slotName, component, moduleId, options) {
210
+ if (this.frozen) throw new Error(`SlotRegistry is frozen. Cannot add to slot "${slotName}" after finalization.`);
211
+ const registration = {
212
+ slotName,
213
+ component,
214
+ moduleId,
215
+ order: options?.order ?? 100,
216
+ props: options?.props
217
+ };
218
+ const existing = this.slots.get(slotName);
219
+ if (existing) existing.push(registration);
220
+ else this.slots.set(slotName, [registration]);
221
+ }
222
+ /**
223
+ * Get all components registered to a slot, sorted by order
224
+ */
225
+ getSlot(name) {
226
+ return [...this.slots.get(name) ?? []].sort((a, b) => a.order - b.order);
227
+ }
228
+ /**
229
+ * Check if a slot has any components registered
230
+ */
231
+ hasSlot(name) {
232
+ const registrations = this.slots.get(name);
233
+ return registrations !== void 0 && registrations.length > 0;
234
+ }
235
+ /**
236
+ * Get all slot names that have components registered
237
+ */
238
+ getSlotNames() {
239
+ return Array.from(this.slots.keys());
240
+ }
241
+ /**
242
+ * Get total number of registered components across all slots
243
+ */
244
+ get size() {
245
+ let count = 0;
246
+ for (const registrations of this.slots.values()) count += registrations.length;
247
+ return count;
248
+ }
249
+ /**
250
+ * Freeze the registry to prevent further modifications
251
+ */
252
+ freeze() {
253
+ this.frozen = true;
254
+ }
255
+ /**
256
+ * Check if registry is frozen
257
+ */
258
+ isFrozen() {
259
+ return this.frozen;
260
+ }
261
+ /**
262
+ * Clear all slots (only works if not frozen)
263
+ */
264
+ clear() {
265
+ if (this.frozen) throw new Error("SlotRegistry is frozen. Cannot clear slots.");
266
+ this.slots.clear();
267
+ }
268
+ };
269
+ /**
270
+ * Context for navigation registry
271
+ */
272
+ const NavContext = createContext(null);
273
+ function KuckitNavProvider({ registry, children }) {
274
+ return createElement(NavContext.Provider, { value: registry }, children);
275
+ }
276
+ /**
277
+ * Hook to access navigation items
278
+ */
279
+ function useKuckitNav() {
280
+ const ctx = useContext(NavContext);
281
+ if (!ctx) throw new Error("useKuckitNav must be used within a KuckitNavProvider");
282
+ return ctx;
283
+ }
284
+ /**
285
+ * Hook to get sorted navigation items
286
+ */
287
+ function useNavItems() {
288
+ return useKuckitNav().getAll();
289
+ }
290
+ /**
291
+ * Hook to get hierarchical navigation tree
292
+ */
293
+ function useNavTree() {
294
+ return useKuckitNav().buildTree();
295
+ }
296
+ /**
297
+ * Create a component registry for storing named components
298
+ */
299
+ function createComponentRegistry() {
300
+ const components = /* @__PURE__ */ new Map();
301
+ return {
302
+ register: (name, component) => {
303
+ if (components.has(name)) console.warn(`Component "${name}" is already registered. Overwriting.`);
304
+ components.set(name, component);
305
+ },
306
+ get: (name) => components.get(name),
307
+ has: (name) => components.has(name),
308
+ getAll: () => new Map(components)
309
+ };
310
+ }
311
+ /**
312
+ * Validates that a capability string matches expected patterns
313
+ */
314
+ const isValidCapability = (cap) => {
315
+ if ([
316
+ "nav.item",
317
+ "settings.page",
318
+ "dashboard.widget",
319
+ "api.webhook",
320
+ "api.public",
321
+ "slot.provider"
322
+ ].includes(cap)) return true;
323
+ if (cap.startsWith("custom.") && cap.length > 7) return true;
324
+ return false;
325
+ };
326
+ /**
327
+ * Registry for loaded Kuckit client modules
328
+ *
329
+ * Tracks all loaded modules and their capabilities for querying.
330
+ */
331
+ var ClientModuleRegistry = class {
332
+ modules = /* @__PURE__ */ new Map();
333
+ frozen = false;
334
+ /**
335
+ * Register a loaded module
336
+ * @throws Error if registry is frozen or module ID already exists
337
+ */
338
+ register(module) {
339
+ if (this.frozen) throw new Error(`ClientModuleRegistry is frozen. Cannot register module "${module.id}" after finalization.`);
340
+ if (this.modules.has(module.id)) throw new Error(`Module with ID "${module.id}" is already registered.`);
341
+ const capabilities = module.capabilities ?? [];
342
+ for (const cap of capabilities) if (!isValidCapability(cap)) throw new Error(`Invalid capability "${cap}" in module "${module.id}". Capabilities must be built-in (nav.item, settings.page, etc.) or custom.* prefixed.`);
343
+ this.modules.set(module.id, {
344
+ id: module.id,
345
+ displayName: module.displayName,
346
+ description: module.description,
347
+ version: module.version,
348
+ capabilities
349
+ });
350
+ }
351
+ /**
352
+ * Get all registered modules
353
+ */
354
+ getAll() {
355
+ return Array.from(this.modules.values());
356
+ }
357
+ /**
358
+ * Get a module by ID
359
+ */
360
+ getById(id) {
361
+ return this.modules.get(id);
362
+ }
363
+ /**
364
+ * Check if a module is registered
365
+ */
366
+ has(id) {
367
+ return this.modules.has(id);
368
+ }
369
+ /**
370
+ * Get all modules that have a specific capability
371
+ */
372
+ getWithCapability(capability) {
373
+ return this.getAll().filter((mod) => mod.capabilities.includes(capability));
374
+ }
375
+ /**
376
+ * Check if a specific module has a capability
377
+ */
378
+ hasCapability(moduleId, capability) {
379
+ const module = this.modules.get(moduleId);
380
+ return module ? module.capabilities.includes(capability) : false;
381
+ }
382
+ /**
383
+ * Get all unique capabilities across all modules
384
+ */
385
+ getAllCapabilities() {
386
+ const caps = /* @__PURE__ */ new Set();
387
+ for (const mod of this.modules.values()) for (const cap of mod.capabilities) caps.add(cap);
388
+ return Array.from(caps);
389
+ }
390
+ /**
391
+ * Freeze the registry to prevent further modifications
392
+ */
393
+ freeze() {
394
+ this.frozen = true;
395
+ }
396
+ /**
397
+ * Check if registry is frozen
398
+ */
399
+ isFrozen() {
400
+ return this.frozen;
401
+ }
402
+ /**
403
+ * Get the number of registered modules
404
+ */
405
+ get size() {
406
+ return this.modules.size;
407
+ }
408
+ /**
409
+ * Clear all modules (only works if not frozen)
410
+ */
411
+ clear() {
412
+ if (this.frozen) throw new Error("ClientModuleRegistry is frozen. Cannot clear modules.");
413
+ this.modules.clear();
414
+ }
415
+ };
416
+ let globalClientRegistry = null;
417
+ /**
418
+ * Get the global client module registry
419
+ * Creates one if it doesn't exist
420
+ */
421
+ function getClientModuleRegistry() {
422
+ if (!globalClientRegistry) globalClientRegistry = new ClientModuleRegistry();
423
+ return globalClientRegistry;
424
+ }
425
+ /**
426
+ * Get all client modules that have a specific capability
427
+ * Convenience function that uses the global registry
428
+ */
429
+ function getClientModulesWithCapability(capability) {
430
+ return getClientModuleRegistry().getWithCapability(capability);
431
+ }
432
+ /**
433
+ * Reset the global client registry (mainly for testing)
434
+ */
435
+ function resetClientModuleRegistry() {
436
+ globalClientRegistry = null;
437
+ }
438
+ /**
439
+ * Factory function to create fresh registries
440
+ */
441
+ function createRegistries() {
442
+ return {
443
+ routeRegistry: new RouteRegistry(),
444
+ navRegistry: new NavRegistry(),
445
+ componentRegistry: createComponentRegistry(),
446
+ slotRegistry: new SlotRegistry(),
447
+ moduleRegistry: new ClientModuleRegistry()
448
+ };
449
+ }
450
+
451
+ //#endregion
452
+ //#region src/loader.ts
453
+ /**
454
+ * Validates that a module has the correct shape
455
+ */
456
+ const validateModule = (candidate, source) => {
457
+ if (!candidate || typeof candidate !== "object") throw new Error(`Invalid Kuckit client module from "${source}": expected an object with module definition`);
458
+ const mod = candidate;
459
+ if (!mod.id || typeof mod.id !== "string") throw new Error(`Invalid Kuckit client module from "${source}": missing or invalid 'id' property`);
460
+ if (mod.register !== void 0 && typeof mod.register !== "function") throw new Error(`Invalid Kuckit client module from "${source}": 'register' must be a function`);
461
+ if (mod.onUnload !== void 0 && typeof mod.onUnload !== "function") throw new Error(`Invalid Kuckit client module from "${source}": 'onUnload' must be a function`);
462
+ return mod;
463
+ };
464
+ /**
465
+ * Load and initialize Kuckit client modules
466
+ *
467
+ * This function orchestrates the client module loading lifecycle:
468
+ *
469
+ * 1. **Import Phase**: Dynamic import each module package (or use direct reference)
470
+ * 2. **Register Phase**: Run register() hooks with context (routes, nav, components, slots)
471
+ * 3. **Finalize Phase**: Freeze registries and call onComplete
472
+ *
473
+ * @example
474
+ * ```tsx
475
+ * const { modules, routeRegistry, navRegistry, slotRegistry, moduleRegistry } = await loadKuckitClientModules({
476
+ * orpc,
477
+ * queryClient,
478
+ * env: 'production',
479
+ * modules: [
480
+ * { package: '@acme/billing-module/client' },
481
+ * { module: myLocalModule },
482
+ * ],
483
+ * })
484
+ *
485
+ * // Use routeRegistry to build TanStack Router routes
486
+ * const moduleRoutes = routeRegistry.getAll()
487
+ *
488
+ * // Use navRegistry for sidebar navigation
489
+ * const navItems = navRegistry.getAll()
490
+ *
491
+ * // Use slotRegistry with KuckitSlot component
492
+ * <KuckitSlot name="dashboard.widgets" />
493
+ *
494
+ * // Query modules by capability
495
+ * const navModules = moduleRegistry.getWithCapability('nav.item')
496
+ * ```
497
+ */
498
+ const loadKuckitClientModules = async (opts) => {
499
+ const { orpc, queryClient, env, modules, onRegisterComponent, onRegisterRoute, onRegisterNavItem, onRegisterSlot, onComplete } = opts;
500
+ const loadedModules = [];
501
+ const routeRegistry = new RouteRegistry();
502
+ const navRegistry = new NavRegistry();
503
+ const componentRegistry = createComponentRegistry();
504
+ const slotRegistry = new SlotRegistry();
505
+ const moduleRegistry = getClientModuleRegistry();
506
+ for (const spec of modules) {
507
+ if (spec.disabled) continue;
508
+ let validated;
509
+ let source;
510
+ if (spec.module) {
511
+ validated = validateModule(spec.module, spec.module.id ?? "direct-module");
512
+ source = "direct";
513
+ } else if (spec.package) {
514
+ let imported;
515
+ try {
516
+ imported = await import(
517
+ /* @vite-ignore */
518
+ spec.package
519
+ );
520
+ } catch (error) {
521
+ const message = error instanceof Error ? error.message : String(error);
522
+ throw new Error(`Failed to import client module "${spec.package}": ${message}`);
523
+ }
524
+ validated = validateModule(imported.kuckitClientModule ?? imported.default, spec.package);
525
+ source = "package";
526
+ } else throw new Error("ClientModuleSpec must have either \"module\" or \"package\" property");
527
+ moduleRegistry.register(validated);
528
+ loadedModules.push({
529
+ ...validated,
530
+ _source: source
531
+ });
532
+ }
533
+ for (const mod of loadedModules) if (mod.register) {
534
+ const ctx = {
535
+ orpc,
536
+ queryClient,
537
+ env,
538
+ registerComponent: (name, component) => {
539
+ componentRegistry.register(name, component);
540
+ onRegisterComponent?.(name, component);
541
+ },
542
+ addRoute: (route) => {
543
+ routeRegistry.add(route);
544
+ onRegisterRoute?.(route);
545
+ },
546
+ addNavItem: (item) => {
547
+ navRegistry.add(item);
548
+ onRegisterNavItem?.(item);
549
+ },
550
+ registerSlot: (slotName, component, options) => {
551
+ slotRegistry.add(slotName, component, mod.id, options);
552
+ onRegisterSlot?.(slotName, component, mod.id, options);
553
+ }
554
+ };
555
+ try {
556
+ await mod.register(ctx);
557
+ } catch (error) {
558
+ const message = error instanceof Error ? error.message : String(error);
559
+ throw new Error(`Client module "${mod.id}" register() failed: ${message}`);
560
+ }
561
+ }
562
+ routeRegistry.freeze();
563
+ navRegistry.freeze();
564
+ slotRegistry.freeze();
565
+ const result = {
566
+ modules: loadedModules,
567
+ routeRegistry,
568
+ navRegistry,
569
+ componentRegistry,
570
+ slotRegistry,
571
+ moduleRegistry
572
+ };
573
+ if (onComplete) await onComplete(result);
574
+ return result;
575
+ };
576
+ /**
577
+ * Create an unload handler for loaded client modules
578
+ *
579
+ * Returns a function that calls onUnload() on all modules in reverse order.
580
+ *
581
+ * @example
582
+ * ```ts
583
+ * const unload = createClientModuleUnloadHandler(loadedModules)
584
+ *
585
+ * // When cleaning up (e.g., HMR, route unmount)
586
+ * await unload()
587
+ * ```
588
+ */
589
+ const createClientModuleUnloadHandler = (modules) => {
590
+ return async () => {
591
+ for (const mod of [...modules].reverse()) if (mod.onUnload) try {
592
+ await mod.onUnload();
593
+ } catch (error) {
594
+ console.error(`Client module "${mod.id}" onUnload() failed:`, error);
595
+ }
596
+ };
597
+ };
598
+
599
+ //#endregion
600
+ //#region src/slots.ts
601
+ /**
602
+ * Context for slot registry
603
+ */
604
+ const SlotContext = createContext(null);
605
+ function KuckitSlotProvider({ registry, children }) {
606
+ return createElement(SlotContext.Provider, { value: registry }, children);
607
+ }
608
+ /**
609
+ * Hook to access the slot registry
610
+ */
611
+ function useSlotRegistry() {
612
+ const ctx = useContext(SlotContext);
613
+ if (!ctx) throw new Error("useSlotRegistry must be used within a KuckitSlotProvider");
614
+ return ctx;
615
+ }
616
+ /**
617
+ * Hook to get components registered to a specific slot
618
+ */
619
+ function useSlot(name) {
620
+ return useSlotRegistry().getSlot(name);
621
+ }
622
+ /**
623
+ * Hook to check if a slot has any components
624
+ */
625
+ function useHasSlot(name) {
626
+ return useSlotRegistry().hasSlot(name);
627
+ }
628
+ /**
629
+ * Component that renders all components registered to a named slot
630
+ *
631
+ * @example
632
+ * ```tsx
633
+ * // In host app layout
634
+ * <KuckitSlot name="dashboard.widgets" />
635
+ *
636
+ * // With fallback
637
+ * <KuckitSlot name="settings.tabs" fallback={<EmptyState />} />
638
+ *
639
+ * // With wrapper
640
+ * <KuckitSlot name="nav.items" wrapperTag="nav" wrapperClassName="flex gap-2" />
641
+ * ```
642
+ */
643
+ function KuckitSlot({ name, fallback, wrapperClassName, wrapperTag, slotProps }) {
644
+ const ctx = useContext(SlotContext);
645
+ if (!ctx) return fallback ?? null;
646
+ const registrations = ctx.getSlot(name);
647
+ if (registrations.length === 0) return fallback ?? null;
648
+ const rendered = registrations.map((reg, index) => {
649
+ const Component = reg.component;
650
+ const combinedProps = {
651
+ ...reg.props,
652
+ ...slotProps
653
+ };
654
+ return createElement(Component, {
655
+ key: `${reg.moduleId}-${index}`,
656
+ ...combinedProps
657
+ });
658
+ });
659
+ if (wrapperTag) return createElement(wrapperTag, { className: wrapperClassName }, rendered);
660
+ if (wrapperClassName) return createElement("div", { className: wrapperClassName }, rendered);
661
+ return createElement(Fragment, null, rendered);
662
+ }
663
+
664
+ //#endregion
665
+ //#region src/rpc-context.ts
666
+ /**
667
+ * Context for RPC client access
668
+ */
669
+ const RpcContext = createContext(null);
670
+ function KuckitRpcProvider({ client, children }) {
671
+ return createElement(RpcContext.Provider, { value: client }, children);
672
+ }
673
+ /**
674
+ * Hook to access the RPC client
675
+ *
676
+ * @example
677
+ * ```tsx
678
+ * import { useRpc } from '@kuckit/sdk-react'
679
+ * import type { AppRouterClient } from '@kuckit/api/routers/index'
680
+ *
681
+ * function MyComponent() {
682
+ * const rpc = useRpc<AppRouterClient>()
683
+ * const result = await rpc.myRouter.myProcedure({ input: 'value' })
684
+ * }
685
+ * ```
686
+ */
687
+ function useRpc() {
688
+ const ctx = useContext(RpcContext);
689
+ if (!ctx) throw new Error("useRpc must be used within a KuckitRpcProvider");
690
+ return ctx;
691
+ }
692
+ /**
693
+ * Hook to check if RPC client is available
694
+ */
695
+ function useHasRpc() {
696
+ return useContext(RpcContext) !== null;
697
+ }
698
+
699
+ //#endregion
700
+ export { ClientModuleRegistry, KuckitNavProvider, KuckitRpcProvider, KuckitSlot, KuckitSlotProvider, NavRegistry, RouteRegistry, SlotRegistry, createClientModuleUnloadHandler, createComponentRegistry, createRegistries, defineKuckitClientModule, getClientModuleRegistry, getClientModulesWithCapability, loadKuckitClientModules, resetClientModuleRegistry, useHasRpc, useHasSlot, useKuckitNav, useNavItems, useNavTree, useRpc, useSlot, useSlotRegistry };