@stacksjs/push 0.70.87 → 0.70.88
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/package.json +4 -4
- package/dist/drivers/expo.d.ts +0 -51
- package/dist/drivers/fcm.d.ts +0 -77
- package/dist/drivers/index.d.ts +0 -2
- package/dist/index.d.ts +0 -25
- package/dist/index.js +0 -2
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/push",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.70.
|
|
5
|
+
"version": "0.70.88",
|
|
6
6
|
"description": "The Stacks Push integration",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"prepublishOnly": "bun run build"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
|
-
"@stacksjs/cli": "0.70.
|
|
55
|
-
"@stacksjs/config": "0.70.
|
|
54
|
+
"@stacksjs/cli": "0.70.88",
|
|
55
|
+
"@stacksjs/config": "0.70.88",
|
|
56
56
|
"better-dx": "^0.2.16",
|
|
57
|
-
"@stacksjs/error-handling": "0.70.
|
|
57
|
+
"@stacksjs/error-handling": "0.70.88"
|
|
58
58
|
}
|
|
59
59
|
}
|
package/dist/drivers/expo.d.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { PushResult } from '@stacksjs/types';
|
|
2
|
-
/**
|
|
3
|
-
* Validates an Expo push token
|
|
4
|
-
*/
|
|
5
|
-
export declare function isExpoPushToken(token: string): boolean;
|
|
6
|
-
/**
|
|
7
|
-
* Send a push notification via Expo Push Service
|
|
8
|
-
*/
|
|
9
|
-
export declare function send(message: ExpoPushMessage): Promise<PushResult>;
|
|
10
|
-
/**
|
|
11
|
-
* Get push notification receipts from Expo
|
|
12
|
-
*/
|
|
13
|
-
export declare function getReceipts(ticketIds: string[]): Promise<Record<string, ExpoPushReceipt>>;
|
|
14
|
-
/**
|
|
15
|
-
* Send multiple push notifications in chunks (Expo recommends max 100 per request)
|
|
16
|
-
*/
|
|
17
|
-
export declare function sendBatch(messages: ExpoPushMessage[], chunkSize?: number): Promise<PushResult[]>;
|
|
18
|
-
declare const __dtsx_default_export__: {
|
|
19
|
-
send: typeof send;
|
|
20
|
-
sendBatch: typeof sendBatch;
|
|
21
|
-
getReceipts: typeof getReceipts;
|
|
22
|
-
isExpoPushToken: typeof isExpoPushToken
|
|
23
|
-
};
|
|
24
|
-
export declare interface ExpoPushMessage {
|
|
25
|
-
to: string | string[]
|
|
26
|
-
title?: string
|
|
27
|
-
body: string
|
|
28
|
-
data?: Record<string, any>
|
|
29
|
-
sound?: 'default' | null
|
|
30
|
-
badge?: number
|
|
31
|
-
channelId?: string
|
|
32
|
-
priority?: 'default' | 'normal' | 'high'
|
|
33
|
-
ttl?: number
|
|
34
|
-
}
|
|
35
|
-
export declare interface ExpoPushTicket {
|
|
36
|
-
id?: string
|
|
37
|
-
status: 'ok' | 'error'
|
|
38
|
-
message?: string
|
|
39
|
-
details?: {
|
|
40
|
-
error?: 'DeviceNotRegistered' | 'MessageTooBig' | 'MessageRateExceeded' | 'InvalidCredentials'
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
export declare interface ExpoPushReceipt {
|
|
44
|
-
status: 'ok' | 'error'
|
|
45
|
-
message?: string
|
|
46
|
-
details?: {
|
|
47
|
-
error?: string
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
export { send as Send };
|
|
51
|
-
export default __dtsx_default_export__;
|
package/dist/drivers/fcm.d.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
import type { PushResult } from '@stacksjs/types';
|
|
2
|
-
/**
|
|
3
|
-
* Configure FCM with server key or service account
|
|
4
|
-
*/
|
|
5
|
-
export declare function configure(options: FCMConfig): void;
|
|
6
|
-
/**
|
|
7
|
-
* Send push notification via FCM Legacy API (using server key)
|
|
8
|
-
*/
|
|
9
|
-
export declare function sendLegacy(message: FCMMessage): Promise<PushResult>;
|
|
10
|
-
/**
|
|
11
|
-
* Send push notification via FCM v1 API (using service account)
|
|
12
|
-
*/
|
|
13
|
-
export declare function send(message: FCMMessage): Promise<PushResult>;
|
|
14
|
-
/**
|
|
15
|
-
* Send to multiple tokens
|
|
16
|
-
*/
|
|
17
|
-
export declare function sendMulticast(tokens: string[], message: Omit<FCMMessage, 'to' | 'registrationIds'>): Promise<PushResult[]>;
|
|
18
|
-
/**
|
|
19
|
-
* Send to a topic
|
|
20
|
-
*/
|
|
21
|
-
export declare function sendToTopic(topic: string, message: Omit<FCMMessage, 'to' | 'registrationIds' | 'topic'>): Promise<PushResult>;
|
|
22
|
-
/**
|
|
23
|
-
* Subscribe tokens to a topic
|
|
24
|
-
*/
|
|
25
|
-
export declare function subscribeToTopic(tokens: string[], topic: string): Promise<boolean>;
|
|
26
|
-
/**
|
|
27
|
-
* Unsubscribe tokens from a topic
|
|
28
|
-
*/
|
|
29
|
-
export declare function unsubscribeFromTopic(tokens: string[], topic: string): Promise<boolean>;
|
|
30
|
-
declare const __dtsx_default_export__: {
|
|
31
|
-
send: typeof send;
|
|
32
|
-
sendLegacy: typeof sendLegacy;
|
|
33
|
-
sendMulticast: typeof sendMulticast;
|
|
34
|
-
sendToTopic: typeof sendToTopic;
|
|
35
|
-
subscribeToTopic: typeof subscribeToTopic;
|
|
36
|
-
unsubscribeFromTopic: typeof unsubscribeFromTopic;
|
|
37
|
-
configure: typeof configure
|
|
38
|
-
};
|
|
39
|
-
export declare interface FCMMessage {
|
|
40
|
-
to?: string
|
|
41
|
-
registrationIds?: string[]
|
|
42
|
-
topic?: string
|
|
43
|
-
condition?: string
|
|
44
|
-
notification?: {
|
|
45
|
-
title: string
|
|
46
|
-
body: string
|
|
47
|
-
icon?: string
|
|
48
|
-
image?: string
|
|
49
|
-
sound?: string
|
|
50
|
-
badge?: string
|
|
51
|
-
clickAction?: string
|
|
52
|
-
tag?: string
|
|
53
|
-
}
|
|
54
|
-
data?: Record<string, string>
|
|
55
|
-
priority?: 'high' | 'normal'
|
|
56
|
-
ttl?: number
|
|
57
|
-
collapseKey?: string
|
|
58
|
-
}
|
|
59
|
-
export declare interface FCMConfig {
|
|
60
|
-
serverKey?: string
|
|
61
|
-
projectId?: string
|
|
62
|
-
serviceAccount?: {
|
|
63
|
-
clientEmail: string
|
|
64
|
-
privateKey: string
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
export declare interface FCMResponse {
|
|
68
|
-
multicastId?: number
|
|
69
|
-
success: number
|
|
70
|
-
failure: number
|
|
71
|
-
results?: Array<{
|
|
72
|
-
messageId?: string
|
|
73
|
-
error?: string
|
|
74
|
-
}>
|
|
75
|
-
}
|
|
76
|
-
export { send as Send };
|
|
77
|
-
export default __dtsx_default_export__;
|
package/dist/drivers/index.d.ts
DELETED
package/dist/index.d.ts
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import * as expo from './drivers/expo';
|
|
2
|
-
import * as fcm from './drivers/fcm';
|
|
3
|
-
import type { PushResult } from '@stacksjs/types';
|
|
4
|
-
/**
|
|
5
|
-
* Send a push notification using the specified driver
|
|
6
|
-
*/
|
|
7
|
-
export declare function send(to: string | string[], notification: PushNotification, options?: SendOptions): Promise<PushResult>;
|
|
8
|
-
/**
|
|
9
|
-
* Configure FCM with credentials
|
|
10
|
-
*/
|
|
11
|
-
export declare function configureFCM(config: fcm.FCMConfig): void;
|
|
12
|
-
export declare interface PushNotification {
|
|
13
|
-
title?: string
|
|
14
|
-
body: string
|
|
15
|
-
data?: Record<string, any>
|
|
16
|
-
badge?: number
|
|
17
|
-
sound?: 'default' | null
|
|
18
|
-
priority?: 'high' | 'normal' | 'default'
|
|
19
|
-
}
|
|
20
|
-
export declare interface SendOptions {
|
|
21
|
-
driver?: PushDriver
|
|
22
|
-
}
|
|
23
|
-
export type PushDriver = 'expo' | 'fcm';
|
|
24
|
-
export * from './drivers/index';
|
|
25
|
-
export { expo, fcm };
|
package/dist/index.js
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
// @bun
|
|
2
|
-
var y=Object.defineProperty;var h=(G)=>G;function T(G,J){this[G]=h.bind(null,J)}var z=(G,J)=>{for(var N in J)y(G,N,{get:J[N],enumerable:!0,configurable:!0,set:T.bind(J,N)})};var E={};z(E,{sendBatch:()=>C,send:()=>j,isExpoPushToken:()=>w,getReceipts:()=>B,default:()=>u,Send:()=>j});import{log as X}from"@stacksjs/cli";var d="https://exp.host/--/api/v2/push/send";function w(G){return/^Expo(?:nent)?PushToken\[.+\]$/.test(G)||/^[a-zA-Z0-9-_]+$/.test(G)}async function j(G){let J=Array.isArray(G.to)?G.to:[G.to],N=J.filter((W)=>!w(W));if(N.length>0)X.warn(`Invalid Expo push tokens found: ${N.join(", ")}`);let Q=J.filter(w);if(Q.length===0)return{success:!1,provider:"expo",message:"No valid Expo push tokens provided"};try{let W=Q.map(($)=>({to:$,title:G.title,body:G.body,data:G.data,sound:G.sound??"default",badge:G.badge,channelId:G.channelId,priority:G.priority??"high",ttl:G.ttl})),Z=4096;for(let $ of W){let K=new TextEncoder().encode(JSON.stringify($)).length;if(K>4096)return{success:!1,provider:"expo",message:`Expo message payload (${K} bytes) exceeds 4 KiB per-message limit`}}let q=await fetch(d,{method:"POST",headers:{Accept:"application/json","Accept-Encoding":"gzip, deflate","Content-Type":"application/json"},body:JSON.stringify(W)});if(!q.ok){let $=await q.text();return X.error(`Expo push failed: ${$}`),{success:!1,provider:"expo",message:`HTTP ${q.status}: ${$}`}}let A=(await q.json()).data,V=A.filter(($)=>$.status==="error");if(V.length>0)X.warn(`Some push notifications failed: ${JSON.stringify(V)}`);let I=A.filter(($)=>$.status==="ok").length;return X.info(`Expo push sent: ${I}/${A.length} successful`),{success:V.length===0,provider:"expo",message:`Sent ${I}/${A.length} notifications`,messageId:A.find(($)=>$.id)?.id,data:{tickets:A}}}catch(W){let Z=W instanceof Error?W:Error(String(W));return X.error(`Expo push error: ${Z.message}`),{success:!1,provider:"expo",message:Z.message}}}async function B(G){let J=await fetch("https://exp.host/--/api/v2/push/getReceipts",{method:"POST",headers:{Accept:"application/json","Content-Type":"application/json"},body:JSON.stringify({ids:G})});if(!J.ok)throw Error(`Failed to get receipts: ${J.status}`);return(await J.json()).data}async function C(G,J=100){let N=[];for(let Q=0;Q<G.length;Q+=J){let W=G.slice(Q,Q+J),Z=await Promise.all(W.map((q)=>j(q)));N.push(...Z)}return N}var u={send:j,sendBatch:C,getReceipts:B,isExpoPushToken:w};var M={};z(M,{unsubscribeFromTopic:()=>b,subscribeToTopic:()=>S,sendToTopic:()=>L,sendMulticast:()=>U,sendLegacy:()=>P,send:()=>O,default:()=>f,configure:()=>F,Send:()=>O});import{log as H}from"@stacksjs/cli";var l="https://fcm.googleapis.com/fcm/send",p="https://fcm.googleapis.com/v1/projects",Y={};function F(G){Y={...Y,...G}}async function k(){if(!Y.serviceAccount)throw Error("Service account not configured for FCM v1 API");let{clientEmail:G,privateKey:J}=Y.serviceAccount,N=Math.floor(Date.now()/1000),Q={alg:"RS256",typ:"JWT"},W={iss:G,scope:"https://www.googleapis.com/auth/firebase.messaging",aud:"https://oauth2.googleapis.com/token",iat:N,exp:N+3600},Z=new TextEncoder,q=btoa(JSON.stringify(Q)),D=btoa(JSON.stringify(W)),A=`${q}.${D}`,V=J.replace(/-----BEGIN PRIVATE KEY-----/,"").replace(/-----END PRIVATE KEY-----/,"").replace(/\n/g,""),I=Uint8Array.from(atob(V),(x)=>x.charCodeAt(0)),$=await crypto.subtle.importKey("pkcs8",I,{name:"RSASSA-PKCS1-v1_5",hash:"SHA-256"},!1,["sign"]),K=await crypto.subtle.sign("RSASSA-PKCS1-v1_5",$,Z.encode(A)),R=btoa(String.fromCharCode(...new Uint8Array(K))).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,""),v=`${q}.${D}.${R}`,_=await fetch("https://oauth2.googleapis.com/token",{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:`grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer&assertion=${v}`});if(!_.ok)throw Error(`Failed to get access token: ${_.status}`);return(await _.json()).access_token}async function P(G){if(!Y.serverKey)return{success:!1,provider:"fcm",message:"FCM server key not configured"};try{let J=await fetch(l,{method:"POST",headers:{Authorization:`key=${Y.serverKey}`,"Content-Type":"application/json"},body:JSON.stringify({to:G.to,registration_ids:G.registrationIds,topic:G.topic?`/topics/${G.topic}`:void 0,condition:G.condition,notification:G.notification,data:G.data,priority:G.priority??"high",time_to_live:G.ttl,collapse_key:G.collapseKey})});if(!J.ok){let Q=await J.text();return H.error(`FCM push failed: ${Q}`),{success:!1,provider:"fcm",message:`HTTP ${J.status}: ${Q}`}}let N=await J.json();if(N.failure>0)H.warn(`FCM: ${N.failure} notifications failed`);return H.info(`FCM push sent: ${N.success} successful, ${N.failure} failed`),{success:N.failure===0,provider:"fcm",message:`Sent ${N.success} notifications`,messageId:N.results?.[0]?.messageId,data:N}}catch(J){let N=J instanceof Error?J:Error(String(J));return H.error(`FCM push error: ${N.message}`),{success:!1,provider:"fcm",message:N.message}}}async function O(G){if(!Y.serviceAccount||!Y.projectId)return P(G);try{let J=await k(),N=`${p}/${Y.projectId}/messages:send`,Q={message:{notification:G.notification,data:G.data,android:{priority:G.priority??"high",ttl:G.ttl?`${G.ttl}s`:void 0,collapseKey:G.collapseKey},apns:{headers:{"apns-priority":G.priority==="high"?"10":"5"}}}};if(G.to)Q.message.token=G.to;else if(G.topic)Q.message.topic=G.topic;else if(G.condition)Q.message.condition=G.condition;let W=await fetch(N,{method:"POST",headers:{Authorization:`Bearer ${J}`,"Content-Type":"application/json"},body:JSON.stringify(Q)});if(!W.ok){let q=await W.text();return H.error(`FCM v1 push failed: ${q}`),{success:!1,provider:"fcm",message:`HTTP ${W.status}: ${q}`}}let Z=await W.json();return H.info(`FCM v1 push sent: ${Z.name}`),{success:!0,provider:"fcm",message:"Notification sent successfully",messageId:Z.name}}catch(J){let N=J instanceof Error?J:Error(String(J));return H.error(`FCM v1 push error: ${N.message}`),{success:!1,provider:"fcm",message:N.message}}}async function U(G,J){if(Y.serviceAccount&&Y.projectId)return Promise.all(G.map((N)=>O({...J,to:N})));return[await P({...J,registrationIds:G})]}async function L(G,J){return O({...J,topic:G})}async function S(G,J){if(!Y.serverKey)throw Error("FCM server key required for topic subscription");return(await fetch("https://iid.googleapis.com/iid/v1:batchAdd",{method:"POST",headers:{Authorization:`key=${Y.serverKey}`,"Content-Type":"application/json"},body:JSON.stringify({to:`/topics/${J}`,registration_tokens:G})})).ok}async function b(G,J){if(!Y.serverKey)throw Error("FCM server key required for topic unsubscription");return(await fetch("https://iid.googleapis.com/iid/v1:batchRemove",{method:"POST",headers:{Authorization:`key=${Y.serverKey}`,"Content-Type":"application/json"},body:JSON.stringify({to:`/topics/${J}`,registration_tokens:G})})).ok}var f={send:O,sendLegacy:P,sendMulticast:U,sendToTopic:L,subscribeToTopic:S,unsubscribeFromTopic:b,configure:F};async function a(G,J,N={}){let Q=N.driver??"expo";if(Q==="expo")return j({to:G,title:J.title,body:J.body,data:J.data,badge:J.badge,sound:J.sound,priority:J.priority});if(Q==="fcm"){let W=Array.isArray(G)?G:[G];if(W.length===1)return O({to:W[0],notification:{title:J.title??"",body:J.body},data:J.data,priority:J.priority==="default"?"normal":J.priority});let Z=await U(W,{notification:{title:J.title??"",body:J.body},data:J.data,priority:J.priority==="default"?"normal":J.priority});return{success:Z.every((D)=>D.success),provider:"fcm",message:`Sent to ${Z.filter((D)=>D.success).length}/${Z.length} devices`}}return{success:!1,provider:Q,message:`Unknown push driver: ${Q}`}}function t(G){F(G)}export{a as send,M as fcm,E as expo,t as configureFCM};
|