kmod-cli 1.0.10

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 (66) hide show
  1. package/README.md +53 -0
  2. package/bin/gen-components.js +68 -0
  3. package/bin/index.js +153 -0
  4. package/component-templates/components/access-denied.tsx +130 -0
  5. package/component-templates/components/breadcumb.tsx +42 -0
  6. package/component-templates/components/count-down.tsx +94 -0
  7. package/component-templates/components/count-input.tsx +221 -0
  8. package/component-templates/components/date-range-calendar/button.tsx +61 -0
  9. package/component-templates/components/date-range-calendar/calendar.tsx +132 -0
  10. package/component-templates/components/date-range-calendar/date-input.tsx +259 -0
  11. package/component-templates/components/date-range-calendar/date-range-picker.tsx +594 -0
  12. package/component-templates/components/date-range-calendar/label.tsx +31 -0
  13. package/component-templates/components/date-range-calendar/popover.tsx +32 -0
  14. package/component-templates/components/date-range-calendar/select.tsx +125 -0
  15. package/component-templates/components/date-range-calendar/switch.tsx +30 -0
  16. package/component-templates/components/datetime-picker/button.tsx +61 -0
  17. package/component-templates/components/datetime-picker/calendar.tsx +156 -0
  18. package/component-templates/components/datetime-picker/datetime-picker.tsx +75 -0
  19. package/component-templates/components/datetime-picker/input.tsx +20 -0
  20. package/component-templates/components/datetime-picker/label.tsx +18 -0
  21. package/component-templates/components/datetime-picker/period-input.tsx +62 -0
  22. package/component-templates/components/datetime-picker/popover.tsx +32 -0
  23. package/component-templates/components/datetime-picker/select.tsx +125 -0
  24. package/component-templates/components/datetime-picker/time-picker-input.tsx +131 -0
  25. package/component-templates/components/datetime-picker/time-picker-utils.tsx +204 -0
  26. package/component-templates/components/datetime-picker/time-picker.tsx +59 -0
  27. package/component-templates/components/gradient-outline.tsx +233 -0
  28. package/component-templates/components/gradient-svg.tsx +157 -0
  29. package/component-templates/components/grid-layout.tsx +69 -0
  30. package/component-templates/components/hydrate-guard.tsx +40 -0
  31. package/component-templates/components/image.tsx +92 -0
  32. package/component-templates/components/loader-slash-gradient.tsx +85 -0
  33. package/component-templates/components/masonry-gallery.tsx +221 -0
  34. package/component-templates/components/modal.tsx +110 -0
  35. package/component-templates/components/multi-select.tsx +447 -0
  36. package/component-templates/components/non-hydration.tsx +27 -0
  37. package/component-templates/components/portal.tsx +34 -0
  38. package/component-templates/components/segments-circle.tsx +235 -0
  39. package/component-templates/components/single-select.tsx +248 -0
  40. package/component-templates/components/stroke-circle.tsx +57 -0
  41. package/component-templates/components/table/column-table.tsx +15 -0
  42. package/component-templates/components/table/data-table.tsx +339 -0
  43. package/component-templates/components/table/readme.tsx +95 -0
  44. package/component-templates/components/table/table.tsx +60 -0
  45. package/component-templates/components/text-hover-effect.tsx +120 -0
  46. package/component-templates/components/timout-loader.tsx +52 -0
  47. package/component-templates/components/toast.tsx +994 -0
  48. package/component-templates/configs/config.ts +33 -0
  49. package/component-templates/configs/feature-config.tsx +432 -0
  50. package/component-templates/configs/keys.ts +7 -0
  51. package/component-templates/core/api-service.ts +202 -0
  52. package/component-templates/core/calculate.ts +18 -0
  53. package/component-templates/core/idb.ts +166 -0
  54. package/component-templates/core/storage.ts +213 -0
  55. package/component-templates/hooks/count-down.ts +38 -0
  56. package/component-templates/hooks/fade-on-scroll.ts +52 -0
  57. package/component-templates/hooks/safe-action.ts +59 -0
  58. package/component-templates/hooks/spam-guard.ts +31 -0
  59. package/component-templates/lib/utils.ts +6 -0
  60. package/component-templates/providers/feature-guard.tsx +432 -0
  61. package/component-templates/queries/query.tsx +775 -0
  62. package/component-templates/utils/colors/color-by-text.ts +307 -0
  63. package/component-templates/utils/colors/stripe-effect.ts +100 -0
  64. package/component-templates/utils/hash/hash-aes.ts +35 -0
  65. package/components.json +348 -0
  66. package/package.json +60 -0
@@ -0,0 +1,432 @@
1
+ // A reusable, library-style Feature Flags system for React/TypeScript
2
+ // ---------------------------------------------------------------
3
+ // Quick start:
4
+ //
5
+ // import { FeatureConfig } from "./feature-config";
6
+ // import { FeatureKey } from "./feature-keys";
7
+ //
8
+ // export const {
9
+ // FeatureProvider,
10
+ // Feature,
11
+ // FeatureLock,
12
+ // useFeature,
13
+ // useFlags,
14
+ // refresh,
15
+ // setFlags,
16
+ // sources,
17
+ // } = FeatureConfig({
18
+ // keys: FeatureKey, // enum or keys array
19
+ // sources: [
20
+ // sources.local({
21
+ // flags: {
22
+ // [FeatureKey.NewDashboard]: false,
23
+ // [FeatureKey.BetaButton]: true,
24
+ // },
25
+ // priority: 0,
26
+ // }),
27
+ // sources.storage({ storageKey: "feature_flags", priority: 5 }),
28
+ // sources.remote({
29
+ // priority: 10, // remote override local
30
+ // fetch: async () => {
31
+ // const res = await fetch("/api/feature-flags");
32
+ // return (await res.json()) as Record<string, boolean>;
33
+ // },
34
+ // transform: (raw) => raw, // optional map
35
+ // }),
36
+ // ],
37
+ // strategy: "last-wins", // "any-true" | "all-true" | custom reducer
38
+ // strict: "warn", // "error" | "warn" | "silent"
39
+ // });
40
+ //
41
+ // // App root
42
+ // <FeatureProvider>{children}</FeatureProvider>
43
+ //
44
+ // // Usage
45
+ // <Feature feature={FeatureKey.NewDashboard}>New UI</Feature>
46
+ // <FeatureLock feature={FeatureKey.BetaButton}>Beta locked</FeatureLock>
47
+ // ---------------------------------------------------------------
48
+
49
+ "use client";
50
+ import React, {
51
+ createContext,
52
+ ReactNode,
53
+ useContext,
54
+ useEffect,
55
+ useMemo,
56
+ useState,
57
+ } from 'react';
58
+
59
+ // -----------------------------
60
+ // Types
61
+ // -----------------------------
62
+ export type EnumLike = Record<string, string | number> | readonly string[];
63
+
64
+ type InferKeys<E extends EnumLike> = E extends readonly string[]
65
+ ? E[number]
66
+ : E extends Record<string, infer V>
67
+ ? Extract<V, string> | Extract<V, number> extends infer U
68
+ ? Extract<U, string> // we only accept string keys for flags
69
+ : never
70
+ : never;
71
+
72
+ export type FlagMap<K extends string> = Partial<Record<K, boolean>>;
73
+
74
+ export type FeatureSource<K extends string> = {
75
+ name?: string;
76
+ priority?: number; // higher runs later if last-wins
77
+ load: (ctx: {
78
+ abortSignal?: AbortSignal;
79
+ }) => Promise<FlagMap<K>> | FlagMap<K>;
80
+ save?: (flags: FlagMap<K>) => Promise<void> | void; // optional, for writable stores
81
+ };
82
+
83
+ export type MergeStrategy<K extends string> =
84
+ | "last-wins"
85
+ | "any-true"
86
+ | "all-true"
87
+ | ((entries: { name: string; flags: FlagMap<K> }[]) => FlagMap<K>);
88
+
89
+ export type StrictMode = "error" | "warn" | "silent";
90
+
91
+ export interface FeatureOptions<
92
+ E extends EnumLike,
93
+ K extends string = InferKeys<E> & string
94
+ > {
95
+ keys: E; // enum object or readonly string[]
96
+ sources?: FeatureSource<K>[];
97
+ strategy?: MergeStrategy<K>;
98
+ strict?: StrictMode;
99
+ initialFlags?: FlagMap<K>; // SSR hydration or preloaded
100
+ }
101
+
102
+ // -----------------------------
103
+ // Helpers
104
+ // -----------------------------
105
+ function normalizeEnum<E extends EnumLike>(e: E): string[] {
106
+ if (Array.isArray(e)) return e as string[];
107
+ // TS enum has both keys and reverse mapping for numeric enums –
108
+ // we're interested in string values only.
109
+ return Object.values(e).filter((v): v is string => typeof v === "string");
110
+ }
111
+
112
+ function filterUnknownKeys<K extends string>(
113
+ keys: readonly string[],
114
+ flags: FlagMap<K>,
115
+ strict: StrictMode,
116
+ sourceName: string
117
+ ): FlagMap<K> {
118
+ const allowed = new Set(keys);
119
+ const out: FlagMap<K> = {};
120
+ for (const [k, v] of Object.entries(flags)) {
121
+ if (allowed.has(k)) {
122
+ (out as any)[k] = !!v;
123
+ } else if (strict !== "silent") {
124
+ const msg = `[FeatureConfig] Unknown key "${k}" from ${sourceName}`;
125
+ strict === "error" ? console.error(msg) : console.warn(msg);
126
+ }
127
+ }
128
+ return out;
129
+ }
130
+
131
+ function mergeFlags<K extends string>(
132
+ entries: { name: string; flags: FlagMap<K> }[],
133
+ strategy: MergeStrategy<K>
134
+ ): FlagMap<K> {
135
+ if (typeof strategy === "function") return strategy(entries);
136
+
137
+ if (strategy === "any-true") {
138
+ const out: FlagMap<K> = {};
139
+ for (const e of entries) {
140
+ for (const [k, v] of Object.entries(e.flags)) {
141
+ (out as any)[k] = Boolean((out as any)[k]) || Boolean(v);
142
+ }
143
+ }
144
+ return out;
145
+ }
146
+
147
+ if (strategy === "all-true") {
148
+ // Start with union of keys, then AND them across sources
149
+ const out: Record<string, boolean> = {};
150
+ const allKeys = new Set<string>();
151
+ entries.forEach((e) => Object.keys(e.flags).forEach((k) => allKeys.add(k)));
152
+ allKeys.forEach((k) => {
153
+ out[k] = entries.every((e) => e.flags[k as K] === true);
154
+ });
155
+ return out as FlagMap<K>;
156
+ }
157
+
158
+ // default: last-wins (based on order of entries)
159
+ const out: FlagMap<K> = {};
160
+ for (const e of entries) Object.assign(out, e.flags);
161
+ return out;
162
+ }
163
+
164
+ // -----------------------------
165
+ // Built-in Sources (adapters)
166
+ // -----------------------------
167
+ function localSource<K extends string>(opts: {
168
+ flags: FlagMap<K>;
169
+ priority?: number;
170
+ name?: string;
171
+ }): FeatureSource<K> {
172
+ const { flags, priority = 0, name = "local" } = opts;
173
+ return {
174
+ name,
175
+ priority,
176
+ load: () => flags,
177
+ };
178
+ }
179
+
180
+ function storageSource<K extends string>(opts: {
181
+ storageKey: string;
182
+ priority?: number;
183
+ name?: string;
184
+ storage?: Storage | null; // default localStorage if available
185
+ }): FeatureSource<K> {
186
+ const { storageKey, priority = 0, name = "storage", storage } = opts;
187
+ return {
188
+ name,
189
+ priority,
190
+ load: () => {
191
+ try {
192
+ const s =
193
+ storage ??
194
+ (typeof window !== "undefined" ? window.localStorage : null);
195
+ const raw = s?.getItem(storageKey);
196
+ return raw ? (JSON.parse(raw) as FlagMap<K>) : {};
197
+ } catch (e) {
198
+ console.warn(`[FeatureConfig] storageSource load error`, e);
199
+ return {};
200
+ }
201
+ },
202
+ };
203
+ }
204
+
205
+ function remoteSource<K extends string>(opts: {
206
+ fetch: (ctx: {
207
+ abortSignal?: AbortSignal;
208
+ }) => Promise<Record<string, boolean>>;
209
+ transform?: (raw: Record<string, boolean>) => FlagMap<K>;
210
+ priority?: number;
211
+ name?: string;
212
+ }): FeatureSource<K> {
213
+ const { fetch, transform, priority = 10, name = "remote" } = opts;
214
+ return {
215
+ name,
216
+ priority,
217
+ load: async ({ abortSignal }) => {
218
+ const raw = await fetch({ abortSignal });
219
+ return transform ? transform(raw) : (raw as FlagMap<K>);
220
+ },
221
+ };
222
+ }
223
+
224
+ // -----------------------------
225
+ // Factory – FeatureConfig
226
+ // -----------------------------
227
+ export function FeatureConfig<
228
+ E extends EnumLike,
229
+ K extends string = InferKeys<E> & string
230
+ >(options: FeatureOptions<E, K>) {
231
+ const allKeys = normalizeEnum(options.keys);
232
+ const strict: StrictMode = options.strict ?? "warn";
233
+ const strategy: MergeStrategy<K> = options.strategy ?? "last-wins";
234
+
235
+ // Context shape
236
+ type Ctx = {
237
+ flags: FlagMap<K>;
238
+ loading: boolean;
239
+ refresh: () => Promise<void>;
240
+ setFlags: (patch: FlagMap<K> | ((prev: FlagMap<K>) => FlagMap<K>)) => void;
241
+ isEnabled: (k: K) => boolean;
242
+ };
243
+
244
+ const FeatureContext = createContext<Ctx | null>(null);
245
+
246
+ function FeatureProvider({ children }: { children: ReactNode }) {
247
+ const [flags, _setFlags] = useState<FlagMap<K>>(options.initialFlags ?? {});
248
+ const [loading, setLoading] = useState<boolean>(true);
249
+ const sourcesSorted = useMemo(
250
+ () =>
251
+ (options.sources ?? [])
252
+ .map((s, i) => ({
253
+ i,
254
+ s,
255
+ priority: s.priority ?? 0,
256
+ name: s.name ?? `source_${i}`,
257
+ }))
258
+ .sort((a, b) => a.priority - b.priority),
259
+ // eslint-disable-next-line react-hooks/exhaustive-deps
260
+ []
261
+ );
262
+
263
+ const safeSetFlags = (
264
+ next: FlagMap<K> | ((prev: FlagMap<K>) => FlagMap<K>)
265
+ ) => {
266
+ _setFlags((prev) => {
267
+ const nextFlags =
268
+ typeof next === "function" ? (next as any)(prev) : next;
269
+ return nextFlags;
270
+ });
271
+ };
272
+
273
+ const refresh = async (): Promise<void> => {
274
+ if (!sourcesSorted.length) {
275
+ // even without sources, enforce key filtering on initial flags
276
+ setLoading(false);
277
+ return;
278
+ }
279
+ setLoading(true);
280
+ const ctrl = new AbortController();
281
+ const collected: { name: string; flags: FlagMap<K> }[] = [];
282
+
283
+ try {
284
+ for (const { s, name } of sourcesSorted) {
285
+ try {
286
+ const data = await s.load({ abortSignal: ctrl.signal });
287
+ const filtered = filterUnknownKeys<K>(allKeys, data, strict, name);
288
+ collected.push({ name, flags: filtered });
289
+ } catch (e) {
290
+ console.warn(`[FeatureConfig] Source "${name}" load failed`, e);
291
+ collected.push({ name, flags: {} });
292
+ }
293
+ }
294
+ const merged = mergeFlags<K>(collected, strategy);
295
+ // also filter initial flags/options
296
+ const initialFiltered = filterUnknownKeys<K>(
297
+ allKeys,
298
+ flags,
299
+ strict,
300
+ "initial"
301
+ );
302
+ _setFlags({ ...initialFiltered, ...merged });
303
+ } finally {
304
+ setLoading(false);
305
+ }
306
+ };
307
+
308
+ useEffect(() => {
309
+ // On mount: validate initial flags & then fetch from sources
310
+ const initialFiltered = filterUnknownKeys<K>(
311
+ allKeys,
312
+ options.initialFlags ?? {},
313
+ strict,
314
+ "initial"
315
+ );
316
+ if (Object.keys(initialFiltered).length) _setFlags(initialFiltered);
317
+ // Fetch async sources
318
+ refresh();
319
+ // eslint-disable-next-line react-hooks/exhaustive-deps
320
+ }, []);
321
+
322
+ const isEnabled = (k: K) => Boolean(flags[k]);
323
+
324
+ const ctx: Ctx = {
325
+ flags,
326
+ loading,
327
+ refresh,
328
+ setFlags: safeSetFlags,
329
+ isEnabled,
330
+ };
331
+
332
+ return (
333
+ <FeatureContext.Provider value={ctx}>{children}</FeatureContext.Provider>
334
+ );
335
+ }
336
+
337
+ // Hooks & Components
338
+ function useFlags() {
339
+ const ctx = useContext(FeatureContext);
340
+ if (!ctx) throw new Error("useFlags must be used inside FeatureProvider");
341
+ return ctx;
342
+ }
343
+
344
+ function useFeature(key: K) {
345
+ const { isEnabled, loading } = useFlags();
346
+ return { enabled: isEnabled(key), loading };
347
+ }
348
+
349
+ type GateProps = {
350
+ feature: K;
351
+ fallback?: ReactNode;
352
+ loadingFallback?: ReactNode;
353
+ children: ReactNode;
354
+ };
355
+
356
+ function Feature({ feature, fallback = null, loadingFallback = null, children }: GateProps) {
357
+ const { enabled, loading } = useFeature(feature);
358
+ if (loading) return <>{loadingFallback}</>;
359
+ return <>{enabled ? children : fallback}</>;
360
+ }
361
+
362
+ function FeatureLock({ feature, fallback = null, loadingFallback = null, children }: GateProps) {
363
+ const { enabled, loading } = useFeature(feature);
364
+ if (loading) return <>{loadingFallback}</>;
365
+ return <>{enabled ? fallback : children}</>;
366
+ }
367
+
368
+ // public helpers (instance-scoped)
369
+ const api = {
370
+ FeatureProvider,
371
+ useFeature,
372
+ useFlags,
373
+ Feature,
374
+ FeatureLock,
375
+ refresh: () => {
376
+ const ctx = (FeatureContext as any)._currentValue as ReturnType<
377
+ typeof useFlags
378
+ > | null;
379
+ // Note: in strict React this isn't guaranteed across roots; prefer calling useFlags() in components.
380
+ if (ctx?.refresh) return ctx.refresh();
381
+ return Promise.resolve();
382
+ },
383
+ setFlags: (patch: FlagMap<K> | ((prev: FlagMap<K>) => FlagMap<K>)) => {
384
+ const ctx = (FeatureContext as any)._currentValue as ReturnType<
385
+ typeof useFlags
386
+ > | null;
387
+ if (ctx?.setFlags) ctx.setFlags(patch);
388
+ },
389
+ sources: {
390
+ local: localSource<K>,
391
+ storage: storageSource<K>,
392
+ remote: remoteSource<K>,
393
+ },
394
+ } as const;
395
+
396
+ return api;
397
+ }
398
+
399
+ // feature-config-builder
400
+ /**
401
+ * automatically create feature defaults from enum
402
+ * @param keysEnum - enum of feature keys
403
+ * @param manualDefaults - manual overrides for defaults
404
+ * @param globalDefault - global default for all features (default: true)
405
+ * @returns - feature defaults object
406
+ */
407
+ export function createFeatureDefaults<
408
+ T extends Record<string, string> | Record<string, number>
409
+ >(
410
+ keysEnum: T,
411
+ manualDefaults: Partial<Record<T[keyof T], boolean>> = {},
412
+ globalDefault: boolean = true
413
+ ): Record<T[keyof T], boolean> {
414
+ const keys = Object.values(keysEnum) as T[keyof T][];
415
+ const defaults: Record<T[keyof T], boolean> = {} as any;
416
+
417
+ for (const key of keys) {
418
+ defaults[key] = globalDefault;
419
+ }
420
+
421
+ // merge manual override
422
+ return { ...defaults, ...manualDefaults };
423
+ }
424
+
425
+ // -----------------------------
426
+ // Example enum (you can delete this from your build and place in a separate file)
427
+ // -----------------------------
428
+ // export enum FeatureKey {
429
+ // NewDashboard = "newDashboard",
430
+ // BetaButton = "betaButton",
431
+ // AiAssistant = "aiAssistant",
432
+ // }