@jamiexiongr/panda-hub 0.1.7 → 0.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.
@@ -5,7 +5,7 @@ import {
5
5
  resolveTailscalePublicationMode,
6
6
  resolveTailscaleServePort,
7
7
  startPandaSessionService
8
- } from "./chunk-JBN6ZR6W.mjs";
8
+ } from "./chunk-WFHIGEWQ.mjs";
9
9
 
10
10
  // release/panda-hub/src/index.ts
11
11
  import fs from "fs";
@@ -15,14 +15,15 @@ import { fileURLToPath } from "url";
15
15
  // release/panda-hub/package.json
16
16
  var package_default = {
17
17
  name: "@jamiexiongr/panda-hub",
18
- version: "0.1.7",
18
+ version: "0.1.8",
19
19
  type: "module",
20
20
  private: false,
21
21
  description: "Panda hub runtime",
22
22
  dependencies: {
23
23
  "@fastify/cors": "^10.0.2",
24
24
  "@fastify/websocket": "^11.0.2",
25
- fastify: "^5.2.1"
25
+ fastify: "^5.2.1",
26
+ "web-push": "^3.6.7"
26
27
  },
27
28
  bin: {
28
29
  "panda-hub": "./bin/panda-hub.cjs"
package/dist/cli.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  import {
2
2
  startJamiexiongrHub
3
- } from "./chunk-D2AAEM52.mjs";
3
+ } from "./chunk-YPDN7Z3X.mjs";
4
4
  import {
5
5
  resolveTailscalePublicationMode
6
- } from "./chunk-JBN6ZR6W.mjs";
6
+ } from "./chunk-WFHIGEWQ.mjs";
7
+ import "./chunk-AEQMWH7D.mjs";
7
8
 
8
9
  // release/panda-hub/src/cli.ts
9
10
  void startJamiexiongrHub({
@@ -0,0 +1,157 @@
1
+ import {
2
+ external_exports
3
+ } from "./chunk-AEQMWH7D.mjs";
4
+
5
+ // packages/provider-codex/src/hub-web-push.ts
6
+ import fs from "fs/promises";
7
+ import path from "path";
8
+ import webpush from "web-push";
9
+ var storedVapidConfigSchema = external_exports.object({
10
+ version: external_exports.literal(1),
11
+ public_key: external_exports.string(),
12
+ private_key: external_exports.string(),
13
+ subject: external_exports.string(),
14
+ created_at: external_exports.string(),
15
+ updated_at: external_exports.string()
16
+ });
17
+ var isoNow = () => (/* @__PURE__ */ new Date()).toISOString();
18
+ var normalizeSubject = (value) => {
19
+ const trimmed = value?.trim() ?? "";
20
+ if (trimmed) {
21
+ return trimmed;
22
+ }
23
+ return "mailto:panda@localhost";
24
+ };
25
+ var loadStoredVapidConfig = async (storageFilePath) => {
26
+ try {
27
+ const raw = await fs.readFile(storageFilePath, "utf8");
28
+ return storedVapidConfigSchema.parse(JSON.parse(raw));
29
+ } catch (error) {
30
+ if (error?.code === "ENOENT") {
31
+ return null;
32
+ }
33
+ throw error;
34
+ }
35
+ };
36
+ var writeStoredVapidConfig = async (storageFilePath, config) => {
37
+ await fs.mkdir(path.dirname(storageFilePath), { recursive: true });
38
+ await fs.writeFile(storageFilePath, JSON.stringify(config, null, 2), "utf8");
39
+ };
40
+ var resolveVapidConfig = async (options) => {
41
+ const configuredPublicKey = options.publicKey?.trim() ?? "";
42
+ const configuredPrivateKey = options.privateKey?.trim() ?? "";
43
+ const subject = normalizeSubject(options.subject);
44
+ if (configuredPublicKey || configuredPrivateKey) {
45
+ if (!configuredPublicKey || !configuredPrivateKey) {
46
+ return {
47
+ publicConfig: {
48
+ supported: false,
49
+ vapid_public_key: null,
50
+ subject: subject ?? null,
51
+ reason: "PANDA_WEB_PUSH_PUBLIC_KEY and PANDA_WEB_PUSH_PRIVATE_KEY must both be set."
52
+ },
53
+ privateKey: null
54
+ };
55
+ }
56
+ return {
57
+ publicConfig: {
58
+ supported: true,
59
+ vapid_public_key: configuredPublicKey,
60
+ subject,
61
+ reason: null
62
+ },
63
+ privateKey: configuredPrivateKey
64
+ };
65
+ }
66
+ const stored = await loadStoredVapidConfig(options.storageFilePath);
67
+ if (stored) {
68
+ return {
69
+ publicConfig: {
70
+ supported: true,
71
+ vapid_public_key: stored.public_key,
72
+ subject: stored.subject,
73
+ reason: null
74
+ },
75
+ privateKey: stored.private_key
76
+ };
77
+ }
78
+ const generated = webpush.generateVAPIDKeys();
79
+ const now = isoNow();
80
+ const persisted = storedVapidConfigSchema.parse({
81
+ version: 1,
82
+ public_key: generated.publicKey,
83
+ private_key: generated.privateKey,
84
+ subject,
85
+ created_at: now,
86
+ updated_at: now
87
+ });
88
+ await writeStoredVapidConfig(options.storageFilePath, persisted);
89
+ options.logger?.info?.(
90
+ { storageFilePath: options.storageFilePath },
91
+ "Generated persistent Panda Web Push VAPID keys."
92
+ );
93
+ return {
94
+ publicConfig: {
95
+ supported: true,
96
+ vapid_public_key: persisted.public_key,
97
+ subject: persisted.subject,
98
+ reason: null
99
+ },
100
+ privateKey: persisted.private_key
101
+ };
102
+ };
103
+ var createHubWebPushNotifier = async (options) => {
104
+ const resolved = await resolveVapidConfig(options);
105
+ if (!resolved.publicConfig.supported || !resolved.publicConfig.vapid_public_key || !resolved.privateKey) {
106
+ return {
107
+ publicConfig: resolved.publicConfig,
108
+ async sendNotification() {
109
+ return {
110
+ ok: false,
111
+ statusCode: null,
112
+ shouldRemove: false,
113
+ message: "Hub Web Push is not configured."
114
+ };
115
+ }
116
+ };
117
+ }
118
+ webpush.setVapidDetails(
119
+ resolved.publicConfig.subject ?? normalizeSubject(options.subject),
120
+ resolved.publicConfig.vapid_public_key,
121
+ resolved.privateKey
122
+ );
123
+ return {
124
+ publicConfig: resolved.publicConfig,
125
+ async sendNotification(subscription, payload) {
126
+ try {
127
+ await webpush.sendNotification(
128
+ subscription,
129
+ JSON.stringify({
130
+ title: payload.title,
131
+ body: payload.body,
132
+ url: payload.url,
133
+ tag: payload.tag,
134
+ sessionId: payload.sessionId ?? null
135
+ }),
136
+ {
137
+ TTL: 60,
138
+ urgency: "high",
139
+ topic: payload.tag.slice(0, 32)
140
+ }
141
+ );
142
+ return { ok: true, statusCode: 201, shouldRemove: false };
143
+ } catch (error) {
144
+ const statusCode = typeof error === "object" && error !== null && "statusCode" in error && typeof error.statusCode === "number" ? error.statusCode : null;
145
+ return {
146
+ ok: false,
147
+ statusCode,
148
+ shouldRemove: statusCode === 404 || statusCode === 410,
149
+ message: error instanceof Error ? error.message : String(error)
150
+ };
151
+ }
152
+ }
153
+ };
154
+ };
155
+ export {
156
+ createHubWebPushNotifier
157
+ };
package/dist/index.mjs CHANGED
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  startJamiexiongrHub
3
- } from "./chunk-D2AAEM52.mjs";
4
- import "./chunk-JBN6ZR6W.mjs";
3
+ } from "./chunk-YPDN7Z3X.mjs";
4
+ import "./chunk-WFHIGEWQ.mjs";
5
+ import "./chunk-AEQMWH7D.mjs";
5
6
  export {
6
7
  startJamiexiongrHub
7
8
  };