@aaronshaf/plane 0.1.3 → 0.1.6

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,124 +1,138 @@
1
- import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "bun:test"
2
- import { Effect } from "effect"
3
- import { http, HttpResponse } from "msw"
4
- import { setupServer } from "msw/node"
5
- import { _clearProjectCache } from "@/resolve"
1
+ import {
2
+ afterAll,
3
+ afterEach,
4
+ beforeAll,
5
+ beforeEach,
6
+ describe,
7
+ expect,
8
+ it,
9
+ } from "bun:test";
10
+ import { Effect } from "effect";
11
+ import { http, HttpResponse } from "msw";
12
+ import { setupServer } from "msw/node";
13
+ import { _clearProjectCache } from "@/resolve";
6
14
 
7
- const BASE = "http://pages-test.local"
8
- const WS = "testws"
15
+ const BASE = "http://pages-test.local";
16
+ const WS = "testws";
9
17
 
10
- const PROJECTS = [{ id: "proj-acme", identifier: "ACME", name: "Acme Project" }]
18
+ const PROJECTS = [
19
+ { id: "proj-acme", identifier: "ACME", name: "Acme Project" },
20
+ ];
11
21
  const PAGES = [
12
- {
13
- id: "pg1",
14
- name: "Architecture Overview",
15
- description_html: "<p>Our architecture...</p>",
16
- created_at: "2025-01-10T10:00:00Z",
17
- updated_at: "2025-01-15T10:00:00Z",
18
- },
19
- {
20
- id: "pg2",
21
- name: "Migration Guide",
22
- description_html: null,
23
- created_at: "2025-01-05T10:00:00Z",
24
- },
25
- ]
22
+ {
23
+ id: "pg1",
24
+ name: "Architecture Overview",
25
+ description_html: "<p>Our architecture...</p>",
26
+ created_at: "2025-01-10T10:00:00Z",
27
+ updated_at: "2025-01-15T10:00:00Z",
28
+ },
29
+ {
30
+ id: "pg2",
31
+ name: "Migration Guide",
32
+ description_html: null,
33
+ created_at: "2025-01-05T10:00:00Z",
34
+ },
35
+ ];
26
36
 
27
37
  const server = setupServer(
28
- http.get(`${BASE}/api/v1/workspaces/${WS}/projects/`, () =>
29
- HttpResponse.json({ results: PROJECTS }),
30
- ),
31
- http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/pages/`, () =>
32
- HttpResponse.json({ results: PAGES }),
33
- ),
34
- http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/pages/pg1/`, () =>
35
- HttpResponse.json(PAGES[0]),
36
- ),
37
- )
38
+ http.get(`${BASE}/api/v1/workspaces/${WS}/projects/`, () =>
39
+ HttpResponse.json({ results: PROJECTS }),
40
+ ),
41
+ http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/pages/`, () =>
42
+ HttpResponse.json({ results: PAGES }),
43
+ ),
44
+ http.get(
45
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/pages/pg1/`,
46
+ () => HttpResponse.json(PAGES[0]),
47
+ ),
48
+ );
38
49
 
39
- beforeAll(() => server.listen({ onUnhandledRequest: "error" }))
40
- afterAll(() => server.close())
50
+ beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
51
+ afterAll(() => server.close());
41
52
 
42
53
  beforeEach(() => {
43
- _clearProjectCache()
44
- process.env["PLANE_HOST"] = BASE
45
- process.env["PLANE_WORKSPACE"] = WS
46
- process.env["PLANE_API_TOKEN"] = "test-token"
47
- })
54
+ _clearProjectCache();
55
+ process.env["PLANE_HOST"] = BASE;
56
+ process.env["PLANE_WORKSPACE"] = WS;
57
+ process.env["PLANE_API_TOKEN"] = "test-token";
58
+ });
48
59
 
49
60
  afterEach(() => {
50
- server.resetHandlers()
51
- delete process.env["PLANE_HOST"]
52
- delete process.env["PLANE_WORKSPACE"]
53
- delete process.env["PLANE_API_TOKEN"]
54
- })
61
+ server.resetHandlers();
62
+ delete process.env["PLANE_HOST"];
63
+ delete process.env["PLANE_WORKSPACE"];
64
+ delete process.env["PLANE_API_TOKEN"];
65
+ });
55
66
 
56
67
  describe("pagesList", () => {
57
- it("lists pages with updated date and name", async () => {
58
- const { pagesList } = await import("@/commands/pages")
59
- const logs: string[] = []
60
- const orig = console.log
61
- console.log = (...args: unknown[]) => logs.push(args.join(" "))
62
- try {
63
- await Effect.runPromise((pagesList as any).handler({ project: "ACME" }))
64
- } finally {
65
- console.log = orig
66
- }
67
- const output = logs.join("\n")
68
- expect(output).toContain("pg1")
69
- expect(output).toContain("2025-01-15")
70
- expect(output).toContain("Architecture Overview")
71
- expect(output).toContain("pg2")
72
- expect(output).toContain("Migration Guide")
73
- })
68
+ it("lists pages with updated date and name", async () => {
69
+ const { pagesList } = await import("@/commands/pages");
70
+ const logs: string[] = [];
71
+ const orig = console.log;
72
+ console.log = (...args: unknown[]) => logs.push(args.join(" "));
73
+ try {
74
+ await Effect.runPromise((pagesList as any).handler({ project: "ACME" }));
75
+ } finally {
76
+ console.log = orig;
77
+ }
78
+ const output = logs.join("\n");
79
+ expect(output).toContain("pg1");
80
+ expect(output).toContain("2025-01-15");
81
+ expect(output).toContain("Architecture Overview");
82
+ expect(output).toContain("pg2");
83
+ expect(output).toContain("Migration Guide");
84
+ });
74
85
 
75
- it("falls back to created_at when no updated_at", async () => {
76
- const { pagesList } = await import("@/commands/pages")
77
- const logs: string[] = []
78
- const orig = console.log
79
- console.log = (...args: unknown[]) => logs.push(args.join(" "))
80
- try {
81
- await Effect.runPromise((pagesList as any).handler({ project: "ACME" }))
82
- } finally {
83
- console.log = orig
84
- }
85
- // pg2 has no updated_at, should use created_at
86
- expect(logs.join("\n")).toContain("2025-01-05")
87
- })
86
+ it("falls back to created_at when no updated_at", async () => {
87
+ const { pagesList } = await import("@/commands/pages");
88
+ const logs: string[] = [];
89
+ const orig = console.log;
90
+ console.log = (...args: unknown[]) => logs.push(args.join(" "));
91
+ try {
92
+ await Effect.runPromise((pagesList as any).handler({ project: "ACME" }));
93
+ } finally {
94
+ console.log = orig;
95
+ }
96
+ // pg2 has no updated_at, should use created_at
97
+ expect(logs.join("\n")).toContain("2025-01-05");
98
+ });
88
99
 
89
- it("shows 'No pages' when empty", async () => {
90
- server.use(
91
- http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/pages/`, () =>
92
- HttpResponse.json({ results: [] }),
93
- ),
94
- )
95
- const { pagesList } = await import("@/commands/pages")
96
- const logs: string[] = []
97
- const orig = console.log
98
- console.log = (...args: unknown[]) => logs.push(args.join(" "))
99
- try {
100
- await Effect.runPromise((pagesList as any).handler({ project: "ACME" }))
101
- } finally {
102
- console.log = orig
103
- }
104
- expect(logs.join("\n")).toBe("No pages")
105
- })
106
- })
100
+ it("shows 'No pages' when empty", async () => {
101
+ server.use(
102
+ http.get(
103
+ `${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/pages/`,
104
+ () => HttpResponse.json({ results: [] }),
105
+ ),
106
+ );
107
+ const { pagesList } = await import("@/commands/pages");
108
+ const logs: string[] = [];
109
+ const orig = console.log;
110
+ console.log = (...args: unknown[]) => logs.push(args.join(" "));
111
+ try {
112
+ await Effect.runPromise((pagesList as any).handler({ project: "ACME" }));
113
+ } finally {
114
+ console.log = orig;
115
+ }
116
+ expect(logs.join("\n")).toBe("No pages");
117
+ });
118
+ });
107
119
 
108
120
  describe("pagesGet", () => {
109
- it("prints full JSON for a page", async () => {
110
- const { pagesGet } = await import("@/commands/pages")
111
- const logs: string[] = []
112
- const orig = console.log
113
- console.log = (...args: unknown[]) => logs.push(args.join(" "))
114
- try {
115
- await Effect.runPromise((pagesGet as any).handler({ project: "ACME", pageId: "pg1" }))
116
- } finally {
117
- console.log = orig
118
- }
119
- const parsed = JSON.parse(logs.join("\n"))
120
- expect(parsed.id).toBe("pg1")
121
- expect(parsed.name).toBe("Architecture Overview")
122
- expect(parsed.description_html).toContain("architecture")
123
- })
124
- })
121
+ it("prints full JSON for a page", async () => {
122
+ const { pagesGet } = await import("@/commands/pages");
123
+ const logs: string[] = [];
124
+ const orig = console.log;
125
+ console.log = (...args: unknown[]) => logs.push(args.join(" "));
126
+ try {
127
+ await Effect.runPromise(
128
+ (pagesGet as any).handler({ project: "ACME", pageId: "pg1" }),
129
+ );
130
+ } finally {
131
+ console.log = orig;
132
+ }
133
+ const parsed = JSON.parse(logs.join("\n"));
134
+ expect(parsed.id).toBe("pg1");
135
+ expect(parsed.name).toBe("Architecture Overview");
136
+ expect(parsed.description_html).toContain("architecture");
137
+ });
138
+ });
@@ -1,178 +1,208 @@
1
- import { afterAll, afterEach, beforeAll, beforeEach, describe, expect, it } from "bun:test"
2
- import { Effect } from "effect"
3
- import { http, HttpResponse } from "msw"
4
- import { setupServer } from "msw/node"
5
1
  import {
6
- resolveProject,
7
- parseIssueRef,
8
- findIssueBySeq,
9
- getStateId,
10
- _clearProjectCache,
11
- } from "@/resolve"
2
+ afterAll,
3
+ afterEach,
4
+ beforeAll,
5
+ beforeEach,
6
+ describe,
7
+ expect,
8
+ it,
9
+ } from "bun:test";
10
+ import { Effect } from "effect";
11
+ import { http, HttpResponse } from "msw";
12
+ import { setupServer } from "msw/node";
13
+ import {
14
+ resolveProject,
15
+ parseIssueRef,
16
+ findIssueBySeq,
17
+ getStateId,
18
+ _clearProjectCache,
19
+ } from "@/resolve";
12
20
 
13
- const BASE = "http://test.local"
14
- const WS = "testws"
21
+ const BASE = "http://test.local";
22
+ const WS = "testws";
15
23
 
16
24
  const PROJECTS = [
17
- { id: "proj-acme", identifier: "ACME", name: "Acme Project" },
18
- { id: "proj-web", identifier: "WEB", name: "Web Project" },
19
- ]
25
+ { id: "proj-acme", identifier: "ACME", name: "Acme Project" },
26
+ { id: "proj-web", identifier: "WEB", name: "Web Project" },
27
+ ];
20
28
 
21
29
  const ISSUES = [
22
- { id: "i1", sequence_id: 29, name: "Migrate Button", priority: "high", state: "s1" },
23
- { id: "i2", sequence_id: 30, name: "Migrate TextInput", priority: "medium", state: "s2" },
24
- ]
30
+ {
31
+ id: "i1",
32
+ sequence_id: 29,
33
+ name: "Migrate Button",
34
+ priority: "high",
35
+ state: "s1",
36
+ },
37
+ {
38
+ id: "i2",
39
+ sequence_id: 30,
40
+ name: "Migrate TextInput",
41
+ priority: "medium",
42
+ state: "s2",
43
+ },
44
+ ];
25
45
 
26
46
  const STATES = [
27
- { id: "s-backlog", name: "Backlog", group: "backlog" },
28
- { id: "s-todo", name: "Todo", group: "unstarted" },
29
- { id: "s-progress", name: "In Progress", group: "started" },
30
- { id: "s-done", name: "Done", group: "completed" },
31
- ]
47
+ { id: "s-backlog", name: "Backlog", group: "backlog" },
48
+ { id: "s-todo", name: "Todo", group: "unstarted" },
49
+ { id: "s-progress", name: "In Progress", group: "started" },
50
+ { id: "s-done", name: "Done", group: "completed" },
51
+ ];
32
52
 
33
53
  const server = setupServer(
34
- http.get(`${BASE}/api/v1/workspaces/${WS}/projects/`, () =>
35
- HttpResponse.json({ results: PROJECTS }),
36
- ),
37
- http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/`, () =>
38
- HttpResponse.json({ results: ISSUES }),
39
- ),
40
- http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/states/`, () =>
41
- HttpResponse.json({ results: STATES }),
42
- ),
43
- )
44
-
45
- beforeAll(() => server.listen({ onUnhandledRequest: "error" }))
46
- afterAll(() => server.close())
54
+ http.get(`${BASE}/api/v1/workspaces/${WS}/projects/`, () =>
55
+ HttpResponse.json({ results: PROJECTS }),
56
+ ),
57
+ http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/issues/`, () =>
58
+ HttpResponse.json({ results: ISSUES }),
59
+ ),
60
+ http.get(`${BASE}/api/v1/workspaces/${WS}/projects/proj-acme/states/`, () =>
61
+ HttpResponse.json({ results: STATES }),
62
+ ),
63
+ );
64
+
65
+ beforeAll(() => server.listen({ onUnhandledRequest: "error" }));
66
+ afterAll(() => server.close());
47
67
 
48
68
  beforeEach(() => {
49
- _clearProjectCache()
50
- process.env["PLANE_HOST"] = BASE
51
- process.env["PLANE_WORKSPACE"] = WS
52
- process.env["PLANE_API_TOKEN"] = "test-token"
53
- })
69
+ _clearProjectCache();
70
+ process.env["PLANE_HOST"] = BASE;
71
+ process.env["PLANE_WORKSPACE"] = WS;
72
+ process.env["PLANE_API_TOKEN"] = "test-token";
73
+ });
54
74
 
55
75
  afterEach(() => {
56
- server.resetHandlers()
57
- delete process.env["PLANE_HOST"]
58
- delete process.env["PLANE_WORKSPACE"]
59
- delete process.env["PLANE_API_TOKEN"]
60
- })
76
+ server.resetHandlers();
77
+ delete process.env["PLANE_HOST"];
78
+ delete process.env["PLANE_WORKSPACE"];
79
+ delete process.env["PLANE_API_TOKEN"];
80
+ });
61
81
 
62
82
  describe("resolveProject", () => {
63
- it("resolves a known project identifier", async () => {
64
- const result = await Effect.runPromise(resolveProject("ACME"))
65
- expect(result.key).toBe("ACME")
66
- expect(result.id).toBe("proj-acme")
67
- })
68
-
69
- it("is case-insensitive", async () => {
70
- const result = await Effect.runPromise(resolveProject("acme"))
71
- expect(result.key).toBe("ACME")
72
- })
73
-
74
- it("resolves WEB too", async () => {
75
- const result = await Effect.runPromise(resolveProject("WEB"))
76
- expect(result.id).toBe("proj-web")
77
- })
78
-
79
- it("fails for unknown project", async () => {
80
- const result = await Effect.runPromise(Effect.either(resolveProject("NOPE")))
81
- expect(result._tag).toBe("Left")
82
- if (result._tag === "Left") {
83
- expect(result.left.message).toContain("Unknown project")
84
- expect(result.left.message).toContain("NOPE")
85
- }
86
- })
87
-
88
- it("uses the cache on second call", async () => {
89
- await Effect.runPromise(resolveProject("ACME"))
90
- // Override with empty cache hit should prevent re-fetching
91
- server.use(
92
- http.get(`${BASE}/api/v1/workspaces/${WS}/projects/`, () =>
93
- HttpResponse.json({ results: [] }),
94
- ),
95
- )
96
- const result = await Effect.runPromise(resolveProject("WEB"))
97
- expect(result.id).toBe("proj-web") // still from cache
98
- })
99
- })
83
+ it("resolves a known project identifier", async () => {
84
+ const result = await Effect.runPromise(resolveProject("ACME"));
85
+ expect(result.key).toBe("ACME");
86
+ expect(result.id).toBe("proj-acme");
87
+ });
88
+
89
+ it("is case-insensitive", async () => {
90
+ const result = await Effect.runPromise(resolveProject("acme"));
91
+ expect(result.key).toBe("ACME");
92
+ });
93
+
94
+ it("resolves WEB too", async () => {
95
+ const result = await Effect.runPromise(resolveProject("WEB"));
96
+ expect(result.id).toBe("proj-web");
97
+ });
98
+
99
+ it("fails for unknown project", async () => {
100
+ const result = await Effect.runPromise(
101
+ Effect.either(resolveProject("NOPE")),
102
+ );
103
+ expect(result._tag).toBe("Left");
104
+ if (result._tag === "Left") {
105
+ expect(result.left.message).toContain("Unknown project");
106
+ expect(result.left.message).toContain("NOPE");
107
+ }
108
+ });
109
+
110
+ it("uses the cache on second call", async () => {
111
+ await Effect.runPromise(resolveProject("ACME"));
112
+ // Override with empty — cache hit should prevent re-fetching
113
+ server.use(
114
+ http.get(`${BASE}/api/v1/workspaces/${WS}/projects/`, () =>
115
+ HttpResponse.json({ results: [] }),
116
+ ),
117
+ );
118
+ const result = await Effect.runPromise(resolveProject("WEB"));
119
+ expect(result.id).toBe("proj-web"); // still from cache
120
+ });
121
+ });
100
122
 
101
123
  describe("parseIssueRef", () => {
102
- it("parses a valid ref", async () => {
103
- const result = await Effect.runPromise(parseIssueRef("ACME-29"))
104
- expect(result.projKey).toBe("ACME")
105
- expect(result.seq).toBe(29)
106
- expect(result.projectId).toBe("proj-acme")
107
- })
108
-
109
- it("is case-insensitive", async () => {
110
- const result = await Effect.runPromise(parseIssueRef("acme-29"))
111
- expect(result.projKey).toBe("ACME")
112
- expect(result.seq).toBe(29)
113
- })
114
-
115
- it("fails on missing dash", async () => {
116
- const result = await Effect.runPromise(Effect.either(parseIssueRef("ACME29")))
117
- expect(result._tag).toBe("Left")
118
- })
119
-
120
- it("fails on non-numeric sequence", async () => {
121
- const result = await Effect.runPromise(Effect.either(parseIssueRef("ACME-abc")))
122
- expect(result._tag).toBe("Left")
123
- if (result._tag === "Left") {
124
- expect(result.left.message).toContain("Invalid issue ref")
125
- }
126
- })
127
-
128
- it("fails on empty string", async () => {
129
- const result = await Effect.runPromise(Effect.either(parseIssueRef("")))
130
- expect(result._tag).toBe("Left")
131
- })
132
- })
124
+ it("parses a valid ref", async () => {
125
+ const result = await Effect.runPromise(parseIssueRef("ACME-29"));
126
+ expect(result.projKey).toBe("ACME");
127
+ expect(result.seq).toBe(29);
128
+ expect(result.projectId).toBe("proj-acme");
129
+ });
130
+
131
+ it("is case-insensitive", async () => {
132
+ const result = await Effect.runPromise(parseIssueRef("acme-29"));
133
+ expect(result.projKey).toBe("ACME");
134
+ expect(result.seq).toBe(29);
135
+ });
136
+
137
+ it("fails on missing dash", async () => {
138
+ const result = await Effect.runPromise(
139
+ Effect.either(parseIssueRef("ACME29")),
140
+ );
141
+ expect(result._tag).toBe("Left");
142
+ });
143
+
144
+ it("fails on non-numeric sequence", async () => {
145
+ const result = await Effect.runPromise(
146
+ Effect.either(parseIssueRef("ACME-abc")),
147
+ );
148
+ expect(result._tag).toBe("Left");
149
+ if (result._tag === "Left") {
150
+ expect(result.left.message).toContain("Invalid issue ref");
151
+ }
152
+ });
153
+
154
+ it("fails on empty string", async () => {
155
+ const result = await Effect.runPromise(Effect.either(parseIssueRef("")));
156
+ expect(result._tag).toBe("Left");
157
+ });
158
+ });
133
159
 
134
160
  describe("findIssueBySeq", () => {
135
- it("finds issue by sequence_id", async () => {
136
- const issue = await Effect.runPromise(findIssueBySeq("proj-acme", 29))
137
- expect(issue.id).toBe("i1")
138
- expect(issue.name).toBe("Migrate Button")
139
- })
140
-
141
- it("finds second issue", async () => {
142
- const issue = await Effect.runPromise(findIssueBySeq("proj-acme", 30))
143
- expect(issue.sequence_id).toBe(30)
144
- })
145
-
146
- it("fails when issue not found", async () => {
147
- const result = await Effect.runPromise(Effect.either(findIssueBySeq("proj-acme", 999)))
148
- expect(result._tag).toBe("Left")
149
- if (result._tag === "Left") {
150
- expect(result.left.message).toContain("#999 not found")
151
- }
152
- })
153
- })
161
+ it("finds issue by sequence_id", async () => {
162
+ const issue = await Effect.runPromise(findIssueBySeq("proj-acme", 29));
163
+ expect(issue.id).toBe("i1");
164
+ expect(issue.name).toBe("Migrate Button");
165
+ });
166
+
167
+ it("finds second issue", async () => {
168
+ const issue = await Effect.runPromise(findIssueBySeq("proj-acme", 30));
169
+ expect(issue.sequence_id).toBe(30);
170
+ });
171
+
172
+ it("fails when issue not found", async () => {
173
+ const result = await Effect.runPromise(
174
+ Effect.either(findIssueBySeq("proj-acme", 999)),
175
+ );
176
+ expect(result._tag).toBe("Left");
177
+ if (result._tag === "Left") {
178
+ expect(result.left.message).toContain("#999 not found");
179
+ }
180
+ });
181
+ });
154
182
 
155
183
  describe("getStateId", () => {
156
- it("finds state by group name", async () => {
157
- const id = await Effect.runPromise(getStateId("proj-acme", "completed"))
158
- expect(id).toBe("s-done")
159
- })
160
-
161
- it("finds state by exact name (case-insensitive)", async () => {
162
- const id = await Effect.runPromise(getStateId("proj-acme", "in progress"))
163
- expect(id).toBe("s-progress")
164
- })
165
-
166
- it("finds backlog state", async () => {
167
- const id = await Effect.runPromise(getStateId("proj-acme", "backlog"))
168
- expect(id).toBe("s-backlog")
169
- })
170
-
171
- it("fails for unknown state", async () => {
172
- const result = await Effect.runPromise(Effect.either(getStateId("proj-acme", "nope")))
173
- expect(result._tag).toBe("Left")
174
- if (result._tag === "Left") {
175
- expect(result.left.message).toContain("State not found")
176
- }
177
- })
178
- })
184
+ it("finds state by group name", async () => {
185
+ const id = await Effect.runPromise(getStateId("proj-acme", "completed"));
186
+ expect(id).toBe("s-done");
187
+ });
188
+
189
+ it("finds state by exact name (case-insensitive)", async () => {
190
+ const id = await Effect.runPromise(getStateId("proj-acme", "in progress"));
191
+ expect(id).toBe("s-progress");
192
+ });
193
+
194
+ it("finds backlog state", async () => {
195
+ const id = await Effect.runPromise(getStateId("proj-acme", "backlog"));
196
+ expect(id).toBe("s-backlog");
197
+ });
198
+
199
+ it("fails for unknown state", async () => {
200
+ const result = await Effect.runPromise(
201
+ Effect.either(getStateId("proj-acme", "nope")),
202
+ );
203
+ expect(result._tag).toBe("Left");
204
+ if (result._tag === "Left") {
205
+ expect(result.left.message).toContain("State not found");
206
+ }
207
+ });
208
+ });