@close-by/clay 0.2.6 → 0.2.8

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,278 @@
1
+ # Getting started
2
+
3
+ ## Requirements
4
+
5
+ - [Bun](https://bun.sh/) 1.1+ (runtime for `clay` and app code)
6
+ - Node-compatible OS (macOS / Linux / WSL)
7
+ - For local tarball installs: npm (links co-installed `file:` packages; Bun may try the registry for rewritten versions)
8
+
9
+ ## Outside the monorepo
10
+
11
+ Consumers import `@close-by/clay` and run apps with the `clay` binary from `@close-by/clay-cli`. The CLI ships prebuilt Vite assets (`client-dist`); you do **not** need `@close-by/clay-client` or a monorepo `build:client`.
12
+
13
+ Walkthrough (tasks list → two pages + `--app`): [Sample app](./tutorial.md).
14
+
15
+ ### From local packs (this repo)
16
+
17
+ ```bash
18
+ # In the Clay checkout:
19
+ bun install
20
+ bun run pack:publishable # build:client + bun pm pack → dist-pack/*.tgz
21
+ # workspace:* is rewritten to real versions inside each tarball
22
+
23
+ # In your app directory:
24
+ npm install \
25
+ /path/to/clay/dist-pack/close-by-clay-core-0.1.0.tgz \
26
+ /path/to/clay/dist-pack/close-by-clay-auth-0.1.0.tgz \
27
+ /path/to/clay/dist-pack/close-by-clay-compiler-0.1.0.tgz \
28
+ /path/to/clay/dist-pack/close-by-clay-persistence-file-0.1.0.tgz \
29
+ /path/to/clay/dist-pack/close-by-clay-components-0.1.0.tgz \
30
+ /path/to/clay/dist-pack/close-by-clay-server-0.1.0.tgz \
31
+ /path/to/clay/dist-pack/close-by-clay-0.1.0.tgz \
32
+ /path/to/clay/dist-pack/close-by-clay-cli-0.1.0.tgz
33
+ ```
34
+
35
+ Install **all** runtime tarballs together so `@close-by/clay` and `@close-by/clay-*@0.1.0` resolve from the co-installed packs (they are not on the public registry yet).
36
+
37
+ ### When published to npm
38
+
39
+ ```bash
40
+ bun add @close-by/clay-cli @close-by/clay
41
+ # transitive: core, components, server, persistence-file
42
+ # optional: bun add @close-by/clay-auth
43
+ ```
44
+
45
+ ### Publishing to npm (maintainers)
46
+
47
+ Runtime packages are scoped (`@close-by/clay` and `@close-by/clay-*`) and public (`publishConfig.access: public`). Publish **in dependency order** so the registry can resolve rewritten versions:
48
+
49
+ 1. `@close-by/clay-core`
50
+ 2. `@close-by/clay-auth`
51
+ 3. `@close-by/clay-compiler`
52
+ 4. `@close-by/clay-persistence-file`
53
+ 5. `@close-by/clay-components`
54
+ 6. `@close-by/clay-server`
55
+ 7. `@close-by/clay`
56
+ 8. `@close-by/clay-cli`
57
+
58
+ ```bash
59
+ # In the Clay checkout:
60
+ bun install
61
+ npm login # once; needs rights on the @close-by org
62
+
63
+ bun run publish:dry # pack + validate + npm publish --dry-run (no upload)
64
+ bun run publish:npm # pack + validate + real npm publish (same order)
65
+ # Re-run is safe: versions already on npm are skipped.
66
+ ```
67
+
68
+ Equivalent manual flow from packed tarballs:
69
+
70
+ ```bash
71
+ bun run pack:publishable
72
+ npm publish ./dist-pack/close-by-clay-core-0.1.0.tgz --access public
73
+ npm publish ./dist-pack/close-by-clay-auth-0.1.0.tgz --access public
74
+ npm publish ./dist-pack/close-by-clay-compiler-0.1.0.tgz --access public
75
+ npm publish ./dist-pack/close-by-clay-persistence-file-0.1.0.tgz --access public
76
+ npm publish ./dist-pack/close-by-clay-components-0.1.0.tgz --access public
77
+ npm publish ./dist-pack/close-by-clay-server-0.1.0.tgz --access public
78
+ npm publish ./dist-pack/close-by-clay-0.1.0.tgz --access public
79
+ npm publish ./dist-pack/close-by-clay-cli-0.1.0.tgz --access public
80
+ ```
81
+
82
+ `publish:dry` checks each tarball for rewritten deps (no `workspace:*`), `license` / README / LICENSE, CLI `bin` + `client-dist`, `@close-by/clay` `docs/` (via `prepack` → `scripts/sync-docs.ts`), then runs `npm publish --dry-run`. `publish:npm` looks up each `name@version` on the registry and **skips** if it is already published (resume after a partial failure). Use `--skip-pack` to reuse an existing `dist-pack/`: `bun scripts/publish-from-pack.ts --dry-run --skip-pack`.
83
+
84
+ After install, version-matched docs live at `node_modules/@close-by/clay/docs/` (import paths like `@close-by/clay/docs/api.md` with Bun, or `resolveClayDocsDir` from `@close-by/clay/docs-dir`).
85
+
86
+ ### Run
87
+
88
+ ```typescript
89
+ // hello.ts
90
+ import { ui } from '@close-by/clay';
91
+
92
+ export default function () {
93
+ ui.label('Hello Clay');
94
+ ui.button('Ping', { onClick: () => ui.notify('hi', 'success') });
95
+ }
96
+ ```
97
+
98
+ ```bash
99
+ bunx clay hello.ts # → http://localhost:3000 (opens browser)
100
+ ```
101
+
102
+ `ui.run(() => { … })` also works; CLI `-p` / `--title` / `--app` apply when the entry uses a default export or `ui.page` and lets the CLI start the server.
103
+
104
+ ## Three-line app (`clay`) — monorepo
105
+
106
+ ```typescript
107
+ // hello.ts
108
+ import { ui } from '@close-by/clay';
109
+
110
+ ui.run(() => {
111
+ ui.label('Hello Clay');
112
+ ui.button('Ping', { onClick: () => ui.notify('hi', 'success') });
113
+ });
114
+ ```
115
+
116
+ **This monorepo:** build the client once (also copies assets into `@close-by/clay-cli` for packaging), then use the workspace CLI:
117
+
118
+ ```bash
119
+ bun install
120
+ bun run build:client # Vite → packages/client/dist (+ copy → packages/cli/client-dist)
121
+ bun run clay hello.ts # → http://localhost:3000 (opens browser)
122
+ ```
123
+
124
+ The CLI prefers `packages/cli/client-dist` when present, otherwise falls back to `packages/client/dist` so local `bun run clay` / `demo:cli` work after a single `build:client`.
125
+
126
+ Or a default export (CLI registers `/` and starts the server for you):
127
+
128
+ ```typescript
129
+ import { ui } from '@close-by/clay';
130
+
131
+ export default function () {
132
+ ui.label('Hello Clay');
133
+ }
134
+ ```
135
+
136
+ ```bash
137
+ bun run clay hello.ts --port 4000
138
+ ```
139
+
140
+ ### Multi-page SPA with shell
141
+
142
+ ```bash
143
+ bun run clay ./pages --app --title "My App"
144
+ # loadPages + ui.run({ app: { title, nav: navFromPages() } })
145
+ ```
146
+
147
+ | Flag | Meaning |
148
+ |------|---------|
149
+ | `-p, --port` | Port (default `3000`) |
150
+ | `-t, --title` | HTML / shell title |
151
+ | `--app` | Dashboard shell + nav from discovered pages |
152
+ | `--no-open` | Do not open the browser |
153
+ | `--reload` | Restart on file changes (`bun --watch`); opens the browser once; prints `↻ clay: reloading…` and re-imports pages (clears `require.cache`) |
154
+ | `--reactive-let` | Enable compile-time reactive `let` Bun loader (**off** by default; still needs `// @clay-reactive` or `"use reactive";` in source) |
155
+ | `--no-reactive-let` | Disable the reactive-let plugin (default) |
156
+
157
+ See [`docs/reactive-let.md`](./reactive-let.md) for `ui.state` / `ui.auto` (recommended), `ui.label(() => …)`, and the opt-in Phase 2 `let` transform.
158
+
159
+ **Complex pages (Orders-scale):** use Phase 1 `ui.state` + `ui.auto`, or opt-in reactive `let` with the [region decision table](./reactive-let.md#decision-table-complex-pages) — who wraps `ui.auto` for top-level helpers, widget callbacks, inline UI, and handlers.
160
+
161
+ For production deployment, auth, and server exposure, see [Production & security](./production.md).
162
+
163
+ ## Install and run the demo
164
+
165
+ ```bash
166
+ bun install
167
+ bun run build:client # builds packages/client → dist, copies into @close-by/clay-cli
168
+ bun run demo # starts apps/demo on :4000
169
+ # or
170
+ bun run demo:cli # same examples via `clay … --app`
171
+ ```
172
+
173
+ Or in one step:
174
+
175
+ ```bash
176
+ bun run dev
177
+ ```
178
+
179
+ Open:
180
+
181
+ - http://localhost:4000 — home with links to examples
182
+ - http://localhost:4000/examples/counter
183
+ - http://localhost:4000/examples/todo
184
+ - http://localhost:4000/examples/form-demo
185
+ - http://localhost:4000/examples/auth
186
+
187
+ | Script | What it does |
188
+ |--------|----------------|
189
+ | `bun run build:client` | Vite production build of the React client + copy into `@close-by/clay-cli` |
190
+ | `bun run pack:publishable` | `build:client` + pack `@close-by/clay-core` … `@close-by/clay` … `@close-by/clay-cli` → `dist-pack/` |
191
+ | `bun run publish:dry` | Pack + validate tarballs + `npm publish --dry-run` (no registry upload) |
192
+ | `bun run publish:npm` | Pack + validate + `npm publish` in order (requires `npm login`) |
193
+ | `bun run demo` | Start demo server (`main.ts`; expects client already built) |
194
+ | `bun run demo:cli` | Start demo via `clay apps/demo/src/examples --app` (loads `_run.ts` auth config) |
195
+ | `bun run clay …` | CLI runtime (`@close-by/clay-cli`) |
196
+ | `bun run dev` | Build client, then start demo |
197
+ | `bun test` | Run package tests |
198
+
199
+ ### Packaging notes (maintainers)
200
+
201
+ - Source packages keep `workspace:*` for the monorepo; `bun pm pack` / publish rewrites them to the package version.
202
+ - `@close-by/clay-client` is **private**; only its Vite `dist` is copied into `@close-by/clay-cli` (`copy-client` / CLI `prepack`, also invoked by `build:client`).
203
+ - Root stays `private: true`. Each runtime package ships `license`, `README.md`, `LICENSE`, and `publishConfig.access: public`.
204
+ - Publish order: `core` → `auth` → `compiler` → `persistence-file` → `components` → `server` → `ui` → `cli` (see **Publishing to npm** above).
205
+
206
+ ## Library mode (multi-page entrypoint)
207
+
208
+ Create a page file and an entrypoint:
209
+
210
+ ```typescript
211
+ // pages/counter.ts
212
+ import { ui } from '@close-by/clay';
213
+
214
+ export const pageMeta = { label: 'Counter', icon: 'hash', order: 10 };
215
+
216
+ ui.page('/counter', () => {
217
+ let count = 0;
218
+ const label = ui.label(`Count: ${count}`).classes('text-2xl');
219
+
220
+ ui.row(() => {
221
+ ui.button('-', {
222
+ variant: 'destructive',
223
+ onClick: () => {
224
+ count--;
225
+ label.setText(`Count: ${count}`);
226
+ },
227
+ });
228
+ ui.button('+', {
229
+ onClick: () => {
230
+ count++;
231
+ label.setText(`Count: ${count}`);
232
+ },
233
+ });
234
+ }, { gap: 2 });
235
+ });
236
+ ```
237
+
238
+ ```typescript
239
+ // main.ts
240
+ import { ui } from '@close-by/clay';
241
+
242
+ await ui.loadPages(new URL('./pages', import.meta.url));
243
+
244
+ ui.run({
245
+ port: 4000,
246
+ title: 'My App',
247
+ css: './globals.css', // optional theme overrides
248
+ app: {
249
+ title: 'My App',
250
+ nav: ui.navFromPages(),
251
+ },
252
+ });
253
+ ```
254
+
255
+ Point `clientDir` with `resolveClayClientDir()` (or omit it — `ui.run` does the same) instead of hardcoding `node_modules/@close-by/clay-cli/client-dist`. Details: [Boot](./boot.md).
256
+
257
+ Use `css` to inject your own stylesheet after the built client styles. **Token overrides** (`--primary`, `--background`, …) always work. **Tailwind utilities** in page modules are auto-scanned by default (`clay` / `ui.run`) — see [Tailwind](./tailwind.md).
258
+
259
+ ## Project layout (monorepo)
260
+
261
+ ```
262
+ apps/demo/ Demo pages + server entry (`loadPages` + `ui.run({ app })`)
263
+ packages/cli/ `clay` binary — file/dir launcher + shipped client-dist
264
+ packages/ui/ NiceGUI-style ui facade (`loadPages`, `navFromPages`, `run`)
265
+ packages/core/ Element tree, session, page wrapper, reactive, storage (tab/user/app)
266
+ packages/auth/ Optional password hash, login limiter, requireAuth / requireRole, audit
267
+ packages/persistence-file/ File-backed PersistenceAdapter for storage.configure
268
+ packages/components/ Element factories (button, input, dataTable, areaChart / barChart / lineChart / pieChart / radarChart / radialChart, …)
269
+ packages/client/ React + ShadCN renderer (Sonner toasts, BoundDataTable, …) — private, not published
270
+ packages/server/ Bun HTTP + WebSocket
271
+ docs/ This documentation
272
+ ```
273
+
274
+ ## Next
275
+
276
+ - [Concepts](./concepts.md) — sessions, patches, refreshable
277
+ - [API](./api.md) — full `ui.*` reference
278
+ - [Examples](./examples.md) — demo catalog
@@ -0,0 +1,167 @@
1
+ # JSX investigation — context & recommendation
2
+
3
+ Status: **investigation complete** (2026-08). Spike B POC landed in `@close-by/clay-core/jsx-runtime` (experimental). Spike A deferred.
4
+
5
+ Related backlog: [TASKS.md](../TASKS.md) Later → Investigate `ui.jsx`. Historical HTMX-era note: [iterations/iteration-10.md](../iterations/iteration-10.md) (string `render()` → JSX; **superseded** by today’s Element + WebSocket model).
6
+
7
+ ---
8
+
9
+ ## Problem
10
+
11
+ Apps (notably UQ Hub) still build markup as **strings** and pass them to `ui.html`:
12
+
13
+ | Use (uq-hub) | Example |
14
+ |--------------|---------|
15
+ | Status dots | `` ui.html(`<span class="… rounded-full ${color}">`) `` |
16
+ | Sparkline bars | `` ui.html(`<div style="width:…;background:…">`) `` |
17
+ | SigNoz iframe | `` ui.html(`<iframe src="${url}" …>`) `` |
18
+ | Trace icon link | HTML `<a>` + inline SVG string |
19
+
20
+ Pain:
21
+
22
+ 1. No IDE structure / prop types / rename
23
+ 2. Easy XSS if values aren’t escaped
24
+ 3. Can’t compose real Clay components inside the blob
25
+ 4. Client path is `dangerouslySetInnerHTML` — no Clay events inside the string
26
+
27
+ Goal: **author markup as markup** with IDE support for tags + user-defined components — without inventing a second UI stack.
28
+
29
+ ---
30
+
31
+ ## Current architecture (constraints)
32
+
33
+ ```
34
+ Page builder (Bun) → Element tree → JSON mount/patch → ElementRenderer (React)
35
+ ```
36
+
37
+ - `new Element(type, props)` **attaches immediately** to `getCurrentParent()` via `attachToContext`.
38
+ - Layout uses **callback children**: `ui.row(props, () => { ui.button(…) })` so children run *after* the parent is on the stack.
39
+ - Standard JSX evaluates **children before parents**. That fights attach-on-construct unless we create nodes **detached** and **reparent**.
40
+
41
+ POC fix: `Element.adopt(child)` + `withDetached(() => new Element(…))` in `jsx()`.
42
+
43
+ ---
44
+
45
+ ## Spike A — JSX → static HTML → `ui.html`
46
+
47
+ ```tsx
48
+ ui.html(renderToStaticMarkup(
49
+ <div className="flex">
50
+ <span className="h-1.5 w-1.5 rounded-full bg-emerald-500" />
51
+ </div>
52
+ ));
53
+ ```
54
+
55
+ | Pros | Cons |
56
+ |------|------|
57
+ | Familiar HTML tags | Still opaque to Clay (one `html` node) |
58
+ | IDE for DOM + React-ish function components | **No** `onClick` / Clay handlers inside |
59
+ | Fits iframe / SVG blobs | Pulls React (or similar) into the **server** app, or a custom HTML serializer |
60
+ | | Duplicates styling (Tailwind must scan these modules too) |
61
+
62
+ **Verdict:** Only useful for true opaque HTML (iframe, third-party SVG). Most UQ Hub dots/bars should become Clay nodes instead. Prefer first-class `ui.iframe` over A for SigNoz embed.
63
+
64
+ ---
65
+
66
+ ## Spike B — JSX → Clay `Element` tree
67
+
68
+ ```tsx
69
+ /** @jsxImportSource @close-by/clay-core */
70
+ function StatusDot({ ok }: { ok: boolean }) {
71
+ return <badge text={ok ? 'up' : 'down'} size="xs" color={ok ? 'emerald' : 'red'} />;
72
+ }
73
+
74
+ ui.page('/', () => {
75
+ <row gap={2}>
76
+ <StatusDot ok={true} />
77
+ <button text="Refresh" icon="refresh-cw" onClick={() => void load()} />
78
+ </row>;
79
+ });
80
+ ```
81
+
82
+ | Pros | Cons |
83
+ |------|------|
84
+ | Same protocol / renderer / events | Intrinsics are Clay types (`button`, `row`), **not** HTML (`div`, `span`) |
85
+ | IDE + custom components with props | Children-first requires `adopt` (POC done) |
86
+ | Mix with `ui.*` under same parent | Layout/API mismatch: `ui.button(text, props)` vs `<button text props>` |
87
+ | Aligns with long-term DX | Need docs so people don’t expect DOM JSX |
88
+
89
+ **POC (landed):**
90
+
91
+ - `Element.adopt`
92
+ - `@close-by/clay-core/jsx-runtime` (`jsx` / `jsxs` / `Fragment`)
93
+ - Tests: `packages/core/src/jsx-runtime.test.ts`
94
+
95
+ Opt-in:
96
+
97
+ ```json
98
+ "compilerOptions": {
99
+ "jsx": "react-jsx",
100
+ "jsxImportSource": "@close-by/clay-core"
101
+ }
102
+ ```
103
+
104
+ (Use `.tsx` page modules; keep default Clay apps on `.ts` + `ui.*` until DX is documented. Resolves to `@close-by/clay-core/jsx-runtime`.)
105
+
106
+ ---
107
+
108
+ ## Mixing `ui.*` and JSX
109
+
110
+ | Pattern | Supported? |
111
+ |---------|------------|
112
+ | JSX inside `ui.row(() => { … })` | Yes — `jsx()` attaches to current parent |
113
+ | `ui.button` inside a function component used as `<Foo />` | Yes — if the component runs under `withParent` / current session |
114
+ | `<div>` HTML intrinsic | **No** (Spike B) — use Clay `container` / `row` / future `box`, or Spike A / `ui.html` |
115
+ | `ui.html` string escape hatch | Keep forever for trusted blobs |
116
+
117
+ ---
118
+
119
+ ## Trust model
120
+
121
+ | API | Trust |
122
+ |-----|--------|
123
+ | `ui.html(string)` | Trusted HTML only (XSS if user data interpolated) |
124
+ | Spike A static markup | Same as `ui.html` once stringified |
125
+ | Spike B Clay JSX | Props go through Element / protocol; handlers stay on server — **preferred** |
126
+
127
+ ---
128
+
129
+ ## Recommendation (phased)
130
+
131
+ ### Phase 0 — eliminate most strings without JSX (high ROI)
132
+
133
+ Ship small primitives that replace UQ Hub’s `ui.html` call sites:
134
+
135
+ 1. **`ui.iframe({ src, title?, className?, … })`** — SigNoz embed
136
+ 2. **`ui.dot({ color / tone, className? })`** or document `badge` + `size: 'xs'` as status chip (dots may still want a 6px circle — tiny `container`/`html` one-liner ok)
137
+ 3. Prefer **`ui.externalLink` / `ui.copyButton`** (already shipped) over HTML `<a>` icon buttons; SVG brand icons can stay `ui.html` until `ui.image` / icon upload exists
138
+
139
+ ### Phase 1 — productize Spike B (recommended north-star)
140
+
141
+ 1. Export jsx runtime from `@close-by/clay` as well (`jsxImportSource: "@close-by/clay"`) for one import path
142
+ 2. Document Clay intrinsics list + function-component rules in `docs/jsx.md`
143
+ 3. Demo page mixing `<row>` JSX with `ui.auto` / `ui.state`
144
+ 4. Consider `ui.jsx` as a **hyperscript alias** only if needed; automatic runtime is enough
145
+ 5. Do **not** default `jsxImportSource` in the Clay monorepo root (client already uses React JSX)
146
+
147
+ ### Phase 2 — Spike A only if still needed
148
+
149
+ If trusted SVG/iframe authoring remains painful after Phase 0–1, add optional `renderClayHtml(vnode)` **without** depending on React (small tag→string walker), feeding `ui.html`. Otherwise skip.
150
+
151
+ ---
152
+
153
+ ## Decision
154
+
155
+ | Spike | Decision |
156
+ |-------|----------|
157
+ | **B (Clay JSX)** | **Pursue** — POC in core; next = docs + clay re-export + demo |
158
+ | **A (SSR HTML)** | **Defer / maybe skip** — replaced by `ui.iframe` + Clay nodes for hub cases |
159
+ | **`ui.html(string)`** | **Keep** as escape hatch |
160
+
161
+ ---
162
+
163
+ ## Open questions
164
+
165
+ 1. Should Clay intrinsics use DOM-like names (`div` → `container`) for familiarity, or stay honest (`container` / `row`)?
166
+ 2. TypeScript JSX intrinsic elements (`JSX.IntrinsicElements`) for autocomplete of `button` / `row` props?
167
+ 3. Can `ui.auto(() => <row>…</row>)` be the documented reactive pattern once B is stable?
package/docs/kibana.md ADDED
@@ -0,0 +1,138 @@
1
+ # Kibana (`@close-by/clay-kibana`)
2
+
3
+ Authenticated (or internal) Kibana REST client: arbitrary API calls, Elasticsearch via console proxy or `/elasticsearch`, and `search_after` pagination for walking every matching log line.
4
+
5
+ ```typescript
6
+ import { Kibana } from '@close-by/clay-kibana';
7
+
8
+ // Modern Kibana (console proxy)
9
+ const kbn = new Kibana({
10
+ baseUrl: 'https://kibana.example.com',
11
+ apiKey: process.env.KIBANA_API_KEY!, // or username + password
12
+ space: 'sales', // optional
13
+ });
14
+
15
+ // Kibana 6.x style (same as `…/elasticsearch/{index}/_search`)
16
+ const logs = new Kibana({
17
+ baseUrl: 'https://kibana.internal.factsandit.pt',
18
+ kbnVersion: '6.8.0',
19
+ esTransport: 'path', // → /elasticsearch/…
20
+ });
21
+ ```
22
+
23
+ ## Auth & headers
24
+
25
+ | Option | Description |
26
+ |--------|-------------|
27
+ | `apiKey` | `Authorization: ApiKey …` (optional) |
28
+ | `username` + `password` | `Authorization: Basic …` (optional) |
29
+ | `kbnVersion` | Sets `kbn-version` (needed on older Kibana ES proxies) |
30
+ | `space` | Prefixes Kibana API paths with `/s/{space}` |
31
+ | `headers` | Extra headers on every request |
32
+ | `fetch` | Inject a custom `fetch` (tests) |
33
+
34
+ Every request also sends `kbn-xsrf: true`.
35
+
36
+ ## Elasticsearch transport
37
+
38
+ | `esTransport` | Behavior |
39
+ |---------------|----------|
40
+ | `'console'` (default) | `POST /api/console/proxy?path=…&method=…` |
41
+ | `'path'` | `{esPathPrefix}/{index}/_search` (default prefix `/elasticsearch`) |
42
+
43
+ ```typescript
44
+ const result = await logs.search('logstash-production-logback-*', {
45
+ query: { match_all: {} },
46
+ size: 10,
47
+ });
48
+
49
+ await logs.es('GET', '_cluster/health');
50
+ ```
51
+
52
+ ## Paginate every hit (`search_after`)
53
+
54
+ ```typescript
55
+ for await (const hit of logs.paginateSearch(index, body, { pageSize: 1000 })) {
56
+ // …
57
+ }
58
+ ```
59
+
60
+ ## Guided query builder
61
+
62
+ Chain filters instead of hand-writing Elasticsearch JSON:
63
+
64
+ ```typescript
65
+ type LogLine = { '@timestamp': string; message: string };
66
+
67
+ for await (const hit of logs
68
+ .query<LogLine>('logstash-production-logback-*')
69
+ .lastDays(1) // or .between(start, end)
70
+ .match('*receivePartnerEvent*')
71
+ .exclude('*ag_ibersol_webhook_notify_queue*')
72
+ .select('@timestamp', 'message')
73
+ .pageSize(1000)
74
+ .stream()) {
75
+ console.log(hit._source?.message);
76
+ }
77
+
78
+ // Or collect sources
79
+ const lines = await logs
80
+ .query<LogLine>('logstash-production-logback-*')
81
+ .between(start, end)
82
+ .match('*receivePartnerEvent*')
83
+ .select('@timestamp', 'message')
84
+ .sources();
85
+ ```
86
+
87
+ | Method | Purpose |
88
+ |--------|---------|
89
+ | `from` / `to` / `between` / `lastDays` | `@timestamp` range (override field with `timeField`) |
90
+ | `match(query, field?)` | `query_string` must (default field `message`) |
91
+ | `exclude(query, field?)` | `query_string` must_not |
92
+ | `term` / `where` / `whereNot` | Exact term or raw clauses |
93
+ | `select(...fields)` | `_source` filter |
94
+ | `pageSize` / `sort` | Pagination controls |
95
+ | `body()` | Inspect built ES JSON |
96
+ | `first()` / `stream()` / `all()` / `sources()` | Execute |
97
+ | `toParquet(path)` | Export all hits to a Parquet file (via DuckDB) |
98
+
99
+ ### Export to Parquet
100
+
101
+ ```typescript
102
+ const { path, rows } = await logs
103
+ .query('logstash-production-logback-*')
104
+ .lastDays(1)
105
+ .match('*receivePartnerEvent*')
106
+ .select('@timestamp', 'message')
107
+ .toParquet('./data/logs.parquet');
108
+
109
+ // Later in DuckDB:
110
+ // SELECT * FROM 'data/logs.parquet'
111
+ // or COPY / CREATE TABLE … AS SELECT * FROM 'data/logs.parquet'
112
+ ```
113
+
114
+ Options: optional `map(hit => row)` for a custom shape. By default each Parquet row is the document **`_source` only** (no `_id`, `_index`, `_type`, `_score`, or `sort`).
115
+
116
+ ```sql
117
+ SELECT message, "@timestamp" FROM 'data/logs.parquet'
118
+ ```
119
+
120
+ ## Raw Kibana REST
121
+
122
+ ```typescript
123
+ const spaces = await kbn.request('GET', '/api/spaces/space');
124
+ ```
125
+
126
+ Failures throw `KibanaError` with `status` and parsed `body`.
127
+
128
+ ## Saved Objects
129
+
130
+ ```typescript
131
+ const found = await kbn.findSavedObjects({
132
+ type: ['dashboard', 'visualization'],
133
+ search: 'Sales',
134
+ perPage: 20,
135
+ });
136
+
137
+ const dash = await kbn.getSavedObject('dashboard', 'my-dashboard-id');
138
+ ```
package/docs/llms.txt ADDED
@@ -0,0 +1,44 @@
1
+ # Clay
2
+
3
+ > Server-driven UI for TypeScript. Write imperative `ui.*` on the server; Clay owns a per-tab element tree and syncs it to a React + ShadCN client over WebSocket. Inspired by NiceGUI.
4
+
5
+ Clay is not a React app framework. Do not write React, JSX, or client components for screens. App code uses `@close-by/clay` (`ui.page`, `ui.run`, `ui.button`, …). The React client is a renderer.
6
+
7
+ Notes for agents:
8
+
9
+ - Public API and wire protocol are camelCase (`onClick`, `bindValue`, `setText`).
10
+ - Page builders run once per WebSocket session (per browser tab). `let` is per-tab, not shared.
11
+ - Update existing nodes with `setText` / `setValue` / bindings. Prefer `ui.state` + `ui.auto` (+ `ui.timer`) for async/live UIs; `ui.refreshable` for simple one-shot rebuilds. Shared or persisted state: `ui.storage` (`tab` / `user` / `app`).
12
+ - Never use `window` / `navigator` in page code — use `ui.clipboard`, `ui.setUrlHash`, `ui.openExternal`.
13
+ - Runtime is Bun. In this repo: `bun run build:client`, then `bun run demo` (http://localhost:4000) or `bun run clay hello.ts`.
14
+ - `ui.ai.*` is visual primitives only — no model or streaming runtime.
15
+
16
+ ## Docs
17
+
18
+ - [Human README](README.md): What Clay is, what you can build, try-it commands
19
+ - [Getting started](docs/getting-started.md): Install, `clay` CLI, first app, publishing
20
+ - [Boot](docs/boot.md): CLI vs library, `clientDir`, `_run.ts`, reload stubs
21
+ - [Tailwind](docs/tailwind.md): Scanned utilities + `ui.run({ css })`
22
+ - [Sample app](docs/tutorial.md): Step-by-step tasks list, then two pages + shell
23
+ - [Concepts](docs/concepts.md): Sessions, element tree, patches, refreshable, bindings, storage
24
+ - [Browser APIs](docs/browser-apis.md): `ui.clipboard` / hash / `openExternal` — no `window` in page code
25
+ - [Ops patterns](docs/ops-patterns.md): Dense consoles — chips, feeds, sensitive codeBlock, auth nav
26
+ - [API reference](docs/api.md): Complete `ui.*` and Element API
27
+ - [Elements](docs/elements.md): Wire types and client mapping
28
+ - [Documentation index](docs/README.md): Full guide list
29
+
30
+ ## Examples
31
+
32
+ - [Examples catalog](docs/examples.md): Demo routes and patterns
33
+ - [Demo pages](apps/demo/src/examples/): Runnable `ui.page` modules
34
+
35
+ ## Optional
36
+
37
+ - [Architecture](docs/architecture.md): Packages and data flow
38
+ - [WebSocket protocol](docs/protocol.md): Message formats
39
+ - [AI UI](docs/ai.md): `ui.ai.*` visual primitives
40
+ - [Reactive let](docs/reactive-let.md): `ui.state` / `ui.auto` and the compile-time `let` transform
41
+ - [DuckDB](docs/duckdb.md): `@close-by/clay-duckdb`
42
+ - [ClickHouse](docs/clickhouse.md): `@close-by/clay-clickhouse`
43
+ - [Kibana](docs/kibana.md): `@close-by/clay-kibana`
44
+ - [NiceGUI](https://nicegui.io/documentation): Inspiration for the `ui.*` style