@qorejs/qore 1.0.6 → 2.0.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 (46) hide show
  1. package/MIGRATION.md +124 -0
  2. package/README.md +70 -40
  3. package/README.zh-CN.md +24 -12
  4. package/dist/src/core/response-types.d.ts +6 -2
  5. package/dist/src/core/response-types.d.ts.map +1 -1
  6. package/dist/src/core/stream-types.d.ts +18 -2
  7. package/dist/src/core/stream-types.d.ts.map +1 -1
  8. package/dist/src/core/stream.d.ts +1 -1
  9. package/dist/src/core/stream.d.ts.map +1 -1
  10. package/dist/src/core/stream.js +123 -9
  11. package/dist/src/core/stream.js.map +1 -1
  12. package/dist/src/dom.d.ts +6 -0
  13. package/dist/src/dom.d.ts.map +1 -0
  14. package/dist/src/dom.js +5 -0
  15. package/dist/src/dom.js.map +1 -0
  16. package/dist/src/index.d.ts +3 -18
  17. package/dist/src/index.d.ts.map +1 -1
  18. package/dist/src/index.js +0 -12
  19. package/dist/src/index.js.map +1 -1
  20. package/dist/src/providers.d.ts +8 -0
  21. package/dist/src/providers.d.ts.map +1 -0
  22. package/dist/src/providers.js +8 -0
  23. package/dist/src/providers.js.map +1 -0
  24. package/dist/src/react.d.ts +63 -0
  25. package/dist/src/react.d.ts.map +1 -0
  26. package/dist/src/react.js +123 -0
  27. package/dist/src/react.js.map +1 -0
  28. package/dist/src/server.d.ts +3 -0
  29. package/dist/src/server.d.ts.map +1 -0
  30. package/dist/src/server.js +3 -0
  31. package/dist/src/server.js.map +1 -0
  32. package/dist/src/shared/utils.d.ts +2 -1
  33. package/dist/src/shared/utils.d.ts.map +1 -1
  34. package/dist/src/shared/utils.js.map +1 -1
  35. package/dist/src/signals.d.ts +4 -0
  36. package/dist/src/signals.d.ts.map +1 -0
  37. package/dist/src/signals.js +3 -0
  38. package/dist/src/signals.js.map +1 -0
  39. package/docs/api.md +42 -7
  40. package/docs/architecture.md +36 -34
  41. package/docs/comparisons.md +23 -20
  42. package/docs/concepts.md +6 -14
  43. package/docs/providers.md +19 -1
  44. package/docs/react.md +23 -7
  45. package/docs/runtime.md +49 -0
  46. package/package.json +35 -4
@@ -1,6 +1,6 @@
1
1
  # Architecture
2
2
 
3
- Qore is a small runtime with four layers.
3
+ Qore owns streamed state. Host frameworks own rendering and component lifecycle.
4
4
 
5
5
  ```text
6
6
  Provider / AsyncIterable
@@ -9,49 +9,51 @@ Provider / AsyncIterable
9
9
  Qore Stream Runtime
10
10
  |
11
11
  v
12
- Readonly Signal
12
+ Readonly state + subscriptions
13
13
  |
14
- v
15
- DOM Binding
14
+ +--> React adapter --> React components
15
+ +--> Host framework integration
16
+ +--> Optional DOM renderer --> text nodes
16
17
  ```
17
18
 
18
- ## Provider / AsyncIterable
19
-
20
- Providers expose streaming data as async iterables. Qore ships adapters for OpenAI, Anthropic, OpenRouter, DeepSeek, Ollama, generic SSE, and generic line-delimited streams.
21
-
22
- Adapters should run on the server or another trusted runtime when they need API keys.
23
-
24
- ## Stream Runtime
19
+ ## Package boundaries
25
20
 
26
- The stream runtime owns:
21
+ - `@qorejs/qore`: accumulation, stream lifecycle, event projections, backpressure,
22
+ async iteration, and stream inspection.
23
+ - `@qorejs/qore/react`: React external-store hooks with cached snapshots and cleanup.
24
+ - `@qorejs/qore/dom`: optional browser-only rendering, lists, mounting and DOM properties.
25
+ - `@qorejs/qore/signals`: optional mutable signals, computed values, effects and scopes.
26
+ - `@qorejs/qore/providers`: optional vendor and generic SSE/NDJSON adapters.
27
+ - `@qorejs/qore/server`: optional SSE response serialization.
27
28
 
28
- - lifecycle: `idle`, `streaming`, `completed`, `failed`, `aborted`
29
- - abort propagation
30
- - retry and resume contracts for hosted SSE providers
31
- - backpressure buffering
32
- - async iterator bridging
33
- - reducer-based accumulation
34
- - development-time stream inspection through `globalThis.__QORE_DEVTOOLS__`
29
+ These are independently importable subpaths in one package. Core has no runtime
30
+ import of DOM, provider, server or React entrypoints. React is an optional peer,
31
+ so non-React users do not need to install it.
35
32
 
36
- ## Readonly Signal
37
-
38
- Every `QoreStream` is a readonly signal:
39
-
40
- ```js
41
- const answer = stream(model.chat('hello'));
42
- answer();
43
- ```
33
+ ## Internal state
44
34
 
45
- Users can observe stream state, but they cannot mutate runtime-owned fields such as `status`, `chunks`, or `error`.
35
+ The runtime still uses the existing reactive engine internally. Moving its public
36
+ UI primitives to `/signals` avoids requiring applications to adopt a second state
37
+ API. It does not claim that dependency tracking has been removed from the runtime.
38
+ The DOM renderer and stream core share the same engine instance.
46
39
 
47
- ## DOM Binding
40
+ Streams expose readonly values through `answer()`, `peek()` and `subscribe()`;
41
+ applications cannot write the runtime-owned lifecycle fields. Subscription cleanup
42
+ and stream cancellation are distinct: observing an externally owned stream does
43
+ not transfer ownership. The React managed hook aborts the streams it creates.
48
44
 
49
- The DOM layer is intentionally direct. `text(() => answer())` subscribes to the signal and updates a text node.
45
+ ## Integration boundaries
50
46
 
51
- There is no virtual DOM path on the hot streaming update.
47
+ Provider API keys belong on a server or trusted runtime. Browsers consume an
48
+ application endpoint. `/server` can serialize that endpoint as SSE.
52
49
 
53
- ## Server Boundary
50
+ React supplies its renderer and hydration. Managed hooks start work in client
51
+ effects; snapshot hooks provide deterministic server fallbacks. Qore's optional
52
+ DOM renderer has no SSR or hydration implementation, and adding a full framework
53
+ stack is outside the current roadmap.
54
54
 
55
- Qore includes `createSSEResponse(...)` so applications can produce server-side streams and consume them with the same client runtime.
55
+ ## Resource limits
56
56
 
57
- The DOM layer is browser-only in `1.0.x`; SSR and hydration are explicit future work.
57
+ `maxItems` bounds collection values, not full chunk history or iterator backlog.
58
+ Backpressure governs the producer buffer. Do not treat either option as a total
59
+ memory bound; retention and multicasting semantics remain separate runtime work.
@@ -1,35 +1,38 @@
1
1
  # Comparisons
2
2
 
3
- Qore's target is not "another UI framework." Its target is streamed interface state.
3
+ Qore focuses on streamed interface state and integrates with existing UI frameworks.
4
4
 
5
5
  ## React / Vercel AI SDK
6
6
 
7
- React and the Vercel AI SDK are strong tools for React applications. They still normally route streamed data through React state and render scheduling.
7
+ The Qore React adapter exposes streams through `useSyncExternalStore`. Updates
8
+ still use React rendering and reconciliation. Qore adds stream lifecycle and event
9
+ projection contracts; it does not make React render like a direct DOM renderer.
8
10
 
9
- ```text
10
- React / Vercel AI SDK:
11
- Token -> Hook state -> Component render -> Reconcile
11
+ The optional `/dom` renderer can update a dependent text node directly. That is a
12
+ separate integration path, not a property of all Qore integrations.
12
13
 
13
- Qore:
14
- Token -> Stream signal -> Text node
15
- ```
14
+ ## Solid and Vue
16
15
 
17
- Qore avoids snapshot-style transcript rewrites. That is the runtime distinction.
18
-
19
- ## Solid
20
-
21
- Solid has excellent fine-grained reactivity. Qore's difference is the native stream primitive: a stream is already a signal and an async iterable.
22
-
23
- ## Vue
24
-
25
- Vue refs are ergonomic, but provider streams still need to be adapted into state by user code. Qore makes that adaptation the default primitive.
16
+ Their reactive systems already own application state and rendering. Applications
17
+ can bridge Qore subscriptions into native signals or refs and dispose subscriptions
18
+ with their framework lifecycle. They do not need Qore's optional `/signals` or
19
+ `/dom` APIs. Qore's responsibility is stream consumption and lifecycle.
26
20
 
27
21
  ## RxJS
28
22
 
29
- RxJS is a powerful observable toolkit. Qore is smaller and UI-directed: the result of stream composition is still a readonly signal that can bind directly to a DOM node.
23
+ Stream composition overlaps with observable libraries. Qore's particular contract
24
+ combines the current accumulated value, subscriptions, async iteration and lifecycle.
25
+ Choose based on required stream semantics and integration costs.
30
26
 
31
27
  ## When Not To Use Qore
32
28
 
33
- Do not use Qore as a replacement for a full app framework if you need routing, forms, UI component catalogs, or a large ecosystem surface.
29
+ An existing application with adequate stream handling may not need another runtime.
30
+ Qore does not provide routing, forms, an application component ecosystem, or its own
31
+ SSR/hydration stack. The standalone DOM renderer is optional.
32
+
33
+ ## Benchmark Language
34
34
 
35
- Use Qore where the hard part is streamed data becoming reactive interface state.
35
+ The current benchmark compares fine-grained stream updates with snapshot-style
36
+ transcript rewrites. It is not an optimized React, Vue or Solid comparison and does
37
+ not establish a framework-wide performance ranking. Measure real workloads,
38
+ including parsing, rendering, interaction latency and retained memory.
package/docs/concepts.md CHANGED
@@ -10,19 +10,9 @@ A stream is how data moves. A signal is how UI reacts. Qore treats them as one r
10
10
 
11
11
  ## The Problem
12
12
 
13
- Most UI frameworks start with a snapshot model:
14
-
15
- ```text
16
- fetch -> state snapshot -> render
17
- ```
18
-
19
- AI and realtime interfaces are different. The useful data often arrives as a sequence:
20
-
21
- ```text
22
- token -> token -> tool event -> status -> token -> done
23
- ```
24
-
25
- If each chunk has to be copied into component state, reconciled, and rerendered as a transcript snapshot, the runtime path becomes wider than the problem needs.
13
+ AI and realtime interfaces receive sequences of tokens, tool events, status,
14
+ diffs and terminal events. A runtime can centralize accumulation and lifecycle
15
+ while the existing UI framework continues to manage components and rendering.
26
16
 
27
17
  ## The Qore Model
28
18
 
@@ -43,9 +33,11 @@ The same value is:
43
33
  - an async iterable
44
34
  - a lifecycle-aware stream state
45
35
 
46
- That means a UI can bind directly to streamed data:
36
+ The optional `@qorejs/qore/dom` renderer can bind directly to streamed data:
47
37
 
48
38
  ```js
39
+ import { h, text } from '@qorejs/qore/dom';
40
+
49
41
  h('article', {}, text(() => answer()));
50
42
  ```
51
43
 
package/docs/providers.md CHANGED
@@ -45,7 +45,8 @@ Supported adapters:
45
45
  ## Example
46
46
 
47
47
  ```js
48
- import { createOpenAI, stream } from '@qorejs/qore';
48
+ import { createOpenAI } from '@qorejs/qore/providers';
49
+ import { stream } from '@qorejs/qore';
49
50
 
50
51
  const openai = createOpenAI({
51
52
  apiKey: process.env.OPENAI_API_KEY,
@@ -56,3 +57,20 @@ const answer = stream(openai.chat('Explain stream = signal'));
56
57
  ```
57
58
 
58
59
  Keep this code on the server when it uses real secrets.
60
+
61
+
62
+ ## Server Boundary
63
+
64
+ Provider adapters are designed for server-side or otherwise trusted runtimes. A browser application should not receive provider API keys. The recommended production path is:
65
+
66
+ ```text
67
+ Browser UI -> your SSE / NDJSON endpoint -> provider adapter -> model provider
68
+ ```
69
+
70
+ Then the browser consumes your endpoint as a Qore stream:
71
+
72
+ ```ts
73
+ const answer = stream(fetch('/api/agent').then((response) => response.body));
74
+ ```
75
+
76
+ For a complete application boundary example, see [`examples/server-sse-to-qore-client.ts`](../examples/server-sse-to-qore-client.ts).
package/docs/react.md CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Qore's core runtime is framework-neutral. The React adapter lets React apps consume Qore streams without treating streaming as a special rendering case.
4
4
 
5
- > Status: the adapter is release-ready in [`packages/react`](../packages/react), but the npm package is not published yet. Publishing currently fails with npm `E404` for `@qorejs/react`, which means the `@qorejs` npm scope/package access still needs to be provisioned before the install command below becomes valid.
5
+ The hooks ship with Qore 2 at `@qorejs/qore/react`. The separate
6
+ `@qorejs/react` package is an unpublished compatibility wrapper, not an installation requirement.
6
7
 
7
8
  ```bash
8
- # Available after npm package access is provisioned.
9
- npm i @qorejs/qore @qorejs/react
9
+ npm i @qorejs/qore react react-dom
10
10
  ```
11
11
 
12
12
  ## Why It Exists
@@ -30,7 +30,7 @@ Use `useQoreStream` when a component owns the stream lifecycle.
30
30
 
31
31
  ```tsx
32
32
  import { stream } from '@qorejs/qore';
33
- import { useQoreStream } from '@qorejs/react';
33
+ import { useQoreStream } from '@qorejs/qore/react';
34
34
 
35
35
  export function Answer({ prompt }: { prompt: string }) {
36
36
  const answer = useQoreStream(
@@ -57,7 +57,7 @@ Use `useQoreStreamSnapshot` when the stream is created outside React and the com
57
57
 
58
58
  ```tsx
59
59
  import type { QoreStream } from '@qorejs/qore';
60
- import { useQoreStreamSnapshot } from '@qorejs/react';
60
+ import { useQoreStreamSnapshot } from '@qorejs/qore/react';
61
61
 
62
62
  export function Transcript({ answer }: { answer: QoreStream<string, string> }) {
63
63
  const snapshot = useQoreStreamSnapshot(answer, { initialValue: '' });
@@ -74,7 +74,7 @@ Use `useQoreSignalSelector` when a React component only needs one derived slice
74
74
 
75
75
  ```tsx
76
76
  import type { QoreStream } from '@qorejs/qore';
77
- import { useQoreSignalSelector } from '@qorejs/react';
77
+ import { useQoreSignalSelector } from '@qorejs/qore/react';
78
78
 
79
79
  function TokenCounter({ answer }: { answer: QoreStream<string, string> }) {
80
80
  const tokenCount = useQoreSignalSelector(answer.chunks, (chunks) => chunks.length);
@@ -90,7 +90,7 @@ Use `useQoreSignal` for any Qore readonly signal.
90
90
 
91
91
  ```tsx
92
92
  import type { ReadonlySignal } from '@qorejs/qore';
93
- import { useQoreSignal } from '@qorejs/react';
93
+ import { useQoreSignal } from '@qorejs/qore/react';
94
94
 
95
95
  export function Counter({ count }: { count: ReadonlySignal<number> }) {
96
96
  const value = useQoreSignal(count);
@@ -103,3 +103,19 @@ export function Counter({ count }: { count: ReadonlySignal<number> }) {
103
103
  Provider adapters are still intended for server-side or trusted runtimes. In browser React apps, stream from your own SSE or NDJSON endpoint instead of exposing provider API keys.
104
104
 
105
105
  Keep dependency arrays honest. If the stream factory reads `prompt`, `model`, `conversationId`, or auth/session state, include those values in the dependency list.
106
+
107
+ ## Snapshot and rendering contract
108
+
109
+ The adapter caches emitted snapshots so repeated React reads receive the same
110
+ reference, including stream chunk arrays. Subscription setup reads the current
111
+ source through immediate delivery, covering updates between render and subscribe.
112
+ Selectors may use `isEqual` to retain an equivalent selected value.
113
+
114
+ `useQoreStreamSnapshot` uses `initialValue` and idle lifecycle fields during server
115
+ rendering and hydration. Network work for `useQoreStream` starts in an effect on the
116
+ client. For `useQoreSignal` and selectors, supply `getServerSnapshot` when the server
117
+ value differs from the client source. Keep request-specific streams out of shared
118
+ server globals. These hooks do not bypass React rendering.
119
+
120
+ `maxItems` bounds a collection's displayed value, not its retained chunk history
121
+ or iterator queue. This release does not introduce bounded total stream memory.
package/docs/runtime.md CHANGED
@@ -50,6 +50,55 @@ stream.switchMap(promptChanges, (prompt) => openai.chat(prompt));
50
50
 
51
51
  The composed result is still a stream signal.
52
52
 
53
+ ## Structured JSON
54
+
55
+ AI providers increasingly return structured output as streamed text. Keep that
56
+ boundary reactive too:
57
+
58
+ ```ts
59
+ const result = stream.json<{ title: string; items: string[] }>(provider.chat(prompt), {
60
+ validate(value): value is { title: string; items: string[] } {
61
+ return typeof value === 'object'
62
+ && value !== null
63
+ && 'title' in value
64
+ && 'items' in value;
65
+ }
66
+ });
67
+
68
+ result(); // null until valid JSON is available, then the parsed object
69
+ ```
70
+
71
+ `stream.json()` parses the accumulated text after each chunk. It publishes valid
72
+ JSON as signal state and fails the stream if the source completes without valid
73
+ JSON. It intentionally does not pretend that arbitrary half-written JSON can be
74
+ parsed safely.
75
+
76
+ For event streams that arrive as line-delimited JSON, use `stream.ndjson()`:
77
+
78
+ ```ts
79
+ const events = stream.ndjson<{ type: string; value: unknown }>(agentEvents);
80
+
81
+ events(); // accumulated structured events
82
+ ```
83
+
84
+ Each valid line is published as a chunk and added to the signal array. Blank
85
+ lines are ignored, and an invalid final line fails the stream while keeping the
86
+ events that were already safely published.
87
+
88
+ ## Bounded Collections
89
+
90
+ Agent timelines, logs, and tool-event feeds can run much longer than a chat
91
+ answer. Use `maxItems` when the UI only needs the latest window:
92
+
93
+ ```ts
94
+ const timeline = stream.events(agent.run(task), { maxItems: 200 });
95
+ const logs = stream.ndjson(logSource, { maxItems: 500 });
96
+ const rows = stream.list(rowSource, { maxItems: 100 });
97
+ ```
98
+
99
+ The current signal value keeps the newest items. Async iteration, chunk history,
100
+ and `chunkCount()` still reflect the full stream lifecycle.
101
+
53
102
  ## Event Streams
54
103
 
55
104
  Provider streams are only the transport boundary. Agent interfaces need a richer runtime surface: text tokens, tool calls, status updates, reasoning notes, diffs, artifacts, retries, and errors can all be modeled as typed events.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qorejs/qore",
3
- "version": "1.0.6",
3
+ "version": "2.0.0",
4
4
  "description": "Qore is a reactive stream runtime for AI-native interfaces.",
5
5
  "type": "module",
6
6
  "main": "./dist/src/index.js",
@@ -10,14 +10,35 @@
10
10
  "types": "./dist/src/index.d.ts",
11
11
  "import": "./dist/src/index.js"
12
12
  },
13
- "./package.json": "./package.json"
13
+ "./package.json": "./package.json",
14
+ "./signals": {
15
+ "types": "./dist/src/signals.d.ts",
16
+ "import": "./dist/src/signals.js"
17
+ },
18
+ "./dom": {
19
+ "types": "./dist/src/dom.d.ts",
20
+ "import": "./dist/src/dom.js"
21
+ },
22
+ "./providers": {
23
+ "types": "./dist/src/providers.d.ts",
24
+ "import": "./dist/src/providers.js"
25
+ },
26
+ "./server": {
27
+ "types": "./dist/src/server.d.ts",
28
+ "import": "./dist/src/server.js"
29
+ },
30
+ "./react": {
31
+ "types": "./dist/src/react.d.ts",
32
+ "import": "./dist/src/react.js"
33
+ }
14
34
  },
15
35
  "files": [
16
36
  "dist/src",
17
37
  "README.md",
18
38
  "README.zh-CN.md",
19
39
  "docs",
20
- "LICENSE"
40
+ "LICENSE",
41
+ "MIGRATION.md"
21
42
  ],
22
43
  "sideEffects": false,
23
44
  "scripts": {
@@ -58,7 +79,6 @@
58
79
  "anthropic",
59
80
  "deepseek",
60
81
  "ollama",
61
- "framework",
62
82
  "async-iterable"
63
83
  ],
64
84
  "homepage": "https://qorejs.dev/",
@@ -77,7 +97,18 @@
77
97
  "@playwright/test": "^1.60.0",
78
98
  "@types/node": "^25.6.0",
79
99
  "@types/react": "^19.2.17",
100
+ "@types/react-dom": "^19.2.3",
101
+ "esbuild": "^0.25.12",
80
102
  "react": "^19.2.7",
103
+ "react-dom": "^19.2.7",
81
104
  "typescript": "^6.0.3"
105
+ },
106
+ "peerDependencies": {
107
+ "react": ">=18.2.0"
108
+ },
109
+ "peerDependenciesMeta": {
110
+ "react": {
111
+ "optional": true
112
+ }
82
113
  }
83
114
  }