@loutrejs/loutre 0.3.0 → 0.4.1

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.
@@ -1,22 +1,25 @@
1
1
  import { type ApplicationDefinition, type BootstrapArguments, type HasHttp, type HostBindingApplication } from '../application/index.js';
2
2
  type IsAny<TValue> = 0 extends 1 & TValue ? true : false;
3
3
  type HttpApplication<TDefinition extends ApplicationDefinition> = IsAny<TDefinition> extends true ? TDefinition : HasHttp<TDefinition> extends true ? TDefinition : never;
4
- export type BunServeOptions<TDefinition extends ApplicationDefinition> = {
4
+ export type BunCreateOptions<TDefinition extends ApplicationDefinition> = {
5
5
  readonly application: HttpApplication<TDefinition>;
6
+ readonly environment?: unknown;
7
+ } & BootstrapArguments<TDefinition>;
8
+ export interface BunServeOptions {
6
9
  readonly port?: number;
7
10
  readonly hostname?: string;
8
11
  readonly shutdownHooks?: boolean;
9
- readonly environment?: unknown;
10
- } & BootstrapArguments<TDefinition>;
11
- export interface BunServeHandle<TDefinition extends ApplicationDefinition = ApplicationDefinition> {
12
- readonly application: HostBindingApplication<TDefinition>;
12
+ }
13
+ export interface BunListenerHandle {
13
14
  readonly port: number;
14
- close(signal?: string): Promise<void>;
15
15
  }
16
+ export type BunRuntimeApplication<TDefinition extends ApplicationDefinition = ApplicationDefinition> = HostBindingApplication<TDefinition> & {
17
+ serve(options?: BunServeOptions): Promise<BunListenerHandle>;
18
+ };
16
19
  export declare const bunRuntime: {
17
20
  readonly runtime: 'bun';
18
21
  readonly capabilities: Set<string>;
19
- readonly serve: typeof serve;
22
+ readonly create: typeof create;
20
23
  };
21
- declare function serve<const TDefinition extends ApplicationDefinition>(options: BunServeOptions<TDefinition>): Promise<BunServeHandle<TDefinition>>;
24
+ declare function create<const TDefinition extends ApplicationDefinition>(options: BunCreateOptions<TDefinition>): Promise<BunRuntimeApplication<TDefinition>>;
22
25
  export {};
@@ -15,16 +15,13 @@ export const bunRuntime = {
15
15
  'env.runtime',
16
16
  'crypto.random',
17
17
  ]),
18
- serve,
18
+ create,
19
19
  };
20
- async function serve(options) {
20
+ async function create(options) {
21
21
  assertRuntimeEngine('bun');
22
22
  const bun = bunGlobal();
23
- if (!bun?.serve) {
24
- throw new Error('LUTRE_BUN_UNAVAILABLE: Bun.serve() is not available.');
25
- }
26
23
  const startedAt = performance.now();
27
- const environment = bun.env ?? {};
24
+ const environment = bun?.env ?? {};
28
25
  const presentation = startStartupPresentation({ version: LOUTRE_VERSION }, {
29
26
  terminal: detectPresentationTerminal(process.stdout, environment),
30
27
  write: (value) => console.log(value),
@@ -37,53 +34,32 @@ async function serve(options) {
37
34
  const http = 'http' in host ? host.http : undefined;
38
35
  if (!http) {
39
36
  await host.application.close();
40
- throw new Error('LUTRE_RUNTIME_HTTP_REQUIRED: bunRuntime.serve() requires an HTTP-capable Application.');
37
+ throw new Error('LUTRE_RUNTIME_HTTP_REQUIRED: bunRuntime.create() requires an HTTP-capable Application.');
41
38
  }
42
39
  await host.application.init();
43
- if ('triggers' in host.application)
44
- await host.application.triggers.start();
40
+ const application = host.application;
41
+ const closeApplication = host.application.close.bind(host.application);
45
42
  let server;
46
- const requestedPort = options.port;
47
- let port = requestedPort ?? 3000;
48
- while (true) {
49
- try {
50
- server = bun.serve({
51
- port,
52
- ...(options.hostname === undefined
53
- ? {}
54
- : { hostname: options.hostname }),
55
- fetch: createBunFetchDriver(http),
56
- });
57
- break;
58
- }
59
- catch (error) {
60
- if (requestedPort !== undefined || !canRetryOnNextPort(error, port)) {
61
- await host.application.close().catch(() => undefined);
62
- throw error;
63
- }
64
- port += 1;
65
- }
66
- }
67
- presentation.ready({
68
- server: serverUrl(options.hostname, port),
69
- runtime: `Bun ${bun.version ?? 'unknown'}`,
70
- environment: environment.NODE_ENV ?? 'development',
71
- startupDurationMs: performance.now() - startedAt,
72
- });
43
+ let removeShutdownHooks;
44
+ let serving = false;
73
45
  let closed = false;
74
- const closeRuntime = async (signal) => {
46
+ const close = async (signal) => {
75
47
  if (closed)
76
48
  return;
77
49
  closed = true;
50
+ removeShutdownHooks?.();
51
+ removeShutdownHooks = undefined;
78
52
  const errors = [];
79
- try {
80
- await server.stop(true);
81
- }
82
- catch (error) {
83
- errors.push(error);
53
+ if (server) {
54
+ try {
55
+ await server.stop(true);
56
+ }
57
+ catch (error) {
58
+ errors.push(error);
59
+ }
84
60
  }
85
61
  try {
86
- await host.application.close(signal);
62
+ await closeApplication(signal);
87
63
  }
88
64
  catch (error) {
89
65
  errors.push(error);
@@ -91,14 +67,60 @@ async function serve(options) {
91
67
  if (errors.length > 0)
92
68
  throw new AggregateError(errors, 'Bun runtime shutdown failed');
93
69
  };
94
- const removeShutdownHooks = options.shutdownHooks === false
95
- ? undefined
96
- : registerBunShutdownHooks(closeRuntime);
97
- const close = async (signal) => {
98
- removeShutdownHooks?.();
99
- await closeRuntime(signal);
70
+ const serve = async (serveOptions = {}) => {
71
+ if (closed) {
72
+ throw new Error('LUTRE_APP_STOPPED: Application is stopped.');
73
+ }
74
+ if (serving) {
75
+ throw new Error('LUTRE_RUNTIME_ALREADY_SERVING: Bun runtime Application is already serving.');
76
+ }
77
+ if (!bun?.serve) {
78
+ await close().catch(() => undefined);
79
+ throw new Error('LUTRE_BUN_UNAVAILABLE: Bun.serve() is not available.');
80
+ }
81
+ serving = true;
82
+ try {
83
+ if ('triggers' in application)
84
+ await application.triggers.start();
85
+ const requestedPort = serveOptions.port;
86
+ let port = requestedPort ?? 3000;
87
+ while (true) {
88
+ try {
89
+ server = bun.serve({
90
+ port,
91
+ ...(serveOptions.hostname === undefined
92
+ ? {}
93
+ : { hostname: serveOptions.hostname }),
94
+ fetch: createBunFetchDriver(http),
95
+ });
96
+ break;
97
+ }
98
+ catch (error) {
99
+ if (requestedPort !== undefined || !canRetryOnNextPort(error, port)) {
100
+ throw error;
101
+ }
102
+ port += 1;
103
+ }
104
+ }
105
+ presentation.ready({
106
+ server: serverUrl(serveOptions.hostname, port),
107
+ runtime: `Bun ${bun.version ?? 'unknown'}`,
108
+ environment: environment.NODE_ENV ?? 'development',
109
+ startupDurationMs: performance.now() - startedAt,
110
+ });
111
+ removeShutdownHooks =
112
+ serveOptions.shutdownHooks === false
113
+ ? undefined
114
+ : registerBunShutdownHooks(close);
115
+ return { port };
116
+ }
117
+ catch (error) {
118
+ await close().catch(() => undefined);
119
+ throw error;
120
+ }
100
121
  };
101
- return { application: host.application, port, close };
122
+ Object.assign(application, { serve, close });
123
+ return application;
102
124
  }
103
125
  function registerBunShutdownHooks(close) {
104
126
  const handlers = new Map();
@@ -5,26 +5,28 @@ export type DenoRuntimeOptions<TDefinition extends ApplicationDefinition> = {
5
5
  readonly application: HttpApplication<TDefinition>;
6
6
  readonly environment?: unknown;
7
7
  } & BootstrapArguments<TDefinition>;
8
- export type DenoServeOptions<TDefinition extends ApplicationDefinition> = DenoRuntimeOptions<TDefinition> & {
8
+ export type DenoCreateOptions<TDefinition extends ApplicationDefinition> = DenoRuntimeOptions<TDefinition>;
9
+ export interface DenoServeOptions {
9
10
  readonly port?: number;
10
11
  readonly hostname?: string;
11
12
  readonly shutdownHooks?: boolean;
12
- };
13
+ }
13
14
  export interface DenoBinding {
14
15
  fetch(request: Request): Promise<Response>;
15
16
  close(signal?: string): Promise<void>;
16
17
  }
17
- export interface DenoServeHandle<TDefinition extends ApplicationDefinition = ApplicationDefinition> {
18
- readonly application: HostBindingApplication<TDefinition>;
18
+ export interface DenoListenerHandle {
19
19
  readonly port: number;
20
- close(signal?: string): Promise<void>;
21
20
  }
21
+ export type DenoRuntimeApplication<TDefinition extends ApplicationDefinition = ApplicationDefinition> = HostBindingApplication<TDefinition> & {
22
+ serve(options?: DenoServeOptions): Promise<DenoListenerHandle>;
23
+ };
22
24
  export declare const denoRuntime: {
23
25
  readonly runtime: 'deno';
24
26
  readonly capabilities: Set<string>;
25
27
  readonly bind: typeof bind;
26
- readonly serve: typeof serve;
28
+ readonly create: typeof create;
27
29
  };
28
30
  declare function bind<const TDefinition extends ApplicationDefinition>(options: DenoRuntimeOptions<TDefinition>): DenoBinding;
29
- declare function serve<const TDefinition extends ApplicationDefinition>(options: DenoServeOptions<TDefinition>): Promise<DenoServeHandle<TDefinition>>;
31
+ declare function create<const TDefinition extends ApplicationDefinition>(options: DenoCreateOptions<TDefinition>): Promise<DenoRuntimeApplication<TDefinition>>;
30
32
  export {};
@@ -16,7 +16,7 @@ export const denoRuntime = {
16
16
  'crypto.random',
17
17
  ]),
18
18
  bind,
19
- serve,
19
+ create,
20
20
  };
21
21
  function bind(options) {
22
22
  assertRuntimeEngine('deno');
@@ -34,18 +34,15 @@ function bind(options) {
34
34
  close: (signal) => invocation.application.close(signal),
35
35
  };
36
36
  }
37
- async function serve(options) {
37
+ async function create(options) {
38
38
  assertRuntimeEngine('deno');
39
39
  const deno = denoGlobal();
40
- if (!deno?.serve) {
41
- throw new Error('LUTRE_DENO_UNAVAILABLE: Deno.serve() is not available.');
42
- }
43
40
  const startedAt = performance.now();
44
- const isTTY = deno.stdout?.isTerminal?.() === true;
41
+ const isTTY = deno?.stdout?.isTerminal?.() === true;
45
42
  const presentation = startStartupPresentation({ version: LOUTRE_VERSION }, {
46
43
  terminal: {
47
44
  isTTY,
48
- color: isTTY && deno.env?.get?.('NO_COLOR') === undefined,
45
+ color: isTTY && deno?.env?.get?.('NO_COLOR') === undefined,
49
46
  },
50
47
  write: (value) => console.log(value),
51
48
  });
@@ -53,55 +50,32 @@ async function serve(options) {
53
50
  const http = 'http' in host ? host.http : undefined;
54
51
  if (!http) {
55
52
  await host.application.close();
56
- throw new Error('LUTRE_RUNTIME_HTTP_REQUIRED: denoRuntime.serve() requires an HTTP-capable Application.');
53
+ throw new Error('LUTRE_RUNTIME_HTTP_REQUIRED: denoRuntime.create() requires an HTTP-capable Application.');
57
54
  }
58
55
  await host.application.init();
59
- if ('triggers' in host.application)
60
- await host.application.triggers.start();
56
+ const application = host.application;
57
+ const closeApplication = host.application.close.bind(host.application);
61
58
  let server;
62
- const requestedPort = options.port;
63
- let port = requestedPort ?? 3000;
64
- while (true) {
65
- try {
66
- server = deno.serve({
67
- port,
68
- ...(options.hostname === undefined
69
- ? {}
70
- : { hostname: options.hostname }),
71
- onListen: () => undefined,
72
- }, createDenoFetchDriver(http));
73
- break;
74
- }
75
- catch (error) {
76
- if (requestedPort !== undefined || !canRetryOnNextPort(error, port)) {
77
- await host.application.close().catch(() => undefined);
78
- throw error;
79
- }
80
- port += 1;
81
- }
82
- }
83
- presentation.ready({
84
- server: serverUrl(options.hostname, port),
85
- runtime: `Deno ${deno.version?.deno ?? 'unknown'}`,
86
- environment: deno.env?.get?.('DENO_ENV') ??
87
- deno.env?.get?.('NODE_ENV') ??
88
- 'development',
89
- startupDurationMs: performance.now() - startedAt,
90
- });
59
+ let removeShutdownHooks;
60
+ let serving = false;
91
61
  let closed = false;
92
- const closeRuntime = async (signal) => {
62
+ const close = async (signal) => {
93
63
  if (closed)
94
64
  return;
95
65
  closed = true;
66
+ removeShutdownHooks?.();
67
+ removeShutdownHooks = undefined;
96
68
  const errors = [];
97
- try {
98
- await server.shutdown();
99
- }
100
- catch (error) {
101
- errors.push(error);
69
+ if (server) {
70
+ try {
71
+ await server.shutdown();
72
+ }
73
+ catch (error) {
74
+ errors.push(error);
75
+ }
102
76
  }
103
77
  try {
104
- await host.application.close(signal);
78
+ await closeApplication(signal);
105
79
  }
106
80
  catch (error) {
107
81
  errors.push(error);
@@ -109,14 +83,62 @@ async function serve(options) {
109
83
  if (errors.length > 0)
110
84
  throw new AggregateError(errors, 'Deno runtime shutdown failed');
111
85
  };
112
- const removeShutdownHooks = options.shutdownHooks === false
113
- ? undefined
114
- : registerDenoShutdownHooks(deno, closeRuntime);
115
- const close = async (signal) => {
116
- removeShutdownHooks?.();
117
- await closeRuntime(signal);
86
+ const serve = async (serveOptions = {}) => {
87
+ if (closed) {
88
+ throw new Error('LUTRE_APP_STOPPED: Application is stopped.');
89
+ }
90
+ if (serving) {
91
+ throw new Error('LUTRE_RUNTIME_ALREADY_SERVING: Deno runtime Application is already serving.');
92
+ }
93
+ if (!deno?.serve) {
94
+ await close().catch(() => undefined);
95
+ throw new Error('LUTRE_DENO_UNAVAILABLE: Deno.serve() is not available.');
96
+ }
97
+ serving = true;
98
+ try {
99
+ if ('triggers' in application)
100
+ await application.triggers.start();
101
+ const requestedPort = serveOptions.port;
102
+ let port = requestedPort ?? 3000;
103
+ while (true) {
104
+ try {
105
+ server = deno.serve({
106
+ port,
107
+ ...(serveOptions.hostname === undefined
108
+ ? {}
109
+ : { hostname: serveOptions.hostname }),
110
+ onListen: () => undefined,
111
+ }, createDenoFetchDriver(http));
112
+ break;
113
+ }
114
+ catch (error) {
115
+ if (requestedPort !== undefined || !canRetryOnNextPort(error, port)) {
116
+ throw error;
117
+ }
118
+ port += 1;
119
+ }
120
+ }
121
+ presentation.ready({
122
+ server: serverUrl(serveOptions.hostname, port),
123
+ runtime: `Deno ${deno.version?.deno ?? 'unknown'}`,
124
+ environment: deno.env?.get?.('DENO_ENV') ??
125
+ deno.env?.get?.('NODE_ENV') ??
126
+ 'development',
127
+ startupDurationMs: performance.now() - startedAt,
128
+ });
129
+ removeShutdownHooks =
130
+ serveOptions.shutdownHooks === false
131
+ ? undefined
132
+ : registerDenoShutdownHooks(deno, close);
133
+ return { port };
134
+ }
135
+ catch (error) {
136
+ await close().catch(() => undefined);
137
+ throw error;
138
+ }
118
139
  };
119
- return { application: host.application, port, close };
140
+ Object.assign(application, { serve, close });
141
+ return application;
120
142
  }
121
143
  function registerDenoShutdownHooks(deno, close) {
122
144
  if (!deno.addSignalListener || !deno.removeSignalListener) {
@@ -109,6 +109,9 @@ function createRuntimeState(options) {
109
109
  function createInvocationApplication(state) {
110
110
  const application = {
111
111
  graph: state.graph,
112
+ get(token) {
113
+ return state.runtime.get(token);
114
+ },
112
115
  async init() {
113
116
  await state.runtime.initialize();
114
117
  return application;
@@ -1,4 +1,4 @@
1
- import type { ArgsClass, ModuleProtocols, ModuleInstance, SchemaInput, TaskArguments, TaskDescriptor, TaskOutput, TriggerDescriptor } from '../core/index.js';
1
+ import type { ArgsClass, ModuleProtocols, ModuleInstance, SchemaInput, TaskArguments, TaskDescriptor, TaskOutput, TokenLike, TokenValue, TriggerDescriptor } from '../core/index.js';
2
2
  import type { ApplicationGraphIR } from '../graph/index.js';
3
3
  import type { Logger } from '../runtime/index.js';
4
4
  export interface ApplicationDefinitionOptions<TModules extends readonly ModuleInstance[], TArguments extends ArgsClass | undefined, TTasks extends readonly TaskDescriptor<any, any>[], TTriggers extends readonly TriggerDescriptor[]> {
@@ -27,6 +27,7 @@ export type BootstrapArguments<TDefinition extends ApplicationDefinition> = TDef
27
27
  };
28
28
  export interface BaseApplication {
29
29
  readonly graph: ApplicationGraphIR;
30
+ get<TToken extends TokenLike>(token: TToken): TokenValue<TToken>;
30
31
  init(): Promise<this>;
31
32
  close(signal?: string): Promise<void>;
32
33
  }
@@ -1 +1 @@
1
- export declare const LOUTRE_VERSION = "0.3.0";
1
+ export declare const LOUTRE_VERSION = "0.4.1";
@@ -1,2 +1,2 @@
1
1
  // package manifestとのずれを避けるため、scripts/generate-version.mjsがbuild前に更新する。
2
- export const LOUTRE_VERSION = '0.3.0';
2
+ export const LOUTRE_VERSION = '0.4.1';
@@ -1,4 +1,4 @@
1
- import { type ArgsClass, type EnvClass, type ModuleInstance, type ModuleTemplate, type TaskArguments, type TaskDescriptor, type TaskOutput } from '../core/index.js';
1
+ import { type ArgsClass, type EnvClass, type ModuleInstance, type ModuleTemplate, type TaskArguments, type TaskDescriptor, type TaskOutput, type TokenLike } from '../core/index.js';
2
2
  import { Container, type RuntimeModuleGraph } from './di.js';
3
3
  import { Logger } from './logger.js';
4
4
  export interface ApplicationRuntimeOptions {
@@ -27,6 +27,7 @@ export declare class ApplicationRuntime {
27
27
  readonly container: Container;
28
28
  constructor(roots: readonly (ModuleInstance | ModuleTemplate<void>)[], options?: ApplicationRuntimeOptions);
29
29
  initialize(): Promise<void>;
30
+ get<T>(token: TokenLike<T>): T;
30
31
  run<TTask extends TaskDescriptor<any, any>>(task: TTask, ...args: TaskArguments<TTask>): Promise<TaskOutput<TTask>>;
31
32
  /** public run()ではTrigger専用Taskを拒否するため、Trigger Host専用経路を分ける。 */
32
33
  runTask<TTask extends TaskDescriptor<any, any>>(task: TTask, ...args: TaskArguments<TTask>): Promise<TaskOutput<TTask>>;
@@ -164,6 +164,18 @@ export class ApplicationRuntime {
164
164
  }
165
165
  this.#prepared = true;
166
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
+ }
167
179
  run(task, ...args) {
168
180
  if (!this.#publicTasks.has(task)) {
169
181
  return Promise.reject(new Error(`LUTRE_TASK_002: Task ${task.name} is not registered in Application.tasks.`));
@@ -283,9 +295,12 @@ export class ApplicationRuntime {
283
295
  }
284
296
  }
285
297
  function applicationStateError(code) {
286
- return new Error(code === 'LUTRE_APP_STOPPING'
287
- ? `${code}: Application is stopping and cannot accept new executions.`
288
- : `${code}: Application is stopped and cannot accept new executions.`);
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}`);
289
304
  }
290
305
  function collectApplicationPipelines(modules) {
291
306
  const pipelines = [];
@@ -24,6 +24,7 @@ export declare class Container {
24
24
  bindEnvironment(environment: EnvClass, value: object): void;
25
25
  bindArguments(argumentsContract: ArgsClass, value: object): void;
26
26
  resolve<T>(token: TokenLike<T>): T;
27
+ get<T>(token: TokenLike<T>): T;
27
28
  probeClass<T>(target: Class<T>): T;
28
29
  prepareImplementation(implementation: ImplementationDescriptor): void;
29
30
  implementationRuntime(implementation: ImplementationDescriptor): object;
@@ -110,6 +110,37 @@ export class Container {
110
110
  resolve(token) {
111
111
  return this.#resolve(token);
112
112
  }
113
+ get(token) {
114
+ const provider = this.#providers.get(token);
115
+ if (!provider) {
116
+ if (isEnvClass(token)) {
117
+ throw new DependencyResolutionError(`LUTRE_ENV_002: ${token.name} is not declared by any Module.environment.`);
118
+ }
119
+ if (isArgsClass(token)) {
120
+ throw new DependencyResolutionError(`LUTRE_ARGS_002: ${token.name} is not declared by Application.arguments.`);
121
+ }
122
+ throw new DependencyResolutionError(`LUTRE_DI_UNRESOLVED: Application requires ${tokenName(token)}, but no provider is declared for ${tokenName(token)}.`);
123
+ }
124
+ if (provider.scope !== 'application') {
125
+ throw new DependencyResolutionError(`LUTRE_DI_SCOPED_GET: ${tokenName(token)} is ${provider.scope}-scoped and cannot be retrieved with Application.get().`);
126
+ }
127
+ if (provider.kind === 'environment') {
128
+ if (this.#environment.has(provider.provide)) {
129
+ return this.#environment.get(provider.provide);
130
+ }
131
+ throw new DependencyResolutionError(`LUTRE_ENV_005: Environment ${provider.provide.name} requires a runtime Environment source before Application initialization.`);
132
+ }
133
+ if (provider.kind === 'arguments') {
134
+ if (this.#arguments.has(provider.provide)) {
135
+ return this.#arguments.get(provider.provide);
136
+ }
137
+ throw new DependencyResolutionError(`LUTRE_ARGS_005: Arguments ${provider.provide.name} requires runtime Arguments before Application initialization.`);
138
+ }
139
+ if (this.#applicationCache.has(token)) {
140
+ return this.#applicationCache.get(token);
141
+ }
142
+ throw new DependencyResolutionError(`LUTRE_DI_NOT_INITIALIZED: ${tokenName(token)} has not been constructed by Application initialization.`);
143
+ }
113
144
  probeClass(target) {
114
145
  try {
115
146
  return this.#instantiate(target, [target]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@loutrejs/loutre",
3
- "version": "0.3.0",
3
+ "version": "0.4.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "files": [