lalph 0.2.17 → 0.2.19

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "lalph",
3
3
  "type": "module",
4
- "version": "0.2.17",
4
+ "version": "0.2.19",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -23,11 +23,11 @@
23
23
  "@changesets/changelog-github": "^0.5.2",
24
24
  "@changesets/cli": "^2.29.8",
25
25
  "@effect/language-service": "^0.72.0",
26
- "@effect/platform-node": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node@48538de",
26
+ "@effect/platform-node": "https://pkg.pr.new/Effect-TS/effect-smol/@effect/platform-node@a5fa8be",
27
27
  "@linear/sdk": "^72.0.0",
28
28
  "@octokit/plugin-rest-endpoint-methods": "^17.0.0",
29
29
  "@octokit/types": "^16.0.0",
30
- "effect": "https://pkg.pr.new/Effect-TS/effect-smol/effect@48538de",
30
+ "effect": "https://pkg.pr.new/Effect-TS/effect-smol/effect@a5fa8be",
31
31
  "husky": "^9.1.7",
32
32
  "lint-staged": "^16.2.7",
33
33
  "octokit": "^5.0.5",
package/src/Github/Cli.ts CHANGED
@@ -10,9 +10,9 @@ import {
10
10
  } from "effect"
11
11
  import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"
12
12
  import {
13
- CommentsData,
14
- ReviewComment,
15
13
  Comment,
14
+ GithubPullRequestData,
15
+ ReviewComment,
16
16
  } from "../domain/GithubComment.ts"
17
17
 
18
18
  export class GithubCli extends ServiceMap.Service<GithubCli>()(
@@ -41,17 +41,19 @@ export class GithubCli extends ServiceMap.Service<GithubCli>()(
41
41
  const reviewComments = (pr: number) =>
42
42
  ChildProcess.make`gh api graphql -f owner=${owner} -f repo=${repo} -F pr=${pr} -f query=${githubReviewCommentsQuery}`.pipe(
43
43
  ChildProcess.string,
44
- Effect.flatMap(Schema.decodeEffect(CommentsFromJson)),
44
+ Effect.flatMap(Schema.decodeEffect(PullRequestDataFromJson)),
45
45
  Effect.map((data) => {
46
46
  const comments =
47
- data.data.repository.pullRequest.comments.edges.map(
48
- (edge) => edge.node,
47
+ data.data.repository.pullRequest.comments.nodes.filter(
48
+ (c) => !c.author.login.startsWith("github"),
49
49
  )
50
- const reviewThreads =
51
- data.data.repository.pullRequest.reviewThreads.edges.map(
52
- (edge) => edge.node,
50
+ const reviews =
51
+ data.data.repository.pullRequest.reviews.nodes.filter(
52
+ (r) => r.body.trim().length > 0,
53
53
  )
54
- return { comments, reviewThreads } as const
54
+ const reviewThreads =
55
+ data.data.repository.pullRequest.reviewThreads.nodes
56
+ return { comments, reviews, reviewThreads } as const
55
57
  }),
56
58
  Effect.provideService(
57
59
  ChildProcessSpawner.ChildProcessSpawner,
@@ -61,12 +63,16 @@ export class GithubCli extends ServiceMap.Service<GithubCli>()(
61
63
 
62
64
  const prFeedbackMd = (pr: number) =>
63
65
  reviewComments(pr).pipe(
64
- Effect.map(({ comments, reviewThreads }) => {
66
+ Effect.map(({ comments, reviewThreads, reviews }) => {
65
67
  const eligibleReviewThreads = reviewThreads.filter(
66
68
  (thread) => thread.shouldDisplayThread,
67
69
  )
68
70
 
69
- if (comments.length === 0 && eligibleReviewThreads.length === 0) {
71
+ if (
72
+ comments.length === 0 &&
73
+ eligibleReviewThreads.length === 0 &&
74
+ reviews.length === 0
75
+ ) {
70
76
  return `No review comments found.`
71
77
  }
72
78
 
@@ -90,6 +96,23 @@ Comments are rendered in XML format.`
90
96
  ${reviewCommentsMd}`
91
97
  }
92
98
 
99
+ if (reviews.length > 0) {
100
+ const reviewsXml = reviews
101
+ .map(
102
+ (review) => `<review author="${review.author.login}">
103
+ <body><![CDATA[${review.body}]]></body>
104
+ </review>`,
105
+ )
106
+ .join("\n")
107
+ content += `
108
+
109
+ ## Reviews
110
+
111
+ <reviews>
112
+ ${reviewsXml}
113
+ </reviews>`
114
+ }
115
+
93
116
  if (comments.length > 0) {
94
117
  const generalCommentsXml = comments
95
118
  .map((comment) => renderGeneralComment(comment))
@@ -155,45 +178,54 @@ const renderGeneralComment = (
155
178
 
156
179
  // Schema definitions and GraphQL query
157
180
 
158
- const CommentsFromJson = Schema.fromJsonString(CommentsData)
181
+ const PullRequestDataFromJson = Schema.fromJsonString(GithubPullRequestData)
159
182
 
160
183
  const githubReviewCommentsQuery = `
161
- query FetchPRComments($owner: String!, $repo: String!, $pr: Int!) {
162
- repository(owner: $owner, name: $repo) {
163
- pullRequest(number: $pr) {
164
- url
165
- reviewDecision
166
- reviewThreads(first: 100) {
167
- edges {
168
- node {
169
- isCollapsed
170
- isOutdated
171
- isResolved
172
- comments(first: 100) {
173
- nodes {
174
- id
175
- author { login }
176
- body
177
- path
178
- originalLine
179
- diffHunk
180
- createdAt
181
- }
182
- }
183
- }
184
+ query FetchPRComments($owner: String!, $repo: String!, $pr: Int!) {
185
+ repository(owner: $owner, name: $repo) {
186
+ pullRequest(number: $pr) {
187
+ url
188
+ reviewDecision
189
+ reviews(first: 100) {
190
+ nodes {
191
+ id
192
+ author {
193
+ login
184
194
  }
195
+ body
185
196
  }
186
- comments(first: 100) {
187
- edges {
188
- node {
197
+ }
198
+ reviewThreads(first: 100) {
199
+ nodes {
200
+ isCollapsed
201
+ isOutdated
202
+ isResolved
203
+ comments(first: 100) {
204
+ nodes {
189
205
  id
206
+ author {
207
+ login
208
+ }
190
209
  body
191
- author { login }
210
+ path
211
+ originalLine
212
+ diffHunk
192
213
  createdAt
193
214
  }
194
215
  }
195
216
  }
196
217
  }
218
+ comments(first: 100) {
219
+ nodes {
220
+ id
221
+ body
222
+ author {
223
+ login
224
+ }
225
+ createdAt
226
+ }
227
+ }
197
228
  }
198
229
  }
230
+ }
199
231
  `
package/src/Settings.ts CHANGED
@@ -20,9 +20,10 @@ export class Settings extends ServiceMap.Service<Settings>()("lalph/Settings", {
20
20
  make: Effect.gen(function* () {
21
21
  const kvs = yield* KeyValueStore.KeyValueStore
22
22
  const projectKvs = yield* ProjectsKvs
23
- const store = KeyValueStore.prefix(kvs, "settings.")
24
23
  const reactivity = yield* Reactivity.Reactivity
25
24
 
25
+ const store = KeyValueStore.prefix(kvs, "settings.")
26
+
26
27
  const cache = yield* Cache.make({
27
28
  lookup(setting: Setting<string, Schema.Codec<any, any>>) {
28
29
  const s = KeyValueStore.toSchemaStore(store, setting.schema)
@@ -63,7 +64,7 @@ export class Settings extends ServiceMap.Service<Settings>()("lalph/Settings", {
63
64
  onSome: (v) => Effect.orDie(s.set(setting.name, v)),
64
65
  })
65
66
  return reactivity.mutation(
66
- [`settings.${setting.name}`],
67
+ [`settings:${setting.name}`],
67
68
  Effect.andThen(update, setCache),
68
69
  )
69
70
  }
@@ -109,7 +110,7 @@ export class Settings extends ServiceMap.Service<Settings>()("lalph/Settings", {
109
110
  onSome: (v) => Effect.orDie(s.set(setting.name, v)),
110
111
  })
111
112
  yield* reactivity.mutation(
112
- [`settings.${projectId}.${setting.name}`],
113
+ [`settings.${projectId}:${setting.name}`],
113
114
  Effect.andThen(update, setCache),
114
115
  )
115
116
  },
@@ -162,7 +163,9 @@ export class Settings extends ServiceMap.Service<Settings>()("lalph/Settings", {
162
163
  > {
163
164
  const read = pipe(
164
165
  Settings.runtime.atom(Settings.get(setting)),
165
- atomRuntime.withReactivity([`settings.${setting.name}`]),
166
+ atomRuntime.withReactivity({
167
+ settings: [setting.name],
168
+ }),
166
169
  )
167
170
  const set = Settings.runtime.fn<Option.Option<S["Type"]>>()((value) =>
168
171
  Settings.set(setting, value),
@@ -198,9 +201,9 @@ export class Settings extends ServiceMap.Service<Settings>()("lalph/Settings", {
198
201
  Effect.provideService(CurrentProjectId, options.projectId),
199
202
  ),
200
203
  ),
201
- atomRuntime.withReactivity([
202
- `settings.${options.projectId}.${options.setting.name}`,
203
- ]),
204
+ atomRuntime.withReactivity({
205
+ [`settings.${options.projectId}`]: [options.setting.name],
206
+ }),
204
207
  )
205
208
  const set = Settings.runtime.fn<Option.Option<S["Type"]>>()((value) =>
206
209
  Settings.setProject(options.setting, value).pipe(
@@ -1,4 +1,4 @@
1
- import { Effect, Option } from "effect"
1
+ import { Effect, FileSystem, Option, Path } from "effect"
2
2
  import { Command } from "effect/unstable/cli"
3
3
  import { allProjects, getAllProjects, selectProject } from "../../Projects.ts"
4
4
  import { Settings } from "../../Settings.ts"
@@ -8,6 +8,8 @@ export const commandProjectsRm = Command.make("rm").pipe(
8
8
  Command.withDescription("Remove a project"),
9
9
  Command.withHandler(
10
10
  Effect.fnUntraced(function* () {
11
+ const fs = yield* FileSystem.FileSystem
12
+ const pathService = yield* Path.Path
11
13
  const projects = yield* getAllProjects
12
14
  if (projects.length === 0) {
13
15
  return yield* Effect.log("There are no projects to remove.")
@@ -15,6 +17,14 @@ export const commandProjectsRm = Command.make("rm").pipe(
15
17
  const project = yield* selectProject
16
18
  const newProjects = projects.filter((p) => p.id !== project.id)
17
19
  yield* Settings.set(allProjects, Option.some(newProjects))
20
+ const kvsPath = pathService.join(
21
+ ".lalph",
22
+ "projects",
23
+ encodeURIComponent(project.id),
24
+ )
25
+ if (yield* fs.exists(kvsPath)) {
26
+ yield* fs.remove(kvsPath)
27
+ }
18
28
  }),
19
29
  ),
20
30
  Command.provide(Settings.layer),
@@ -1,62 +1,70 @@
1
1
  import * as S from "effect/Schema"
2
2
 
3
- export class Author extends S.Class<Author>("github/Author")({
3
+ export class Author extends S.Class<Author>("Author")({
4
4
  login: S.String,
5
5
  }) {}
6
6
 
7
- export class Comment extends S.Class<Comment>("github/Comment")({
7
+ export class Comment extends S.Class<Comment>("Comment")({
8
8
  id: S.String,
9
9
  body: S.String,
10
10
  author: Author,
11
- // createdAt: S.String,
12
- }) {}
13
-
14
- export class CommentsEdge extends S.Class<CommentsEdge>("github/CommentsEdge")({
15
- node: Comment,
11
+ createdAt: S.String,
16
12
  }) {}
17
13
 
18
14
  export class PullRequestComments extends S.Class<PullRequestComments>(
19
15
  "PullRequestComments",
20
16
  )({
21
- edges: S.Array(CommentsEdge),
17
+ nodes: S.Array(Comment),
22
18
  }) {}
23
19
 
24
- export class PullRequest extends S.Class<PullRequest>("github/PullRequest")({
20
+ export class PullRequest extends S.Class<PullRequest>("PullRequest")({
25
21
  url: S.String,
22
+ reviewDecision: S.Null,
23
+ reviews: S.suspend(() => Reviews),
26
24
  reviewThreads: S.suspend(() => ReviewThreads),
27
25
  comments: PullRequestComments,
28
26
  }) {}
29
27
 
30
- export class Repository extends S.Class<Repository>("github/Repository")({
28
+ export class Repository extends S.Class<Repository>("Repository")({
31
29
  pullRequest: PullRequest,
32
30
  }) {}
33
31
 
34
- export class Data extends S.Class<Data>("github/Data")({
32
+ export class Data extends S.Class<Data>("Data")({
35
33
  repository: Repository,
36
34
  }) {}
37
35
 
38
- export class CommentsData extends S.Class<CommentsData>("github/CommentsData")({
36
+ export class GithubPullRequestData extends S.Class<GithubPullRequestData>(
37
+ "GithubPullRequestData",
38
+ )({
39
39
  data: Data,
40
40
  }) {}
41
41
 
42
- export class ReviewComment extends S.Class<ReviewComment>(
43
- "github/ReviewComment",
44
- )({
42
+ export class Review extends S.Class<Review>("Review")({
43
+ id: S.String,
44
+ author: Author,
45
+ body: S.String,
46
+ }) {}
47
+
48
+ export class Reviews extends S.Class<Reviews>("Reviews")({
49
+ nodes: S.Array(Review),
50
+ }) {}
51
+
52
+ export class ReviewComment extends S.Class<ReviewComment>("ReviewComment")({
45
53
  id: S.String,
46
54
  author: Author,
47
55
  body: S.String,
48
56
  path: S.String,
49
- originalLine: S.NullOr(S.Number),
57
+ originalLine: S.Number,
50
58
  diffHunk: S.String,
51
- // createdAt: S.String,
59
+ createdAt: S.String,
52
60
  }) {}
53
61
 
54
- export class NodeComments extends S.Class<NodeComments>("github/NodeComments")({
62
+ export class NodeComments extends S.Class<NodeComments>("NodeComments")({
55
63
  nodes: S.Array(ReviewComment),
56
64
  }) {}
57
65
 
58
- export class ReviewThreadNode extends S.Class<ReviewThreadNode>(
59
- "github/ReviewThreadNode",
66
+ export class ReviewThreadsNode extends S.Class<ReviewThreadsNode>(
67
+ "ReviewThreadsNode",
60
68
  )({
61
69
  isCollapsed: S.Boolean,
62
70
  isOutdated: S.Boolean,
@@ -67,14 +75,6 @@ export class ReviewThreadNode extends S.Class<ReviewThreadNode>(
67
75
  readonly shouldDisplayThread = !this.isCollapsed && !this.isOutdated
68
76
  }
69
77
 
70
- export class ReviewThreadsEdge extends S.Class<ReviewThreadsEdge>(
71
- "ReviewThreadsEdge",
72
- )({
73
- node: ReviewThreadNode,
74
- }) {}
75
-
76
- export class ReviewThreads extends S.Class<ReviewThreads>(
77
- "github/ReviewThreads",
78
- )({
79
- edges: S.Array(ReviewThreadsEdge),
78
+ export class ReviewThreads extends S.Class<ReviewThreads>("ReviewThreads")({
79
+ nodes: S.Array(ReviewThreadsNode),
80
80
  }) {}