arri 0.24.1 → 0.26.0
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 +13 -11
- package/dist/cli.cjs +6 -6
- package/dist/cli.mjs +6 -6
- package/dist/index.cjs +5 -5
- package/dist/index.d.cts +13 -31
- package/dist/index.d.mts +13 -31
- package/dist/index.d.ts +13 -31
- package/dist/index.mjs +2 -2
- package/dist/shared/{arri.ebc63bd6.mjs → arri.21567b92.mjs} +30 -119
- package/dist/shared/{arri.f4b47a61.cjs → arri.593bcad9.cjs} +28 -120
- package/package.json +10 -9
package/README.md
CHANGED
|
@@ -259,22 +259,24 @@ export default defineEventStreamRpc({
|
|
|
259
259
|
createdAt: a.timestamp(),
|
|
260
260
|
updatedAt: a.timestamp(),
|
|
261
261
|
}),
|
|
262
|
-
handler({ params,
|
|
262
|
+
handler({ params, stream }) {
|
|
263
|
+
// initialize the stream and send it to the client
|
|
264
|
+
stream.init();
|
|
265
|
+
|
|
263
266
|
// send a message every second
|
|
264
267
|
const interval = setInterval(async () => {
|
|
265
|
-
await
|
|
268
|
+
await stream.push({
|
|
266
269
|
id: "1",
|
|
267
270
|
name: "John Doe",
|
|
268
271
|
createdAt: new Date(),
|
|
269
272
|
updatedAt: new Date(),
|
|
270
273
|
});
|
|
271
274
|
}, 1000);
|
|
275
|
+
|
|
272
276
|
// cleanup when the client disconnects
|
|
273
|
-
|
|
277
|
+
stream.on("close", () => {
|
|
274
278
|
clearInterval(interval);
|
|
275
279
|
});
|
|
276
|
-
// start streaming events to the client
|
|
277
|
-
connection.start();
|
|
278
280
|
},
|
|
279
281
|
});
|
|
280
282
|
```
|
|
@@ -282,11 +284,11 @@ export default defineEventStreamRpc({
|
|
|
282
284
|
#### EventStreamConnection methods
|
|
283
285
|
|
|
284
286
|
```ts
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
287
|
+
stream.push(data: Data, eventId?: string)
|
|
288
|
+
stream.pushError(error: ArriRequestError, eventId?: string)
|
|
289
|
+
stream.init()
|
|
290
|
+
stream.end()
|
|
291
|
+
stream.on(e: 'request:close' | 'close', callback: () => any)
|
|
290
292
|
```
|
|
291
293
|
|
|
292
294
|
### Adding Non-RPC Routes
|
|
@@ -491,7 +493,7 @@ arri build [flags]
|
|
|
491
493
|
arri init [dir]
|
|
492
494
|
```
|
|
493
495
|
|
|
494
|
-
##
|
|
496
|
+
## Development
|
|
495
497
|
|
|
496
498
|
### Building
|
|
497
499
|
|
package/dist/cli.cjs
CHANGED
|
@@ -12,7 +12,7 @@ const esbuild = require('esbuild');
|
|
|
12
12
|
const esbuildPluginReplace = require('esbuild-plugin-replace');
|
|
13
13
|
const path = require('pathe');
|
|
14
14
|
const prettier = require('prettier');
|
|
15
|
-
const config = require('./shared/arri.
|
|
15
|
+
const config = require('./shared/arri.593bcad9.cjs');
|
|
16
16
|
const globby = require('globby');
|
|
17
17
|
const scule = require('scule');
|
|
18
18
|
const arriCodegenDart = require('arri-codegen-dart');
|
|
@@ -22,7 +22,7 @@ const chokidar = require('chokidar');
|
|
|
22
22
|
const Degit = require('degit');
|
|
23
23
|
require('h3');
|
|
24
24
|
require('arri-validate');
|
|
25
|
-
require('
|
|
25
|
+
require('h3-sse');
|
|
26
26
|
|
|
27
27
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
28
28
|
|
|
@@ -49,9 +49,9 @@ const Degit__default = /*#__PURE__*/_interopDefaultCompat(Degit);
|
|
|
49
49
|
|
|
50
50
|
const GEN_APP_FILE = "__arri_app.js";
|
|
51
51
|
const GEN_SERVER_ENTRY_FILE = "__arri_server.js";
|
|
52
|
-
const OUT_APP_FILE = "app.
|
|
53
|
-
const OUT_SERVER_ENTRY = "server.
|
|
54
|
-
const OUT_CODEGEN = "codegen.
|
|
52
|
+
const OUT_APP_FILE = "app.mjs";
|
|
53
|
+
const OUT_SERVER_ENTRY = "server.mjs";
|
|
54
|
+
const OUT_CODEGEN = "codegen.mjs";
|
|
55
55
|
async function setupWorkingDir(config) {
|
|
56
56
|
const arriDir = path__default.resolve(config.rootDir, config.buildDir);
|
|
57
57
|
const outDir = path__default.resolve(config.rootDir, ".output");
|
|
@@ -294,7 +294,7 @@ async function createServerEntryFile(config) {
|
|
|
294
294
|
js: `import { createRequire as topLevelCreateRequire } from 'module';
|
|
295
295
|
const require = topLevelCreateRequire(import.meta.url);`
|
|
296
296
|
},
|
|
297
|
-
plugins: [esbuildPluginReplace.replace({ "virtual:arri/app": "./app.
|
|
297
|
+
plugins: [esbuildPluginReplace.replace({ "virtual:arri/app": "./app.mjs" })],
|
|
298
298
|
allowOverwrite: true,
|
|
299
299
|
outfile: path__default.resolve(config.rootDir, ".output", OUT_SERVER_ENTRY)
|
|
300
300
|
});
|
package/dist/cli.mjs
CHANGED
|
@@ -11,7 +11,7 @@ import { build as build$1 } from 'esbuild';
|
|
|
11
11
|
import { replace } from 'esbuild-plugin-replace';
|
|
12
12
|
import path from 'pathe';
|
|
13
13
|
import prettier from 'prettier';
|
|
14
|
-
import { n as defaultConfig, m as isResolvedArriConfig, a as DEV_DEFINITION_ENDPOINT } from './shared/arri.
|
|
14
|
+
import { n as defaultConfig, m as isResolvedArriConfig, a as DEV_DEFINITION_ENDPOINT } from './shared/arri.21567b92.mjs';
|
|
15
15
|
import { globby } from 'globby';
|
|
16
16
|
import { camelCase, kebabCase } from 'scule';
|
|
17
17
|
import { dartClientGenerator } from 'arri-codegen-dart';
|
|
@@ -21,13 +21,13 @@ import chokidar from 'chokidar';
|
|
|
21
21
|
import Degit from 'degit';
|
|
22
22
|
import 'h3';
|
|
23
23
|
import 'arri-validate';
|
|
24
|
-
import '
|
|
24
|
+
import 'h3-sse';
|
|
25
25
|
|
|
26
26
|
const GEN_APP_FILE = "__arri_app.js";
|
|
27
27
|
const GEN_SERVER_ENTRY_FILE = "__arri_server.js";
|
|
28
|
-
const OUT_APP_FILE = "app.
|
|
29
|
-
const OUT_SERVER_ENTRY = "server.
|
|
30
|
-
const OUT_CODEGEN = "codegen.
|
|
28
|
+
const OUT_APP_FILE = "app.mjs";
|
|
29
|
+
const OUT_SERVER_ENTRY = "server.mjs";
|
|
30
|
+
const OUT_CODEGEN = "codegen.mjs";
|
|
31
31
|
async function setupWorkingDir(config) {
|
|
32
32
|
const arriDir = path.resolve(config.rootDir, config.buildDir);
|
|
33
33
|
const outDir = path.resolve(config.rootDir, ".output");
|
|
@@ -270,7 +270,7 @@ async function createServerEntryFile(config) {
|
|
|
270
270
|
js: `import { createRequire as topLevelCreateRequire } from 'module';
|
|
271
271
|
const require = topLevelCreateRequire(import.meta.url);`
|
|
272
272
|
},
|
|
273
|
-
plugins: [replace({ "virtual:arri/app": "./app.
|
|
273
|
+
plugins: [replace({ "virtual:arri/app": "./app.mjs" })],
|
|
274
274
|
allowOverwrite: true,
|
|
275
275
|
outfile: path.resolve(config.rootDir, ".output", OUT_SERVER_ENTRY)
|
|
276
276
|
});
|
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const config = require('./shared/arri.
|
|
3
|
+
const config = require('./shared/arri.593bcad9.cjs');
|
|
4
4
|
const h3 = require('h3');
|
|
5
|
+
const h3Sse = require('h3-sse');
|
|
5
6
|
require('arri-validate');
|
|
6
|
-
require('node:process');
|
|
7
7
|
require('arri-codegen-utils');
|
|
8
8
|
require('scule');
|
|
9
9
|
|
|
@@ -23,8 +23,6 @@ exports.defineEventStreamRpc = config.defineEventStreamRpc;
|
|
|
23
23
|
exports.defineRoute = config.defineRoute;
|
|
24
24
|
exports.defineRpc = config.defineRpc;
|
|
25
25
|
exports.errorResponseFromValidationErrors = config.errorResponseFromValidationErrors;
|
|
26
|
-
exports.formatSse = config.formatSse;
|
|
27
|
-
exports.formatSseList = config.formatSseList;
|
|
28
26
|
exports.getRpcParamName = config.getRpcParamName;
|
|
29
27
|
exports.getRpcPath = config.getRpcPath;
|
|
30
28
|
exports.getRpcResponseName = config.getRpcResponseName;
|
|
@@ -38,9 +36,11 @@ exports.isRpcParamSchema = config.isRpcParamSchema;
|
|
|
38
36
|
exports.registerEventStreamRpc = config.registerEventStreamRpc;
|
|
39
37
|
exports.registerRoute = config.registerRoute;
|
|
40
38
|
exports.registerRpc = config.registerRpc;
|
|
41
|
-
exports.setSseHeaders = config.setSseHeaders;
|
|
42
39
|
exports.validateRpcRequestInput = config.validateRpcRequestInput;
|
|
43
40
|
exports.defineMiddleware = defineMiddleware;
|
|
44
41
|
Object.keys(h3).forEach(function (k) {
|
|
45
42
|
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = h3[k];
|
|
46
43
|
});
|
|
44
|
+
Object.keys(h3Sse).forEach(function (k) {
|
|
45
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) exports[k] = h3Sse[k];
|
|
46
|
+
});
|
package/dist/index.d.cts
CHANGED
|
@@ -2,6 +2,8 @@ import { HttpMethod, RpcHttpMethod, RpcDefinition, SchemaFormProperties, SchemaF
|
|
|
2
2
|
import { AObjectSchema, ASchema, InferType, a, ValidationError, ValueError, ARecordSchema, ADiscriminatorSchema } from 'arri-validate';
|
|
3
3
|
import { H3EventContext, H3Event, Router, H3Error, App } from 'h3';
|
|
4
4
|
export * from 'h3';
|
|
5
|
+
import { EventStream } from 'h3-sse';
|
|
6
|
+
export * from 'h3-sse';
|
|
5
7
|
import { BuildOptions } from 'esbuild';
|
|
6
8
|
|
|
7
9
|
interface MiddlewareEventContext extends H3EventContext {
|
|
@@ -58,19 +60,15 @@ declare function errorResponseFromValidationErrors(errors: ValidationError["erro
|
|
|
58
60
|
type StatusCode = number | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 428 | 429 | 431 | 451 | 500 | 501 | 502 | 503 | 504 | 505 | 507 | 511;
|
|
59
61
|
declare function handleH3Error(err: unknown, event: H3Event, onError: ArriOptions["onError"]): Promise<void>;
|
|
60
62
|
|
|
61
|
-
declare function setSseHeaders(event: H3Event): void;
|
|
62
63
|
declare function defineEventStreamRpc<TParams extends RpcParamSchema | undefined = undefined, TResponse extends RpcParamSchema | undefined | never = undefined>(config: Omit<EventStreamRpc<TParams, TResponse>, "isEventStream">): EventStreamRpc<TParams, TResponse>;
|
|
63
64
|
declare function isEventStreamRpc(input: unknown): input is EventStreamRpc<any, any>;
|
|
64
65
|
interface EventStreamRpc<TParams extends RpcParamSchema | undefined = undefined, TResponse extends RpcParamSchema | undefined = undefined> extends Omit<Rpc<true, TParams, TResponse>, "handler" | "postHandler"> {
|
|
65
66
|
isEventStream: true;
|
|
66
67
|
handler: EventStreamRpcHandler<TParams extends RpcParamSchema ? InferType<TParams> : undefined, TResponse extends RpcParamSchema ? InferType<TResponse> : undefined>;
|
|
67
68
|
}
|
|
68
|
-
type EventStreamRpcHandler<TParams, TResponse> = (context: EventStreamRpcHandlerContext<TParams, TResponse>, event:
|
|
69
|
-
interface EventStreamRpcEvent<TParams, TResponse> extends RpcEvent<TParams> {
|
|
70
|
-
connection: EventStreamConnection<TResponse>;
|
|
71
|
-
}
|
|
69
|
+
type EventStreamRpcHandler<TParams, TResponse> = (context: EventStreamRpcHandlerContext<TParams, TResponse>, event: RpcEvent<TResponse>) => void | Promise<void>;
|
|
72
70
|
interface EventStreamRpcHandlerContext<TParams = any, TResponse = any> extends RpcHandlerContext<TParams> {
|
|
73
|
-
|
|
71
|
+
stream: EventStreamConnection<TResponse>;
|
|
74
72
|
}
|
|
75
73
|
interface EventStreamConnectionOptions<TData> {
|
|
76
74
|
validator: (input: unknown) => input is TData;
|
|
@@ -78,34 +76,20 @@ interface EventStreamConnectionOptions<TData> {
|
|
|
78
76
|
serializer: (input: TData) => string;
|
|
79
77
|
pingInterval?: number;
|
|
80
78
|
}
|
|
81
|
-
/**
|
|
82
|
-
* A server sent event
|
|
83
|
-
*/
|
|
84
|
-
interface Sse {
|
|
85
|
-
id?: string;
|
|
86
|
-
event?: string;
|
|
87
|
-
data: string;
|
|
88
|
-
}
|
|
89
|
-
declare function formatSse({ id, data, event }: Sse): string;
|
|
90
|
-
declare function formatSseList(events: Sse[]): string;
|
|
91
79
|
declare class EventStreamConnection<TData> {
|
|
92
|
-
lastEventId
|
|
93
|
-
private readonly
|
|
94
|
-
private readonly readable;
|
|
95
|
-
private readonly writer;
|
|
96
|
-
private writerIsClosed;
|
|
97
|
-
private readonly encoder;
|
|
80
|
+
readonly lastEventId?: string;
|
|
81
|
+
private readonly h3Event;
|
|
98
82
|
private readonly validationErrors;
|
|
99
83
|
private readonly validator;
|
|
100
84
|
private readonly serializer;
|
|
101
|
-
private readonly h3Event;
|
|
102
85
|
private pingInterval;
|
|
103
86
|
private readonly pingIntervalMs;
|
|
87
|
+
readonly eventStream: EventStream;
|
|
104
88
|
constructor(event: H3Event, opts: EventStreamConnectionOptions<TData>);
|
|
105
89
|
/**
|
|
106
|
-
*
|
|
90
|
+
* Initialize the stream. This must be called before sending any events.
|
|
107
91
|
*/
|
|
108
|
-
|
|
92
|
+
init(): void;
|
|
109
93
|
/**
|
|
110
94
|
* Publish a new event. Events published with this hook will trigger the `onData()` hooks of any connected clients.
|
|
111
95
|
*/
|
|
@@ -115,15 +99,13 @@ declare class EventStreamConnection<TData> {
|
|
|
115
99
|
* Publish an error event. This will trigger the `onError` hooks of any connected clients.
|
|
116
100
|
*/
|
|
117
101
|
pushError(error: ErrorResponse, eventId?: string): Promise<void>;
|
|
118
|
-
private publishEvents;
|
|
119
|
-
private publishEvent;
|
|
120
102
|
private cleanup;
|
|
121
103
|
/**
|
|
122
104
|
* Tell clients that the stream has ended and close the connection.
|
|
123
105
|
*/
|
|
124
|
-
|
|
125
|
-
on(event: "
|
|
126
|
-
on(event: "
|
|
106
|
+
close(): Promise<void>;
|
|
107
|
+
on(event: "close", callback: () => any): void;
|
|
108
|
+
on(event: "request:close", callback: () => any): void;
|
|
127
109
|
}
|
|
128
110
|
declare function registerEventStreamRpc(router: Router, path: string, procedure: EventStreamRpc<any, any> & {
|
|
129
111
|
name: string;
|
|
@@ -257,4 +239,4 @@ type ResolvedArriConfig = Required<ArriConfig>;
|
|
|
257
239
|
declare const defaultConfig: Required<ArriConfig>;
|
|
258
240
|
declare function defineConfig(config: ArriConfig): ResolvedArriConfig;
|
|
259
241
|
|
|
260
|
-
export { ArriApp, type ArriConfig, type ArriOptions, type ArriRoute, ArriRouter, type ArriRouterBase, DEV_DEFINITION_ENDPOINT, DEV_ENDPOINT_ROOT, ErrorResponse, EventStreamConnection, type EventStreamConnectionOptions, type EventStreamRpc, type
|
|
242
|
+
export { ArriApp, type ArriConfig, type ArriOptions, type ArriRoute, ArriRouter, type ArriRouterBase, DEV_DEFINITION_ENDPOINT, DEV_ENDPOINT_ROOT, ErrorResponse, EventStreamConnection, type EventStreamConnectionOptions, type EventStreamRpc, type EventStreamRpcHandler, type EventStreamRpcHandlerContext, type ExtractParam, type ExtractParams, type HandlerContext, type HttpRpc, type Middleware, type MiddlewareEvent, type MiddlewareEventContext, type ModelMap, type NamedRpc, type PostRouteEvent, type PostRouteEventContext, type ResolvedArriConfig, type RouteEvent, type RouteEventContext, type RouteOptions, type Rpc, type RpcEvent, type RpcHandler, type RpcHandlerContext, type RpcParamSchema, type RpcPostEvent, type RpcPostHandler, type RpcPostHandlerContext, type StatusCode, createRpcDefinition, defaultConfig, defineConfig, defineError, defineEventStreamRpc, defineMiddleware, defineRoute, defineRpc, errorResponseFromValidationErrors, getRpcParamName, getRpcPath, getRpcResponseName, handleH3Error, handleRoute, isArriConfig, isEventStreamRpc, isResolvedArriConfig, isRpc, isRpcParamSchema, registerEventStreamRpc, registerRoute, registerRpc, validateRpcRequestInput };
|
package/dist/index.d.mts
CHANGED
|
@@ -2,6 +2,8 @@ import { HttpMethod, RpcHttpMethod, RpcDefinition, SchemaFormProperties, SchemaF
|
|
|
2
2
|
import { AObjectSchema, ASchema, InferType, a, ValidationError, ValueError, ARecordSchema, ADiscriminatorSchema } from 'arri-validate';
|
|
3
3
|
import { H3EventContext, H3Event, Router, H3Error, App } from 'h3';
|
|
4
4
|
export * from 'h3';
|
|
5
|
+
import { EventStream } from 'h3-sse';
|
|
6
|
+
export * from 'h3-sse';
|
|
5
7
|
import { BuildOptions } from 'esbuild';
|
|
6
8
|
|
|
7
9
|
interface MiddlewareEventContext extends H3EventContext {
|
|
@@ -58,19 +60,15 @@ declare function errorResponseFromValidationErrors(errors: ValidationError["erro
|
|
|
58
60
|
type StatusCode = number | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 428 | 429 | 431 | 451 | 500 | 501 | 502 | 503 | 504 | 505 | 507 | 511;
|
|
59
61
|
declare function handleH3Error(err: unknown, event: H3Event, onError: ArriOptions["onError"]): Promise<void>;
|
|
60
62
|
|
|
61
|
-
declare function setSseHeaders(event: H3Event): void;
|
|
62
63
|
declare function defineEventStreamRpc<TParams extends RpcParamSchema | undefined = undefined, TResponse extends RpcParamSchema | undefined | never = undefined>(config: Omit<EventStreamRpc<TParams, TResponse>, "isEventStream">): EventStreamRpc<TParams, TResponse>;
|
|
63
64
|
declare function isEventStreamRpc(input: unknown): input is EventStreamRpc<any, any>;
|
|
64
65
|
interface EventStreamRpc<TParams extends RpcParamSchema | undefined = undefined, TResponse extends RpcParamSchema | undefined = undefined> extends Omit<Rpc<true, TParams, TResponse>, "handler" | "postHandler"> {
|
|
65
66
|
isEventStream: true;
|
|
66
67
|
handler: EventStreamRpcHandler<TParams extends RpcParamSchema ? InferType<TParams> : undefined, TResponse extends RpcParamSchema ? InferType<TResponse> : undefined>;
|
|
67
68
|
}
|
|
68
|
-
type EventStreamRpcHandler<TParams, TResponse> = (context: EventStreamRpcHandlerContext<TParams, TResponse>, event:
|
|
69
|
-
interface EventStreamRpcEvent<TParams, TResponse> extends RpcEvent<TParams> {
|
|
70
|
-
connection: EventStreamConnection<TResponse>;
|
|
71
|
-
}
|
|
69
|
+
type EventStreamRpcHandler<TParams, TResponse> = (context: EventStreamRpcHandlerContext<TParams, TResponse>, event: RpcEvent<TResponse>) => void | Promise<void>;
|
|
72
70
|
interface EventStreamRpcHandlerContext<TParams = any, TResponse = any> extends RpcHandlerContext<TParams> {
|
|
73
|
-
|
|
71
|
+
stream: EventStreamConnection<TResponse>;
|
|
74
72
|
}
|
|
75
73
|
interface EventStreamConnectionOptions<TData> {
|
|
76
74
|
validator: (input: unknown) => input is TData;
|
|
@@ -78,34 +76,20 @@ interface EventStreamConnectionOptions<TData> {
|
|
|
78
76
|
serializer: (input: TData) => string;
|
|
79
77
|
pingInterval?: number;
|
|
80
78
|
}
|
|
81
|
-
/**
|
|
82
|
-
* A server sent event
|
|
83
|
-
*/
|
|
84
|
-
interface Sse {
|
|
85
|
-
id?: string;
|
|
86
|
-
event?: string;
|
|
87
|
-
data: string;
|
|
88
|
-
}
|
|
89
|
-
declare function formatSse({ id, data, event }: Sse): string;
|
|
90
|
-
declare function formatSseList(events: Sse[]): string;
|
|
91
79
|
declare class EventStreamConnection<TData> {
|
|
92
|
-
lastEventId
|
|
93
|
-
private readonly
|
|
94
|
-
private readonly readable;
|
|
95
|
-
private readonly writer;
|
|
96
|
-
private writerIsClosed;
|
|
97
|
-
private readonly encoder;
|
|
80
|
+
readonly lastEventId?: string;
|
|
81
|
+
private readonly h3Event;
|
|
98
82
|
private readonly validationErrors;
|
|
99
83
|
private readonly validator;
|
|
100
84
|
private readonly serializer;
|
|
101
|
-
private readonly h3Event;
|
|
102
85
|
private pingInterval;
|
|
103
86
|
private readonly pingIntervalMs;
|
|
87
|
+
readonly eventStream: EventStream;
|
|
104
88
|
constructor(event: H3Event, opts: EventStreamConnectionOptions<TData>);
|
|
105
89
|
/**
|
|
106
|
-
*
|
|
90
|
+
* Initialize the stream. This must be called before sending any events.
|
|
107
91
|
*/
|
|
108
|
-
|
|
92
|
+
init(): void;
|
|
109
93
|
/**
|
|
110
94
|
* Publish a new event. Events published with this hook will trigger the `onData()` hooks of any connected clients.
|
|
111
95
|
*/
|
|
@@ -115,15 +99,13 @@ declare class EventStreamConnection<TData> {
|
|
|
115
99
|
* Publish an error event. This will trigger the `onError` hooks of any connected clients.
|
|
116
100
|
*/
|
|
117
101
|
pushError(error: ErrorResponse, eventId?: string): Promise<void>;
|
|
118
|
-
private publishEvents;
|
|
119
|
-
private publishEvent;
|
|
120
102
|
private cleanup;
|
|
121
103
|
/**
|
|
122
104
|
* Tell clients that the stream has ended and close the connection.
|
|
123
105
|
*/
|
|
124
|
-
|
|
125
|
-
on(event: "
|
|
126
|
-
on(event: "
|
|
106
|
+
close(): Promise<void>;
|
|
107
|
+
on(event: "close", callback: () => any): void;
|
|
108
|
+
on(event: "request:close", callback: () => any): void;
|
|
127
109
|
}
|
|
128
110
|
declare function registerEventStreamRpc(router: Router, path: string, procedure: EventStreamRpc<any, any> & {
|
|
129
111
|
name: string;
|
|
@@ -257,4 +239,4 @@ type ResolvedArriConfig = Required<ArriConfig>;
|
|
|
257
239
|
declare const defaultConfig: Required<ArriConfig>;
|
|
258
240
|
declare function defineConfig(config: ArriConfig): ResolvedArriConfig;
|
|
259
241
|
|
|
260
|
-
export { ArriApp, type ArriConfig, type ArriOptions, type ArriRoute, ArriRouter, type ArriRouterBase, DEV_DEFINITION_ENDPOINT, DEV_ENDPOINT_ROOT, ErrorResponse, EventStreamConnection, type EventStreamConnectionOptions, type EventStreamRpc, type
|
|
242
|
+
export { ArriApp, type ArriConfig, type ArriOptions, type ArriRoute, ArriRouter, type ArriRouterBase, DEV_DEFINITION_ENDPOINT, DEV_ENDPOINT_ROOT, ErrorResponse, EventStreamConnection, type EventStreamConnectionOptions, type EventStreamRpc, type EventStreamRpcHandler, type EventStreamRpcHandlerContext, type ExtractParam, type ExtractParams, type HandlerContext, type HttpRpc, type Middleware, type MiddlewareEvent, type MiddlewareEventContext, type ModelMap, type NamedRpc, type PostRouteEvent, type PostRouteEventContext, type ResolvedArriConfig, type RouteEvent, type RouteEventContext, type RouteOptions, type Rpc, type RpcEvent, type RpcHandler, type RpcHandlerContext, type RpcParamSchema, type RpcPostEvent, type RpcPostHandler, type RpcPostHandlerContext, type StatusCode, createRpcDefinition, defaultConfig, defineConfig, defineError, defineEventStreamRpc, defineMiddleware, defineRoute, defineRpc, errorResponseFromValidationErrors, getRpcParamName, getRpcPath, getRpcResponseName, handleH3Error, handleRoute, isArriConfig, isEventStreamRpc, isResolvedArriConfig, isRpc, isRpcParamSchema, registerEventStreamRpc, registerRoute, registerRpc, validateRpcRequestInput };
|
package/dist/index.d.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { HttpMethod, RpcHttpMethod, RpcDefinition, SchemaFormProperties, SchemaF
|
|
|
2
2
|
import { AObjectSchema, ASchema, InferType, a, ValidationError, ValueError, ARecordSchema, ADiscriminatorSchema } from 'arri-validate';
|
|
3
3
|
import { H3EventContext, H3Event, Router, H3Error, App } from 'h3';
|
|
4
4
|
export * from 'h3';
|
|
5
|
+
import { EventStream } from 'h3-sse';
|
|
6
|
+
export * from 'h3-sse';
|
|
5
7
|
import { BuildOptions } from 'esbuild';
|
|
6
8
|
|
|
7
9
|
interface MiddlewareEventContext extends H3EventContext {
|
|
@@ -58,19 +60,15 @@ declare function errorResponseFromValidationErrors(errors: ValidationError["erro
|
|
|
58
60
|
type StatusCode = number | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 428 | 429 | 431 | 451 | 500 | 501 | 502 | 503 | 504 | 505 | 507 | 511;
|
|
59
61
|
declare function handleH3Error(err: unknown, event: H3Event, onError: ArriOptions["onError"]): Promise<void>;
|
|
60
62
|
|
|
61
|
-
declare function setSseHeaders(event: H3Event): void;
|
|
62
63
|
declare function defineEventStreamRpc<TParams extends RpcParamSchema | undefined = undefined, TResponse extends RpcParamSchema | undefined | never = undefined>(config: Omit<EventStreamRpc<TParams, TResponse>, "isEventStream">): EventStreamRpc<TParams, TResponse>;
|
|
63
64
|
declare function isEventStreamRpc(input: unknown): input is EventStreamRpc<any, any>;
|
|
64
65
|
interface EventStreamRpc<TParams extends RpcParamSchema | undefined = undefined, TResponse extends RpcParamSchema | undefined = undefined> extends Omit<Rpc<true, TParams, TResponse>, "handler" | "postHandler"> {
|
|
65
66
|
isEventStream: true;
|
|
66
67
|
handler: EventStreamRpcHandler<TParams extends RpcParamSchema ? InferType<TParams> : undefined, TResponse extends RpcParamSchema ? InferType<TResponse> : undefined>;
|
|
67
68
|
}
|
|
68
|
-
type EventStreamRpcHandler<TParams, TResponse> = (context: EventStreamRpcHandlerContext<TParams, TResponse>, event:
|
|
69
|
-
interface EventStreamRpcEvent<TParams, TResponse> extends RpcEvent<TParams> {
|
|
70
|
-
connection: EventStreamConnection<TResponse>;
|
|
71
|
-
}
|
|
69
|
+
type EventStreamRpcHandler<TParams, TResponse> = (context: EventStreamRpcHandlerContext<TParams, TResponse>, event: RpcEvent<TResponse>) => void | Promise<void>;
|
|
72
70
|
interface EventStreamRpcHandlerContext<TParams = any, TResponse = any> extends RpcHandlerContext<TParams> {
|
|
73
|
-
|
|
71
|
+
stream: EventStreamConnection<TResponse>;
|
|
74
72
|
}
|
|
75
73
|
interface EventStreamConnectionOptions<TData> {
|
|
76
74
|
validator: (input: unknown) => input is TData;
|
|
@@ -78,34 +76,20 @@ interface EventStreamConnectionOptions<TData> {
|
|
|
78
76
|
serializer: (input: TData) => string;
|
|
79
77
|
pingInterval?: number;
|
|
80
78
|
}
|
|
81
|
-
/**
|
|
82
|
-
* A server sent event
|
|
83
|
-
*/
|
|
84
|
-
interface Sse {
|
|
85
|
-
id?: string;
|
|
86
|
-
event?: string;
|
|
87
|
-
data: string;
|
|
88
|
-
}
|
|
89
|
-
declare function formatSse({ id, data, event }: Sse): string;
|
|
90
|
-
declare function formatSseList(events: Sse[]): string;
|
|
91
79
|
declare class EventStreamConnection<TData> {
|
|
92
|
-
lastEventId
|
|
93
|
-
private readonly
|
|
94
|
-
private readonly readable;
|
|
95
|
-
private readonly writer;
|
|
96
|
-
private writerIsClosed;
|
|
97
|
-
private readonly encoder;
|
|
80
|
+
readonly lastEventId?: string;
|
|
81
|
+
private readonly h3Event;
|
|
98
82
|
private readonly validationErrors;
|
|
99
83
|
private readonly validator;
|
|
100
84
|
private readonly serializer;
|
|
101
|
-
private readonly h3Event;
|
|
102
85
|
private pingInterval;
|
|
103
86
|
private readonly pingIntervalMs;
|
|
87
|
+
readonly eventStream: EventStream;
|
|
104
88
|
constructor(event: H3Event, opts: EventStreamConnectionOptions<TData>);
|
|
105
89
|
/**
|
|
106
|
-
*
|
|
90
|
+
* Initialize the stream. This must be called before sending any events.
|
|
107
91
|
*/
|
|
108
|
-
|
|
92
|
+
init(): void;
|
|
109
93
|
/**
|
|
110
94
|
* Publish a new event. Events published with this hook will trigger the `onData()` hooks of any connected clients.
|
|
111
95
|
*/
|
|
@@ -115,15 +99,13 @@ declare class EventStreamConnection<TData> {
|
|
|
115
99
|
* Publish an error event. This will trigger the `onError` hooks of any connected clients.
|
|
116
100
|
*/
|
|
117
101
|
pushError(error: ErrorResponse, eventId?: string): Promise<void>;
|
|
118
|
-
private publishEvents;
|
|
119
|
-
private publishEvent;
|
|
120
102
|
private cleanup;
|
|
121
103
|
/**
|
|
122
104
|
* Tell clients that the stream has ended and close the connection.
|
|
123
105
|
*/
|
|
124
|
-
|
|
125
|
-
on(event: "
|
|
126
|
-
on(event: "
|
|
106
|
+
close(): Promise<void>;
|
|
107
|
+
on(event: "close", callback: () => any): void;
|
|
108
|
+
on(event: "request:close", callback: () => any): void;
|
|
127
109
|
}
|
|
128
110
|
declare function registerEventStreamRpc(router: Router, path: string, procedure: EventStreamRpc<any, any> & {
|
|
129
111
|
name: string;
|
|
@@ -257,4 +239,4 @@ type ResolvedArriConfig = Required<ArriConfig>;
|
|
|
257
239
|
declare const defaultConfig: Required<ArriConfig>;
|
|
258
240
|
declare function defineConfig(config: ArriConfig): ResolvedArriConfig;
|
|
259
241
|
|
|
260
|
-
export { ArriApp, type ArriConfig, type ArriOptions, type ArriRoute, ArriRouter, type ArriRouterBase, DEV_DEFINITION_ENDPOINT, DEV_ENDPOINT_ROOT, ErrorResponse, EventStreamConnection, type EventStreamConnectionOptions, type EventStreamRpc, type
|
|
242
|
+
export { ArriApp, type ArriConfig, type ArriOptions, type ArriRoute, ArriRouter, type ArriRouterBase, DEV_DEFINITION_ENDPOINT, DEV_ENDPOINT_ROOT, ErrorResponse, EventStreamConnection, type EventStreamConnectionOptions, type EventStreamRpc, type EventStreamRpcHandler, type EventStreamRpcHandlerContext, type ExtractParam, type ExtractParams, type HandlerContext, type HttpRpc, type Middleware, type MiddlewareEvent, type MiddlewareEventContext, type ModelMap, type NamedRpc, type PostRouteEvent, type PostRouteEventContext, type ResolvedArriConfig, type RouteEvent, type RouteEventContext, type RouteOptions, type Rpc, type RpcEvent, type RpcHandler, type RpcHandlerContext, type RpcParamSchema, type RpcPostEvent, type RpcPostHandler, type RpcPostHandlerContext, type StatusCode, createRpcDefinition, defaultConfig, defineConfig, defineError, defineEventStreamRpc, defineMiddleware, defineRoute, defineRpc, errorResponseFromValidationErrors, getRpcParamName, getRpcPath, getRpcResponseName, handleH3Error, handleRoute, isArriConfig, isEventStreamRpc, isResolvedArriConfig, isRpc, isRpcParamSchema, registerEventStreamRpc, registerRoute, registerRpc, validateRpcRequestInput };
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { A as ArriApp, p as ArriRouter, a as DEV_DEFINITION_ENDPOINT, D as DEV_ENDPOINT_ROOT, E as ErrorResponse,
|
|
1
|
+
export { A as ArriApp, p as ArriRouter, a as DEV_DEFINITION_ENDPOINT, D as DEV_ENDPOINT_ROOT, E as ErrorResponse, x as EventStreamConnection, c as createRpcDefinition, n as defaultConfig, o as defineConfig, h as defineError, u as defineEventStreamRpc, q as defineRoute, d as defineRpc, j as errorResponseFromValidationErrors, e as getRpcParamName, g as getRpcPath, f as getRpcResponseName, k as handleH3Error, t as handleRoute, l as isArriConfig, w as isEventStreamRpc, m as isResolvedArriConfig, b as isRpc, i as isRpcParamSchema, y as registerEventStreamRpc, s as registerRoute, r as registerRpc, v as validateRpcRequestInput } from './shared/arri.21567b92.mjs';
|
|
2
2
|
export * from 'h3';
|
|
3
|
+
export * from 'h3-sse';
|
|
3
4
|
import 'arri-validate';
|
|
4
|
-
import 'node:process';
|
|
5
5
|
import 'arri-codegen-utils';
|
|
6
6
|
import 'scule';
|
|
7
7
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { createError, isError, setResponseStatus, send, eventHandler, isPreflightRequest, setResponseHeader, readRawBody, getValidatedQuery,
|
|
1
|
+
import { createError, isError, setResponseStatus, send, eventHandler, isPreflightRequest, setResponseHeader, readRawBody, getValidatedQuery, defineEventHandler, getQuery, setHeader, createRouter, createApp, sendError } from 'h3';
|
|
2
2
|
import { a, isAObjectSchema, isARecordSchema, isADiscriminatorSchema } from 'arri-validate';
|
|
3
|
-
import {
|
|
3
|
+
import { createEventStream, sendEventStream } from 'h3-sse';
|
|
4
4
|
import { isRpcHttpMethod, removeDisallowedChars } from 'arri-codegen-utils';
|
|
5
5
|
import { kebabCase, pascalCase } from 'scule';
|
|
6
6
|
|
|
@@ -431,19 +431,6 @@ var __publicField$2 = (obj, key, value) => {
|
|
|
431
431
|
__defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
432
432
|
return value;
|
|
433
433
|
};
|
|
434
|
-
function setSseHeaders(event) {
|
|
435
|
-
const isHttp2 = getHeader(event, ":path") !== void 0 && getHeader(event, ":method") !== void 0;
|
|
436
|
-
const input = {
|
|
437
|
-
"Content-Type": "text/event-stream",
|
|
438
|
-
"Cache-Control": "private, no-cache, no-store, no-transform, must-revalidate, max-age=0",
|
|
439
|
-
Pragma: "no-cache",
|
|
440
|
-
"X-Accel-Buffering": "no"
|
|
441
|
-
};
|
|
442
|
-
if (!isHttp2) {
|
|
443
|
-
input.Connection = "keep-alive";
|
|
444
|
-
}
|
|
445
|
-
setResponseHeaders(event, input);
|
|
446
|
-
}
|
|
447
434
|
function defineEventStreamRpc(config) {
|
|
448
435
|
return {
|
|
449
436
|
...config,
|
|
@@ -454,69 +441,35 @@ function defineEventStreamRpc(config) {
|
|
|
454
441
|
function isEventStreamRpc(input) {
|
|
455
442
|
return isRpc(input) && "isEventStream" in input && input.isEventStream === true;
|
|
456
443
|
}
|
|
457
|
-
function formatSse({ id, data, event }) {
|
|
458
|
-
const parts = [];
|
|
459
|
-
if (id) {
|
|
460
|
-
parts.push(`id: ${id}`);
|
|
461
|
-
}
|
|
462
|
-
if (event) {
|
|
463
|
-
parts.push(`event: ${event}`);
|
|
464
|
-
}
|
|
465
|
-
parts.push(`data: ${data}`);
|
|
466
|
-
const payload = `${parts.join("\n")}
|
|
467
|
-
|
|
468
|
-
`;
|
|
469
|
-
return payload;
|
|
470
|
-
}
|
|
471
|
-
function formatSseList(events) {
|
|
472
|
-
let output = "";
|
|
473
|
-
for (const event of events) {
|
|
474
|
-
output += formatSse(event);
|
|
475
|
-
}
|
|
476
|
-
return output;
|
|
477
|
-
}
|
|
478
444
|
class EventStreamConnection {
|
|
479
445
|
constructor(event, opts) {
|
|
480
446
|
__publicField$2(this, "lastEventId");
|
|
481
|
-
__publicField$2(this, "
|
|
482
|
-
__publicField$2(this, "readable");
|
|
483
|
-
__publicField$2(this, "writer");
|
|
484
|
-
__publicField$2(this, "writerIsClosed", false);
|
|
485
|
-
__publicField$2(this, "encoder");
|
|
447
|
+
__publicField$2(this, "h3Event");
|
|
486
448
|
__publicField$2(this, "validationErrors");
|
|
487
449
|
__publicField$2(this, "validator");
|
|
488
450
|
__publicField$2(this, "serializer");
|
|
489
|
-
__publicField$2(this, "h3Event");
|
|
490
451
|
// for some reason Rollup cannot output DTS when this is set to NodeJS.Timeout
|
|
491
452
|
__publicField$2(this, "pingInterval");
|
|
492
453
|
__publicField$2(this, "pingIntervalMs");
|
|
454
|
+
__publicField$2(this, "eventStream");
|
|
493
455
|
this.h3Event = event;
|
|
494
|
-
|
|
495
|
-
this.lastEventId =
|
|
496
|
-
const { readable, writable } = new TransformStream();
|
|
497
|
-
this.writable = writable;
|
|
498
|
-
this.readable = readable;
|
|
499
|
-
this.writer = writable.getWriter();
|
|
500
|
-
this.encoder = new TextEncoder();
|
|
456
|
+
this.eventStream = createEventStream(event, true);
|
|
457
|
+
this.lastEventId = this.eventStream.lastEventId;
|
|
501
458
|
this.pingIntervalMs = opts.pingInterval ?? 6e4;
|
|
502
459
|
this.serializer = opts.serializer;
|
|
503
460
|
this.validator = opts.validator;
|
|
504
461
|
this.validationErrors = opts.validationErrors;
|
|
505
|
-
|
|
506
|
-
this.
|
|
462
|
+
this.eventStream.on("close", () => {
|
|
463
|
+
this.cleanup();
|
|
507
464
|
});
|
|
508
465
|
}
|
|
509
466
|
/**
|
|
510
|
-
*
|
|
467
|
+
* Initialize the stream. This must be called before sending any events.
|
|
511
468
|
*/
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
setResponseStatus(this.h3Event, 200);
|
|
515
|
-
this.h3Event._handled = true;
|
|
516
|
-
void sendStream(this.h3Event, this.readable);
|
|
469
|
+
init() {
|
|
470
|
+
void sendEventStream(this.h3Event, this.eventStream);
|
|
517
471
|
this.pingInterval = setInterval(async () => {
|
|
518
|
-
await this.
|
|
519
|
-
id: this.lastEventId,
|
|
472
|
+
await this.eventStream.push({
|
|
520
473
|
event: "ping",
|
|
521
474
|
data: ""
|
|
522
475
|
});
|
|
@@ -546,11 +499,11 @@ class EventStreamConnection {
|
|
|
546
499
|
data: JSON.stringify(errorResponse2)
|
|
547
500
|
});
|
|
548
501
|
}
|
|
549
|
-
await this.
|
|
502
|
+
await this.eventStream.push(events);
|
|
550
503
|
return;
|
|
551
504
|
}
|
|
552
505
|
if (this.validator(data)) {
|
|
553
|
-
await this.
|
|
506
|
+
await this.eventStream.push({
|
|
554
507
|
id: eventId,
|
|
555
508
|
event: "message",
|
|
556
509
|
data: this.serializer(data)
|
|
@@ -563,7 +516,7 @@ class EventStreamConnection {
|
|
|
563
516
|
statusMessage: "Failed to serialize response. Response does not match specified schema.",
|
|
564
517
|
data: errors
|
|
565
518
|
};
|
|
566
|
-
await this.
|
|
519
|
+
await this.eventStream.push({
|
|
567
520
|
id: eventId,
|
|
568
521
|
event: "error",
|
|
569
522
|
data: JSON.stringify(errorResponse)
|
|
@@ -589,79 +542,34 @@ class EventStreamConnection {
|
|
|
589
542
|
* Publish an error event. This will trigger the `onError` hooks of any connected clients.
|
|
590
543
|
*/
|
|
591
544
|
async pushError(error, eventId) {
|
|
592
|
-
await this.
|
|
545
|
+
await this.eventStream.push({
|
|
593
546
|
id: eventId,
|
|
594
547
|
event: "error",
|
|
595
548
|
data: JSON.stringify(error)
|
|
596
549
|
});
|
|
597
550
|
}
|
|
598
|
-
|
|
599
|
-
const payload = formatSseList(events);
|
|
600
|
-
if (this.writerIsClosed) {
|
|
601
|
-
try {
|
|
602
|
-
this.h3Event.node.res.end();
|
|
603
|
-
} catch (_) {
|
|
604
|
-
await this.cleanup();
|
|
605
|
-
}
|
|
606
|
-
return;
|
|
607
|
-
}
|
|
608
|
-
await this.writer.write(this.encoder.encode(payload));
|
|
609
|
-
}
|
|
610
|
-
async publishEvent(event) {
|
|
611
|
-
const payload = formatSse(event);
|
|
612
|
-
if (this.writerIsClosed) {
|
|
613
|
-
try {
|
|
614
|
-
this.h3Event.node.res.end();
|
|
615
|
-
} catch (_) {
|
|
616
|
-
await this.cleanup();
|
|
617
|
-
}
|
|
618
|
-
return;
|
|
619
|
-
}
|
|
620
|
-
await this.writer.write(this.encoder.encode(payload));
|
|
621
|
-
}
|
|
622
|
-
async cleanup() {
|
|
551
|
+
cleanup() {
|
|
623
552
|
if (this.pingInterval) {
|
|
624
553
|
clearInterval(this.pingInterval);
|
|
625
554
|
}
|
|
626
|
-
if (!this.writerIsClosed) {
|
|
627
|
-
try {
|
|
628
|
-
await this.writer.close();
|
|
629
|
-
} catch (_) {
|
|
630
|
-
}
|
|
631
|
-
}
|
|
632
555
|
}
|
|
633
556
|
/**
|
|
634
557
|
* Tell clients that the stream has ended and close the connection.
|
|
635
558
|
*/
|
|
636
|
-
async
|
|
637
|
-
await this.
|
|
559
|
+
async close() {
|
|
560
|
+
await this.eventStream.push({
|
|
638
561
|
event: "done",
|
|
639
562
|
data: "this stream has ended"
|
|
640
563
|
}).catch();
|
|
641
|
-
await
|
|
642
|
-
nextTick(() => {
|
|
643
|
-
try {
|
|
644
|
-
this.h3Event.node.res.end();
|
|
645
|
-
resolve(true);
|
|
646
|
-
} catch (err) {
|
|
647
|
-
reject(err);
|
|
648
|
-
}
|
|
649
|
-
});
|
|
650
|
-
});
|
|
564
|
+
await this.eventStream.close();
|
|
651
565
|
}
|
|
652
566
|
on(event, callback) {
|
|
653
567
|
switch (event) {
|
|
654
|
-
case "
|
|
655
|
-
this.
|
|
656
|
-
await callback();
|
|
657
|
-
await this.cleanup();
|
|
658
|
-
});
|
|
568
|
+
case "close":
|
|
569
|
+
this.eventStream.on("close", callback);
|
|
659
570
|
break;
|
|
660
|
-
case "
|
|
661
|
-
this.
|
|
662
|
-
await callback();
|
|
663
|
-
await this.cleanup();
|
|
664
|
-
});
|
|
571
|
+
case "request:close":
|
|
572
|
+
this.eventStream.on("request:close", callback);
|
|
665
573
|
break;
|
|
666
574
|
}
|
|
667
575
|
}
|
|
@@ -695,7 +603,7 @@ function registerEventStreamRpc(router, path, procedure, opts) {
|
|
|
695
603
|
procedure.params
|
|
696
604
|
);
|
|
697
605
|
}
|
|
698
|
-
const
|
|
606
|
+
const stream = new EventStreamConnection(event, {
|
|
699
607
|
pingInterval: procedure.pingInterval,
|
|
700
608
|
validator: responseValidator?.validate ?? function() {
|
|
701
609
|
return true;
|
|
@@ -710,11 +618,14 @@ function registerEventStreamRpc(router, path, procedure, opts) {
|
|
|
710
618
|
return [];
|
|
711
619
|
}
|
|
712
620
|
});
|
|
713
|
-
event.context.
|
|
621
|
+
event.context.stream = stream;
|
|
714
622
|
await procedure.handler(
|
|
715
623
|
event.context,
|
|
716
624
|
event
|
|
717
625
|
);
|
|
626
|
+
if (!event.handled && !stream.eventStream._handled) {
|
|
627
|
+
await sendEventStream(event, stream.eventStream);
|
|
628
|
+
}
|
|
718
629
|
} catch (err) {
|
|
719
630
|
await handleH3Error(err, event, opts.onError);
|
|
720
631
|
}
|
|
@@ -1086,4 +997,4 @@ function defineConfig(config) {
|
|
|
1086
997
|
};
|
|
1087
998
|
}
|
|
1088
999
|
|
|
1089
|
-
export { ArriApp as A,
|
|
1000
|
+
export { ArriApp as A, DEV_ENDPOINT_ROOT as D, ErrorResponse as E, DEV_DEFINITION_ENDPOINT as a, isRpc as b, createRpcDefinition as c, defineRpc as d, getRpcParamName as e, getRpcResponseName as f, getRpcPath as g, defineError as h, isRpcParamSchema as i, errorResponseFromValidationErrors as j, handleH3Error as k, isArriConfig as l, isResolvedArriConfig as m, defaultConfig as n, defineConfig as o, ArriRouter as p, defineRoute as q, registerRpc as r, registerRoute as s, handleRoute as t, defineEventStreamRpc as u, validateRpcRequestInput as v, isEventStreamRpc as w, EventStreamConnection as x, registerEventStreamRpc as y };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const h3 = require('h3');
|
|
4
4
|
const arriValidate = require('arri-validate');
|
|
5
|
-
const
|
|
5
|
+
const h3Sse = require('h3-sse');
|
|
6
6
|
const arriCodegenUtils = require('arri-codegen-utils');
|
|
7
7
|
const scule = require('scule');
|
|
8
8
|
|
|
@@ -433,19 +433,6 @@ var __publicField$2 = (obj, key, value) => {
|
|
|
433
433
|
__defNormalProp$2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
434
434
|
return value;
|
|
435
435
|
};
|
|
436
|
-
function setSseHeaders(event) {
|
|
437
|
-
const isHttp2 = h3.getHeader(event, ":path") !== void 0 && h3.getHeader(event, ":method") !== void 0;
|
|
438
|
-
const input = {
|
|
439
|
-
"Content-Type": "text/event-stream",
|
|
440
|
-
"Cache-Control": "private, no-cache, no-store, no-transform, must-revalidate, max-age=0",
|
|
441
|
-
Pragma: "no-cache",
|
|
442
|
-
"X-Accel-Buffering": "no"
|
|
443
|
-
};
|
|
444
|
-
if (!isHttp2) {
|
|
445
|
-
input.Connection = "keep-alive";
|
|
446
|
-
}
|
|
447
|
-
h3.setResponseHeaders(event, input);
|
|
448
|
-
}
|
|
449
436
|
function defineEventStreamRpc(config) {
|
|
450
437
|
return {
|
|
451
438
|
...config,
|
|
@@ -456,69 +443,35 @@ function defineEventStreamRpc(config) {
|
|
|
456
443
|
function isEventStreamRpc(input) {
|
|
457
444
|
return isRpc(input) && "isEventStream" in input && input.isEventStream === true;
|
|
458
445
|
}
|
|
459
|
-
function formatSse({ id, data, event }) {
|
|
460
|
-
const parts = [];
|
|
461
|
-
if (id) {
|
|
462
|
-
parts.push(`id: ${id}`);
|
|
463
|
-
}
|
|
464
|
-
if (event) {
|
|
465
|
-
parts.push(`event: ${event}`);
|
|
466
|
-
}
|
|
467
|
-
parts.push(`data: ${data}`);
|
|
468
|
-
const payload = `${parts.join("\n")}
|
|
469
|
-
|
|
470
|
-
`;
|
|
471
|
-
return payload;
|
|
472
|
-
}
|
|
473
|
-
function formatSseList(events) {
|
|
474
|
-
let output = "";
|
|
475
|
-
for (const event of events) {
|
|
476
|
-
output += formatSse(event);
|
|
477
|
-
}
|
|
478
|
-
return output;
|
|
479
|
-
}
|
|
480
446
|
class EventStreamConnection {
|
|
481
447
|
constructor(event, opts) {
|
|
482
448
|
__publicField$2(this, "lastEventId");
|
|
483
|
-
__publicField$2(this, "
|
|
484
|
-
__publicField$2(this, "readable");
|
|
485
|
-
__publicField$2(this, "writer");
|
|
486
|
-
__publicField$2(this, "writerIsClosed", false);
|
|
487
|
-
__publicField$2(this, "encoder");
|
|
449
|
+
__publicField$2(this, "h3Event");
|
|
488
450
|
__publicField$2(this, "validationErrors");
|
|
489
451
|
__publicField$2(this, "validator");
|
|
490
452
|
__publicField$2(this, "serializer");
|
|
491
|
-
__publicField$2(this, "h3Event");
|
|
492
453
|
// for some reason Rollup cannot output DTS when this is set to NodeJS.Timeout
|
|
493
454
|
__publicField$2(this, "pingInterval");
|
|
494
455
|
__publicField$2(this, "pingIntervalMs");
|
|
456
|
+
__publicField$2(this, "eventStream");
|
|
495
457
|
this.h3Event = event;
|
|
496
|
-
|
|
497
|
-
this.lastEventId =
|
|
498
|
-
const { readable, writable } = new TransformStream();
|
|
499
|
-
this.writable = writable;
|
|
500
|
-
this.readable = readable;
|
|
501
|
-
this.writer = writable.getWriter();
|
|
502
|
-
this.encoder = new TextEncoder();
|
|
458
|
+
this.eventStream = h3Sse.createEventStream(event, true);
|
|
459
|
+
this.lastEventId = this.eventStream.lastEventId;
|
|
503
460
|
this.pingIntervalMs = opts.pingInterval ?? 6e4;
|
|
504
461
|
this.serializer = opts.serializer;
|
|
505
462
|
this.validator = opts.validator;
|
|
506
463
|
this.validationErrors = opts.validationErrors;
|
|
507
|
-
|
|
508
|
-
this.
|
|
464
|
+
this.eventStream.on("close", () => {
|
|
465
|
+
this.cleanup();
|
|
509
466
|
});
|
|
510
467
|
}
|
|
511
468
|
/**
|
|
512
|
-
*
|
|
469
|
+
* Initialize the stream. This must be called before sending any events.
|
|
513
470
|
*/
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
h3.setResponseStatus(this.h3Event, 200);
|
|
517
|
-
this.h3Event._handled = true;
|
|
518
|
-
void h3.sendStream(this.h3Event, this.readable);
|
|
471
|
+
init() {
|
|
472
|
+
void h3Sse.sendEventStream(this.h3Event, this.eventStream);
|
|
519
473
|
this.pingInterval = setInterval(async () => {
|
|
520
|
-
await this.
|
|
521
|
-
id: this.lastEventId,
|
|
474
|
+
await this.eventStream.push({
|
|
522
475
|
event: "ping",
|
|
523
476
|
data: ""
|
|
524
477
|
});
|
|
@@ -548,11 +501,11 @@ class EventStreamConnection {
|
|
|
548
501
|
data: JSON.stringify(errorResponse2)
|
|
549
502
|
});
|
|
550
503
|
}
|
|
551
|
-
await this.
|
|
504
|
+
await this.eventStream.push(events);
|
|
552
505
|
return;
|
|
553
506
|
}
|
|
554
507
|
if (this.validator(data)) {
|
|
555
|
-
await this.
|
|
508
|
+
await this.eventStream.push({
|
|
556
509
|
id: eventId,
|
|
557
510
|
event: "message",
|
|
558
511
|
data: this.serializer(data)
|
|
@@ -565,7 +518,7 @@ class EventStreamConnection {
|
|
|
565
518
|
statusMessage: "Failed to serialize response. Response does not match specified schema.",
|
|
566
519
|
data: errors
|
|
567
520
|
};
|
|
568
|
-
await this.
|
|
521
|
+
await this.eventStream.push({
|
|
569
522
|
id: eventId,
|
|
570
523
|
event: "error",
|
|
571
524
|
data: JSON.stringify(errorResponse)
|
|
@@ -591,79 +544,34 @@ class EventStreamConnection {
|
|
|
591
544
|
* Publish an error event. This will trigger the `onError` hooks of any connected clients.
|
|
592
545
|
*/
|
|
593
546
|
async pushError(error, eventId) {
|
|
594
|
-
await this.
|
|
547
|
+
await this.eventStream.push({
|
|
595
548
|
id: eventId,
|
|
596
549
|
event: "error",
|
|
597
550
|
data: JSON.stringify(error)
|
|
598
551
|
});
|
|
599
552
|
}
|
|
600
|
-
|
|
601
|
-
const payload = formatSseList(events);
|
|
602
|
-
if (this.writerIsClosed) {
|
|
603
|
-
try {
|
|
604
|
-
this.h3Event.node.res.end();
|
|
605
|
-
} catch (_) {
|
|
606
|
-
await this.cleanup();
|
|
607
|
-
}
|
|
608
|
-
return;
|
|
609
|
-
}
|
|
610
|
-
await this.writer.write(this.encoder.encode(payload));
|
|
611
|
-
}
|
|
612
|
-
async publishEvent(event) {
|
|
613
|
-
const payload = formatSse(event);
|
|
614
|
-
if (this.writerIsClosed) {
|
|
615
|
-
try {
|
|
616
|
-
this.h3Event.node.res.end();
|
|
617
|
-
} catch (_) {
|
|
618
|
-
await this.cleanup();
|
|
619
|
-
}
|
|
620
|
-
return;
|
|
621
|
-
}
|
|
622
|
-
await this.writer.write(this.encoder.encode(payload));
|
|
623
|
-
}
|
|
624
|
-
async cleanup() {
|
|
553
|
+
cleanup() {
|
|
625
554
|
if (this.pingInterval) {
|
|
626
555
|
clearInterval(this.pingInterval);
|
|
627
556
|
}
|
|
628
|
-
if (!this.writerIsClosed) {
|
|
629
|
-
try {
|
|
630
|
-
await this.writer.close();
|
|
631
|
-
} catch (_) {
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
557
|
}
|
|
635
558
|
/**
|
|
636
559
|
* Tell clients that the stream has ended and close the connection.
|
|
637
560
|
*/
|
|
638
|
-
async
|
|
639
|
-
await this.
|
|
561
|
+
async close() {
|
|
562
|
+
await this.eventStream.push({
|
|
640
563
|
event: "done",
|
|
641
564
|
data: "this stream has ended"
|
|
642
565
|
}).catch();
|
|
643
|
-
await
|
|
644
|
-
node_process.nextTick(() => {
|
|
645
|
-
try {
|
|
646
|
-
this.h3Event.node.res.end();
|
|
647
|
-
resolve(true);
|
|
648
|
-
} catch (err) {
|
|
649
|
-
reject(err);
|
|
650
|
-
}
|
|
651
|
-
});
|
|
652
|
-
});
|
|
566
|
+
await this.eventStream.close();
|
|
653
567
|
}
|
|
654
568
|
on(event, callback) {
|
|
655
569
|
switch (event) {
|
|
656
|
-
case "
|
|
657
|
-
this.
|
|
658
|
-
await callback();
|
|
659
|
-
await this.cleanup();
|
|
660
|
-
});
|
|
570
|
+
case "close":
|
|
571
|
+
this.eventStream.on("close", callback);
|
|
661
572
|
break;
|
|
662
|
-
case "
|
|
663
|
-
this.
|
|
664
|
-
await callback();
|
|
665
|
-
await this.cleanup();
|
|
666
|
-
});
|
|
573
|
+
case "request:close":
|
|
574
|
+
this.eventStream.on("request:close", callback);
|
|
667
575
|
break;
|
|
668
576
|
}
|
|
669
577
|
}
|
|
@@ -697,7 +605,7 @@ function registerEventStreamRpc(router, path, procedure, opts) {
|
|
|
697
605
|
procedure.params
|
|
698
606
|
);
|
|
699
607
|
}
|
|
700
|
-
const
|
|
608
|
+
const stream = new EventStreamConnection(event, {
|
|
701
609
|
pingInterval: procedure.pingInterval,
|
|
702
610
|
validator: responseValidator?.validate ?? function() {
|
|
703
611
|
return true;
|
|
@@ -712,11 +620,14 @@ function registerEventStreamRpc(router, path, procedure, opts) {
|
|
|
712
620
|
return [];
|
|
713
621
|
}
|
|
714
622
|
});
|
|
715
|
-
event.context.
|
|
623
|
+
event.context.stream = stream;
|
|
716
624
|
await procedure.handler(
|
|
717
625
|
event.context,
|
|
718
626
|
event
|
|
719
627
|
);
|
|
628
|
+
if (!event.handled && !stream.eventStream._handled) {
|
|
629
|
+
await h3Sse.sendEventStream(event, stream.eventStream);
|
|
630
|
+
}
|
|
720
631
|
} catch (err) {
|
|
721
632
|
await handleH3Error(err, event, opts.onError);
|
|
722
633
|
}
|
|
@@ -1102,8 +1013,6 @@ exports.defineEventStreamRpc = defineEventStreamRpc;
|
|
|
1102
1013
|
exports.defineRoute = defineRoute;
|
|
1103
1014
|
exports.defineRpc = defineRpc;
|
|
1104
1015
|
exports.errorResponseFromValidationErrors = errorResponseFromValidationErrors;
|
|
1105
|
-
exports.formatSse = formatSse;
|
|
1106
|
-
exports.formatSseList = formatSseList;
|
|
1107
1016
|
exports.getRpcParamName = getRpcParamName;
|
|
1108
1017
|
exports.getRpcPath = getRpcPath;
|
|
1109
1018
|
exports.getRpcResponseName = getRpcResponseName;
|
|
@@ -1117,5 +1026,4 @@ exports.isRpcParamSchema = isRpcParamSchema;
|
|
|
1117
1026
|
exports.registerEventStreamRpc = registerEventStreamRpc;
|
|
1118
1027
|
exports.registerRoute = registerRoute;
|
|
1119
1028
|
exports.registerRpc = registerRpc;
|
|
1120
|
-
exports.setSseHeaders = setSseHeaders;
|
|
1121
1029
|
exports.validateRpcRequestInput = validateRpcRequestInput;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "arri",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -26,28 +26,29 @@
|
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@joshmossas/listhen": "1.6.0",
|
|
29
|
-
"c12": "^1.
|
|
29
|
+
"c12": "^1.9.0",
|
|
30
30
|
"chokidar": "^3.6.0",
|
|
31
31
|
"citty": "^0.1.6",
|
|
32
32
|
"consola": "^3.2.3",
|
|
33
33
|
"degit": "^2.8.4",
|
|
34
|
-
"esbuild": "^0.20.
|
|
34
|
+
"esbuild": "^0.20.1",
|
|
35
35
|
"esbuild-plugin-replace": "^1.4.0",
|
|
36
36
|
"globby": "^14.0.1",
|
|
37
|
-
"h3": "^1.10.
|
|
37
|
+
"h3": "^1.10.2",
|
|
38
|
+
"h3-sse": "^0.0.9",
|
|
38
39
|
"jiti": "^1.21.0",
|
|
39
40
|
"ofetch": "^1.3.3",
|
|
40
41
|
"pathe": "^1.1.2",
|
|
41
42
|
"prettier": "^3.2.5",
|
|
42
43
|
"scule": "^1.3.0",
|
|
43
44
|
"uncrypto": "^0.1.3",
|
|
44
|
-
"arri-codegen-dart": "0.
|
|
45
|
-
"arri-
|
|
46
|
-
"arri-codegen-
|
|
47
|
-
"arri-
|
|
45
|
+
"arri-codegen-dart": "0.26.0",
|
|
46
|
+
"arri-validate": "0.26.0",
|
|
47
|
+
"arri-codegen-ts": "0.26.0",
|
|
48
|
+
"arri-codegen-utils": "0.26.0"
|
|
48
49
|
},
|
|
49
50
|
"devDependencies": {
|
|
50
51
|
"@types/degit": "^2.8.6",
|
|
51
|
-
"bun-types": "^1.0.
|
|
52
|
+
"bun-types": "^1.0.28"
|
|
52
53
|
}
|
|
53
54
|
}
|