@stapel/chat-react 0.1.0 → 0.2.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.
package/CHANGELOG.md CHANGED
@@ -1 +1,50 @@
1
1
  # @stapel/chat-react
2
+
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - ca35e19: New pair: `@stapel/chat-react` — the React half of `stapel-chat`, wiring both
8
+ of its transports behind one seam.
9
+
10
+ The storefront spec ruled polling-only for chat v1, and it was right about the
11
+ fleet it surveyed: the resumable consumer existed but `stapel_chat.routing`
12
+ exported nothing, so no host could mount it. stapel-chat 0.2.2 ships that mount
13
+ (`ws/chat/<uuid:conversation_id>`). So this pair carries **two transports and
14
+ one seam** — `useChatFreshness(streamKey, mapToQueryKeys, { fallbackRefetchInterval })`,
15
+ deliberately the signature the realtime substrate reserves for
16
+ `useSignalInvalidate`:
17
+
18
+ - a typed client for the module's own protocol — `hello{last_seq}` → `welcome`
19
+ → replay → `replay_done` → live frames, seq-deduped on both ends, resume from
20
+ the cursor the STORE holds (not the one the socket opened with), `error{resync}`
21
+ forwarded verbatim, close codes 4401/4403 treated as answers rather than
22
+ faults (no reconnect), everything else reconnected with jittered backoff;
23
+ - polling by `seq`, visibility-aware, with exponential backoff on consecutive
24
+ failures — used whenever the socket is not carrying the stream, and for the
25
+ inbox, which has no socket at all (the module fans out per thread).
26
+
27
+ Both ends do the same thing with what they learn: refetch the thread query,
28
+ whose query function advances the window BY SEQ
29
+ (`?direction=prev&anchor=<tip>`). The screens are written once and the tests
30
+ run against both transports. Writes never go over the socket: the `send` frame
31
+ is typed (a mirror must be complete) and never emitted, because its refusals
32
+ carry socket-local codes with no i18n key and no remediation, while
33
+ `POST …/messages` answers with the persisted row and a real error envelope.
34
+
35
+ Surface: `<ConversationList>` (server-computed unread counts, as a LoadState so
36
+ a badge cannot read "0" during an outage), `<ConversationThread>` (a contiguous
37
+ seq-ordered window — a hole is re-read, never stitched — with backfill and an
38
+ automatic, monotonic read marker), `<MessageComposer>` (code-point length
39
+ counting, so an emoji is one character on both sides of the wire), and
40
+ `<StartDirectChat>` — "message the seller", get-or-create over the module's own
41
+ participant-pair idempotency. An opt-in antd skin at `@stapel/chat-react/default`
42
+ and a member-surface nav entry.
43
+
44
+ Two contract facts recorded rather than papered over:
45
+ `CreateConversationRequest.scope_key` is ignored by the server, so a direct
46
+ thread cannot be scoped to a listing and the pair exposes no argument that
47
+ pretends otherwise; and stapel-chat ships no `translations/` directory, so the
48
+ pair authors ru/es for the twelve error keys the module owns (the
49
+ stapel-forms/stapel_attributes precedent) while the cross-cutting keys come from
50
+ stapel-core's catalogue.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Stapel contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -4,11 +4,11 @@
4
4
  *
5
5
  * ── What changed since the spec ─────────────────────────────────────────────
6
6
  *
7
- * `tasks/darom-storefront-design.md` §3.6 ruled polling-by-seq for chat v1,
7
+ * The storefront spec §3.6 ruled polling-by-seq for chat v1,
8
8
  * and it was right about the fleet it surveyed: stapel-chat had the resumable
9
9
  * consumer but `routing.py` exported nothing, so no host could mount it.
10
10
  * stapel-chat 0.2.2 ships that mount (`ws/chat/<uuid:conversation_id>`) and
11
- * the darom fleet runs it. So the pair carries BOTH transports and picks at
11
+ * the client fleet runs it. So the pair carries BOTH transports and picks at
12
12
  * runtime — a deployment without sockets (WSGI, no channel layer, a
13
13
  * misconfigured proxy) is not a broken chat, it is a chat that refreshes on a
14
14
  * timer.
@@ -10,7 +10,7 @@ import type { ChatReconnectOptions, ChatWebSocketFactory } from "../realtime/cha
10
10
  export interface ChatRealtimeOptions {
11
11
  /**
12
12
  * Where this deployment mounts `stapel_chat.routing.websocket_urlpatterns`,
13
- * e.g. `wss://darom.example/ws/chat/`. Omit to derive it from `baseUrl`
13
+ * e.g. `wss://shop.example/ws/chat/`. Omit to derive it from `baseUrl`
14
14
  * (the canonical `/ws/chat/` at the API's own origin). Pass `null` to turn
15
15
  * the socket transport OFF explicitly — a host that knows its backend runs
16
16
  * under WSGI says so here instead of letting every tab fail a handshake
package/llms.txt CHANGED
@@ -1,4 +1,4 @@
1
- # @stapel/chat-react 0.1.0
1
+ # @stapel/chat-react 0.2.0
2
2
 
3
3
  Headless React flow pair for stapel-chat (contract >=0.2 <0.3) — business + state, zero visual opinion.
4
4
  Built on @stapel/core: typed client + StapelApiError envelope, auth token refresh,
package/manifest.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$generated": "by scripts/gen-manifest.mjs — do not edit; drift-gated (pnpm gen:manifest:check)",
3
3
  "package": "@stapel/chat-react",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "backend": {
6
6
  "module": "stapel-chat",
7
7
  "contract": ">=0.2 <0.3"
package/nav-manifest.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "package": "@stapel/chat-react",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "entries": [
5
5
  {
6
6
  "id": "chat.conversations",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stapel/chat-react",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Headless React pair for stapel-chat: typed API client, TanStack Query hooks, a seq-ordered thread store, and ONE transport seam behind which the module's own resumable WebSocket protocol (hello/welcome/replay/replay_done/message/error) and seq-anchored polling are interchangeable — the UI never knows which is live. Conversation list with unread badges, thread replay + live tail, REST send, direct-thread idempotency for \"message the seller\", monotonic read markers. Zero visual opinion in the main entry; an opt-in /default subpath ships the antd skin.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -47,13 +47,6 @@
47
47
  "nav-manifest.json",
48
48
  "llms.txt"
49
49
  ],
50
- "scripts": {
51
- "build": "tsc -p tsconfig.json",
52
- "test": "tsc -p tsconfig.demo.json && vitest run --exclude test/prodBundlePurity.test.ts",
53
- "test:pack": "vitest run test/prodBundlePurity.test.ts",
54
- "lint": "eslint .",
55
- "size": "size-limit"
56
- },
57
50
  "size-limit": [
58
51
  {
59
52
  "name": "index — headless pair (client + hooks + thread store + transport seam)",
@@ -89,9 +82,6 @@
89
82
  },
90
83
  "devDependencies": {
91
84
  "@size-limit/preset-small-lib": "^11.2.0",
92
- "@stapel/core": "workspace:^",
93
- "@stapel/showcase": "workspace:^",
94
- "@stapel/tokens": "workspace:^",
95
85
  "@tanstack/react-query": "^5.81.0",
96
86
  "@testing-library/react": "^16.3.0",
97
87
  "@types/react": "^19.1.0",
@@ -103,12 +93,22 @@
103
93
  "react-dom": "^19.1.0",
104
94
  "size-limit": "^11.2.0",
105
95
  "typescript": "^5.8.3",
106
- "vitest": "^3.2.4"
96
+ "vitest": "^3.2.4",
97
+ "@stapel/core": "^0.15.0",
98
+ "@stapel/showcase": "^0.2.0",
99
+ "@stapel/tokens": "^0.5.0"
107
100
  },
108
101
  "engines": {
109
102
  "node": ">=22"
110
103
  },
111
104
  "publishConfig": {
112
105
  "access": "public"
106
+ },
107
+ "scripts": {
108
+ "build": "tsc -p tsconfig.json",
109
+ "test": "tsc -p tsconfig.demo.json && vitest run --exclude test/prodBundlePurity.test.ts",
110
+ "test:pack": "vitest run test/prodBundlePurity.test.ts",
111
+ "lint": "eslint .",
112
+ "size": "size-limit"
113
113
  }
114
- }
114
+ }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$generated": "by scripts/gen-events.mjs — do not edit; drift-gated (pnpm gen:events:check)",
3
3
  "package": "@stapel/chat-react",
4
- "version": "0.1.0",
4
+ "version": "0.2.0",
5
5
  "defined": [],
6
6
  "flows": []
7
7
  }