@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 +85 -3
- package/dist/runtime/collection.d.ts.map +1 -1
- package/dist/runtime/collection.mjs +4 -3
- package/package.json +2 -2
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:
|
|
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":"
|
|
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
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
36
|
+
"@quadrokit/shared": "^0.3.14",
|
|
37
37
|
"undici": "^6.21.0"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|