@quadrokit/client 0.3.12 → 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/generate/codegen.d.ts.map +1 -1
- package/dist/generate/codegen.mjs +3 -0
- package/dist/runtime/catalog-builder.mjs +1 -1
- package/dist/runtime/class-function.d.ts +0 -4
- package/dist/runtime/class-function.d.ts.map +1 -1
- package/dist/runtime/class-function.mjs +28 -6
- package/dist/runtime/collection.d.ts.map +1 -1
- package/dist/runtime/collection.mjs +58 -42
- package/dist/runtime/data-class.d.ts.map +1 -1
- package/dist/runtime/data-class.mjs +10 -2
- package/dist/runtime/datastore.d.ts +2 -1
- package/dist/runtime/datastore.d.ts.map +1 -1
- package/dist/runtime/datastore.mjs +8 -3
- package/dist/runtime/events.d.ts +65 -0
- package/dist/runtime/events.d.ts.map +1 -0
- package/dist/runtime/events.mjs +86 -0
- package/dist/runtime/http.d.ts +24 -3
- package/dist/runtime/http.d.ts.map +1 -1
- package/dist/runtime/http.mjs +65 -14
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.d.ts.map +1 -1
- package/dist/runtime/index.mjs +1 -0
- package/dist/runtime/rxjs.d.ts +20 -0
- package/dist/runtime/rxjs.d.ts.map +1 -0
- package/dist/runtime/rxjs.mjs +27 -0
- package/package.json +13 -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":"codegen.d.ts","sourceRoot":"","sources":["../../src/generate/codegen.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAsC,WAAW,EAAE,MAAM,mBAAmB,CAAA;AA4GxF,yFAAyF;AACzF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,WAAW,GAAG;IAC7D,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAA;QACZ,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QACnC,QAAQ,EAAE,MAAM,EAAE,CAAA;QAClB,iBAAiB,EAAE,MAAM,EAAE,CAAA;QAC3B,2BAA2B,EAAE,MAAM,EAAE,CAAA;QACrC,oBAAoB,EAAE,MAAM,EAAE,CAAA;QAC9B,SAAS,EAAE;YACT,IAAI,EAAE,MAAM,CAAA;YACZ,WAAW,EAAE,MAAM,CAAA;YACnB,2BAA2B,EAAE,MAAM,EAAE,CAAA;SACtC,EAAE,CAAA;KACJ,EAAE,CAAA;IACH,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACzC,aAAa,EAAE,OAAO,CAAA;CACvB,CAuBA;
|
|
1
|
+
{"version":3,"file":"codegen.d.ts","sourceRoot":"","sources":["../../src/generate/codegen.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAsC,WAAW,EAAE,MAAM,mBAAmB,CAAA;AA4GxF,yFAAyF;AACzF,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,WAAW,GAAG;IAC7D,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAA;QACZ,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;QACnC,QAAQ,EAAE,MAAM,EAAE,CAAA;QAClB,iBAAiB,EAAE,MAAM,EAAE,CAAA;QAC3B,2BAA2B,EAAE,MAAM,EAAE,CAAA;QACrC,oBAAoB,EAAE,MAAM,EAAE,CAAA;QAC9B,SAAS,EAAE;YACT,IAAI,EAAE,MAAM,CAAA;YACZ,WAAW,EAAE,MAAM,CAAA;YACnB,2BAA2B,EAAE,MAAM,EAAE,CAAA;SACtC,EAAE,CAAA;KACJ,EAAE,CAAA;IACH,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACzC,aAAa,EAAE,OAAO,CAAA;CACvB,CAuBA;AA6GD,uIAAuI;AACvI,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,WAAW,GAAG,IAAI,CAYvE;AAED,wBAAsB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAsBxF"}
|
|
@@ -193,6 +193,8 @@ export interface QuadroClientConfig {
|
|
|
193
193
|
baseURL: string;
|
|
194
194
|
fetchImpl?: typeof fetch;
|
|
195
195
|
defaultHeaders?: Record<string, string>;
|
|
196
|
+
/** Optional bus for request lifecycle events (loading / success / error). See {@link QuadroEventBus} from \`@quadrokit/client/runtime\`. */
|
|
197
|
+
events?: import('@quadrokit/client/runtime').QuadroEventBus;
|
|
196
198
|
}
|
|
197
199
|
`;
|
|
198
200
|
const body = `
|
|
@@ -201,6 +203,7 @@ export function createClient(config: QuadroClientConfig): QuadroClient {
|
|
|
201
203
|
baseURL: config.baseURL,
|
|
202
204
|
fetchImpl: config.fetchImpl,
|
|
203
205
|
defaultHeaders: config.defaultHeaders,
|
|
206
|
+
events: config.events,
|
|
204
207
|
});
|
|
205
208
|
return buildQuadroClientFromCatalogSpec(
|
|
206
209
|
http,
|
|
@@ -86,7 +86,7 @@ export function buildQuadroClientFromCatalogSpec(http, spec, meta) {
|
|
|
86
86
|
const out = {};
|
|
87
87
|
if (spec.hasAuthentify) {
|
|
88
88
|
out.authentify = {
|
|
89
|
-
login: (body) => callDatastorePath(http, ['authentify', 'login'], { body }),
|
|
89
|
+
login: (body) => callDatastorePath(http, ['authentify', 'login'], { body }, { operation: 'auth.login' }),
|
|
90
90
|
};
|
|
91
91
|
}
|
|
92
92
|
for (const c of spec.classes) {
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ORDA class functions over REST ([4D docs](https://developer.4d.com/docs/REST/classFunctions.html)):
|
|
3
|
-
* dataclass, entity selection (entityCollection), and entity methods.
|
|
4
|
-
*/
|
|
5
1
|
import type { QuadroHttp } from './http.js';
|
|
6
2
|
import { type MethodSelectionQuery } from './query.js';
|
|
7
3
|
export type ClassFunctionHttpOptions = {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"class-function.d.ts","sourceRoot":"","sources":["../../src/runtime/class-function.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"class-function.d.ts","sourceRoot":"","sources":["../../src/runtime/class-function.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAA6B,KAAK,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAEjF,MAAM,MAAM,wBAAwB,GAAG;IACrC,4EAA4E;IAC5E,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IACvB,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,CAAA;AAED,uGAAuG;AACvG,wBAAgB,yBAAyB,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,CAAC,CAK7D;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,CAAC,EAC3C,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,MAAM,EACjB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,SAAS,OAAO,EAAE,EACxB,OAAO,CAAC,EAAE,wBAAwB,GAAG;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9D,OAAO,CAAC,CAAC,CAAC,CAkCZ;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAAG,MAAM,EAC1B,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,SAAS,OAAO,EAAE,EACxB,OAAO,CAAC,EAAE,wBAAwB,GAAG;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,GAC9D,OAAO,CAAC,CAAC,CAAC,CAoCZ;AAED,MAAM,MAAM,6BAA6B,GAAG,wBAAwB,GAAG;IACrE,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB;;;OAGG;IACH,SAAS,CAAC,EAAE,oBAAoB,CAAA;IAChC;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;GAGG;AACH,wBAAsB,4BAA4B,CAAC,CAAC,EAClD,IAAI,EAAE,UAAU,EAChB,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EACnC,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,SAAS,OAAO,EAAE,EACxB,OAAO,CAAC,EAAE,6BAA6B,GACtC,OAAO,CAAC,CAAC,CAAC,CA4CZ"}
|
|
@@ -14,6 +14,12 @@ export async function callDataClassFunction(http, dataClass, functionName, args,
|
|
|
14
14
|
const method = options?.method ?? 'POST';
|
|
15
15
|
const unwrap = options?.unwrapResult !== false;
|
|
16
16
|
const base = `/${dataClass}/${functionName}`;
|
|
17
|
+
const ctx = {
|
|
18
|
+
operation: 'function.dataclass',
|
|
19
|
+
className: dataClass,
|
|
20
|
+
methodName: functionName,
|
|
21
|
+
attributes: { argCount: args.length },
|
|
22
|
+
};
|
|
17
23
|
let body;
|
|
18
24
|
if (method === 'GET') {
|
|
19
25
|
const params = new URLSearchParams();
|
|
@@ -21,14 +27,14 @@ export async function callDataClassFunction(http, dataClass, functionName, args,
|
|
|
21
27
|
body = await http.json(`${base}?${params.toString()}`, {
|
|
22
28
|
method: 'GET',
|
|
23
29
|
signal: options?.signal,
|
|
24
|
-
});
|
|
30
|
+
}, ctx);
|
|
25
31
|
}
|
|
26
32
|
else {
|
|
27
33
|
body = await http.json(base, {
|
|
28
34
|
method: 'POST',
|
|
29
35
|
body: JSON.stringify([...args]),
|
|
30
36
|
signal: options?.signal,
|
|
31
|
-
});
|
|
37
|
+
}, ctx);
|
|
32
38
|
}
|
|
33
39
|
return (unwrap ? unwrapClassFunctionResult(body) : body);
|
|
34
40
|
}
|
|
@@ -40,6 +46,13 @@ export async function callEntityFunction(http, dataClass, entityKey, functionNam
|
|
|
40
46
|
const unwrap = options?.unwrapResult !== false;
|
|
41
47
|
const key = encodeURIComponent(String(entityKey));
|
|
42
48
|
const base = `/${dataClass}(${key})/${functionName}`;
|
|
49
|
+
const ctx = {
|
|
50
|
+
operation: 'function.entity',
|
|
51
|
+
className: dataClass,
|
|
52
|
+
methodName: functionName,
|
|
53
|
+
entityKey,
|
|
54
|
+
attributes: { argCount: args.length },
|
|
55
|
+
};
|
|
43
56
|
let body;
|
|
44
57
|
if (method === 'GET') {
|
|
45
58
|
const params = new URLSearchParams();
|
|
@@ -47,14 +60,14 @@ export async function callEntityFunction(http, dataClass, entityKey, functionNam
|
|
|
47
60
|
body = await http.json(`${base}?${params.toString()}`, {
|
|
48
61
|
method: 'GET',
|
|
49
62
|
signal: options?.signal,
|
|
50
|
-
});
|
|
63
|
+
}, ctx);
|
|
51
64
|
}
|
|
52
65
|
else {
|
|
53
66
|
body = await http.json(base, {
|
|
54
67
|
method: 'POST',
|
|
55
68
|
body: JSON.stringify([...args]),
|
|
56
69
|
signal: options?.signal,
|
|
57
|
-
});
|
|
70
|
+
}, ctx);
|
|
58
71
|
}
|
|
59
72
|
return (unwrap ? unwrapClassFunctionResult(body) : body);
|
|
60
73
|
}
|
|
@@ -72,6 +85,15 @@ export async function callEntityCollectionFunction(http, dataClass, relationMap,
|
|
|
72
85
|
path += `/$entityset/${encodeURIComponent(id)}`;
|
|
73
86
|
}
|
|
74
87
|
path += buildMethodSelectionQuery(dataClass, relationMap, options?.selection);
|
|
88
|
+
const ctx = {
|
|
89
|
+
operation: 'function.entityCollection',
|
|
90
|
+
className: dataClass,
|
|
91
|
+
methodName: functionName,
|
|
92
|
+
attributes: {
|
|
93
|
+
argCount: args.length,
|
|
94
|
+
...(options?.entitySet ? { entitySet: options.entitySet } : {}),
|
|
95
|
+
},
|
|
96
|
+
};
|
|
75
97
|
let body;
|
|
76
98
|
if (method === 'GET') {
|
|
77
99
|
const params = new URLSearchParams();
|
|
@@ -80,14 +102,14 @@ export async function callEntityCollectionFunction(http, dataClass, relationMap,
|
|
|
80
102
|
body = await http.json(`${path}${join}${params.toString()}`, {
|
|
81
103
|
method: 'GET',
|
|
82
104
|
signal: options?.signal,
|
|
83
|
-
});
|
|
105
|
+
}, ctx);
|
|
84
106
|
}
|
|
85
107
|
else {
|
|
86
108
|
body = await http.json(path, {
|
|
87
109
|
method: 'POST',
|
|
88
110
|
body: JSON.stringify([...args]),
|
|
89
111
|
signal: options?.signal,
|
|
90
|
-
});
|
|
112
|
+
}, ctx);
|
|
91
113
|
}
|
|
92
114
|
return (unwrap ? unwrapClassFunctionResult(body) : body);
|
|
93
115
|
}
|
|
@@ -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';
|
|
@@ -93,57 +94,68 @@ export function createCollection(ctx, initialOptions, mapRow) {
|
|
|
93
94
|
}
|
|
94
95
|
}
|
|
95
96
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
97
|
+
const listContext = {
|
|
98
|
+
operation: 'collection.list',
|
|
99
|
+
className: ctx.className,
|
|
100
|
+
attributes: {
|
|
101
|
+
page,
|
|
102
|
+
useEntitySet,
|
|
103
|
+
...(initialOptions.filter !== undefined ? { filter: initialOptions.filter } : {}),
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
return ctx.http.runWithEvents(listContext, ctx.path, 'GET', async () => {
|
|
107
|
+
if (useEntitySet && !entitySetUrl) {
|
|
108
|
+
const qsCreate = buildEntitySetCreationParams(initialOptions);
|
|
109
|
+
const createPath = `${ctx.path}${qsCreate}`;
|
|
110
|
+
const resCreate = await ctx.http.rawRequest(createPath, {
|
|
111
|
+
signal: initialOptions.signal,
|
|
112
|
+
});
|
|
113
|
+
const textCreate = await resCreate.text();
|
|
114
|
+
if (!resCreate.ok) {
|
|
115
|
+
throw new QuadroHttpError(resCreate.status, textCreate);
|
|
116
|
+
}
|
|
117
|
+
const jsonCreate = textCreate ? JSON.parse(textCreate) : [];
|
|
118
|
+
await parseEntitySetFromResponse(jsonCreate, resCreate);
|
|
119
|
+
if (!entitySetUrl) {
|
|
120
|
+
return unwrapEntityList(ctx.className, jsonCreate);
|
|
121
|
+
}
|
|
122
|
+
const qsPage = buildEntitySetPageParams(skip, pageSize, initialOptions.select);
|
|
123
|
+
const base = entitySetUrl;
|
|
124
|
+
const pagePath = qsPage
|
|
125
|
+
? base.includes('?')
|
|
126
|
+
? `${base}&${qsPage.slice(1)}`
|
|
127
|
+
: `${base}${qsPage}`
|
|
128
|
+
: base;
|
|
129
|
+
const res = await ctx.http.rawRequest(pagePath, {
|
|
130
|
+
signal: initialOptions.signal,
|
|
131
|
+
});
|
|
132
|
+
const text = await res.text();
|
|
133
|
+
if (!res.ok) {
|
|
134
|
+
throw new QuadroHttpError(res.status, text);
|
|
135
|
+
}
|
|
136
|
+
const json = text ? JSON.parse(text) : [];
|
|
137
|
+
return unwrapEntityList(ctx.className, json);
|
|
105
138
|
}
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
139
|
+
let requestPath;
|
|
140
|
+
if (entitySetUrl && useEntitySet) {
|
|
141
|
+
const qs = buildEntitySetPageParams(skip, pageSize, initialOptions.select);
|
|
142
|
+
const base = entitySetUrl;
|
|
143
|
+
requestPath = qs ? (base.includes('?') ? `${base}&${qs.slice(1)}` : `${base}${qs}`) : base;
|
|
110
144
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
: `${base}${qsPage}`
|
|
117
|
-
: base;
|
|
118
|
-
const res = await ctx.http.request(pagePath, {
|
|
145
|
+
else {
|
|
146
|
+
const qs = buildListSearchParams(ctx.className, { ...initialOptions, page }, ctx.relationMap);
|
|
147
|
+
requestPath = `${ctx.path}${qs}`;
|
|
148
|
+
}
|
|
149
|
+
const res = await ctx.http.rawRequest(requestPath, {
|
|
119
150
|
signal: initialOptions.signal,
|
|
120
151
|
});
|
|
121
152
|
const text = await res.text();
|
|
122
153
|
if (!res.ok) {
|
|
123
|
-
throw new
|
|
154
|
+
throw new QuadroHttpError(res.status, text);
|
|
124
155
|
}
|
|
125
156
|
const json = text ? JSON.parse(text) : [];
|
|
126
157
|
return unwrapEntityList(ctx.className, json);
|
|
127
|
-
}
|
|
128
|
-
let requestPath;
|
|
129
|
-
if (entitySetUrl && useEntitySet) {
|
|
130
|
-
const qs = buildEntitySetPageParams(skip, pageSize, initialOptions.select);
|
|
131
|
-
const base = entitySetUrl;
|
|
132
|
-
requestPath = qs ? (base.includes('?') ? `${base}&${qs.slice(1)}` : `${base}${qs}`) : base;
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
const qs = buildListSearchParams(ctx.className, { ...initialOptions, page }, ctx.relationMap);
|
|
136
|
-
requestPath = `${ctx.path}${qs}`;
|
|
137
|
-
}
|
|
138
|
-
const res = await ctx.http.request(requestPath, {
|
|
139
|
-
signal: initialOptions.signal,
|
|
140
158
|
});
|
|
141
|
-
const text = await res.text();
|
|
142
|
-
if (!res.ok) {
|
|
143
|
-
throw new Error(`List failed ${res.status}: ${text.slice(0, 200)}`);
|
|
144
|
-
}
|
|
145
|
-
const json = text ? JSON.parse(text) : [];
|
|
146
|
-
return unwrapEntityList(ctx.className, json);
|
|
147
159
|
}
|
|
148
160
|
async function* iteratePages() {
|
|
149
161
|
let page = initialOptions.page ?? 1;
|
|
@@ -190,6 +202,10 @@ export function createCollection(ctx, initialOptions, mapRow) {
|
|
|
190
202
|
}
|
|
191
203
|
await Promise.all(ids.map((id) => ctx.http.void(`${ctx.className}(${encodeURIComponent(String(id))})`, {
|
|
192
204
|
method: 'DELETE',
|
|
205
|
+
}, {
|
|
206
|
+
operation: 'dataclass.delete',
|
|
207
|
+
className: ctx.className,
|
|
208
|
+
entityKey: id,
|
|
193
209
|
})));
|
|
194
210
|
seenIds.clear();
|
|
195
211
|
},
|
|
@@ -201,7 +217,7 @@ export function createCollection(ctx, initialOptions, mapRow) {
|
|
|
201
217
|
entitySetUrl = undefined;
|
|
202
218
|
try {
|
|
203
219
|
const path = appendReleaseQuery(toRelease);
|
|
204
|
-
const res = await ctx.http.request(path, { method: 'GET' });
|
|
220
|
+
const res = await ctx.http.request(path, { method: 'GET' }, { operation: 'collection.release', className: ctx.className });
|
|
205
221
|
await res.text();
|
|
206
222
|
}
|
|
207
223
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data-class.d.ts","sourceRoot":"","sources":["../../src/runtime/data-class.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,iBAAiB,EAAoB,MAAM,iBAAiB,CAAA;AACjG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE3C,OAAO,EAAgB,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE5D,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,UAAU,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,wGAAwG;IACxG,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CAC5B;AAED,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,sBAAsB;QAIjE,CAAC,SAAS,SAAS,MAAM,EAAE,0BACnB,iBAAiB,GAAG;QAAE,MAAM,CAAC,EAAE,CAAC,CAAA;KAAE,GAC3C,gBAAgB,CAAC,CAAC,SAAS,SAAS,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAc7C,CAAC,SAAS,SAAS,MAAM,EAAE,oBAC/B,MAAM,GAAG,MAAM,YACT;QAAE,MAAM,CAAC,EAAE,CAAC,CAAA;KAAE,GACvB,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"data-class.d.ts","sourceRoot":"","sources":["../../src/runtime/data-class.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,KAAK,gBAAgB,EAAE,KAAK,iBAAiB,EAAoB,MAAM,iBAAiB,CAAA;AACjG,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE3C,OAAO,EAAgB,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE5D,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,UAAU,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,wGAAwG;IACxG,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAA;CAC5B;AAED,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,OAAO,EAAE,GAAG,EAAE,sBAAsB;QAIjE,CAAC,SAAS,SAAS,MAAM,EAAE,0BACnB,iBAAiB,GAAG;QAAE,MAAM,CAAC,EAAE,CAAC,CAAA;KAAE,GAC3C,gBAAgB,CAAC,CAAC,SAAS,SAAS,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;QAc7C,CAAC,SAAS,SAAS,MAAM,EAAE,oBAC/B,MAAM,GAAG,MAAM,YACT;QAAE,MAAM,CAAC,EAAE,CAAC,CAAA;KAAE,GACvB,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;eAiBH,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;UAkB7C,CAAC,SAAS,SAAS,MAAM,EAAE,wBACvB,MAAM,YACJ,iBAAiB,GAAG;QAAE,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,CAAC,CAAA;KAAE,GAC/D,gBAAgB,CAAC,CAAC,SAAS,SAAS,KAAK,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC;EAe1D;AA6BD,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,UAAU,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;IACnB,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAA;IACzB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACnC,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAA;CAC5C;AAED,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;IACnB,gFAAgF;IAChF,2BAA2B,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAChD;AAED,6FAA6F;AAC7F,wBAAgB,2BAA2B,CAAC,CAAC,SAAS,MAAM,EAC1D,MAAM,EAAE,CAAC,EACT,GAAG,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,CAAC,EACvE,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,CAAC,CAiBH;AAgBD,iEAAiE;AACjE,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,IAAI,CAAC,sBAAsB,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,GAAG,UAAU,CAAC,EACpF,WAAW,EAAE,SAAS,MAAM,EAAE,GAC7B,IAAI,CAmBN;AAED,yEAAyE;AACzE,wBAAgB,wBAAwB,CACtC,GAAG,EAAE,sBAAsB,EAC3B,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,MAAM,EACvB,2BAA2B,CAAC,EAAE,SAAS,MAAM,EAAE;SAKxC,CAAC,SAAS,SAAS,MAAM,EAAE,0BACpB,iBAAiB,GAAG;QAAE,MAAM,CAAC,EAAE,CAAC,CAAA;KAAE,GAC3C,gBAAgB,CAAC,OAAO,CAAC;EAmB/B;AAED,wBAAgB,iBAAiB,CAC/B,GAAG,EAAE,OAAO,EACZ,GAAG,EAAE,sBAAsB,EAC3B,SAAS,EAAE,SAAS,qBAAqB,EAAE,GAC1C,IAAI,CAYN;AAED,OAAO,EAAE,gBAAgB,EAAE,CAAA"}
|
|
@@ -19,13 +19,21 @@ export function makeDataClassApi(cfg) {
|
|
|
19
19
|
? buildEntityParams(cfg.className, options.select, cfg.relationMap)
|
|
20
20
|
: '';
|
|
21
21
|
const key = encodeURIComponent(String(id));
|
|
22
|
-
const body = await cfg.http.json(`${cfg.className}(${key})${qs}
|
|
22
|
+
const body = await cfg.http.json(`${cfg.className}(${key})${qs}`, {}, {
|
|
23
|
+
operation: 'dataclass.get',
|
|
24
|
+
className: cfg.className,
|
|
25
|
+
entityKey: id,
|
|
26
|
+
});
|
|
23
27
|
return unwrapEntity(cfg.className, body);
|
|
24
28
|
},
|
|
25
29
|
async delete(id) {
|
|
26
30
|
const key = encodeURIComponent(String(id));
|
|
27
31
|
try {
|
|
28
|
-
await cfg.http.void(`${cfg.className}(${key})`, { method: 'DELETE' }
|
|
32
|
+
await cfg.http.void(`${cfg.className}(${key})`, { method: 'DELETE' }, {
|
|
33
|
+
operation: 'dataclass.delete',
|
|
34
|
+
className: cfg.className,
|
|
35
|
+
entityKey: id,
|
|
36
|
+
});
|
|
29
37
|
return true;
|
|
30
38
|
}
|
|
31
39
|
catch {
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { QuadroRequestContext } from './events.js';
|
|
1
2
|
import type { QuadroHttp } from './http.js';
|
|
2
3
|
/** Call nested datastore paths like `authentify/login` (POST by default). */
|
|
3
4
|
export declare function callDatastorePath(http: QuadroHttp, segments: readonly string[], init?: {
|
|
4
5
|
method?: 'GET' | 'POST';
|
|
5
6
|
body?: unknown;
|
|
6
|
-
}): Promise<unknown>;
|
|
7
|
+
}, context?: QuadroRequestContext): Promise<unknown>;
|
|
7
8
|
export declare function createDatastoreNamespace(_http: QuadroHttp, tree: Record<string, unknown>): Record<string, unknown>;
|
|
8
9
|
//# sourceMappingURL=datastore.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"datastore.d.ts","sourceRoot":"","sources":["../../src/runtime/datastore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE3C,6EAA6E;AAC7E,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"datastore.d.ts","sourceRoot":"","sources":["../../src/runtime/datastore.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAA;AAE3C,6EAA6E;AAC7E,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,UAAU,EAChB,QAAQ,EAAE,SAAS,MAAM,EAAE,EAC3B,IAAI,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,EAClD,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,OAAO,CAAC,CAoBlB;AAED,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,UAAU,EACjB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAEzB"}
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
/** Call nested datastore paths like `authentify/login` (POST by default). */
|
|
2
|
-
export async function callDatastorePath(http, segments, init) {
|
|
2
|
+
export async function callDatastorePath(http, segments, init, context) {
|
|
3
3
|
const path = `/${segments.join('/')}`;
|
|
4
4
|
const method = init?.method ?? 'POST';
|
|
5
|
+
const ctx = context ??
|
|
6
|
+
{
|
|
7
|
+
operation: 'datastore',
|
|
8
|
+
methodName: segments.join('/'),
|
|
9
|
+
};
|
|
5
10
|
if (method === 'GET') {
|
|
6
|
-
return http.json(path, { method: 'GET' });
|
|
11
|
+
return http.json(path, { method: 'GET' }, ctx);
|
|
7
12
|
}
|
|
8
13
|
return http.json(path, {
|
|
9
14
|
method: 'POST',
|
|
10
15
|
body: init?.body !== undefined ? JSON.stringify(init.body) : undefined,
|
|
11
|
-
});
|
|
16
|
+
}, ctx);
|
|
12
17
|
}
|
|
13
18
|
export function createDatastoreNamespace(_http, tree) {
|
|
14
19
|
return tree;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured lifecycle events for all Quadro HTTP-backed operations.
|
|
3
|
+
* Use {@link QuadroEventBus} with {@link QuadroHttp} options, or bridge to RxJS via `@quadrokit/client/rx`.
|
|
4
|
+
*/
|
|
5
|
+
/** What the client is doing (for filtering and UI). */
|
|
6
|
+
export type QuadroOperation = 'http.json' | 'http.void' | 'http.request' | 'collection.list' | 'collection.release' | 'dataclass.get' | 'dataclass.delete' | 'function.dataclass' | 'function.entity' | 'function.entityCollection' | 'datastore' | 'auth.login';
|
|
7
|
+
/** Optional metadata for an operation (class, method, entity key, arbitrary tags). */
|
|
8
|
+
export type QuadroRequestContext = {
|
|
9
|
+
operation: QuadroOperation;
|
|
10
|
+
/** Dataclass name when applicable. */
|
|
11
|
+
className?: string;
|
|
12
|
+
/** 4D method / function name for `function.*` operations. */
|
|
13
|
+
methodName?: string;
|
|
14
|
+
/** Primary key for entity-scoped calls. */
|
|
15
|
+
entityKey?: string | number;
|
|
16
|
+
/** Extra structured data (page, filter summary, datastore path, …). */
|
|
17
|
+
attributes?: Record<string, unknown>;
|
|
18
|
+
};
|
|
19
|
+
export type QuadroLoadingEvent = {
|
|
20
|
+
type: 'loading';
|
|
21
|
+
operationId: string;
|
|
22
|
+
context: QuadroRequestContext;
|
|
23
|
+
path: string;
|
|
24
|
+
method: string;
|
|
25
|
+
startedAt: number;
|
|
26
|
+
};
|
|
27
|
+
export type QuadroSuccessEvent = {
|
|
28
|
+
type: 'success';
|
|
29
|
+
operationId: string;
|
|
30
|
+
context: QuadroRequestContext;
|
|
31
|
+
path: string;
|
|
32
|
+
durationMs: number;
|
|
33
|
+
/** HTTP status when applicable. */
|
|
34
|
+
status?: number;
|
|
35
|
+
/** Result payload: parsed JSON, rows, void, or a summary for raw `Response`. */
|
|
36
|
+
body?: unknown;
|
|
37
|
+
};
|
|
38
|
+
export type QuadroErrorEvent = {
|
|
39
|
+
type: 'error';
|
|
40
|
+
operationId: string;
|
|
41
|
+
context: QuadroRequestContext;
|
|
42
|
+
path: string;
|
|
43
|
+
durationMs: number;
|
|
44
|
+
message: string;
|
|
45
|
+
error: unknown;
|
|
46
|
+
/** HTTP status when the failure came from the transport. */
|
|
47
|
+
status?: number;
|
|
48
|
+
};
|
|
49
|
+
export type QuadroClientEvent = QuadroLoadingEvent | QuadroSuccessEvent | QuadroErrorEvent;
|
|
50
|
+
/**
|
|
51
|
+
* Subscribe to {@link QuadroClientEvent} from a {@link QuadroHttp} client.
|
|
52
|
+
* Pass `new QuadroEventBus()` as `events` in {@link QuadroFetchOptions}.
|
|
53
|
+
*/
|
|
54
|
+
export declare class QuadroEventBus {
|
|
55
|
+
private readonly listeners;
|
|
56
|
+
subscribe(listener: (e: QuadroClientEvent) => void): () => void;
|
|
57
|
+
/** @internal */
|
|
58
|
+
emit(event: QuadroClientEvent): void;
|
|
59
|
+
/**
|
|
60
|
+
* Run an async operation with loading → success | error events.
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
63
|
+
runWithEvents<T>(context: QuadroRequestContext, path: string, method: string, fn: () => Promise<T>): Promise<T>;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/runtime/events.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,uDAAuD;AACvD,MAAM,MAAM,eAAe,GACvB,WAAW,GACX,WAAW,GACX,cAAc,GACd,iBAAiB,GACjB,oBAAoB,GACpB,eAAe,GACf,kBAAkB,GAClB,oBAAoB,GACpB,iBAAiB,GACjB,2BAA2B,GAC3B,WAAW,GACX,YAAY,CAAA;AAEhB,sFAAsF;AACtF,MAAM,MAAM,oBAAoB,GAAG;IACjC,SAAS,EAAE,eAAe,CAAA;IAC1B,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,6DAA6D;IAC7D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC3B,uEAAuE;IACvE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACrC,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,SAAS,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,oBAAoB,CAAA;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,SAAS,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,oBAAoB,CAAA;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,mCAAmC;IACnC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,gFAAgF;IAChF,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,EAAE,OAAO,CAAA;IACb,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,oBAAoB,CAAA;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,KAAK,EAAE,OAAO,CAAA;IACd,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,GAAG,kBAAkB,GAAG,gBAAgB,CAAA;AAM1F;;;GAGG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA4C;IAEtE,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,iBAAiB,KAAK,IAAI,GAAG,MAAM,IAAI;IAO/D,gBAAgB;IAChB,IAAI,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI;IAUpC;;;OAGG;IACH,aAAa,CAAC,CAAC,EACb,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC;CAmDd"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structured lifecycle events for all Quadro HTTP-backed operations.
|
|
3
|
+
* Use {@link QuadroEventBus} with {@link QuadroHttp} options, or bridge to RxJS via `@quadrokit/client/rx`.
|
|
4
|
+
*/
|
|
5
|
+
function newOperationId() {
|
|
6
|
+
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 11)}`;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Subscribe to {@link QuadroClientEvent} from a {@link QuadroHttp} client.
|
|
10
|
+
* Pass `new QuadroEventBus()` as `events` in {@link QuadroFetchOptions}.
|
|
11
|
+
*/
|
|
12
|
+
export class QuadroEventBus {
|
|
13
|
+
listeners = new Set();
|
|
14
|
+
subscribe(listener) {
|
|
15
|
+
this.listeners.add(listener);
|
|
16
|
+
return () => {
|
|
17
|
+
this.listeners.delete(listener);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
/** @internal */
|
|
21
|
+
emit(event) {
|
|
22
|
+
for (const fn of this.listeners) {
|
|
23
|
+
try {
|
|
24
|
+
fn(event);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
/* listener errors must not break the client */
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Run an async operation with loading → success | error events.
|
|
33
|
+
* @internal
|
|
34
|
+
*/
|
|
35
|
+
runWithEvents(context, path, method, fn) {
|
|
36
|
+
const operationId = newOperationId();
|
|
37
|
+
const startedAt = Date.now();
|
|
38
|
+
this.emit({ type: 'loading', operationId, context, path, method, startedAt });
|
|
39
|
+
return (async () => {
|
|
40
|
+
try {
|
|
41
|
+
const result = await fn();
|
|
42
|
+
const durationMs = Date.now() - startedAt;
|
|
43
|
+
let body = result;
|
|
44
|
+
if (result instanceof Response) {
|
|
45
|
+
body = {
|
|
46
|
+
kind: 'response',
|
|
47
|
+
status: result.status,
|
|
48
|
+
ok: result.ok,
|
|
49
|
+
url: result.url,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
this.emit({
|
|
53
|
+
type: 'success',
|
|
54
|
+
operationId,
|
|
55
|
+
context,
|
|
56
|
+
path,
|
|
57
|
+
durationMs,
|
|
58
|
+
status: result instanceof Response ? result.status : undefined,
|
|
59
|
+
body,
|
|
60
|
+
});
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
const durationMs = Date.now() - startedAt;
|
|
65
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
66
|
+
const status = error &&
|
|
67
|
+
typeof error === 'object' &&
|
|
68
|
+
'status' in error &&
|
|
69
|
+
typeof error.status === 'number'
|
|
70
|
+
? error.status
|
|
71
|
+
: undefined;
|
|
72
|
+
this.emit({
|
|
73
|
+
type: 'error',
|
|
74
|
+
operationId,
|
|
75
|
+
context,
|
|
76
|
+
path,
|
|
77
|
+
durationMs,
|
|
78
|
+
message,
|
|
79
|
+
error,
|
|
80
|
+
status,
|
|
81
|
+
});
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
})();
|
|
85
|
+
}
|
|
86
|
+
}
|
package/dist/runtime/http.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
+
import type { QuadroClientEvent, QuadroEventBus, QuadroRequestContext } from './events.js';
|
|
1
2
|
export interface QuadroFetchOptions {
|
|
2
3
|
baseURL: string;
|
|
3
4
|
fetchImpl?: typeof fetch;
|
|
4
5
|
/** Extra headers on every request (e.g. Authorization for generate). */
|
|
5
6
|
defaultHeaders?: Record<string, string>;
|
|
7
|
+
/**
|
|
8
|
+
* Optional bus for {@link QuadroClientEvent}: loading, success, error per logical operation.
|
|
9
|
+
* Create with `new QuadroEventBus()` and pass here; subscribe before calling the client.
|
|
10
|
+
*/
|
|
11
|
+
events?: QuadroEventBus;
|
|
6
12
|
}
|
|
7
13
|
export declare function normalizeBaseURL(baseURL: string): string;
|
|
8
14
|
/**
|
|
@@ -14,8 +20,23 @@ export declare class QuadroHttp {
|
|
|
14
20
|
private readonly opts;
|
|
15
21
|
constructor(opts: QuadroFetchOptions);
|
|
16
22
|
get baseURL(): string;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
23
|
+
/** Optional event bus from constructor options. */
|
|
24
|
+
get events(): QuadroEventBus | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Run a logical operation with loading / success / error events (when `events` is set).
|
|
27
|
+
* Use from collection code and other multi-step flows that do not use {@link json}.
|
|
28
|
+
*/
|
|
29
|
+
runWithEvents<T>(context: QuadroRequestContext, path: string, method: string, fn: () => Promise<T>): Promise<T>;
|
|
30
|
+
/** Subscribe without reaching into options: `quadro._http.subscribe(cb)`. */
|
|
31
|
+
subscribe(listener: (e: QuadroClientEvent) => void): () => void;
|
|
32
|
+
/** @internal Raw fetch without events (used inside {@link runWithEvents}). */
|
|
33
|
+
rawRequest(path: string, init?: RequestInit): Promise<Response>;
|
|
34
|
+
/**
|
|
35
|
+
* Low-level request. When `events` is configured, emits loading/success/error unless
|
|
36
|
+
* you pass `context: undefined` and use {@link rawRequest} for manual control.
|
|
37
|
+
*/
|
|
38
|
+
request(path: string, init?: RequestInit, context?: QuadroRequestContext): Promise<Response>;
|
|
39
|
+
json<T>(path: string, init?: RequestInit, context?: QuadroRequestContext): Promise<T>;
|
|
40
|
+
void(path: string, init?: RequestInit, context?: QuadroRequestContext): Promise<void>;
|
|
20
41
|
}
|
|
21
42
|
//# sourceMappingURL=http.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/runtime/http.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/runtime/http.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAE1F,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACxB,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC;;;OAGG;IACH,MAAM,CAAC,EAAE,cAAc,CAAA;CACxB;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAa5D;AAED,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,kBAAkB;IAErD,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,mDAAmD;IACnD,IAAI,MAAM,IAAI,cAAc,GAAG,SAAS,CAEvC;IAED;;;OAGG;IACH,aAAa,CAAC,CAAC,EACb,OAAO,EAAE,oBAAoB,EAC7B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC;IAQb,6EAA6E;IAC7E,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,iBAAiB,KAAK,IAAI,GAAG,MAAM,IAAI;IAI/D,8EAA8E;IACxE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAwBzE;;;OAGG;IACG,OAAO,CACX,IAAI,EAAE,MAAM,EACZ,IAAI,GAAE,WAAgB,EACtB,OAAO,CAAC,EAAE,oBAAoB,GAC7B,OAAO,CAAC,QAAQ,CAAC;IAUd,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,CAAC,CAAC;IAqBzF,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,EAAE,OAAO,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;CAgBhG"}
|
package/dist/runtime/http.mjs
CHANGED
|
@@ -29,7 +29,27 @@ export class QuadroHttp {
|
|
|
29
29
|
get baseURL() {
|
|
30
30
|
return normalizeBaseURL(this.opts.baseURL);
|
|
31
31
|
}
|
|
32
|
-
|
|
32
|
+
/** Optional event bus from constructor options. */
|
|
33
|
+
get events() {
|
|
34
|
+
return this.opts.events;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Run a logical operation with loading / success / error events (when `events` is set).
|
|
38
|
+
* Use from collection code and other multi-step flows that do not use {@link json}.
|
|
39
|
+
*/
|
|
40
|
+
runWithEvents(context, path, method, fn) {
|
|
41
|
+
const bus = this.opts.events;
|
|
42
|
+
if (!bus) {
|
|
43
|
+
return fn();
|
|
44
|
+
}
|
|
45
|
+
return bus.runWithEvents(context, path, method, fn);
|
|
46
|
+
}
|
|
47
|
+
/** Subscribe without reaching into options: `quadro._http.subscribe(cb)`. */
|
|
48
|
+
subscribe(listener) {
|
|
49
|
+
return this.opts.events?.subscribe(listener) ?? (() => { });
|
|
50
|
+
}
|
|
51
|
+
/** @internal Raw fetch without events (used inside {@link runWithEvents}). */
|
|
52
|
+
async rawRequest(path, init = {}) {
|
|
33
53
|
const url = path.startsWith('http')
|
|
34
54
|
? path
|
|
35
55
|
: `${this.baseURL}${path.startsWith('/') ? '' : '/'}${path}`;
|
|
@@ -52,22 +72,53 @@ export class QuadroHttp {
|
|
|
52
72
|
headers,
|
|
53
73
|
});
|
|
54
74
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
|
|
75
|
+
/**
|
|
76
|
+
* Low-level request. When `events` is configured, emits loading/success/error unless
|
|
77
|
+
* you pass `context: undefined` and use {@link rawRequest} for manual control.
|
|
78
|
+
*/
|
|
79
|
+
async request(path, init = {}, context) {
|
|
80
|
+
const ctx = context ?? { operation: 'http.request' };
|
|
81
|
+
const method = (init.method ?? 'GET').toUpperCase();
|
|
82
|
+
const bus = this.opts.events;
|
|
83
|
+
if (!bus) {
|
|
84
|
+
return this.rawRequest(path, init);
|
|
63
85
|
}
|
|
64
|
-
return
|
|
86
|
+
return bus.runWithEvents(ctx, path, method, () => this.rawRequest(path, init));
|
|
65
87
|
}
|
|
66
|
-
async
|
|
67
|
-
const
|
|
68
|
-
|
|
88
|
+
async json(path, init = {}, context) {
|
|
89
|
+
const ctx = context ?? { operation: 'http.json' };
|
|
90
|
+
const method = (init.method ?? 'GET').toUpperCase();
|
|
91
|
+
const bus = this.opts.events;
|
|
92
|
+
const exec = async () => {
|
|
93
|
+
const res = await this.rawRequest(path, init);
|
|
69
94
|
const text = await res.text();
|
|
70
|
-
|
|
95
|
+
if (!res.ok) {
|
|
96
|
+
throw new QuadroHttpError(res.status, text);
|
|
97
|
+
}
|
|
98
|
+
if (!text) {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
return JSON.parse(text);
|
|
102
|
+
};
|
|
103
|
+
if (!bus) {
|
|
104
|
+
return exec();
|
|
105
|
+
}
|
|
106
|
+
return bus.runWithEvents(ctx, path, method, exec);
|
|
107
|
+
}
|
|
108
|
+
async void(path, init = {}, context) {
|
|
109
|
+
const ctx = context ?? { operation: 'http.void' };
|
|
110
|
+
const method = (init.method ?? 'GET').toUpperCase();
|
|
111
|
+
const bus = this.opts.events;
|
|
112
|
+
const exec = async () => {
|
|
113
|
+
const res = await this.rawRequest(path, init);
|
|
114
|
+
if (!res.ok) {
|
|
115
|
+
const text = await res.text();
|
|
116
|
+
throw new QuadroHttpError(res.status, text);
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
if (!bus) {
|
|
120
|
+
return exec();
|
|
71
121
|
}
|
|
122
|
+
return bus.runWithEvents(ctx, path, method, exec);
|
|
72
123
|
}
|
|
73
124
|
}
|
package/dist/runtime/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export { type CollectionContext, type CollectionHandle, type CollectionOptions,
|
|
|
5
5
|
export { attachEntityClassMethods, attachRelatedApis, bindEntityCollectionMethods, type DataClassRuntimeConfig, type EntityNavigationConfig, makeDataClassApi, makeRelatedCollectionApi, type RelatedNavigationSpec, } from './data-class.js';
|
|
6
6
|
export { callDatastorePath, createDatastoreNamespace } from './datastore.js';
|
|
7
7
|
export { QuadroHttpError } from './errors.js';
|
|
8
|
+
export { type QuadroClientEvent, type QuadroErrorEvent, QuadroEventBus, type QuadroLoadingEvent, type QuadroOperation, type QuadroRequestContext, type QuadroSuccessEvent, } from './events.js';
|
|
8
9
|
export { mountPathFromBaseURL, normalizeBaseURL, type QuadroFetchOptions, QuadroHttp, } from './http.js';
|
|
9
10
|
export type { Paths1, QuadroAttributePaths, SelectedEntity } from './paths.js';
|
|
10
11
|
export { buildEntitySetCreationParams, buildEntitySetPageParams, buildListSearchParams, buildMethodSelectionQuery, type ListQueryParams, type MethodSelectionQuery, } from './query.js';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gCAAgC,EAChC,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,GACxB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,KAAK,wBAAwB,EAC7B,qBAAqB,EACrB,4BAA4B,EAC5B,kBAAkB,EAClB,KAAK,6BAA6B,EAClC,yBAAyB,GAC1B,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,gBAAgB,GACjB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,2BAA2B,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,gBAAgB,EAChB,wBAAwB,EACxB,KAAK,qBAAqB,GAC3B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,UAAU,GACX,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,MAAM,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC9E,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,qBAAqB,EACrB,yBAAyB,EACzB,KAAK,eAAe,EACpB,KAAK,oBAAoB,GAC1B,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/runtime/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gCAAgC,EAChC,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,GACxB,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,KAAK,wBAAwB,EAC7B,qBAAqB,EACrB,4BAA4B,EAC5B,kBAAkB,EAClB,KAAK,6BAA6B,EAClC,yBAAyB,GAC1B,MAAM,qBAAqB,CAAA;AAC5B,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EACtB,gBAAgB,GACjB,MAAM,iBAAiB,CAAA;AACxB,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,2BAA2B,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,gBAAgB,EAChB,wBAAwB,EACxB,KAAK,qBAAqB,GAC3B,MAAM,iBAAiB,CAAA;AACxB,OAAO,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAA;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAC7C,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,cAAc,EACd,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,GACxB,MAAM,aAAa,CAAA;AACpB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,KAAK,kBAAkB,EACvB,UAAU,GACX,MAAM,WAAW,CAAA;AAClB,YAAY,EAAE,MAAM,EAAE,oBAAoB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAC9E,OAAO,EACL,4BAA4B,EAC5B,wBAAwB,EACxB,qBAAqB,EACrB,yBAAyB,EACzB,KAAK,eAAe,EACpB,KAAK,oBAAoB,GAC1B,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA"}
|
package/dist/runtime/index.mjs
CHANGED
|
@@ -4,6 +4,7 @@ export { createCollection, } from './collection.mjs';
|
|
|
4
4
|
export { attachEntityClassMethods, attachRelatedApis, bindEntityCollectionMethods, makeDataClassApi, makeRelatedCollectionApi, } from './data-class.mjs';
|
|
5
5
|
export { callDatastorePath, createDatastoreNamespace } from './datastore.mjs';
|
|
6
6
|
export { QuadroHttpError } from './errors.mjs';
|
|
7
|
+
export { QuadroEventBus, } from './events.mjs';
|
|
7
8
|
export { mountPathFromBaseURL, normalizeBaseURL, QuadroHttp, } from './http.mjs';
|
|
8
9
|
export { buildEntitySetCreationParams, buildEntitySetPageParams, buildListSearchParams, buildMethodSelectionQuery, } from './query.mjs';
|
|
9
10
|
export { unwrapEntity, unwrapEntityList } from './unwrap.mjs';
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RxJS bridge for {@link QuadroEventBus}. Install `rxjs` as a dependency in your app.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { QuadroEventBus } from '@quadrokit/client/runtime'
|
|
7
|
+
* import { quadroEventsObservable } from '@quadrokit/client/rx'
|
|
8
|
+
* import { filter } from 'rxjs/operators'
|
|
9
|
+
*
|
|
10
|
+
* const bus = new QuadroEventBus()
|
|
11
|
+
* const quadro = createClient({ baseURL: '/rest', events: bus })
|
|
12
|
+
* quadroEventsObservable(bus)
|
|
13
|
+
* .pipe(filter((e) => e.type === 'error'))
|
|
14
|
+
* .subscribe(console.warn)
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
import { Observable } from 'rxjs';
|
|
18
|
+
import type { QuadroClientEvent, QuadroEventBus } from './events.js';
|
|
19
|
+
export declare function quadroEventsObservable(bus: QuadroEventBus): Observable<QuadroClientEvent>;
|
|
20
|
+
//# sourceMappingURL=rxjs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rxjs.d.ts","sourceRoot":"","sources":["../../src/runtime/rxjs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAA;AACjC,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAEpE,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,cAAc,GAAG,UAAU,CAAC,iBAAiB,CAAC,CASzF"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RxJS bridge for {@link QuadroEventBus}. Install `rxjs` as a dependency in your app.
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```ts
|
|
6
|
+
* import { QuadroEventBus } from '@quadrokit/client/runtime'
|
|
7
|
+
* import { quadroEventsObservable } from '@quadrokit/client/rx'
|
|
8
|
+
* import { filter } from 'rxjs/operators'
|
|
9
|
+
*
|
|
10
|
+
* const bus = new QuadroEventBus()
|
|
11
|
+
* const quadro = createClient({ baseURL: '/rest', events: bus })
|
|
12
|
+
* quadroEventsObservable(bus)
|
|
13
|
+
* .pipe(filter((e) => e.type === 'error'))
|
|
14
|
+
* .subscribe(console.warn)
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
import { Observable } from 'rxjs';
|
|
18
|
+
export function quadroEventsObservable(bus) {
|
|
19
|
+
return new Observable((subscriber) => {
|
|
20
|
+
const unsub = bus.subscribe((e) => {
|
|
21
|
+
subscriber.next(e);
|
|
22
|
+
});
|
|
23
|
+
return () => {
|
|
24
|
+
unsub();
|
|
25
|
+
};
|
|
26
|
+
});
|
|
27
|
+
}
|
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",
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
"./runtime": {
|
|
18
18
|
"types": "./dist/runtime/index.d.ts",
|
|
19
19
|
"import": "./dist/runtime/index.mjs"
|
|
20
|
+
},
|
|
21
|
+
"./rx": {
|
|
22
|
+
"types": "./dist/runtime/rxjs.d.ts",
|
|
23
|
+
"import": "./dist/runtime/rxjs.mjs"
|
|
20
24
|
}
|
|
21
25
|
},
|
|
22
26
|
"files": [
|
|
@@ -29,15 +33,22 @@
|
|
|
29
33
|
"generate:fixture": "bun run src/cli.ts generate --url file://../../assets/catalog.json --out ../../.quadrokit/generated-demo"
|
|
30
34
|
},
|
|
31
35
|
"dependencies": {
|
|
32
|
-
"@quadrokit/shared": "^0.3.
|
|
36
|
+
"@quadrokit/shared": "^0.3.14",
|
|
33
37
|
"undici": "^6.21.0"
|
|
34
38
|
},
|
|
35
39
|
"peerDependencies": {
|
|
40
|
+
"rxjs": ">=7.8.0",
|
|
36
41
|
"typescript": ">=5.4"
|
|
37
42
|
},
|
|
43
|
+
"peerDependenciesMeta": {
|
|
44
|
+
"rxjs": {
|
|
45
|
+
"optional": true
|
|
46
|
+
}
|
|
47
|
+
},
|
|
38
48
|
"devDependencies": {
|
|
39
49
|
"@types/bun": "^1.3.11",
|
|
40
50
|
"@types/node": "^25.5.0",
|
|
51
|
+
"rxjs": "^7.8.2",
|
|
41
52
|
"typescript": "^5.9.3"
|
|
42
53
|
}
|
|
43
54
|
}
|