@flattype/models 1.5.0 → 1.7.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.
Files changed (2) hide show
  1. package/README.md +116 -4
  2. package/package.json +3 -3
package/README.md CHANGED
@@ -19,6 +19,8 @@
19
19
  <a href="https://www.npmjs.com/package/@flattype/react"><img src="https://img.shields.io/npm/v/@flattype/react?label=%40flattype%2Freact&color=8b6cff" alt="npm @flattype/react"></a>
20
20
  <a href="https://www.npmjs.com/package/@flattype/vue"><img src="https://img.shields.io/npm/v/@flattype/vue?label=%40flattype%2Fvue&color=8b6cff" alt="npm @flattype/vue"></a>
21
21
  <a href="https://www.npmjs.com/package/@flattype/angular"><img src="https://img.shields.io/npm/v/@flattype/angular?label=%40flattype%2Fangular&color=8b6cff" alt="npm @flattype/angular"></a>
22
+ <a href="https://www.npmjs.com/package/@flattype/svelte"><img src="https://img.shields.io/npm/v/@flattype/svelte?label=%40flattype%2Fsvelte&color=8b6cff" alt="npm @flattype/svelte"></a>
23
+ <a href="https://www.npmjs.com/package/@flattype/ai"><img src="https://img.shields.io/npm/v/@flattype/ai?label=%40flattype%2Fai&color=8b6cff" alt="npm @flattype/ai"></a>
22
24
  <a href="https://github.com/karammm/flattype/actions/workflows/ci.yml"><img src="https://github.com/karammm/flattype/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
23
25
  <img src="https://img.shields.io/badge/core-3.7_KB_min%2Bgz-2fd4b2" alt="3.7 KB min+gz">
24
26
  <img src="https://img.shields.io/badge/dependencies-0-2fd4b2" alt="No third-party dependencies">
@@ -33,6 +35,8 @@
33
35
  <a href="#react"><b>React</b></a> ·
34
36
  <a href="#vue"><b>Vue</b></a> ·
35
37
  <a href="#angular"><b>Angular</b></a> ·
38
+ <a href="#svelte-and-solid"><b>Svelte &amp; Solid</b></a> ·
39
+ <a href="#claude-and-llms"><b>Claude &amp; LLMs</b></a> ·
36
40
  <a href="#benchmarks"><b>Benchmarks</b></a> ·
37
41
  <a href="https://github.com/karammm/flattype/blob/main/CHANGELOG.md"><b>Changelog</b></a> ·
38
42
  <a href="https://github.com/karammm/flattype/blob/main/ROADMAP.md"><b>Roadmap</b></a>
@@ -41,10 +45,10 @@
41
45
  <p align="center">
42
46
  <a href="https://github.com/karammm/flattype/blob/main/docs/assets/flattype-intro.mp4">
43
47
  <img src="https://raw.githubusercontent.com/karammm/flattype/main/docs/assets/flattype-intro.gif" width="820"
44
- alt="FlatType.js in 71 seconds: an API response encoded to Flat Notation, how tables and dot paths keep it small, typed models mapped from snake_case JSON, an Express server answering in Flat Notation or JSON, React, Vue and Angular in real apps, and the size comparison with JSON and TOON">
48
+ alt="FlatType.js in 77 seconds: an API response encoded to Flat Notation, how tables and dot paths keep it small, typed models mapped from snake_case JSON, an Express server answering in Flat Notation or JSON, React, Vue, Angular and Svelte in real apps, and the size comparison with JSON and TOON">
45
49
  </a>
46
50
  <br>
47
- <sub>▶ <a href="https://github.com/karammm/flattype/blob/main/docs/assets/flattype-intro.mp4">Watch the 71-second intro in full quality</a>. Every result shown is real output, generated by <a href="https://github.com/karammm/flattype/blob/main/scripts/video/record.mjs"><code>scripts/video/record.mjs</code></a>.</sub>
51
+ <sub>▶ <a href="https://github.com/karammm/flattype/blob/main/docs/assets/flattype-intro.mp4">Watch the 77-second intro in full quality</a>. Every result shown is real output, generated by <a href="https://github.com/karammm/flattype/blob/main/scripts/video/record.mjs"><code>scripts/video/record.mjs</code></a>.</sub>
48
52
  </p>
49
53
 
50
54
  ---
@@ -103,7 +107,7 @@ app.use(flatExpress()); // server: res.json() answers in Flat Notation when the
103
107
  const users = await flatFetch("/api/users", { model: User, list: true }); // client: User[]
104
108
  ```
105
109
 
106
- And in React, Vue or Angular:
110
+ And in React, Vue, Angular, Svelte or Solid:
107
111
 
108
112
  ```tsx
109
113
  import { useFlat } from "@flattype/react"; // or "@flattype/vue": the same API, with refs
@@ -112,6 +116,10 @@ const { data: users, loading, error } = useFlat("/api/users", { model: User, lis
112
116
 
113
117
  // Angular: signals on top of HttpClient
114
118
  users = flatResource(() => "/api/users", { model: User, list: true }); // users.value(): User[] | undefined
119
+
120
+ // Svelte: a store, $users.data; Solid: a resource
121
+ const users = flatStore("/api/users", { model: User, list: true });
122
+ const [users] = createFlatResource(() => "/api/users", { model: User, list: true });
115
123
  ```
116
124
 
117
125
  ## Why FlatType
@@ -133,9 +141,11 @@ users = flatResource(() => "/api/users", { model: User, list: true }); // users.
133
141
  | [`@flattype/react`](https://github.com/karammm/flattype/blob/main/packages/react) | `useFlat`, `useFlatSuspense`, `useFlatStream`, `useModel`, `FlatProvider` | +1.4 KB |
134
142
  | [`@flattype/vue`](https://github.com/karammm/flattype/blob/main/packages/vue) | The same composables for Vue 3, with reactive URLs, plus the `createFlat()` plugin | +1.2 KB |
135
143
  | [`@flattype/angular`](https://github.com/karammm/flattype/blob/main/packages/angular) | `flatInterceptor` for HttpClient, the signal-based `flatResource` / `flatStream`, the `flatModel` pipe | +1.8 KB |
144
+ | [`@flattype/svelte`](https://github.com/karammm/flattype/blob/main/packages/svelte) | Svelte stores (`flatStore`, `flatStreamStore`, `modelStore`), and `/solid` with `createFlatResource` / `createFlatStream` | +1.1 KB / +0.9 KB |
145
+ | [`@flattype/ai`](https://github.com/karammm/flattype/blob/main/packages/ai) | `toPrompt`, `fromModelOutput`, `countTokens`, and MCP / Claude tool results | +1.3 KB |
136
146
  | [`@flattype/shared`](https://github.com/karammm/flattype/blob/main/packages/shared) | Shared TypeScript types | types only |
137
147
 
138
- Svelte bindings, Claude helpers and a CLI are planned. See the [roadmap](https://github.com/karammm/flattype/blob/main/ROADMAP.md).
148
+ A CLI with code generation is planned. See the [roadmap](https://github.com/karammm/flattype/blob/main/ROADMAP.md).
139
149
 
140
150
  ## The format
141
151
 
@@ -381,6 +391,108 @@ export class UsersComponent {
381
391
 
382
392
  Angular 19+ (built with partial compilation, so it works with every later version). A runnable Angular 22 app with Playwright tests in three browsers is in [`examples/angular-app`](https://github.com/karammm/flattype/blob/main/examples/angular-app).
383
393
 
394
+ ## Svelte and Solid
395
+
396
+ ```bash
397
+ npm install @flattype/svelte @flattype/models
398
+ ```
399
+
400
+ **Svelte** (3, 4 and 5). The stores follow the Svelte store contract, so `$store` just works:
401
+
402
+ ```svelte
403
+ <script lang="ts">
404
+ import { createFlat } from "@flattype/svelte";
405
+ import { derived, writable } from "svelte/store";
406
+
407
+ const api = createFlat({ baseURL: "/api", headers: () => ({ authorization: `Bearer ${token}` }) });
408
+
409
+ const users = api.store("/users", { model: User, list: true }); // $users.data: User[] | undefined
410
+ const id = writable(1);
411
+ const user = api.store(derived(id, (i) => `/users/${i}`), { model: User }); // the URL can be a store
412
+ const events = api.streamStore("/export", { model: Row, table: "rows" }); // rows while downloading
413
+ </script>
414
+
415
+ {#each $users.data ?? [] as u (u.id)}<li>{u.fullName}</li>{/each}
416
+ <p>{$events.done ? "Done" : `Loading… ${$events.rows.length}`}</p>
417
+ ```
418
+
419
+ **Solid** (`@flattype/svelte/solid`):
420
+
421
+ ```tsx
422
+ import { createFlatResource, createFlatStream } from "@flattype/svelte/solid";
423
+
424
+ const [user, { refetch }] = createFlatResource(() => `/users/${props.id}`, { baseURL: "/api", model: User });
425
+ const events = createFlatStream(() => "/api/export", { model: Row, table: "rows" });
426
+
427
+ <Suspense fallback={<Spinner />}>
428
+ <Show when={user()}>{(u) => <h2>{u().fullName}</h2>}</Show>
429
+ </Suspense>;
430
+ ```
431
+
432
+ | API | What it does |
433
+ | --- | --- |
434
+ | `flatStore(url, options)` | A store of `{ data, error, loading }` plus `refetch()`. Lazy: it fetches on the first subscriber and aborts when the last one leaves. A URL store change aborts the old request |
435
+ | `flatStreamStore(url, options)` | A store of `{ rows, done, error }` plus `restart()`. One update per network chunk |
436
+ | `modelStore(source, Model)` | Maps another store's value to a model |
437
+ | `createFlatResource(() => url, options)` | A Solid resource, for `<Suspense>` and `<ErrorBoundary>`. Aborts on change and on cleanup |
438
+ | `createFlatStream(() => url, options)` | `rows`, `done` and `error` accessors plus `restart()` |
439
+
440
+ The stores also work in Solid, or anywhere else, through the store contract: `from(flatStore(...))`. Runnable apps with Playwright tests in three browsers are in [`examples/svelte-app`](https://github.com/karammm/flattype/blob/main/examples/svelte-app) and [`examples/solid-app`](https://github.com/karammm/flattype/blob/main/examples/solid-app).
441
+
442
+ ## Claude and LLMs
443
+
444
+ ```bash
445
+ npm install @flattype/ai
446
+ ```
447
+
448
+ ```ts
449
+ import Anthropic from "@anthropic-ai/sdk";
450
+ import { countTokens, fromModelOutput, outputInstructions, toPrompt } from "@flattype/ai";
451
+
452
+ const client = new Anthropic();
453
+
454
+ // 1. Data in: Flat Notation inside <data> tags, with a one-line legend the model reads once.
455
+ const response = await client.beta.messages.create({
456
+ model: "claude-opus-5-5",
457
+ max_tokens: 16000,
458
+ betas: ["server-side-fallback-2026-07-01"],
459
+ fallbacks: "default", // if a safety classifier declines, retry on the recommended fallback model
460
+ messages: [{
461
+ role: "user",
462
+ content: `${toPrompt(orders, { name: "orders" })}\n\nTotal spent per customer?\n\n${outputInstructions([{ customer: "Alice", spent: 120 }])}`,
463
+ }],
464
+ });
465
+
466
+ // 2. Data out: tolerant of tags, fences, prose and JSON, then validated into typed models.
467
+ const reply = response.content.filter((b) => b.type === "text").map((b) => b.text).join("\n");
468
+ const totals = fromModelOutput(reply, { model: Total, list: true }); // Total[]
469
+
470
+ // 3. Measure it with Claude's own tokenizer (count_tokens is free).
471
+ const { json, flat, saved } = await countTokens(client, orders);
472
+ ```
473
+
474
+ **Tool results** shrink too, which matters most for agents that call tools over and over:
475
+
476
+ ```ts
477
+ import { claudeToolResult, toolResult } from "@flattype/ai";
478
+
479
+ // MCP server (@modelcontextprotocol/sdk): returns a CallToolResult with Flat Notation text
480
+ server.registerTool("list_orders", { description: "…" }, async () => toolResult(await db.orders()));
481
+
482
+ // Claude API tool loop: a tool_result block for the next user message
483
+ messages.push({ role: "user", content: [claudeToolResult(toolUse.id, await runTool(toolUse))] });
484
+ ```
485
+
486
+ | API | What it does |
487
+ | --- | --- |
488
+ | `toPrompt(data, { name, legend })` | `<data format="flat-notation">` plus a one-line legend. The block is still valid Flat Notation; put the legend in your system prompt once with `legend: false` |
489
+ | `outputInstructions(example)` | Asks the model to answer in Flat Notation and shows the exact shape you expect |
490
+ | `fromModelOutput(reply, { model, list })` | Finds the data (last `<data>` block, code fence, or the text itself), skips prose lines, and accepts JSON too |
491
+ | `countTokens(client, data, { model })` | `{ json, flat, saved }` in real Claude tokens, minus per-message overhead. Takes any client with `messages.countTokens` |
492
+ | `toolResult(data, { structured })` / `claudeToolResult(id, data)` | MCP `CallToolResult` and Claude API `tool_result` blocks |
493
+
494
+ See [`examples/ai-claude`](https://github.com/karammm/flattype/blob/main/examples/ai-claude) for a token report, a full Claude round trip, and a runnable MCP server (`claude mcp add flattype-orders -- node mcp-server.mjs`).
495
+
384
496
  ## Benchmarks
385
497
 
386
498
  `pnpm compare` on the fixtures in [`fixtures/`](https://github.com/karammm/flattype/blob/main/fixtures/index.mjs) (Node 24, Apple Silicon):
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flattype/models",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "description": "Typed models for API data: key mapping, coercion and defaults, from JSON or Flat Notation.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -33,8 +33,8 @@
33
33
  "flattype"
34
34
  ],
35
35
  "dependencies": {
36
- "@flattype/core": "1.5.0",
37
- "@flattype/shared": "1.5.0"
36
+ "@flattype/core": "1.7.0",
37
+ "@flattype/shared": "1.7.0"
38
38
  },
39
39
  "publishConfig": {
40
40
  "access": "public"