@xpufx/paseo-forges 0.1.0 → 0.1.2
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/client/active-forge.ts +1 -1
- package/client/board-alert.tsx +1 -1
- package/client/foreign-link.tsx +1 -1
- package/client/hook-queue-panel.tsx +1 -1
- package/client/issues-pill.tsx +2 -2
- package/client/label-chip.tsx +1 -1
- package/client/linkifier.tsx +1 -1
- package/client/notification-card.tsx +71 -0
- package/client/vendor/paseo-plugin-helper/utils/clipboard.ts +11 -4
- package/client/webhook-card.tsx +1 -1
- package/index.client.tsx +127 -0
- package/index.server.ts +63 -0
- package/package.json +4 -2
- package/server/issues.ts +2 -2
- package/server/settings.ts +1 -1
- package/shared/hook-queue.ts +1 -1
- package/shared/issues.ts +1 -1
- package/shared/notification.ts +33 -0
package/client/active-forge.ts
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
resolveForgeTarget,
|
|
9
9
|
type ForgeRepoIdentity,
|
|
10
10
|
} from "../shared/issues.js";
|
|
11
|
-
import { usePluginSettings, useRpcQuery } from "paseo-plugin-helper/
|
|
11
|
+
import { usePluginSettings, useRpcQuery } from "./vendor/paseo-plugin-helper/index";
|
|
12
12
|
|
|
13
13
|
/** Workspace directory backing an agent, or undefined while it cannot resolve. */
|
|
14
14
|
export function useAgentDirectory(agentId: string): string | undefined {
|
package/client/board-alert.tsx
CHANGED
package/client/foreign-link.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { Text } from "react-native";
|
|
3
|
-
import { Badge } from "paseo-plugin-helper/
|
|
3
|
+
import { Badge } from "./vendor/paseo-plugin-helper/index";
|
|
4
4
|
|
|
5
5
|
/** Shared label for the cross-repo marker, so every surface reads the same. */
|
|
6
6
|
export const FOREIGN_LINK_LABEL = "foreign";
|
package/client/issues-pill.tsx
CHANGED
|
@@ -31,8 +31,8 @@ import {
|
|
|
31
31
|
getClientHost,
|
|
32
32
|
type RenderModalProps,
|
|
33
33
|
type RenderPillProps,
|
|
34
|
-
} from "paseo-plugin-helper/
|
|
35
|
-
import { hasFuzzyHighlight, normalizeSearchQuery } from "paseo-plugin-helper/
|
|
34
|
+
} from "./vendor/paseo-plugin-helper/index";
|
|
35
|
+
import { hasFuzzyHighlight, normalizeSearchQuery } from "../shared/vendor/paseo-plugin-helper/index";
|
|
36
36
|
import { HookQueueView } from "./hook-queue-panel.js";
|
|
37
37
|
import {
|
|
38
38
|
ATTENTION_LABELS,
|
package/client/label-chip.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { View, type StyleProp, type ViewStyle } from "react-native";
|
|
3
|
-
import { Badge, usePluginTheme } from "paseo-plugin-helper/
|
|
3
|
+
import { Badge, usePluginTheme } from "./vendor/paseo-plugin-helper/index";
|
|
4
4
|
import { planLabelChip, type ForgeLabel, type LabelChipHalf } from "../shared/issues.js";
|
|
5
5
|
|
|
6
6
|
/** Inner corners join the halves; the scope's right border becomes the divider. */
|
package/client/linkifier.tsx
CHANGED
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
} from "@getpaseo/plugin/client";
|
|
9
9
|
import type { PluginTheme } from "@getpaseo/plugin";
|
|
10
10
|
import { Icon } from "@getpaseo/plugin/client/react-native";
|
|
11
|
-
import { ForgeIcon, copyToClipboard } from "paseo-plugin-helper/
|
|
11
|
+
import { ForgeIcon, copyToClipboard } from "./vendor/paseo-plugin-helper/index";
|
|
12
12
|
import {
|
|
13
13
|
classifyForgeLink,
|
|
14
14
|
classifyForgeUrl,
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { StyleSheet, Text, View } from "react-native";
|
|
3
|
+
import type {
|
|
4
|
+
PluginTimelineItemProps,
|
|
5
|
+
PluginTimelineRendererContribution,
|
|
6
|
+
PluginTimelineTransformerContribution,
|
|
7
|
+
} from "@getpaseo/plugin/client";
|
|
8
|
+
import { Icon } from "@getpaseo/plugin/client/react-native";
|
|
9
|
+
import { Badge } from "./vendor/paseo-plugin-helper/index";
|
|
10
|
+
import {
|
|
11
|
+
forgejoNotificationCardSchema,
|
|
12
|
+
forgejoNotificationItem,
|
|
13
|
+
type ForgejoNotificationCardData,
|
|
14
|
+
} from "../shared/notification.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Forgejo digest notifications are emitted as host `notification` rows rather
|
|
18
|
+
* than chat messages. Turn them into a typed card without claiming unrelated
|
|
19
|
+
* notifications (including provider errors and other plugins' notices).
|
|
20
|
+
*/
|
|
21
|
+
export const forgejoNotificationTransformer: PluginTimelineTransformerContribution<"notification"> = {
|
|
22
|
+
id: "forgejo-notification",
|
|
23
|
+
query: { itemType: "notification" },
|
|
24
|
+
transform({ item }) {
|
|
25
|
+
return forgejoNotificationItem(item);
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function levelColor(
|
|
30
|
+
theme: PluginTimelineItemProps<ForgejoNotificationCardData>["theme"],
|
|
31
|
+
level: ForgejoNotificationCardData["level"],
|
|
32
|
+
) {
|
|
33
|
+
if (level === "error") return theme.colors.statusDanger;
|
|
34
|
+
if (level === "warning") return theme.colors.statusWarning;
|
|
35
|
+
return theme.colors.accent;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function ForgejoNotificationCard({
|
|
39
|
+
item,
|
|
40
|
+
theme,
|
|
41
|
+
}: PluginTimelineItemProps<ForgejoNotificationCardData>) {
|
|
42
|
+
const color = levelColor(theme, item.data.level);
|
|
43
|
+
return (
|
|
44
|
+
<View style={[styles.card, { backgroundColor: theme.colors.surface1, borderColor: theme.colors.border }]}>
|
|
45
|
+
<View style={styles.header}>
|
|
46
|
+
<Icon name="Bell" size={14} color={color} />
|
|
47
|
+
<Text style={[styles.title, { color: theme.colors.foreground }]}>Forgejo digest</Text>
|
|
48
|
+
<Badge variant={item.data.level === "error" ? "danger" : item.data.level === "warning" ? "warning" : "info"} label={item.data.level} />
|
|
49
|
+
</View>
|
|
50
|
+
<Text style={[styles.message, { color: theme.colors.foreground }]}>{item.data.message}</Text>
|
|
51
|
+
<Text style={[styles.footer, { color: theme.colors.foregroundMuted }]}>via forges</Text>
|
|
52
|
+
</View>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const forgejoNotificationRenderer: PluginTimelineRendererContribution<
|
|
57
|
+
typeof forgejoNotificationCardSchema
|
|
58
|
+
> = {
|
|
59
|
+
kind: "forgejo-notification",
|
|
60
|
+
version: 1,
|
|
61
|
+
schema: forgejoNotificationCardSchema,
|
|
62
|
+
Component: ForgejoNotificationCard,
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const styles = StyleSheet.create({
|
|
66
|
+
card: { borderRadius: 8, borderWidth: 1, padding: 8, marginVertical: 2, gap: 6 },
|
|
67
|
+
header: { flexDirection: "row", alignItems: "center", gap: 6 },
|
|
68
|
+
title: { fontSize: 12, fontWeight: "600", flex: 1 },
|
|
69
|
+
message: { fontSize: 13 },
|
|
70
|
+
footer: { fontSize: 10, fontStyle: "italic" },
|
|
71
|
+
});
|
|
@@ -24,16 +24,23 @@ export interface ClipboardEnvironment {
|
|
|
24
24
|
* `react-native-web` `Clipboard.setString` reports success even when its
|
|
25
25
|
* `document.execCommand("copy")` fails, which leaves the previous clipboard
|
|
26
26
|
* item in place while the UI claims success (xpufx-org/paseo#278); it is only
|
|
27
|
-
* usable off-DOM (native), where it is the real platform clipboard.
|
|
28
|
-
*
|
|
27
|
+
* usable off-DOM (native), where it is the real platform clipboard. The same
|
|
28
|
+
* applies to any `setStringAsync` whose DOM fallback is that unverified
|
|
29
|
+
* `execCommand`. In a DOM the checked `execCommand` fallback is preferred to
|
|
30
|
+
* those unverifiable paths.
|
|
29
31
|
*/
|
|
30
32
|
export function clipboardTierOrder(env: ClipboardEnvironment): ClipboardTier[] {
|
|
31
33
|
const tiers: ClipboardTier[] = [];
|
|
32
34
|
if (env.hasNavigatorClipboard) tiers.push("navigator");
|
|
33
35
|
if (env.hasHostCopyText) tiers.push("host");
|
|
34
|
-
|
|
36
|
+
// On a DOM both RN-web paths are unverifiable: `setString` reports success
|
|
37
|
+
// even when its `execCommand` no-ops, and a `setStringAsync` that falls back
|
|
38
|
+
// to it does the same. Off-DOM they are the real native clipboard, so they
|
|
39
|
+
// lead there; on a DOM only the checked `execCommand` fallback is honest.
|
|
40
|
+
const rnDomOk = !env.isDom;
|
|
41
|
+
if (env.hasRnSetStringAsync && rnDomOk) {
|
|
35
42
|
tiers.push("rnAsync");
|
|
36
|
-
} else if (env.hasRnClipboard &&
|
|
43
|
+
} else if (env.hasRnClipboard && rnDomOk) {
|
|
37
44
|
tiers.push("rnSync");
|
|
38
45
|
}
|
|
39
46
|
tiers.push("execCommand");
|
package/client/webhook-card.tsx
CHANGED
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
PluginTimelineTransformerContribution,
|
|
7
7
|
} from "@getpaseo/plugin/client";
|
|
8
8
|
import type { PluginTheme } from "@getpaseo/plugin";
|
|
9
|
-
import { Badge, ForgeIcon, Icon } from "paseo-plugin-helper/
|
|
9
|
+
import { Badge, ForgeIcon, Icon } from "./vendor/paseo-plugin-helper/index";
|
|
10
10
|
import {
|
|
11
11
|
forgejoWebhookCardSchema,
|
|
12
12
|
forgejoWebhookItem,
|
package/index.client.tsx
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { PluginClientContext } from "@getpaseo/plugin/client";
|
|
2
|
+
import { useRpc } from "@getpaseo/plugin/client";
|
|
3
|
+
import { Icon, Modal, useToast, ScrollView, FlatList, TextInput as HostTextInput, copyText } from "@getpaseo/plugin/client/react-native";
|
|
4
|
+
import {
|
|
5
|
+
initClientHelpers,
|
|
6
|
+
registerComposerPill,
|
|
7
|
+
registerSidebarSurface,
|
|
8
|
+
} from "./client/vendor/paseo-plugin-helper/index";
|
|
9
|
+
import {
|
|
10
|
+
ISSUES_PILL_ID,
|
|
11
|
+
ForgePill,
|
|
12
|
+
ForgeIssuesModal,
|
|
13
|
+
ForgeIssuesPanel,
|
|
14
|
+
} from "./client/issues-pill.js";
|
|
15
|
+
import { createForgeLabelResolver } from "./client/pill-label.js";
|
|
16
|
+
import {
|
|
17
|
+
forgeLinkUserTransformer,
|
|
18
|
+
forgeLinkAssistantTransformer,
|
|
19
|
+
forgeLinkRenderer,
|
|
20
|
+
} from "./client/linkifier.js";
|
|
21
|
+
import {
|
|
22
|
+
forgeBoardAlertUserTransformer,
|
|
23
|
+
forgeBoardAlertAssistantTransformer,
|
|
24
|
+
forgeBoardAlertRenderer,
|
|
25
|
+
} from "./client/board-alert.js";
|
|
26
|
+
import {
|
|
27
|
+
forgejoWebhookUserTransformer,
|
|
28
|
+
forgejoWebhookRenderer,
|
|
29
|
+
} from "./client/webhook-card.js";
|
|
30
|
+
import {
|
|
31
|
+
forgejoNotificationTransformer,
|
|
32
|
+
forgejoNotificationRenderer,
|
|
33
|
+
} from "./client/notification-card.js";
|
|
34
|
+
import {
|
|
35
|
+
ForgeHookQueuePanel,
|
|
36
|
+
ForgeHookQueueSurface,
|
|
37
|
+
} from "./client/hook-queue-panel.js";
|
|
38
|
+
|
|
39
|
+
initClientHelpers({ Icon, Modal, useRpc, useToast, copyText, ScrollView, FlatList, TextInput: HostTextInput });
|
|
40
|
+
|
|
41
|
+
export default function contribute(client: PluginClientContext) {
|
|
42
|
+
// Hook cards are registered before the linkifier: first-match-wins, and hook
|
|
43
|
+
// messages carry a bare issue URL the linkifier would otherwise claim.
|
|
44
|
+
const removeWebhookUser = client.addTimelineTransformer(forgejoWebhookUserTransformer);
|
|
45
|
+
const removeWebhookRenderer = client.addTimelineRenderer(forgejoWebhookRenderer);
|
|
46
|
+
const removeNotification = client.addTimelineTransformer(forgejoNotificationTransformer);
|
|
47
|
+
const removeNotificationRenderer = client.addTimelineRenderer(forgejoNotificationRenderer);
|
|
48
|
+
const removeUserLink = client.addTimelineTransformer(forgeLinkUserTransformer);
|
|
49
|
+
const removeAssistantLink = client.addTimelineTransformer(forgeLinkAssistantTransformer);
|
|
50
|
+
const removeLinkRenderer = client.addTimelineRenderer(forgeLinkRenderer);
|
|
51
|
+
const removeBoardAlertUser = client.addTimelineTransformer(forgeBoardAlertUserTransformer);
|
|
52
|
+
const removeBoardAlertAssistant = client.addTimelineTransformer(forgeBoardAlertAssistantTransformer);
|
|
53
|
+
const removeBoardAlertRenderer = client.addTimelineRenderer(forgeBoardAlertRenderer);
|
|
54
|
+
|
|
55
|
+
// RPC is host-provided and absent on some older hosts; the resolver degrades
|
|
56
|
+
// to the workspace-name / ellipsis fallback instead of failing registration.
|
|
57
|
+
const rawRpc = typeof client.rpc === "function" ? client.rpc.bind(client) : null;
|
|
58
|
+
const forgeLabel = createForgeLabelResolver({
|
|
59
|
+
rpc: (contract, input) =>
|
|
60
|
+
rawRpc
|
|
61
|
+
? (rawRpc(contract as never, input as never) as Promise<unknown>)
|
|
62
|
+
: Promise.reject(new Error("client.rpc unavailable")),
|
|
63
|
+
resolveWorkspace: async (workspaceId) => {
|
|
64
|
+
const workspace = await client.paseo.workspaces.ref(workspaceId).refresh();
|
|
65
|
+
return workspace
|
|
66
|
+
? {
|
|
67
|
+
directory: workspace.workspaceDirectory ?? undefined,
|
|
68
|
+
projectRootPath: workspace.projectRootPath,
|
|
69
|
+
}
|
|
70
|
+
: null;
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
const removePill = registerComposerPill(client, {
|
|
75
|
+
id: ISSUES_PILL_ID,
|
|
76
|
+
title: "issues",
|
|
77
|
+
modalTitle: "Forge Issues",
|
|
78
|
+
modalIcon: "GitPullRequest",
|
|
79
|
+
icon: "GitPullRequest",
|
|
80
|
+
resolveLabel: (ctx) => forgeLabel.resolve(ctx),
|
|
81
|
+
refreshIntervalMs: 5000,
|
|
82
|
+
renderPill: (props) => <ForgePill {...props} />,
|
|
83
|
+
renderModal: (props) => <ForgeIssuesModal {...props} />,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const removePanel = client.addWorkspacePanel({
|
|
87
|
+
id: "forges-issues",
|
|
88
|
+
title: "Forge Issues",
|
|
89
|
+
icon: "GitPullRequest",
|
|
90
|
+
context: "workspace",
|
|
91
|
+
locations: ["workspace", "explorer"],
|
|
92
|
+
Component: ForgeIssuesPanel,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const removeQueuePanel = client.addWorkspacePanel({
|
|
96
|
+
id: "forges-queues",
|
|
97
|
+
title: "Forge Queues",
|
|
98
|
+
icon: "Layers",
|
|
99
|
+
context: "workspace",
|
|
100
|
+
locations: ["workspace", "explorer"],
|
|
101
|
+
Component: ForgeHookQueuePanel,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const removeQueueSurface = registerSidebarSurface(client, {
|
|
105
|
+
id: "queues",
|
|
106
|
+
title: "Forge Queues",
|
|
107
|
+
icon: "Layers",
|
|
108
|
+
Component: ForgeHookQueueSurface,
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return () => {
|
|
112
|
+
removeQueueSurface?.();
|
|
113
|
+
removeQueuePanel?.();
|
|
114
|
+
removePanel();
|
|
115
|
+
removePill();
|
|
116
|
+
removeWebhookUser();
|
|
117
|
+
removeWebhookRenderer();
|
|
118
|
+
removeNotification();
|
|
119
|
+
removeNotificationRenderer();
|
|
120
|
+
removeUserLink();
|
|
121
|
+
removeAssistantLink();
|
|
122
|
+
removeLinkRenderer();
|
|
123
|
+
removeBoardAlertUser();
|
|
124
|
+
removeBoardAlertAssistant();
|
|
125
|
+
removeBoardAlertRenderer();
|
|
126
|
+
};
|
|
127
|
+
}
|
package/index.server.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { PluginServerContext } from "@getpaseo/plugin/server";
|
|
2
|
+
import { createPluginLogger } from "./server/vendor/paseo-plugin-helper/index";
|
|
3
|
+
import {
|
|
4
|
+
addCommentContract,
|
|
5
|
+
createIssueContract,
|
|
6
|
+
forgeContextContract,
|
|
7
|
+
forgeSettingsContract,
|
|
8
|
+
issueDetailContract,
|
|
9
|
+
openIssuesContract,
|
|
10
|
+
searchIssuesContract,
|
|
11
|
+
setLabelContract,
|
|
12
|
+
} from "./shared/issues.js";
|
|
13
|
+
import {
|
|
14
|
+
handleAddComment,
|
|
15
|
+
handleCreateIssue,
|
|
16
|
+
handleForgeContext,
|
|
17
|
+
handleIssueDetail,
|
|
18
|
+
handleOpenIssues,
|
|
19
|
+
handleSearchIssues,
|
|
20
|
+
handleSetLabel,
|
|
21
|
+
} from "./server/issues.js";
|
|
22
|
+
import { settingsHandlers } from "./server/settings.js";
|
|
23
|
+
import {
|
|
24
|
+
hookStatusContract,
|
|
25
|
+
hookQueuesContract,
|
|
26
|
+
hookPauseContract,
|
|
27
|
+
hookResumeContract,
|
|
28
|
+
hookDrainContract,
|
|
29
|
+
} from "./shared/hook-queue.js";
|
|
30
|
+
import {
|
|
31
|
+
handleHookStatus,
|
|
32
|
+
handleHookQueues,
|
|
33
|
+
handleHookPause,
|
|
34
|
+
handleHookResume,
|
|
35
|
+
handleHookDrain,
|
|
36
|
+
} from "./server/hook-queue.js";
|
|
37
|
+
|
|
38
|
+
const log = createPluginLogger("forges");
|
|
39
|
+
|
|
40
|
+
export default function contribute(server: PluginServerContext) {
|
|
41
|
+
server.handle(openIssuesContract, handleOpenIssues);
|
|
42
|
+
server.handle(searchIssuesContract, handleSearchIssues);
|
|
43
|
+
server.handle(forgeContextContract, handleForgeContext);
|
|
44
|
+
server.handle(issueDetailContract, handleIssueDetail);
|
|
45
|
+
server.handle(setLabelContract, handleSetLabel);
|
|
46
|
+
server.handle(addCommentContract, handleAddComment);
|
|
47
|
+
server.handle(createIssueContract, handleCreateIssue);
|
|
48
|
+
|
|
49
|
+
server.handle(hookStatusContract, handleHookStatus);
|
|
50
|
+
server.handle(hookQueuesContract, handleHookQueues);
|
|
51
|
+
server.handle(hookPauseContract, handleHookPause);
|
|
52
|
+
server.handle(hookResumeContract, handleHookResume);
|
|
53
|
+
server.handle(hookDrainContract, handleHookDrain);
|
|
54
|
+
|
|
55
|
+
// forge.install-labels is operator-only: the optional label-set install is
|
|
56
|
+
// hidden from the release surface (issue #163). handleInstallLabels stays in
|
|
57
|
+
// server/issues.ts for our own board; the RPC is deliberately not registered.
|
|
58
|
+
server.handle(forgeSettingsContract.get, settingsHandlers.get);
|
|
59
|
+
server.handle(forgeSettingsContract.update, settingsHandlers.update);
|
|
60
|
+
server.handle(forgeSettingsContract.reset, settingsHandlers.reset);
|
|
61
|
+
log.info("forges server handlers registered");
|
|
62
|
+
return () => {};
|
|
63
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xpufx/paseo-forges",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Work with Forge/Gitea-family issues from inside Paseo via the embedded fetch API client.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"scripts": {
|
|
9
9
|
"stamp": "node -e \"import('./server/vendor/paseo-plugin-helper/version.ts').then(m => m.stampVersion({ targetFile: 'shared/version.ts' }))\"",
|
|
10
10
|
"typecheck": "tsc --noEmit",
|
|
11
|
-
"test": "esbuild shared/issues.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-issues.test.mjs --log-level=error && esbuild shared/webhook.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-webhook.test.mjs --log-level=error && esbuild shared/hook-queue.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-hook-queue.test.mjs --log-level=error && esbuild server/git-origin.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-git-origin.test.mjs --log-level=error && esbuild server/registry.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-registry.test.mjs --log-level=error && esbuild server/forge-guard.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-forge-guard.test.mjs --log-level=error && esbuild server/forge-client.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-forge-client.test.mjs --log-level=error && esbuild server/hook-queue.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-server-hook-queue.test.mjs --log-level=error && node --test ${TMPDIR:-/tmp}/paseo-forges-issues.test.mjs ${TMPDIR:-/tmp}/paseo-forges-webhook.test.mjs ${TMPDIR:-/tmp}/paseo-forges-hook-queue.test.mjs ${TMPDIR:-/tmp}/paseo-forges-git-origin.test.mjs ${TMPDIR:-/tmp}/paseo-forges-registry.test.mjs ${TMPDIR:-/tmp}/paseo-forges-forge-guard.test.mjs ${TMPDIR:-/tmp}/paseo-forges-forge-client.test.mjs ${TMPDIR:-/tmp}/paseo-forges-server-hook-queue.test.mjs"
|
|
11
|
+
"test": "esbuild shared/issues.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-issues.test.mjs --log-level=error && esbuild shared/webhook.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-webhook.test.mjs --log-level=error && esbuild shared/notification.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-notification.test.mjs --log-level=error && esbuild shared/hook-queue.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-hook-queue.test.mjs --log-level=error && esbuild server/git-origin.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-git-origin.test.mjs --log-level=error && esbuild server/registry.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-registry.test.mjs --log-level=error && esbuild server/forge-guard.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-forge-guard.test.mjs --log-level=error && esbuild server/forge-client.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-forge-client.test.mjs --log-level=error && esbuild server/hook-queue.test.ts --bundle --platform=node --format=esm --outfile=${TMPDIR:-/tmp}/paseo-forges-server-hook-queue.test.mjs --log-level=error && node --test ${TMPDIR:-/tmp}/paseo-forges-issues.test.mjs ${TMPDIR:-/tmp}/paseo-forges-webhook.test.mjs ${TMPDIR:-/tmp}/paseo-forges-notification.test.mjs ${TMPDIR:-/tmp}/paseo-forges-hook-queue.test.mjs ${TMPDIR:-/tmp}/paseo-forges-git-origin.test.mjs ${TMPDIR:-/tmp}/paseo-forges-registry.test.mjs ${TMPDIR:-/tmp}/paseo-forges-forge-guard.test.mjs ${TMPDIR:-/tmp}/paseo-forges-forge-client.test.mjs ${TMPDIR:-/tmp}/paseo-forges-server-hook-queue.test.mjs"
|
|
12
12
|
},
|
|
13
13
|
"devDependencies": {
|
|
14
14
|
"@getpaseo/plugin": "0.9.0-beta.2",
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
"paseo-plugin.json",
|
|
22
22
|
"README.md",
|
|
23
23
|
"LICENSE",
|
|
24
|
+
"index.client.tsx",
|
|
25
|
+
"index.server.ts",
|
|
24
26
|
"client",
|
|
25
27
|
"server",
|
|
26
28
|
"shared",
|
package/server/issues.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createPluginLogger } from "paseo-plugin-helper/
|
|
1
|
+
import { createPluginLogger } from "./vendor/paseo-plugin-helper/index";
|
|
2
2
|
import {
|
|
3
3
|
INSTALL_LABEL_MODES,
|
|
4
4
|
IssueDetailSchema,
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
type SetLabelOutput,
|
|
32
32
|
validateCreateIssueInput,
|
|
33
33
|
} from "../shared/issues.js";
|
|
34
|
-
import type { RpcOutput } from "paseo-plugin-helper/
|
|
34
|
+
import type { RpcOutput } from "../shared/vendor/paseo-plugin-helper/index";
|
|
35
35
|
import { ForgeClient, type ForgejoIssueDetail } from "./forge-client.js";
|
|
36
36
|
import { ForgeGuard, type GuardLogLevel } from "./forge-guard.js";
|
|
37
37
|
import { gitOriginForDirectory } from "./git-origin.js";
|
package/server/settings.ts
CHANGED
package/shared/hook-queue.ts
CHANGED
package/shared/issues.ts
CHANGED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
/** The notification emitted by the Forgejo digest worker. */
|
|
4
|
+
export const FORGEJO_DIGEST_PREFIX = "🔔 Forgejo digest";
|
|
5
|
+
|
|
6
|
+
export const forgejoNotificationCardSchema = z.object({
|
|
7
|
+
level: z.enum(["info", "warning", "error"]),
|
|
8
|
+
message: z.string(),
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
export type ForgejoNotificationCardData = z.infer<typeof forgejoNotificationCardSchema>;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Creates a presentation-only card for Forgejo digest notifications. Other
|
|
15
|
+
* host notifications deliberately pass through to the host renderer: this
|
|
16
|
+
* plugin owns the digest format, not the notification timeline type itself.
|
|
17
|
+
*/
|
|
18
|
+
export function forgejoNotificationItem(item: {
|
|
19
|
+
level: ForgejoNotificationCardData["level"];
|
|
20
|
+
message: string;
|
|
21
|
+
}) {
|
|
22
|
+
if (!item.message.trimStart().startsWith(FORGEJO_DIGEST_PREFIX)) return undefined;
|
|
23
|
+
return {
|
|
24
|
+
items: [
|
|
25
|
+
{
|
|
26
|
+
type: "plugin" as const,
|
|
27
|
+
kind: "forgejo-notification",
|
|
28
|
+
version: 1,
|
|
29
|
+
data: { level: item.level, message: item.message },
|
|
30
|
+
},
|
|
31
|
+
],
|
|
32
|
+
};
|
|
33
|
+
}
|