@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,339 +0,0 @@
1
- import { argumentsProvider, loadArgs, loadEnv, normalizeProvider, } from '../core/index.js';
2
- import { collectRuntimeModuleGraph, Container, } from './di.js';
3
- import { Logger } from './logger.js';
4
- const NO_ENVIRONMENT_SOURCE = Symbol('loutre.no-environment-source');
5
- let currentEnvironmentSource = NO_ENVIRONMENT_SOURCE;
6
- export function initializeWithEnvironment(application, environmentSource) {
7
- const previous = currentEnvironmentSource;
8
- currentEnvironmentSource = environmentSource;
9
- try {
10
- return application.initialize();
11
- }
12
- finally {
13
- currentEnvironmentSource = previous;
14
- }
15
- }
16
- export class EnvironmentBindingError extends Error {
17
- code = 'LUTRE_ENV_003';
18
- constructor(environment, cause) {
19
- super(`Environment validation failed for ${environment.name}.`, { cause });
20
- this.name = 'EnvironmentBindingError';
21
- }
22
- }
23
- export class ArgumentsBindingError extends Error {
24
- code = 'LUTRE_ARGS_003';
25
- constructor(argumentsContract, cause) {
26
- super(`Arguments validation failed for ${argumentsContract.name}.`, {
27
- cause,
28
- });
29
- this.name = 'ArgumentsBindingError';
30
- }
31
- }
32
- export class ApplicationRuntime {
33
- graph;
34
- container;
35
- #instancesByModule = new Map();
36
- #logger;
37
- #environmentSource;
38
- #arguments;
39
- #argumentsSource;
40
- #tasks;
41
- #publicTasks;
42
- #initialized = false;
43
- #initializing;
44
- #prepared = false;
45
- #state = 'created';
46
- #activeExecutions = 0;
47
- #idleWaiters = new Set();
48
- #shutdown;
49
- constructor(roots, options = {}) {
50
- this.#logger = options.logger ?? new Logger();
51
- this.#environmentSource =
52
- 'environmentSource' in options
53
- ? options.environmentSource
54
- : NO_ENVIRONMENT_SOURCE;
55
- this.#arguments = options.arguments;
56
- this.#argumentsSource =
57
- 'argumentsSource' in options ? options.argumentsSource : Object.freeze({});
58
- this.#tasks = new Set(options.tasks ?? []);
59
- this.#publicTasks = new Set(options.publicTasks ?? []);
60
- this.graph = collectRuntimeModuleGraph(roots);
61
- const providers = [
62
- ...this.graph.providers,
63
- ...(this.#arguments ? [argumentsProvider(this.#arguments)] : []),
64
- ];
65
- this.container = new Container(providers, {
66
- logger: this.#logger,
67
- });
68
- }
69
- initialize() {
70
- if (this.#initialized)
71
- return Promise.resolve();
72
- if (this.#initializing)
73
- return this.#initializing;
74
- if (this.#state === 'stopping') {
75
- return Promise.reject(applicationStateError('LUTRE_APP_STOPPING'));
76
- }
77
- if (this.#state === 'stopped') {
78
- return Promise.reject(applicationStateError('LUTRE_APP_STOPPED'));
79
- }
80
- const source = this.#environmentSource === NO_ENVIRONMENT_SOURCE
81
- ? currentEnvironmentSource
82
- : this.#environmentSource;
83
- const initialization = this.#initialize(source).finally(() => {
84
- if (this.#initializing === initialization) {
85
- this.#initializing = undefined;
86
- }
87
- });
88
- this.#initializing = initialization;
89
- return initialization;
90
- }
91
- async #initialize(environmentSource) {
92
- this.#state = 'initializing';
93
- try {
94
- await this.#bindEnvironment(environmentSource);
95
- await this.#bindArguments();
96
- this.#prepareRuntime();
97
- for (const module of this.graph.modules) {
98
- const instances = await this.#resolveModuleProviders(module);
99
- this.#instancesByModule.set(module, instances);
100
- for (const instance of instances)
101
- await callLifecycle(instance, 'onModuleInit');
102
- await this.#runHook(module.definition.lifecycle?.onModuleInit);
103
- }
104
- for (const module of this.graph.modules) {
105
- for (const instance of this.#instancesByModule.get(module) ?? []) {
106
- await callLifecycle(instance, 'onApplicationBootstrap');
107
- }
108
- await this.#runHook(module.definition.lifecycle?.onApplicationBootstrap);
109
- }
110
- this.#initialized = true;
111
- if (this.#state !== 'stopping')
112
- this.#state = 'running';
113
- }
114
- catch (error) {
115
- const cleanupErrors = await this.#rollbackInitialization();
116
- this.#state = 'stopped';
117
- if (cleanupErrors.length > 0) {
118
- throw new AggregateError([error, ...cleanupErrors], 'Application initialization failed and rollback cleanup also failed', { cause: error });
119
- }
120
- throw error;
121
- }
122
- }
123
- async #bindEnvironment(source) {
124
- const environmentProviders = this.graph.providers.filter((provider) => provider.kind === 'environment');
125
- if (environmentProviders.length === 0)
126
- return;
127
- if (source === NO_ENVIRONMENT_SOURCE) {
128
- throw new Error('LUTRE_ENV_005: Application declares Environment Contracts, but the Runtime did not provide an Environment source.');
129
- }
130
- for (const provider of environmentProviders) {
131
- try {
132
- const value = await loadEnv(provider.provide, source);
133
- this.container.bindEnvironment(provider.provide, value);
134
- }
135
- catch (error) {
136
- throw new EnvironmentBindingError(provider.provide, error);
137
- }
138
- }
139
- }
140
- async #bindArguments() {
141
- if (!this.#arguments)
142
- return;
143
- try {
144
- const value = await loadArgs(this.#arguments, this.#argumentsSource);
145
- this.container.bindArguments(this.#arguments, value);
146
- }
147
- catch (error) {
148
- throw new ArgumentsBindingError(this.#arguments, error);
149
- }
150
- }
151
- #prepareRuntime() {
152
- if (this.#prepared)
153
- return;
154
- for (const module of this.graph.modules) {
155
- for (const implementation of module.definition.implementations ?? []) {
156
- this.container.prepareImplementation(implementation);
157
- }
158
- }
159
- for (const pipeline of collectApplicationPipelines(this.graph.modules)) {
160
- this.container.preparePipeline(pipeline);
161
- }
162
- for (const task of this.#tasks) {
163
- this.container.prepareTask(task);
164
- }
165
- this.#prepared = true;
166
- }
167
- get(token) {
168
- if (this.#state === 'created' || this.#state === 'initializing') {
169
- throw applicationStateError('LUTRE_APP_NOT_INITIALIZED');
170
- }
171
- if (this.#state === 'stopping') {
172
- throw applicationStateError('LUTRE_APP_STOPPING');
173
- }
174
- if (this.#state === 'stopped') {
175
- throw applicationStateError('LUTRE_APP_STOPPED');
176
- }
177
- return this.container.get(token);
178
- }
179
- run(task, ...args) {
180
- if (!this.#publicTasks.has(task)) {
181
- return Promise.reject(new Error(`LUTRE_TASK_002: Task ${task.name} is not registered in Application.tasks.`));
182
- }
183
- return this.#invokeTask(task, args);
184
- }
185
- /** public run()ではTrigger専用Taskを拒否するため、Trigger Host専用経路を分ける。 */
186
- runTask(task, ...args) {
187
- if (!this.#tasks.has(task)) {
188
- return Promise.reject(new Error(`LUTRE_TASK_NOT_REGISTERED: Task ${task.name} is not registered in this Application.`));
189
- }
190
- return this.#invokeTask(task, args);
191
- }
192
- #invokeTask(task, args) {
193
- return this.execute(async () => {
194
- const runtime = this.container.taskRuntime(task);
195
- return Reflect.apply(runtime, undefined, args);
196
- });
197
- }
198
- async execute(execution) {
199
- if (this.#state === 'stopping')
200
- throw applicationStateError('LUTRE_APP_STOPPING');
201
- if (this.#state === 'stopped')
202
- throw applicationStateError('LUTRE_APP_STOPPED');
203
- this.#activeExecutions += 1;
204
- try {
205
- await this.initialize();
206
- return await execution();
207
- }
208
- finally {
209
- this.#activeExecutions -= 1;
210
- if (this.#activeExecutions === 0) {
211
- for (const resolve of this.#idleWaiters)
212
- resolve();
213
- this.#idleWaiters.clear();
214
- }
215
- }
216
- }
217
- stopAcceptingExecutions() {
218
- if (this.#state !== 'stopped')
219
- this.#state = 'stopping';
220
- }
221
- shutdown(signal) {
222
- if (this.#state === 'stopped')
223
- return Promise.resolve();
224
- if (this.#shutdown)
225
- return this.#shutdown;
226
- this.stopAcceptingExecutions();
227
- const shutdown = this.#shutdownRuntime(signal);
228
- this.#shutdown = shutdown;
229
- return shutdown;
230
- }
231
- async #shutdownRuntime(signal) {
232
- if (this.#initializing) {
233
- await this.#initializing.catch(() => undefined);
234
- }
235
- if (this.#activeExecutions > 0) {
236
- await new Promise((resolve) => this.#idleWaiters.add(resolve));
237
- }
238
- const errors = [];
239
- const modules = this.graph.modules.toReversed();
240
- try {
241
- for (const module of modules) {
242
- for (const instance of (this.#instancesByModule.get(module) ?? []).toReversed()) {
243
- await collectCleanupError(() => callLifecycle(instance, 'onModuleDestroy'), errors);
244
- }
245
- await collectCleanupError(() => this.#runHook(module.definition.lifecycle?.onModuleDestroy), errors);
246
- }
247
- for (const module of modules) {
248
- for (const instance of (this.#instancesByModule.get(module) ?? []).toReversed()) {
249
- await collectCleanupError(() => callLifecycle(instance, 'beforeApplicationShutdown', signal), errors);
250
- }
251
- await collectCleanupError(() => this.#runHook(module.definition.lifecycle?.beforeApplicationShutdown), errors);
252
- }
253
- for (const module of modules) {
254
- for (const instance of (this.#instancesByModule.get(module) ?? []).toReversed()) {
255
- await collectCleanupError(() => callLifecycle(instance, 'onApplicationShutdown', signal), errors);
256
- }
257
- await collectCleanupError(() => this.#runHook(module.definition.lifecycle?.onApplicationShutdown), errors);
258
- }
259
- }
260
- finally {
261
- this.#state = 'stopped';
262
- }
263
- if (errors.length > 0) {
264
- throw new AggregateError(errors, 'Application shutdown cleanup failed');
265
- }
266
- }
267
- async #rollbackInitialization() {
268
- const errors = [];
269
- const modules = [...this.#instancesByModule.keys()].toReversed();
270
- for (const module of modules) {
271
- await collectCleanupError(() => this.#runHook(module.definition.lifecycle?.onModuleDestroy), errors);
272
- for (const instance of (this.#instancesByModule.get(module) ?? []).toReversed()) {
273
- await collectCleanupError(() => callLifecycle(instance, 'onModuleDestroy'), errors);
274
- }
275
- }
276
- return errors;
277
- }
278
- async #resolveModuleProviders(module) {
279
- const providers = (module.definition.providers ?? []).map(normalizeProvider);
280
- const applicationProviders = providers.filter((provider) => provider.scope === 'application' &&
281
- provider.kind !== 'environment' &&
282
- provider.kind !== 'arguments');
283
- const instances = [];
284
- this.#instancesByModule.set(module, instances);
285
- for (const provider of applicationProviders) {
286
- instances.push(this.container.resolve(provider.provide));
287
- }
288
- return instances;
289
- }
290
- async #runHook(hook) {
291
- if (!hook)
292
- return;
293
- const dependencies = hook.inject.map((token) => this.container.resolve(token));
294
- await hook.run(...dependencies);
295
- }
296
- }
297
- function applicationStateError(code) {
298
- const message = code === 'LUTRE_APP_NOT_INITIALIZED'
299
- ? 'Application is not initialized.'
300
- : code === 'LUTRE_APP_STOPPING'
301
- ? 'Application is stopping and cannot accept new executions.'
302
- : 'Application is stopped and cannot accept new executions.';
303
- return new Error(`${code}: ${message}`);
304
- }
305
- function collectApplicationPipelines(modules) {
306
- const pipelines = [];
307
- for (const module of modules) {
308
- for (const implementation of module.definition.implementations ?? []) {
309
- for (const procedureName of implementation.procedures) {
310
- const procedure = implementation.contract.procedures[procedureName];
311
- const protocol = procedure?.protocols[implementation.protocol];
312
- const pipeline = protocol?.pipeline ?? protocol?.definition?.pipeline;
313
- if (pipeline)
314
- pipelines.push(pipeline);
315
- }
316
- }
317
- }
318
- return pipelines;
319
- }
320
- async function collectCleanupError(cleanup, errors) {
321
- try {
322
- await cleanup();
323
- }
324
- catch (error) {
325
- errors.push(error);
326
- }
327
- }
328
- export function createApplicationRuntime(roots, options = {}) {
329
- return new ApplicationRuntime(roots, options);
330
- }
331
- async function callLifecycle(instance, method, signal) {
332
- if (!instance ||
333
- (typeof instance !== 'object' && typeof instance !== 'function'))
334
- return;
335
- const candidate = instance[method];
336
- if (typeof candidate !== 'function')
337
- return;
338
- await Reflect.apply(candidate, instance, signal === undefined ? [] : [signal]);
339
- }
@@ -1,11 +0,0 @@
1
- import { type LayerDescriptor, type LayerRuntime, type PipelineItem, type TerminalLayerDescriptor, type ValidationLayerDescriptor } from '../core/index.js';
2
- export interface PipelineHooks<TContext extends object, TResult> {
3
- readonly context: TContext;
4
- readonly validate: (layer: ValidationLayerDescriptor, context: TContext) => void | Promise<void>;
5
- readonly terminal: (layer: TerminalLayerDescriptor, context: TContext) => TResult | Promise<TResult>;
6
- readonly layer: (descriptor: LayerDescriptor) => LayerRuntime<object, readonly [], unknown>;
7
- }
8
- export declare class LayerContractError extends Error {
9
- constructor(message: string);
10
- }
11
- export declare function executePipeline<TContext extends object, TResult>(pipeline: readonly PipelineItem[], hooks: PipelineHooks<TContext, TResult>): Promise<TResult>;
@@ -1,161 +0,0 @@
1
- import { childPipelineOf, contextKeyName, isShortCircuit, layerDefinitionOf, } from '../core/index.js';
2
- export class LayerContractError extends Error {
3
- constructor(message) {
4
- super(message);
5
- this.name = 'LayerContractError';
6
- }
7
- }
8
- export async function executePipeline(pipeline, hooks) {
9
- const context = hooks.context;
10
- const flow = await executeSegment(pipeline, 0, hooks, context, new Set());
11
- if (flow.kind === 'continue') {
12
- throw new Error('Pipeline did not produce a result');
13
- }
14
- return flow.result;
15
- }
16
- async function executeSegment(pipeline, index, hooks, context, availableKeys) {
17
- const item = pipeline[index];
18
- if (!item)
19
- return { kind: 'continue' };
20
- if (item.kind === 'validation') {
21
- await hooks.validate(item, context);
22
- return executeSegment(pipeline, index + 1, hooks, context, availableKeys);
23
- }
24
- if (item.kind === 'terminal') {
25
- return {
26
- kind: 'complete',
27
- result: await hooks.terminal(item, context),
28
- };
29
- }
30
- const definition = layerDefinitionOf(item);
31
- assertRequiredContext(definition.name, definition.requires, availableKeys);
32
- const child = childPipelineOf(item);
33
- const continuation = child === undefined
34
- ? () => executeSegment(pipeline, index + 1, hooks, context, availableKeys)
35
- : () => executeSegment(child, 0, hooks, context, availableKeys);
36
- const flow = await executeLayer(definition, hooks.layer(definition), continuation, context, availableKeys);
37
- if (flow.kind === 'complete' || child === undefined)
38
- return flow;
39
- return executeSegment(pipeline, index + 1, hooks, context, availableKeys);
40
- }
41
- async function executeLayer(layer, runtime, continuation, context, availableKeys) {
42
- let calls = 0;
43
- let continuationFlow = { kind: 'continue' };
44
- let continuationFailed = false;
45
- let continuationError;
46
- let contractError;
47
- let completion;
48
- const next = (...arguments_) => {
49
- calls += 1;
50
- if (calls > 1) {
51
- contractError = new LayerContractError(`LUTRE_LAYER_NEXT_REENTRY: Layer ${layer.name} can call next() only once`);
52
- return Promise.reject(contractError);
53
- }
54
- applyProvidedContext(layer, context, availableKeys, arguments_);
55
- completion = (async () => {
56
- try {
57
- continuationFlow = await continuation();
58
- }
59
- catch (error) {
60
- continuationFailed = true;
61
- continuationError = error;
62
- throw error;
63
- }
64
- })();
65
- return completion;
66
- };
67
- let runtimeResult;
68
- let runtimeError;
69
- let runtimeFailed = false;
70
- try {
71
- runtimeResult = await Reflect.apply(runtime, undefined, [context, next]);
72
- }
73
- catch (error) {
74
- runtimeFailed = true;
75
- runtimeError = error;
76
- }
77
- if (completion) {
78
- try {
79
- await completion;
80
- }
81
- catch {
82
- // continuation failureはLayerが握り潰しても下で元の値を再throwする。
83
- }
84
- }
85
- if (continuationFailed)
86
- throw continuationError;
87
- if (contractError)
88
- throw contractError;
89
- if (runtimeFailed)
90
- throw runtimeError;
91
- if (isShortCircuit(runtimeResult)) {
92
- if (calls > 0) {
93
- throw new LayerContractError(`LUTRE_LAYER_SHORT_CIRCUIT_AFTER_NEXT: Layer ${layer.name} cannot shortCircuit after next()`);
94
- }
95
- return {
96
- kind: 'complete',
97
- result: runtimeResult.result,
98
- };
99
- }
100
- if (calls === 0) {
101
- throw new LayerContractError(`LUTRE_LAYER_NEXT_SKIPPED: Layer ${layer.name} must call next() exactly once`);
102
- }
103
- return continuationFlow;
104
- }
105
- function assertRequiredContext(layerName, requiredKeys, availableKeys) {
106
- for (const required of requiredKeys) {
107
- if (!availableKeys.has(required)) {
108
- throw new LayerContractError(`Context Key ${contextKeyName(required)} required by ${layerName} is unavailable`);
109
- }
110
- }
111
- }
112
- function applyProvidedContext(layer, context, availableKeys, arguments_) {
113
- if (arguments_.length > 1) {
114
- throw new LayerContractError(`Layer ${layer.name} can pass only one Context object to next()`);
115
- }
116
- if (layer.provides.length === 0) {
117
- if (arguments_.length > 0) {
118
- const provided = arguments_[0];
119
- const property = firstProperty(provided);
120
- throw new LayerContractError(property === undefined
121
- ? `Layer ${layer.name} does not declare any provided Context`
122
- : `Layer ${layer.name} provided undeclared Context property ${property}`);
123
- }
124
- return;
125
- }
126
- const provided = arguments_[0];
127
- if (typeof provided !== 'object' ||
128
- provided === null ||
129
- Array.isArray(provided)) {
130
- throw new LayerContractError(`Layer ${layer.name} must pass its declared Context to next(object)`);
131
- }
132
- const additions = provided;
133
- const declaredNames = new Set(layer.provides.map((key) => key.name));
134
- for (const property of Object.keys(additions)) {
135
- if (!declaredNames.has(property)) {
136
- throw new LayerContractError(`Layer ${layer.name} provided undeclared Context property ${property}`);
137
- }
138
- }
139
- const providedNames = new Set();
140
- for (const key of layer.provides) {
141
- if (providedNames.has(key.name)) {
142
- throw new LayerContractError(`Layer ${layer.name} declared duplicate Context property ${key.name}`);
143
- }
144
- providedNames.add(key.name);
145
- if (!Object.hasOwn(additions, key.name)) {
146
- throw new LayerContractError(`Layer ${layer.name} did not provide declared Context Key ${contextKeyName(key)}`);
147
- }
148
- if (Object.hasOwn(context, key.name)) {
149
- throw new LayerContractError(`Layer ${layer.name} cannot overwrite existing Context property ${key.name}`);
150
- }
151
- }
152
- Object.assign(context, additions);
153
- for (const key of layer.provides)
154
- availableKeys.add(key);
155
- }
156
- function firstProperty(value) {
157
- if (typeof value !== 'object' || value === null || Array.isArray(value)) {
158
- return undefined;
159
- }
160
- return Object.keys(value)[0];
161
- }
File without changes