🚨 Active Supply Chain Attack:node-ipc Package Compromised.Learn More
Socket
Book a DemoSign in
Socket

@notifyon/web

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@notifyon/web - npm Package Compare versions

Comparing version
0.1.3
to
0.1.4
+1
-1
dist/index.d.mts

@@ -17,3 +17,3 @@ interface NotifyOnWebConfig {

readonly config: NotifyOnWebConfig;
private ably;
private pusher;
private channel;

@@ -20,0 +20,0 @@ private sound;

@@ -17,3 +17,3 @@ interface NotifyOnWebConfig {

readonly config: NotifyOnWebConfig;
private ably;
private pusher;
private channel;

@@ -20,0 +20,0 @@ private sound;

@@ -1,6 +0,6 @@

'use strict';var h=(s,e,n)=>new Promise((o,r)=>{var p=i=>{try{t(n.next(i));}catch(l){r(l);}},a=i=>{try{t(n.throw(i));}catch(l){r(l);}},t=i=>i.done?o(i.value):Promise.resolve(i.value).then(p,a);t((n=n.apply(s,e)).next());});var u=class extends Error{constructor(e){super(e),this.name="NotifyOnWebError";}};var y=class{constructor(){this.audioContext=null;}play(){return h(this,null,function*(){try{this.audioContext||(this.audioContext=new(window.AudioContext||window.webkitAudioContext));let e=this.audioContext.currentTime;this.playTone(349,e,.1,.18,!0),this.playTone(440,e+.08,.08,.22,!0),this.playTone(523,e+.15,.04,.15,!1);}catch(e){}})}playTone(e,n,o,r,p=false){if(!this.audioContext)return;let a=this.audioContext.createOscillator(),t=this.audioContext.createGain(),i=this.audioContext.createDelay(.5);i.delayTime.value=.02;let l=this.audioContext.createGain();l.gain.value=.15;let d=this.audioContext.createBiquadFilter();if(d.type="lowpass",d.frequency.value=1800,d.Q.value=1.2,a.connect(d),d.connect(t),t.connect(this.audioContext.destination),t.connect(i),i.connect(l),l.connect(this.audioContext.destination),a.type="sine",a.frequency.value=e,p){let f=this.audioContext.createOscillator(),b=this.audioContext.createGain();f.frequency.value=5,b.gain.value=2,f.connect(b),b.connect(a.frequency),f.start(n),f.stop(n+r);}t.gain.setValueAtTime(1e-5,n),t.gain.exponentialRampToValueAtTime(o,n+.01),t.gain.setValueAtTime(o*.9,n+r-.06),t.gain.exponentialRampToValueAtTime(1e-5,n+r),a.start(n),a.stop(n+r);}destroy(){this.audioContext&&(this.audioContext.close(),this.audioContext=null);}};var m="CF7JaA.MUFxFA:-9iIuCIf6yhXp5SV31j4Pvy8SreELcO_04ifYgleK5E",c=null,g=class{constructor(e){this.ably=null;this.channel=null;this.connected=false;if(!e.publicKey)throw new u("Public key is required");if(!e.userId)throw new u("User ID is required");this.config=e,this.sound=new y,this.connect();}connect(){return h(this,null,function*(){if(!this.connected)try{let{default:e}=yield import('ably');this.ably=new e.Realtime({key:m,logLevel:0});let n=`project:${this.config.publicKey}:user:${this.config.userId}`;this.channel=this.ably.channels.get(n),this.channel.subscribe("notification",o=>{this.handleNotification(o.data);}),this.ably.connection.on("connected",()=>{this.connected=!0;}),this.ably.connection.on("disconnected",()=>{this.connected=!1;}),this.ably.connection.on("failed",()=>{this.connected=!1;});}catch(e){throw new u(`Failed to connect: ${e}`)}})}disconnect(){this.channel&&(this.channel.unsubscribe(),this.channel=null),this.ably&&(this.ably.close(),this.ably=null,this.connected=false);}destroy(){this.disconnect(),this.sound.destroy(),c===this&&(c=null);}handleNotification(e){try{let n=e.channels||{sound:!0,push:!1};n.sound&&this.sound.play(),n.push&&(document.hidden||!document.hasFocus())&&this.handlePushNotification(e);}catch(n){}}handlePushNotification(e){if("Notification"in window)if(Notification.permission==="granted")try{let n=new Notification("Task Complete",{body:`${e.message||"Your task has finished running"}
'use strict';var x=require('pusher-js');function _interopDefault(e){return e&&e.__esModule?e:{default:e}}var x__default=/*#__PURE__*/_interopDefault(x);var v=(o,e,n)=>new Promise((s,r)=>{var p=i=>{try{t(n.next(i));}catch(u){r(u);}},c=i=>{try{t(n.throw(i));}catch(u){r(u);}},t=i=>i.done?s(i.value):Promise.resolve(i.value).then(p,c);t((n=n.apply(o,e)).next());});var l=class extends Error{constructor(e){super(e),this.name="NotifyOnWebError";}};var f=class{constructor(){this.audioContext=null;}play(){return v(this,null,function*(){try{this.audioContext||(this.audioContext=new(window.AudioContext||window.webkitAudioContext));let e=this.audioContext.currentTime;this.playTone(349,e,.1,.18,!0),this.playTone(440,e+.08,.08,.22,!0),this.playTone(523,e+.15,.04,.15,!1);}catch(e){}})}playTone(e,n,s,r,p=false){if(!this.audioContext)return;let c=this.audioContext.createOscillator(),t=this.audioContext.createGain(),i=this.audioContext.createDelay(.5);i.delayTime.value=.02;let u=this.audioContext.createGain();u.gain.value=.15;let d=this.audioContext.createBiquadFilter();if(d.type="lowpass",d.frequency.value=1800,d.Q.value=1.2,c.connect(d),d.connect(t),t.connect(this.audioContext.destination),t.connect(i),i.connect(u),u.connect(this.audioContext.destination),c.type="sine",c.frequency.value=e,p){let h=this.audioContext.createOscillator(),y=this.audioContext.createGain();h.frequency.value=5,y.gain.value=2,h.connect(y),y.connect(c.frequency),h.start(n),h.stop(n+r);}t.gain.setValueAtTime(1e-5,n),t.gain.exponentialRampToValueAtTime(s,n+.01),t.gain.setValueAtTime(s*.9,n+r-.06),t.gain.exponentialRampToValueAtTime(1e-5,n+r),c.start(n),c.stop(n+r);}destroy(){this.audioContext&&(this.audioContext.close(),this.audioContext=null);}};var g="70225167dde6bac400ea",m="mt1",a=null,b=class{constructor(e){this.pusher=null;this.channel=null;this.connected=false;if(!e.publicKey)throw new l("Public key is required");if(!e.userId)throw new l("User ID is required");this.config=e,this.sound=new f,this.connect();}connect(){if(!this.connected)try{this.pusher=new x__default.default(g,{cluster:m});let e=`project-${this.config.publicKey}-user-${this.config.userId}`;this.channel=this.pusher.subscribe(e),this.channel.bind("notification",n=>{this.handleNotification(n);}),this.pusher.connection.bind("connected",()=>{this.connected=!0;}),this.pusher.connection.bind("disconnected",()=>{this.connected=!1;}),this.pusher.connection.bind("failed",()=>{this.connected=!1;});}catch(e){throw new l(`Failed to connect: ${e}`)}}disconnect(){this.channel&&this.pusher&&(this.pusher.unsubscribe(this.channel.name),this.channel=null),this.pusher&&(this.pusher.disconnect(),this.pusher=null,this.connected=false);}destroy(){this.disconnect(),this.sound.destroy(),a===this&&(a=null);}handleNotification(e){try{let n=e.channels||{sound:!0,push:!1};n.sound&&this.sound.play(),n.push&&(document.hidden||!document.hasFocus())&&this.handlePushNotification(e);}catch(n){}}handlePushNotification(e){if("Notification"in window)if(Notification.permission==="granted")try{let n=new Notification("Task Complete",{body:`${e.message||"Your task has finished running"}
Powered by NotifyOn`,icon:"/favicon.ico",tag:e.id,requireInteraction:!1,silent:!1});n.onclick=()=>{window.focus(),n.close();};}catch(n){}else Notification.permission!=="denied"&&Notification.requestPermission().then(n=>{if(n==="granted"){let o=new Notification("Task Complete",{body:`${e.message||"Your task has finished running"}
Powered by NotifyOn`,icon:"/favicon.ico",tag:e.id,requireInteraction:!1,silent:!1});n.onclick=()=>{window.focus(),n.close();};}catch(n){}else Notification.permission!=="denied"&&Notification.requestPermission().then(n=>{if(n==="granted"){let s=new Notification("Task Complete",{body:`${e.message||"Your task has finished running"}
Powered by NotifyOn`,icon:"/favicon.ico",tag:e.id,requireInteraction:false,silent:false});o.onclick=()=>{window.focus(),o.close();};}});}};function v(s){if(c){let e=c.config;if(e.publicKey===s.publicKey&&e.userId===s.userId)return c;c.destroy();}return c=new g(s),c}exports.connect=v;//# sourceMappingURL=index.js.map
Powered by NotifyOn`,icon:"/favicon.ico",tag:e.id,requireInteraction:false,silent:false});s.onclick=()=>{window.focus(),s.close();};}});}};function C(o){if(a){let e=a.config;if(e.publicKey===o.publicKey&&e.userId===o.userId)return a;a.destroy();}return a=new b(o),a}exports.connect=C;//# sourceMappingURL=index.js.map
//# sourceMappingURL=index.js.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/types.ts","../src/sound.ts","../src/client.ts"],"names":["NotifyOnWebError","message","NotificationSound","__async","now","error","frequency","startTime","volume","duration","addVibrato","oscillator","gainNode","delay","delayGain","filter","vibrato","vibratoGain","ABLY_SUBSCRIBE_KEY","globalInstance","NotifyOnWeb","config","AblyLib","channelName","data","channels","notification","permission","connect","existingConfig"],"mappings":"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","file":"index.js","sourcesContent":["export interface NotifyOnWebConfig {\n publicKey: string; // NotifyOn project public key\n userId: string;\n}\n\nexport interface NotificationEvent {\n id: string;\n message: string;\n timestamp: string;\n channels?: {\n sound?: boolean;\n push?: boolean;\n };\n}\n\nexport class NotifyOnWebError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'NotifyOnWebError';\n }\n}","export class NotificationSound {\n private audioContext: AudioContext | null = null;\n\n /**\n * Play a notification sound using Web Audio API\n */\n async play(): Promise<void> {\n try {\n // Create audio context if not exists\n if (!this.audioContext) {\n this.audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();\n }\n\n const now = this.audioContext.currentTime;\n \n // Create a natural, pleasant chime with mid-range tones\n // First tone - F4 (349Hz) with slight vibrato for naturalness\n this.playTone(349, now, 0.1, 0.18, true);\n \n // Second tone - A4 (440Hz) - major third up, very harmonious\n this.playTone(440, now + 0.08, 0.08, 0.22, true);\n \n // Third soft tone - C5 (523Hz) - adds sparkle\n this.playTone(523, now + 0.15, 0.04, 0.15, false);\n \n } catch (error) {\n // Silently handle errors to avoid polluting user's console\n }\n }\n\n private playTone(frequency: number, startTime: number, volume: number, duration: number, addVibrato: boolean = false): void {\n if (!this.audioContext) return;\n \n // Create oscillator and nodes for this tone\n const oscillator = this.audioContext.createOscillator();\n const gainNode = this.audioContext.createGain();\n \n // Add reverb-like effect with delay for warmth\n const delay = this.audioContext.createDelay(0.5);\n delay.delayTime.value = 0.02;\n \n const delayGain = this.audioContext.createGain();\n delayGain.gain.value = 0.15; // Very subtle reverb\n \n // Add a low-pass filter with some resonance for character\n const filter = this.audioContext.createBiquadFilter();\n filter.type = 'lowpass';\n filter.frequency.value = 1800; // Balanced cutoff\n filter.Q.value = 1.2; // Slight resonance for character\n \n // Connect main signal path: oscillator -> filter -> gain -> destination\n oscillator.connect(filter);\n filter.connect(gainNode);\n gainNode.connect(this.audioContext.destination);\n \n // Connect delay path for subtle reverb: gain -> delay -> delayGain -> destination\n gainNode.connect(delay);\n delay.connect(delayGain);\n delayGain.connect(this.audioContext.destination);\n \n // Use sine wave for the most gentle sound\n oscillator.type = 'sine';\n oscillator.frequency.value = frequency;\n \n // Add subtle vibrato for more natural, less mechanical sound\n if (addVibrato) {\n const vibrato = this.audioContext.createOscillator();\n const vibratoGain = this.audioContext.createGain();\n vibrato.frequency.value = 5; // 5Hz vibrato rate\n vibratoGain.gain.value = 2; // Very subtle frequency modulation\n vibrato.connect(vibratoGain);\n vibratoGain.connect(oscillator.frequency);\n vibrato.start(startTime);\n vibrato.stop(startTime + duration);\n }\n \n // Natural envelope with varied attack for organic feel\n gainNode.gain.setValueAtTime(0.00001, startTime);\n gainNode.gain.exponentialRampToValueAtTime(volume, startTime + 0.01); // Quick but soft attack\n gainNode.gain.setValueAtTime(volume * 0.9, startTime + duration - 0.06); // Slight decay\n gainNode.gain.exponentialRampToValueAtTime(0.00001, startTime + duration); // Natural release\n \n // Play the tone\n oscillator.start(startTime);\n oscillator.stop(startTime + duration);\n }\n\n /**\n * Clean up audio context\n */\n destroy(): void {\n if (this.audioContext) {\n this.audioContext.close();\n this.audioContext = null;\n }\n }\n}","import type { NotifyOnWebConfig, NotificationEvent } from \"./types\";\nimport { NotifyOnWebError } from \"./types\";\nimport { NotificationSound } from \"./sound\";\n\n// Type-only import for TypeScript\nimport type Ably from \"ably\";\n\nconst ABLY_SUBSCRIBE_KEY =\n \"CF7JaA.MUFxFA:-9iIuCIf6yhXp5SV31j4Pvy8SreELcO_04ifYgleK5E\";\n\n// Singleton instance storage\nlet globalInstance: NotifyOnWeb | null = null;\n\nexport class NotifyOnWeb {\n public readonly config: NotifyOnWebConfig;\n private ably: Ably.Realtime | null = null;\n private channel: Ably.RealtimeChannel | null = null;\n private sound: NotificationSound;\n private connected = false;\n\n constructor(config: NotifyOnWebConfig) {\n if (!config.publicKey) {\n throw new NotifyOnWebError(\"Public key is required\");\n }\n if (!config.userId) {\n throw new NotifyOnWebError(\"User ID is required\");\n }\n\n this.config = config;\n\n this.sound = new NotificationSound();\n\n // Auto-connect on instantiation\n this.connect();\n }\n\n /**\n * Start listening for notifications (called automatically)\n */\n private async connect(): Promise<void> {\n if (this.connected) {\n return;\n }\n\n try {\n // Dynamically import Ably only on client side\n const { default: AblyLib } = await import(\"ably\");\n \n // Initialize Ably with our internal subscribe key\n this.ably = new AblyLib.Realtime({\n key: ABLY_SUBSCRIBE_KEY,\n // Optional: log level for debugging\n logLevel: 0, // 0 = verbose, 4 = none\n });\n\n // Use last 8 characters of public key for channel naming\n // This provides enough uniqueness while keeping channel names manageable\n const channelName = `project:${this.config.publicKey}:user:${this.config.userId}`;\n this.channel = this.ably.channels.get(channelName);\n\n // Subscribe to notification messages\n this.channel.subscribe(\"notification\", (message) => {\n this.handleNotification(message.data);\n });\n\n // Monitor connection state\n this.ably.connection.on(\"connected\", () => {\n this.connected = true;\n });\n\n this.ably.connection.on(\"disconnected\", () => {\n this.connected = false;\n });\n\n this.ably.connection.on(\"failed\", () => {\n this.connected = false;\n });\n } catch (error) {\n throw new NotifyOnWebError(`Failed to connect: ${error}`);\n }\n }\n\n /**\n * Stop listening for notifications (called automatically on destroy)\n */\n private disconnect(): void {\n if (this.channel) {\n this.channel.unsubscribe();\n this.channel = null;\n }\n\n if (this.ably) {\n this.ably.close();\n this.ably = null;\n this.connected = false;\n }\n }\n\n /**\n * Clean up resources\n */\n destroy(): void {\n this.disconnect();\n this.sound.destroy();\n // Clear global instance if this is the one being destroyed\n if (globalInstance === this) {\n globalInstance = null;\n }\n }\n\n private handleNotification(data: NotificationEvent): void {\n try {\n // Check which channels are enabled for this notification\n const channels = data.channels || { sound: true, push: false }; // Default to sound enabled\n\n // Progressive notification strategy:\n // 1. Always play sound if enabled\n // 2. Show push only if enabled AND tab is not visible\n\n // Handle sound notification (always if enabled)\n if (channels.sound) {\n this.sound.play();\n }\n\n // Handle push notification (only if tab is not visible/focused)\n if (channels.push) {\n // Check if document is hidden or not focused\n const isTabHidden = document.hidden || !document.hasFocus();\n\n if (isTabHidden) {\n this.handlePushNotification(data);\n }\n }\n } catch (error) {\n // Silently handle errors to avoid polluting user's console\n }\n }\n\n private handlePushNotification(data: NotificationEvent): void {\n // Check if browser supports notifications\n if (!(\"Notification\" in window)) {\n return;\n }\n\n // Check notification permission\n if (Notification.permission === \"granted\") {\n // Create push notification\n try {\n const notification = new Notification(\"Task Complete\", {\n body: `${\n data.message || \"Your task has finished running\"\n }\\n\\nPowered by NotifyOn`,\n icon: \"/favicon.ico\",\n tag: data.id,\n requireInteraction: false,\n silent: false,\n });\n\n // Handle click - focus the tab/window\n notification.onclick = () => {\n window.focus();\n notification.close();\n };\n } catch (error) {\n // Silently handle notification creation errors\n }\n } else if (Notification.permission !== \"denied\") {\n // Request permission if not yet decided\n Notification.requestPermission().then((permission) => {\n if (permission === \"granted\") {\n const notification = new Notification(\"Task Complete\", {\n body: `${\n data.message || \"Your task has finished running\"\n }\\n\\nPowered by NotifyOn`,\n icon: \"/favicon.ico\",\n tag: data.id,\n requireInteraction: false,\n silent: false,\n });\n\n // Handle click - focus the tab/window\n notification.onclick = () => {\n window.focus();\n notification.close();\n };\n }\n });\n }\n }\n}\n\n// Simple function API with singleton pattern\nexport function connect(config: NotifyOnWebConfig): NotifyOnWeb {\n // Check if we already have a connection with the same config\n if (globalInstance) {\n const existingConfig = globalInstance.config;\n // If connecting with same publicKey and userId, return existing instance\n if (\n existingConfig.publicKey === config.publicKey &&\n existingConfig.userId === config.userId\n ) {\n return globalInstance;\n }\n // If different config, destroy old connection and create new one\n globalInstance.destroy();\n }\n \n // Create and store new instance\n globalInstance = new NotifyOnWeb(config);\n return globalInstance;\n}\n"]}
{"version":3,"sources":["../src/types.ts","../src/sound.ts","../src/client.ts"],"names":["NotifyOnWebError","message","NotificationSound","__async","now","error","frequency","startTime","volume","duration","addVibrato","oscillator","gainNode","delay","delayGain","filter","vibrato","vibratoGain","PUSHER_APP_KEY","PUSHER_APP_CLUSTER","globalInstance","NotifyOnWeb","config","Pusher","channelName","data","channels","notification","permission","connect","existingConfig"],"mappings":"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","file":"index.js","sourcesContent":["export interface NotifyOnWebConfig {\n publicKey: string; // NotifyOn project public key\n userId: string;\n}\n\nexport interface NotificationEvent {\n id: string;\n message: string;\n timestamp: string;\n channels?: {\n sound?: boolean;\n push?: boolean;\n };\n}\n\nexport class NotifyOnWebError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"NotifyOnWebError\";\n }\n}\n","export class NotificationSound {\n private audioContext: AudioContext | null = null;\n\n /**\n * Play a notification sound using Web Audio API\n */\n async play(): Promise<void> {\n try {\n // Create audio context if not exists\n if (!this.audioContext) {\n this.audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();\n }\n\n const now = this.audioContext.currentTime;\n \n // Create a natural, pleasant chime with mid-range tones\n // First tone - F4 (349Hz) with slight vibrato for naturalness\n this.playTone(349, now, 0.1, 0.18, true);\n \n // Second tone - A4 (440Hz) - major third up, very harmonious\n this.playTone(440, now + 0.08, 0.08, 0.22, true);\n \n // Third soft tone - C5 (523Hz) - adds sparkle\n this.playTone(523, now + 0.15, 0.04, 0.15, false);\n \n } catch (error) {\n // Silently handle errors to avoid polluting user's console\n }\n }\n\n private playTone(frequency: number, startTime: number, volume: number, duration: number, addVibrato: boolean = false): void {\n if (!this.audioContext) return;\n \n // Create oscillator and nodes for this tone\n const oscillator = this.audioContext.createOscillator();\n const gainNode = this.audioContext.createGain();\n \n // Add reverb-like effect with delay for warmth\n const delay = this.audioContext.createDelay(0.5);\n delay.delayTime.value = 0.02;\n \n const delayGain = this.audioContext.createGain();\n delayGain.gain.value = 0.15; // Very subtle reverb\n \n // Add a low-pass filter with some resonance for character\n const filter = this.audioContext.createBiquadFilter();\n filter.type = 'lowpass';\n filter.frequency.value = 1800; // Balanced cutoff\n filter.Q.value = 1.2; // Slight resonance for character\n \n // Connect main signal path: oscillator -> filter -> gain -> destination\n oscillator.connect(filter);\n filter.connect(gainNode);\n gainNode.connect(this.audioContext.destination);\n \n // Connect delay path for subtle reverb: gain -> delay -> delayGain -> destination\n gainNode.connect(delay);\n delay.connect(delayGain);\n delayGain.connect(this.audioContext.destination);\n \n // Use sine wave for the most gentle sound\n oscillator.type = 'sine';\n oscillator.frequency.value = frequency;\n \n // Add subtle vibrato for more natural, less mechanical sound\n if (addVibrato) {\n const vibrato = this.audioContext.createOscillator();\n const vibratoGain = this.audioContext.createGain();\n vibrato.frequency.value = 5; // 5Hz vibrato rate\n vibratoGain.gain.value = 2; // Very subtle frequency modulation\n vibrato.connect(vibratoGain);\n vibratoGain.connect(oscillator.frequency);\n vibrato.start(startTime);\n vibrato.stop(startTime + duration);\n }\n \n // Natural envelope with varied attack for organic feel\n gainNode.gain.setValueAtTime(0.00001, startTime);\n gainNode.gain.exponentialRampToValueAtTime(volume, startTime + 0.01); // Quick but soft attack\n gainNode.gain.setValueAtTime(volume * 0.9, startTime + duration - 0.06); // Slight decay\n gainNode.gain.exponentialRampToValueAtTime(0.00001, startTime + duration); // Natural release\n \n // Play the tone\n oscillator.start(startTime);\n oscillator.stop(startTime + duration);\n }\n\n /**\n * Clean up audio context\n */\n destroy(): void {\n if (this.audioContext) {\n this.audioContext.close();\n this.audioContext = null;\n }\n }\n}","import type { NotifyOnWebConfig, NotificationEvent } from \"./types\";\nimport { NotifyOnWebError } from \"./types\";\nimport { NotificationSound } from \"./sound\";\nimport Pusher from \"pusher-js\";\n\nconst PUSHER_APP_KEY = \"70225167dde6bac400ea\";\nconst PUSHER_APP_CLUSTER = \"mt1\";\n\n// Singleton instance storage\nlet globalInstance: NotifyOnWeb | null = null;\n\nexport class NotifyOnWeb {\n public readonly config: NotifyOnWebConfig;\n private pusher: Pusher | null = null;\n private channel: any | null = null;\n private sound: NotificationSound;\n private connected = false;\n\n constructor(config: NotifyOnWebConfig) {\n if (!config.publicKey) {\n throw new NotifyOnWebError(\"Public key is required\");\n }\n if (!config.userId) {\n throw new NotifyOnWebError(\"User ID is required\");\n }\n\n this.config = config;\n\n this.sound = new NotificationSound();\n\n // Auto-connect on instantiation\n this.connect();\n }\n\n /**\n * Start listening for notifications (called automatically)\n */\n private connect(): void {\n if (this.connected) {\n return;\n }\n\n try {\n this.pusher = new Pusher(PUSHER_APP_KEY, {\n cluster: PUSHER_APP_CLUSTER,\n });\n\n // Channel naming convention matches server\n const channelName = `project-${this.config.publicKey}-user-${this.config.userId}`;\n this.channel = this.pusher.subscribe(channelName);\n\n // Subscribe to notification events\n this.channel.bind(\"notification\", (data: any) => {\n this.handleNotification(data);\n });\n\n // Monitor connection state\n this.pusher.connection.bind(\"connected\", () => {\n this.connected = true;\n });\n\n this.pusher.connection.bind(\"disconnected\", () => {\n this.connected = false;\n });\n\n this.pusher.connection.bind(\"failed\", () => {\n this.connected = false;\n });\n } catch (error) {\n throw new NotifyOnWebError(`Failed to connect: ${error}`);\n }\n }\n\n /**\n * Stop listening for notifications (called automatically on destroy)\n */\n private disconnect(): void {\n if (this.channel && this.pusher) {\n this.pusher.unsubscribe(this.channel.name);\n this.channel = null;\n }\n\n if (this.pusher) {\n this.pusher.disconnect();\n this.pusher = null;\n this.connected = false;\n }\n }\n\n /**\n * Clean up resources\n */\n destroy(): void {\n this.disconnect();\n this.sound.destroy();\n // Clear global instance if this is the one being destroyed\n if (globalInstance === this) {\n globalInstance = null;\n }\n }\n\n private handleNotification(data: NotificationEvent): void {\n try {\n // Check which channels are enabled for this notification\n const channels = data.channels || { sound: true, push: false }; // Default to sound enabled\n\n // Progressive notification strategy:\n // 1. Always play sound if enabled\n // 2. Show push only if enabled AND tab is not visible\n\n // Handle sound notification (always if enabled)\n if (channels.sound) {\n this.sound.play();\n }\n\n // Handle push notification (only if tab is not visible/focused)\n if (channels.push) {\n // Check if document is hidden or not focused\n const isTabHidden = document.hidden || !document.hasFocus();\n\n if (isTabHidden) {\n this.handlePushNotification(data);\n }\n }\n } catch (error) {\n // Silently handle errors to avoid polluting user's console\n }\n }\n\n private handlePushNotification(data: NotificationEvent): void {\n // Check if browser supports notifications\n if (!(\"Notification\" in window)) {\n return;\n }\n\n // Check notification permission\n if (Notification.permission === \"granted\") {\n // Create push notification\n try {\n const notification = new Notification(\"Task Complete\", {\n body: `${\n data.message || \"Your task has finished running\"\n }\\n\\nPowered by NotifyOn`,\n icon: \"/favicon.ico\",\n tag: data.id,\n requireInteraction: false,\n silent: false,\n });\n\n // Handle click - focus the tab/window\n notification.onclick = () => {\n window.focus();\n notification.close();\n };\n } catch (error) {\n // Silently handle notification creation errors\n }\n } else if (Notification.permission !== \"denied\") {\n // Request permission if not yet decided\n Notification.requestPermission().then((permission) => {\n if (permission === \"granted\") {\n const notification = new Notification(\"Task Complete\", {\n body: `${\n data.message || \"Your task has finished running\"\n }\\n\\nPowered by NotifyOn`,\n icon: \"/favicon.ico\",\n tag: data.id,\n requireInteraction: false,\n silent: false,\n });\n\n // Handle click - focus the tab/window\n notification.onclick = () => {\n window.focus();\n notification.close();\n };\n }\n });\n }\n }\n}\n\n// Simple function API with singleton pattern\nexport function connect(config: NotifyOnWebConfig): NotifyOnWeb {\n // Check if we already have a connection with the same config\n if (globalInstance) {\n const existingConfig = globalInstance.config;\n // If connecting with same publicKey and userId, return existing instance\n if (\n existingConfig.publicKey === config.publicKey &&\n existingConfig.userId === config.userId\n ) {\n return globalInstance;\n }\n // If different config, destroy old connection and create new one\n globalInstance.destroy();\n }\n\n // Create and store new instance\n globalInstance = new NotifyOnWeb(config);\n return globalInstance;\n}\n"]}

@@ -1,6 +0,6 @@

var h=(s,e,n)=>new Promise((o,r)=>{var p=i=>{try{t(n.next(i));}catch(l){r(l);}},a=i=>{try{t(n.throw(i));}catch(l){r(l);}},t=i=>i.done?o(i.value):Promise.resolve(i.value).then(p,a);t((n=n.apply(s,e)).next());});var u=class extends Error{constructor(e){super(e),this.name="NotifyOnWebError";}};var y=class{constructor(){this.audioContext=null;}play(){return h(this,null,function*(){try{this.audioContext||(this.audioContext=new(window.AudioContext||window.webkitAudioContext));let e=this.audioContext.currentTime;this.playTone(349,e,.1,.18,!0),this.playTone(440,e+.08,.08,.22,!0),this.playTone(523,e+.15,.04,.15,!1);}catch(e){}})}playTone(e,n,o,r,p=false){if(!this.audioContext)return;let a=this.audioContext.createOscillator(),t=this.audioContext.createGain(),i=this.audioContext.createDelay(.5);i.delayTime.value=.02;let l=this.audioContext.createGain();l.gain.value=.15;let d=this.audioContext.createBiquadFilter();if(d.type="lowpass",d.frequency.value=1800,d.Q.value=1.2,a.connect(d),d.connect(t),t.connect(this.audioContext.destination),t.connect(i),i.connect(l),l.connect(this.audioContext.destination),a.type="sine",a.frequency.value=e,p){let f=this.audioContext.createOscillator(),b=this.audioContext.createGain();f.frequency.value=5,b.gain.value=2,f.connect(b),b.connect(a.frequency),f.start(n),f.stop(n+r);}t.gain.setValueAtTime(1e-5,n),t.gain.exponentialRampToValueAtTime(o,n+.01),t.gain.setValueAtTime(o*.9,n+r-.06),t.gain.exponentialRampToValueAtTime(1e-5,n+r),a.start(n),a.stop(n+r);}destroy(){this.audioContext&&(this.audioContext.close(),this.audioContext=null);}};var m="CF7JaA.MUFxFA:-9iIuCIf6yhXp5SV31j4Pvy8SreELcO_04ifYgleK5E",c=null,g=class{constructor(e){this.ably=null;this.channel=null;this.connected=false;if(!e.publicKey)throw new u("Public key is required");if(!e.userId)throw new u("User ID is required");this.config=e,this.sound=new y,this.connect();}connect(){return h(this,null,function*(){if(!this.connected)try{let{default:e}=yield import('ably');this.ably=new e.Realtime({key:m,logLevel:0});let n=`project:${this.config.publicKey}:user:${this.config.userId}`;this.channel=this.ably.channels.get(n),this.channel.subscribe("notification",o=>{this.handleNotification(o.data);}),this.ably.connection.on("connected",()=>{this.connected=!0;}),this.ably.connection.on("disconnected",()=>{this.connected=!1;}),this.ably.connection.on("failed",()=>{this.connected=!1;});}catch(e){throw new u(`Failed to connect: ${e}`)}})}disconnect(){this.channel&&(this.channel.unsubscribe(),this.channel=null),this.ably&&(this.ably.close(),this.ably=null,this.connected=false);}destroy(){this.disconnect(),this.sound.destroy(),c===this&&(c=null);}handleNotification(e){try{let n=e.channels||{sound:!0,push:!1};n.sound&&this.sound.play(),n.push&&(document.hidden||!document.hasFocus())&&this.handlePushNotification(e);}catch(n){}}handlePushNotification(e){if("Notification"in window)if(Notification.permission==="granted")try{let n=new Notification("Task Complete",{body:`${e.message||"Your task has finished running"}
import x from'pusher-js';var v=(o,e,n)=>new Promise((s,r)=>{var p=i=>{try{t(n.next(i));}catch(u){r(u);}},c=i=>{try{t(n.throw(i));}catch(u){r(u);}},t=i=>i.done?s(i.value):Promise.resolve(i.value).then(p,c);t((n=n.apply(o,e)).next());});var l=class extends Error{constructor(e){super(e),this.name="NotifyOnWebError";}};var f=class{constructor(){this.audioContext=null;}play(){return v(this,null,function*(){try{this.audioContext||(this.audioContext=new(window.AudioContext||window.webkitAudioContext));let e=this.audioContext.currentTime;this.playTone(349,e,.1,.18,!0),this.playTone(440,e+.08,.08,.22,!0),this.playTone(523,e+.15,.04,.15,!1);}catch(e){}})}playTone(e,n,s,r,p=false){if(!this.audioContext)return;let c=this.audioContext.createOscillator(),t=this.audioContext.createGain(),i=this.audioContext.createDelay(.5);i.delayTime.value=.02;let u=this.audioContext.createGain();u.gain.value=.15;let d=this.audioContext.createBiquadFilter();if(d.type="lowpass",d.frequency.value=1800,d.Q.value=1.2,c.connect(d),d.connect(t),t.connect(this.audioContext.destination),t.connect(i),i.connect(u),u.connect(this.audioContext.destination),c.type="sine",c.frequency.value=e,p){let h=this.audioContext.createOscillator(),y=this.audioContext.createGain();h.frequency.value=5,y.gain.value=2,h.connect(y),y.connect(c.frequency),h.start(n),h.stop(n+r);}t.gain.setValueAtTime(1e-5,n),t.gain.exponentialRampToValueAtTime(s,n+.01),t.gain.setValueAtTime(s*.9,n+r-.06),t.gain.exponentialRampToValueAtTime(1e-5,n+r),c.start(n),c.stop(n+r);}destroy(){this.audioContext&&(this.audioContext.close(),this.audioContext=null);}};var g="70225167dde6bac400ea",m="mt1",a=null,b=class{constructor(e){this.pusher=null;this.channel=null;this.connected=false;if(!e.publicKey)throw new l("Public key is required");if(!e.userId)throw new l("User ID is required");this.config=e,this.sound=new f,this.connect();}connect(){if(!this.connected)try{this.pusher=new x(g,{cluster:m});let e=`project-${this.config.publicKey}-user-${this.config.userId}`;this.channel=this.pusher.subscribe(e),this.channel.bind("notification",n=>{this.handleNotification(n);}),this.pusher.connection.bind("connected",()=>{this.connected=!0;}),this.pusher.connection.bind("disconnected",()=>{this.connected=!1;}),this.pusher.connection.bind("failed",()=>{this.connected=!1;});}catch(e){throw new l(`Failed to connect: ${e}`)}}disconnect(){this.channel&&this.pusher&&(this.pusher.unsubscribe(this.channel.name),this.channel=null),this.pusher&&(this.pusher.disconnect(),this.pusher=null,this.connected=false);}destroy(){this.disconnect(),this.sound.destroy(),a===this&&(a=null);}handleNotification(e){try{let n=e.channels||{sound:!0,push:!1};n.sound&&this.sound.play(),n.push&&(document.hidden||!document.hasFocus())&&this.handlePushNotification(e);}catch(n){}}handlePushNotification(e){if("Notification"in window)if(Notification.permission==="granted")try{let n=new Notification("Task Complete",{body:`${e.message||"Your task has finished running"}
Powered by NotifyOn`,icon:"/favicon.ico",tag:e.id,requireInteraction:!1,silent:!1});n.onclick=()=>{window.focus(),n.close();};}catch(n){}else Notification.permission!=="denied"&&Notification.requestPermission().then(n=>{if(n==="granted"){let o=new Notification("Task Complete",{body:`${e.message||"Your task has finished running"}
Powered by NotifyOn`,icon:"/favicon.ico",tag:e.id,requireInteraction:!1,silent:!1});n.onclick=()=>{window.focus(),n.close();};}catch(n){}else Notification.permission!=="denied"&&Notification.requestPermission().then(n=>{if(n==="granted"){let s=new Notification("Task Complete",{body:`${e.message||"Your task has finished running"}
Powered by NotifyOn`,icon:"/favicon.ico",tag:e.id,requireInteraction:false,silent:false});o.onclick=()=>{window.focus(),o.close();};}});}};function v(s){if(c){let e=c.config;if(e.publicKey===s.publicKey&&e.userId===s.userId)return c;c.destroy();}return c=new g(s),c}export{v as connect};//# sourceMappingURL=index.mjs.map
Powered by NotifyOn`,icon:"/favicon.ico",tag:e.id,requireInteraction:false,silent:false});s.onclick=()=>{window.focus(),s.close();};}});}};function C(o){if(a){let e=a.config;if(e.publicKey===o.publicKey&&e.userId===o.userId)return a;a.destroy();}return a=new b(o),a}export{C as connect};//# sourceMappingURL=index.mjs.map
//# sourceMappingURL=index.mjs.map

@@ -1,1 +0,1 @@

{"version":3,"sources":["../src/types.ts","../src/sound.ts","../src/client.ts"],"names":["NotifyOnWebError","message","NotificationSound","__async","now","error","frequency","startTime","volume","duration","addVibrato","oscillator","gainNode","delay","delayGain","filter","vibrato","vibratoGain","ABLY_SUBSCRIBE_KEY","globalInstance","NotifyOnWeb","config","AblyLib","channelName","data","channels","notification","permission","connect","existingConfig"],"mappings":"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","file":"index.mjs","sourcesContent":["export interface NotifyOnWebConfig {\n publicKey: string; // NotifyOn project public key\n userId: string;\n}\n\nexport interface NotificationEvent {\n id: string;\n message: string;\n timestamp: string;\n channels?: {\n sound?: boolean;\n push?: boolean;\n };\n}\n\nexport class NotifyOnWebError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'NotifyOnWebError';\n }\n}","export class NotificationSound {\n private audioContext: AudioContext | null = null;\n\n /**\n * Play a notification sound using Web Audio API\n */\n async play(): Promise<void> {\n try {\n // Create audio context if not exists\n if (!this.audioContext) {\n this.audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();\n }\n\n const now = this.audioContext.currentTime;\n \n // Create a natural, pleasant chime with mid-range tones\n // First tone - F4 (349Hz) with slight vibrato for naturalness\n this.playTone(349, now, 0.1, 0.18, true);\n \n // Second tone - A4 (440Hz) - major third up, very harmonious\n this.playTone(440, now + 0.08, 0.08, 0.22, true);\n \n // Third soft tone - C5 (523Hz) - adds sparkle\n this.playTone(523, now + 0.15, 0.04, 0.15, false);\n \n } catch (error) {\n // Silently handle errors to avoid polluting user's console\n }\n }\n\n private playTone(frequency: number, startTime: number, volume: number, duration: number, addVibrato: boolean = false): void {\n if (!this.audioContext) return;\n \n // Create oscillator and nodes for this tone\n const oscillator = this.audioContext.createOscillator();\n const gainNode = this.audioContext.createGain();\n \n // Add reverb-like effect with delay for warmth\n const delay = this.audioContext.createDelay(0.5);\n delay.delayTime.value = 0.02;\n \n const delayGain = this.audioContext.createGain();\n delayGain.gain.value = 0.15; // Very subtle reverb\n \n // Add a low-pass filter with some resonance for character\n const filter = this.audioContext.createBiquadFilter();\n filter.type = 'lowpass';\n filter.frequency.value = 1800; // Balanced cutoff\n filter.Q.value = 1.2; // Slight resonance for character\n \n // Connect main signal path: oscillator -> filter -> gain -> destination\n oscillator.connect(filter);\n filter.connect(gainNode);\n gainNode.connect(this.audioContext.destination);\n \n // Connect delay path for subtle reverb: gain -> delay -> delayGain -> destination\n gainNode.connect(delay);\n delay.connect(delayGain);\n delayGain.connect(this.audioContext.destination);\n \n // Use sine wave for the most gentle sound\n oscillator.type = 'sine';\n oscillator.frequency.value = frequency;\n \n // Add subtle vibrato for more natural, less mechanical sound\n if (addVibrato) {\n const vibrato = this.audioContext.createOscillator();\n const vibratoGain = this.audioContext.createGain();\n vibrato.frequency.value = 5; // 5Hz vibrato rate\n vibratoGain.gain.value = 2; // Very subtle frequency modulation\n vibrato.connect(vibratoGain);\n vibratoGain.connect(oscillator.frequency);\n vibrato.start(startTime);\n vibrato.stop(startTime + duration);\n }\n \n // Natural envelope with varied attack for organic feel\n gainNode.gain.setValueAtTime(0.00001, startTime);\n gainNode.gain.exponentialRampToValueAtTime(volume, startTime + 0.01); // Quick but soft attack\n gainNode.gain.setValueAtTime(volume * 0.9, startTime + duration - 0.06); // Slight decay\n gainNode.gain.exponentialRampToValueAtTime(0.00001, startTime + duration); // Natural release\n \n // Play the tone\n oscillator.start(startTime);\n oscillator.stop(startTime + duration);\n }\n\n /**\n * Clean up audio context\n */\n destroy(): void {\n if (this.audioContext) {\n this.audioContext.close();\n this.audioContext = null;\n }\n }\n}","import type { NotifyOnWebConfig, NotificationEvent } from \"./types\";\nimport { NotifyOnWebError } from \"./types\";\nimport { NotificationSound } from \"./sound\";\n\n// Type-only import for TypeScript\nimport type Ably from \"ably\";\n\nconst ABLY_SUBSCRIBE_KEY =\n \"CF7JaA.MUFxFA:-9iIuCIf6yhXp5SV31j4Pvy8SreELcO_04ifYgleK5E\";\n\n// Singleton instance storage\nlet globalInstance: NotifyOnWeb | null = null;\n\nexport class NotifyOnWeb {\n public readonly config: NotifyOnWebConfig;\n private ably: Ably.Realtime | null = null;\n private channel: Ably.RealtimeChannel | null = null;\n private sound: NotificationSound;\n private connected = false;\n\n constructor(config: NotifyOnWebConfig) {\n if (!config.publicKey) {\n throw new NotifyOnWebError(\"Public key is required\");\n }\n if (!config.userId) {\n throw new NotifyOnWebError(\"User ID is required\");\n }\n\n this.config = config;\n\n this.sound = new NotificationSound();\n\n // Auto-connect on instantiation\n this.connect();\n }\n\n /**\n * Start listening for notifications (called automatically)\n */\n private async connect(): Promise<void> {\n if (this.connected) {\n return;\n }\n\n try {\n // Dynamically import Ably only on client side\n const { default: AblyLib } = await import(\"ably\");\n \n // Initialize Ably with our internal subscribe key\n this.ably = new AblyLib.Realtime({\n key: ABLY_SUBSCRIBE_KEY,\n // Optional: log level for debugging\n logLevel: 0, // 0 = verbose, 4 = none\n });\n\n // Use last 8 characters of public key for channel naming\n // This provides enough uniqueness while keeping channel names manageable\n const channelName = `project:${this.config.publicKey}:user:${this.config.userId}`;\n this.channel = this.ably.channels.get(channelName);\n\n // Subscribe to notification messages\n this.channel.subscribe(\"notification\", (message) => {\n this.handleNotification(message.data);\n });\n\n // Monitor connection state\n this.ably.connection.on(\"connected\", () => {\n this.connected = true;\n });\n\n this.ably.connection.on(\"disconnected\", () => {\n this.connected = false;\n });\n\n this.ably.connection.on(\"failed\", () => {\n this.connected = false;\n });\n } catch (error) {\n throw new NotifyOnWebError(`Failed to connect: ${error}`);\n }\n }\n\n /**\n * Stop listening for notifications (called automatically on destroy)\n */\n private disconnect(): void {\n if (this.channel) {\n this.channel.unsubscribe();\n this.channel = null;\n }\n\n if (this.ably) {\n this.ably.close();\n this.ably = null;\n this.connected = false;\n }\n }\n\n /**\n * Clean up resources\n */\n destroy(): void {\n this.disconnect();\n this.sound.destroy();\n // Clear global instance if this is the one being destroyed\n if (globalInstance === this) {\n globalInstance = null;\n }\n }\n\n private handleNotification(data: NotificationEvent): void {\n try {\n // Check which channels are enabled for this notification\n const channels = data.channels || { sound: true, push: false }; // Default to sound enabled\n\n // Progressive notification strategy:\n // 1. Always play sound if enabled\n // 2. Show push only if enabled AND tab is not visible\n\n // Handle sound notification (always if enabled)\n if (channels.sound) {\n this.sound.play();\n }\n\n // Handle push notification (only if tab is not visible/focused)\n if (channels.push) {\n // Check if document is hidden or not focused\n const isTabHidden = document.hidden || !document.hasFocus();\n\n if (isTabHidden) {\n this.handlePushNotification(data);\n }\n }\n } catch (error) {\n // Silently handle errors to avoid polluting user's console\n }\n }\n\n private handlePushNotification(data: NotificationEvent): void {\n // Check if browser supports notifications\n if (!(\"Notification\" in window)) {\n return;\n }\n\n // Check notification permission\n if (Notification.permission === \"granted\") {\n // Create push notification\n try {\n const notification = new Notification(\"Task Complete\", {\n body: `${\n data.message || \"Your task has finished running\"\n }\\n\\nPowered by NotifyOn`,\n icon: \"/favicon.ico\",\n tag: data.id,\n requireInteraction: false,\n silent: false,\n });\n\n // Handle click - focus the tab/window\n notification.onclick = () => {\n window.focus();\n notification.close();\n };\n } catch (error) {\n // Silently handle notification creation errors\n }\n } else if (Notification.permission !== \"denied\") {\n // Request permission if not yet decided\n Notification.requestPermission().then((permission) => {\n if (permission === \"granted\") {\n const notification = new Notification(\"Task Complete\", {\n body: `${\n data.message || \"Your task has finished running\"\n }\\n\\nPowered by NotifyOn`,\n icon: \"/favicon.ico\",\n tag: data.id,\n requireInteraction: false,\n silent: false,\n });\n\n // Handle click - focus the tab/window\n notification.onclick = () => {\n window.focus();\n notification.close();\n };\n }\n });\n }\n }\n}\n\n// Simple function API with singleton pattern\nexport function connect(config: NotifyOnWebConfig): NotifyOnWeb {\n // Check if we already have a connection with the same config\n if (globalInstance) {\n const existingConfig = globalInstance.config;\n // If connecting with same publicKey and userId, return existing instance\n if (\n existingConfig.publicKey === config.publicKey &&\n existingConfig.userId === config.userId\n ) {\n return globalInstance;\n }\n // If different config, destroy old connection and create new one\n globalInstance.destroy();\n }\n \n // Create and store new instance\n globalInstance = new NotifyOnWeb(config);\n return globalInstance;\n}\n"]}
{"version":3,"sources":["../src/types.ts","../src/sound.ts","../src/client.ts"],"names":["NotifyOnWebError","message","NotificationSound","__async","now","error","frequency","startTime","volume","duration","addVibrato","oscillator","gainNode","delay","delayGain","filter","vibrato","vibratoGain","PUSHER_APP_KEY","PUSHER_APP_CLUSTER","globalInstance","NotifyOnWeb","config","Pusher","channelName","data","channels","notification","permission","connect","existingConfig"],"mappings":"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","file":"index.mjs","sourcesContent":["export interface NotifyOnWebConfig {\n publicKey: string; // NotifyOn project public key\n userId: string;\n}\n\nexport interface NotificationEvent {\n id: string;\n message: string;\n timestamp: string;\n channels?: {\n sound?: boolean;\n push?: boolean;\n };\n}\n\nexport class NotifyOnWebError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"NotifyOnWebError\";\n }\n}\n","export class NotificationSound {\n private audioContext: AudioContext | null = null;\n\n /**\n * Play a notification sound using Web Audio API\n */\n async play(): Promise<void> {\n try {\n // Create audio context if not exists\n if (!this.audioContext) {\n this.audioContext = new (window.AudioContext || (window as any).webkitAudioContext)();\n }\n\n const now = this.audioContext.currentTime;\n \n // Create a natural, pleasant chime with mid-range tones\n // First tone - F4 (349Hz) with slight vibrato for naturalness\n this.playTone(349, now, 0.1, 0.18, true);\n \n // Second tone - A4 (440Hz) - major third up, very harmonious\n this.playTone(440, now + 0.08, 0.08, 0.22, true);\n \n // Third soft tone - C5 (523Hz) - adds sparkle\n this.playTone(523, now + 0.15, 0.04, 0.15, false);\n \n } catch (error) {\n // Silently handle errors to avoid polluting user's console\n }\n }\n\n private playTone(frequency: number, startTime: number, volume: number, duration: number, addVibrato: boolean = false): void {\n if (!this.audioContext) return;\n \n // Create oscillator and nodes for this tone\n const oscillator = this.audioContext.createOscillator();\n const gainNode = this.audioContext.createGain();\n \n // Add reverb-like effect with delay for warmth\n const delay = this.audioContext.createDelay(0.5);\n delay.delayTime.value = 0.02;\n \n const delayGain = this.audioContext.createGain();\n delayGain.gain.value = 0.15; // Very subtle reverb\n \n // Add a low-pass filter with some resonance for character\n const filter = this.audioContext.createBiquadFilter();\n filter.type = 'lowpass';\n filter.frequency.value = 1800; // Balanced cutoff\n filter.Q.value = 1.2; // Slight resonance for character\n \n // Connect main signal path: oscillator -> filter -> gain -> destination\n oscillator.connect(filter);\n filter.connect(gainNode);\n gainNode.connect(this.audioContext.destination);\n \n // Connect delay path for subtle reverb: gain -> delay -> delayGain -> destination\n gainNode.connect(delay);\n delay.connect(delayGain);\n delayGain.connect(this.audioContext.destination);\n \n // Use sine wave for the most gentle sound\n oscillator.type = 'sine';\n oscillator.frequency.value = frequency;\n \n // Add subtle vibrato for more natural, less mechanical sound\n if (addVibrato) {\n const vibrato = this.audioContext.createOscillator();\n const vibratoGain = this.audioContext.createGain();\n vibrato.frequency.value = 5; // 5Hz vibrato rate\n vibratoGain.gain.value = 2; // Very subtle frequency modulation\n vibrato.connect(vibratoGain);\n vibratoGain.connect(oscillator.frequency);\n vibrato.start(startTime);\n vibrato.stop(startTime + duration);\n }\n \n // Natural envelope with varied attack for organic feel\n gainNode.gain.setValueAtTime(0.00001, startTime);\n gainNode.gain.exponentialRampToValueAtTime(volume, startTime + 0.01); // Quick but soft attack\n gainNode.gain.setValueAtTime(volume * 0.9, startTime + duration - 0.06); // Slight decay\n gainNode.gain.exponentialRampToValueAtTime(0.00001, startTime + duration); // Natural release\n \n // Play the tone\n oscillator.start(startTime);\n oscillator.stop(startTime + duration);\n }\n\n /**\n * Clean up audio context\n */\n destroy(): void {\n if (this.audioContext) {\n this.audioContext.close();\n this.audioContext = null;\n }\n }\n}","import type { NotifyOnWebConfig, NotificationEvent } from \"./types\";\nimport { NotifyOnWebError } from \"./types\";\nimport { NotificationSound } from \"./sound\";\nimport Pusher from \"pusher-js\";\n\nconst PUSHER_APP_KEY = \"70225167dde6bac400ea\";\nconst PUSHER_APP_CLUSTER = \"mt1\";\n\n// Singleton instance storage\nlet globalInstance: NotifyOnWeb | null = null;\n\nexport class NotifyOnWeb {\n public readonly config: NotifyOnWebConfig;\n private pusher: Pusher | null = null;\n private channel: any | null = null;\n private sound: NotificationSound;\n private connected = false;\n\n constructor(config: NotifyOnWebConfig) {\n if (!config.publicKey) {\n throw new NotifyOnWebError(\"Public key is required\");\n }\n if (!config.userId) {\n throw new NotifyOnWebError(\"User ID is required\");\n }\n\n this.config = config;\n\n this.sound = new NotificationSound();\n\n // Auto-connect on instantiation\n this.connect();\n }\n\n /**\n * Start listening for notifications (called automatically)\n */\n private connect(): void {\n if (this.connected) {\n return;\n }\n\n try {\n this.pusher = new Pusher(PUSHER_APP_KEY, {\n cluster: PUSHER_APP_CLUSTER,\n });\n\n // Channel naming convention matches server\n const channelName = `project-${this.config.publicKey}-user-${this.config.userId}`;\n this.channel = this.pusher.subscribe(channelName);\n\n // Subscribe to notification events\n this.channel.bind(\"notification\", (data: any) => {\n this.handleNotification(data);\n });\n\n // Monitor connection state\n this.pusher.connection.bind(\"connected\", () => {\n this.connected = true;\n });\n\n this.pusher.connection.bind(\"disconnected\", () => {\n this.connected = false;\n });\n\n this.pusher.connection.bind(\"failed\", () => {\n this.connected = false;\n });\n } catch (error) {\n throw new NotifyOnWebError(`Failed to connect: ${error}`);\n }\n }\n\n /**\n * Stop listening for notifications (called automatically on destroy)\n */\n private disconnect(): void {\n if (this.channel && this.pusher) {\n this.pusher.unsubscribe(this.channel.name);\n this.channel = null;\n }\n\n if (this.pusher) {\n this.pusher.disconnect();\n this.pusher = null;\n this.connected = false;\n }\n }\n\n /**\n * Clean up resources\n */\n destroy(): void {\n this.disconnect();\n this.sound.destroy();\n // Clear global instance if this is the one being destroyed\n if (globalInstance === this) {\n globalInstance = null;\n }\n }\n\n private handleNotification(data: NotificationEvent): void {\n try {\n // Check which channels are enabled for this notification\n const channels = data.channels || { sound: true, push: false }; // Default to sound enabled\n\n // Progressive notification strategy:\n // 1. Always play sound if enabled\n // 2. Show push only if enabled AND tab is not visible\n\n // Handle sound notification (always if enabled)\n if (channels.sound) {\n this.sound.play();\n }\n\n // Handle push notification (only if tab is not visible/focused)\n if (channels.push) {\n // Check if document is hidden or not focused\n const isTabHidden = document.hidden || !document.hasFocus();\n\n if (isTabHidden) {\n this.handlePushNotification(data);\n }\n }\n } catch (error) {\n // Silently handle errors to avoid polluting user's console\n }\n }\n\n private handlePushNotification(data: NotificationEvent): void {\n // Check if browser supports notifications\n if (!(\"Notification\" in window)) {\n return;\n }\n\n // Check notification permission\n if (Notification.permission === \"granted\") {\n // Create push notification\n try {\n const notification = new Notification(\"Task Complete\", {\n body: `${\n data.message || \"Your task has finished running\"\n }\\n\\nPowered by NotifyOn`,\n icon: \"/favicon.ico\",\n tag: data.id,\n requireInteraction: false,\n silent: false,\n });\n\n // Handle click - focus the tab/window\n notification.onclick = () => {\n window.focus();\n notification.close();\n };\n } catch (error) {\n // Silently handle notification creation errors\n }\n } else if (Notification.permission !== \"denied\") {\n // Request permission if not yet decided\n Notification.requestPermission().then((permission) => {\n if (permission === \"granted\") {\n const notification = new Notification(\"Task Complete\", {\n body: `${\n data.message || \"Your task has finished running\"\n }\\n\\nPowered by NotifyOn`,\n icon: \"/favicon.ico\",\n tag: data.id,\n requireInteraction: false,\n silent: false,\n });\n\n // Handle click - focus the tab/window\n notification.onclick = () => {\n window.focus();\n notification.close();\n };\n }\n });\n }\n }\n}\n\n// Simple function API with singleton pattern\nexport function connect(config: NotifyOnWebConfig): NotifyOnWeb {\n // Check if we already have a connection with the same config\n if (globalInstance) {\n const existingConfig = globalInstance.config;\n // If connecting with same publicKey and userId, return existing instance\n if (\n existingConfig.publicKey === config.publicKey &&\n existingConfig.userId === config.userId\n ) {\n return globalInstance;\n }\n // If different config, destroy old connection and create new one\n globalInstance.destroy();\n }\n\n // Create and store new instance\n globalInstance = new NotifyOnWeb(config);\n return globalInstance;\n}\n"]}
{
"name": "@notifyon/web",
"version": "0.1.3",
"version": "0.1.4",
"description": "NotifyOn Web SDK",

@@ -45,3 +45,3 @@ "publishConfig": {

"dependencies": {
"ably": "^2.12.0"
"pusher-js": "^8.4.0"
},

@@ -48,0 +48,0 @@ "scripts": {

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display