@loutrejs/loutre 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/aws-lambda.d.ts +52 -0
- package/dist/adapters/aws-lambda.js +114 -0
- package/dist/adapters/bun.d.ts +20 -0
- package/dist/adapters/bun.js +82 -0
- package/dist/adapters/cloudflare-workers.d.ts +18 -0
- package/dist/adapters/cloudflare-workers.js +55 -0
- package/dist/adapters/deno.d.ts +28 -0
- package/dist/adapters/deno.js +106 -0
- package/dist/adapters/electron.d.ts +27 -0
- package/dist/adapters/electron.js +52 -0
- package/dist/application/binding.d.ts +33 -0
- package/dist/application/binding.js +292 -0
- package/dist/application/host.d.ts +8 -0
- package/dist/application/host.js +14 -0
- package/dist/application/index.d.ts +62 -0
- package/dist/application/index.js +11 -0
- package/dist/application/openapi.d.ts +28 -0
- package/dist/application/openapi.js +362 -0
- package/dist/application/queue.d.ts +3 -0
- package/dist/application/queue.js +4 -0
- package/dist/application/scheduler.d.ts +2 -0
- package/dist/application/scheduler.js +45 -0
- package/dist/core/args.d.ts +16 -0
- package/dist/core/args.js +29 -0
- package/dist/core/context-key.d.ts +14 -0
- package/dist/core/context-key.js +14 -0
- package/dist/core/contract.d.ts +54 -0
- package/dist/core/contract.js +30 -0
- package/dist/core/env.d.ts +16 -0
- package/dist/core/env.js +29 -0
- package/dist/core/error.d.ts +24 -0
- package/dist/core/error.js +25 -0
- package/dist/core/execution.d.ts +78 -0
- package/dist/core/execution.js +61 -0
- package/dist/core/implementation.d.ts +34 -0
- package/dist/core/implementation.js +30 -0
- package/dist/core/index.d.ts +15 -0
- package/dist/core/index.js +15 -0
- package/dist/core/injection.d.ts +28 -0
- package/dist/core/injection.js +35 -0
- package/dist/core/layer.d.ts +109 -0
- package/dist/core/layer.js +52 -0
- package/dist/core/lifecycle.d.ts +34 -0
- package/dist/core/lifecycle.js +3 -0
- package/dist/core/module.d.ts +53 -0
- package/dist/core/module.js +31 -0
- package/dist/core/provider.d.ts +57 -0
- package/dist/core/provider.js +63 -0
- package/dist/core/runtime-input.d.ts +11 -0
- package/dist/core/runtime-input.js +1 -0
- package/dist/core/schema.d.ts +13 -0
- package/dist/core/schema.js +20 -0
- package/dist/core/token.d.ts +16 -0
- package/dist/core/token.js +13 -0
- package/dist/graph/graph.d.ts +17 -0
- package/dist/graph/graph.js +1078 -0
- package/dist/graph/index.d.ts +2 -0
- package/dist/graph/index.js +2 -0
- package/dist/graph/ir.d.ts +159 -0
- package/dist/graph/ir.js +1 -0
- package/dist/http/application.d.ts +18 -0
- package/dist/http/application.js +506 -0
- package/dist/http/basic-auth.d.ts +37 -0
- package/dist/http/basic-auth.js +67 -0
- package/dist/http/cors.d.ts +17 -0
- package/dist/http/cors.js +171 -0
- package/dist/http/definitions.d.ts +217 -0
- package/dist/http/definitions.js +65 -0
- package/dist/http/hosted-protocol.d.ts +4 -0
- package/dist/http/hosted-protocol.js +4 -0
- package/dist/http/index.d.ts +7 -0
- package/dist/http/index.js +6 -0
- package/dist/http/params.d.ts +2 -0
- package/dist/http/params.js +19 -0
- package/dist/http/path.d.ts +55 -0
- package/dist/http/path.js +88 -0
- package/dist/http/request-body.d.ts +4 -0
- package/dist/http/request-body.js +6 -0
- package/dist/http/validate.d.ts +20 -0
- package/dist/http/validate.js +6 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/message-port/environment.d.ts +2 -0
- package/dist/message-port/environment.js +4 -0
- package/dist/message-port/index.d.ts +65 -0
- package/dist/message-port/index.js +182 -0
- package/dist/runtime/application.d.ts +37 -0
- package/dist/runtime/application.js +324 -0
- package/dist/runtime/capability.d.ts +16 -0
- package/dist/runtime/capability.js +18 -0
- package/dist/runtime/di.d.ts +37 -0
- package/dist/runtime/di.js +441 -0
- package/dist/runtime/error.d.ts +10 -0
- package/dist/runtime/error.js +15 -0
- package/dist/runtime/index.d.ts +7 -0
- package/dist/runtime/index.js +7 -0
- package/dist/runtime/logger.d.ts +36 -0
- package/dist/runtime/logger.js +207 -0
- package/dist/runtime/node-capabilities.d.ts +5 -0
- package/dist/runtime/node-capabilities.js +18 -0
- package/dist/runtime/pipeline.d.ts +11 -0
- package/dist/runtime/pipeline.js +161 -0
- package/package.json +81 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
export class ConsoleLoggerBackend {
|
|
2
|
+
#colors;
|
|
3
|
+
#prefix;
|
|
4
|
+
constructor(options = {}) {
|
|
5
|
+
this.#colors = options.colors ?? detectConsoleColors();
|
|
6
|
+
this.#prefix = options.prefix ?? 'Loutre';
|
|
7
|
+
}
|
|
8
|
+
write(record) {
|
|
9
|
+
writeToConsole(record.level, formatConsoleLogRecord(record, this.#prefix, this.#colors));
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class JsonConsoleLoggerBackend {
|
|
13
|
+
write(record) {
|
|
14
|
+
writeToConsole(record.level, serializeLogRecord(record));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
class SilentLoggerBackend {
|
|
18
|
+
write(_record) { }
|
|
19
|
+
}
|
|
20
|
+
export class Logger {
|
|
21
|
+
backend;
|
|
22
|
+
context;
|
|
23
|
+
constructor(backend = new ConsoleLoggerBackend(), context = {}) {
|
|
24
|
+
this.backend = backend;
|
|
25
|
+
this.context = context;
|
|
26
|
+
}
|
|
27
|
+
child(context) {
|
|
28
|
+
return new Logger(this.backend, { ...this.context, ...context });
|
|
29
|
+
}
|
|
30
|
+
debug(message, context = {}) {
|
|
31
|
+
this.#write('debug', message, context);
|
|
32
|
+
}
|
|
33
|
+
info(message, context = {}) {
|
|
34
|
+
this.#write('info', message, context);
|
|
35
|
+
}
|
|
36
|
+
warn(message, context = {}) {
|
|
37
|
+
this.#write('warn', message, context);
|
|
38
|
+
}
|
|
39
|
+
error(message, context = {}) {
|
|
40
|
+
this.#write('error', message, context);
|
|
41
|
+
}
|
|
42
|
+
#write(level, message, context) {
|
|
43
|
+
this.backend.write({
|
|
44
|
+
...this.context,
|
|
45
|
+
...context,
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
47
|
+
level,
|
|
48
|
+
message,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export class SilentLogger extends Logger {
|
|
53
|
+
constructor(context = {}) {
|
|
54
|
+
super(new SilentLoggerBackend(), context);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
function serializeLogRecord(record) {
|
|
58
|
+
const seen = new WeakSet();
|
|
59
|
+
try {
|
|
60
|
+
return JSON.stringify(record, (_key, value) => {
|
|
61
|
+
if (typeof value === 'bigint')
|
|
62
|
+
return value.toString();
|
|
63
|
+
if (value instanceof Error) {
|
|
64
|
+
return {
|
|
65
|
+
name: value.name,
|
|
66
|
+
message: value.message,
|
|
67
|
+
...(value.stack === undefined ? {} : { stack: value.stack }),
|
|
68
|
+
...(value.cause === undefined ? {} : { cause: value.cause }),
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
if (value !== null && typeof value === 'object') {
|
|
72
|
+
if (seen.has(value))
|
|
73
|
+
return '[Circular]';
|
|
74
|
+
seen.add(value);
|
|
75
|
+
}
|
|
76
|
+
return value;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
return JSON.stringify({
|
|
81
|
+
timestamp: record.timestamp,
|
|
82
|
+
level: 'error',
|
|
83
|
+
message: 'Log record serialization failed',
|
|
84
|
+
serializationError: error instanceof Error ? error.message : String(error),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function writeToConsole(level, value) {
|
|
89
|
+
const target = level === 'error'
|
|
90
|
+
? console.error
|
|
91
|
+
: level === 'warn'
|
|
92
|
+
? console.warn
|
|
93
|
+
: console.log;
|
|
94
|
+
target(value);
|
|
95
|
+
}
|
|
96
|
+
function formatConsoleLogRecord(record, prefix, colors) {
|
|
97
|
+
const { timestamp, level, message, ...recordContext } = record;
|
|
98
|
+
const context = { ...recordContext };
|
|
99
|
+
const source = typeof context.source === 'string'
|
|
100
|
+
? context.source
|
|
101
|
+
: typeof context.protocol === 'string'
|
|
102
|
+
? context.protocol
|
|
103
|
+
: undefined;
|
|
104
|
+
if (source !== undefined)
|
|
105
|
+
delete context.source;
|
|
106
|
+
const runtimeProcess = getRuntimeProcess();
|
|
107
|
+
const processLabel = runtimeProcess?.pid === undefined
|
|
108
|
+
? `[${prefix}]`
|
|
109
|
+
: `[${prefix}] ${runtimeProcess.pid}`;
|
|
110
|
+
const levelLabel = level === 'info' ? 'LOG' : level.toUpperCase();
|
|
111
|
+
const levelColor = level === 'error' ? 31 : level === 'warn' ? 33 : level === 'debug' ? 36 : 32;
|
|
112
|
+
const sourceLabel = source === undefined ? '' : `${paint(`[${source}]`, 33, colors)} `;
|
|
113
|
+
const formattedContext = formatLogContext(context);
|
|
114
|
+
const contextSuffix = formattedContext === '' ? '' : ` ${paint(formattedContext, 90, colors)}`;
|
|
115
|
+
return `${paint(processLabel, 32, colors)} - ${formatLocalTimestamp(timestamp)} ${paint(levelLabel.padStart(7), levelColor, colors)} ${sourceLabel}${paint(message, levelColor, colors)}${contextSuffix}`;
|
|
116
|
+
}
|
|
117
|
+
function formatLogContext(context) {
|
|
118
|
+
const entries = Object.entries(context);
|
|
119
|
+
if (entries.length === 0)
|
|
120
|
+
return '';
|
|
121
|
+
return `{ ${entries
|
|
122
|
+
.map(([key, value]) => `${formatLogKey(key)}=${formatLogValue(value)}`)
|
|
123
|
+
.join(', ')} }`;
|
|
124
|
+
}
|
|
125
|
+
function formatLogKey(key) {
|
|
126
|
+
return /^[A-Za-z_$][\w$]*$/u.test(key) ? key : JSON.stringify(key);
|
|
127
|
+
}
|
|
128
|
+
function formatLogValue(value, ancestors = new WeakSet()) {
|
|
129
|
+
try {
|
|
130
|
+
if (value === null)
|
|
131
|
+
return 'null';
|
|
132
|
+
if (value === undefined)
|
|
133
|
+
return 'undefined';
|
|
134
|
+
if (typeof value === 'string') {
|
|
135
|
+
return /^[\w./:@+-]+$/u.test(value) ? value : JSON.stringify(value);
|
|
136
|
+
}
|
|
137
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
138
|
+
return String(value);
|
|
139
|
+
if (typeof value === 'bigint')
|
|
140
|
+
return `${value}n`;
|
|
141
|
+
if (typeof value === 'symbol')
|
|
142
|
+
return String(value);
|
|
143
|
+
if (typeof value === 'function')
|
|
144
|
+
return `[Function ${value.name || 'anonymous'}]`;
|
|
145
|
+
if (ancestors.has(value))
|
|
146
|
+
return '[Circular]';
|
|
147
|
+
ancestors.add(value);
|
|
148
|
+
try {
|
|
149
|
+
if (value instanceof Date)
|
|
150
|
+
return value.toISOString();
|
|
151
|
+
if (value instanceof Error) {
|
|
152
|
+
return formatLogValue({
|
|
153
|
+
name: value.name,
|
|
154
|
+
message: value.message,
|
|
155
|
+
...(value.stack === undefined ? {} : { stack: value.stack }),
|
|
156
|
+
...(value.cause === undefined ? {} : { cause: value.cause }),
|
|
157
|
+
}, ancestors);
|
|
158
|
+
}
|
|
159
|
+
if (Array.isArray(value)) {
|
|
160
|
+
return `[${value.map((item) => formatLogValue(item, ancestors)).join(', ')}]`;
|
|
161
|
+
}
|
|
162
|
+
return `{ ${Object.entries(value)
|
|
163
|
+
.map(([key, item]) => `${formatLogKey(key)}=${formatLogValue(item, ancestors)}`)
|
|
164
|
+
.join(', ')} }`;
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
ancestors.delete(value);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (error) {
|
|
171
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
172
|
+
return `[Unformattable: ${message}]`;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function formatLocalTimestamp(timestamp) {
|
|
176
|
+
const date = new Date(timestamp);
|
|
177
|
+
if (Number.isNaN(date.getTime()))
|
|
178
|
+
return timestamp;
|
|
179
|
+
return ([
|
|
180
|
+
date.getFullYear(),
|
|
181
|
+
String(date.getMonth() + 1).padStart(2, '0'),
|
|
182
|
+
String(date.getDate()).padStart(2, '0'),
|
|
183
|
+
].join('/') +
|
|
184
|
+
` ${[
|
|
185
|
+
String(date.getHours()).padStart(2, '0'),
|
|
186
|
+
String(date.getMinutes()).padStart(2, '0'),
|
|
187
|
+
String(date.getSeconds()).padStart(2, '0'),
|
|
188
|
+
].join(':')}.${String(date.getMilliseconds()).padStart(3, '0')}`);
|
|
189
|
+
}
|
|
190
|
+
function paint(value, color, enabled) {
|
|
191
|
+
return enabled ? `\u001B[${color}m${value}\u001B[39m` : value;
|
|
192
|
+
}
|
|
193
|
+
function getRuntimeProcess() {
|
|
194
|
+
return globalThis.process;
|
|
195
|
+
}
|
|
196
|
+
function detectConsoleColors() {
|
|
197
|
+
const runtimeProcess = getRuntimeProcess();
|
|
198
|
+
const environment = runtimeProcess?.env;
|
|
199
|
+
if (environment?.NO_COLOR !== undefined ||
|
|
200
|
+
environment?.NODE_DISABLE_COLORS !== undefined) {
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
if (environment?.FORCE_COLOR !== undefined) {
|
|
204
|
+
return environment.FORCE_COLOR !== '0';
|
|
205
|
+
}
|
|
206
|
+
return runtimeProcess?.stdout?.isTTY === true;
|
|
207
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** CLIが`@loutrejs/node`へ依存するとBun/DenoでもNode.js専用moduleを評価するため、Capability metadataだけをruntime-neutral側へ分離する。 */
|
|
2
|
+
export const nodeRuntimeCapabilities = {
|
|
3
|
+
runtime: 'node',
|
|
4
|
+
capabilities: new Set([
|
|
5
|
+
'http.server',
|
|
6
|
+
'http.request.streaming',
|
|
7
|
+
'http.response.streaming',
|
|
8
|
+
'stream.readable',
|
|
9
|
+
'stream.writable',
|
|
10
|
+
'messagePort.send',
|
|
11
|
+
'messagePort.receive',
|
|
12
|
+
'messagePort.transfer',
|
|
13
|
+
'runtime.longLived',
|
|
14
|
+
'runtime.shutdownHook',
|
|
15
|
+
'env.runtime',
|
|
16
|
+
'crypto.random',
|
|
17
|
+
]),
|
|
18
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
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>;
|
|
@@ -0,0 +1,161 @@
|
|
|
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が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}のnext()は1回だけ実行できます`);
|
|
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}はnext()後にshortCircuitできません`);
|
|
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}はnext()を1回実行する必要があります`);
|
|
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(`${layerName}が必要とするContext Key ${contextKeyName(required)}は利用できません`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function applyProvidedContext(layer, context, availableKeys, arguments_) {
|
|
113
|
+
if (arguments_.length > 1) {
|
|
114
|
+
throw new LayerContractError(`${layer.name}のnext()へ渡せるContext objectは1つだけです`);
|
|
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.name}はContextをprovideすると宣言していません`
|
|
122
|
+
: `${layer.name}が未宣言のContext property ${property}をprovideしました`);
|
|
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.name}はprovidesで宣言したContextを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.name}が未宣言のContext property ${property}をprovideしました`);
|
|
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.name}がContext property ${key.name}を重複して宣言しました`);
|
|
143
|
+
}
|
|
144
|
+
providedNames.add(key.name);
|
|
145
|
+
if (!Object.hasOwn(additions, key.name)) {
|
|
146
|
+
throw new LayerContractError(`${layer.name}が宣言したContext Key ${contextKeyName(key)}をprovideしませんでした`);
|
|
147
|
+
}
|
|
148
|
+
if (Object.hasOwn(context, key.name)) {
|
|
149
|
+
throw new LayerContractError(`${layer.name}は既存の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
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@loutrejs/loutre",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"./host": {
|
|
16
|
+
"types": "./dist/application/host.d.ts",
|
|
17
|
+
"import": "./dist/application/host.js"
|
|
18
|
+
},
|
|
19
|
+
"./binding": {
|
|
20
|
+
"types": "./dist/application/binding.d.ts",
|
|
21
|
+
"import": "./dist/application/binding.js"
|
|
22
|
+
},
|
|
23
|
+
"./openapi": {
|
|
24
|
+
"types": "./dist/application/openapi.d.ts",
|
|
25
|
+
"import": "./dist/application/openapi.js"
|
|
26
|
+
},
|
|
27
|
+
"./graph": {
|
|
28
|
+
"types": "./dist/graph/index.d.ts",
|
|
29
|
+
"import": "./dist/graph/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./runtime": {
|
|
32
|
+
"types": "./dist/runtime/index.d.ts",
|
|
33
|
+
"import": "./dist/runtime/index.js"
|
|
34
|
+
},
|
|
35
|
+
"./http": {
|
|
36
|
+
"types": "./dist/http/index.d.ts",
|
|
37
|
+
"import": "./dist/http/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./message-port": {
|
|
40
|
+
"types": "./dist/message-port/index.d.ts",
|
|
41
|
+
"import": "./dist/message-port/index.js"
|
|
42
|
+
},
|
|
43
|
+
"./message-port/environment": {
|
|
44
|
+
"types": "./dist/message-port/environment.d.ts",
|
|
45
|
+
"import": "./dist/message-port/environment.js"
|
|
46
|
+
},
|
|
47
|
+
"./runtime/bun": {
|
|
48
|
+
"types": "./dist/adapters/bun.d.ts",
|
|
49
|
+
"import": "./dist/adapters/bun.js"
|
|
50
|
+
},
|
|
51
|
+
"./runtime/deno": {
|
|
52
|
+
"types": "./dist/adapters/deno.d.ts",
|
|
53
|
+
"import": "./dist/adapters/deno.js"
|
|
54
|
+
},
|
|
55
|
+
"./runtime/cloudflare-workers": {
|
|
56
|
+
"types": "./dist/adapters/cloudflare-workers.d.ts",
|
|
57
|
+
"import": "./dist/adapters/cloudflare-workers.js"
|
|
58
|
+
},
|
|
59
|
+
"./runtime/aws-lambda": {
|
|
60
|
+
"types": "./dist/adapters/aws-lambda.d.ts",
|
|
61
|
+
"import": "./dist/adapters/aws-lambda.js"
|
|
62
|
+
},
|
|
63
|
+
"./runtime/electron": {
|
|
64
|
+
"types": "./dist/adapters/electron.d.ts",
|
|
65
|
+
"import": "./dist/adapters/electron.js"
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
"sideEffects": false,
|
|
69
|
+
"publishConfig": {
|
|
70
|
+
"access": "public",
|
|
71
|
+
"provenance": true
|
|
72
|
+
},
|
|
73
|
+
"dependencies": {
|
|
74
|
+
"@standard-schema/spec": "^1.1.0"
|
|
75
|
+
},
|
|
76
|
+
"repository": {
|
|
77
|
+
"type": "git",
|
|
78
|
+
"url": "https://github.com/come25136/loutrejs",
|
|
79
|
+
"directory": "packages/loutre"
|
|
80
|
+
}
|
|
81
|
+
}
|