@sentry/junior-github 0.107.1 → 0.109.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/README.md +3 -3
- package/SETUP.md +13 -8
- package/dist/db/database.d.ts +5 -0
- package/dist/db/schema.d.ts +470 -0
- package/dist/index.js +775 -139
- package/dist/issue-outcomes/store.d.ts +30 -0
- package/dist/{pull-request-outcomes → outcomes}/report.d.ts +2 -2
- package/dist/pull-request-outcomes/commit-composition.d.ts +6 -0
- package/dist/pull-request-outcomes/store.d.ts +18 -5
- package/dist/tools/footer.d.ts +2 -0
- package/dist/webhooks/handler.d.ts +8 -2
- package/dist/webhooks/issue-outcome.d.ts +6 -0
- package/dist/webhooks/ownership.d.ts +2 -0
- package/dist/webhooks/pull-request-outcome.d.ts +6 -1
- package/migrations/0001_issue_outcomes.sql +14 -0
- package/migrations/0002_pull_request_commit_composition.sql +1 -0
- package/migrations/0003_pull_request_conversations.sql +1 -0
- package/migrations/0004_marvelous_toad_men.sql +5 -0
- package/migrations/meta/0001_snapshot.json +256 -0
- package/migrations/meta/0002_snapshot.json +262 -0
- package/migrations/meta/0003_snapshot.json +269 -0
- package/migrations/meta/0004_snapshot.json +275 -0
- package/migrations/meta/_journal.json +28 -0
- package/package.json +2 -2
- package/skills/attach-github-assets/SOURCES.md +2 -2
- package/skills/attach-github-assets/SPEC.md +2 -2
- package/skills/github-code/SKILL.md +3 -14
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { GitHubDb } from "../db/database.js";
|
|
3
|
+
declare const githubIssueOutcomeInputSchema: z.ZodObject<{
|
|
4
|
+
candidateOwned: z.ZodBoolean;
|
|
5
|
+
closedAt: z.ZodOptional<z.ZodDate>;
|
|
6
|
+
issueId: z.ZodString;
|
|
7
|
+
number: z.ZodNumber;
|
|
8
|
+
openedAt: z.ZodDate;
|
|
9
|
+
repositoryFullName: z.ZodString;
|
|
10
|
+
repositoryId: z.ZodString;
|
|
11
|
+
state: z.ZodEnum<{
|
|
12
|
+
open: "open";
|
|
13
|
+
closed: "closed";
|
|
14
|
+
}>;
|
|
15
|
+
stateReason: z.ZodOptional<z.ZodEnum<{
|
|
16
|
+
completed: "completed";
|
|
17
|
+
duplicate: "duplicate";
|
|
18
|
+
not_planned: "not_planned";
|
|
19
|
+
reopened: "reopened";
|
|
20
|
+
}>>;
|
|
21
|
+
updatedAt: z.ZodDate;
|
|
22
|
+
}, z.core.$strict>;
|
|
23
|
+
export type GitHubIssueOutcomeInput = z.output<typeof githubIssueOutcomeInputSchema>;
|
|
24
|
+
/**
|
|
25
|
+
* Record the newest lifecycle projection for one Junior-owned issue.
|
|
26
|
+
* Ownership-qualified opening or closing events insert; later events update
|
|
27
|
+
* existing rows, and older provider timestamps cannot regress them.
|
|
28
|
+
*/
|
|
29
|
+
export declare function recordGitHubIssueOutcome(db: GitHubDb, input: GitHubIssueOutcomeInput): Promise<void>;
|
|
30
|
+
export {};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PluginOperationalReportContent } from "@sentry/junior-plugin-api";
|
|
2
|
-
import type { GitHubDb } from "
|
|
3
|
-
/** Build the generic dashboard report for Junior-owned
|
|
2
|
+
import type { GitHubDb } from "../db/database.js";
|
|
3
|
+
/** Build the generic dashboard report for Junior-owned GitHub work outcomes. */
|
|
4
4
|
export declare function buildGitHubOutcomeReport(args: {
|
|
5
5
|
db: GitHubDb;
|
|
6
6
|
nowMs: number;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { GitHubPullRequestCommitComposition } from "../db/schema.js";
|
|
2
|
+
/** Classify whether every Git author in a PR commit list is Junior's bot. */
|
|
3
|
+
export declare function classifyGitHubPullRequestCommitComposition(args: {
|
|
4
|
+
botEmail: string;
|
|
5
|
+
loadPage(page: number, perPage: number): Promise<unknown>;
|
|
6
|
+
}): Promise<GitHubPullRequestCommitComposition | undefined>;
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import type { PgDatabase } from "drizzle-orm/pg-core";
|
|
2
|
-
import type { PgQueryResultHKT } from "drizzle-orm/pg-core/session";
|
|
3
1
|
import { z } from "zod";
|
|
4
|
-
import {
|
|
5
|
-
|
|
2
|
+
import type { GitHubDb } from "../db/database.js";
|
|
3
|
+
import { type GitHubPullRequestCommitComposition } from "../db/schema.js";
|
|
6
4
|
declare const githubPullRequestOutcomeInputSchema: z.ZodObject<{
|
|
7
5
|
candidateOwned: z.ZodBoolean;
|
|
8
6
|
closedAt: z.ZodOptional<z.ZodDate>;
|
|
7
|
+
commitComposition: z.ZodOptional<z.ZodEnum<{
|
|
8
|
+
junior_only: "junior_only";
|
|
9
|
+
mixed: "mixed";
|
|
10
|
+
}>>;
|
|
9
11
|
mergedAt: z.ZodOptional<z.ZodDate>;
|
|
10
12
|
number: z.ZodNumber;
|
|
11
13
|
openedAt: z.ZodDate;
|
|
@@ -20,10 +22,21 @@ declare const githubPullRequestOutcomeInputSchema: z.ZodObject<{
|
|
|
20
22
|
updatedAt: z.ZodDate;
|
|
21
23
|
}, z.core.$strict>;
|
|
22
24
|
export type GitHubPullRequestOutcomeInput = z.output<typeof githubPullRequestOutcomeInputSchema>;
|
|
25
|
+
declare const githubPullRequestConversationsInputSchema: z.ZodObject<{
|
|
26
|
+
conversationIds: z.ZodArray<z.ZodString>;
|
|
27
|
+
pullRequestId: z.ZodString;
|
|
28
|
+
}, z.core.$strict>;
|
|
29
|
+
export type GitHubPullRequestConversationsInput = z.output<typeof githubPullRequestConversationsInputSchema>;
|
|
23
30
|
/**
|
|
24
31
|
* Record the newest lifecycle projection for one Junior-owned pull request.
|
|
25
32
|
* An ownership-qualified opening or terminal recovery inserts; later events
|
|
26
33
|
* update existing rows, and older provider timestamps cannot regress it.
|
|
34
|
+
* Returns the written row state so follow-up enrichment can remain idempotent.
|
|
27
35
|
*/
|
|
28
|
-
export declare function recordGitHubPullRequestOutcome(db: GitHubDb, input: GitHubPullRequestOutcomeInput): Promise<
|
|
36
|
+
export declare function recordGitHubPullRequestOutcome(db: GitHubDb, input: GitHubPullRequestOutcomeInput): Promise<{
|
|
37
|
+
applied: boolean;
|
|
38
|
+
commitComposition: GitHubPullRequestCommitComposition | undefined;
|
|
39
|
+
}>;
|
|
40
|
+
/** Append native conversation ids to an existing Junior-owned PR projection. */
|
|
41
|
+
export declare function recordGitHubPullRequestConversations(db: GitHubDb, input: GitHubPullRequestConversationsInput): Promise<boolean>;
|
|
29
42
|
export {};
|
package/dist/tools/footer.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ export declare const GITHUB_SESSION_FOOTER_END = "<!-- junior-session-footer:end
|
|
|
3
3
|
export declare function sentryConversationUrl(conversationId: string): string | undefined;
|
|
4
4
|
/** Build the Junior session footer, preferring a host-provided dashboard link. */
|
|
5
5
|
export declare function githubConversationFooter(conversationId: string, dashboardUrl?: string): string | undefined;
|
|
6
|
+
/** Read opaque native conversation ids from Junior-owned GitHub footers. */
|
|
7
|
+
export declare function githubConversationIds(body: string | null | undefined): string[];
|
|
6
8
|
/**
|
|
7
9
|
* Append (or replace an existing) Junior session footer to a GitHub body string.
|
|
8
10
|
* Without a dashboard or Sentry link, returns the body unchanged (existing footer stripped).
|
|
@@ -1,9 +1,15 @@
|
|
|
1
|
-
import type { PluginRoute, ResourceEventPublisher } from "@sentry/junior-plugin-api";
|
|
2
|
-
import type { GitHubDb } from "../
|
|
1
|
+
import type { PluginLogger, PluginRoute, ResourceEventPublisher } from "@sentry/junior-plugin-api";
|
|
2
|
+
import type { GitHubDb } from "../db/database.js";
|
|
3
|
+
import type { GitHubPullRequestCommitComposition } from "../db/schema.js";
|
|
3
4
|
/** Create the public, signed GitHub webhook route owned by the plugin. */
|
|
4
5
|
export declare function createGitHubWebhookRoute(args: {
|
|
5
6
|
botEmail(): string | undefined;
|
|
7
|
+
classifyPullRequestCommits?(input: {
|
|
8
|
+
number: number;
|
|
9
|
+
repositoryFullName: string;
|
|
10
|
+
}): Promise<GitHubPullRequestCommitComposition | undefined>;
|
|
6
11
|
db: GitHubDb;
|
|
12
|
+
log?: Pick<PluginLogger, "error">;
|
|
7
13
|
resourceEvents: ResourceEventPublisher;
|
|
8
14
|
webhookSecret(): string | undefined;
|
|
9
15
|
}): PluginRoute;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { GitHubIssueOutcomeInput } from "../issue-outcomes/store.js";
|
|
2
|
+
/** Normalize only the fields required for the Junior-owned issue projection. */
|
|
3
|
+
export declare function normalizeGitHubIssueOutcome(args: {
|
|
4
|
+
body: unknown;
|
|
5
|
+
botEmail?: string;
|
|
6
|
+
}): GitHubIssueOutcomeInput | undefined;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import type { GitHubPullRequestOutcomeInput } from "../pull-request-outcomes/store.js";
|
|
1
|
+
import type { GitHubPullRequestConversationsInput, GitHubPullRequestOutcomeInput } from "../pull-request-outcomes/store.js";
|
|
2
2
|
/** Normalize only the fields required for the Junior-owned PR projection. */
|
|
3
3
|
export declare function normalizeGitHubPullRequestOutcome(args: {
|
|
4
4
|
body: unknown;
|
|
5
5
|
botEmail?: string;
|
|
6
6
|
}): GitHubPullRequestOutcomeInput | undefined;
|
|
7
|
+
/** Normalize native conversation ids written to a Junior-owned PR by its bot. */
|
|
8
|
+
export declare function normalizeGitHubPullRequestConversations(args: {
|
|
9
|
+
body: unknown;
|
|
10
|
+
botEmail?: string;
|
|
11
|
+
}): GitHubPullRequestConversationsInput | undefined;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
CREATE TABLE "junior_github_issues" (
|
|
2
|
+
"issue_id" text PRIMARY KEY NOT NULL,
|
|
3
|
+
"repository_id" text NOT NULL,
|
|
4
|
+
"repository_full_name" text NOT NULL,
|
|
5
|
+
"number" integer NOT NULL,
|
|
6
|
+
"state" text NOT NULL,
|
|
7
|
+
"opened_at" timestamp with time zone NOT NULL,
|
|
8
|
+
"closed_at" timestamp with time zone,
|
|
9
|
+
"updated_at" timestamp with time zone NOT NULL
|
|
10
|
+
);
|
|
11
|
+
--> statement-breakpoint
|
|
12
|
+
CREATE INDEX "junior_github_issues_opened_at_idx" ON "junior_github_issues" USING btree ("opened_at");--> statement-breakpoint
|
|
13
|
+
CREATE INDEX "junior_github_issues_closed_at_idx" ON "junior_github_issues" USING btree ("closed_at");--> statement-breakpoint
|
|
14
|
+
CREATE INDEX "junior_github_issues_open_idx" ON "junior_github_issues" USING btree ("issue_id") WHERE "junior_github_issues"."state" = 'open';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE "junior_github_pull_requests" ADD COLUMN "commit_composition" text;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE "junior_github_pull_requests" ADD COLUMN "conversation_ids" text[] DEFAULT ARRAY[]::text[] NOT NULL;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "8be552be-bd1e-4cdf-8784-ed1b84438690",
|
|
3
|
+
"prevId": "2c771be0-ac94-4eff-a69a-ade43801be34",
|
|
4
|
+
"version": "7",
|
|
5
|
+
"dialect": "postgresql",
|
|
6
|
+
"tables": {
|
|
7
|
+
"public.junior_github_issues": {
|
|
8
|
+
"name": "junior_github_issues",
|
|
9
|
+
"schema": "",
|
|
10
|
+
"columns": {
|
|
11
|
+
"issue_id": {
|
|
12
|
+
"name": "issue_id",
|
|
13
|
+
"type": "text",
|
|
14
|
+
"primaryKey": true,
|
|
15
|
+
"notNull": true
|
|
16
|
+
},
|
|
17
|
+
"repository_id": {
|
|
18
|
+
"name": "repository_id",
|
|
19
|
+
"type": "text",
|
|
20
|
+
"primaryKey": false,
|
|
21
|
+
"notNull": true
|
|
22
|
+
},
|
|
23
|
+
"repository_full_name": {
|
|
24
|
+
"name": "repository_full_name",
|
|
25
|
+
"type": "text",
|
|
26
|
+
"primaryKey": false,
|
|
27
|
+
"notNull": true
|
|
28
|
+
},
|
|
29
|
+
"number": {
|
|
30
|
+
"name": "number",
|
|
31
|
+
"type": "integer",
|
|
32
|
+
"primaryKey": false,
|
|
33
|
+
"notNull": true
|
|
34
|
+
},
|
|
35
|
+
"state": {
|
|
36
|
+
"name": "state",
|
|
37
|
+
"type": "text",
|
|
38
|
+
"primaryKey": false,
|
|
39
|
+
"notNull": true
|
|
40
|
+
},
|
|
41
|
+
"opened_at": {
|
|
42
|
+
"name": "opened_at",
|
|
43
|
+
"type": "timestamp with time zone",
|
|
44
|
+
"primaryKey": false,
|
|
45
|
+
"notNull": true
|
|
46
|
+
},
|
|
47
|
+
"closed_at": {
|
|
48
|
+
"name": "closed_at",
|
|
49
|
+
"type": "timestamp with time zone",
|
|
50
|
+
"primaryKey": false,
|
|
51
|
+
"notNull": false
|
|
52
|
+
},
|
|
53
|
+
"updated_at": {
|
|
54
|
+
"name": "updated_at",
|
|
55
|
+
"type": "timestamp with time zone",
|
|
56
|
+
"primaryKey": false,
|
|
57
|
+
"notNull": true
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"indexes": {
|
|
61
|
+
"junior_github_issues_opened_at_idx": {
|
|
62
|
+
"name": "junior_github_issues_opened_at_idx",
|
|
63
|
+
"columns": [
|
|
64
|
+
{
|
|
65
|
+
"expression": "opened_at",
|
|
66
|
+
"isExpression": false,
|
|
67
|
+
"asc": true,
|
|
68
|
+
"nulls": "last"
|
|
69
|
+
}
|
|
70
|
+
],
|
|
71
|
+
"isUnique": false,
|
|
72
|
+
"concurrently": false,
|
|
73
|
+
"method": "btree",
|
|
74
|
+
"with": {}
|
|
75
|
+
},
|
|
76
|
+
"junior_github_issues_closed_at_idx": {
|
|
77
|
+
"name": "junior_github_issues_closed_at_idx",
|
|
78
|
+
"columns": [
|
|
79
|
+
{
|
|
80
|
+
"expression": "closed_at",
|
|
81
|
+
"isExpression": false,
|
|
82
|
+
"asc": true,
|
|
83
|
+
"nulls": "last"
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
"isUnique": false,
|
|
87
|
+
"concurrently": false,
|
|
88
|
+
"method": "btree",
|
|
89
|
+
"with": {}
|
|
90
|
+
},
|
|
91
|
+
"junior_github_issues_open_idx": {
|
|
92
|
+
"name": "junior_github_issues_open_idx",
|
|
93
|
+
"columns": [
|
|
94
|
+
{
|
|
95
|
+
"expression": "issue_id",
|
|
96
|
+
"isExpression": false,
|
|
97
|
+
"asc": true,
|
|
98
|
+
"nulls": "last"
|
|
99
|
+
}
|
|
100
|
+
],
|
|
101
|
+
"isUnique": false,
|
|
102
|
+
"where": "\"junior_github_issues\".\"state\" = 'open'",
|
|
103
|
+
"concurrently": false,
|
|
104
|
+
"method": "btree",
|
|
105
|
+
"with": {}
|
|
106
|
+
}
|
|
107
|
+
},
|
|
108
|
+
"foreignKeys": {},
|
|
109
|
+
"compositePrimaryKeys": {},
|
|
110
|
+
"uniqueConstraints": {},
|
|
111
|
+
"policies": {},
|
|
112
|
+
"checkConstraints": {},
|
|
113
|
+
"isRLSEnabled": false
|
|
114
|
+
},
|
|
115
|
+
"public.junior_github_pull_requests": {
|
|
116
|
+
"name": "junior_github_pull_requests",
|
|
117
|
+
"schema": "",
|
|
118
|
+
"columns": {
|
|
119
|
+
"pull_request_id": {
|
|
120
|
+
"name": "pull_request_id",
|
|
121
|
+
"type": "text",
|
|
122
|
+
"primaryKey": true,
|
|
123
|
+
"notNull": true
|
|
124
|
+
},
|
|
125
|
+
"repository_id": {
|
|
126
|
+
"name": "repository_id",
|
|
127
|
+
"type": "text",
|
|
128
|
+
"primaryKey": false,
|
|
129
|
+
"notNull": true
|
|
130
|
+
},
|
|
131
|
+
"repository_full_name": {
|
|
132
|
+
"name": "repository_full_name",
|
|
133
|
+
"type": "text",
|
|
134
|
+
"primaryKey": false,
|
|
135
|
+
"notNull": true
|
|
136
|
+
},
|
|
137
|
+
"number": {
|
|
138
|
+
"name": "number",
|
|
139
|
+
"type": "integer",
|
|
140
|
+
"primaryKey": false,
|
|
141
|
+
"notNull": true
|
|
142
|
+
},
|
|
143
|
+
"state": {
|
|
144
|
+
"name": "state",
|
|
145
|
+
"type": "text",
|
|
146
|
+
"primaryKey": false,
|
|
147
|
+
"notNull": true
|
|
148
|
+
},
|
|
149
|
+
"opened_at": {
|
|
150
|
+
"name": "opened_at",
|
|
151
|
+
"type": "timestamp with time zone",
|
|
152
|
+
"primaryKey": false,
|
|
153
|
+
"notNull": true
|
|
154
|
+
},
|
|
155
|
+
"merged_at": {
|
|
156
|
+
"name": "merged_at",
|
|
157
|
+
"type": "timestamp with time zone",
|
|
158
|
+
"primaryKey": false,
|
|
159
|
+
"notNull": false
|
|
160
|
+
},
|
|
161
|
+
"closed_at": {
|
|
162
|
+
"name": "closed_at",
|
|
163
|
+
"type": "timestamp with time zone",
|
|
164
|
+
"primaryKey": false,
|
|
165
|
+
"notNull": false
|
|
166
|
+
},
|
|
167
|
+
"updated_at": {
|
|
168
|
+
"name": "updated_at",
|
|
169
|
+
"type": "timestamp with time zone",
|
|
170
|
+
"primaryKey": false,
|
|
171
|
+
"notNull": true
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
"indexes": {
|
|
175
|
+
"junior_github_pull_requests_opened_at_idx": {
|
|
176
|
+
"name": "junior_github_pull_requests_opened_at_idx",
|
|
177
|
+
"columns": [
|
|
178
|
+
{
|
|
179
|
+
"expression": "opened_at",
|
|
180
|
+
"isExpression": false,
|
|
181
|
+
"asc": true,
|
|
182
|
+
"nulls": "last"
|
|
183
|
+
}
|
|
184
|
+
],
|
|
185
|
+
"isUnique": false,
|
|
186
|
+
"concurrently": false,
|
|
187
|
+
"method": "btree",
|
|
188
|
+
"with": {}
|
|
189
|
+
},
|
|
190
|
+
"junior_github_pull_requests_merged_at_idx": {
|
|
191
|
+
"name": "junior_github_pull_requests_merged_at_idx",
|
|
192
|
+
"columns": [
|
|
193
|
+
{
|
|
194
|
+
"expression": "merged_at",
|
|
195
|
+
"isExpression": false,
|
|
196
|
+
"asc": true,
|
|
197
|
+
"nulls": "last"
|
|
198
|
+
}
|
|
199
|
+
],
|
|
200
|
+
"isUnique": false,
|
|
201
|
+
"concurrently": false,
|
|
202
|
+
"method": "btree",
|
|
203
|
+
"with": {}
|
|
204
|
+
},
|
|
205
|
+
"junior_github_pull_requests_closed_at_idx": {
|
|
206
|
+
"name": "junior_github_pull_requests_closed_at_idx",
|
|
207
|
+
"columns": [
|
|
208
|
+
{
|
|
209
|
+
"expression": "closed_at",
|
|
210
|
+
"isExpression": false,
|
|
211
|
+
"asc": true,
|
|
212
|
+
"nulls": "last"
|
|
213
|
+
}
|
|
214
|
+
],
|
|
215
|
+
"isUnique": false,
|
|
216
|
+
"concurrently": false,
|
|
217
|
+
"method": "btree",
|
|
218
|
+
"with": {}
|
|
219
|
+
},
|
|
220
|
+
"junior_github_pull_requests_open_idx": {
|
|
221
|
+
"name": "junior_github_pull_requests_open_idx",
|
|
222
|
+
"columns": [
|
|
223
|
+
{
|
|
224
|
+
"expression": "pull_request_id",
|
|
225
|
+
"isExpression": false,
|
|
226
|
+
"asc": true,
|
|
227
|
+
"nulls": "last"
|
|
228
|
+
}
|
|
229
|
+
],
|
|
230
|
+
"isUnique": false,
|
|
231
|
+
"where": "\"junior_github_pull_requests\".\"state\" = 'open'",
|
|
232
|
+
"concurrently": false,
|
|
233
|
+
"method": "btree",
|
|
234
|
+
"with": {}
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
"foreignKeys": {},
|
|
238
|
+
"compositePrimaryKeys": {},
|
|
239
|
+
"uniqueConstraints": {},
|
|
240
|
+
"policies": {},
|
|
241
|
+
"checkConstraints": {},
|
|
242
|
+
"isRLSEnabled": false
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
"enums": {},
|
|
246
|
+
"schemas": {},
|
|
247
|
+
"sequences": {},
|
|
248
|
+
"roles": {},
|
|
249
|
+
"policies": {},
|
|
250
|
+
"views": {},
|
|
251
|
+
"_meta": {
|
|
252
|
+
"columns": {},
|
|
253
|
+
"schemas": {},
|
|
254
|
+
"tables": {}
|
|
255
|
+
}
|
|
256
|
+
}
|