@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
@@ -0,0 +1,346 @@
1
+ import { diagnostic, isErrorDiagnostic } from './diagnostic.js';
2
+ import { isExecutionDefinition, } from './extension.js';
3
+ import { isFrameworkProvidedToken } from './token-internal.js';
4
+ import { tokenName } from './token.js';
5
+ import { createModelBuildContext } from './model/context.js';
6
+ import { resolveLifecycleDependencies } from './model/lifecycle.js';
7
+ import { collectModuleDeclarations } from './model/modules.js';
8
+ import { compileArgumentsProvider, resolveModuleExports, resolveProviderDependencies, } from './model/providers.js';
9
+ import { snapshotExecutionExtension } from './model/snapshot.js';
10
+ import { validateHostNamespaces, validateNodeIds } from './model/validation.js';
11
+ import { getSourceLocation } from './source-location.js';
12
+ import { isTokenVisible, moduleDeclaresToken } from './model/visibility.js';
13
+ class ApplicationModelExtensionRegistry {
14
+ #ordered;
15
+ #groups;
16
+ constructor(groups) {
17
+ this.#ordered = Object.freeze([...groups]);
18
+ this.#groups = new Map(groups.map((group) => [group.extension.identity, group]));
19
+ }
20
+ get(extension) {
21
+ return this.#groups.get(extension.identity);
22
+ }
23
+ values() {
24
+ return this.#ordered;
25
+ }
26
+ [Symbol.iterator]() {
27
+ return this.#ordered[Symbol.iterator]();
28
+ }
29
+ }
30
+ export class ApplicationModelError extends Error {
31
+ diagnostics;
32
+ code = 'LUTRE_APPLICATION_MODEL_INVALID';
33
+ constructor(diagnostics) {
34
+ super(`Application Model is invalid:\n${diagnostics
35
+ .map((item) => `${item.code} at ${item.path}: ${item.message}`)
36
+ .join('\n')}`);
37
+ this.diagnostics = diagnostics;
38
+ this.name = 'ApplicationModelError';
39
+ }
40
+ }
41
+ export function assertValidApplicationModel(model) {
42
+ const errors = model.diagnostics.filter(isErrorDiagnostic);
43
+ if (errors.length > 0)
44
+ throw new ApplicationModelError(errors);
45
+ return model;
46
+ }
47
+ export function buildApplicationModel(options) {
48
+ const context = createModelBuildContext(options);
49
+ collectModuleDeclarations(context);
50
+ resolveModuleExports(context);
51
+ compileArgumentsProvider(context);
52
+ resolveProviderDependencies(context);
53
+ resolveLifecycleDependencies(context);
54
+ const { modules, moduleIds, nodes, edges, diagnostics, providers, executions, providerNodes, } = context;
55
+ const extensionExecutions = new Map();
56
+ const extensionNames = new Map();
57
+ const extensionIdentities = new Map();
58
+ const executionIds = new Set();
59
+ const capabilityNodeIds = new Set();
60
+ const visitedDefinitions = new Set();
61
+ const compiledDefinitions = new Map();
62
+ const executionReferences = [];
63
+ const executionReferenceResults = new Map();
64
+ const getExecutionReferences = (definition) => {
65
+ const cached = executionReferenceResults.get(definition);
66
+ if (cached)
67
+ return cached;
68
+ const references = definition.extension.references;
69
+ if (!references) {
70
+ const result = { references: Object.freeze([]) };
71
+ executionReferenceResults.set(definition, result);
72
+ return result;
73
+ }
74
+ try {
75
+ const result = {
76
+ references: references(definition),
77
+ };
78
+ executionReferenceResults.set(definition, result);
79
+ return result;
80
+ }
81
+ catch (error) {
82
+ const result = { error };
83
+ executionReferenceResults.set(definition, result);
84
+ return result;
85
+ }
86
+ };
87
+ const rootDefinitionOwners = new Map();
88
+ for (const module of modules) {
89
+ for (const value of module.definition.executions ?? []) {
90
+ if (!isExecutionDefinition(value))
91
+ continue;
92
+ const owners = rootDefinitionOwners.get(value) ?? new Set();
93
+ owners.add(module);
94
+ rootDefinitionOwners.set(value, owners);
95
+ }
96
+ }
97
+ const reachableDefinitionModules = new Map();
98
+ for (const module of modules) {
99
+ const queue = (module.definition.executions ?? []).filter(isExecutionDefinition);
100
+ const seen = new Set();
101
+ let queueIndex = 0;
102
+ while (queueIndex < queue.length) {
103
+ const value = queue[queueIndex++];
104
+ if (seen.has(value))
105
+ continue;
106
+ seen.add(value);
107
+ const explicitOwners = rootDefinitionOwners.get(value);
108
+ if (explicitOwners && !explicitOwners.has(module))
109
+ continue;
110
+ const reachableModules = reachableDefinitionModules.get(value) ?? new Set();
111
+ reachableModules.add(module);
112
+ reachableDefinitionModules.set(value, reachableModules);
113
+ const referenceResult = getExecutionReferences(value);
114
+ if ('error' in referenceResult)
115
+ continue;
116
+ for (const reference of referenceResult.references) {
117
+ if (!isExecutionDefinition(reference))
118
+ continue;
119
+ if (reference.extension.identity !== value.extension.identity)
120
+ continue;
121
+ queue.push(reference);
122
+ }
123
+ }
124
+ }
125
+ const definitionOwners = new Map();
126
+ const ambiguousDefinitions = new Set();
127
+ for (const [definition, explicitOwners] of rootDefinitionOwners) {
128
+ if (explicitOwners.size === 1) {
129
+ definitionOwners.set(definition, [...explicitOwners][0]);
130
+ }
131
+ else {
132
+ ambiguousDefinitions.add(definition);
133
+ }
134
+ }
135
+ for (const [definition, reachableModules] of reachableDefinitionModules) {
136
+ if (rootDefinitionOwners.has(definition))
137
+ continue;
138
+ if (reachableModules.size === 1) {
139
+ definitionOwners.set(definition, [...reachableModules][0]);
140
+ }
141
+ else {
142
+ ambiguousDefinitions.add(definition);
143
+ }
144
+ }
145
+ if (ambiguousDefinitions.size > 0) {
146
+ diagnostics.push(diagnostic('LUTRE_EXECUTION_OWNER_AMBIGUOUS', 'Execution Definition ownership is ambiguous across multiple Modules. Register each shared Definition in exactly one Module.executions; other Modules may reference it.', 'application.executions'));
147
+ }
148
+ for (const module of modules) {
149
+ const moduleId = moduleIds.get(module);
150
+ const rootDefinitions = module.definition.executions ?? [];
151
+ const rootDefinitionIndexes = new Map();
152
+ for (const [definitionIndex, value] of rootDefinitions.entries()) {
153
+ if (isExecutionDefinition(value) && !rootDefinitionIndexes.has(value)) {
154
+ rootDefinitionIndexes.set(value, definitionIndex);
155
+ }
156
+ }
157
+ const discoveredDefinitionIndexes = new Map();
158
+ const queue = rootDefinitions.map((value, definitionIndex) => ({
159
+ value,
160
+ definitionIndex,
161
+ path: `${moduleId}.executions.${definitionIndex}`,
162
+ }));
163
+ let nextDiscoveredDefinitionIndex = rootDefinitions.length;
164
+ let queueIndex = 0;
165
+ while (queueIndex < queue.length) {
166
+ const { value, definitionIndex, path } = queue[queueIndex++];
167
+ if (!isExecutionDefinition(value)) {
168
+ diagnostics.push(diagnostic('LUTRE_EXECUTION_DEFINITION_INVALID', 'Module executions and referenced executions accept only branded Execution Definitions.', path));
169
+ continue;
170
+ }
171
+ if (ambiguousDefinitions.has(value))
172
+ continue;
173
+ if (definitionOwners.get(value) !== module)
174
+ continue;
175
+ if (visitedDefinitions.has(value))
176
+ continue;
177
+ visitedDefinitions.add(value);
178
+ const extension = value.extension;
179
+ const namedIdentity = extensionNames.get(extension.name);
180
+ if (namedIdentity && namedIdentity.identity !== extension.identity) {
181
+ diagnostics.push(diagnostic('LUTRE_EXTENSION_ABI_MISMATCH', `Extension ${extension.name} mixes incompatible ABI versions ${namedIdentity.abiVersion} and ${extension.abiVersion}.`, path));
182
+ continue;
183
+ }
184
+ const identityOwner = extensionIdentities.get(extension.identity);
185
+ if (identityOwner && identityOwner.name !== extension.name) {
186
+ diagnostics.push(diagnostic('LUTRE_EXTENSION_IDENTITY_COLLISION', `Extension identity is shared by ${identityOwner.name} and ${extension.name}.`, path));
187
+ continue;
188
+ }
189
+ const canonicalExtension = identityOwner?.extension ?? snapshotExecutionExtension(extension);
190
+ extensionNames.set(extension.name, {
191
+ identity: extension.identity,
192
+ abiVersion: extension.abiVersion,
193
+ });
194
+ extensionIdentities.set(extension.identity, {
195
+ name: extension.name,
196
+ extension: canonicalExtension,
197
+ });
198
+ let contribution;
199
+ try {
200
+ contribution = extension.compile(value, {
201
+ moduleId,
202
+ definitionIndex,
203
+ });
204
+ }
205
+ catch (error) {
206
+ diagnostics.push(diagnostic('LUTRE_EXTENSION_COMPILE', describeError(error), path));
207
+ continue;
208
+ }
209
+ if (executionIds.has(contribution.id)) {
210
+ diagnostics.push(diagnostic('LUTRE_EXECUTION_ID_COLLISION', `Execution id ${contribution.id} is already registered.`, path));
211
+ continue;
212
+ }
213
+ executionIds.add(contribution.id);
214
+ const executionSource = getSourceLocation(value);
215
+ const execution = Object.freeze({
216
+ ...contribution,
217
+ dependencies: Object.freeze([...contribution.dependencies]),
218
+ capabilities: Object.freeze([...contribution.capabilities]),
219
+ moduleId,
220
+ ...(executionSource === undefined ? {} : { source: executionSource }),
221
+ });
222
+ compiledDefinitions.set(value, execution);
223
+ executions.push(execution);
224
+ nodes.push(execution);
225
+ edges.push({ from: moduleId, to: execution.id, kind: 'owns' });
226
+ const grouped = extensionExecutions.get(extension.identity) ?? {
227
+ extension: canonicalExtension,
228
+ executions: [],
229
+ };
230
+ grouped.executions.push(execution);
231
+ extensionExecutions.set(extension.identity, grouped);
232
+ for (const dependency of execution.dependencies) {
233
+ const provider = providerNodes.get(dependency);
234
+ if (provider) {
235
+ edges.push({ from: execution.id, to: provider.id, kind: 'injects' });
236
+ const providerModule = modules.find((candidate) => moduleIds.get(candidate) === provider.moduleId);
237
+ if (providerModule &&
238
+ provider.moduleId !== moduleId &&
239
+ !moduleDeclaresToken(module, dependency) &&
240
+ !isTokenVisible(module, providerModule, dependency)) {
241
+ diagnostics.push(diagnostic('LUTRE_MODULE_VISIBILITY', `Execution ${execution.id} depends on private ${tokenName(dependency)} from another Module.`, execution.id));
242
+ }
243
+ }
244
+ else if (!isFrameworkProvidedToken(dependency)) {
245
+ diagnostics.push(diagnostic('LUTRE_EXECUTION_DEPENDENCY_MISSING', `Execution ${execution.id} requires ${tokenName(dependency)}, but no provider is declared.`, execution.id));
246
+ }
247
+ }
248
+ for (const capability of execution.capabilities) {
249
+ const capabilityId = `capability:${capability.id}`;
250
+ if (!capabilityNodeIds.has(capabilityId)) {
251
+ capabilityNodeIds.add(capabilityId);
252
+ nodes.push({
253
+ kind: 'framework',
254
+ id: capabilityId,
255
+ frameworkKind: 'runtime-capability',
256
+ name: capability.id,
257
+ });
258
+ }
259
+ edges.push({
260
+ from: execution.id,
261
+ to: capabilityId,
262
+ kind: 'requires',
263
+ });
264
+ }
265
+ const referenceResult = getExecutionReferences(value);
266
+ if ('error' in referenceResult) {
267
+ diagnostics.push(diagnostic('LUTRE_EXTENSION_REFERENCES', describeError(referenceResult.error), path));
268
+ continue;
269
+ }
270
+ for (const [referenceIndex, reference,] of referenceResult.references.entries()) {
271
+ const referencePath = `${path}.references.${referenceIndex}`;
272
+ if (!isExecutionDefinition(reference)) {
273
+ diagnostics.push(diagnostic('LUTRE_EXECUTION_REFERENCE_INVALID', 'Execution references accept only branded Execution Definitions.', referencePath));
274
+ continue;
275
+ }
276
+ if (reference.extension.identity !== extension.identity) {
277
+ diagnostics.push(diagnostic('LUTRE_EXECUTION_REFERENCE_EXTENSION', `Execution ${execution.id} cannot reference an execution owned by ${reference.extension.name}.`, referencePath));
278
+ continue;
279
+ }
280
+ executionReferences.push({ from: value, to: reference });
281
+ let referencedDefinitionIndex = rootDefinitionIndexes.get(reference);
282
+ if (referencedDefinitionIndex === undefined) {
283
+ referencedDefinitionIndex = discoveredDefinitionIndexes.get(reference);
284
+ if (referencedDefinitionIndex === undefined) {
285
+ referencedDefinitionIndex = nextDiscoveredDefinitionIndex++;
286
+ discoveredDefinitionIndexes.set(reference, referencedDefinitionIndex);
287
+ }
288
+ }
289
+ queue.push({
290
+ value: reference,
291
+ definitionIndex: referencedDefinitionIndex,
292
+ path: referencePath,
293
+ });
294
+ }
295
+ }
296
+ }
297
+ for (const reference of executionReferences) {
298
+ const from = compiledDefinitions.get(reference.from);
299
+ const to = compiledDefinitions.get(reference.to);
300
+ if (!from || !to)
301
+ continue;
302
+ edges.push({ from: from.id, to: to.id, kind: 'references' });
303
+ }
304
+ const extensionGroups = [...extensionExecutions.values()].map(({ extension, executions: ownedExecutions }) => {
305
+ const extensionId = `extension:${extension.name}`;
306
+ nodes.push({
307
+ kind: 'framework',
308
+ id: extensionId,
309
+ frameworkKind: 'execution-extension',
310
+ name: extension.name,
311
+ });
312
+ for (const execution of ownedExecutions) {
313
+ edges.push({ from: extensionId, to: execution.id, kind: 'starts' });
314
+ }
315
+ if (extension.validate) {
316
+ try {
317
+ diagnostics.push(...extension.validate({ executions: ownedExecutions }));
318
+ }
319
+ catch (error) {
320
+ diagnostics.push(diagnostic('LUTRE_EXTENSION_VALIDATE', describeError(error), extensionId));
321
+ }
322
+ }
323
+ return Object.freeze({
324
+ extension,
325
+ executions: Object.freeze([...ownedExecutions]),
326
+ });
327
+ });
328
+ const extensions = new ApplicationModelExtensionRegistry(extensionGroups);
329
+ validateHostNamespaces(extensionGroups, diagnostics);
330
+ validateNodeIds(nodes, diagnostics);
331
+ return Object.freeze({
332
+ kind: 'application-model',
333
+ ...(options.arguments === undefined
334
+ ? {}
335
+ : { arguments: options.arguments }),
336
+ providers: Object.freeze(providers),
337
+ nodes: Object.freeze(nodes.map((node) => Object.freeze(node))),
338
+ edges: Object.freeze(edges.map((edge) => Object.freeze(edge))),
339
+ executions: Object.freeze(executions),
340
+ extensions,
341
+ diagnostics: Object.freeze(diagnostics.map((item) => Object.freeze({ ...item }))),
342
+ });
343
+ }
344
+ function describeError(error) {
345
+ return error instanceof Error ? error.message : String(error);
346
+ }
@@ -1,5 +1,5 @@
1
1
  import type { EnvClass } from './env.js';
2
- import type { ImplementationDescriptor } from './implementation.js';
2
+ import type { ExecutionDefinition, ExtensionOfDefinition, HostApiOfExtension } from './extension.js';
3
3
  import type { ModuleLifecycle } from './lifecycle.js';
4
4
  import { type ProviderDeclaration } from './provider.js';
5
5
  export interface ModuleDefinition {
@@ -8,24 +8,19 @@ export interface ModuleDefinition {
8
8
  readonly imports?: readonly ModuleInstance[];
9
9
  readonly environment?: readonly EnvClass[];
10
10
  readonly providers?: readonly ProviderDeclaration[];
11
- readonly implementations?: readonly ImplementationDescriptor[];
11
+ readonly executions?: readonly ExecutionDefinition[];
12
12
  readonly exports?: readonly unknown[];
13
13
  readonly lifecycle?: ModuleLifecycle;
14
- readonly requires?: readonly string[];
15
14
  }
16
15
  export declare const moduleTypeInfo: unique symbol;
17
16
  export interface ModuleTypeInfo<TDefinition extends ModuleDefinition = ModuleDefinition> {
18
- readonly protocols: ProtocolsOfModuleDefinition<TDefinition>;
19
- readonly capabilities: CapabilitiesOfModuleDefinition<TDefinition>;
17
+ readonly extensions: ExtensionsOfModuleDefinition<TDefinition>;
20
18
  }
21
- type DirectProtocols<TDefinition extends ModuleDefinition> = TDefinition['implementations'] extends readonly ImplementationDescriptor[] ? TDefinition['implementations'][number]['protocol'] : never;
22
- type ImportedProtocols<TDefinition extends ModuleDefinition> = TDefinition['imports'] extends readonly ModuleInstance[] ? ModuleProtocols<TDefinition['imports'][number]> : never;
23
- export type ProtocolsOfModuleDefinition<TDefinition extends ModuleDefinition> = DirectProtocols<TDefinition> | ImportedProtocols<TDefinition>;
24
- type DirectCapabilities<TDefinition extends ModuleDefinition> = TDefinition['implementations'] extends readonly ImplementationDescriptor[] ? TDefinition['implementations'][number]['capabilities'][number] : never;
25
- type ImportedCapabilities<TDefinition extends ModuleDefinition> = TDefinition['imports'] extends readonly ModuleInstance[] ? ModuleCapabilities<TDefinition['imports'][number]> : never;
26
- export type CapabilitiesOfModuleDefinition<TDefinition extends ModuleDefinition> = DirectCapabilities<TDefinition> | ImportedCapabilities<TDefinition>;
27
- export type ModuleProtocols<TModule> = TModule extends ModuleInstance<infer TDefinition> ? ProtocolsOfModuleDefinition<TDefinition> : never;
28
- export type ModuleCapabilities<TModule> = TModule extends ModuleInstance<infer TDefinition> ? CapabilitiesOfModuleDefinition<TDefinition> : never;
19
+ type DirectExtensions<TDefinition extends ModuleDefinition> = TDefinition['executions'] extends readonly ExecutionDefinition[] ? ExtensionOfDefinition<TDefinition['executions'][number]> : never;
20
+ type ImportedExtensions<TDefinition extends ModuleDefinition> = TDefinition['imports'] extends readonly ModuleInstance[] ? ModuleExtensions<TDefinition['imports'][number]> : never;
21
+ export type ExtensionsOfModuleDefinition<TDefinition extends ModuleDefinition> = DirectExtensions<TDefinition> | ImportedExtensions<TDefinition>;
22
+ export type ModuleExtensions<TModule> = TModule extends ModuleInstance<infer TDefinition> ? ExtensionsOfModuleDefinition<TDefinition> : never;
23
+ export type ModuleHostApis<TModule> = HostApiOfExtension<ModuleExtensions<TModule>>;
29
24
  export interface ModuleInstance<TDefinition extends ModuleDefinition = ModuleDefinition> {
30
25
  readonly kind: 'module-instance';
31
26
  readonly template: AnyModuleTemplate;
@@ -0,0 +1,8 @@
1
+ export interface SourceLocation {
2
+ readonly file: string;
3
+ readonly line?: number;
4
+ readonly column?: number;
5
+ }
6
+ export declare function registerSourceLocation<T>(value: T, source: SourceLocation): T;
7
+ export declare function getSourceLocation(value: unknown): SourceLocation | undefined;
8
+ export declare function inheritSourceLocation<T>(target: T, source: unknown): T;
@@ -0,0 +1,31 @@
1
+ const sourceMetadata = new WeakMap();
2
+ function asObject(value) {
3
+ return (typeof value === 'object' && value !== null) ||
4
+ typeof value === 'function'
5
+ ? value
6
+ : undefined;
7
+ }
8
+ function snapshotSourceLocation(source) {
9
+ return Object.freeze({
10
+ file: source.file,
11
+ ...(source.line === undefined ? {} : { line: source.line }),
12
+ ...(source.column === undefined ? {} : { column: source.column }),
13
+ });
14
+ }
15
+ export function registerSourceLocation(value, source) {
16
+ const target = asObject(value);
17
+ if (!target || sourceMetadata.has(target))
18
+ return value;
19
+ sourceMetadata.set(target, snapshotSourceLocation(source));
20
+ return value;
21
+ }
22
+ export function getSourceLocation(value) {
23
+ const target = asObject(value);
24
+ return target ? sourceMetadata.get(target) : undefined;
25
+ }
26
+ export function inheritSourceLocation(target, source) {
27
+ const location = getSourceLocation(source);
28
+ return location === undefined
29
+ ? target
30
+ : registerSourceLocation(target, location);
31
+ }
@@ -0,0 +1,3 @@
1
+ import type { TokenLike } from './token.js';
2
+ export declare const frameworkProvidedTokenBrand: unique symbol;
3
+ export declare function isFrameworkProvidedToken(token: TokenLike): boolean;
@@ -0,0 +1,4 @@
1
+ export const frameworkProvidedTokenBrand = Symbol.for('loutre.framework-provided-token');
2
+ export function isFrameworkProvidedToken(token) {
3
+ return (token[frameworkProvidedTokenBrand] === true);
4
+ }
@@ -0,0 +1,7 @@
1
+ declare const typeValue: unique symbol;
2
+ export interface Type<T> {
3
+ readonly [typeValue]: T;
4
+ }
5
+ export type TypeOf<TType> = TType extends Type<infer TValue> ? TValue : never;
6
+ export declare function type<T>(): Type<T>;
7
+ export {};
@@ -0,0 +1,4 @@
1
+ const typeCarrier = Object.freeze({});
2
+ export function type() {
3
+ return typeCarrier;
4
+ }
@@ -1 +1 @@
1
- export declare const LOUTRE_VERSION = "0.4.1";
1
+ export declare const LOUTRE_VERSION = "0.6.0";
@@ -1,2 +1,2 @@
1
1
  // package manifestとのずれを避けるため、scripts/generate-version.mjsがbuild前に更新する。
2
- export const LOUTRE_VERSION = '0.4.1';
2
+ export const LOUTRE_VERSION = '0.6.0';
@@ -1,3 +1,2 @@
1
- export * from './ir.js';
2
- export { StaticValidationError, assertValidCompilation, validateGraph, type ApplicationCompilationInput, } from './graph.js';
3
- export { buildApplicationGraph, compileApplication } from './compiler.js';
1
+ export { projectApplicationModel } from './model.js';
2
+ export type { ApplicationModelGraphIR, GraphEdgeIR, GraphNodeIR, JsonPrimitive, JsonValue, } from './model.js';
@@ -1,3 +1 @@
1
- export * from './ir.js';
2
- export { StaticValidationError, assertValidCompilation, validateGraph, } from './graph.js';
3
- export { buildApplicationGraph, compileApplication } from './compiler.js';
1
+ export { projectApplicationModel } from './model.js';
@@ -0,0 +1,34 @@
1
+ import { type ApplicationModel, type ApplicationModelEdge, type ApplicationModelNode, type Diagnostic, type SourceLocation } from '../core/index.js';
2
+ export type JsonPrimitive = string | number | boolean | null;
3
+ export type JsonValue = JsonPrimitive | {
4
+ readonly [key: string]: JsonValue;
5
+ } | readonly JsonValue[];
6
+ export interface GraphNodeIR {
7
+ readonly id: string;
8
+ readonly kind: ApplicationModelNode['kind'];
9
+ readonly name?: string;
10
+ readonly module?: string;
11
+ readonly executionKind?: string;
12
+ readonly capabilities?: readonly string[];
13
+ readonly extension?: {
14
+ readonly name: string;
15
+ readonly hostNamespace?: string;
16
+ readonly metadata?: JsonValue;
17
+ };
18
+ readonly attributes?: Readonly<Record<string, JsonValue>>;
19
+ readonly source?: SourceLocation;
20
+ }
21
+ export interface GraphEdgeIR {
22
+ readonly from: string;
23
+ readonly to: string;
24
+ readonly kind: ApplicationModelEdge['kind'];
25
+ }
26
+ export interface ApplicationModelGraphIR {
27
+ readonly nodes: readonly GraphNodeIR[];
28
+ readonly edges: readonly GraphEdgeIR[];
29
+ readonly diagnostics: readonly Diagnostic[];
30
+ readonly modules: readonly GraphNodeIR[];
31
+ readonly providers: readonly GraphNodeIR[];
32
+ readonly executions: readonly GraphNodeIR[];
33
+ }
34
+ export declare function projectApplicationModel(model: ApplicationModel): ApplicationModelGraphIR;
@@ -0,0 +1,127 @@
1
+ import { diagnostic, tokenName, } from '../core/index.js';
2
+ export function projectApplicationModel(model) {
3
+ const diagnostics = [...model.diagnostics];
4
+ const projectedExecutions = new Map();
5
+ for (const group of model.extensions) {
6
+ for (const execution of projectExtensionGroup(group, diagnostics)) {
7
+ projectedExecutions.set(execution.id, execution);
8
+ }
9
+ }
10
+ const nodes = model.nodes.map((node) => node.kind === 'execution'
11
+ ? (projectedExecutions.get(node.id) ?? projectExecutionBase(node))
12
+ : projectCoreNode(node));
13
+ const edges = model.edges.map((edge) => ({ ...edge }));
14
+ return Object.freeze({
15
+ nodes: Object.freeze(nodes),
16
+ edges: Object.freeze(edges),
17
+ diagnostics: Object.freeze(diagnostics),
18
+ modules: Object.freeze(nodes.filter((node) => node.kind === 'module')),
19
+ providers: Object.freeze(nodes.filter((node) => node.kind === 'provider')),
20
+ executions: Object.freeze(nodes.filter((node) => node.kind === 'execution')),
21
+ });
22
+ }
23
+ function projectCoreNode(node) {
24
+ switch (node.kind) {
25
+ case 'module':
26
+ return {
27
+ id: node.id,
28
+ kind: node.kind,
29
+ ...(node.source === undefined ? {} : { source: node.source }),
30
+ ...(node.name === undefined ? {} : { name: node.name }),
31
+ ...(node.description === undefined
32
+ ? {}
33
+ : { attributes: { description: node.description } }),
34
+ };
35
+ case 'provider':
36
+ return {
37
+ id: node.id,
38
+ kind: node.kind,
39
+ name: tokenName(node.token),
40
+ module: node.moduleId,
41
+ ...(node.source === undefined ? {} : { source: node.source }),
42
+ attributes: {
43
+ providerKind: node.provider.kind,
44
+ scope: node.provider.scope,
45
+ },
46
+ };
47
+ case 'lifecycle':
48
+ return {
49
+ id: node.id,
50
+ kind: node.kind,
51
+ name: node.phase,
52
+ module: node.moduleId,
53
+ ...(node.source === undefined ? {} : { source: node.source }),
54
+ };
55
+ case 'framework':
56
+ return {
57
+ id: node.id,
58
+ kind: node.kind,
59
+ name: node.name,
60
+ ...(node.source === undefined ? {} : { source: node.source }),
61
+ attributes: { frameworkKind: node.frameworkKind },
62
+ };
63
+ }
64
+ }
65
+ function projectExtensionGroup(group, diagnostics) {
66
+ return group.executions.map((execution) => {
67
+ let metadata;
68
+ const project = group.extension.projectGraph;
69
+ if (project) {
70
+ try {
71
+ const projected = project({ execution });
72
+ if (projected !== undefined) {
73
+ if (!isJsonValue(projected)) {
74
+ diagnostics.push(diagnostic('LUTRE_EXTENSION_PROJECTION_NOT_SERIALIZABLE', `Extension ${group.extension.name} returned non-serializable metadata.`, execution.id));
75
+ }
76
+ else {
77
+ metadata = projected;
78
+ }
79
+ }
80
+ }
81
+ catch (error) {
82
+ diagnostics.push(diagnostic('LUTRE_EXTENSION_PROJECTION', error instanceof Error ? error.message : String(error), execution.id));
83
+ }
84
+ }
85
+ return {
86
+ ...projectExecutionBase(execution),
87
+ extension: {
88
+ name: group.extension.name,
89
+ ...(group.extension.host === undefined
90
+ ? {}
91
+ : { hostNamespace: group.extension.host.namespace }),
92
+ ...(metadata === undefined ? {} : { metadata }),
93
+ },
94
+ };
95
+ });
96
+ }
97
+ function projectExecutionBase(execution) {
98
+ return {
99
+ id: execution.id,
100
+ kind: execution.kind,
101
+ module: execution.moduleId,
102
+ executionKind: execution.executionKind,
103
+ capabilities: execution.capabilities.map((capability) => capability.id),
104
+ ...(execution.source === undefined ? {} : { source: execution.source }),
105
+ };
106
+ }
107
+ function isJsonValue(value, ancestors = new Set()) {
108
+ if (value === null ||
109
+ typeof value === 'string' ||
110
+ typeof value === 'boolean') {
111
+ return true;
112
+ }
113
+ if (typeof value === 'number')
114
+ return Number.isFinite(value);
115
+ if (typeof value !== 'object')
116
+ return false;
117
+ if (ancestors.has(value))
118
+ return false;
119
+ const nextAncestors = new Set(ancestors).add(value);
120
+ if (Array.isArray(value)) {
121
+ return value.every((item) => isJsonValue(item, nextAncestors));
122
+ }
123
+ const prototype = Object.getPrototypeOf(value);
124
+ if (prototype !== Object.prototype && prototype !== null)
125
+ return false;
126
+ return Object.values(value).every((item) => isJsonValue(item, nextAncestors));
127
+ }