@langchain/langgraph-api 1.1.2 → 1.1.8

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 (100) hide show
  1. package/package.json +9 -8
  2. package/CHANGELOG.md +0 -297
  3. package/dist/api/assistants.d.mts +0 -3
  4. package/dist/api/assistants.mjs +0 -193
  5. package/dist/api/meta.d.mts +0 -3
  6. package/dist/api/meta.mjs +0 -65
  7. package/dist/api/runs.d.mts +0 -3
  8. package/dist/api/runs.mjs +0 -324
  9. package/dist/api/store.d.mts +0 -3
  10. package/dist/api/store.mjs +0 -111
  11. package/dist/api/threads.d.mts +0 -3
  12. package/dist/api/threads.mjs +0 -143
  13. package/dist/auth/custom.d.mts +0 -9
  14. package/dist/auth/custom.mjs +0 -32
  15. package/dist/auth/index.d.mts +0 -43
  16. package/dist/auth/index.mjs +0 -163
  17. package/dist/cli/entrypoint.d.mts +0 -1
  18. package/dist/cli/entrypoint.mjs +0 -41
  19. package/dist/cli/spawn.d.mts +0 -42
  20. package/dist/cli/spawn.mjs +0 -47
  21. package/dist/cli/utils/ipc/client.d.mts +0 -5
  22. package/dist/cli/utils/ipc/client.mjs +0 -47
  23. package/dist/cli/utils/ipc/utils/get-pipe-path.d.mts +0 -1
  24. package/dist/cli/utils/ipc/utils/get-pipe-path.mjs +0 -29
  25. package/dist/cli/utils/ipc/utils/temporary-directory.d.mts +0 -5
  26. package/dist/cli/utils/ipc/utils/temporary-directory.mjs +0 -40
  27. package/dist/command.d.mts +0 -11
  28. package/dist/command.mjs +0 -15
  29. package/dist/experimental/embed.d.mts +0 -42
  30. package/dist/experimental/embed.mjs +0 -299
  31. package/dist/graph/api.d.mts +0 -1
  32. package/dist/graph/api.mjs +0 -2
  33. package/dist/graph/load.d.mts +0 -19
  34. package/dist/graph/load.hooks.d.mts +0 -2
  35. package/dist/graph/load.hooks.mjs +0 -52
  36. package/dist/graph/load.mjs +0 -96
  37. package/dist/graph/load.utils.d.mts +0 -22
  38. package/dist/graph/load.utils.mjs +0 -49
  39. package/dist/graph/parser/index.d.mts +0 -23
  40. package/dist/graph/parser/index.mjs +0 -58
  41. package/dist/graph/parser/parser.d.mts +0 -77
  42. package/dist/graph/parser/parser.mjs +0 -429
  43. package/dist/graph/parser/parser.worker.d.mts +0 -1
  44. package/dist/graph/parser/parser.worker.mjs +0 -7
  45. package/dist/graph/parser/schema/types.d.mts +0 -154
  46. package/dist/graph/parser/schema/types.mjs +0 -1496
  47. package/dist/graph/parser/schema/types.template.d.mts +0 -1
  48. package/dist/graph/parser/schema/types.template.mts +0 -92
  49. package/dist/http/custom.d.mts +0 -6
  50. package/dist/http/custom.mjs +0 -10
  51. package/dist/http/middleware.d.mts +0 -11
  52. package/dist/http/middleware.mjs +0 -57
  53. package/dist/logging.d.mts +0 -10
  54. package/dist/logging.mjs +0 -115
  55. package/dist/loopback.d.mts +0 -4
  56. package/dist/loopback.mjs +0 -10
  57. package/dist/preload.d.mts +0 -1
  58. package/dist/preload.mjs +0 -29
  59. package/dist/queue.d.mts +0 -2
  60. package/dist/queue.mjs +0 -119
  61. package/dist/schemas.d.mts +0 -1552
  62. package/dist/schemas.mjs +0 -492
  63. package/dist/semver/index.d.mts +0 -15
  64. package/dist/semver/index.mjs +0 -46
  65. package/dist/server.d.mts +0 -175
  66. package/dist/server.mjs +0 -181
  67. package/dist/state.d.mts +0 -3
  68. package/dist/state.mjs +0 -30
  69. package/dist/storage/checkpoint.d.mts +0 -19
  70. package/dist/storage/checkpoint.mjs +0 -127
  71. package/dist/storage/context.d.mts +0 -3
  72. package/dist/storage/context.mjs +0 -11
  73. package/dist/storage/importMap.d.mts +0 -55
  74. package/dist/storage/importMap.mjs +0 -55
  75. package/dist/storage/ops.d.mts +0 -169
  76. package/dist/storage/ops.mjs +0 -1262
  77. package/dist/storage/persist.d.mts +0 -18
  78. package/dist/storage/persist.mjs +0 -81
  79. package/dist/storage/store.d.mts +0 -17
  80. package/dist/storage/store.mjs +0 -41
  81. package/dist/storage/types.d.mts +0 -301
  82. package/dist/storage/types.mjs +0 -1
  83. package/dist/stream.d.mts +0 -43
  84. package/dist/stream.mjs +0 -235
  85. package/dist/ui/load.d.mts +0 -8
  86. package/dist/ui/load.mjs +0 -53
  87. package/dist/utils/abort.d.mts +0 -1
  88. package/dist/utils/abort.mjs +0 -8
  89. package/dist/utils/hono.d.mts +0 -5
  90. package/dist/utils/hono.mjs +0 -24
  91. package/dist/utils/importMap.d.mts +0 -55
  92. package/dist/utils/importMap.mjs +0 -55
  93. package/dist/utils/runnableConfig.d.mts +0 -3
  94. package/dist/utils/runnableConfig.mjs +0 -45
  95. package/dist/utils/serde.d.mts +0 -5
  96. package/dist/utils/serde.mjs +0 -20
  97. package/dist/vitest.config.d.ts +0 -2
  98. package/dist/vitest.config.js +0 -12
  99. package/dist/webhook.d.mts +0 -11
  100. package/dist/webhook.mjs +0 -30
@@ -1 +0,0 @@
1
- export type Equals<X, Y> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? true : false;
@@ -1,92 +0,0 @@
1
- import type { BaseMessage } from "@langchain/core/messages";
2
- import type {
3
- StateType,
4
- UpdateType,
5
- StateDefinition,
6
- } from "@langchain/langgraph";
7
-
8
- // @ts-ignore
9
- type AnyPregel = {
10
- lg_is_pregel: boolean;
11
- stream: (...args: any[]) => any;
12
- invoke: (...args: any[]) => any;
13
- };
14
-
15
- // @ts-ignore
16
- type AnyGraph = {
17
- compiled: boolean;
18
- compile: (...args: any[]) => any;
19
- };
20
-
21
- export type Equals<X, Y> = (<T>() => T extends X ? 1 : 2) extends <
22
- T
23
- >() => T extends Y ? 1 : 2
24
- ? true
25
- : false;
26
-
27
- type MatchBaseMessage<T> = T extends BaseMessage ? BaseMessage : never;
28
- type MatchBaseMessageArray<T> = T extends Array<infer C>
29
- ? Equals<MatchBaseMessage<C>, BaseMessage> extends true
30
- ? BaseMessage[]
31
- : never
32
- : never;
33
-
34
- type Defactorify<T> = T extends (...args: any[]) => infer R
35
- ? Awaited<R>
36
- : Awaited<T>;
37
-
38
- // @ts-ignore
39
- type Inspect<T, TDepth extends Array<0> = []> = TDepth extends [0, 0, 0]
40
- ? any
41
- : T extends unknown
42
- ? {
43
- [K in keyof T]: 0 extends 1 & T[K]
44
- ? T[K]
45
- : Equals<MatchBaseMessageArray<T[K]>, BaseMessage[]> extends true
46
- ? BaseMessage[]
47
- : Equals<MatchBaseMessage<T[K]>, BaseMessage> extends true
48
- ? BaseMessage
49
- : Inspect<T[K], [0, ...TDepth]>;
50
- }
51
- : never;
52
-
53
- type ReflectCompiled<T> = T extends { RunInput: infer S; RunOutput: infer U }
54
- ? { state: S; update: U }
55
- : T extends { "~InputType": infer InputType; "~OutputType": infer OutputType }
56
- ? { state: OutputType; update: InputType }
57
- : never;
58
-
59
- // @ts-ignore
60
- type Reflect<T> = Defactorify<T> extends infer DT
61
- ? DT extends {
62
- compile(...args: any[]): infer Compiled;
63
- }
64
- ? ReflectCompiled<Compiled>
65
- : ReflectCompiled<DT>
66
- : never;
67
-
68
- type BuilderReflectCompiled<T> = T extends {
69
- builder: {
70
- _inputDefinition: infer I extends StateDefinition;
71
- _outputDefinition: infer O extends StateDefinition;
72
- _configSchema?: infer C extends StateDefinition | undefined;
73
- };
74
- }
75
- ? {
76
- input: UpdateType<I>;
77
- output: StateType<O>;
78
- config: UpdateType<Exclude<C, undefined>>;
79
- }
80
- : never;
81
-
82
- // @ts-ignore
83
- type BuilderReflect<T> = Defactorify<T> extends infer DT
84
- ? DT extends {
85
- compile(...args: any[]): infer Compiled;
86
- }
87
- ? BuilderReflectCompiled<Compiled>
88
- : BuilderReflectCompiled<DT>
89
- : never;
90
-
91
- // @ts-ignore
92
- type FilterAny<T> = 0 extends 1 & T ? never : T;
@@ -1,6 +0,0 @@
1
- import { Hono } from "hono";
2
- export declare function registerHttp(appPath: string, options: {
3
- cwd: string;
4
- }): Promise<{
5
- api: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
6
- }>;
@@ -1,10 +0,0 @@
1
- import * as path from "node:path";
2
- import * as url from "node:url";
3
- export async function registerHttp(appPath, options) {
4
- const [userFile, exportSymbol] = appPath.split(":", 2);
5
- const sourceFile = path.resolve(options.cwd, userFile);
6
- const user = (await import(url.pathToFileURL(sourceFile).toString()).then((module) => module[exportSymbol || "default"]));
7
- if (!user)
8
- throw new Error(`Failed to load HTTP app: ${appPath}`);
9
- return { api: user };
10
- }
@@ -1,11 +0,0 @@
1
- import { MiddlewareHandler } from "hono";
2
- export declare const cors: (cors: {
3
- allow_origins?: string[];
4
- allow_origin_regex?: string;
5
- allow_methods?: string[];
6
- allow_headers?: string[];
7
- allow_credentials?: boolean;
8
- expose_headers?: string[];
9
- max_age?: number;
10
- } | undefined) => MiddlewareHandler;
11
- export declare const ensureContentType: () => MiddlewareHandler;
@@ -1,57 +0,0 @@
1
- import { cors as honoCors } from "hono/cors";
2
- export const cors = (cors) => {
3
- if (cors == null) {
4
- return honoCors({
5
- origin: "*",
6
- exposeHeaders: ["content-location", "x-pagination-total"],
7
- });
8
- }
9
- const originRegex = cors.allow_origin_regex
10
- ? new RegExp(cors.allow_origin_regex)
11
- : undefined;
12
- const origin = originRegex
13
- ? (origin) => {
14
- originRegex.lastIndex = 0; // reset regex in case it's a global regex
15
- if (originRegex.test(origin))
16
- return origin;
17
- return undefined;
18
- }
19
- : cors.allow_origins;
20
- if (cors.expose_headers?.length) {
21
- const headersSet = new Set(cors.expose_headers.map((h) => h.toLowerCase()));
22
- if (!headersSet.has("content-location")) {
23
- console.warn("Adding missing `Content-Location` header in `cors.expose_headers`.");
24
- cors.expose_headers.push("content-location");
25
- }
26
- if (!headersSet.has("x-pagination-total")) {
27
- console.warn("Adding missing `X-Pagination-Total` header in `cors.expose_headers`.");
28
- cors.expose_headers.push("x-pagination-total");
29
- }
30
- }
31
- const config = { origin: origin ?? "*" };
32
- if (cors.max_age != null)
33
- config.maxAge = cors.max_age;
34
- if (cors.allow_methods != null)
35
- config.allowMethods = cors.allow_methods;
36
- if (cors.allow_headers != null)
37
- config.allowHeaders = cors.allow_headers;
38
- if (cors.expose_headers != null)
39
- config.exposeHeaders = cors.expose_headers;
40
- if (cors.allow_credentials != null) {
41
- config.credentials = cors.allow_credentials;
42
- }
43
- return honoCors(config);
44
- };
45
- // This is used to match the behavior of the original LangGraph API
46
- // where the content-type is not being validated. Might be nice
47
- // to warn about this in the future and throw an error instead.
48
- export const ensureContentType = () => {
49
- return async (c, next) => {
50
- if (c.req.header("content-type")?.startsWith("text/plain") &&
51
- c.req.method !== "GET" &&
52
- c.req.method !== "OPTIONS") {
53
- c.req.raw.headers.set("content-type", "application/json");
54
- }
55
- await next();
56
- };
57
- };
@@ -1,10 +0,0 @@
1
- import { type Logger } from "winston";
2
- import type { MiddlewareHandler } from "hono";
3
- export declare const logger: Logger;
4
- export declare function registerSdkLogger(): void;
5
- export declare function registerRuntimeLogFormatter(formatter: (info: Record<string, unknown>) => Record<string, unknown>): Promise<void>;
6
- export declare const logError: (error: unknown, options?: {
7
- context?: Record<string, unknown>;
8
- prefix?: string;
9
- }) => void;
10
- export declare const requestLogger: () => MiddlewareHandler;
package/dist/logging.mjs DELETED
@@ -1,115 +0,0 @@
1
- import { createLogger, format, transports } from "winston";
2
- import { logger as honoLogger } from "hono/logger";
3
- import { consoleFormat } from "winston-console-format";
4
- import { parse as stacktraceParser } from "stacktrace-parser";
5
- import { readFileSync } from "node:fs";
6
- import { codeFrameColumns } from "@babel/code-frame";
7
- import path from "node:path";
8
- const LOG_JSON = process.env.LOG_JSON === "true";
9
- const LOG_LEVEL = process.env.LOG_LEVEL || "debug";
10
- let RUNTIME_LOG_FORMATTER;
11
- const applyRuntimeFormatter = format((info) => {
12
- if (!RUNTIME_LOG_FORMATTER)
13
- return info;
14
- return RUNTIME_LOG_FORMATTER(info);
15
- });
16
- export const logger = createLogger({
17
- level: LOG_LEVEL,
18
- format: format.combine(applyRuntimeFormatter(), format.errors({ stack: true }), format.timestamp(), format.json(), ...(!LOG_JSON
19
- ? [
20
- format.colorize({ all: true }),
21
- format.padLevels(),
22
- consoleFormat({
23
- showMeta: true,
24
- metaStrip: ["timestamp"],
25
- inspectOptions: {
26
- depth: Infinity,
27
- colors: true,
28
- maxArrayLength: Infinity,
29
- breakLength: 120,
30
- compact: Infinity,
31
- },
32
- }),
33
- ]
34
- : [
35
- format.printf((info) => {
36
- const { timestamp, level, message, ...rest } = info;
37
- let event;
38
- if (typeof message === "string") {
39
- event = message;
40
- }
41
- else {
42
- event = JSON.stringify(message);
43
- }
44
- if (rest.stack) {
45
- rest.message = event;
46
- event = rest.stack;
47
- }
48
- return JSON.stringify({ timestamp, level, event, ...rest });
49
- }),
50
- ])),
51
- transports: [new transports.Console()],
52
- });
53
- // Expose the logger to be consumed by `getLogger`
54
- export function registerSdkLogger() {
55
- const GLOBAL_LOGGER = Symbol.for("langgraph.api.sdk-logger");
56
- const maybeGlobal = globalThis;
57
- maybeGlobal[GLOBAL_LOGGER] = logger;
58
- }
59
- export async function registerRuntimeLogFormatter(formatter) {
60
- RUNTIME_LOG_FORMATTER = formatter;
61
- }
62
- const formatStack = (stack) => {
63
- if (!stack)
64
- return stack;
65
- const [firstFile] = stacktraceParser(stack).filter((item) => !item.file?.split(path.sep).includes("node_modules") &&
66
- !item.file?.startsWith("node:"));
67
- if (firstFile?.file && firstFile?.lineNumber) {
68
- try {
69
- const filePath = firstFile.file;
70
- const line = firstFile.lineNumber;
71
- const column = firstFile.column ?? 0;
72
- const messageLines = stack.split("\n");
73
- const spliceIndex = messageLines.findIndex((i) => i.includes(filePath));
74
- const padding = " ".repeat(Math.max(0, messageLines[spliceIndex].indexOf("at")));
75
- const highlightCode = process.stdout.isTTY;
76
- let codeFrame = codeFrameColumns(readFileSync(filePath, "utf-8"), { start: { line, column } }, { highlightCode });
77
- codeFrame = codeFrame
78
- .split("\n")
79
- .map((i) => padding + i + "\x1b[0m")
80
- .join("\n");
81
- if (highlightCode) {
82
- codeFrame = "\x1b[36m" + codeFrame + "\x1b[31m";
83
- }
84
- // insert codeframe after the line but dont lose the stack
85
- return [
86
- ...messageLines.slice(0, spliceIndex + 1),
87
- codeFrame,
88
- ...messageLines.slice(spliceIndex + 1),
89
- ].join("\n");
90
- }
91
- catch {
92
- // pass
93
- }
94
- }
95
- return stack;
96
- };
97
- export const logError = (error, options) => {
98
- let message;
99
- let context = options?.context;
100
- if (error instanceof Error) {
101
- message = formatStack(error.stack) || error.message;
102
- }
103
- else {
104
- message = String(error);
105
- context = { ...context, error };
106
- }
107
- if (options?.prefix != null)
108
- message = `${options.prefix}:\n${message}`;
109
- logger.error(message, ...(context != null ? [context] : []));
110
- };
111
- process.on("uncaughtException", (error) => logError(error));
112
- process.on("unhandledRejection", (error) => logError(error));
113
- export const requestLogger = () => honoLogger((message, ...rest) => {
114
- logger.info(message, ...rest);
115
- });
@@ -1,4 +0,0 @@
1
- import { Hono } from "hono";
2
- import type { StorageEnv } from "./storage/types.mjs";
3
- export declare function getLoopbackFetch(): (url: string, init?: RequestInit) => Promise<Response>;
4
- export declare const bindLoopbackFetch: (app: Hono<StorageEnv>) => void;
package/dist/loopback.mjs DELETED
@@ -1,10 +0,0 @@
1
- const fetchSmb = Symbol.for("langgraph_api:fetch");
2
- const global = globalThis;
3
- export function getLoopbackFetch() {
4
- if (!global[fetchSmb])
5
- throw new Error("Loopback fetch is not bound");
6
- return global[fetchSmb];
7
- }
8
- export const bindLoopbackFetch = (app) => {
9
- global[fetchSmb] = async (url, init) => app.request(url, init);
10
- };
@@ -1 +0,0 @@
1
- export {};
package/dist/preload.mjs DELETED
@@ -1,29 +0,0 @@
1
- import { register } from "node:module";
2
- import { pathToFileURL } from "node:url";
3
- import { join } from "node:path";
4
- // arguments passed to the entrypoint: [ppid, payload]
5
- // we only care about the payload, which contains the server definition
6
- const lastArg = process.argv.at(-1);
7
- const options = JSON.parse(lastArg || "{}");
8
- // find the first file, as `parentURL` needs to be a valid file URL
9
- // if no graph found, just assume a dummy default file, which should
10
- // be working fine as well.
11
- const graphFiles = Object.values(options.graphs)
12
- .map((i) => {
13
- if (typeof i === "string") {
14
- return i.split(":").at(0);
15
- }
16
- else if (i && typeof i === "object" && i.path) {
17
- return i.path.split(":").at(0);
18
- }
19
- return null;
20
- })
21
- .filter(Boolean);
22
- const firstGraphFile = graphFiles.at(0) || "index.mts";
23
- // enforce API @langchain/langgraph resolution
24
- register("./graph/load.hooks.mjs", import.meta.url, {
25
- parentURL: "data:",
26
- data: {
27
- parentURL: pathToFileURL(join(options.cwd, firstGraphFile)).toString(),
28
- },
29
- });
package/dist/queue.d.mts DELETED
@@ -1,2 +0,0 @@
1
- import type { Ops } from "./storage/types.mjs";
2
- export declare const queue: (ops: Ops) => Promise<never>;
package/dist/queue.mjs DELETED
@@ -1,119 +0,0 @@
1
- import { streamState, } from "./stream.mjs";
2
- import { logError, logger } from "./logging.mjs";
3
- import { serializeError } from "./utils/serde.mjs";
4
- import { callWebhook } from "./webhook.mjs";
5
- import { getGraph } from "./graph/load.mjs";
6
- const MAX_RETRY_ATTEMPTS = 3;
7
- const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
8
- export const queue = async (ops) => {
9
- while (true) {
10
- for await (const { run, attempt, signal } of ops.runs.next()) {
11
- await worker(ops, run, attempt, signal);
12
- }
13
- // TODO: this is very suboptimal, we should implement subscription to the run
14
- await sleep(1000 * Math.random());
15
- }
16
- };
17
- const worker = async (ops, run, attempt, signal) => {
18
- const startedAt = new Date();
19
- let endedAt = undefined;
20
- let checkpoint = undefined;
21
- let exception = undefined;
22
- let status = undefined;
23
- const temporary = run.kwargs.temporary;
24
- const webhook = run.kwargs.webhook;
25
- logger.info("Starting background run", {
26
- run_id: run.run_id,
27
- run_attempt: attempt,
28
- run_created_at: run.created_at,
29
- run_started_at: startedAt,
30
- run_queue_ms: startedAt.valueOf() - run.created_at.valueOf(),
31
- });
32
- const onCheckpoint = (value) => {
33
- checkpoint = value;
34
- };
35
- const onTaskResult = (result) => {
36
- if (checkpoint == null)
37
- return;
38
- const index = checkpoint.tasks.findIndex((task) => task.id === result.id);
39
- checkpoint.tasks[index] = {
40
- ...checkpoint.tasks[index],
41
- ...result,
42
- };
43
- };
44
- try {
45
- if (attempt > MAX_RETRY_ATTEMPTS) {
46
- throw new Error(`Run ${run.run_id} exceeded max attempts`);
47
- }
48
- const runId = run.run_id;
49
- const resumable = run.kwargs?.resumable ?? false;
50
- try {
51
- const stream = streamState(run, {
52
- getGraph,
53
- attempt,
54
- signal,
55
- ...(!temporary ? { onCheckpoint, onTaskResult } : undefined),
56
- });
57
- for await (const { event, data } of stream) {
58
- await ops.runs.stream.publish({ runId, resumable, event, data });
59
- }
60
- }
61
- catch (error) {
62
- await ops.runs.stream.publish({
63
- runId,
64
- resumable,
65
- event: "error",
66
- data: serializeError(error),
67
- });
68
- throw error;
69
- }
70
- endedAt = new Date();
71
- logger.info("Background run succeeded", {
72
- run_id: run.run_id,
73
- run_attempt: attempt,
74
- run_created_at: run.created_at,
75
- run_started_at: startedAt,
76
- run_ended_at: endedAt,
77
- run_exec_ms: endedAt.valueOf() - startedAt.valueOf(),
78
- });
79
- status = "success";
80
- await ops.runs.setStatus(run.run_id, status);
81
- }
82
- catch (error) {
83
- endedAt = new Date();
84
- if (error instanceof Error)
85
- exception = error;
86
- logError(error, {
87
- prefix: "Background run failed",
88
- context: {
89
- run_id: run.run_id,
90
- run_attempt: attempt,
91
- run_created_at: run.created_at,
92
- run_started_at: startedAt,
93
- run_ended_at: endedAt,
94
- run_exec_ms: endedAt.valueOf() - startedAt.valueOf(),
95
- },
96
- });
97
- status = "error";
98
- await ops.runs.setStatus(run.run_id, "error");
99
- }
100
- finally {
101
- if (temporary) {
102
- await ops.threads.delete(run.thread_id, undefined);
103
- }
104
- else {
105
- await ops.threads.setStatus(run.thread_id, { checkpoint, exception });
106
- }
107
- if (webhook) {
108
- await callWebhook({
109
- checkpoint,
110
- status,
111
- exception,
112
- run,
113
- webhook,
114
- run_started_at: startedAt,
115
- run_ended_at: endedAt,
116
- });
117
- }
118
- }
119
- };