@ozsarman/clarityjs 0.6.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.
@@ -0,0 +1,366 @@
1
+ /**
2
+ * Clarity.js — AI-Native UI Framework
3
+ * TypeScript ambient declarations for the compiler/runtime API.
4
+ *
5
+ * The .clarity DSL is compiled to vanilla JS by the Clarity compiler.
6
+ * These types describe the public API of the compiler and runtime that
7
+ * TypeScript users interact with directly (e.g., calling compile() or
8
+ * mounting components from JS/TS code).
9
+ */
10
+
11
+ // ─── Runtime Types ────────────────────────────────────────────────────────────
12
+
13
+ /** A reactive value container. Read with .get(), write with .set(). */
14
+ export interface Signal<T> {
15
+ get(): T;
16
+ set(value: T): void;
17
+ update(fn: (current: T) => T): void;
18
+ peek(): T;
19
+ readonly _type: 'signal';
20
+ readonly _subscribers: Set<unknown>;
21
+ }
22
+
23
+ /** A read-only derived value. Recomputes lazily when dependencies change. */
24
+ export interface Computed<T> {
25
+ get(): T;
26
+ peek(): T;
27
+ readonly _type: 'computed';
28
+ }
29
+
30
+ /** The AI contract attached to a mounted component's root DOM element. */
31
+ export interface AIContract {
32
+ /** Component name for display / debugging. */
33
+ component: string;
34
+ /** Getters for each declared-readable signal. */
35
+ readable: Record<string, () => unknown>;
36
+ /** Returns a plain-object snapshot of all readable state. */
37
+ snapshot(): Record<string, unknown>;
38
+ /** Callable actions declared safe for AI agents. */
39
+ actionable: Record<string, (...args: unknown[]) => unknown>;
40
+ /** Invoke a declared action by name. */
41
+ act(name: string, ...args: unknown[]): unknown;
42
+ /** Names that AI agents must never read or modify. */
43
+ forbidden: string[];
44
+ }
45
+
46
+ // ─── Runtime API ─────────────────────────────────────────────────────────────
47
+
48
+ /** Create a reactive signal with an initial value. */
49
+ export declare function signal<T>(initialValue: T): Signal<T>;
50
+
51
+ /** Run a function reactively; re-runs whenever its signal dependencies change. */
52
+ export declare function effect(fn: () => void | (() => void)): () => void;
53
+
54
+ /** Create a read-only computed value derived from other signals. */
55
+ export declare function computed<T>(fn: () => T): Computed<T>;
56
+
57
+ /** Batch multiple signal writes — effects flush once after all updates. */
58
+ export declare function batch(fn: () => void): void;
59
+
60
+ /** A Clarity component function: receives props, returns a DOM element. */
61
+ export type ClarityComponent<P extends Record<string, unknown> = Record<string, unknown>> =
62
+ (props?: P) => HTMLElement;
63
+
64
+ /** Mount a Clarity component into a container DOM element. */
65
+ export declare function mount<P extends Record<string, unknown> = Record<string, unknown>>(
66
+ ComponentFn: ClarityComponent<P>,
67
+ container: HTMLElement,
68
+ props?: P
69
+ ): () => void;
70
+
71
+ // ─── Hydration API ─────────────────────────────────────────────────────────────
72
+
73
+ export interface HydrateRootResult {
74
+ /** Remove the component, dispose all effects, and clean up the container. */
75
+ unmount: () => void;
76
+ }
77
+
78
+ /**
79
+ * Attach client-side reactivity to server-rendered HTML.
80
+ *
81
+ * Call this on the client entry point after importing the same component
82
+ * that was used in `renderToDocument()` on the server.
83
+ *
84
+ * @example
85
+ * ```ts
86
+ * import { hydrateRoot } from '@ozsarman/clarityjs/hydrate';
87
+ * import { App } from './App.js';
88
+ *
89
+ * hydrateRoot(document.getElementById('app')!, App);
90
+ * ```
91
+ */
92
+ export declare function hydrateRoot<P extends Record<string, unknown> = Record<string, unknown>>(
93
+ container: Element,
94
+ ComponentFn: ClarityComponent<P>,
95
+ props?: Omit<P, '__ssr'>
96
+ ): HydrateRootResult;
97
+
98
+ /**
99
+ * Returns true if the given container has already been hydrated by Clarity.
100
+ */
101
+ export declare function isHydrated(container: Element | null | undefined): boolean;
102
+
103
+ /**
104
+ * Read the server-serialised state object (`window.__CLARITY_DATA__`).
105
+ * Returns an empty object when not available (client-only render, test env).
106
+ */
107
+ export declare function getSSRData(): Record<string, unknown>;
108
+
109
+ // ─── Error Boundary ────────────────────────────────────────────────────────────
110
+
111
+ export interface ErrorBoundaryOptions {
112
+ /**
113
+ * Child component factory — called to produce the protected content.
114
+ * Any error thrown here is caught by the boundary.
115
+ */
116
+ children: (() => HTMLElement | Node) | HTMLElement | Node;
117
+ /**
118
+ * Fallback UI to display when an error is caught.
119
+ * Can be a function that receives the Error and returns a Node,
120
+ * or a static Node. Defaults to a styled dev-friendly error box.
121
+ */
122
+ fallback?: ((error: Error) => Node) | Node;
123
+ /**
124
+ * Called when an error is caught. Use for logging or telemetry.
125
+ */
126
+ onError?: (error: Error) => void;
127
+ }
128
+
129
+ export interface ErrorBoundaryElement extends HTMLElement {
130
+ /** Re-renders the child content, clearing any caught error state. */
131
+ reset(): void;
132
+ /** Returns the currently caught Error, or null when healthy. */
133
+ getError(): Error | null;
134
+ }
135
+
136
+ /**
137
+ * Wrap a component subtree in an error boundary.
138
+ * Errors thrown during the child's initial render are caught and the
139
+ * fallback UI is shown instead of crashing the page.
140
+ *
141
+ * @example
142
+ * ```ts
143
+ * mount(
144
+ * () => ErrorBoundary({
145
+ * children: () => MyComponent({ data }),
146
+ * fallback: (err) => h('p', {}, [err.message]),
147
+ * onError: (err) => logger.error(err),
148
+ * }),
149
+ * document.getElementById('app')!
150
+ * );
151
+ * ```
152
+ */
153
+ export declare function ErrorBoundary(options: ErrorBoundaryOptions): ErrorBoundaryElement;
154
+
155
+ /**
156
+ * Query the AI contract for a DOM element (or its nearest Clarity ancestor).
157
+ * Returns null if the element has no Clarity component above it.
158
+ */
159
+ export declare function aiQuery(element: Element): AIContract | null;
160
+
161
+ /** Returns all live Clarity components that have declared AI contracts. */
162
+ export declare function aiDiscover(): AIContract[];
163
+
164
+ // ─── Compiler API ─────────────────────────────────────────────────────────────
165
+
166
+ export interface CompileOptions {
167
+ /** Filename used in error messages and source maps. */
168
+ filename?: string;
169
+ /** Runtime import path (default: './clarity-runtime.js'). */
170
+ runtimePath?: string;
171
+ /** Emit a V3 source map inline in the output. */
172
+ sourceMap?: boolean;
173
+ /** Generate TypeScript .d.ts declarations. */
174
+ types?: boolean;
175
+ }
176
+
177
+ export interface CompileResult {
178
+ /** Generated JavaScript source. */
179
+ code: string;
180
+ /** V3 source map (populated when sourceMap: true). */
181
+ map: object | null;
182
+ /** TypeScript declaration string (populated when types: true). */
183
+ dts: string;
184
+ /** Parsed AST (useful for tooling). */
185
+ ast: object;
186
+ /** Flat token array (useful for tooling). */
187
+ tokens: unknown[];
188
+ }
189
+
190
+ /** Compile a .clarity source string to JavaScript. */
191
+ export declare function compile(source: string, options?: CompileOptions): CompileResult;
192
+
193
+ /** Tokenize a .clarity source string (returns the raw token array). */
194
+ export declare function tokenize(source: string, filename?: string): unknown[];
195
+
196
+ /** Parse a token array to an AST. */
197
+ export declare function parse(tokens: unknown[], source?: string): object;
198
+
199
+ // ─── Async State — createQuery / createMutation ────────────────────────────────
200
+
201
+ export interface CreateQueryOptions<T = unknown> {
202
+ /** Set false to disable auto-fetch on creation (default: true). */
203
+ enabled?: boolean;
204
+ /** Milliseconds until cached data is considered stale (default: 30 000). */
205
+ staleTime?: number;
206
+ /** Milliseconds to keep data in the global cache after last use (default: 300 000). */
207
+ cacheTime?: number;
208
+ /** Number of automatic retries on fetch failure (default: 3). */
209
+ retry?: number;
210
+ /** Fixed delay in ms, or a function `(attempt) => ms` for exponential back-off. */
211
+ retryDelay?: number | ((attempt: number) => number);
212
+ /** Re-fetch when the browser tab regains focus (default: true). */
213
+ refetchOnWindowFocus?: boolean;
214
+ /** Polling interval in ms; false/0 to disable (default: false). */
215
+ refetchInterval?: number | false;
216
+ /** Called after a successful fetch. */
217
+ onSuccess?: (data: T, key: string) => void;
218
+ /** Called after all retries are exhausted. */
219
+ onError?: (error: Error, key: string) => void;
220
+ /** Pre-seed data — skips the first network fetch. */
221
+ initialData?: T;
222
+ /** When true, `invalidate()` triggers an immediate background refetch (default: true). */
223
+ invalidateImmediately?: boolean;
224
+ }
225
+
226
+ /** Reactive data-fetching state object returned by `createQuery`. */
227
+ export interface QueryState<T = unknown> {
228
+ /** Current data — `undefined` while the first fetch is in-flight. */
229
+ readonly data: Signal<T | undefined>;
230
+ /** Current error — `null` when healthy. */
231
+ readonly error: Signal<Error | null>;
232
+ /** `true` while the initial (loading) fetch is in-flight. */
233
+ readonly loading: Signal<boolean>;
234
+ /** `true` when serving stale cached data while a background refetch is running. */
235
+ readonly stale: Signal<boolean>;
236
+ /** Force a refetch regardless of cache freshness. */
237
+ refetch(): Promise<void>;
238
+ /**
239
+ * Optimistic update — set data immediately, optionally revalidate.
240
+ * @param updater New value, or an updater function `(current) => next`.
241
+ */
242
+ mutate(updater: T | ((current: T | undefined) => T), options?: { revalidate?: boolean }): void;
243
+ /** Mark as stale and optionally trigger a background refetch. */
244
+ invalidate(): void;
245
+ /** Best-effort cancellation of any in-flight request. */
246
+ cancel(): void;
247
+ /** Dispose timers and event listeners. Call when unmounting. */
248
+ dispose(): void;
249
+ }
250
+
251
+ /**
252
+ * Create a reactive, cached, auto-fetching data source.
253
+ *
254
+ * @param key Cache key string, or a reactive function `() => string` that
255
+ * re-fetches automatically when the returned key changes.
256
+ * @param fetchFn Async function that receives the resolved key and returns data.
257
+ * @param options Optional configuration.
258
+ *
259
+ * @example
260
+ * ```ts
261
+ * const todos = createQuery(
262
+ * 'todos',
263
+ * () => fetch('/api/todos').then(r => r.json()),
264
+ * { staleTime: 60_000 }
265
+ * );
266
+ *
267
+ * // Dynamic key — re-fetches when userId signal changes
268
+ * const post = createQuery(
269
+ * () => `post/${userId.get()}`,
270
+ * (key) => fetch(`/api/${key}`).then(r => r.json())
271
+ * );
272
+ * ```
273
+ */
274
+ export declare function createQuery<T = unknown>(
275
+ key: string | (() => string),
276
+ fetchFn: (key: string) => Promise<T>,
277
+ options?: CreateQueryOptions<T>
278
+ ): QueryState<T>;
279
+
280
+ export interface CreateMutationOptions<T = unknown, V = unknown> {
281
+ /** Called after a successful mutation. */
282
+ onSuccess?: (data: T, variables: V) => void;
283
+ /** Called when the mutation throws. */
284
+ onError?: (error: Error, variables: V) => void;
285
+ /** Called after either success or failure. */
286
+ onSettled?: (data: T | null, error: Error | null, variables: V) => void;
287
+ /** Query key(s) to invalidate on success. */
288
+ invalidates?: string | string[];
289
+ }
290
+
291
+ export interface MutationState<T = unknown, V = unknown> {
292
+ /** Execute the mutation. Throws on failure so callers can catch. */
293
+ mutate(variables: V): Promise<T>;
294
+ /** `true` while the mutation is in-flight. */
295
+ readonly loading: Signal<boolean>;
296
+ /** Current error — `null` when healthy. */
297
+ readonly error: Signal<Error | null>;
298
+ /** Data returned by the last successful mutation. */
299
+ readonly data: Signal<T | undefined>;
300
+ /** Reset loading/error/data to their initial states. */
301
+ reset(): void;
302
+ }
303
+
304
+ /**
305
+ * Create a reactive mutation for POST / PUT / DELETE operations.
306
+ *
307
+ * @example
308
+ * ```ts
309
+ * const addTodo = createMutation(
310
+ * (title: string) =>
311
+ * fetch('/api/todos', { method: 'POST', body: JSON.stringify({ title }) })
312
+ * .then(r => r.json()),
313
+ * { invalidates: 'todos' }
314
+ * );
315
+ *
316
+ * await addTodo.mutate('Buy milk');
317
+ * ```
318
+ */
319
+ export declare function createMutation<T = unknown, V = unknown>(
320
+ mutateFn: (variables: V) => Promise<T>,
321
+ options?: CreateMutationOptions<T, V>
322
+ ): MutationState<T, V>;
323
+
324
+ // ─── Developer Tools ────────────────────────────────────────────────────────────
325
+
326
+ export interface DevtoolsOptions {
327
+ /** Whether the panel starts in the open/visible state (default: false). */
328
+ open?: boolean;
329
+ /** Corner to anchor the panel: 'tl' | 'tr' | 'bl' | 'br' (default: 'br'). */
330
+ position?: 'tl' | 'tr' | 'bl' | 'br';
331
+ /** DOM element to mount the devtools into (default: document.body). */
332
+ container?: HTMLElement;
333
+ }
334
+
335
+ export interface DevtoolsHandle {
336
+ /** Remove the devtools overlay from the DOM and clean up all listeners. */
337
+ dispose(): void;
338
+ }
339
+
340
+ /**
341
+ * Mount the Clarity in-page developer tools overlay.
342
+ *
343
+ * The panel is encapsulated in a Shadow DOM so it never conflicts with
344
+ * the host page's styles. Keyboard shortcut: Ctrl+Shift+D / ⌘⇧D.
345
+ *
346
+ * This is a no-op in environments without `document` (SSR / Node.js).
347
+ *
348
+ * @example
349
+ * ```ts
350
+ * import { initDevtools } from '@ozsarman/clarityjs/devtools';
351
+ * if (import.meta.env.DEV) initDevtools({ open: true });
352
+ * ```
353
+ */
354
+ export declare function initDevtools(options?: DevtoolsOptions): DevtoolsHandle;
355
+
356
+ /** Manually remove a query from the global cache by key. */
357
+ export declare function invalidateQuery(key: string): void;
358
+
359
+ /** Pre-populate the global cache (useful for SSR → client state transfer). */
360
+ export declare function prefetchQuery<T = unknown>(key: string, data: T): void;
361
+
362
+ /** Clear the entire query cache. */
363
+ export declare function clearQueryCache(): void;
364
+
365
+ /** Read a raw cache entry (useful for testing or SSR state inspection). */
366
+ export declare function getCacheEntry<T = unknown>(key: string): { data: T; timestamp: number } | undefined;