nterminal 1.2.67 → 1.2.69

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.
Files changed (50) hide show
  1. package/dist/client/assets/MarkdownPreview-DCHLz_jq.js +1 -0
  2. package/dist/client/assets/{TranscriptMarkdownBody-BHQbyM6J.js → TranscriptMarkdownBody-BaMp6W3l.js} +1 -1
  3. package/dist/client/assets/index-CDfP5jMv.js +55 -0
  4. package/dist/client/assets/{index-BXCYmdDY.js → index-CLUAqRpO.js} +1 -1
  5. package/dist/client/assets/index-CszWmeOA.css +1 -0
  6. package/dist/client/index.html +2 -2
  7. package/dist/client/sw.js +51 -0
  8. package/dist/server/auth/authService.d.ts +1 -0
  9. package/dist/server/auth/authService.js +9 -3
  10. package/dist/server/auth/authService.js.map +1 -1
  11. package/dist/server/http.d.ts +2 -1
  12. package/dist/server/http.js +5 -0
  13. package/dist/server/http.js.map +1 -1
  14. package/dist/server/index.js +30 -2
  15. package/dist/server/index.js.map +1 -1
  16. package/dist/server/notifications/agentCompletionMonitor.d.ts +27 -0
  17. package/dist/server/notifications/agentCompletionMonitor.js +148 -0
  18. package/dist/server/notifications/agentCompletionMonitor.js.map +1 -0
  19. package/dist/server/notifications/clientPresence.d.ts +40 -0
  20. package/dist/server/notifications/clientPresence.js +75 -0
  21. package/dist/server/notifications/clientPresence.js.map +1 -0
  22. package/dist/server/notifications/completionNotificationRouter.d.ts +28 -0
  23. package/dist/server/notifications/completionNotificationRouter.js +100 -0
  24. package/dist/server/notifications/completionNotificationRouter.js.map +1 -0
  25. package/dist/server/notifications/pushNotificationService.d.ts +80 -0
  26. package/dist/server/notifications/pushNotificationService.js +300 -0
  27. package/dist/server/notifications/pushNotificationService.js.map +1 -0
  28. package/dist/server/routes/pushNotificationRoutes.d.ts +11 -0
  29. package/dist/server/routes/pushNotificationRoutes.js +138 -0
  30. package/dist/server/routes/pushNotificationRoutes.js.map +1 -0
  31. package/dist/server/routes/terminalRoutes.d.ts +1 -0
  32. package/dist/server/routes/terminalRoutes.js +4 -1
  33. package/dist/server/routes/terminalRoutes.js.map +1 -1
  34. package/dist/server/storage/fileStore.d.ts +25 -1
  35. package/dist/server/storage/fileStore.js +65 -0
  36. package/dist/server/storage/fileStore.js.map +1 -1
  37. package/dist/server/terminal/TerminalManager.d.ts +14 -0
  38. package/dist/server/terminal/TerminalManager.js +104 -3
  39. package/dist/server/terminal/TerminalManager.js.map +1 -1
  40. package/dist/server/terminal/agentTranscriptStatus.d.ts +11 -0
  41. package/dist/server/terminal/agentTranscriptStatus.js +187 -0
  42. package/dist/server/terminal/agentTranscriptStatus.js.map +1 -0
  43. package/dist/shared/protocol.d.ts +67 -0
  44. package/dist/shared/protocol.js.map +1 -1
  45. package/dist/shared/types.d.ts +4 -0
  46. package/package.json +3 -1
  47. package/public/sw.js +51 -0
  48. package/dist/client/assets/MarkdownPreview-BDn9o2c8.js +0 -1
  49. package/dist/client/assets/index-BgvJmNhz.js +0 -55
  50. package/dist/client/assets/index-Br4cNNw1.css +0 -1
@@ -0,0 +1,28 @@
1
+ import type { TerminalId } from '../../shared/types.js';
2
+ import type { StoredPushSubscription, FileStore } from '../storage/fileStore.js';
3
+ import type { ClientPresenceTracker } from './clientPresence.js';
4
+ import type { PushNotificationService } from './pushNotificationService.js';
5
+ export interface TerminalCompletionEvent {
6
+ agentId: string | null;
7
+ terminalId: TerminalId;
8
+ terminalTitle: string;
9
+ provider?: 'codex' | 'claude';
10
+ commandName?: string | null;
11
+ occurredAt?: string;
12
+ }
13
+ export interface CompletionNotificationRouterOptions {
14
+ now?: () => number;
15
+ coalesceWindowMs?: number;
16
+ }
17
+ export declare class CompletionNotificationRouter {
18
+ private readonly store;
19
+ private readonly pushService;
20
+ private readonly presence;
21
+ private readonly now;
22
+ private readonly coalesceWindowMs;
23
+ private readonly recentTags;
24
+ constructor(store: FileStore, pushService: PushNotificationService, presence: ClientPresenceTracker, options?: CompletionNotificationRouterOptions);
25
+ notifyCompletion(event: TerminalCompletionEvent): Promise<void>;
26
+ private reserveCoalesceTag;
27
+ }
28
+ export declare function selectPushTargets(subscriptions: readonly StoredPushSubscription[], presence: ClientPresenceTracker): StoredPushSubscription[];
@@ -0,0 +1,100 @@
1
+ import { MAIN_SERVER_ID } from '../../shared/protocol.js';
2
+ const defaultCoalesceWindowMs = 30_000;
3
+ export class CompletionNotificationRouter {
4
+ store;
5
+ pushService;
6
+ presence;
7
+ now;
8
+ coalesceWindowMs;
9
+ recentTags = new Map();
10
+ constructor(store, pushService, presence, options = {}) {
11
+ this.store = store;
12
+ this.pushService = pushService;
13
+ this.presence = presence;
14
+ this.now = options.now ?? Date.now;
15
+ this.coalesceWindowMs = options.coalesceWindowMs ?? defaultCoalesceWindowMs;
16
+ }
17
+ async notifyCompletion(event) {
18
+ const tag = completionTag(event);
19
+ const state = await this.store.read();
20
+ const targets = selectPushTargets(state.pushSubscriptions, this.presence);
21
+ if (targets.length === 0) {
22
+ return;
23
+ }
24
+ if (this.reserveCoalesceTag(tag)) {
25
+ return;
26
+ }
27
+ const serverName = event.agentId
28
+ ? (state.agents.find((agent) => agent.id === event.agentId)?.name ?? 'remote server')
29
+ : (state.mainName ?? 'main server');
30
+ await this.pushService.sendToSubscriptions(targets, {
31
+ title: 'NTerminal',
32
+ body: `${completionLabel(event)} finished on ${serverName}`,
33
+ tag,
34
+ url: '/'
35
+ });
36
+ }
37
+ reserveCoalesceTag(tag) {
38
+ const now = this.now();
39
+ const cutoff = now - this.coalesceWindowMs;
40
+ for (const [candidate, at] of this.recentTags) {
41
+ if (at < cutoff) {
42
+ this.recentTags.delete(candidate);
43
+ }
44
+ }
45
+ const previous = this.recentTags.get(tag);
46
+ if (previous !== undefined && previous >= cutoff) {
47
+ return true;
48
+ }
49
+ this.recentTags.set(tag, now);
50
+ return false;
51
+ }
52
+ }
53
+ export function selectPushTargets(subscriptions, presence) {
54
+ const livePages = presence.livePages();
55
+ const visibleClientIds = new Set(livePages.filter((page) => page.visibilityState === 'visible').map((page) => page.clientId));
56
+ const hiddenBrowserNotificationClientIds = new Set(livePages
57
+ .filter((page) => page.visibilityState !== 'visible' && page.browserNotificationsEnabled)
58
+ .map((page) => page.clientId));
59
+ const liveClientIds = new Set(livePages.map((page) => page.clientId));
60
+ const suppressDefaultPush = visibleClientIds.size > 0 || hiddenBrowserNotificationClientIds.size > 0;
61
+ const targets = new Map();
62
+ if (!suppressDefaultPush) {
63
+ const defaultTarget = mostRecentStandaloneSubscription(subscriptions, liveClientIds);
64
+ if (defaultTarget) {
65
+ targets.set(defaultTarget.endpoint, defaultTarget);
66
+ }
67
+ }
68
+ for (const subscription of subscriptions) {
69
+ if (!subscription.alwaysNotify)
70
+ continue;
71
+ if (subscription.displayMode !== 'standalone')
72
+ continue;
73
+ if (subscription.deviceKind !== 'mobile' && subscription.deviceKind !== 'tablet')
74
+ continue;
75
+ if (visibleClientIds.has(subscription.clientId))
76
+ continue;
77
+ targets.set(subscription.endpoint, subscription);
78
+ }
79
+ return [...targets.values()];
80
+ }
81
+ function mostRecentStandaloneSubscription(subscriptions, liveClientIds) {
82
+ return subscriptions
83
+ .filter((subscription) => subscription.displayMode === 'standalone' && !liveClientIds.has(subscription.clientId))
84
+ .sort((left, right) => timestampMs(right.lastActiveAt || right.lastSeenAt) - timestampMs(left.lastActiveAt || left.lastSeenAt))[0] ?? null;
85
+ }
86
+ function completionTag(event) {
87
+ return `nterminal:${event.agentId ?? MAIN_SERVER_ID}:${event.terminalId}:completion`;
88
+ }
89
+ function completionLabel(event) {
90
+ if (event.provider === 'codex')
91
+ return 'Codex task';
92
+ if (event.provider === 'claude')
93
+ return 'Claude task';
94
+ return 'Task';
95
+ }
96
+ function timestampMs(value) {
97
+ const parsed = Date.parse(value);
98
+ return Number.isFinite(parsed) ? parsed : 0;
99
+ }
100
+ //# sourceMappingURL=completionNotificationRouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"completionNotificationRouter.js","sourceRoot":"","sources":["../../../src/server/notifications/completionNotificationRouter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import type { PushNotificationDeviceKind, PushNotificationDeviceSummary, PushNotificationDisplayMode } from '../../shared/protocol.js';
2
+ import type { StoredPushSubscription } from '../storage/fileStore.js';
3
+ import type { FileStore } from '../storage/fileStore.js';
4
+ export interface PushSubscriptionInput {
5
+ clientId: string;
6
+ subscription: {
7
+ endpoint: string;
8
+ keys: {
9
+ p256dh: string;
10
+ auth: string;
11
+ };
12
+ };
13
+ trustedDeviceId: string;
14
+ userAgent: string | null;
15
+ deviceKind: PushNotificationDeviceKind;
16
+ displayMode: PushNotificationDisplayMode;
17
+ label?: string;
18
+ alwaysNotify?: boolean;
19
+ }
20
+ export interface PushNotificationPayload {
21
+ title: string;
22
+ body: string;
23
+ tag: string;
24
+ url?: string;
25
+ }
26
+ export interface PushNotificationServiceOptions {
27
+ subject: string;
28
+ now?: () => Date;
29
+ }
30
+ export declare class PushNotificationService {
31
+ private readonly store;
32
+ private readonly options;
33
+ private readonly now;
34
+ constructor(store: FileStore, options: PushNotificationServiceOptions);
35
+ getStatus(clientId: string | undefined, trustedDeviceId?: string): Promise<{
36
+ supported: boolean;
37
+ vapidPublicKey: string | null;
38
+ currentDevice: PushNotificationDeviceSummary | null;
39
+ subscriptionCount: number;
40
+ }>;
41
+ prepare(clientId: string | undefined, trustedDeviceId?: string): Promise<{
42
+ publicKey: string;
43
+ currentDevice: PushNotificationDeviceSummary | null;
44
+ }>;
45
+ subscribe(input: PushSubscriptionInput): Promise<{
46
+ subscriptionCount: number;
47
+ currentDevice: PushNotificationDeviceSummary | null;
48
+ }>;
49
+ unsubscribe(input: {
50
+ clientId?: string;
51
+ endpoint?: string;
52
+ trustedDeviceId?: string;
53
+ }): Promise<{
54
+ subscriptionCount: number;
55
+ currentDevice: PushNotificationDeviceSummary | null;
56
+ }>;
57
+ updateDevice(input: {
58
+ clientId: string;
59
+ trustedDeviceId?: string;
60
+ label?: string;
61
+ alwaysNotify?: boolean;
62
+ }): Promise<{
63
+ subscriptionCount: number;
64
+ currentDevice: PushNotificationDeviceSummary | null;
65
+ }>;
66
+ markDeviceSeen(input: {
67
+ clientId: string;
68
+ trustedDeviceId?: string;
69
+ deviceKind: PushNotificationDeviceKind;
70
+ displayMode: PushNotificationDisplayMode;
71
+ active: boolean;
72
+ }): Promise<void>;
73
+ sendToSubscriptions(subscriptions: StoredPushSubscription[], payload: PushNotificationPayload): Promise<void>;
74
+ private ensureVapidKeys;
75
+ }
76
+ export declare function toDeviceSummary(subscription: StoredPushSubscription): PushNotificationDeviceSummary;
77
+ export declare class PushNotificationInputError extends Error {
78
+ readonly code: string;
79
+ constructor(code: string);
80
+ }
@@ -0,0 +1,300 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import webPush from 'web-push';
3
+ export class PushNotificationService {
4
+ store;
5
+ options;
6
+ now;
7
+ constructor(store, options) {
8
+ this.store = store;
9
+ this.options = options;
10
+ this.now = options.now ?? (() => new Date());
11
+ }
12
+ async getStatus(clientId, trustedDeviceId) {
13
+ const state = await this.store.read();
14
+ return {
15
+ supported: true,
16
+ vapidPublicKey: state.pushVapidKeys?.publicKey ?? null,
17
+ currentDevice: currentDeviceSummary(state.pushSubscriptions, clientId, trustedDeviceId),
18
+ subscriptionCount: state.pushSubscriptions.length
19
+ };
20
+ }
21
+ async prepare(clientId, trustedDeviceId) {
22
+ const keys = await this.ensureVapidKeys();
23
+ const state = await this.store.read();
24
+ return { publicKey: keys.publicKey, currentDevice: currentDeviceSummary(state.pushSubscriptions, clientId, trustedDeviceId) };
25
+ }
26
+ async subscribe(input) {
27
+ const normalized = normalizeSubscriptionInput(input);
28
+ const now = this.now().toISOString();
29
+ const nextState = await this.store.update((state) => {
30
+ const existing = state.pushSubscriptions.find((subscription) => subscription.endpoint === normalized.subscription.endpoint ||
31
+ (subscription.trustedDeviceId === normalized.trustedDeviceId && subscription.clientId === normalized.clientId));
32
+ const subscription = {
33
+ id: existing?.id ?? randomUUID(),
34
+ trustedDeviceId: normalized.trustedDeviceId,
35
+ clientId: normalized.clientId,
36
+ endpoint: normalized.subscription.endpoint,
37
+ p256dh: normalized.subscription.keys.p256dh,
38
+ auth: normalized.subscription.keys.auth,
39
+ userAgent: normalized.userAgent,
40
+ deviceKind: normalized.deviceKind,
41
+ displayMode: normalized.displayMode,
42
+ label: normalized.label,
43
+ alwaysNotify: existing?.alwaysNotify ?? normalized.alwaysNotify,
44
+ createdAt: existing?.createdAt ?? now,
45
+ updatedAt: now,
46
+ lastSeenAt: now,
47
+ lastActiveAt: now
48
+ };
49
+ return {
50
+ ...state,
51
+ pushSubscriptions: [
52
+ ...state.pushSubscriptions.filter((candidate) => candidate.endpoint !== normalized.subscription.endpoint &&
53
+ !(candidate.trustedDeviceId === normalized.trustedDeviceId && candidate.clientId === normalized.clientId)),
54
+ subscription
55
+ ]
56
+ };
57
+ }, { flush: 'immediate' });
58
+ return {
59
+ subscriptionCount: nextState.pushSubscriptions.length,
60
+ currentDevice: currentDeviceSummary(nextState.pushSubscriptions, normalized.clientId, normalized.trustedDeviceId)
61
+ };
62
+ }
63
+ async unsubscribe(input) {
64
+ const clientId = normalizeOptionalIdentifier(input.clientId, 'invalid_push_client_id');
65
+ const endpoint = input.endpoint === undefined ? undefined : normalizeEndpoint(input.endpoint);
66
+ const trustedDeviceId = normalizeOptionalIdentifier(input.trustedDeviceId, 'invalid_push_trusted_device_id');
67
+ if (!clientId && !endpoint && !trustedDeviceId) {
68
+ throw new PushNotificationInputError('push_unsubscribe_target_required');
69
+ }
70
+ const nextState = await this.store.update((state) => ({
71
+ ...state,
72
+ pushSubscriptions: state.pushSubscriptions.filter((subscription) => {
73
+ const withinScope = !trustedDeviceId || subscription.trustedDeviceId === trustedDeviceId;
74
+ const matchesExplicitTarget = Boolean((endpoint && subscription.endpoint === endpoint) ||
75
+ (clientId && subscription.clientId === clientId));
76
+ const matchesTrustedDeviceOnly = Boolean(!endpoint && !clientId && trustedDeviceId && subscription.trustedDeviceId === trustedDeviceId);
77
+ return !(withinScope && (matchesExplicitTarget || matchesTrustedDeviceOnly));
78
+ })
79
+ }), { flush: 'immediate' });
80
+ return { subscriptionCount: nextState.pushSubscriptions.length, currentDevice: currentDeviceSummary(nextState.pushSubscriptions, clientId, trustedDeviceId) };
81
+ }
82
+ async updateDevice(input) {
83
+ const clientId = normalizeIdentifier(input.clientId, 'invalid_push_client_id');
84
+ const trustedDeviceId = normalizeOptionalIdentifier(input.trustedDeviceId, 'invalid_push_trusted_device_id');
85
+ const label = input.label === undefined ? undefined : normalizeLabel(input.label);
86
+ const alwaysNotify = input.alwaysNotify;
87
+ const now = this.now().toISOString();
88
+ const nextState = await this.store.update((state) => ({
89
+ ...state,
90
+ pushSubscriptions: state.pushSubscriptions.map((subscription) => subscription.clientId === clientId && (!trustedDeviceId || subscription.trustedDeviceId === trustedDeviceId)
91
+ ? {
92
+ ...subscription,
93
+ ...(label !== undefined ? { label } : {}),
94
+ ...(alwaysNotify !== undefined ? { alwaysNotify } : {}),
95
+ updatedAt: now,
96
+ lastSeenAt: now
97
+ }
98
+ : subscription)
99
+ }), { flush: 'immediate' });
100
+ return { subscriptionCount: nextState.pushSubscriptions.length, currentDevice: currentDeviceSummary(nextState.pushSubscriptions, clientId, trustedDeviceId) };
101
+ }
102
+ async markDeviceSeen(input) {
103
+ const clientId = normalizeIdentifier(input.clientId, 'invalid_push_client_id');
104
+ const trustedDeviceId = normalizeOptionalIdentifier(input.trustedDeviceId, 'invalid_push_trusted_device_id');
105
+ const deviceKind = normalizeDeviceKind(input.deviceKind);
106
+ const displayMode = normalizeDisplayMode(input.displayMode);
107
+ if (!(await this.store.read()).pushSubscriptions.some((subscription) => subscription.clientId === clientId && (!trustedDeviceId || subscription.trustedDeviceId === trustedDeviceId))) {
108
+ return;
109
+ }
110
+ const now = this.now().toISOString();
111
+ await this.store.update((state) => ({
112
+ ...state,
113
+ pushSubscriptions: state.pushSubscriptions.map((subscription) => subscription.clientId === clientId && (!trustedDeviceId || subscription.trustedDeviceId === trustedDeviceId)
114
+ ? {
115
+ ...subscription,
116
+ deviceKind,
117
+ displayMode: subscription.displayMode === 'standalone' && displayMode !== 'standalone' ? subscription.displayMode : displayMode,
118
+ lastSeenAt: now,
119
+ lastActiveAt: input.active ? now : subscription.lastActiveAt,
120
+ updatedAt: now
121
+ }
122
+ : subscription)
123
+ }));
124
+ }
125
+ async sendToSubscriptions(subscriptions, payload) {
126
+ const state = await this.store.read();
127
+ if (!state.pushVapidKeys || subscriptions.length === 0) {
128
+ return;
129
+ }
130
+ configureVapid(this.options.subject, state.pushVapidKeys);
131
+ const staleEndpoints = new Set();
132
+ const body = JSON.stringify({
133
+ title: payload.title,
134
+ body: payload.body,
135
+ tag: payload.tag,
136
+ icon: '/icons/app-icon-384.png',
137
+ badge: '/icons/favicon-96.png',
138
+ data: { url: sanitizeNotificationUrl(payload.url) }
139
+ });
140
+ const byEndpoint = new Map();
141
+ for (const subscription of subscriptions) {
142
+ byEndpoint.set(subscription.endpoint, subscription);
143
+ }
144
+ await Promise.allSettled([...byEndpoint.values()].map(async (subscription) => {
145
+ try {
146
+ await webPush.sendNotification({
147
+ endpoint: subscription.endpoint,
148
+ keys: {
149
+ p256dh: subscription.p256dh,
150
+ auth: subscription.auth
151
+ }
152
+ }, body);
153
+ }
154
+ catch (error) {
155
+ if (isStalePushError(error)) {
156
+ staleEndpoints.add(subscription.endpoint);
157
+ }
158
+ }
159
+ }));
160
+ if (staleEndpoints.size > 0) {
161
+ await this.store.update((current) => ({
162
+ ...current,
163
+ pushSubscriptions: current.pushSubscriptions.filter((subscription) => !staleEndpoints.has(subscription.endpoint))
164
+ }), { flush: 'immediate' });
165
+ }
166
+ }
167
+ async ensureVapidKeys() {
168
+ const state = await this.store.read();
169
+ if (state.pushVapidKeys) {
170
+ configureVapid(this.options.subject, state.pushVapidKeys);
171
+ return state.pushVapidKeys;
172
+ }
173
+ const generated = webPush.generateVAPIDKeys();
174
+ const createdAt = this.now().toISOString();
175
+ const nextState = await this.store.update((current) => ({
176
+ ...current,
177
+ pushVapidKeys: { ...generated, createdAt }
178
+ }), { flush: 'immediate' });
179
+ const keys = nextState.pushVapidKeys ?? { ...generated, createdAt };
180
+ configureVapid(this.options.subject, keys);
181
+ return keys;
182
+ }
183
+ }
184
+ function configureVapid(subject, keys) {
185
+ webPush.setVapidDetails(subject, keys.publicKey, keys.privateKey);
186
+ }
187
+ function normalizeSubscriptionInput(input) {
188
+ if (!input || typeof input !== 'object') {
189
+ throw new PushNotificationInputError('invalid_push_subscription');
190
+ }
191
+ return {
192
+ clientId: normalizeIdentifier(input.clientId, 'invalid_push_client_id'),
193
+ trustedDeviceId: normalizeIdentifier(input.trustedDeviceId, 'invalid_push_trusted_device_id'),
194
+ subscription: {
195
+ endpoint: normalizeEndpoint(input.subscription?.endpoint),
196
+ keys: {
197
+ p256dh: normalizeSubscriptionKey(input.subscription?.keys?.p256dh, 'p256dh'),
198
+ auth: normalizeSubscriptionKey(input.subscription?.keys?.auth, 'auth')
199
+ }
200
+ },
201
+ userAgent: input.userAgent,
202
+ deviceKind: normalizeDeviceKind(input.deviceKind),
203
+ displayMode: normalizeDisplayMode(input.displayMode),
204
+ label: normalizeLabel(input.label),
205
+ alwaysNotify: input.alwaysNotify === true
206
+ };
207
+ }
208
+ function normalizeIdentifier(value, code) {
209
+ if (typeof value !== 'string') {
210
+ throw new PushNotificationInputError(code);
211
+ }
212
+ const trimmed = value.trim();
213
+ if (!/^[A-Za-z0-9_.:-]{8,128}$/.test(trimmed)) {
214
+ throw new PushNotificationInputError(code);
215
+ }
216
+ return trimmed;
217
+ }
218
+ function normalizeOptionalIdentifier(value, code) {
219
+ return value === undefined ? undefined : normalizeIdentifier(value, code);
220
+ }
221
+ function normalizeEndpoint(value) {
222
+ if (typeof value !== 'string' || value.length > 4096) {
223
+ throw new PushNotificationInputError('invalid_push_endpoint');
224
+ }
225
+ let parsed;
226
+ try {
227
+ parsed = new URL(value);
228
+ }
229
+ catch {
230
+ throw new PushNotificationInputError('invalid_push_endpoint');
231
+ }
232
+ if (parsed.protocol !== 'https:') {
233
+ throw new PushNotificationInputError('invalid_push_endpoint');
234
+ }
235
+ return parsed.toString();
236
+ }
237
+ function normalizeSubscriptionKey(value, name) {
238
+ if (typeof value !== 'string' || value.length < 8 || value.length > 512 || !/^[a-zA-Z0-9_-]+={0,2}$/.test(value)) {
239
+ throw new PushNotificationInputError(`invalid_push_${name}`);
240
+ }
241
+ return value;
242
+ }
243
+ function normalizeDeviceKind(value) {
244
+ return value === 'desktop' || value === 'mobile' || value === 'tablet' ? value : 'unknown';
245
+ }
246
+ function normalizeDisplayMode(value) {
247
+ return value === 'browser' || value === 'standalone' ? value : 'unknown';
248
+ }
249
+ function normalizeLabel(value) {
250
+ const trimmed = typeof value === 'string' ? value.trim() : '';
251
+ return (trimmed || 'This device').slice(0, 80);
252
+ }
253
+ function sanitizeNotificationUrl(value) {
254
+ if (!value) {
255
+ return '/';
256
+ }
257
+ try {
258
+ const parsed = new URL(value, 'https://nterminal.local');
259
+ if (parsed.origin !== 'https://nterminal.local') {
260
+ return '/';
261
+ }
262
+ return `${parsed.pathname}${parsed.search}${parsed.hash}` || '/';
263
+ }
264
+ catch {
265
+ return '/';
266
+ }
267
+ }
268
+ function isStalePushError(error) {
269
+ const statusCode = error?.statusCode;
270
+ return statusCode === 404 || statusCode === 410;
271
+ }
272
+ function currentDeviceSummary(subscriptions, clientId, trustedDeviceId) {
273
+ if (!clientId) {
274
+ return null;
275
+ }
276
+ const subscription = subscriptions.find((candidate) => candidate.clientId === clientId && (!trustedDeviceId || candidate.trustedDeviceId === trustedDeviceId));
277
+ return subscription ? toDeviceSummary(subscription) : null;
278
+ }
279
+ export function toDeviceSummary(subscription) {
280
+ return {
281
+ id: subscription.id,
282
+ clientId: subscription.clientId,
283
+ deviceKind: subscription.deviceKind,
284
+ displayMode: subscription.displayMode,
285
+ label: subscription.label,
286
+ alwaysNotify: subscription.alwaysNotify,
287
+ createdAt: subscription.createdAt,
288
+ updatedAt: subscription.updatedAt,
289
+ lastSeenAt: subscription.lastSeenAt,
290
+ lastActiveAt: subscription.lastActiveAt
291
+ };
292
+ }
293
+ export class PushNotificationInputError extends Error {
294
+ code;
295
+ constructor(code) {
296
+ super(code);
297
+ this.code = code;
298
+ }
299
+ }
300
+ //# sourceMappingURL=pushNotificationService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pushNotificationService.js","sourceRoot":"","sources":["../../../src/server/notifications/pushNotificationService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import type { FastifyInstance } from 'fastify';
2
+ import type { AppConfig } from '../config.js';
3
+ import type { AuthService } from '../auth/authService.js';
4
+ import type { ClientPresenceTracker } from '../notifications/clientPresence.js';
5
+ import type { PushNotificationService } from '../notifications/pushNotificationService.js';
6
+ export interface PushNotificationRouteServices {
7
+ authService: AuthService;
8
+ pushNotificationService: PushNotificationService;
9
+ clientPresence: ClientPresenceTracker;
10
+ }
11
+ export declare function registerPushNotificationRoutes(app: FastifyInstance, config: AppConfig, services: PushNotificationRouteServices): Promise<void>;