@loutrejs/loutre 0.1.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.
Files changed (103) hide show
  1. package/dist/adapters/aws-lambda.d.ts +52 -0
  2. package/dist/adapters/aws-lambda.js +114 -0
  3. package/dist/adapters/bun.d.ts +20 -0
  4. package/dist/adapters/bun.js +82 -0
  5. package/dist/adapters/cloudflare-workers.d.ts +18 -0
  6. package/dist/adapters/cloudflare-workers.js +55 -0
  7. package/dist/adapters/deno.d.ts +28 -0
  8. package/dist/adapters/deno.js +106 -0
  9. package/dist/adapters/electron.d.ts +27 -0
  10. package/dist/adapters/electron.js +52 -0
  11. package/dist/application/binding.d.ts +33 -0
  12. package/dist/application/binding.js +292 -0
  13. package/dist/application/host.d.ts +8 -0
  14. package/dist/application/host.js +14 -0
  15. package/dist/application/index.d.ts +62 -0
  16. package/dist/application/index.js +11 -0
  17. package/dist/application/openapi.d.ts +28 -0
  18. package/dist/application/openapi.js +362 -0
  19. package/dist/application/queue.d.ts +3 -0
  20. package/dist/application/queue.js +4 -0
  21. package/dist/application/scheduler.d.ts +2 -0
  22. package/dist/application/scheduler.js +45 -0
  23. package/dist/core/args.d.ts +16 -0
  24. package/dist/core/args.js +29 -0
  25. package/dist/core/context-key.d.ts +14 -0
  26. package/dist/core/context-key.js +14 -0
  27. package/dist/core/contract.d.ts +54 -0
  28. package/dist/core/contract.js +30 -0
  29. package/dist/core/env.d.ts +16 -0
  30. package/dist/core/env.js +29 -0
  31. package/dist/core/error.d.ts +24 -0
  32. package/dist/core/error.js +25 -0
  33. package/dist/core/execution.d.ts +78 -0
  34. package/dist/core/execution.js +61 -0
  35. package/dist/core/implementation.d.ts +34 -0
  36. package/dist/core/implementation.js +30 -0
  37. package/dist/core/index.d.ts +15 -0
  38. package/dist/core/index.js +15 -0
  39. package/dist/core/injection.d.ts +28 -0
  40. package/dist/core/injection.js +35 -0
  41. package/dist/core/layer.d.ts +109 -0
  42. package/dist/core/layer.js +52 -0
  43. package/dist/core/lifecycle.d.ts +34 -0
  44. package/dist/core/lifecycle.js +3 -0
  45. package/dist/core/module.d.ts +53 -0
  46. package/dist/core/module.js +31 -0
  47. package/dist/core/provider.d.ts +57 -0
  48. package/dist/core/provider.js +63 -0
  49. package/dist/core/runtime-input.d.ts +11 -0
  50. package/dist/core/runtime-input.js +1 -0
  51. package/dist/core/schema.d.ts +13 -0
  52. package/dist/core/schema.js +20 -0
  53. package/dist/core/token.d.ts +16 -0
  54. package/dist/core/token.js +13 -0
  55. package/dist/graph/graph.d.ts +17 -0
  56. package/dist/graph/graph.js +1078 -0
  57. package/dist/graph/index.d.ts +2 -0
  58. package/dist/graph/index.js +2 -0
  59. package/dist/graph/ir.d.ts +159 -0
  60. package/dist/graph/ir.js +1 -0
  61. package/dist/http/application.d.ts +18 -0
  62. package/dist/http/application.js +506 -0
  63. package/dist/http/basic-auth.d.ts +37 -0
  64. package/dist/http/basic-auth.js +67 -0
  65. package/dist/http/cors.d.ts +17 -0
  66. package/dist/http/cors.js +171 -0
  67. package/dist/http/definitions.d.ts +217 -0
  68. package/dist/http/definitions.js +65 -0
  69. package/dist/http/hosted-protocol.d.ts +4 -0
  70. package/dist/http/hosted-protocol.js +4 -0
  71. package/dist/http/index.d.ts +7 -0
  72. package/dist/http/index.js +6 -0
  73. package/dist/http/params.d.ts +2 -0
  74. package/dist/http/params.js +19 -0
  75. package/dist/http/path.d.ts +55 -0
  76. package/dist/http/path.js +88 -0
  77. package/dist/http/request-body.d.ts +4 -0
  78. package/dist/http/request-body.js +6 -0
  79. package/dist/http/validate.d.ts +20 -0
  80. package/dist/http/validate.js +6 -0
  81. package/dist/index.d.ts +2 -0
  82. package/dist/index.js +2 -0
  83. package/dist/message-port/environment.d.ts +2 -0
  84. package/dist/message-port/environment.js +4 -0
  85. package/dist/message-port/index.d.ts +65 -0
  86. package/dist/message-port/index.js +182 -0
  87. package/dist/runtime/application.d.ts +37 -0
  88. package/dist/runtime/application.js +324 -0
  89. package/dist/runtime/capability.d.ts +16 -0
  90. package/dist/runtime/capability.js +18 -0
  91. package/dist/runtime/di.d.ts +37 -0
  92. package/dist/runtime/di.js +441 -0
  93. package/dist/runtime/error.d.ts +10 -0
  94. package/dist/runtime/error.js +15 -0
  95. package/dist/runtime/index.d.ts +7 -0
  96. package/dist/runtime/index.js +7 -0
  97. package/dist/runtime/logger.d.ts +36 -0
  98. package/dist/runtime/logger.js +207 -0
  99. package/dist/runtime/node-capabilities.d.ts +5 -0
  100. package/dist/runtime/node-capabilities.js +18 -0
  101. package/dist/runtime/pipeline.d.ts +11 -0
  102. package/dist/runtime/pipeline.js +161 -0
  103. package/package.json +81 -0
@@ -0,0 +1,52 @@
1
+ import { type ApplicationDefinition, type BootstrapArguments, type HasHttp } from '../application/index.js';
2
+ type IsAny<TValue> = 0 extends 1 & TValue ? true : false;
3
+ type HttpApplication<TDefinition extends ApplicationDefinition> = IsAny<TDefinition> extends true ? TDefinition : HasHttp<TDefinition> extends true ? TDefinition : never;
4
+ export type AwsLambdaBindBaseOptions<TDefinition extends ApplicationDefinition> = {
5
+ readonly application: HttpApplication<TDefinition>;
6
+ readonly environment?: unknown;
7
+ } & BootstrapArguments<TDefinition>;
8
+ export type AwsLambdaBindOptions<TDefinition extends ApplicationDefinition> = AwsLambdaBindBaseOptions<TDefinition> & {
9
+ readonly response?: 'buffered';
10
+ };
11
+ export type AwsLambdaStreamingBindOptions<TDefinition extends ApplicationDefinition> = AwsLambdaBindBaseOptions<TDefinition> & {
12
+ readonly response: 'streaming';
13
+ };
14
+ export interface AwsLambdaHttpEvent {
15
+ readonly rawPath?: string;
16
+ readonly rawQueryString?: string;
17
+ readonly requestContext?: {
18
+ readonly http?: {
19
+ readonly method?: string;
20
+ };
21
+ };
22
+ readonly headers?: Readonly<Record<string, string | undefined>>;
23
+ readonly body?: string | null;
24
+ readonly isBase64Encoded?: boolean;
25
+ }
26
+ export interface AwsLambdaHttpResult {
27
+ readonly statusCode: number;
28
+ readonly headers: Readonly<Record<string, string>>;
29
+ readonly cookies?: readonly string[];
30
+ readonly body: string;
31
+ readonly isBase64Encoded: boolean;
32
+ }
33
+ export type AwsLambdaHttpHandler = (event: AwsLambdaHttpEvent) => Promise<AwsLambdaHttpResult>;
34
+ export interface AwsLambdaResponseStream {
35
+ write(chunk: Uint8Array): boolean;
36
+ end(): void;
37
+ once?(event: 'drain', listener: () => void): unknown;
38
+ setMetadata?(metadata: {
39
+ readonly statusCode: number;
40
+ readonly headers: Readonly<Record<string, string>>;
41
+ readonly cookies?: readonly string[];
42
+ }): void;
43
+ }
44
+ export type AwsLambdaStreamingHttpHandler = (event: AwsLambdaHttpEvent, output: AwsLambdaResponseStream, context?: unknown) => Promise<void>;
45
+ export declare const awsLambdaRuntime: {
46
+ readonly runtime: 'aws-lambda';
47
+ readonly capabilities: Set<string>;
48
+ readonly bind: typeof bind;
49
+ };
50
+ declare function bind<const TDefinition extends ApplicationDefinition>(options: AwsLambdaStreamingBindOptions<TDefinition>): AwsLambdaStreamingHttpHandler;
51
+ declare function bind<const TDefinition extends ApplicationDefinition>(options: AwsLambdaBindOptions<TDefinition>): AwsLambdaHttpHandler;
52
+ export {};
@@ -0,0 +1,114 @@
1
+ import { binding, } from '../application/index.js';
2
+ export const awsLambdaRuntime = {
3
+ runtime: 'aws-lambda',
4
+ capabilities: new Set([
5
+ 'http.server',
6
+ 'http.response.streaming',
7
+ 'stream.readable',
8
+ 'stream.writable',
9
+ 'env.runtime',
10
+ 'crypto.random',
11
+ ]),
12
+ bind,
13
+ };
14
+ function bind(options) {
15
+ const invocation = binding.invocation({
16
+ application: options.application,
17
+ environment: 'environment' in options ? options.environment : process.env,
18
+ ...('arguments' in options ? { arguments: options.arguments } : {}),
19
+ });
20
+ const http = 'http' in invocation
21
+ ? invocation.http
22
+ : undefined;
23
+ if (!http) {
24
+ void invocation.application.close();
25
+ throw new Error('LUTRE_RUNTIME_HTTP_REQUIRED: awsLambdaRuntime.bind() requires an HTTP-capable Application.');
26
+ }
27
+ if (options.response === 'streaming') {
28
+ const handler = createAwsLambdaStreamingHttpDriver(http);
29
+ const aws = awsLambdaGlobal();
30
+ return aws?.streamifyResponse ? aws.streamifyResponse(handler) : handler;
31
+ }
32
+ return createAwsLambdaHttpDriver(http);
33
+ }
34
+ function createAwsLambdaHttpDriver(application) {
35
+ let initialization;
36
+ return async (event) => {
37
+ initialization ??= application.initialize();
38
+ await initialization;
39
+ const response = await application.handle(toRequest(event));
40
+ const metadata = responseMetadata(response);
41
+ return {
42
+ statusCode: response.status,
43
+ ...metadata,
44
+ body: Buffer.from(await response.arrayBuffer()).toString('base64'),
45
+ isBase64Encoded: true,
46
+ };
47
+ };
48
+ }
49
+ function createAwsLambdaStreamingHttpDriver(application) {
50
+ let initialization;
51
+ return async (event, output) => {
52
+ initialization ??= application.initialize();
53
+ await initialization;
54
+ const response = await application.handle(toRequest(event));
55
+ const metadata = responseMetadata(response);
56
+ const aws = awsLambdaGlobal();
57
+ const outputMetadata = {
58
+ statusCode: response.status,
59
+ headers: metadata.headers,
60
+ ...(metadata.cookies === undefined
61
+ ? {}
62
+ : { multiValueHeaders: { 'Set-Cookie': metadata.cookies } }),
63
+ };
64
+ const stream = aws?.HttpResponseStream?.from
65
+ ? aws.HttpResponseStream.from(output, outputMetadata)
66
+ : output;
67
+ if (stream === output) {
68
+ output.setMetadata?.({
69
+ statusCode: response.status,
70
+ ...metadata,
71
+ });
72
+ }
73
+ const reader = response.body?.getReader();
74
+ if (reader) {
75
+ while (true) {
76
+ const chunk = await reader.read();
77
+ if (chunk.done)
78
+ break;
79
+ if (!stream.write(chunk.value) && stream.once) {
80
+ await new Promise((resolve) => {
81
+ stream.once?.('drain', resolve);
82
+ });
83
+ }
84
+ }
85
+ }
86
+ stream.end();
87
+ };
88
+ }
89
+ function responseMetadata(response) {
90
+ const headers = Object.fromEntries([...response.headers.entries()].filter(([name]) => name !== 'set-cookie'));
91
+ const cookies = response.headers.getSetCookie();
92
+ return {
93
+ headers,
94
+ ...(cookies.length === 0 ? {} : { cookies }),
95
+ };
96
+ }
97
+ function toRequest(event) {
98
+ const path = event.rawPath ?? '/';
99
+ const query = event.rawQueryString ? `?${event.rawQueryString}` : '';
100
+ const body = event.body
101
+ ? event.isBase64Encoded
102
+ ? Buffer.from(event.body, 'base64')
103
+ : event.body
104
+ : undefined;
105
+ return new Request(`https://aws-lambda.local${path}${query}`, {
106
+ method: event.requestContext?.http?.method ?? 'GET',
107
+ headers: Object.fromEntries(Object.entries(event.headers ?? {}).filter((entry) => entry[1] !== undefined)),
108
+ ...(body === undefined ? {} : { body }),
109
+ });
110
+ }
111
+ function awsLambdaGlobal() {
112
+ return globalThis
113
+ .awslambda;
114
+ }
@@ -0,0 +1,20 @@
1
+ import { type ApplicationDefinition, type BootstrapArguments, type HasHttp, type HostBindingApplication } from '../application/index.js';
2
+ type IsAny<TValue> = 0 extends 1 & TValue ? true : false;
3
+ type HttpApplication<TDefinition extends ApplicationDefinition> = IsAny<TDefinition> extends true ? TDefinition : HasHttp<TDefinition> extends true ? TDefinition : never;
4
+ export type BunServeOptions<TDefinition extends ApplicationDefinition> = {
5
+ readonly application: HttpApplication<TDefinition>;
6
+ readonly port: number;
7
+ readonly hostname?: string;
8
+ readonly environment?: unknown;
9
+ } & BootstrapArguments<TDefinition>;
10
+ export interface BunServeHandle<TDefinition extends ApplicationDefinition = ApplicationDefinition> {
11
+ readonly application: HostBindingApplication<TDefinition>;
12
+ close(signal?: string): Promise<void>;
13
+ }
14
+ export declare const bunRuntime: {
15
+ readonly runtime: 'bun';
16
+ readonly capabilities: Set<string>;
17
+ readonly serve: typeof serve;
18
+ };
19
+ declare function serve<const TDefinition extends ApplicationDefinition>(options: BunServeOptions<TDefinition>): Promise<BunServeHandle<TDefinition>>;
20
+ export {};
@@ -0,0 +1,82 @@
1
+ import { binding, } from '../application/index.js';
2
+ export const bunRuntime = {
3
+ runtime: 'bun',
4
+ capabilities: new Set([
5
+ 'http.server',
6
+ 'http.request.streaming',
7
+ 'http.response.streaming',
8
+ 'stream.readable',
9
+ 'stream.writable',
10
+ 'runtime.longLived',
11
+ 'runtime.shutdownHook',
12
+ 'env.runtime',
13
+ 'crypto.random',
14
+ ]),
15
+ serve,
16
+ };
17
+ async function serve(options) {
18
+ const bun = bunGlobal();
19
+ if (!bun?.serve) {
20
+ throw new Error('LUTRE_BUN_UNAVAILABLE: Bun.serve() is not available.');
21
+ }
22
+ const host = binding.host({
23
+ application: options.application,
24
+ environment: 'environment' in options ? options.environment : (bun.env ?? undefined),
25
+ ...('arguments' in options ? { arguments: options.arguments } : {}),
26
+ });
27
+ const http = 'http' in host ? host.http : undefined;
28
+ if (!http) {
29
+ await host.application.close();
30
+ throw new Error('LUTRE_RUNTIME_HTTP_REQUIRED: bunRuntime.serve() requires an HTTP-capable Application.');
31
+ }
32
+ await host.application.init();
33
+ if ('triggers' in host.application)
34
+ await host.application.triggers.start();
35
+ let server;
36
+ try {
37
+ server = bun.serve({
38
+ port: options.port,
39
+ ...(options.hostname === undefined ? {} : { hostname: options.hostname }),
40
+ fetch: createBunFetchDriver(http),
41
+ });
42
+ }
43
+ catch (error) {
44
+ await host.application.close().catch(() => undefined);
45
+ throw error;
46
+ }
47
+ let closed = false;
48
+ return {
49
+ application: host.application,
50
+ async close(signal) {
51
+ if (closed)
52
+ return;
53
+ closed = true;
54
+ const errors = [];
55
+ try {
56
+ await server.stop(true);
57
+ }
58
+ catch (error) {
59
+ errors.push(error);
60
+ }
61
+ try {
62
+ await host.application.close(signal);
63
+ }
64
+ catch (error) {
65
+ errors.push(error);
66
+ }
67
+ if (errors.length > 0)
68
+ throw new AggregateError(errors, 'Bun runtime shutdown failed');
69
+ },
70
+ };
71
+ }
72
+ function createBunFetchDriver(application) {
73
+ let initialization;
74
+ return async (request) => {
75
+ initialization ??= application.initialize();
76
+ await initialization;
77
+ return application.handle(request);
78
+ };
79
+ }
80
+ function bunGlobal() {
81
+ return globalThis.Bun;
82
+ }
@@ -0,0 +1,18 @@
1
+ import { type ApplicationDefinition, type BootstrapArguments, type HasHttp } from '../application/index.js';
2
+ type IsAny<TValue> = 0 extends 1 & TValue ? true : false;
3
+ type HttpApplication<TDefinition extends ApplicationDefinition> = IsAny<TDefinition> extends true ? TDefinition : HasHttp<TDefinition> extends true ? TDefinition : never;
4
+ export type CloudflareWorkersBindOptions<TDefinition extends ApplicationDefinition> = {
5
+ readonly application: HttpApplication<TDefinition>;
6
+ } & BootstrapArguments<TDefinition>;
7
+ export interface CloudflareWorkersBinding {
8
+ fetch(request: Request, environment?: unknown, executionContext?: unknown): Promise<Response>;
9
+ close(signal?: string): Promise<void>;
10
+ }
11
+ export declare const cloudflareWorkersRuntime: {
12
+ readonly runtime: 'cloudflare-workers';
13
+ readonly compatibilityDateMinimum: '2026-08-04';
14
+ readonly capabilities: Set<string>;
15
+ readonly bind: typeof bind;
16
+ };
17
+ declare function bind<const TDefinition extends ApplicationDefinition>(options: CloudflareWorkersBindOptions<TDefinition>): CloudflareWorkersBinding;
18
+ export {};
@@ -0,0 +1,55 @@
1
+ import { binding, } from '../application/index.js';
2
+ export const cloudflareWorkersRuntime = {
3
+ runtime: 'cloudflare-workers',
4
+ compatibilityDateMinimum: '2026-08-04',
5
+ capabilities: new Set([
6
+ 'http.server',
7
+ 'http.request.streaming',
8
+ 'http.response.streaming',
9
+ 'stream.readable',
10
+ 'stream.writable',
11
+ 'background.waitUntil',
12
+ 'env.runtime',
13
+ 'crypto.random',
14
+ ]),
15
+ bind,
16
+ };
17
+ function bind(options) {
18
+ let invocation;
19
+ let fetch;
20
+ const resolve = (environment) => {
21
+ if (invocation && fetch)
22
+ return { invocation, fetch };
23
+ invocation = binding.invocation({
24
+ application: options.application,
25
+ environment,
26
+ ...('arguments' in options ? { arguments: options.arguments } : {}),
27
+ });
28
+ const http = 'http' in invocation
29
+ ? invocation.http
30
+ : undefined;
31
+ if (!http) {
32
+ void invocation.application.close();
33
+ throw new Error('LUTRE_RUNTIME_HTTP_REQUIRED: cloudflareWorkersRuntime.bind() requires an HTTP-capable Application.');
34
+ }
35
+ fetch = createCloudflareWorkersFetchDriver(http);
36
+ return { invocation, fetch };
37
+ };
38
+ return {
39
+ async fetch(request, environment) {
40
+ const resolved = resolve(environment);
41
+ return resolved.fetch(request);
42
+ },
43
+ async close(signal) {
44
+ await invocation?.application.close(signal);
45
+ },
46
+ };
47
+ }
48
+ function createCloudflareWorkersFetchDriver(application) {
49
+ let initialization;
50
+ return async (request) => {
51
+ initialization ??= application.initialize();
52
+ await initialization;
53
+ return application.handle(request);
54
+ };
55
+ }
@@ -0,0 +1,28 @@
1
+ import { type ApplicationDefinition, type BootstrapArguments, type HasHttp, type HostBindingApplication } from '../application/index.js';
2
+ type IsAny<TValue> = 0 extends 1 & TValue ? true : false;
3
+ type HttpApplication<TDefinition extends ApplicationDefinition> = IsAny<TDefinition> extends true ? TDefinition : HasHttp<TDefinition> extends true ? TDefinition : never;
4
+ export type DenoRuntimeOptions<TDefinition extends ApplicationDefinition> = {
5
+ readonly application: HttpApplication<TDefinition>;
6
+ readonly environment?: unknown;
7
+ } & BootstrapArguments<TDefinition>;
8
+ export type DenoServeOptions<TDefinition extends ApplicationDefinition> = DenoRuntimeOptions<TDefinition> & {
9
+ readonly port: number;
10
+ readonly hostname?: string;
11
+ };
12
+ export interface DenoBinding {
13
+ fetch(request: Request): Promise<Response>;
14
+ close(signal?: string): Promise<void>;
15
+ }
16
+ export interface DenoServeHandle<TDefinition extends ApplicationDefinition = ApplicationDefinition> {
17
+ readonly application: HostBindingApplication<TDefinition>;
18
+ close(signal?: string): Promise<void>;
19
+ }
20
+ export declare const denoRuntime: {
21
+ readonly runtime: 'deno';
22
+ readonly capabilities: Set<string>;
23
+ readonly bind: typeof bind;
24
+ readonly serve: typeof serve;
25
+ };
26
+ declare function bind<const TDefinition extends ApplicationDefinition>(options: DenoRuntimeOptions<TDefinition>): DenoBinding;
27
+ declare function serve<const TDefinition extends ApplicationDefinition>(options: DenoServeOptions<TDefinition>): Promise<DenoServeHandle<TDefinition>>;
28
+ export {};
@@ -0,0 +1,106 @@
1
+ import { binding, } from '../application/index.js';
2
+ export const denoRuntime = {
3
+ runtime: 'deno',
4
+ capabilities: new Set([
5
+ 'http.server',
6
+ 'http.request.streaming',
7
+ 'http.response.streaming',
8
+ 'stream.readable',
9
+ 'stream.writable',
10
+ 'runtime.longLived',
11
+ 'runtime.shutdownHook',
12
+ 'env.runtime',
13
+ 'crypto.random',
14
+ ]),
15
+ bind,
16
+ serve,
17
+ };
18
+ function bind(options) {
19
+ const invocation = binding.invocation(bindingOptions(options, denoEnvironment()));
20
+ const http = 'http' in invocation
21
+ ? invocation.http
22
+ : undefined;
23
+ if (!http) {
24
+ void invocation.application.close();
25
+ throw new Error('LUTRE_RUNTIME_HTTP_REQUIRED: denoRuntime.bind() requires an HTTP-capable Application.');
26
+ }
27
+ const fetch = createDenoFetchDriver(http);
28
+ return {
29
+ fetch,
30
+ close: (signal) => invocation.application.close(signal),
31
+ };
32
+ }
33
+ async function serve(options) {
34
+ const deno = denoGlobal();
35
+ if (!deno?.serve) {
36
+ throw new Error('LUTRE_DENO_UNAVAILABLE: Deno.serve() is not available.');
37
+ }
38
+ const host = binding.host(bindingOptions(options, denoEnvironment()));
39
+ const http = 'http' in host ? host.http : undefined;
40
+ if (!http) {
41
+ await host.application.close();
42
+ throw new Error('LUTRE_RUNTIME_HTTP_REQUIRED: denoRuntime.serve() requires an HTTP-capable Application.');
43
+ }
44
+ await host.application.init();
45
+ if ('triggers' in host.application)
46
+ await host.application.triggers.start();
47
+ let server;
48
+ try {
49
+ server = deno.serve({
50
+ port: options.port,
51
+ ...(options.hostname === undefined
52
+ ? {}
53
+ : { hostname: options.hostname }),
54
+ }, createDenoFetchDriver(http));
55
+ }
56
+ catch (error) {
57
+ await host.application.close().catch(() => undefined);
58
+ throw error;
59
+ }
60
+ let closed = false;
61
+ return {
62
+ application: host.application,
63
+ async close(signal) {
64
+ if (closed)
65
+ return;
66
+ closed = true;
67
+ const errors = [];
68
+ try {
69
+ await server.shutdown();
70
+ }
71
+ catch (error) {
72
+ errors.push(error);
73
+ }
74
+ try {
75
+ await host.application.close(signal);
76
+ }
77
+ catch (error) {
78
+ errors.push(error);
79
+ }
80
+ if (errors.length > 0)
81
+ throw new AggregateError(errors, 'Deno runtime shutdown failed');
82
+ },
83
+ };
84
+ }
85
+ function createDenoFetchDriver(application) {
86
+ let initialization;
87
+ return async (request) => {
88
+ initialization ??= application.initialize();
89
+ await initialization;
90
+ return application.handle(request);
91
+ };
92
+ }
93
+ function bindingOptions(options, environment) {
94
+ return {
95
+ application: options.application,
96
+ environment: 'environment' in options ? options.environment : environment,
97
+ ...('arguments' in options ? { arguments: options.arguments } : {}),
98
+ };
99
+ }
100
+ function denoEnvironment() {
101
+ const deno = denoGlobal();
102
+ return deno?.env?.toObject ? deno.env.toObject() : undefined;
103
+ }
104
+ function denoGlobal() {
105
+ return globalThis.Deno;
106
+ }
@@ -0,0 +1,27 @@
1
+ import { type ApplicationDefinition, type BootstrapArguments, type HasMessagePort, type InvocationApplication } from '../application/index.js';
2
+ import { type MessagePortLike } from '../message-port/index.js';
3
+ type IsAny<TValue> = 0 extends 1 & TValue ? true : false;
4
+ type MessagePortApplication<TDefinition extends ApplicationDefinition> = IsAny<TDefinition> extends true ? TDefinition : HasMessagePort<TDefinition> extends true ? TDefinition : never;
5
+ export interface ElectronMessagePortMainLike {
6
+ postMessage(value: unknown): void;
7
+ on(type: 'message', listener: (event: {
8
+ readonly data: unknown;
9
+ }) => void): void;
10
+ start(): void;
11
+ }
12
+ export type ElectronAttachOptions<TDefinition extends ApplicationDefinition> = {
13
+ readonly application: MessagePortApplication<TDefinition>;
14
+ readonly port: MessagePortLike | ElectronMessagePortMainLike;
15
+ readonly environment?: unknown;
16
+ } & BootstrapArguments<TDefinition>;
17
+ export interface ElectronAttachment<TDefinition extends ApplicationDefinition = ApplicationDefinition> {
18
+ readonly application: InvocationApplication<TDefinition>;
19
+ close(signal?: string): Promise<void>;
20
+ }
21
+ export declare const electronRuntime: {
22
+ readonly runtime: 'electron';
23
+ readonly capabilities: Set<string>;
24
+ readonly attach: typeof attach;
25
+ };
26
+ declare function attach<const TDefinition extends ApplicationDefinition>(options: ElectronAttachOptions<TDefinition>): ElectronAttachment<TDefinition>;
27
+ export {};
@@ -0,0 +1,52 @@
1
+ import { binding, } from '../application/index.js';
2
+ import { attachMessagePort, } from '../message-port/index.js';
3
+ export const electronRuntime = {
4
+ runtime: 'electron',
5
+ capabilities: new Set([
6
+ 'messagePort.send',
7
+ 'messagePort.receive',
8
+ 'messagePort.transfer',
9
+ 'stream.readable',
10
+ 'runtime.longLived',
11
+ 'runtime.shutdownHook',
12
+ 'env.runtime',
13
+ 'crypto.random',
14
+ ]),
15
+ attach,
16
+ };
17
+ function attach(options) {
18
+ const invocation = binding.invocation({
19
+ application: options.application,
20
+ environment: 'environment' in options
21
+ ? options.environment
22
+ : typeof process === 'undefined'
23
+ ? undefined
24
+ : process.env,
25
+ ...('arguments' in options ? { arguments: options.arguments } : {}),
26
+ });
27
+ const messagePort = 'messagePort' in invocation
28
+ ? invocation.messagePort
29
+ : undefined;
30
+ if (!messagePort) {
31
+ void invocation.application.close();
32
+ throw new Error('LUTRE_RUNTIME_MESSAGE_PORT_REQUIRED: electronRuntime.attach() requires a MessagePort-capable Application.');
33
+ }
34
+ attachElectronMessagePort(messagePort, options.port);
35
+ return {
36
+ application: invocation.application,
37
+ close: (signal) => invocation.application.close(signal),
38
+ };
39
+ }
40
+ function attachElectronMessagePort(application, port) {
41
+ const initialization = application.initialize();
42
+ void initialization.catch(() => undefined);
43
+ if ('addEventListener' in port) {
44
+ attachMessagePort(application, port);
45
+ return;
46
+ }
47
+ attachMessagePort(application, {
48
+ postMessage: (value) => port.postMessage(value),
49
+ addEventListener: (_type, listener) => port.on('message', listener),
50
+ start: () => port.start(),
51
+ });
52
+ }
@@ -0,0 +1,33 @@
1
+ import { type ProviderDescriptor, type QueueDescriptor } from '../core/index.js';
2
+ import { type HttpProtocolExecution } from '../http/index.js';
3
+ import { type MessagePortProtocolExecution } from '../message-port/index.js';
4
+ import type { ApplicationDefinition, BootstrapArguments, HasHttp, HasMessagePort, HasTriggers, InvocationApplication, QueueConsumerDriver, TriggerApplicationCapability } from './index.js';
5
+ type IsAny<TValue> = 0 extends 1 & TValue ? true : false;
6
+ type ProtocolBinding<TDefinition extends ApplicationDefinition> = IsAny<TDefinition> extends true ? {
7
+ readonly http?: HttpProtocolExecution;
8
+ readonly messagePort?: MessagePortProtocolExecution;
9
+ } : (HasHttp<TDefinition> extends true ? {
10
+ readonly http: HttpProtocolExecution;
11
+ } : {}) & (HasMessagePort<TDefinition> extends true ? {
12
+ readonly messagePort: MessagePortProtocolExecution;
13
+ } : {});
14
+ export type InvocationBinding<TDefinition extends ApplicationDefinition> = {
15
+ readonly application: InvocationApplication<TDefinition>;
16
+ } & ProtocolBinding<TDefinition>;
17
+ export type HostBindingApplication<TDefinition extends ApplicationDefinition> = InvocationApplication<TDefinition> & (HasTriggers<TDefinition> extends true ? TriggerApplicationCapability : {});
18
+ export type HostBinding<TDefinition extends ApplicationDefinition> = {
19
+ readonly application: HostBindingApplication<TDefinition>;
20
+ } & ProtocolBinding<TDefinition>;
21
+ export interface InvocationBindingBaseOptions<TDefinition extends ApplicationDefinition> {
22
+ readonly application: TDefinition;
23
+ readonly environment?: unknown;
24
+ }
25
+ export type InvocationBindingOptions<TDefinition extends ApplicationDefinition> = InvocationBindingBaseOptions<TDefinition> & BootstrapArguments<TDefinition>;
26
+ interface BindingApi {
27
+ invocation<const TDefinition extends ApplicationDefinition>(options: InvocationBindingOptions<TDefinition>): InvocationBinding<TDefinition>;
28
+ host<const TDefinition extends ApplicationDefinition>(options: InvocationBindingOptions<TDefinition>): HostBinding<TDefinition>;
29
+ queue(descriptor: QueueDescriptor, driver: QueueConsumerDriver): ProviderDescriptor;
30
+ }
31
+ /** flatなcreateXXX/bindXXX APIを増殖させないため、外部bindingをnamespaceへ集約する。 */
32
+ export declare const binding: Readonly<BindingApi>;
33
+ export {};