@litelens/core 1.7.1 → 1.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -18,8 +18,72 @@ So this package exists to give plugin authors real types, JSDoc, and editor auto
18
18
 
19
19
  ## Contents
20
20
 
21
- - **`src/index.ts`** hook declarations (currently `useClusterWideAPI`), each a stub that throws if called outside a plugin context.
22
- - **`src/types/`**shared TypeScript types re-exported alongside the hooks (`api`, `nav`, `resources`, `tray`).
21
+ - **`src/clusterWideAPI.ts`** (exported as `clusterWideAPI`) capabilities scoped to the single-cluster view. See "Using `clusterWideAPI`" below.
22
+ - **`src/appWideAPI.ts`** (exported as `appWideAPI`) capabilities with no single-cluster-view constraint. See "Using `appWideAPI`" below.
23
+ - **`src/types/`** — shared TypeScript types re-exported alongside the API namespaces above (`api`, `nav`, `resources`, `tray`).
24
+ - **`src/build/tsupPreset.ts`** (exported as `@litelens/core/tsup-preset`) — shared tsup build config for plugin frontends: Node-only build tooling, kept out of the `.` export above so it never ships in the browser bundle the vendor shim substitutes for. See "Shared plugin build config" below.
25
+
26
+ ## Using `clusterWideAPI`
27
+
28
+ Everything under `clusterWideAPI` is only valid inside the single-cluster view (i.e. a component rendered within `MainLayout`'s subtree). Calling the hooks from an app-wide screen (Settings, Marketplace) throws.
29
+
30
+ - **`useExposeProperties()`** — hook returning cluster-scoped state: `activeContext`, `activeNamespaces`, `activeResource`, `availableNamespaces`, `resourceLinks` (open a built-in resource's detail drawer, e.g. `resourceLinks.pod(namespace, name)`), and `unifiedTray` (`{ openTab }` for the bottom tray).
31
+ - **`useExposeMethods()`** — hook returning cluster-scoped actions, currently `onNavigateToView(view)`.
32
+ - **`registerViews(pluginId, configs)`** — registers your plugin's main view component(s), one per resource you own. Each `config.name` must match the `view` value used in the nav entry you register via `registerNavEntry` — the host mounts whichever registered view's `name` equals the currently active resource. Optionally pass a per-view `stylesheet` (a `Promise` from a CSS import) that loads only when that view mounts.
33
+ - **`registerNavEntry(pluginId, navEntry)`** — registers your plugin's sidebar entry (a single item or a group of items, see `NavEntry` in `src/types/nav.ts`).
34
+ - **`registerTrayFamilies(pluginId, families)`** — registers content components for tray families your plugin owns, keyed by an arbitrary family name your own `openTab` calls reference.
35
+ - **`registerEvents(pluginId, handlers)`** — subscribes your plugin to the host's plugin event bus, keyed by event name.
36
+
37
+ `registerViews`, `registerNavEntry`, `registerTrayFamilies`, and `registerEvents` are typically all called once, at module scope, in your plugin's entry file — that way your view, nav entry, tray content, and event handlers are all available as soon as the host dynamically imports your bundle, with no component needing to mount first. Re-registering under the same `pluginId` replaces the previous registration; the host also unregisters everything automatically when the plugin is uninstalled.
38
+
39
+ ```ts
40
+ import { clusterWideAPI } from "@litelens/core";
41
+ import { HelmChartListView } from "./views/HelmChartListView";
42
+ import { HELM_NAV_ENTRY } from "./nav";
43
+ import { HELM_TRAY_FAMILIES } from "./tray";
44
+
45
+ const PLUGIN_ID = "helm";
46
+
47
+ clusterWideAPI.registerViews(PLUGIN_ID, [{ name: "helm-charts", component: HelmChartListView }]);
48
+ clusterWideAPI.registerNavEntry(PLUGIN_ID, HELM_NAV_ENTRY);
49
+ clusterWideAPI.registerTrayFamilies(PLUGIN_ID, HELM_TRAY_FAMILIES);
50
+ clusterWideAPI.registerEvents(PLUGIN_ID, {
51
+ "helm:release-updated": (payload) => {
52
+ appWideAPI.getQueryClient().invalidateQueries({ queryKey: ["helm", "releases"] });
53
+ },
54
+ });
55
+ ```
56
+
57
+ Inside a mounted view component:
58
+
59
+ ```tsx
60
+ function HelmChartListView() {
61
+ const { activeContext, resourceLinks } = clusterWideAPI.useExposeProperties();
62
+ const { onNavigateToView } = clusterWideAPI.useExposeMethods();
63
+ // ...
64
+ }
65
+ ```
66
+
67
+ ## Using `appWideAPI`
68
+
69
+ - **`registerStylesheets(pluginId, stylesheets)`** — registers your plugin's global stylesheet(s) (e.g. compiled Tailwind output), loaded once at the app level regardless of which of your views is active. Call this once at module scope, alongside the `clusterWideAPI` registration calls above — don't use per-view `stylesheet` on `registerViews` for CSS that should apply across all of your views.
70
+ - **`getQueryClient()`** — returns the host's singleton `QueryClient`. Use this from code that isn't a mounted React component (e.g. the module-scope `registerEvents` handler above) and therefore can't call `useQueryClient()`.
71
+
72
+ ## Shared plugin build config
73
+
74
+ `@litelens/core/tsup-preset` exports `createPluginTsupConfig({ pluginRoot })`, the tsup config every plugin frontend needs: externalizes the host-shared runtime deps (`PLUGIN_SHARED_EXTERNALS`), inlines Tailwind-compiled CSS as text via a local Tailwind CLI pass, and optionally emits a bundle-analysis report to `dist/stats/` when `ANALYZE=true`. A plugin's own `tsup.config.ts` becomes:
75
+
76
+ ```ts
77
+ import { createPluginTsupConfig } from "@litelens/core/tsup-preset";
78
+ import path from "node:path";
79
+ import { fileURLToPath } from "node:url";
80
+
81
+ export default createPluginTsupConfig({
82
+ pluginRoot: path.dirname(fileURLToPath(import.meta.url)),
83
+ });
84
+ ```
85
+
86
+ Pass `entry` or `external` to override the entrypoint or add plugin-specific externals. Unlike the `.` export, this subpath pulls in real Node dependencies (`tsup`, `esbuild-visualizer`, `@tailwindcss/cli`) — they're declared as `dependencies` of this package (not `devDependencies`) so consuming plugins get them transitively.
23
87
 
24
88
  ## Versioning
25
89
 
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { ElementType, ReactNode, ComponentType } from 'react';
2
+ import { QueryClient } from '@tanstack/react-query';
2
3
 
3
4
  interface UseQueryCallback<T = unknown> {
4
5
  select?: (data?: T) => T;
@@ -905,50 +906,182 @@ interface SharedUnifiedTrayContext {
905
906
  }
906
907
 
907
908
  /**
908
- * @litelens/core React hooks for litelens plugin development
909
- *
910
- * This package exports hooks that plugin frontends can consume. The implementations
911
- * are provided by the host at runtime via import injection.
912
- */
913
- /**
914
- * Hook to access cluster-scoped APIs exposed to plugins. Returns a collection
915
- * of capabilities valid only within the single-cluster view — i.e. inside
916
- * MainLayout's subtree (MainLayoutProvider → DetailDrawerProvider). Calling
917
- * this from a component rendered outside that subtree (e.g. Settings,
918
- * Marketplace, or any future app-wide screen) will throw, because the
919
- * underlying host state (DetailDrawerContext) has no provider there. A future
920
- * useAppWideAPI() will cover capabilities that don't have this constraint.
909
+ * Hook to access cluster-scoped property values exposed to plugins. Valid
910
+ * only within the single-cluster view — i.e. inside MainLayout's subtree
911
+ * (MainLayoutProvider DetailDrawerProvider). Calling this from a component
912
+ * rendered outside that subtree (e.g. Settings, Marketplace, or any future
913
+ * app-wide screen) will throw, because the underlying host state
914
+ * (DetailDrawerContext) has no provider there. A future appWideAPI will
915
+ * cover capabilities that don't have this constraint.
921
916
  *
922
917
  * Example:
923
- * const { activeContext, activeNamespaces, activeResource, availableNamespaces, onNavigateToView, resourceLinks, unifiedTray, useRegisterClusterWideEvents, useRegisterNavEntry, useRegisterTrayFamilies } = useClusterWideAPI();
918
+ * const { activeContext, activeNamespaces, activeResource, availableNamespaces, resourceLinks, unifiedTray } = clusterWideAPI.useExposeProperties();
924
919
  * resourceLinks.pod(namespace, podName); // Opens pod detail drawer
925
920
  * unifiedTray.openTab("my-plugin-family", { pluginId: "my-plugin", label: "...", dedupeKey: "..." });
926
- * useRegisterClusterWideEvents({
927
- * "helm:release-updated": (payload) => { console.log("Release updated:", payload); },
928
- * });
929
- * useRegisterNavEntry("helm", "Helm", HELM_NAV_ENTRY);
930
- * useRegisterTrayFamilies("helm", HELM_TRAY_FAMILIES);
931
921
  *
932
922
  * This signature is duplicated from the host's real implementation at
933
- * frontend/src/expose/hooks/useClusterWideAPI.ts (which this package can't
923
+ * frontend/src/expose/hooks/useExposeProperties.ts (which this package can't
934
924
  * import directly — see main.tsx's vendor injection). If that hook's return
935
925
  * type changes, update this signature to match.
936
926
  */
937
- declare function useClusterWideAPI(): {
927
+ declare function useExposeProperties(): {
938
928
  activeContext: string;
939
929
  activeNamespaces: string[];
940
930
  activeResource: string;
941
931
  availableNamespaces: Array<{
942
932
  Name: string;
943
933
  }>;
944
- onNavigateToView: (view: string) => void;
945
934
  resourceLinks: Record<string, (namespace: string, name: string) => void>;
946
935
  unifiedTray: {
947
936
  openTab: (family: string, params: unknown) => void;
948
937
  } | null;
949
- useRegisterClusterWideEvents: (handlers: Record<string, (payload: unknown) => void>) => void;
950
- useRegisterNavEntry: (pluginId: string, pluginName: string, navEntry: NavEntry<string> | undefined) => void;
951
- useRegisterTrayFamilies: (pluginId: string, families: Record<string, ComponentType<SharedUnifiedTrayContentProps>> | undefined) => void;
938
+ };
939
+ /**
940
+ * Hook to access cluster-scoped plain functions exposed to plugins. Valid
941
+ * only within the single-cluster view — same constraints as useExposeProperties().
942
+ *
943
+ * Example:
944
+ * const { onNavigateToView } = clusterWideAPI.useExposeMethods();
945
+ *
946
+ * This signature is duplicated from the host's real implementation at
947
+ * frontend/src/expose/hooks/useExposeMethods.ts (which this package can't
948
+ * import directly — see main.tsx's vendor injection). If that hook's return
949
+ * type changes, update this signature to match.
950
+ */
951
+ declare function useExposeMethods(): {
952
+ onNavigateToView: (view: string) => void;
953
+ };
954
+ /**
955
+ * Register this plugin's event handlers with the host's plugin event bus.
956
+ * Called once by a plugin, typically at module scope alongside
957
+ * registerViews/registerNavEntry/registerTrayFamilies. Re-registering under
958
+ * the same plugin ID replaces its handlers; the host also unregisters them
959
+ * automatically when the plugin is uninstalled.
960
+ *
961
+ * Example:
962
+ * const queryClient = appWideAPI.getQueryClient();
963
+ * clusterWideAPI.registerEvents("helm", {
964
+ * "helm:release-updated": (payload) => { queryClient.invalidateQueries(...); },
965
+ * });
966
+ *
967
+ * This function is replaced at runtime by the host's actual implementation
968
+ * (injected in frontend/src/expose/index.tsx). If you see this error, registerEvents
969
+ * was called before the host initialized the injection.
970
+ */
971
+ declare function registerEvents(pluginId: string, handlers: Record<string, (payload: any) => void>): void;
972
+ /**
973
+ * Register this plugin's tray-family content components with the host's
974
+ * unified bottom tray. Called once by a plugin, typically at module scope
975
+ * alongside registerViews/registerNavEntry, so the plugin's tray content is
976
+ * available as soon as the plugin's bundle is imported — no component needs
977
+ * to mount first.
978
+ *
979
+ * Example:
980
+ * clusterWideAPI.registerTrayFamilies("helm", HELM_TRAY_FAMILIES);
981
+ *
982
+ * This function is replaced at runtime by the host's actual implementation
983
+ * (injected in frontend/src/expose/index.tsx). If you see this error, registerTrayFamilies
984
+ * was called before the host initialized the injection.
985
+ */
986
+ declare function registerTrayFamilies(pluginId: string, families: Record<string, ComponentType<SharedUnifiedTrayContentProps>> | undefined): void;
987
+ /**
988
+ * Register one or more named view components with the host. Called by
989
+ * plugins to provide their main view UI, one per resource the plugin owns.
990
+ * `name` must match the resource's nav entry `view` value (see
991
+ * registerNavEntry) — the host mounts whichever registered view's `name`
992
+ * equals the currently active resource and keeps the rest hidden, so a
993
+ * plugin no longer needs to switch on the active resource itself. Each view
994
+ * may optionally supply its own `stylesheet`, loaded only when that view
995
+ * mounts — for a plugin's global stylesheet (e.g. Tailwind output shared
996
+ * across all its views), use appWideAPI.registerStylesheets instead, which
997
+ * loads once at the app level regardless of which view is active.
998
+ *
999
+ * Example:
1000
+ * clusterWideAPI.registerViews("helm", [
1001
+ * { name: "helm-charts", component: HelmChartListView, stylesheet: import("./chart-list.css") },
1002
+ * { name: "helm-releases", component: HelmReleaseListView },
1003
+ * ]);
1004
+ *
1005
+ * This function is replaced at runtime by the host's actual implementation
1006
+ * (injected in frontend/src/expose/index.tsx). If you see this error, registerViews
1007
+ * was called before the host initialized the injection.
1008
+ */
1009
+ declare function registerViews(pluginId: string, configs: Array<{
1010
+ name: string;
1011
+ component: ComponentType;
1012
+ stylesheet?: Promise<{
1013
+ default: string;
1014
+ }>;
1015
+ }>): void;
1016
+ /**
1017
+ * Register this plugin's sidebar nav entry with the host. Called once by a
1018
+ * plugin, typically at module scope alongside registerViews, so the entry
1019
+ * appears in the sidebar as soon as the plugin's bundle is imported — no
1020
+ * component needs to mount first.
1021
+ *
1022
+ * Example:
1023
+ * clusterWideAPI.registerNavEntry("helm", HELM_NAV_ENTRY);
1024
+ *
1025
+ * This function is replaced at runtime by the host's actual implementation
1026
+ * (injected in frontend/src/expose/index.tsx). If you see this error, registerNavEntry
1027
+ * was called before the host initialized the injection.
1028
+ */
1029
+ declare function registerNavEntry(pluginId: string, navEntry: NavEntry<string> | undefined): void;
1030
+ /**
1031
+ * Cluster-scoped capabilities exposed to plugins, valid only within the
1032
+ * single-cluster view (see useExposeProperties() for the exact constraint).
1033
+ * A future appWideAPI namespace will cover capabilities that don't have
1034
+ * this constraint.
1035
+ *
1036
+ * Example:
1037
+ * const { onNavigateToView } = clusterWideAPI.useExposeMethods();
1038
+ */
1039
+ declare const clusterWideAPI: {
1040
+ useExposeProperties: typeof useExposeProperties;
1041
+ useExposeMethods: typeof useExposeMethods;
1042
+ registerViews: typeof registerViews;
1043
+ registerNavEntry: typeof registerNavEntry;
1044
+ registerTrayFamilies: typeof registerTrayFamilies;
1045
+ registerEvents: typeof registerEvents;
1046
+ };
1047
+
1048
+ /**
1049
+ * Register the plugin's global stylesheet(s) with the host. Unlike
1050
+ * clusterWideAPI.registerViews, this isn't scoped per-view — a plugin's
1051
+ * compiled CSS (e.g. Tailwind output) applies across all of its views, so
1052
+ * it's registered once per plugin rather than attached to each view config.
1053
+ *
1054
+ * Example:
1055
+ * appWideAPI.registerStylesheets("helm", [import("./style.css")]);
1056
+ *
1057
+ * This function is replaced at runtime by the host's actual implementation
1058
+ * (injected in frontend/src/expose/index.tsx). If you see this error,
1059
+ * registerStylesheets was called before the host initialized the injection.
1060
+ */
1061
+ declare function registerStylesheets(pluginId: string, stylesheets: Array<Promise<{
1062
+ default: string;
1063
+ }>>): void;
1064
+ /**
1065
+ * Get the host's singleton QueryClient instance. Useful for plugin code
1066
+ * that isn't a mounted React component (e.g. module-scope registration in
1067
+ * index.ts) and therefore can't call useQueryClient()'s hook.
1068
+ *
1069
+ * Example:
1070
+ * const queryClient = appWideAPI.getQueryClient();
1071
+ * queryClient.invalidateQueries({ queryKey: [...] });
1072
+ *
1073
+ * This function is replaced at runtime by the host's actual implementation
1074
+ * (injected in frontend/src/expose/index.tsx). If you see this error,
1075
+ * getQueryClient was called before the host initialized the injection.
1076
+ */
1077
+ declare function getQueryClient(): QueryClient;
1078
+ /**
1079
+ * App-wide capabilities exposed to plugins — not scoped to the single-cluster
1080
+ * view, unlike clusterWideAPI (see useExposeProperties() for that constraint).
1081
+ */
1082
+ declare const appWideAPI: {
1083
+ registerStylesheets: typeof registerStylesheets;
1084
+ getQueryClient: typeof getQueryClient;
952
1085
  };
953
1086
 
954
- export { type CRBSubject, type ClusterRole, type ClusterRoleBinding, type ConfigMap, type CronJob, type CronJobSummary, type DaemonSet, type DaemonSetSummary, type Deployment, type DeploymentCondition, type DeploymentSummary, type Endpoint, type EndpointAddress, type EndpointPort, type EndpointSlice, type EndpointSliceEndpoint, type EndpointSlicePort, type EndpointSubset, type Event, type HPA, type HPADetail, type HPAMetric, type Ingress, type IngressClass, type IngressDetail, type IngressPath, type IngressRule, type Job, type JobCondition, type JobSummary, type Lease, type LimitRange, type LimitRangeDetail, type ManagedField, type Namespace, type NavEntry, type NavGroup, type NavItem, type NetworkPolicy, type NetworkPolicyDetail, type NetworkPolicyEgressRule, type NetworkPolicyIngressRule, type NetworkPolicyPeer, type Node, type NodeAddress, type NodeCondition, type PersistentVolume, type PersistentVolumeClaim, type PersistentVolumeClaimDetail, type PersistentVolumeDetail, type Pod, type PodCondition, type PodContainerDetail, type PodContainerLastStatus, type PodContainerMount, type PodContainerPort, type PodDisruptionBudget, type PodDisruptionBudgetDetail, type PodSummary, type PodVolume, type PodVolumeSource, type PolicyRule, type PortForward, type PriorityClass, type RBSubject, type ReplicaSet, type ReplicaSetSummary, type ResourceQuota, type ResourceQuotaDetail, type Role, type RoleBinding, type ScaleTargetRef, type Secret, type SecretDetail, type Service, type ServiceAccount, type ServicePort, type SharedNamespaceContext, type SharedUnifiedTrayContentProps, type SharedUnifiedTrayContext, type SharedUnifiedTrayOpenParams, type SharedUnifiedTrayTab, type StatefulSet, type StatefulSetSummary, type StorageClass, type TolerationDetail, type UnifiedTrayAllFamily, type UnifiedTrayCoreFamily, type UseQueryCallback, type ValidatingWebhookConfig, type ValidatingWebhookConfigDetail, type WebhookDetail, useClusterWideAPI };
1087
+ export { type CRBSubject, type ClusterRole, type ClusterRoleBinding, type ConfigMap, type CronJob, type CronJobSummary, type DaemonSet, type DaemonSetSummary, type Deployment, type DeploymentCondition, type DeploymentSummary, type Endpoint, type EndpointAddress, type EndpointPort, type EndpointSlice, type EndpointSliceEndpoint, type EndpointSlicePort, type EndpointSubset, type Event, type HPA, type HPADetail, type HPAMetric, type Ingress, type IngressClass, type IngressDetail, type IngressPath, type IngressRule, type Job, type JobCondition, type JobSummary, type Lease, type LimitRange, type LimitRangeDetail, type ManagedField, type Namespace, type NavEntry, type NavGroup, type NavItem, type NetworkPolicy, type NetworkPolicyDetail, type NetworkPolicyEgressRule, type NetworkPolicyIngressRule, type NetworkPolicyPeer, type Node, type NodeAddress, type NodeCondition, type PersistentVolume, type PersistentVolumeClaim, type PersistentVolumeClaimDetail, type PersistentVolumeDetail, type Pod, type PodCondition, type PodContainerDetail, type PodContainerLastStatus, type PodContainerMount, type PodContainerPort, type PodDisruptionBudget, type PodDisruptionBudgetDetail, type PodSummary, type PodVolume, type PodVolumeSource, type PolicyRule, type PortForward, type PriorityClass, type RBSubject, type ReplicaSet, type ReplicaSetSummary, type ResourceQuota, type ResourceQuotaDetail, type Role, type RoleBinding, type ScaleTargetRef, type Secret, type SecretDetail, type Service, type ServiceAccount, type ServicePort, type SharedNamespaceContext, type SharedUnifiedTrayContentProps, type SharedUnifiedTrayContext, type SharedUnifiedTrayOpenParams, type SharedUnifiedTrayTab, type StatefulSet, type StatefulSetSummary, type StorageClass, type TolerationDetail, type UnifiedTrayAllFamily, type UnifiedTrayCoreFamily, type UseQueryCallback, type ValidatingWebhookConfig, type ValidatingWebhookConfigDetail, type WebhookDetail, appWideAPI, clusterWideAPI };
package/dist/index.js CHANGED
@@ -1,8 +1,57 @@
1
- // src/index.ts
2
- function useClusterWideAPI() {
1
+ // src/clusterWideAPI.ts
2
+ function useExposeProperties() {
3
3
  throw new Error(
4
- "useClusterWideAPI is not available. This hook must be imported from '@litelens/core' within a plugin bundle loaded by the litelens host."
4
+ "clusterWideAPI.useExposeProperties is not available. This hook must be imported from '@litelens/core' within a plugin bundle loaded by the litelens host."
5
5
  );
6
6
  }
7
+ function useExposeMethods() {
8
+ throw new Error(
9
+ "clusterWideAPI.useExposeMethods is not available. This hook must be imported from '@litelens/core' within a plugin bundle loaded by the litelens host."
10
+ );
11
+ }
12
+ function registerEvents(pluginId, handlers) {
13
+ throw new Error(
14
+ "clusterWideAPI.registerEvents is not available. This function must be imported from '@litelens/core' within a plugin bundle loaded by the litelens host."
15
+ );
16
+ }
17
+ function registerTrayFamilies(pluginId, families) {
18
+ throw new Error(
19
+ "clusterWideAPI.registerTrayFamilies is not available. This function must be imported from '@litelens/core' within a plugin bundle loaded by the litelens host."
20
+ );
21
+ }
22
+ function registerViews(pluginId, configs) {
23
+ throw new Error(
24
+ "clusterWideAPI.registerViews is not available. This function must be imported from '@litelens/core' within a plugin bundle loaded by the litelens host."
25
+ );
26
+ }
27
+ function registerNavEntry(pluginId, navEntry) {
28
+ throw new Error(
29
+ "clusterWideAPI.registerNavEntry is not available. This function must be imported from '@litelens/core' within a plugin bundle loaded by the litelens host."
30
+ );
31
+ }
32
+ var clusterWideAPI = {
33
+ useExposeProperties,
34
+ useExposeMethods,
35
+ registerViews,
36
+ registerNavEntry,
37
+ registerTrayFamilies,
38
+ registerEvents
39
+ };
40
+
41
+ // src/appWideAPI.ts
42
+ function registerStylesheets(pluginId, stylesheets) {
43
+ throw new Error(
44
+ "appWideAPI.registerStylesheets is not available. This function must be imported from '@litelens/core' within a plugin bundle loaded by the litelens host."
45
+ );
46
+ }
47
+ function getQueryClient() {
48
+ throw new Error(
49
+ "appWideAPI.getQueryClient is not available. This function must be imported from '@litelens/core' within a plugin bundle loaded by the litelens host."
50
+ );
51
+ }
52
+ var appWideAPI = {
53
+ registerStylesheets,
54
+ getQueryClient
55
+ };
7
56
 
8
- export { useClusterWideAPI };
57
+ export { appWideAPI, clusterWideAPI };
@@ -0,0 +1,33 @@
1
+ import { Options } from 'tsup';
2
+
3
+ /**
4
+ * Externals every plugin frontend must share with the host's own module
5
+ * instances (resolved via the host's import map + vendor shims) rather than
6
+ * bundling its own copy — required for react-dom/@tanstack/react-query
7
+ * context objects to resolve correctly when a plugin component is mounted
8
+ * inline in the host's fiber tree.
9
+ */
10
+ declare const PLUGIN_SHARED_EXTERNALS: readonly ["react", "react-dom", "@litelens/design-system", "@tanstack/react-query", "@litelens/core"];
11
+ interface CreatePluginTsupConfigOptions {
12
+ /** Absolute path to the plugin frontend package's root directory (e.g.
13
+ * `path.dirname(fileURLToPath(import.meta.url))` from the plugin's own
14
+ * `tsup.config.ts`). Used as the Tailwind CLI's `--cwd` and to resolve the
15
+ * package name for the bundle-report title. */
16
+ pluginRoot: string;
17
+ /** Defaults to `["src/index.ts"]`. */
18
+ entry?: string[];
19
+ /** Extra externals beyond {@link PLUGIN_SHARED_EXTERNALS}. */
20
+ external?: string[];
21
+ /** Defaults to `process.env.ANALYZE === "true"`. */
22
+ analyze?: boolean;
23
+ }
24
+ /**
25
+ * Shared tsup config for litelens plugin frontends: bundles `src/index.ts`
26
+ * to a single ESM file, externalizing the host-shared runtime deps, inlining
27
+ * Tailwind-compiled CSS as text (see {@link createInlineTailwindPlugin}), and
28
+ * optionally emitting a bundle-analysis report to `dist/stats/` when
29
+ * `ANALYZE=true`.
30
+ */
31
+ declare function createPluginTsupConfig(options: CreatePluginTsupConfigOptions): Options;
32
+
33
+ export { type CreatePluginTsupConfigOptions, PLUGIN_SHARED_EXTERNALS, createPluginTsupConfig };
@@ -0,0 +1,99 @@
1
+ import { visualizer, prepareVisualizerData } from 'esbuild-visualizer';
2
+ import { execFileSync } from 'child_process';
3
+ import fs from 'fs';
4
+ import { createRequire } from 'module';
5
+ import path from 'path';
6
+
7
+ // src/build/tsupPreset.ts
8
+ var PLUGIN_SHARED_EXTERNALS = [
9
+ "react",
10
+ "react-dom",
11
+ "@litelens/design-system",
12
+ "@tanstack/react-query",
13
+ "@litelens/core"
14
+ ];
15
+ var require2 = createRequire(import.meta.url);
16
+ function resolveTailwindCliEntry() {
17
+ const tailwindCliPkg = JSON.parse(
18
+ fs.readFileSync(require2.resolve("@tailwindcss/cli/package.json"), "utf-8")
19
+ );
20
+ return path.join(
21
+ path.dirname(require2.resolve("@tailwindcss/cli/package.json")),
22
+ tailwindCliPkg.bin.tailwindcss
23
+ );
24
+ }
25
+ var RESOLVED_SUFFIX = ".tw-inline";
26
+ function createInlineTailwindPlugin(pluginRoot) {
27
+ const tailwindCliEntry = resolveTailwindCliEntry();
28
+ return {
29
+ name: "inline-tailwind",
30
+ setup(build) {
31
+ build.onResolve({ filter: /\.css$/ }, (args) => {
32
+ if (args.namespace !== "file") return;
33
+ const absPath = path.isAbsolute(args.path) ? args.path : path.join(args.resolveDir, args.path);
34
+ return { path: absPath + RESOLVED_SUFFIX, namespace: "inline-tailwind" };
35
+ });
36
+ build.onLoad({ filter: /\.tw-inline$/, namespace: "inline-tailwind" }, (args) => {
37
+ const realPath = args.path.slice(0, -RESOLVED_SUFFIX.length);
38
+ const contents = execFileSync(
39
+ process.execPath,
40
+ [tailwindCliEntry, "-i", realPath, "-m", "--cwd", pluginRoot],
41
+ { encoding: "utf-8" }
42
+ );
43
+ return { contents, loader: "text" };
44
+ });
45
+ }
46
+ };
47
+ }
48
+ function createPluginTsupConfig(options) {
49
+ const { pluginRoot, entry = ["src/index.ts"], external = [], analyze } = options;
50
+ const shouldAnalyze = analyze ?? process.env.ANALYZE === "true";
51
+ const pkg = JSON.parse(fs.readFileSync(path.join(pluginRoot, "package.json"), "utf-8"));
52
+ return {
53
+ entry,
54
+ format: ["esm"],
55
+ outDir: "dist",
56
+ target: "es2020",
57
+ splitting: true,
58
+ sourcemap: false,
59
+ clean: true,
60
+ metafile: shouldAnalyze,
61
+ external: [...PLUGIN_SHARED_EXTERNALS, ...external],
62
+ // Tailwind CSS is compiled in-memory by the inline-tailwind esbuild plugin
63
+ // (above) and imported as raw text via `import("./style.css")`, so it ends
64
+ // up embedded directly in dist/index.js instead of shipped as a separate
65
+ // stylesheet asset. Must be set here (tsup's top-level `loader` option)
66
+ // rather than inside esbuildOptions — tsup's own CSS-handling esbuild
67
+ // plugin reads this value before esbuildOptions runs.
68
+ loader: {
69
+ ".css": "text"
70
+ },
71
+ // tsup's own esbuild-plugin array (postcss, svelte, etc.) is built statically
72
+ // before the build starts; a plugin pushed onto `options.plugins` inside
73
+ // esbuildOptions arrives too late; esbuild has already snapshotted the array
74
+ // for setup. `esbuildPlugins` is tsup's supported hook for adding one upfront.
75
+ esbuildPlugins: [createInlineTailwindPlugin(pluginRoot)],
76
+ esbuildOptions: (esbuildOptions) => {
77
+ esbuildOptions.banner = {
78
+ js: "/* Plugin bundle - loaded dynamically */"
79
+ };
80
+ },
81
+ onSuccess: async () => {
82
+ const metafilePath = path.join(pluginRoot, "dist", "metafile-esm.json");
83
+ if (shouldAnalyze && fs.existsSync(metafilePath)) {
84
+ const statsDir = path.join(pluginRoot, "dist", "stats");
85
+ fs.mkdirSync(statsDir, { recursive: true });
86
+ const metadata = JSON.parse(fs.readFileSync(metafilePath, "utf-8"));
87
+ const html = await visualizer(metadata, {
88
+ title: `${pkg.name} Bundle Report`,
89
+ template: "treemap"
90
+ });
91
+ fs.writeFileSync(path.join(statsDir, "bundle-report.html"), html);
92
+ fs.writeFileSync(path.join(statsDir, "bundle-stats.json"), prepareVisualizerData(metadata));
93
+ fs.renameSync(metafilePath, path.join(statsDir, "metafile-esm.json"));
94
+ }
95
+ }
96
+ };
97
+ }
98
+
99
+ export { PLUGIN_SHARED_EXTERNALS, createPluginTsupConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@litelens/core",
3
- "version": "1.7.1",
3
+ "version": "1.7.3",
4
4
  "description": "React hooks and utilities for litelens plugin development",
5
5
  "private": false,
6
6
  "repository": {
@@ -20,22 +20,31 @@
20
20
  "exports": {
21
21
  ".": {
22
22
  "import": "./dist/index.js"
23
+ },
24
+ "./tsup-preset": {
25
+ "import": "./dist/tsup-preset.js"
23
26
  }
24
27
  },
25
28
  "peerDependencies": {
29
+ "@tanstack/react-query": "^5.101.4",
26
30
  "react": "^19.0.0",
27
31
  "react-dom": "^19.0.0"
28
32
  },
33
+ "dependencies": {
34
+ "@tailwindcss/cli": "^4.3.3",
35
+ "esbuild-visualizer": "^0.7.0",
36
+ "tsup": "^8.5.1"
37
+ },
29
38
  "devDependencies": {
39
+ "@tanstack/react-query": "^5.101.4",
30
40
  "@types/node": "^26.2.0",
31
41
  "@types/react": "^19.2.18",
32
42
  "@types/react-dom": "^19.2.4",
33
- "@vitejs/plugin-react": "^6.0.5",
34
- "@vitest/coverage-v8": "^4.1.10",
43
+ "@vitejs/plugin-react": "^6.1.0",
44
+ "@vitest/coverage-v8": "^4.1.11",
35
45
  "jsdom": "^29.1.1",
36
- "tsup": "^8.5.1",
37
46
  "typescript": "^6.0.3",
38
- "vitest": "^4.1.10"
47
+ "vitest": "^4.1.11"
39
48
  },
40
49
  "scripts": {
41
50
  "build": "tsup",