@quadrokit/client 0.3.13 → 0.3.14

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/README.md CHANGED
@@ -16,6 +16,7 @@ The package exposes:
16
16
 
17
17
  - **`@quadrokit/client`** — runtime + types (and anything your generated code needs).
18
18
  - **`@quadrokit/client/runtime`** — low-level helpers if you build custom integrations.
19
+ - **`@quadrokit/client/rx`** — optional RxJS bridge for `QuadroEventBus` (install **`rxjs`** in your app).
19
20
 
20
21
  ---
21
22
 
@@ -53,7 +54,7 @@ Environment variables (often via `.env` in the project root): `VITE_4D_ORIGIN`,
53
54
  |------|---------|
54
55
  | **`types.gen.ts`** | Entity interfaces; **`*Path`** aliases use **`QuadroAttributePaths<T>`** (recursive dot paths, depth-limited for circular relations); **`QuadroClient`** typing. |
55
56
  | **`catalog.gen.json`** | Catalog runtime spec (dataclass layouts, methods, relations) consumed by `@quadrokit/client/runtime` — keeps **`client.gen.ts`** tiny. |
56
- | **`client.gen.ts`** | Thin `createClient(config)` that wires `QuadroHttp` + `buildQuadroClientFromCatalogSpec` + `catalog.gen.json`. |
57
+ | **`client.gen.ts`** | Thin `createClient(config)` that wires `QuadroHttp` + `buildQuadroClientFromCatalogSpec` + `catalog.gen.json`. Config supports optional **`events`** (see [Request lifecycle events](#request-lifecycle-events)). |
57
58
  | **`meta.json`** | `__NAME`, `sessionCookieName` hint for 4D session cookies. |
58
59
 
59
60
  Point your app imports at the generated folder, for example:
@@ -80,10 +81,90 @@ const quadro = createClient({
80
81
  })
81
82
  ```
82
83
 
84
+ Optional **`events: new QuadroEventBus()`** (from **`@quadrokit/client/runtime`**) enables [request lifecycle events](#request-lifecycle-events) on every call through the client.
85
+
83
86
  At runtime, requests use **`credentials: 'include'`** so session cookies (e.g. `4DSID_<datastore>`) are sent when your app and 4D share the same site or proxy. See `meta.json` → `sessionCookieName` after generate.
84
87
 
85
88
  ---
86
89
 
90
+ ## Request lifecycle events
91
+
92
+ Pass a **`QuadroEventBus`** from **`@quadrokit/client/runtime`** into **`createClient({ …, events })`**. The runtime emits a **loading → success** or **loading → error** sequence per logical operation (lists, `get`, `delete`, class functions, datastore paths, login, etc.).
93
+
94
+ ```ts
95
+ import { createClient } from './.quadrokit/generated/client.gen.js'
96
+ import { QuadroEventBus } from '@quadrokit/client/runtime'
97
+
98
+ const events = new QuadroEventBus()
99
+
100
+ const quadro = createClient({
101
+ baseURL: '/rest',
102
+ events,
103
+ })
104
+
105
+ const unsub = events.subscribe((e) => {
106
+ if (e.type === 'loading') {
107
+ console.debug(e.context.operation, e.path, e.method)
108
+ }
109
+ if (e.type === 'success') {
110
+ console.debug(e.durationMs, e.body)
111
+ }
112
+ if (e.type === 'error') {
113
+ console.warn(e.status, e.message, e.error)
114
+ }
115
+ })
116
+
117
+ // later: unsub()
118
+ ```
119
+
120
+ You can also subscribe on the HTTP instance: **`quadro._http.subscribe(listener)`** returns an unsubscribe function.
121
+
122
+ ### Event shapes (`QuadroClientEvent`)
123
+
124
+ | Field | `loading` | `success` | `error` |
125
+ |-------|-----------|-----------|---------|
126
+ | **`type`** | `'loading'` | `'success'` | `'error'` |
127
+ | **`operationId`** | Correlates one start → one outcome | same | same |
128
+ | **`context`** | **`QuadroRequestContext`**: **`operation`** (**`QuadroOperation`** string), optional **`className`**, **`methodName`**, **`entityKey`**, **`attributes`** | same | same |
129
+ | **`path`** | Request path | same | same |
130
+ | **`method`** | HTTP method | — | — |
131
+ | **`startedAt`** | Timestamp | — | — |
132
+ | **`durationMs`** | — | Elapsed | Elapsed |
133
+ | **`status`** | — | If HTTP response is raw `Response` | If thrown value has HTTP **`status`** (e.g. **`QuadroHttpError`**) |
134
+ | **`body`** | — | Result: parsed JSON, rows, `void`, or a small summary for raw **`Response`** | — |
135
+ | **`message` / `error`** | — | — | User-facing message + original error |
136
+
137
+ ### Operations (`QuadroOperation`)
138
+
139
+ Examples include: **`http.json`**, **`http.void`**, **`http.request`**, **`collection.list`**, **`collection.release`**, **`dataclass.get`**, **`dataclass.delete`**, **`function.dataclass`**, **`function.entity`**, **`function.entityCollection`**, **`datastore`**, **`auth.login`**.
140
+
141
+ ### `QuadroHttp` helpers (advanced)
142
+
143
+ When **`events`** is set, **`json`**, **`void`**, and **`request`** accept an optional trailing **`QuadroRequestContext`** so emissions can be tagged. **`QuadroHttp`** also exposes:
144
+
145
+ - **`rawRequest(path, init)`** — fetch without emitting (used inside multi-step flows).
146
+ - **`runWithEvents(context, path, method, fn)`** — run an async function with the same loading/success/error envelope as the built-in methods.
147
+ - **`events`** getter — the bus instance, if any.
148
+
149
+ ---
150
+
151
+ ## RxJS (`@quadrokit/client/rx`)
152
+
153
+ Install **`rxjs`** in your app, then bridge the bus to an observable:
154
+
155
+ ```ts
156
+ import { quadroEventsObservable } from '@quadrokit/client/rx'
157
+ import { filter } from 'rxjs/operators'
158
+
159
+ const sub = quadroEventsObservable(events)
160
+ .pipe(filter((e) => e.type === 'error'))
161
+ .subscribe((e) => console.warn(e.message))
162
+ ```
163
+
164
+ **`rxjs`** is an **optional peer dependency** of `@quadrokit/client`; you only need it if you import `@quadrokit/client/rx`.
165
+
166
+ ---
167
+
87
168
  ## Dataclass API (generated)
88
169
 
89
170
  For each exposed dataclass, the client exposes a namespace such as `quadro.Agency`, `quadro.Reservation`, etc.
@@ -178,7 +259,8 @@ Import from **`@quadrokit/client/runtime`** when you need primitives without cod
178
259
 
179
260
  | Export | Use |
180
261
  |--------|-----|
181
- | **`QuadroHttp`** | Thin wrapper: `json()`, `void()`, `request()` with base URL + default headers. |
262
+ | **`QuadroHttp`** | Thin wrapper: **`json()`**, **`void()`**, **`request()`** with base URL + default headers; optional **`events`**; **`rawRequest`**, **`runWithEvents`**, **`subscribe`**. |
263
+ | **`QuadroEventBus`**, **`QuadroClientEvent`**, **`QuadroLoadingEvent`**, **`QuadroSuccessEvent`**, **`QuadroErrorEvent`**, **`QuadroRequestContext`**, **`QuadroOperation`** | Lifecycle event types and bus (see [Request lifecycle events](#request-lifecycle-events)). |
182
264
  | **`createCollection`** | Build a `CollectionHandle` from a `CollectionContext` + options + row mapper. |
183
265
  | **`callDataClassFunction`**, **`callEntityFunction`**, **`callEntityCollectionFunction`** | Call ORDA functions by name and path (same REST rules as above). |
184
266
  | **`callDatastorePath`**, **`createClient`’s `rpc`** | Arbitrary segments under the REST root, e.g. datastore or singleton paths. |
@@ -191,7 +273,7 @@ The generated `createClient` is the recommended surface; these are for tooling,
191
273
 
192
274
  ## Errors
193
275
 
194
- Failed HTTP responses throw **`QuadroHttpError`** (status + response body). Handle network errors separately (`fetch` failures).
276
+ Failed HTTP responses throw **`QuadroHttpError`** (status + response body). Handle network errors separately (`fetch` failures). When **`events`** is configured, failures also emit a **`QuadroErrorEvent`** with **`message`**, **`error`**, and optional **`status`** before the error propagates.
195
277
 
196
278
  ---
197
279
 
@@ -1 +1 @@
1
- {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../src/runtime/collection.ts"],"names":[],"mappings":"AAAA,OAAO,EAA0C,KAAK,UAAU,EAAE,MAAM,WAAW,CAAA;AACnF,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,YAAY,CAAA;AAGnB,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,UAAU,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,gEAAgE;IAChE,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,sFAAsF;IACtF,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAA;CACnD;AAED,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC,CAAA;AAsE3C;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,GAAG,EAAE,iBAAiB,EACtB,cAAc,EAAE,iBAAiB,EACjC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAChB,gBAAgB,CAAC,CAAC,CAAC,CA+LrB;AAED,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG;IACnD,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACvB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB,CAAA"}
1
+ {"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../src/runtime/collection.ts"],"names":[],"mappings":"AACA,OAAO,EAA0C,KAAK,UAAU,EAAE,MAAM,WAAW,CAAA;AACnF,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,YAAY,CAAA;AAGnB,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,UAAU,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,gEAAgE;IAChE,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,wEAAwE;IACxE,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IAC1B,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,sFAAsF;IACtF,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,6FAA6F;IAC7F,gBAAgB,CAAC,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,IAAI,CAAA;CACnD;AAED,MAAM,MAAM,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,OAAO,KAAK,CAAC,CAAA;AAsE3C;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAChC,GAAG,EAAE,iBAAiB,EACtB,cAAc,EAAE,iBAAiB,EACjC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,GAChB,gBAAgB,CAAC,CAAC,CAAC,CA6LrB;AAED,MAAM,MAAM,gBAAgB,CAAC,CAAC,IAAI,aAAa,CAAC,CAAC,CAAC,GAAG;IACnD,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACvB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB,CAAA"}
@@ -1,3 +1,4 @@
1
+ import { QuadroHttpError } from './errors.mjs';
1
2
  import { mountPathFromBaseURL, normalizeBaseURL } from './http.mjs';
2
3
  import { buildEntitySetCreationParams, buildEntitySetPageParams, buildListSearchParams, } from './query.mjs';
3
4
  import { unwrapEntityList } from './unwrap.mjs';
@@ -111,7 +112,7 @@ export function createCollection(ctx, initialOptions, mapRow) {
111
112
  });
112
113
  const textCreate = await resCreate.text();
113
114
  if (!resCreate.ok) {
114
- throw new Error(`Entity set creation failed ${resCreate.status}: ${textCreate.slice(0, 200)}`);
115
+ throw new QuadroHttpError(resCreate.status, textCreate);
115
116
  }
116
117
  const jsonCreate = textCreate ? JSON.parse(textCreate) : [];
117
118
  await parseEntitySetFromResponse(jsonCreate, resCreate);
@@ -130,7 +131,7 @@ export function createCollection(ctx, initialOptions, mapRow) {
130
131
  });
131
132
  const text = await res.text();
132
133
  if (!res.ok) {
133
- throw new Error(`List failed ${res.status}: ${text.slice(0, 200)}`);
134
+ throw new QuadroHttpError(res.status, text);
134
135
  }
135
136
  const json = text ? JSON.parse(text) : [];
136
137
  return unwrapEntityList(ctx.className, json);
@@ -150,7 +151,7 @@ export function createCollection(ctx, initialOptions, mapRow) {
150
151
  });
151
152
  const text = await res.text();
152
153
  if (!res.ok) {
153
- throw new Error(`List failed ${res.status}: ${text.slice(0, 200)}`);
154
+ throw new QuadroHttpError(res.status, text);
154
155
  }
155
156
  const json = text ? JSON.parse(text) : [];
156
157
  return unwrapEntityList(ctx.className, json);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quadrokit/client",
3
- "version": "0.3.13",
3
+ "version": "0.3.14",
4
4
  "description": "Typed 4D REST client and catalog code generator for QuadroKit",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",
@@ -33,7 +33,7 @@
33
33
  "generate:fixture": "bun run src/cli.ts generate --url file://../../assets/catalog.json --out ../../.quadrokit/generated-demo"
34
34
  },
35
35
  "dependencies": {
36
- "@quadrokit/shared": "^0.3.13",
36
+ "@quadrokit/shared": "^0.3.14",
37
37
  "undici": "^6.21.0"
38
38
  },
39
39
  "peerDependencies": {