@lessly/sdk-app 48.0.4 → 49.0.1

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.
@@ -134,8 +134,58 @@ tools carry a REST binding. Run `npm i @lessly/sdk-app@latest` to pick up newly
134
134
  migrated namespaces. To see exactly what a given version exposes, check its
135
135
  subpaths in `package.json` `exports`, or the `manifest.gen.ts` namespace list.
136
136
 
137
+ ## Streaming (`*Connect` factories)
138
+
139
+ Some catalog tools are ws-bound: instead of a request/response pair they carry
140
+ a `ws` binding, and the generator emits a `<toolId>Connect` factory for them
141
+ next to the option factories. That factory is the **only** sanctioned way to
142
+ stream from an App (APP-001) — it derives `ws(s)://` from your `baseUrl`, opens
143
+ the socket, and hands back a small handle:
144
+
145
+ ```ts
146
+ import { createLesslyApp } from '@lessly/sdk-app';
147
+ import { playgroundWsEchoConnect } from '@lessly/sdk-app/playground';
148
+
149
+ const sdk = createLesslyApp({ baseUrl: 'https://api.lessly.dev', productId: 'prod_123' });
150
+
151
+ const stream = playgroundWsEchoConnect(sdk, { room: 'lobby' });
152
+ const off = stream.onMessage((frame) => console.log(frame));
153
+ stream.onClose(({ code, reason, wasClean }) => console.log('closed', code, reason, wasClean));
154
+ stream.send('hello'); // queued until the socket is OPEN, then flushed in order
155
+ // later
156
+ off();
157
+ stream.close(1000, 'done');
158
+ ```
159
+
160
+ What the handle does and does not do:
161
+
162
+ - **Session cookies ride the upgrade.** No CSRF token and no product header are
163
+ sent — a browser `WebSocket` cannot set headers at all, and the gateway
164
+ authenticates the upgrade from the session.
165
+ - **Frames are raw.** `onMessage` delivers `MessageEvent.data` exactly as it
166
+ arrived (`string`, `ArrayBuffer` or `Blob`); the SDK never parses it. Framing
167
+ is protocol-specific and is documented by the tool's own description.
168
+ - **No reconnection.** A dropped connection surfaces once, through `onClose`
169
+ with the close code (`1006` for an abnormal drop). Whether and how to
170
+ reconnect is the App's decision — the SDK will not retry behind your back.
171
+ - **Params are placed like a GET.** `:token` segments are filled from the input,
172
+ everything else becomes a query parameter.
173
+
174
+ The `*Connect` factory is the App-facing surface, and the only one APP-001
175
+ sanctions. The lower-level pieces it is built on — `sdk.openStream(binding, …)`
176
+ and the `connectStream` runtime — are the SDK's own primitive: they are exported
177
+ so the generated code can use them and so tests can substitute a socket, not so
178
+ that App code can address a route by hand. Call the factory.
179
+
180
+ Streaming needs a browser: outside one (SSR, node tests) there is no
181
+ `WebSocket`, and the SDK says so by name rather than failing opaquely. Pass
182
+ `options.WebSocket` to supply an implementation in tests.
183
+
137
184
  ## What not to do
138
185
 
186
+ - Don't open a `WebSocket` to a platform host, and don't hand-build a
187
+ `ws(s)://` URL from `baseUrl` (APP-001) — use the generated `*Connect`
188
+ factory. A tool without a `ws` binding has no sanctioned stream.
139
189
  - Don't `fetch` a platform host directly, and don't use any other HTTP client
140
190
  for platform data (APP-001) — always go through `sdk` or the generated
141
191
  factories.
package/docs/rules.md CHANGED
@@ -23,6 +23,19 @@ generated `*QueryOptions`/`*MutationOptions` factories built on top of it. An
23
23
  App MUST NOT `fetch` a platform host directly, and MUST NOT use any other HTTP
24
24
  client to reach platform data.
25
25
 
26
+ **Streaming.** The same rule covers WebSockets. Platform streaming MUST go
27
+ through the generated `*Connect` factories (`<toolId>Connect(sdk, params)`,
28
+ imported from the tool's namespace subpath), which are emitted for every
29
+ catalog tool that carries a `ws` binding and are backed by the SDK's
30
+ `connectStream` runtime. An App MUST NOT construct `new WebSocket(...)` against
31
+ a platform host, exactly as it must not `fetch` one — and MUST NOT hand-build a
32
+ `ws(s)://` URL from `baseUrl`. A tool with no `ws` binding has no sanctioned
33
+ stream: there is nothing to open, and reaching for a raw socket is a violation,
34
+ not a workaround.
35
+
36
+ `new WebSocket(...)` against an App's OWN non-platform host (a third-party
37
+ service the App integrates) is outside this rule.
38
+
26
39
  ### APP-002 (MUST) — Frontend-only
27
40
 
28
41
  An App MUST NOT ship a backend service, a database, or an MCP endpoint. All
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lessly/sdk-app",
3
- "version": "48.0.4",
3
+ "version": "49.0.1",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "engines": {
@@ -101,5 +101,5 @@
101
101
  "require": "./dist/waitlist/index.cjs"
102
102
  }
103
103
  },
104
- "sdkContentHash": "sha256:82da00e2c226a8955611954633868a0b341f2c4448c844865f0281540a68671b"
104
+ "sdkContentHash": "sha256:9954f7b662b31bf1716d5023b5b42f83b84a1258101a622ef7a26a933f974200"
105
105
  }
@@ -1,5 +1,5 @@
1
1
  // AUTOGENERATED by scripts/generate.ts — do not edit.
2
- import type { BindingsMap } from '../runtime/types';
2
+ import type { BindingsMap, WsBindingsMap } from '../runtime/types';
3
3
 
4
4
  export const bindings: BindingsMap = {
5
5
  "analytics": {
@@ -8011,3 +8011,5 @@ export const bindings: BindingsMap = {
8011
8011
  }
8012
8012
  }
8013
8013
  };
8014
+
8015
+ export const wsBindings: WsBindingsMap = {};