@loutrejs/loutre 0.7.0 → 0.8.1
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/dist/adapters/bun.d.ts +2 -4
- package/dist/adapters/bun.js +36 -28
- package/dist/adapters/cloudflare-workers.d.ts +2 -4
- package/dist/adapters/cloudflare-workers.js +6 -11
- package/dist/adapters/server-transports.d.ts +4 -0
- package/dist/adapters/server-transports.js +18 -0
- package/dist/adapters/websocket-native.d.ts +24 -0
- package/dist/adapters/websocket-native.js +150 -0
- package/dist/core/extension.d.ts +13 -1
- package/dist/core/extension.js +19 -0
- package/dist/core/index.d.ts +1 -1
- package/dist/core/model/modules.js +13 -17
- package/dist/core/model.js +14 -4
- package/dist/core/module.d.ts +3 -3
- package/dist/devtools/capsule.js +13 -11
- package/dist/devtools/values.js +10 -13
- package/dist/generated/version.d.ts +1 -1
- package/dist/generated/version.js +1 -1
- package/dist/graph/model.js +37 -40
- package/dist/http/auth.d.ts +2 -2
- package/dist/http/auth.js +9 -0
- package/dist/http/client.js +5 -5
- package/dist/http/extension.d.ts +37 -10
- package/dist/http/extension.js +117 -39
- package/dist/http/index.d.ts +3 -2
- package/dist/http/index.js +1 -1
- package/dist/http/openapi.d.ts +3 -4
- package/dist/http/openapi.js +45 -26
- package/dist/http/request-head.d.ts +6 -0
- package/dist/http/request-head.js +1 -0
- package/dist/http/route-validation.d.ts +9 -0
- package/dist/http/route-validation.js +18 -0
- package/dist/http/runtime/response.js +10 -10
- package/dist/runtime/di.js +40 -39
- package/dist/runtime/engine.d.ts +1 -1
- package/dist/runtime/engine.js +1 -2
- package/dist/runtime/node-capabilities.d.ts +1 -1
- package/dist/runtime/node-capabilities.js +2 -1
- package/dist/tasks/extension.js +103 -114
- package/dist/websocket/driver.d.ts +30 -0
- package/dist/websocket/driver.js +96 -0
- package/dist/websocket/extension.d.ts +33 -12
- package/dist/websocket/extension.js +60 -44
- package/dist/websocket/index.d.ts +3 -1
- package/dist/websocket/index.js +1 -0
- package/package.json +3 -6
- package/dist/adapters/deno.d.ts +0 -35
- package/dist/adapters/deno.js +0 -231
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// package manifestとのずれを避けるため、scripts/generate-version.mjsがbuild前に更新する。
|
|
2
|
-
export const LOUTRE_VERSION = '0.
|
|
2
|
+
export const LOUTRE_VERSION = '0.8.1';
|
package/dist/graph/model.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { diagnostic, tokenName, } from '../core/index.js';
|
|
2
|
+
import { match } from 'ts-pattern';
|
|
2
3
|
export function projectApplicationModel(model) {
|
|
3
4
|
const diagnostics = [...model.diagnostics];
|
|
4
5
|
const projectedExecutions = new Map();
|
|
@@ -21,46 +22,42 @@ export function projectApplicationModel(model) {
|
|
|
21
22
|
});
|
|
22
23
|
}
|
|
23
24
|
function projectCoreNode(node) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
...(node.source === undefined ? {} : { source: node.source }),
|
|
61
|
-
attributes: { frameworkKind: node.frameworkKind },
|
|
62
|
-
};
|
|
63
|
-
}
|
|
25
|
+
return match(node)
|
|
26
|
+
.with({ kind: 'module' }, (candidate) => ({
|
|
27
|
+
id: candidate.id,
|
|
28
|
+
kind: candidate.kind,
|
|
29
|
+
...(candidate.source === undefined ? {} : { source: candidate.source }),
|
|
30
|
+
...(candidate.name === undefined ? {} : { name: candidate.name }),
|
|
31
|
+
...(candidate.description === undefined
|
|
32
|
+
? {}
|
|
33
|
+
: { attributes: { description: candidate.description } }),
|
|
34
|
+
}))
|
|
35
|
+
.with({ kind: 'provider' }, (candidate) => ({
|
|
36
|
+
id: candidate.id,
|
|
37
|
+
kind: candidate.kind,
|
|
38
|
+
name: tokenName(candidate.token),
|
|
39
|
+
module: candidate.moduleId,
|
|
40
|
+
...(candidate.source === undefined ? {} : { source: candidate.source }),
|
|
41
|
+
attributes: {
|
|
42
|
+
providerKind: candidate.provider.kind,
|
|
43
|
+
scope: candidate.provider.scope,
|
|
44
|
+
},
|
|
45
|
+
}))
|
|
46
|
+
.with({ kind: 'lifecycle' }, (candidate) => ({
|
|
47
|
+
id: candidate.id,
|
|
48
|
+
kind: candidate.kind,
|
|
49
|
+
name: candidate.phase,
|
|
50
|
+
module: candidate.moduleId,
|
|
51
|
+
...(candidate.source === undefined ? {} : { source: candidate.source }),
|
|
52
|
+
}))
|
|
53
|
+
.with({ kind: 'framework' }, (candidate) => ({
|
|
54
|
+
id: candidate.id,
|
|
55
|
+
kind: candidate.kind,
|
|
56
|
+
name: candidate.name,
|
|
57
|
+
...(candidate.source === undefined ? {} : { source: candidate.source }),
|
|
58
|
+
attributes: { frameworkKind: candidate.frameworkKind },
|
|
59
|
+
}))
|
|
60
|
+
.exhaustive();
|
|
64
61
|
}
|
|
65
62
|
function projectExtensionGroup(group, diagnostics) {
|
|
66
63
|
return group.executions.map((execution) => {
|
package/dist/http/auth.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export interface BasicAuthDefinition<TContribution extends object, TResponse ext
|
|
|
30
30
|
}
|
|
31
31
|
export interface BasicAuthRuntime<TContribution extends object, TResponse extends string, TUnauthorizedBody> {
|
|
32
32
|
authenticate(credentials: BasicAuthCredentials): TContribution | null | undefined | Promise<TContribution | null | undefined>;
|
|
33
|
-
unauthorized(): HttpAuthenticationFailure<TResponse, TUnauthorizedBody
|
|
33
|
+
unauthorized(): HttpAuthenticationFailure<TResponse, TUnauthorizedBody> | Response;
|
|
34
34
|
}
|
|
35
35
|
export declare function basicAuth<const TContribution extends object, const TResponse extends string, TUnauthorizedBody>(definition: BasicAuthDefinition<TContribution, TResponse, TUnauthorizedBody>): HttpMiddleware<TContribution, HttpMiddlewareContext, AuthenticationShortCircuit<TResponse, TUnauthorizedBody>>;
|
|
36
36
|
export interface BearerAuthDefinition<TContribution extends object, TResponse extends string, TUnauthorizedBody> {
|
|
@@ -40,7 +40,7 @@ export interface BearerAuthDefinition<TContribution extends object, TResponse ex
|
|
|
40
40
|
}
|
|
41
41
|
export interface BearerAuthRuntime<TContribution extends object, TResponse extends string, TUnauthorizedBody> {
|
|
42
42
|
authenticate(token: string): TContribution | null | undefined | Promise<TContribution | null | undefined>;
|
|
43
|
-
unauthorized(): HttpAuthenticationFailure<TResponse, TUnauthorizedBody
|
|
43
|
+
unauthorized(): HttpAuthenticationFailure<TResponse, TUnauthorizedBody> | Response;
|
|
44
44
|
}
|
|
45
45
|
export declare function bearerAuth<const TContribution extends object, const TResponse extends string, TUnauthorizedBody>(definition: BearerAuthDefinition<TContribution, TResponse, TUnauthorizedBody>): HttpMiddleware<TContribution, HttpMiddlewareContext, AuthenticationShortCircuit<TResponse, TUnauthorizedBody>>;
|
|
46
46
|
export {};
|
package/dist/http/auth.js
CHANGED
|
@@ -40,6 +40,15 @@ export function bearerAuth(definition) {
|
|
|
40
40
|
});
|
|
41
41
|
}
|
|
42
42
|
function authenticationFailure(failure, challenge) {
|
|
43
|
+
if (failure instanceof Response) {
|
|
44
|
+
const headers = new Headers(failure.headers);
|
|
45
|
+
headers.set('www-authenticate', challenge);
|
|
46
|
+
return new Response(failure.body, {
|
|
47
|
+
status: failure.status,
|
|
48
|
+
statusText: failure.statusText,
|
|
49
|
+
headers,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
43
52
|
return {
|
|
44
53
|
kind: 'http-result',
|
|
45
54
|
response: failure.response,
|
package/dist/http/client.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SchemaValidationError, validateSchema, } from '../core/index.js';
|
|
2
2
|
import { parseHttpPath } from './path.js';
|
|
3
|
+
import { match } from 'ts-pattern';
|
|
3
4
|
export class HttpClientResponseError extends Error {
|
|
4
5
|
status;
|
|
5
6
|
method;
|
|
@@ -84,11 +85,10 @@ async function decodeResponse(procedure, definition, response) {
|
|
|
84
85
|
throw new HttpClientResponseError(response.status, definition.method, definition.path, procedure, `${target} returned a response that does not match its Contract`, lastValidationError ? { cause: lastValidationError } : undefined);
|
|
85
86
|
}
|
|
86
87
|
function responseHeadersSchema(headers) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return undefined;
|
|
88
|
+
return match(headers)
|
|
89
|
+
.when(isStandardSchema, (schema) => schema)
|
|
90
|
+
.when(isResponseHeadersWithDefaults, (definition) => definition.schema)
|
|
91
|
+
.otherwise(() => undefined);
|
|
92
92
|
}
|
|
93
93
|
function isStandardSchema(value) {
|
|
94
94
|
return typeof value === 'object' && value !== null && '~standard' in value;
|
package/dist/http/extension.d.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
|
-
import { type ExecutionDefinition, type ExecutionExtension, type ExecutionExtensionDrainContext, type GenericLayer, type RuntimeCapabilityBinding, type SchemaInput, type SchemaOutput, type StandardSchemaV1, type SourceLocation, type Type } from '../core/index.js';
|
|
1
|
+
import { type ExecutionDefinition, type ExecutionContextView, type ExecutionExtension, type ExecutionExtensionDrainContext, type GenericLayer, type RuntimeCapabilityBinding, type SchemaInput, type SchemaOutput, type StandardSchemaV1, type SourceLocation, type Type } from '../core/index.js';
|
|
2
2
|
import { type HttpPathSegment, type PathParamNames } from './path.js';
|
|
3
|
+
import type { HttpRequestHeadDefinition } from './request-head.js';
|
|
3
4
|
export interface HttpServerDriver {
|
|
4
5
|
readonly runtime: string;
|
|
5
6
|
}
|
|
6
7
|
export declare const HTTP_SERVER: import("../core/extension.js").RuntimeCapability<HttpServerDriver>;
|
|
7
|
-
export interface HttpExecutionRequestDefinition {
|
|
8
|
-
readonly params?: Readonly<Record<string, StandardSchemaV1>>;
|
|
9
|
-
readonly query?: StandardSchemaV1;
|
|
10
|
-
readonly headers?: StandardSchemaV1;
|
|
8
|
+
export interface HttpExecutionRequestDefinition extends HttpRequestHeadDefinition {
|
|
11
9
|
readonly body?: StandardSchemaV1;
|
|
12
10
|
}
|
|
13
11
|
export type HttpHeaderValue = string | readonly string[];
|
|
@@ -56,7 +54,8 @@ export interface HttpExecutionRouteDefinition {
|
|
|
56
54
|
readonly middlewares?: readonly HttpRouteMiddleware[];
|
|
57
55
|
readonly interaction?: 'unary' | 'server-stream';
|
|
58
56
|
}
|
|
59
|
-
type
|
|
57
|
+
type HttpMiddlewareOutcome = HttpExecutionResult | Response;
|
|
58
|
+
type AnyHttpMiddleware = GenericLayer<any, any, HttpMiddlewareOutcome>;
|
|
60
59
|
type HttpRouteMiddleware = AnyHttpMiddleware | HttpValidationMiddleware;
|
|
61
60
|
export interface HttpContractBranchDefinition {
|
|
62
61
|
readonly path?: string;
|
|
@@ -67,7 +66,7 @@ export interface HttpContractBranchDefinition {
|
|
|
67
66
|
export type HttpContractNodeDefinition = HttpExecutionRouteDefinition | HttpContractBranchDefinition;
|
|
68
67
|
export type HttpContractRouteTree = Readonly<Record<string, HttpContractNodeDefinition>>;
|
|
69
68
|
declare const httpMiddlewareShortCircuit: unique symbol;
|
|
70
|
-
export type HttpMiddleware<TContribution extends object = object, TContext extends object = HttpMiddlewareContext, TShortCircuit extends HttpExecutionResult = never> = GenericLayer<TContext, TContribution,
|
|
69
|
+
export type HttpMiddleware<TContribution extends object = object, TContext extends object = HttpMiddlewareContext, TShortCircuit extends HttpExecutionResult = never> = GenericLayer<TContext, TContribution, HttpMiddlewareOutcome> & {
|
|
71
70
|
readonly [httpMiddlewareShortCircuit]?: TShortCircuit;
|
|
72
71
|
};
|
|
73
72
|
export interface HttpMiddlewareContext {
|
|
@@ -123,10 +122,13 @@ type IsHttpContractTreeInheritanceValid<TTree extends HttpContractRouteTree, TRe
|
|
|
123
122
|
type HttpMiddlewareState<TRoute extends HttpExecutionRouteDefinition> = TRoute extends {
|
|
124
123
|
readonly middlewares: infer TMiddlewares extends readonly HttpRouteMiddleware[];
|
|
125
124
|
} ? UnionToIntersection<MiddlewareContribution<TMiddlewares[number]>> : {};
|
|
125
|
+
declare const httpContractBrand: unique symbol;
|
|
126
126
|
export interface HttpContract<TRoutes extends Readonly<Record<string, HttpExecutionRouteDefinition>> = Readonly<Record<string, HttpExecutionRouteDefinition>>> {
|
|
127
127
|
readonly kind: 'http-contract';
|
|
128
128
|
readonly routes: TRoutes;
|
|
129
|
+
readonly [httpContractBrand]: true;
|
|
129
130
|
}
|
|
131
|
+
export declare function isHttpContract(value: unknown): value is HttpContract;
|
|
130
132
|
type RequestValue<TRequest extends HttpExecutionRequestDefinition | undefined, TPart extends keyof HttpExecutionRequestDefinition, TFallback> = TRequest extends HttpExecutionRequestDefinition ? TRequest[TPart] extends StandardSchemaV1 ? SchemaOutput<TRequest[TPart]> : TFallback : TFallback;
|
|
131
133
|
type RawHttpQuery = Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
132
134
|
type RawHttpRequestHeaders = Readonly<Record<string, string | undefined>>;
|
|
@@ -198,6 +200,28 @@ export interface HttpImplementationDefinition<TContract extends HttpContract = H
|
|
|
198
200
|
readonly contract: TContract;
|
|
199
201
|
readonly factory: () => HttpHandlers<TContract>;
|
|
200
202
|
}
|
|
203
|
+
export interface HttpRawRouteDefinition {
|
|
204
|
+
readonly method: string;
|
|
205
|
+
readonly path: string;
|
|
206
|
+
readonly middlewares?: readonly AnyHttpMiddleware[];
|
|
207
|
+
readonly protocol?: string;
|
|
208
|
+
}
|
|
209
|
+
export interface HttpRawExecutionContext<TState = Readonly<Record<string, unknown>>> {
|
|
210
|
+
readonly request: Request;
|
|
211
|
+
readonly signal: AbortSignal;
|
|
212
|
+
readonly state: Readonly<TState>;
|
|
213
|
+
readonly execution: ExecutionContextView;
|
|
214
|
+
}
|
|
215
|
+
type RawMiddlewareState<TMiddlewares extends readonly AnyHttpMiddleware[]> = UnionToIntersection<MiddlewareContribution<TMiddlewares[number]>> & object;
|
|
216
|
+
export interface HttpRawDefinition<TMiddlewares extends readonly AnyHttpMiddleware[] = readonly AnyHttpMiddleware[]> {
|
|
217
|
+
readonly name?: string;
|
|
218
|
+
readonly route: HttpRawRouteDefinition & {
|
|
219
|
+
readonly middlewares?: TMiddlewares;
|
|
220
|
+
};
|
|
221
|
+
readonly factory: () => (context: HttpRawExecutionContext<RawMiddlewareState<TMiddlewares>>) => Response | Promise<Response>;
|
|
222
|
+
}
|
|
223
|
+
export type HttpRawExecutionDefinition = HttpRawDefinition & ExecutionDefinition<typeof httpExecutionExtension>;
|
|
224
|
+
export declare function defineHttpRaw<const TMiddlewares extends readonly AnyHttpMiddleware[] = readonly []>(definition: HttpRawDefinition<TMiddlewares>): HttpRawDefinition<TMiddlewares> & ExecutionDefinition<typeof httpExecutionExtension>;
|
|
201
225
|
interface CompiledHttpRoute {
|
|
202
226
|
readonly name: string;
|
|
203
227
|
readonly method: string;
|
|
@@ -207,10 +231,12 @@ interface CompiledHttpRoute {
|
|
|
207
231
|
readonly definition: HttpExecutionRouteDefinition;
|
|
208
232
|
readonly middlewares: readonly AnyHttpMiddleware[];
|
|
209
233
|
readonly source?: SourceLocation;
|
|
234
|
+
readonly raw?: boolean;
|
|
235
|
+
readonly protocol?: string;
|
|
210
236
|
}
|
|
211
237
|
interface CompiledHttpExecution {
|
|
212
238
|
readonly routes: readonly CompiledHttpRoute[];
|
|
213
|
-
readonly factory: () => Record<string, (context: HttpExecutionContext) =>
|
|
239
|
+
readonly factory: () => Record<string, (context: HttpExecutionContext) => HttpMiddlewareOutcome | Promise<HttpMiddlewareOutcome>>;
|
|
214
240
|
}
|
|
215
241
|
export interface HttpExtensionRuntime {
|
|
216
242
|
fetch(request: Request): Promise<Response>;
|
|
@@ -220,7 +246,7 @@ export interface HttpHostApi {
|
|
|
220
246
|
fetch(request: Request): Promise<Response>;
|
|
221
247
|
}
|
|
222
248
|
declare const httpExecutionExtensionIdentity: unique symbol;
|
|
223
|
-
export declare const httpExecutionExtension: ExecutionExtension<HttpImplementationDefinition & ExecutionDefinition, CompiledHttpExecution, 'http', HttpHostApi, HttpExtensionRuntime> & {
|
|
249
|
+
export declare const httpExecutionExtension: ExecutionExtension<(HttpImplementationDefinition | HttpRawDefinition) & ExecutionDefinition, CompiledHttpExecution, 'http', HttpHostApi, HttpExtensionRuntime> & {
|
|
224
250
|
readonly [httpExecutionExtensionIdentity]: true;
|
|
225
251
|
};
|
|
226
252
|
export type HttpExecutionDefinition<TContract extends HttpContract = HttpContract> = HttpImplementationDefinition<TContract> & ExecutionDefinition<typeof httpExecutionExtension>;
|
|
@@ -328,6 +354,7 @@ export declare const validate: Readonly<{
|
|
|
328
354
|
}>;
|
|
329
355
|
}>;
|
|
330
356
|
export declare const executionHttp: Readonly<{
|
|
357
|
+
raw: typeof defineHttpRaw;
|
|
331
358
|
contract: typeof defineHttpContract;
|
|
332
359
|
implementation: typeof defineHttpImplementation;
|
|
333
360
|
middleware: typeof defineHttpMiddleware;
|
|
@@ -338,7 +365,7 @@ export declare const executionHttp: Readonly<{
|
|
|
338
365
|
}>;
|
|
339
366
|
}>;
|
|
340
367
|
error: typeof httpError;
|
|
341
|
-
extension: ExecutionExtension<HttpImplementationDefinition<HttpContract<Readonly<Record<string, HttpExecutionRouteDefinition>>>> & ExecutionDefinition<import("../core/extension.js").AnyExecutionExtension>, CompiledHttpExecution, "http", HttpHostApi, HttpExtensionRuntime> & {
|
|
368
|
+
extension: ExecutionExtension<(HttpImplementationDefinition<HttpContract<Readonly<Record<string, HttpExecutionRouteDefinition>>>> | HttpRawDefinition<readonly AnyHttpMiddleware[]>) & ExecutionDefinition<import("../core/extension.js").AnyExecutionExtension>, CompiledHttpExecution, "http", HttpHostApi, HttpExtensionRuntime> & {
|
|
342
369
|
readonly [httpExecutionExtensionIdentity]: true;
|
|
343
370
|
};
|
|
344
371
|
serverCapability: import("../core/extension.js").RuntimeCapability<HttpServerDriver>;
|
package/dist/http/extension.js
CHANGED
|
@@ -1,18 +1,35 @@
|
|
|
1
1
|
import { collectInjectedDependencies, getSourceLocation, inheritSourceLocation, composeLayers, defineExecution, defineExecutionExtension, defineLayer, runInInjectionContext, runtimeCapability, SchemaValidationError, validateSchema, } from '../core/index.js';
|
|
2
2
|
import { createCorsActualResponseHeaders, createCorsPreflightResponseHeaders, } from './cors-internal.js';
|
|
3
3
|
import { assertValidHttpMethod, compareHttpPathSpecificity, createHttpDispatchKey, HttpPathDecodeError, matchHttpPath, parseHttpPath, } from './path.js';
|
|
4
|
+
import { findHttpRouteConflicts } from './route-validation.js';
|
|
4
5
|
import { IngressGate } from '../runtime/ingress-gate.js';
|
|
5
6
|
import { AsyncIteratorCleanupDeadlineError, AsyncIteratorLifecycle, } from '../runtime/async-iterator-lifecycle.js';
|
|
6
7
|
import { decodeBody, decodeQuery, HttpInputDecodeError, HttpInputValidationError, HttpUnsupportedMediaTypeError, validatedContentType, validateRequestHeaders, } from './runtime/request.js';
|
|
7
8
|
import { applyFrameworkHeadersToResponse, applyFrameworkResponseHeaders, applyResponseHeaders, isResponseHeadersWithDefaults, isStandardSchema, mergeResponseHeaders, responseHeadersDefaults, responseHeadersSchema, validateResponseHeaders, } from './runtime/response.js';
|
|
9
|
+
import { match as patternMatch } from 'ts-pattern';
|
|
8
10
|
export const HTTP_SERVER = runtimeCapability('http.server');
|
|
11
|
+
const httpContractBrand = Symbol.for('loutre.http-contract');
|
|
12
|
+
export function isHttpContract(value) {
|
|
13
|
+
if (typeof value !== 'object' || value === null)
|
|
14
|
+
return false;
|
|
15
|
+
const candidate = value;
|
|
16
|
+
return (candidate.kind === 'http-contract' &&
|
|
17
|
+
candidate[httpContractBrand] === true &&
|
|
18
|
+
typeof candidate.routes === 'object' &&
|
|
19
|
+
candidate.routes !== null);
|
|
20
|
+
}
|
|
21
|
+
export function defineHttpRaw(definition) {
|
|
22
|
+
return defineExecution(httpExecutionExtension, { ...definition, raw: true });
|
|
23
|
+
}
|
|
9
24
|
export const httpExecutionExtension = defineExecutionExtension({
|
|
10
25
|
kind: 'execution-extension',
|
|
11
26
|
abiVersion: '1',
|
|
12
27
|
name: 'loutre:http',
|
|
13
28
|
compile(definition, context) {
|
|
14
|
-
const
|
|
15
|
-
const routes =
|
|
29
|
+
const raw = 'route' in definition;
|
|
30
|
+
const routes = raw
|
|
31
|
+
? [compileRawRoute(definition.route)]
|
|
32
|
+
: Object.entries(definition.contract.routes).map(([name, route]) => compileHttpRoute(name, route, getSourceLocation(definition.contract)));
|
|
16
33
|
const id = definition.name || `${context.moduleId}.http.${context.definitionIndex}`;
|
|
17
34
|
const dependencies = new Set(collectInjectedDependencies({
|
|
18
35
|
kind: 'execution',
|
|
@@ -43,25 +60,22 @@ export const httpExecutionExtension = defineExecutionExtension({
|
|
|
43
60
|
],
|
|
44
61
|
compiled: Object.freeze({
|
|
45
62
|
routes: Object.freeze(routes),
|
|
46
|
-
factory:
|
|
63
|
+
factory: (raw
|
|
64
|
+
? () => ({ raw: definition.factory() })
|
|
65
|
+
: definition.factory),
|
|
47
66
|
}),
|
|
48
67
|
};
|
|
49
68
|
},
|
|
50
69
|
validate({ executions }) {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
},
|
|
61
|
-
];
|
|
62
|
-
}
|
|
63
|
-
dispatches.set(route.dispatch, execution.id);
|
|
64
|
-
return [];
|
|
70
|
+
const routes = executions.flatMap((execution) => execution.compiled.routes.map((route) => ({
|
|
71
|
+
method: route.method,
|
|
72
|
+
path: route.path,
|
|
73
|
+
owner: execution.id,
|
|
74
|
+
})));
|
|
75
|
+
return findHttpRouteConflicts(routes).map(({ existing, route }) => ({
|
|
76
|
+
code: 'LUTRE_HTTP_DUPLICATE_ROUTE',
|
|
77
|
+
message: `${route.method} ${route.path} conflicts with ${existing.owner}.`,
|
|
78
|
+
path: route.owner,
|
|
65
79
|
}));
|
|
66
80
|
},
|
|
67
81
|
createRuntime(context) {
|
|
@@ -81,10 +95,16 @@ export const httpExecutionExtension = defineExecutionExtension({
|
|
|
81
95
|
? {}
|
|
82
96
|
: { source: getSourceLocation(middleware) }),
|
|
83
97
|
})),
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
98
|
+
...(route.raw
|
|
99
|
+
? {
|
|
100
|
+
raw: true,
|
|
101
|
+
...(route.protocol === undefined
|
|
102
|
+
? {}
|
|
103
|
+
: { protocol: route.protocol }),
|
|
104
|
+
}
|
|
105
|
+
: {
|
|
106
|
+
responses: Object.fromEntries(Object.entries(route.definition.responses).map(([name, response]) => [name, { status: response.status }])),
|
|
107
|
+
}),
|
|
88
108
|
})),
|
|
89
109
|
}),
|
|
90
110
|
host: {
|
|
@@ -101,12 +121,14 @@ export function httpError(definition, map) {
|
|
|
101
121
|
}
|
|
102
122
|
export function defineHttpContract(routes) {
|
|
103
123
|
const resolvedRoutes = resolveHttpContractRoutes(routes);
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
124
|
+
const canonicalRoutes = Object.fromEntries(Object.entries(resolvedRoutes).map(([name, route]) => [
|
|
125
|
+
name,
|
|
126
|
+
compileHttpRoute(name, route).definition,
|
|
127
|
+
]));
|
|
107
128
|
return Object.freeze({
|
|
108
129
|
kind: 'http-contract',
|
|
109
|
-
|
|
130
|
+
[httpContractBrand]: true,
|
|
131
|
+
routes: Object.freeze(canonicalRoutes),
|
|
110
132
|
});
|
|
111
133
|
}
|
|
112
134
|
function resolveHttpContractRoutes(tree) {
|
|
@@ -186,6 +208,7 @@ export const validate = Object.freeze({
|
|
|
186
208
|
body: bodyValidation,
|
|
187
209
|
});
|
|
188
210
|
export const executionHttp = Object.freeze({
|
|
211
|
+
raw: defineHttpRaw,
|
|
189
212
|
contract: defineHttpContract,
|
|
190
213
|
implementation: defineHttpImplementation,
|
|
191
214
|
middleware: defineHttpMiddleware,
|
|
@@ -203,7 +226,29 @@ export function withHttpFrameworkHeaders(result, headers) {
|
|
|
203
226
|
[httpFrameworkHeaders]: { ...current, ...headers },
|
|
204
227
|
};
|
|
205
228
|
}
|
|
229
|
+
function compileRawRoute(route) {
|
|
230
|
+
if (route.method !== '*')
|
|
231
|
+
assertValidHttpMethod(route.method);
|
|
232
|
+
const segments = parseHttpPath(route.path);
|
|
233
|
+
return Object.freeze({
|
|
234
|
+
name: 'raw',
|
|
235
|
+
method: route.method.toUpperCase(),
|
|
236
|
+
path: route.path,
|
|
237
|
+
segments,
|
|
238
|
+
dispatch: createHttpDispatchKey(route.method, segments),
|
|
239
|
+
definition: Object.freeze({
|
|
240
|
+
method: route.method,
|
|
241
|
+
path: route.path,
|
|
242
|
+
responses: Object.freeze({}),
|
|
243
|
+
}),
|
|
244
|
+
middlewares: Object.freeze([...(route.middlewares ?? [])]),
|
|
245
|
+
raw: true,
|
|
246
|
+
...(route.protocol === undefined ? {} : { protocol: route.protocol }),
|
|
247
|
+
});
|
|
248
|
+
}
|
|
206
249
|
function compileHttpRoute(name, route, routeSourceFallback) {
|
|
250
|
+
if (route.method === '*')
|
|
251
|
+
throw new TypeError('method: *はhttp.raw()でのみ利用できます。');
|
|
207
252
|
assertValidHttpMethod(route.method);
|
|
208
253
|
const segments = parseHttpPath(route.path);
|
|
209
254
|
assertValidHttpRouteDefinition(route, segments);
|
|
@@ -281,15 +326,15 @@ function compileHttpMiddlewares(route) {
|
|
|
281
326
|
return Object.freeze(compiled);
|
|
282
327
|
}
|
|
283
328
|
function compileHttpValidationMiddleware(middleware, route) {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
290
|
-
return createBodyValidationMiddleware(schema);
|
|
329
|
+
return patternMatch(middleware)
|
|
330
|
+
.with({ part: 'body' }, () => {
|
|
331
|
+
const schema = route.request?.body;
|
|
332
|
+
if (!schema) {
|
|
333
|
+
throw new TypeError(`HTTP ${route.method} ${route.path} uses validate.body but declares no request body.`);
|
|
291
334
|
}
|
|
292
|
-
|
|
335
|
+
return createBodyValidationMiddleware(schema);
|
|
336
|
+
})
|
|
337
|
+
.exhaustive();
|
|
293
338
|
}
|
|
294
339
|
function createBodyValidationMiddleware(schema) {
|
|
295
340
|
return defineLayer({
|
|
@@ -562,14 +607,33 @@ function createHttpExtensionRuntime(executions, applicationRuntime) {
|
|
|
562
607
|
kind: 'http.handler',
|
|
563
608
|
name: handlerName,
|
|
564
609
|
graphNodeId: match.executionId,
|
|
565
|
-
},
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
610
|
+
}, match.route.raw
|
|
611
|
+
? undefined
|
|
612
|
+
: {
|
|
613
|
+
input: middlewareContext.input,
|
|
614
|
+
invoke: (input) => replayHttpHandler(applicationRuntime, handler, match, input, middlewareContext.state),
|
|
615
|
+
});
|
|
569
616
|
const invoke = () => handler({
|
|
617
|
+
...(match.route.raw
|
|
618
|
+
? {
|
|
619
|
+
request,
|
|
620
|
+
execution: {
|
|
621
|
+
signal: lease.signal,
|
|
622
|
+
annotate: (attributes) => lease.annotate?.(attributes),
|
|
623
|
+
beginOperation: (metadata) => {
|
|
624
|
+
const begin = () => applicationRuntime.beginOperation?.(metadata) ?? {
|
|
625
|
+
complete() { },
|
|
626
|
+
};
|
|
627
|
+
return lease.run ? lease.run(begin) : begin();
|
|
628
|
+
},
|
|
629
|
+
},
|
|
630
|
+
}
|
|
631
|
+
: {}),
|
|
570
632
|
input: middlewareContext.input,
|
|
571
633
|
response: middlewareContext.response,
|
|
572
|
-
signal:
|
|
634
|
+
signal: match.route.raw
|
|
635
|
+
? request.signal
|
|
636
|
+
: middlewareContext.signal,
|
|
573
637
|
state: middlewareContext.state,
|
|
574
638
|
});
|
|
575
639
|
let result;
|
|
@@ -596,6 +660,20 @@ function createHttpExtensionRuntime(executions, applicationRuntime) {
|
|
|
596
660
|
const result = await (lease.run ? lease.run(dispatch) : dispatch());
|
|
597
661
|
executionResult = result;
|
|
598
662
|
hasExecutionResult = true;
|
|
663
|
+
if (result instanceof Response) {
|
|
664
|
+
if (result.body) {
|
|
665
|
+
const stream = createLeasedReadableStream(result.body, lease.signal, finishExecution);
|
|
666
|
+
adoptResponseStream(stream);
|
|
667
|
+
return complete(new Response(stream.stream, {
|
|
668
|
+
status: result.status,
|
|
669
|
+
statusText: result.statusText,
|
|
670
|
+
headers: result.headers,
|
|
671
|
+
}));
|
|
672
|
+
}
|
|
673
|
+
return complete(result);
|
|
674
|
+
}
|
|
675
|
+
if (match.route.raw)
|
|
676
|
+
throw new TypeError('Raw handlerとmiddlewareはResponseを返す必要があります。');
|
|
599
677
|
const finalized = await finalizeHttpResult(match.route.definition, result, lease.signal, finishExecution);
|
|
600
678
|
if (finalized.streamControl)
|
|
601
679
|
adoptResponseStream(finalized.streamControl);
|
|
@@ -634,7 +712,7 @@ function createHttpExtensionRuntime(executions, applicationRuntime) {
|
|
|
634
712
|
}
|
|
635
713
|
function findRuntimeHttpRoute(routes, method, pathname) {
|
|
636
714
|
for (const candidate of routes) {
|
|
637
|
-
if (candidate.route.method !== method)
|
|
715
|
+
if (candidate.route.method !== method && candidate.route.method !== '*')
|
|
638
716
|
continue;
|
|
639
717
|
const params = matchHttpPath(candidate.route.segments, pathname);
|
|
640
718
|
if (!params)
|
package/dist/http/index.d.ts
CHANGED
|
@@ -4,7 +4,8 @@ export { cors } from './cors.js';
|
|
|
4
4
|
export type { CorsLayerDescriptor, CorsOptions, CorsOrigin } from './cors.js';
|
|
5
5
|
export { basicAuth, bearerAuth } from './auth.js';
|
|
6
6
|
export type { BasicAuthContext, BasicAuthCredentials, BasicAuthDefinition, BasicAuthUnauthorized, BasicAuthRuntime, BearerAuthContext, BearerAuthDefinition, BearerAuthRuntime, BearerAuthUnauthorized, HttpAuthenticationFailure, } from './auth.js';
|
|
7
|
-
export { bindHttpServer, defineHttpContract, defineHttpImplementation, defineHttpMiddleware, httpError, executionHttp as http, httpExecutionExtension, HTTP_SERVER, validate, } from './extension.js';
|
|
8
|
-
export type { HttpContract, HttpContractBranchDefinition, HttpContractNodeDefinition, HttpContractRouteTree, HttpExecutionContext, HttpExecutionDefinition, HttpExecutionRequestDefinition, HttpExecutionResponseDefinition, HttpExecutionResult, HttpExecutionRouteDefinition, HttpErrorMapping, HttpErrorMatcher, HttpExtensionRuntime, HttpHandlers, HttpHeaderValue, HttpHeaders, HttpHostApi, HttpImplementationDefinition, HttpMiddleware, HttpMiddlewareContext, HttpResponseHeadersDefinition, HttpResponseResult, HttpResponseHeadersWithDefaults, HttpServerDriver, HttpValidationMiddleware, HttpValidationPart, ResolvedHttpContractRoutes, } from './extension.js';
|
|
7
|
+
export { bindHttpServer, defineHttpContract, defineHttpImplementation, defineHttpMiddleware, defineHttpRaw, httpError, executionHttp as http, httpExecutionExtension, isHttpContract, HTTP_SERVER, validate, } from './extension.js';
|
|
8
|
+
export type { HttpRawDefinition, HttpRawRouteDefinition, HttpRawExecutionContext, HttpRawExecutionDefinition, HttpContract, HttpContractBranchDefinition, HttpContractNodeDefinition, HttpContractRouteTree, HttpExecutionContext, HttpExecutionDefinition, HttpExecutionRequestDefinition, HttpExecutionResponseDefinition, HttpExecutionResult, HttpExecutionRouteDefinition, HttpErrorMapping, HttpErrorMatcher, HttpExtensionRuntime, HttpHandlers, HttpHeaderValue, HttpHeaders, HttpHostApi, HttpImplementationDefinition, HttpMiddleware, HttpMiddlewareContext, HttpResponseHeadersDefinition, HttpResponseResult, HttpResponseHeadersWithDefaults, HttpServerDriver, HttpValidationMiddleware, HttpValidationPart, ResolvedHttpContractRoutes, } from './extension.js';
|
|
9
9
|
export { compareHttpPathSpecificity, HttpPathDecodeError, matchHttpPath, normalizeHttpPath, parseHttpPath, } from './path.js';
|
|
10
10
|
export type { HttpPathSegment, PathParamNames } from './path.js';
|
|
11
|
+
export type { HttpRequestHeadDefinition } from './request-head.js';
|
package/dist/http/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { createHttpClient, fetchHttpTransport, HttpClientResponseError, } from './client.js';
|
|
2
2
|
export { cors } from './cors.js';
|
|
3
3
|
export { basicAuth, bearerAuth } from './auth.js';
|
|
4
|
-
export { bindHttpServer, defineHttpContract, defineHttpImplementation, defineHttpMiddleware, httpError, executionHttp as http, httpExecutionExtension, HTTP_SERVER, validate, } from './extension.js';
|
|
4
|
+
export { bindHttpServer, defineHttpContract, defineHttpImplementation, defineHttpMiddleware, defineHttpRaw, httpError, executionHttp as http, httpExecutionExtension, isHttpContract, HTTP_SERVER, validate, } from './extension.js';
|
|
5
5
|
export { compareHttpPathSpecificity, HttpPathDecodeError, matchHttpPath, normalizeHttpPath, parseHttpPath, } from './path.js';
|
package/dist/http/openapi.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ApplicationModel } from '../core/index.js';
|
|
2
|
-
import { type HttpExecutionResponseDefinition, type HttpExecutionRouteDefinition } from './extension.js';
|
|
2
|
+
import { type HttpContract, type HttpExecutionResponseDefinition, type HttpExecutionRouteDefinition } from './extension.js';
|
|
3
3
|
export interface OpenApiInfo {
|
|
4
4
|
readonly title: string;
|
|
5
5
|
readonly version: string;
|
|
@@ -37,7 +37,6 @@ export interface HttpOperationTarget {
|
|
|
37
37
|
};
|
|
38
38
|
readonly procedure: string;
|
|
39
39
|
}
|
|
40
|
-
export
|
|
41
|
-
|
|
42
|
-
}, options: GenerateOpenApiOptions): OpenApiDocument;
|
|
40
|
+
export type OpenApiSource = ApplicationModel | HttpContract;
|
|
41
|
+
export declare function generateOpenApi(source: OpenApiSource, options: GenerateOpenApiOptions): OpenApiDocument;
|
|
43
42
|
export {};
|