@loutrejs/loutre 0.4.1 → 0.6.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 (164) hide show
  1. package/README.md +63 -32
  2. package/dist/adapters/aws-lambda.d.ts +5 -3
  3. package/dist/adapters/aws-lambda.js +53 -23
  4. package/dist/adapters/bun.d.ts +7 -4
  5. package/dist/adapters/bun.js +52 -42
  6. package/dist/adapters/cloudflare-workers.d.ts +5 -3
  7. package/dist/adapters/cloudflare-workers.js +23 -28
  8. package/dist/adapters/deno.d.ts +7 -4
  9. package/dist/adapters/deno.js +94 -57
  10. package/dist/adapters/electron.d.ts +13 -5
  11. package/dist/adapters/electron.js +76 -28
  12. package/dist/application/definition.d.ts +31 -0
  13. package/dist/application/definition.js +16 -0
  14. package/dist/application/index.d.ts +4 -63
  15. package/dist/application/index.js +2 -11
  16. package/dist/application/kernel-internal.d.ts +2 -0
  17. package/dist/application/kernel-internal.js +10 -0
  18. package/dist/application/kernel.d.ts +21 -0
  19. package/dist/application/kernel.js +65 -0
  20. package/dist/core/diagnostic.d.ts +9 -0
  21. package/dist/core/diagnostic.js +9 -0
  22. package/dist/core/extension.d.ts +107 -0
  23. package/dist/core/extension.js +73 -0
  24. package/dist/core/generic-layer.d.ts +26 -0
  25. package/dist/core/generic-layer.js +116 -0
  26. package/dist/core/index.d.ts +31 -15
  27. package/dist/core/index.js +15 -15
  28. package/dist/core/injection.d.ts +4 -13
  29. package/dist/core/injection.js +63 -0
  30. package/dist/core/model/context.d.ts +18 -0
  31. package/dist/core/model/context.js +34 -0
  32. package/dist/core/model/lifecycle.d.ts +5 -0
  33. package/dist/core/model/lifecycle.js +44 -0
  34. package/dist/core/model/modules.d.ts +2 -0
  35. package/dist/core/model/modules.js +101 -0
  36. package/dist/core/model/providers.d.ts +4 -0
  37. package/dist/core/model/providers.js +74 -0
  38. package/dist/core/model/snapshot.d.ts +6 -0
  39. package/dist/core/model/snapshot.js +31 -0
  40. package/dist/core/model/types.d.ts +78 -0
  41. package/dist/core/model/validation.d.ts +4 -0
  42. package/dist/core/model/validation.js +40 -0
  43. package/dist/core/model/visibility.d.ts +4 -0
  44. package/dist/core/model/visibility.js +20 -0
  45. package/dist/core/model.d.ts +10 -0
  46. package/dist/core/model.js +346 -0
  47. package/dist/core/module.d.ts +8 -13
  48. package/dist/core/source-location.d.ts +8 -0
  49. package/dist/core/source-location.js +31 -0
  50. package/dist/core/token-internal.d.ts +3 -0
  51. package/dist/core/token-internal.js +4 -0
  52. package/dist/core/type.d.ts +7 -0
  53. package/dist/core/type.js +4 -0
  54. package/dist/generated/version.d.ts +1 -1
  55. package/dist/generated/version.js +1 -1
  56. package/dist/graph/index.d.ts +2 -3
  57. package/dist/graph/index.js +1 -3
  58. package/dist/graph/model.d.ts +34 -0
  59. package/dist/graph/model.js +127 -0
  60. package/dist/http/auth.d.ts +46 -0
  61. package/dist/http/auth.js +96 -0
  62. package/dist/http/client.d.ts +28 -31
  63. package/dist/http/client.js +60 -41
  64. package/dist/http/cors-internal.d.ts +29 -0
  65. package/dist/http/cors-internal.js +160 -0
  66. package/dist/http/cors.d.ts +5 -8
  67. package/dist/http/cors.js +7 -165
  68. package/dist/http/extension.d.ts +348 -0
  69. package/dist/http/extension.js +947 -0
  70. package/dist/http/index.d.ts +9 -7
  71. package/dist/http/index.js +5 -7
  72. package/dist/{application → http}/openapi.d.ts +12 -3
  73. package/dist/{application → http}/openapi.js +164 -101
  74. package/dist/http/path.d.ts +2 -39
  75. package/dist/http/path.js +7 -2
  76. package/dist/http/runtime/request.d.ts +17 -0
  77. package/dist/http/runtime/request.js +109 -0
  78. package/dist/http/runtime/response.d.ts +18 -0
  79. package/dist/http/runtime/response.js +108 -0
  80. package/dist/index.d.ts +4 -0
  81. package/dist/index.js +2 -0
  82. package/dist/message-port/extension.d.ts +66 -0
  83. package/dist/message-port/extension.js +395 -0
  84. package/dist/message-port/index.d.ts +4 -73
  85. package/dist/message-port/index.js +2 -189
  86. package/dist/message-port/transport.d.ts +11 -0
  87. package/dist/message-port/transport.js +45 -0
  88. package/dist/runtime/async-iterator-lifecycle.d.ts +12 -0
  89. package/dist/runtime/async-iterator-lifecycle.js +95 -0
  90. package/dist/runtime/capability.d.ts +11 -11
  91. package/dist/runtime/capability.js +3 -3
  92. package/dist/runtime/di.d.ts +2 -22
  93. package/dist/runtime/di.js +8 -285
  94. package/dist/runtime/execution-tracker.d.ts +8 -0
  95. package/dist/runtime/execution-tracker.js +54 -0
  96. package/dist/runtime/index.d.ts +15 -9
  97. package/dist/runtime/index.js +9 -9
  98. package/dist/runtime/ingress-gate.d.ts +7 -0
  99. package/dist/runtime/ingress-gate.js +33 -0
  100. package/dist/runtime/kernel.d.ts +23 -0
  101. package/dist/runtime/kernel.js +354 -0
  102. package/dist/runtime/logger.d.ts +2 -0
  103. package/dist/runtime/logger.js +2 -0
  104. package/dist/runtime/node-capabilities.d.ts +1 -1
  105. package/dist/runtime/node-capabilities.js +1 -1
  106. package/dist/runtime/server-port.d.ts +2 -0
  107. package/dist/runtime/server-port.js +11 -0
  108. package/dist/tasks/extension.d.ts +144 -0
  109. package/dist/tasks/extension.js +499 -0
  110. package/dist/tasks/index.d.ts +2 -0
  111. package/dist/tasks/index.js +1 -0
  112. package/dist/tasks/runtime/cron.d.ts +6 -0
  113. package/dist/tasks/runtime/cron.js +89 -0
  114. package/dist/websocket/errors.d.ts +9 -0
  115. package/dist/websocket/errors.js +18 -0
  116. package/dist/websocket/extension.d.ts +154 -0
  117. package/dist/websocket/extension.js +524 -0
  118. package/dist/websocket/index.d.ts +3 -0
  119. package/dist/websocket/index.js +2 -0
  120. package/package.json +12 -16
  121. package/dist/application/binding.d.ts +0 -33
  122. package/dist/application/binding.js +0 -295
  123. package/dist/application/host.d.ts +0 -8
  124. package/dist/application/host.js +0 -14
  125. package/dist/application/queue.d.ts +0 -3
  126. package/dist/application/queue.js +0 -4
  127. package/dist/application/scheduler.d.ts +0 -2
  128. package/dist/application/scheduler.js +0 -45
  129. package/dist/core/context-key.d.ts +0 -14
  130. package/dist/core/context-key.js +0 -14
  131. package/dist/core/contract.d.ts +0 -94
  132. package/dist/core/contract.js +0 -79
  133. package/dist/core/execution.d.ts +0 -78
  134. package/dist/core/execution.js +0 -61
  135. package/dist/core/implementation.d.ts +0 -34
  136. package/dist/core/implementation.js +0 -30
  137. package/dist/core/layer.d.ts +0 -109
  138. package/dist/core/layer.js +0 -52
  139. package/dist/graph/compiler.d.ts +0 -4
  140. package/dist/graph/compiler.js +0 -228
  141. package/dist/graph/graph.d.ts +0 -17
  142. package/dist/graph/graph.js +0 -1103
  143. package/dist/graph/ir.d.ts +0 -175
  144. package/dist/http/application.d.ts +0 -18
  145. package/dist/http/application.js +0 -506
  146. package/dist/http/basic-auth.d.ts +0 -37
  147. package/dist/http/basic-auth.js +0 -67
  148. package/dist/http/definitions.d.ts +0 -225
  149. package/dist/http/definitions.js +0 -69
  150. package/dist/http/hosted-protocol.d.ts +0 -4
  151. package/dist/http/hosted-protocol.js +0 -4
  152. package/dist/http/params.d.ts +0 -2
  153. package/dist/http/params.js +0 -19
  154. package/dist/http/request-body.d.ts +0 -4
  155. package/dist/http/request-body.js +0 -6
  156. package/dist/http/validate.d.ts +0 -20
  157. package/dist/http/validate.js +0 -6
  158. package/dist/message-port/environment.d.ts +0 -2
  159. package/dist/message-port/environment.js +0 -4
  160. package/dist/runtime/application.d.ts +0 -38
  161. package/dist/runtime/application.js +0 -339
  162. package/dist/runtime/pipeline.d.ts +0 -11
  163. package/dist/runtime/pipeline.js +0 -161
  164. /package/dist/{graph/ir.js → core/model/types.js} +0 -0
@@ -1,7 +1,10 @@
1
- import { binding, } from '../application/index.js';
1
+ import { createKernelApplication, } from '../application/index.js';
2
+ import { bindApplicationCapability } from '../application/kernel-internal.js';
3
+ import { httpExecutionExtension } from '../http/index.js';
2
4
  import { LOUTRE_VERSION, startStartupPresentation } from '../presentation.js';
3
5
  import { assertRuntimeEngine } from '../runtime/engine.js';
4
6
  import { serverUrl } from '../runtime/server-url.js';
7
+ import { canRetryOnNextPort, initialServerPort, } from '../runtime/server-port.js';
5
8
  export const denoRuntime = {
6
9
  runtime: 'deno',
7
10
  capabilities: new Set([
@@ -20,18 +23,29 @@ export const denoRuntime = {
20
23
  };
21
24
  function bind(options) {
22
25
  assertRuntimeEngine('deno');
23
- const invocation = binding.invocation(bindingOptions(options, denoEnvironment()));
24
- const http = 'http' in invocation
25
- ? invocation.http
26
- : undefined;
27
- if (!http) {
28
- void invocation.application.close();
29
- throw new Error('LUTRE_RUNTIME_HTTP_REQUIRED: denoRuntime.bind() requires an HTTP-capable Application.');
26
+ if (options.application.model.extensions.get(httpExecutionExtension) ===
27
+ undefined) {
28
+ throw new Error('LUTRE_RUNTIME_HTTP_REQUIRED: denoRuntime.bind() requires the HTTP Execution Extension.');
30
29
  }
31
- const fetch = createDenoFetchDriver(http);
30
+ const application = createKernelApplication({
31
+ ...options,
32
+ application: options.application,
33
+ capabilities: [
34
+ bindApplicationCapability(options.application.model, 'http.server', {
35
+ runtime: 'deno',
36
+ }),
37
+ ...(options.capabilities ?? []),
38
+ ],
39
+ environment: 'environment' in options ? options.environment : denoEnvironment(),
40
+ });
41
+ let initialization;
32
42
  return {
33
- fetch,
34
- close: (signal) => invocation.application.close(signal),
43
+ async fetch(request) {
44
+ initialization ??= application.init();
45
+ await initialization;
46
+ return application.http.fetch(request);
47
+ },
48
+ close: (signal) => application.close(signal),
35
49
  };
36
50
  }
37
51
  async function create(options) {
@@ -46,42 +60,83 @@ async function create(options) {
46
60
  },
47
61
  write: (value) => console.log(value),
48
62
  });
49
- const host = binding.host(bindingOptions(options, denoEnvironment()));
50
- const http = 'http' in host ? host.http : undefined;
51
- if (!http) {
52
- await host.application.close();
53
- throw new Error('LUTRE_RUNTIME_HTTP_REQUIRED: denoRuntime.create() requires an HTTP-capable Application.');
63
+ if (options.application.model.extensions.get(httpExecutionExtension) ===
64
+ undefined) {
65
+ throw new Error('LUTRE_RUNTIME_HTTP_REQUIRED: denoRuntime.create() requires the HTTP Execution Extension.');
54
66
  }
55
- await host.application.init();
56
- const application = host.application;
57
- const closeApplication = host.application.close.bind(host.application);
67
+ const hosted = createKernelApplication({
68
+ ...options,
69
+ application: options.application,
70
+ capabilities: [
71
+ bindApplicationCapability(options.application.model, 'http.server', {
72
+ runtime: 'deno',
73
+ }),
74
+ ...(options.capabilities ?? []),
75
+ ],
76
+ environment: 'environment' in options ? options.environment : denoEnvironment(),
77
+ });
78
+ await hosted.init();
79
+ const application = hosted;
80
+ const http = hosted
81
+ .http;
82
+ const closeApplication = application.close.bind(application);
58
83
  let server;
59
84
  let removeShutdownHooks;
60
85
  let serving = false;
61
86
  let closed = false;
62
- const close = async (signal) => {
87
+ let closingPromise;
88
+ let serverClosingPromise;
89
+ const beginServerClose = () => {
90
+ if (serverClosingPromise)
91
+ return serverClosingPromise;
92
+ if (!server)
93
+ return Promise.resolve();
94
+ const closingServer = server;
95
+ serverClosingPromise = Promise.resolve()
96
+ .then(() => closingServer.shutdown())
97
+ .then(() => {
98
+ if (server === closingServer)
99
+ server = undefined;
100
+ }, (error) => {
101
+ serverClosingPromise = undefined;
102
+ throw error;
103
+ });
104
+ void serverClosingPromise.catch(() => undefined);
105
+ return serverClosingPromise;
106
+ };
107
+ const close = (signal) => {
63
108
  if (closed)
64
- return;
65
- closed = true;
66
- removeShutdownHooks?.();
67
- removeShutdownHooks = undefined;
68
- const errors = [];
69
- if (server) {
109
+ return Promise.resolve();
110
+ if (closingPromise)
111
+ return closingPromise;
112
+ closingPromise = (async () => {
113
+ removeShutdownHooks?.();
114
+ removeShutdownHooks = undefined;
115
+ const errors = [];
116
+ const serverClosing = beginServerClose();
70
117
  try {
71
- await server.shutdown();
118
+ await closeApplication(signal);
72
119
  }
73
120
  catch (error) {
74
121
  errors.push(error);
75
122
  }
76
- }
77
- try {
78
- await closeApplication(signal);
79
- }
80
- catch (error) {
81
- errors.push(error);
82
- }
83
- if (errors.length > 0)
84
- throw new AggregateError(errors, 'Deno runtime shutdown failed');
123
+ if (errors.length === 0) {
124
+ try {
125
+ await serverClosing;
126
+ }
127
+ catch (error) {
128
+ errors.push(error);
129
+ }
130
+ }
131
+ if (errors.length > 0) {
132
+ throw new AggregateError(errors, 'Deno runtime shutdown failed');
133
+ }
134
+ closed = true;
135
+ })().catch((error) => {
136
+ closingPromise = undefined;
137
+ throw error;
138
+ });
139
+ return closingPromise;
85
140
  };
86
141
  const serve = async (serveOptions = {}) => {
87
142
  if (closed) {
@@ -96,10 +151,8 @@ async function create(options) {
96
151
  }
97
152
  serving = true;
98
153
  try {
99
- if ('triggers' in application)
100
- await application.triggers.start();
101
154
  const requestedPort = serveOptions.port;
102
- let port = requestedPort ?? 3000;
155
+ let port = initialServerPort(requestedPort);
103
156
  while (true) {
104
157
  try {
105
158
  server = deno.serve({
@@ -161,28 +214,12 @@ function registerDenoShutdownHooks(deno, close) {
161
214
  }
162
215
  return remove;
163
216
  }
164
- function canRetryOnNextPort(error, port) {
165
- return port < 65_535 && isAddressInUseError(error);
166
- }
167
- function isAddressInUseError(error) {
168
- return (typeof error === 'object' &&
169
- error !== null &&
170
- 'code' in error &&
171
- error.code === 'EADDRINUSE');
172
- }
173
217
  function createDenoFetchDriver(application) {
174
218
  let initialization;
175
219
  return async (request) => {
176
- initialization ??= application.initialize();
220
+ initialization ??= application.initialize?.() ?? Promise.resolve();
177
221
  await initialization;
178
- return application.handle(request);
179
- };
180
- }
181
- function bindingOptions(options, environment) {
182
- return {
183
- application: options.application,
184
- environment: 'environment' in options ? options.environment : environment,
185
- ...('arguments' in options ? { arguments: options.arguments } : {}),
222
+ return application.fetch(request);
186
223
  };
187
224
  }
188
225
  function denoEnvironment() {
@@ -1,7 +1,14 @@
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;
1
+ import { type ApplicationDefinition, type BootstrapArguments, type KernelHostedApplication, type RequireApplicationExtension } from '../application/index.js';
2
+ import type { RuntimeCapabilityBinding } from '../core/index.js';
3
+ import { messagePortExtension } from '../message-port/index.js';
4
+ type MessagePortApplication<TDefinition extends ApplicationDefinition> = RequireApplicationExtension<TDefinition, typeof messagePortExtension>;
5
+ export interface MessagePortLike {
6
+ postMessage(value: unknown): void;
7
+ addEventListener(type: 'message', listener: (event: {
8
+ readonly data: unknown;
9
+ }) => void): void;
10
+ start?(): void;
11
+ }
5
12
  export interface ElectronMessagePortMainLike {
6
13
  postMessage(value: unknown): void;
7
14
  on(type: 'message', listener: (event: {
@@ -13,9 +20,10 @@ export type ElectronAttachOptions<TDefinition extends ApplicationDefinition> = {
13
20
  readonly application: MessagePortApplication<TDefinition>;
14
21
  readonly port: MessagePortLike | ElectronMessagePortMainLike;
15
22
  readonly environment?: unknown;
23
+ readonly capabilities?: readonly RuntimeCapabilityBinding[];
16
24
  } & BootstrapArguments<TDefinition>;
17
25
  export interface ElectronAttachment<TDefinition extends ApplicationDefinition = ApplicationDefinition> {
18
- readonly application: InvocationApplication<TDefinition>;
26
+ readonly application: KernelHostedApplication<TDefinition>;
19
27
  close(signal?: string): Promise<void>;
20
28
  }
21
29
  export declare const electronRuntime: {
@@ -1,5 +1,5 @@
1
- import { binding, } from '../application/index.js';
2
- import { attachMessagePort, } from '../message-port/index.js';
1
+ import { createKernelApplication, } from '../application/index.js';
2
+ import { messagePortExtension } from '../message-port/index.js';
3
3
  import { assertRuntimeEngine } from '../runtime/engine.js';
4
4
  export const electronRuntime = {
5
5
  runtime: 'electron',
@@ -17,38 +17,86 @@ export const electronRuntime = {
17
17
  };
18
18
  function attach(options) {
19
19
  assertRuntimeEngine('electron');
20
- const invocation = binding.invocation({
20
+ if (options.application.model.extensions.get(messagePortExtension) === undefined) {
21
+ throw new Error('LUTRE_RUNTIME_MESSAGE_PORT_REQUIRED: electronRuntime.attach() requires the MessagePort Execution Extension.');
22
+ }
23
+ const environment = 'environment' in options
24
+ ? options.environment
25
+ : typeof process === 'undefined'
26
+ ? undefined
27
+ : process.env;
28
+ const application = createKernelApplication({
29
+ ...options,
21
30
  application: options.application,
22
- environment: 'environment' in options
23
- ? options.environment
24
- : typeof process === 'undefined'
25
- ? undefined
26
- : process.env,
27
- ...('arguments' in options ? { arguments: options.arguments } : {}),
31
+ environment,
28
32
  });
29
- const messagePort = 'messagePort' in invocation
30
- ? invocation.messagePort
31
- : undefined;
32
- if (!messagePort) {
33
- void invocation.application.close();
34
- throw new Error('LUTRE_RUNTIME_MESSAGE_PORT_REQUIRED: electronRuntime.attach() requires a MessagePort-capable Application.');
35
- }
36
- attachElectronMessagePort(messagePort, options.port);
33
+ const initialization = application.init();
34
+ void initialization.catch(() => undefined);
35
+ attachElectronMessagePortInvocation(async (method, input) => {
36
+ await initialization;
37
+ const host = application.messagePort;
38
+ return host.invoke(method, input);
39
+ }, options.port);
37
40
  return {
38
- application: invocation.application,
39
- close: (signal) => invocation.application.close(signal),
41
+ application,
42
+ close: (signal) => application.close(signal),
40
43
  };
41
44
  }
42
- function attachElectronMessagePort(application, port) {
43
- const initialization = application.initialize();
44
- void initialization.catch(() => undefined);
45
- if ('addEventListener' in port) {
46
- attachMessagePort(application, port);
47
- return;
48
- }
49
- attachMessagePort(application, {
45
+ function attachElectronMessagePortInvocation(invoke, port) {
46
+ const normalized = normalizeMessagePort(port);
47
+ normalized.addEventListener('message', async (event) => {
48
+ const request = event.data;
49
+ try {
50
+ const result = await invoke(request.procedure, request.input);
51
+ if (isAsyncIterable(result.value)) {
52
+ for await (const value of result.value) {
53
+ postToMessagePort(normalized, {
54
+ id: request.id,
55
+ response: result.response,
56
+ value,
57
+ done: false,
58
+ });
59
+ }
60
+ postToMessagePort(normalized, {
61
+ id: request.id,
62
+ response: result.response,
63
+ done: true,
64
+ });
65
+ }
66
+ else {
67
+ postToMessagePort(normalized, {
68
+ id: request.id,
69
+ response: result.response,
70
+ value: result.value,
71
+ done: true,
72
+ });
73
+ }
74
+ }
75
+ catch (error) {
76
+ postToMessagePort(normalized, {
77
+ id: request.id,
78
+ error: error instanceof Error ? error.message : String(error),
79
+ done: true,
80
+ });
81
+ }
82
+ });
83
+ normalized.start?.();
84
+ }
85
+ function postToMessagePort(port, value) {
86
+ port.postMessage(value);
87
+ }
88
+ function normalizeMessagePort(port) {
89
+ if ('addEventListener' in port)
90
+ return port;
91
+ return {
50
92
  postMessage: (value) => port.postMessage(value),
51
93
  addEventListener: (_type, listener) => port.on('message', listener),
52
94
  start: () => port.start(),
53
- });
95
+ };
96
+ }
97
+ function isAsyncIterable(value) {
98
+ return (typeof value === 'object' &&
99
+ value !== null &&
100
+ Symbol.asyncIterator in value &&
101
+ typeof value[Symbol.asyncIterator] === 'function');
54
102
  }
@@ -0,0 +1,31 @@
1
+ /// <reference lib="esnext.disposable" preserve="true" />
2
+ import type { ArgsClass, ApplicationModel, ExecutionExtension, HostApiOfExtension, ModuleExtensions, ModuleInstance, SchemaInput } from '../core/index.js';
3
+ import type { Logger } from '../runtime/index.js';
4
+ export interface ApplicationDefinitionOptions<TModules extends readonly ModuleInstance[], TArguments extends ArgsClass | undefined> {
5
+ readonly modules: TModules;
6
+ readonly arguments?: TArguments;
7
+ readonly logger?: Logger;
8
+ }
9
+ export interface ApplicationDefinition<TModules extends readonly ModuleInstance[] = readonly ModuleInstance[], TArguments extends ArgsClass | undefined = ArgsClass | undefined> {
10
+ readonly kind: 'application-definition';
11
+ readonly model: ApplicationModel;
12
+ readonly modules: TModules;
13
+ readonly arguments: TArguments;
14
+ readonly logger?: Logger;
15
+ }
16
+ export declare function defineApplication<const TModules extends readonly ModuleInstance[], const TArguments extends ArgsClass | undefined = undefined>(options: ApplicationDefinitionOptions<TModules, TArguments>): ApplicationDefinition<TModules, TArguments>;
17
+ export type ApplicationArgumentsInput<TDefinition extends ApplicationDefinition> = TDefinition['arguments'] extends ArgsClass<infer TSchema> ? SchemaInput<TSchema> : never;
18
+ export type BootstrapArguments<TDefinition extends ApplicationDefinition> = TDefinition['arguments'] extends ArgsClass<infer TSchema> ? {} extends SchemaInput<TSchema> ? {
19
+ readonly arguments?: SchemaInput<TSchema>;
20
+ } : {
21
+ readonly arguments: SchemaInput<TSchema>;
22
+ } : {
23
+ readonly arguments?: never;
24
+ };
25
+ type UnionToIntersection<TUnion> = (TUnion extends unknown ? (value: TUnion) => void : never) extends (value: infer TIntersection) => void ? TIntersection : never;
26
+ export type ApplicationExtensions<TDefinition extends ApplicationDefinition> = ModuleExtensions<TDefinition['modules'][number]>;
27
+ export type ApplicationExtensionHostApis<TDefinition extends ApplicationDefinition> = UnionToIntersection<HostApiOfExtension<ApplicationExtensions<TDefinition>>>;
28
+ type IsAny<TValue> = 0 extends 1 & TValue ? true : false;
29
+ export type RequireApplicationHost<TDefinition extends ApplicationDefinition, TNamespace extends PropertyKey> = IsAny<TDefinition> extends true ? TDefinition : TNamespace extends keyof ApplicationExtensionHostApis<TDefinition> ? TDefinition : never;
30
+ export type RequireApplicationExtension<TDefinition extends ApplicationDefinition, TExtension extends ExecutionExtension> = IsAny<TDefinition> extends true ? TDefinition : [Extract<ApplicationExtensions<TDefinition>, TExtension>] extends [never] ? never : TDefinition;
31
+ export {};
@@ -0,0 +1,16 @@
1
+ /// <reference lib="esnext.disposable" preserve="true" />
2
+ import { buildApplicationModel } from '../core/index.js';
3
+ export function defineApplication(options) {
4
+ return Object.freeze({
5
+ kind: 'application-definition',
6
+ model: buildApplicationModel({
7
+ modules: options.modules,
8
+ ...(options.arguments === undefined
9
+ ? {}
10
+ : { arguments: options.arguments }),
11
+ }),
12
+ modules: options.modules,
13
+ arguments: options.arguments,
14
+ ...(options.logger === undefined ? {} : { logger: options.logger }),
15
+ });
16
+ }
@@ -1,63 +1,4 @@
1
- import type { ArgsClass, ModuleProtocols, ModuleInstance, SchemaInput, TaskArguments, TaskDescriptor, TaskOutput, TokenLike, TokenValue, TriggerDescriptor } from '../core/index.js';
2
- import type { ApplicationGraphIR } from '../graph/index.js';
3
- import type { Logger } from '../runtime/index.js';
4
- export interface ApplicationDefinitionOptions<TModules extends readonly ModuleInstance[], TArguments extends ArgsClass | undefined, TTasks extends readonly TaskDescriptor<any, any>[], TTriggers extends readonly TriggerDescriptor[]> {
5
- readonly modules: TModules;
6
- readonly arguments?: TArguments;
7
- readonly tasks?: TTasks;
8
- readonly triggers?: TTriggers;
9
- readonly logger?: Logger;
10
- }
11
- export interface ApplicationDefinition<TModules extends readonly ModuleInstance[] = readonly ModuleInstance[], TArguments extends ArgsClass | undefined = ArgsClass | undefined, TTasks extends readonly TaskDescriptor<any, any>[] = readonly TaskDescriptor<any, any>[], TTriggers extends readonly TriggerDescriptor[] = readonly TriggerDescriptor[]> {
12
- readonly kind: 'application-definition';
13
- readonly modules: TModules;
14
- readonly arguments: TArguments;
15
- readonly tasks: TTasks;
16
- readonly triggers: TTriggers;
17
- readonly logger?: Logger;
18
- }
19
- export declare function defineApplication<const TModules extends readonly ModuleInstance[], const TArguments extends ArgsClass | undefined = undefined, const TTasks extends readonly TaskDescriptor<any, any>[] = readonly [], const TTriggers extends readonly TriggerDescriptor[] = readonly []>(options: ApplicationDefinitionOptions<TModules, TArguments, TTasks, TTriggers>): ApplicationDefinition<TModules, TArguments, TTasks, TTriggers>;
20
- export type ApplicationArgumentsInput<TDefinition extends ApplicationDefinition> = TDefinition['arguments'] extends ArgsClass<infer TSchema> ? SchemaInput<TSchema> : never;
21
- export type BootstrapArguments<TDefinition extends ApplicationDefinition> = TDefinition['arguments'] extends ArgsClass<infer TSchema> ? {} extends SchemaInput<TSchema> ? {
22
- readonly arguments?: SchemaInput<TSchema>;
23
- } : {
24
- readonly arguments: SchemaInput<TSchema>;
25
- } : {
26
- readonly arguments?: never;
27
- };
28
- export interface BaseApplication {
29
- readonly graph: ApplicationGraphIR;
30
- get<TToken extends TokenLike>(token: TToken): TokenValue<TToken>;
31
- init(): Promise<this>;
32
- close(signal?: string): Promise<void>;
33
- }
34
- export interface TaskApplicationCapability<TTasks extends readonly TaskDescriptor<any, any>[]> {
35
- run<TTask extends TTasks[number]>(task: TTask, ...args: TaskArguments<TTask>): Promise<TaskOutput<TTask>>;
36
- }
37
- export interface HttpApplicationCapability {
38
- fetch(request: Request): Promise<Response>;
39
- }
40
- export interface TriggerApplicationCapability {
41
- readonly triggers: {
42
- start(): Promise<void>;
43
- stop(): Promise<void>;
44
- };
45
- }
46
- export interface QueueConsumerHandle {
47
- stop(): Promise<void>;
48
- }
49
- export interface QueueConsumerDriver {
50
- start(options: {
51
- consume(payload: unknown): Promise<void>;
52
- }): Promise<QueueConsumerHandle>;
53
- }
54
- export type HasProtocol<TDefinition extends ApplicationDefinition, TProtocol extends string> = Extract<ModuleProtocols<TDefinition['modules'][number]>, TProtocol> extends never ? false : true;
55
- export type HasHttp<TDefinition extends ApplicationDefinition> = HasProtocol<TDefinition, 'http'>;
56
- export type HasMessagePort<TDefinition extends ApplicationDefinition> = HasProtocol<TDefinition, 'messagePort'>;
57
- export type HasTasks<TDefinition extends ApplicationDefinition> = TDefinition['tasks'] extends readonly [] ? false : true;
58
- export type HasTriggers<TDefinition extends ApplicationDefinition> = TDefinition['triggers'] extends readonly [] ? false : true;
59
- type TaskCapability<TDefinition extends ApplicationDefinition> = TDefinition['tasks'] extends readonly [] ? {} : TaskApplicationCapability<TDefinition['tasks']>;
60
- export type HostedApplication<TDefinition extends ApplicationDefinition> = BaseApplication & TaskCapability<TDefinition> & (HasHttp<TDefinition> extends true ? HttpApplicationCapability : {}) & (HasTriggers<TDefinition> extends true ? TriggerApplicationCapability : {});
61
- export type InvocationApplication<TDefinition extends ApplicationDefinition> = BaseApplication & TaskCapability<TDefinition>;
62
- export { binding } from './binding.js';
63
- export type { HostBinding, HostBindingApplication, InvocationBinding, InvocationBindingBaseOptions, InvocationBindingOptions, } from './binding.js';
1
+ export { defineApplication } from './definition.js';
2
+ export type { ApplicationArgumentsInput, ApplicationDefinition, ApplicationDefinitionOptions, ApplicationExtensionHostApis, ApplicationExtensions, BootstrapArguments, RequireApplicationExtension, RequireApplicationHost, } from './definition.js';
3
+ export { bootstrapApplication, createKernelApplication } from './kernel.js';
4
+ export type { KernelApplicationBase, KernelApplicationOptions, KernelHostedApplication, } from './kernel.js';
@@ -1,11 +1,2 @@
1
- export function defineApplication(options) {
2
- return Object.freeze({
3
- kind: 'application-definition',
4
- modules: options.modules,
5
- arguments: options.arguments,
6
- tasks: options.tasks ?? [],
7
- triggers: options.triggers ?? [],
8
- ...(options.logger === undefined ? {} : { logger: options.logger }),
9
- });
10
- }
11
- export { binding } from './binding.js';
1
+ export { defineApplication } from './definition.js';
2
+ export { bootstrapApplication, createKernelApplication } from './kernel.js';
@@ -0,0 +1,2 @@
1
+ import { type ApplicationModel, type RuntimeCapabilityBinding } from '../core/index.js';
2
+ export declare function bindApplicationCapability<TValue>(model: ApplicationModel, id: string, value: TValue): RuntimeCapabilityBinding<TValue>;
@@ -0,0 +1,10 @@
1
+ import { bindRuntimeCapability, } from '../core/index.js';
2
+ export function bindApplicationCapability(model, id, value) {
3
+ const capability = model.executions
4
+ .flatMap((execution) => execution.capabilities)
5
+ .find((candidate) => candidate.id === id);
6
+ if (!capability) {
7
+ throw new Error(`LUTRE_CAPABILITY_NOT_REQUIRED: ${id}`);
8
+ }
9
+ return bindRuntimeCapability(capability, value);
10
+ }
@@ -0,0 +1,21 @@
1
+ /// <reference lib="esnext.disposable" preserve="true" />
2
+ import type { RuntimeCapabilityBinding, TokenLike, TokenValue } from '../core/index.js';
3
+ import { type ApplicationModelGraphIR } from '../graph/index.js';
4
+ import { type Logger } from '../runtime/index.js';
5
+ import type { ApplicationDefinition, ApplicationExtensionHostApis, BootstrapArguments } from './definition.js';
6
+ export interface KernelApplicationBase extends AsyncDisposable {
7
+ readonly graph: ApplicationModelGraphIR;
8
+ init(): Promise<this>;
9
+ get<TToken extends TokenLike>(token: TToken): TokenValue<TToken>;
10
+ close(signal?: string): Promise<void>;
11
+ }
12
+ export type KernelHostedApplication<TDefinition extends ApplicationDefinition> = KernelApplicationBase & ApplicationExtensionHostApis<TDefinition>;
13
+ export type KernelApplicationOptions<TDefinition extends ApplicationDefinition> = {
14
+ readonly application: TDefinition;
15
+ readonly capabilities?: readonly RuntimeCapabilityBinding[];
16
+ readonly environment?: unknown;
17
+ readonly logger?: Logger;
18
+ readonly forceShutdownTimeoutMs?: number;
19
+ } & BootstrapArguments<TDefinition>;
20
+ export declare function createKernelApplication<const TDefinition extends ApplicationDefinition>(options: KernelApplicationOptions<TDefinition>): KernelHostedApplication<TDefinition>;
21
+ export declare function bootstrapApplication<const TDefinition extends ApplicationDefinition>(options: KernelApplicationOptions<TDefinition>): Promise<KernelHostedApplication<TDefinition>>;
@@ -0,0 +1,65 @@
1
+ /// <reference lib="esnext.disposable" preserve="true" />
2
+ import { projectApplicationModel, } from '../graph/index.js';
3
+ import { ApplicationKernelRuntime } from '../runtime/index.js';
4
+ export function createKernelApplication(options) {
5
+ const logger = options.logger ?? options.application.logger;
6
+ const runtime = new ApplicationKernelRuntime(options.application.model, {
7
+ ...options,
8
+ ...(logger === undefined ? {} : { logger }),
9
+ ...('environment' in options
10
+ ? { environmentSource: options.environment }
11
+ : {}),
12
+ ...('arguments' in options ? { argumentsSource: options.arguments } : {}),
13
+ });
14
+ let initialized = false;
15
+ const application = Object.assign(Object.create(null), {
16
+ graph: projectApplicationModel(options.application.model),
17
+ async init() {
18
+ await runtime.initialize();
19
+ if (initialized)
20
+ return application;
21
+ const createdNamespaces = [];
22
+ try {
23
+ for (const modelExtension of options.application.model.extensions) {
24
+ const host = modelExtension.extension.host;
25
+ if (!host)
26
+ continue;
27
+ application[host.namespace] = host.create({
28
+ executions: modelExtension.executions,
29
+ runtime: runtime.extensionRuntime(modelExtension.extension),
30
+ applicationRuntime: runtime,
31
+ });
32
+ createdNamespaces.push(host.namespace);
33
+ }
34
+ initialized = true;
35
+ return application;
36
+ }
37
+ catch (error) {
38
+ for (const namespace of createdNamespaces)
39
+ delete application[namespace];
40
+ try {
41
+ await runtime.shutdown();
42
+ }
43
+ catch (cleanupError) {
44
+ throw new AggregateError([error, cleanupError], 'Host API creation failed and runtime rollback also failed.', { cause: cleanupError });
45
+ }
46
+ throw error;
47
+ }
48
+ },
49
+ get(token) {
50
+ return runtime.get(token);
51
+ },
52
+ close(signal) {
53
+ return runtime.shutdown(signal);
54
+ },
55
+ [Symbol.asyncDispose]() {
56
+ return application.close();
57
+ },
58
+ });
59
+ return application;
60
+ }
61
+ export async function bootstrapApplication(options) {
62
+ const application = createKernelApplication(options);
63
+ await application.init();
64
+ return application;
65
+ }
@@ -0,0 +1,9 @@
1
+ export interface Diagnostic {
2
+ readonly code: string;
3
+ readonly message: string;
4
+ readonly path: string;
5
+ readonly severity?: 'error' | 'warning';
6
+ }
7
+ export declare function isErrorDiagnostic(item: Diagnostic): boolean;
8
+ export declare function hasErrorDiagnostics(diagnostics: readonly Diagnostic[]): boolean;
9
+ export declare function diagnostic(code: string, message: string, path: string, severity?: Diagnostic['severity']): Diagnostic;
@@ -0,0 +1,9 @@
1
+ export function isErrorDiagnostic(item) {
2
+ return (item.severity ?? 'error') === 'error';
3
+ }
4
+ export function hasErrorDiagnostics(diagnostics) {
5
+ return diagnostics.some(isErrorDiagnostic);
6
+ }
7
+ export function diagnostic(code, message, path, severity = 'error') {
8
+ return Object.freeze({ code, message, path, severity });
9
+ }