@pikku/fetch 0.12.1 → 0.12.3
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 +23 -0
- package/dist/cjs/core-pikku-fetch.d.ts +16 -0
- package/dist/cjs/core-pikku-fetch.js +80 -1
- package/dist/cjs/index.d.ts +2 -1
- package/dist/esm/core-pikku-fetch.d.ts +16 -0
- package/dist/esm/core-pikku-fetch.js +80 -1
- package/dist/esm/index.d.ts +2 -1
- package/dist/esm/index.js +1 -1
- package/package.json +1 -1
- package/src/core-pikku-fetch.ts +84 -1
- package/src/index.test.ts +11 -0
- package/src/index.ts +2 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
+
## 0.12.3
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- 409ec80: feat(console): Tests page with live SSE streaming and function test harness
|
|
6
|
+
- `@pikku/addon-console`: add `streamFunctionTests` SSE function that runs the
|
|
7
|
+
cucumber/c8 test harness and streams structured per-scenario events
|
|
8
|
+
(scenario-start, step, scenario-done, done)
|
|
9
|
+
- `@pikku/console`: TestsPage live run view — renders scenario names and step
|
|
10
|
+
status in real time during a test run via SSE; adds `usePikkuSSE` hook and
|
|
11
|
+
`showRunButton` prop
|
|
12
|
+
- `@pikku/fetch`: add `subscribePikkuSSE` helper for typed server-sent event
|
|
13
|
+
streams
|
|
14
|
+
- `@pikku/cli`: wire SSE-returning functions through the console serialiser and
|
|
15
|
+
RPC wrapper so the stream route is included in generated clients
|
|
16
|
+
|
|
17
|
+
## 0.12.2
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- 9060165: New realtime events system: `pikku realtime` generates a typed `PikkuRealtime` client that pairs with `PikkuRPC`. A `/events` channel can be scaffolded to fan out server events to subscribers over SSE. `pikku dev` wires `LocalEventHubService` automatically so realtime works out of the box locally. The React provider exposes `PikkuRealtime` alongside `PikkuRPC`.
|
|
22
|
+
- 9060165: Fix `@pikku/addon-graph` package exports so generated bootstrap files can be imported correctly. The Node.js HTTP server adapter is unified across dev, standalone, and container deployments. Next.js gains a worker-RPC transport. Date values in fetch responses now deserialise correctly.
|
|
23
|
+
|
|
1
24
|
## 0.12.0
|
|
2
25
|
|
|
3
26
|
## 0.12.1
|
|
@@ -45,6 +45,11 @@ export declare class CorePikkuFetch {
|
|
|
45
45
|
* @param {string} serverUrl - The server URL to be set.
|
|
46
46
|
*/
|
|
47
47
|
setServerUrl(serverUrl: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Returns the configured base server URL (without trailing slash), or
|
|
50
|
+
* undefined if it hasn't been set yet.
|
|
51
|
+
*/
|
|
52
|
+
getServerUrl(): string | undefined;
|
|
48
53
|
/**
|
|
49
54
|
* Sets the JWT for authorization.
|
|
50
55
|
*
|
|
@@ -93,6 +98,17 @@ export declare class CorePikkuFetch {
|
|
|
93
98
|
* @throws {Response} - Throws the response if the status code is greater than 400.
|
|
94
99
|
*/
|
|
95
100
|
api(uri: string, method: HTTPMethod, data: any, options?: RequestInit): Promise<any>;
|
|
101
|
+
/**
|
|
102
|
+
* Opens an SSE stream to the given path and calls `handler` for each parsed
|
|
103
|
+
* JSON event. Returns a handle with a `close()` method that aborts the stream.
|
|
104
|
+
*
|
|
105
|
+
* @param path - Server-relative path (e.g. `/function-tests/stream`)
|
|
106
|
+
* @param handler - Called with each decoded JSON event
|
|
107
|
+
* @param onError - Called once if the stream errors (and is not already closed)
|
|
108
|
+
*/
|
|
109
|
+
subscribeToSSE<T = unknown>(path: string, handler: (event: T) => void, onError?: (err: unknown) => void): {
|
|
110
|
+
close: () => void;
|
|
111
|
+
};
|
|
96
112
|
/**
|
|
97
113
|
* Makes a raw fetch request with the specified URI, method, and data.
|
|
98
114
|
*
|
|
@@ -57,6 +57,13 @@ class CorePikkuFetch {
|
|
|
57
57
|
}
|
|
58
58
|
this.options.serverUrl = serverUrl;
|
|
59
59
|
}
|
|
60
|
+
/**
|
|
61
|
+
* Returns the configured base server URL (without trailing slash), or
|
|
62
|
+
* undefined if it hasn't been set yet.
|
|
63
|
+
*/
|
|
64
|
+
getServerUrl() {
|
|
65
|
+
return this.options.serverUrl;
|
|
66
|
+
}
|
|
60
67
|
/**
|
|
61
68
|
* Sets the JWT for authorization.
|
|
62
69
|
*
|
|
@@ -159,6 +166,78 @@ class CorePikkuFetch {
|
|
|
159
166
|
}
|
|
160
167
|
});
|
|
161
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Opens an SSE stream to the given path and calls `handler` for each parsed
|
|
171
|
+
* JSON event. Returns a handle with a `close()` method that aborts the stream.
|
|
172
|
+
*
|
|
173
|
+
* @param path - Server-relative path (e.g. `/function-tests/stream`)
|
|
174
|
+
* @param handler - Called with each decoded JSON event
|
|
175
|
+
* @param onError - Called once if the stream errors (and is not already closed)
|
|
176
|
+
*/
|
|
177
|
+
subscribeToSSE(path, handler, onError) {
|
|
178
|
+
this.verifyServerUrlSet();
|
|
179
|
+
const url = path.startsWith('/')
|
|
180
|
+
? `${this.options.serverUrl}${path}`
|
|
181
|
+
: `${this.options.serverUrl}/${path}`;
|
|
182
|
+
const controller = new AbortController();
|
|
183
|
+
let closed = false;
|
|
184
|
+
const run = () => __awaiter(this, void 0, void 0, function* () {
|
|
185
|
+
try {
|
|
186
|
+
const response = yield (0, pikku_fetch_js_1.corePikkuFetch)(url, null, {
|
|
187
|
+
method: 'GET',
|
|
188
|
+
mode: this.options.mode,
|
|
189
|
+
credentials: this.options.credentials,
|
|
190
|
+
headers: Object.assign(Object.assign({}, this.getHeaders()), { Accept: 'text/event-stream' }),
|
|
191
|
+
signal: controller.signal,
|
|
192
|
+
});
|
|
193
|
+
if (!response.ok || !response.body) {
|
|
194
|
+
throw new Error(`SSE request failed: ${response.status}`);
|
|
195
|
+
}
|
|
196
|
+
const reader = response.body
|
|
197
|
+
.pipeThrough(new TextDecoderStream())
|
|
198
|
+
.getReader();
|
|
199
|
+
let buffer = '';
|
|
200
|
+
while (!closed) {
|
|
201
|
+
const { done, value } = yield reader.read();
|
|
202
|
+
if (done)
|
|
203
|
+
break;
|
|
204
|
+
buffer += value;
|
|
205
|
+
let sep;
|
|
206
|
+
while ((sep = buffer.indexOf('\n\n')) !== -1) {
|
|
207
|
+
const raw = buffer.slice(0, sep);
|
|
208
|
+
buffer = buffer.slice(sep + 2);
|
|
209
|
+
const data = raw
|
|
210
|
+
.split('\n')
|
|
211
|
+
.filter((l) => l.startsWith('data:'))
|
|
212
|
+
.map((l) => l.slice(5).trimStart())
|
|
213
|
+
.join('\n');
|
|
214
|
+
if (!data)
|
|
215
|
+
continue;
|
|
216
|
+
let parsed;
|
|
217
|
+
try {
|
|
218
|
+
parsed = JSON.parse(data);
|
|
219
|
+
}
|
|
220
|
+
catch (_a) {
|
|
221
|
+
/* ignore malformed event */
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
handler(parsed);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch (err) {
|
|
229
|
+
if (!closed)
|
|
230
|
+
onError === null || onError === void 0 ? void 0 : onError(err);
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
run();
|
|
234
|
+
return {
|
|
235
|
+
close: () => {
|
|
236
|
+
closed = true;
|
|
237
|
+
controller.abort();
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
}
|
|
162
241
|
/**
|
|
163
242
|
* Makes a raw fetch request with the specified URI, method, and data.
|
|
164
243
|
*
|
|
@@ -198,7 +277,7 @@ class CorePikkuFetch {
|
|
|
198
277
|
* @returns {any} - The transformed data.
|
|
199
278
|
*/
|
|
200
279
|
transformDates(data) {
|
|
201
|
-
if (
|
|
280
|
+
if (this.options.transformDate === false) {
|
|
202
281
|
return data;
|
|
203
282
|
}
|
|
204
283
|
return (0, transform_date_js_1.transformDates)(data);
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -6,5 +6,6 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module @pikku/fetch
|
|
8
8
|
*/
|
|
9
|
-
export { CorePikkuFetch
|
|
9
|
+
export { CorePikkuFetch } from './core-pikku-fetch.js';
|
|
10
|
+
export type { CorePikkuFetchOptions, HTTPMethod } from './core-pikku-fetch.js';
|
|
10
11
|
export { corePikkuFetch } from './pikku-fetch.js';
|
|
@@ -45,6 +45,11 @@ export declare class CorePikkuFetch {
|
|
|
45
45
|
* @param {string} serverUrl - The server URL to be set.
|
|
46
46
|
*/
|
|
47
47
|
setServerUrl(serverUrl: string): void;
|
|
48
|
+
/**
|
|
49
|
+
* Returns the configured base server URL (without trailing slash), or
|
|
50
|
+
* undefined if it hasn't been set yet.
|
|
51
|
+
*/
|
|
52
|
+
getServerUrl(): string | undefined;
|
|
48
53
|
/**
|
|
49
54
|
* Sets the JWT for authorization.
|
|
50
55
|
*
|
|
@@ -93,6 +98,17 @@ export declare class CorePikkuFetch {
|
|
|
93
98
|
* @throws {Response} - Throws the response if the status code is greater than 400.
|
|
94
99
|
*/
|
|
95
100
|
api(uri: string, method: HTTPMethod, data: any, options?: RequestInit): Promise<any>;
|
|
101
|
+
/**
|
|
102
|
+
* Opens an SSE stream to the given path and calls `handler` for each parsed
|
|
103
|
+
* JSON event. Returns a handle with a `close()` method that aborts the stream.
|
|
104
|
+
*
|
|
105
|
+
* @param path - Server-relative path (e.g. `/function-tests/stream`)
|
|
106
|
+
* @param handler - Called with each decoded JSON event
|
|
107
|
+
* @param onError - Called once if the stream errors (and is not already closed)
|
|
108
|
+
*/
|
|
109
|
+
subscribeToSSE<T = unknown>(path: string, handler: (event: T) => void, onError?: (err: unknown) => void): {
|
|
110
|
+
close: () => void;
|
|
111
|
+
};
|
|
96
112
|
/**
|
|
97
113
|
* Makes a raw fetch request with the specified URI, method, and data.
|
|
98
114
|
*
|
|
@@ -46,6 +46,13 @@ export class CorePikkuFetch {
|
|
|
46
46
|
}
|
|
47
47
|
this.options.serverUrl = serverUrl;
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Returns the configured base server URL (without trailing slash), or
|
|
51
|
+
* undefined if it hasn't been set yet.
|
|
52
|
+
*/
|
|
53
|
+
getServerUrl() {
|
|
54
|
+
return this.options.serverUrl;
|
|
55
|
+
}
|
|
49
56
|
/**
|
|
50
57
|
* Sets the JWT for authorization.
|
|
51
58
|
*
|
|
@@ -137,6 +144,78 @@ export class CorePikkuFetch {
|
|
|
137
144
|
return;
|
|
138
145
|
}
|
|
139
146
|
}
|
|
147
|
+
/**
|
|
148
|
+
* Opens an SSE stream to the given path and calls `handler` for each parsed
|
|
149
|
+
* JSON event. Returns a handle with a `close()` method that aborts the stream.
|
|
150
|
+
*
|
|
151
|
+
* @param path - Server-relative path (e.g. `/function-tests/stream`)
|
|
152
|
+
* @param handler - Called with each decoded JSON event
|
|
153
|
+
* @param onError - Called once if the stream errors (and is not already closed)
|
|
154
|
+
*/
|
|
155
|
+
subscribeToSSE(path, handler, onError) {
|
|
156
|
+
this.verifyServerUrlSet();
|
|
157
|
+
const url = path.startsWith('/')
|
|
158
|
+
? `${this.options.serverUrl}${path}`
|
|
159
|
+
: `${this.options.serverUrl}/${path}`;
|
|
160
|
+
const controller = new AbortController();
|
|
161
|
+
let closed = false;
|
|
162
|
+
const run = async () => {
|
|
163
|
+
try {
|
|
164
|
+
const response = await corePikkuFetch(url, null, {
|
|
165
|
+
method: 'GET',
|
|
166
|
+
mode: this.options.mode,
|
|
167
|
+
credentials: this.options.credentials,
|
|
168
|
+
headers: { ...this.getHeaders(), Accept: 'text/event-stream' },
|
|
169
|
+
signal: controller.signal,
|
|
170
|
+
});
|
|
171
|
+
if (!response.ok || !response.body) {
|
|
172
|
+
throw new Error(`SSE request failed: ${response.status}`);
|
|
173
|
+
}
|
|
174
|
+
const reader = response.body
|
|
175
|
+
.pipeThrough(new TextDecoderStream())
|
|
176
|
+
.getReader();
|
|
177
|
+
let buffer = '';
|
|
178
|
+
while (!closed) {
|
|
179
|
+
const { done, value } = await reader.read();
|
|
180
|
+
if (done)
|
|
181
|
+
break;
|
|
182
|
+
buffer += value;
|
|
183
|
+
let sep;
|
|
184
|
+
while ((sep = buffer.indexOf('\n\n')) !== -1) {
|
|
185
|
+
const raw = buffer.slice(0, sep);
|
|
186
|
+
buffer = buffer.slice(sep + 2);
|
|
187
|
+
const data = raw
|
|
188
|
+
.split('\n')
|
|
189
|
+
.filter((l) => l.startsWith('data:'))
|
|
190
|
+
.map((l) => l.slice(5).trimStart())
|
|
191
|
+
.join('\n');
|
|
192
|
+
if (!data)
|
|
193
|
+
continue;
|
|
194
|
+
let parsed;
|
|
195
|
+
try {
|
|
196
|
+
parsed = JSON.parse(data);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
/* ignore malformed event */
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
202
|
+
handler(parsed);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
catch (err) {
|
|
207
|
+
if (!closed)
|
|
208
|
+
onError?.(err);
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
run();
|
|
212
|
+
return {
|
|
213
|
+
close: () => {
|
|
214
|
+
closed = true;
|
|
215
|
+
controller.abort();
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
140
219
|
/**
|
|
141
220
|
* Makes a raw fetch request with the specified URI, method, and data.
|
|
142
221
|
*
|
|
@@ -180,7 +259,7 @@ export class CorePikkuFetch {
|
|
|
180
259
|
* @returns {any} - The transformed data.
|
|
181
260
|
*/
|
|
182
261
|
transformDates(data) {
|
|
183
|
-
if (
|
|
262
|
+
if (this.options.transformDate === false) {
|
|
184
263
|
return data;
|
|
185
264
|
}
|
|
186
265
|
return transformDates(data);
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -6,5 +6,6 @@
|
|
|
6
6
|
*
|
|
7
7
|
* @module @pikku/fetch
|
|
8
8
|
*/
|
|
9
|
-
export { CorePikkuFetch
|
|
9
|
+
export { CorePikkuFetch } from './core-pikku-fetch.js';
|
|
10
|
+
export type { CorePikkuFetchOptions, HTTPMethod } from './core-pikku-fetch.js';
|
|
10
11
|
export { corePikkuFetch } from './pikku-fetch.js';
|
package/dist/esm/index.js
CHANGED
package/package.json
CHANGED
package/src/core-pikku-fetch.ts
CHANGED
|
@@ -72,6 +72,14 @@ export class CorePikkuFetch {
|
|
|
72
72
|
this.options.serverUrl = serverUrl
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Returns the configured base server URL (without trailing slash), or
|
|
77
|
+
* undefined if it hasn't been set yet.
|
|
78
|
+
*/
|
|
79
|
+
public getServerUrl(): string | undefined {
|
|
80
|
+
return this.options.serverUrl
|
|
81
|
+
}
|
|
82
|
+
|
|
75
83
|
/**
|
|
76
84
|
* Sets the JWT for authorization.
|
|
77
85
|
*
|
|
@@ -185,6 +193,81 @@ export class CorePikkuFetch {
|
|
|
185
193
|
}
|
|
186
194
|
}
|
|
187
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Opens an SSE stream to the given path and calls `handler` for each parsed
|
|
198
|
+
* JSON event. Returns a handle with a `close()` method that aborts the stream.
|
|
199
|
+
*
|
|
200
|
+
* @param path - Server-relative path (e.g. `/function-tests/stream`)
|
|
201
|
+
* @param handler - Called with each decoded JSON event
|
|
202
|
+
* @param onError - Called once if the stream errors (and is not already closed)
|
|
203
|
+
*/
|
|
204
|
+
public subscribeToSSE<T = unknown>(
|
|
205
|
+
path: string,
|
|
206
|
+
handler: (event: T) => void,
|
|
207
|
+
onError?: (err: unknown) => void
|
|
208
|
+
): { close: () => void } {
|
|
209
|
+
this.verifyServerUrlSet()
|
|
210
|
+
const url = path.startsWith('/')
|
|
211
|
+
? `${this.options.serverUrl}${path}`
|
|
212
|
+
: `${this.options.serverUrl}/${path}`
|
|
213
|
+
|
|
214
|
+
const controller = new AbortController()
|
|
215
|
+
let closed = false
|
|
216
|
+
|
|
217
|
+
const run = async () => {
|
|
218
|
+
try {
|
|
219
|
+
const response = await corePikkuFetch(url, null, {
|
|
220
|
+
method: 'GET',
|
|
221
|
+
mode: this.options.mode,
|
|
222
|
+
credentials: this.options.credentials,
|
|
223
|
+
headers: { ...this.getHeaders(), Accept: 'text/event-stream' },
|
|
224
|
+
signal: controller.signal,
|
|
225
|
+
})
|
|
226
|
+
if (!response.ok || !response.body) {
|
|
227
|
+
throw new Error(`SSE request failed: ${response.status}`)
|
|
228
|
+
}
|
|
229
|
+
const reader = response.body
|
|
230
|
+
.pipeThrough(new TextDecoderStream())
|
|
231
|
+
.getReader()
|
|
232
|
+
let buffer = ''
|
|
233
|
+
while (!closed) {
|
|
234
|
+
const { done, value } = await reader.read()
|
|
235
|
+
if (done) break
|
|
236
|
+
buffer += value
|
|
237
|
+
let sep: number
|
|
238
|
+
while ((sep = buffer.indexOf('\n\n')) !== -1) {
|
|
239
|
+
const raw = buffer.slice(0, sep)
|
|
240
|
+
buffer = buffer.slice(sep + 2)
|
|
241
|
+
const data = raw
|
|
242
|
+
.split('\n')
|
|
243
|
+
.filter((l) => l.startsWith('data:'))
|
|
244
|
+
.map((l) => l.slice(5).trimStart())
|
|
245
|
+
.join('\n')
|
|
246
|
+
if (!data) continue
|
|
247
|
+
let parsed: T
|
|
248
|
+
try {
|
|
249
|
+
parsed = JSON.parse(data) as T
|
|
250
|
+
} catch {
|
|
251
|
+
/* ignore malformed event */
|
|
252
|
+
continue
|
|
253
|
+
}
|
|
254
|
+
handler(parsed)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
} catch (err) {
|
|
258
|
+
if (!closed) onError?.(err)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
run()
|
|
263
|
+
return {
|
|
264
|
+
close: () => {
|
|
265
|
+
closed = true
|
|
266
|
+
controller.abort()
|
|
267
|
+
},
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
188
271
|
/**
|
|
189
272
|
* Makes a raw fetch request with the specified URI, method, and data.
|
|
190
273
|
*
|
|
@@ -234,7 +317,7 @@ export class CorePikkuFetch {
|
|
|
234
317
|
* @returns {any} - The transformed data.
|
|
235
318
|
*/
|
|
236
319
|
private transformDates(data: any): any {
|
|
237
|
-
if (
|
|
320
|
+
if (this.options.transformDate === false) {
|
|
238
321
|
return data
|
|
239
322
|
}
|
|
240
323
|
return transformDates(data)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import { describe, test } from 'node:test'
|
|
3
|
+
|
|
4
|
+
import { CorePikkuFetch, corePikkuFetch } from './index.js'
|
|
5
|
+
|
|
6
|
+
describe('@pikku/fetch', () => {
|
|
7
|
+
test('exports the public fetch client API', () => {
|
|
8
|
+
assert.equal(typeof CorePikkuFetch, 'function')
|
|
9
|
+
assert.equal(typeof corePikkuFetch, 'function')
|
|
10
|
+
})
|
|
11
|
+
})
|
package/src/index.ts
CHANGED
|
@@ -7,9 +7,6 @@
|
|
|
7
7
|
* @module @pikku/fetch
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
export {
|
|
11
|
-
|
|
12
|
-
CorePikkuFetchOptions,
|
|
13
|
-
HTTPMethod,
|
|
14
|
-
} from './core-pikku-fetch.js'
|
|
10
|
+
export { CorePikkuFetch } from './core-pikku-fetch.js'
|
|
11
|
+
export type { CorePikkuFetchOptions, HTTPMethod } from './core-pikku-fetch.js'
|
|
15
12
|
export { corePikkuFetch } from './pikku-fetch.js'
|