krisspy-sdk 0.7.0 → 0.8.1

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/dist/index.d.mts CHANGED
@@ -827,6 +827,114 @@ declare class KrisspyAnalytics {
827
827
  private setupNavigationTracking;
828
828
  }
829
829
 
830
+ /**
831
+ * Notifications Module - Expo Push Notifications for Krisspy backends
832
+ * Compatible with Expo Go, EAS builds, and bare React Native.
833
+ */
834
+
835
+ interface PushToken {
836
+ id: string;
837
+ token: string;
838
+ platform?: string;
839
+ device_id?: string;
840
+ created_at: string;
841
+ updated_at: string;
842
+ }
843
+ interface NotificationTicket {
844
+ id?: string;
845
+ status: 'ok' | 'error';
846
+ message?: string;
847
+ details?: {
848
+ error?: string;
849
+ };
850
+ }
851
+ interface SendNotificationParams {
852
+ /** Target user ID (required for send, omit for broadcast) */
853
+ userId?: string;
854
+ /** Notification title */
855
+ title: string;
856
+ /** Notification body text */
857
+ body: string;
858
+ /** Custom data payload (available in notification handler) */
859
+ data?: Record<string, any>;
860
+ /** Sound: 'default' or null to disable */
861
+ sound?: 'default' | null;
862
+ /** iOS badge count */
863
+ badge?: number;
864
+ /** Priority: 'default', 'normal', or 'high' */
865
+ priority?: 'default' | 'normal' | 'high';
866
+ }
867
+ declare class KrisspyNotifications {
868
+ private http;
869
+ private backendId;
870
+ constructor(http: HttpClient, backendId: string);
871
+ /**
872
+ * Register a push token for the current user.
873
+ * Call this after getting the ExpoPushToken from expo-notifications.
874
+ *
875
+ * @example
876
+ * import * as Notifications from 'expo-notifications'
877
+ * const { data: token } = await Notifications.getExpoPushTokenAsync()
878
+ * await krisspy.notifications.register(token, 'ios')
879
+ */
880
+ register(token: string, platform?: 'ios' | 'android' | 'web'): Promise<{
881
+ data: PushToken | null;
882
+ error: KrisspyError | null;
883
+ }>;
884
+ /**
885
+ * Unregister a push token (e.g., on sign out or when user disables notifications).
886
+ */
887
+ unregister(token: string): Promise<{
888
+ error: KrisspyError | null;
889
+ }>;
890
+ /**
891
+ * List all push tokens registered for the current user.
892
+ */
893
+ getTokens(): Promise<{
894
+ data: PushToken[] | null;
895
+ error: KrisspyError | null;
896
+ }>;
897
+ /**
898
+ * Send a push notification to a specific user.
899
+ * Requires service key (server-side only).
900
+ *
901
+ * @example
902
+ * // In an Azure Function / serverless function:
903
+ * const krisspy = createClient({ backendId: '...', apiKey: '...', serviceKey: process.env.SERVICE_KEY })
904
+ * await krisspy.notifications.send({
905
+ * userId: 'user123',
906
+ * title: 'New message',
907
+ * body: 'You have a new message from Alice',
908
+ * data: { screen: 'chat', chatId: '456' },
909
+ * })
910
+ */
911
+ send(params: SendNotificationParams & {
912
+ userId: string;
913
+ }): Promise<{
914
+ data: {
915
+ tickets: NotificationTicket[];
916
+ } | null;
917
+ error: KrisspyError | null;
918
+ }>;
919
+ /**
920
+ * Broadcast a push notification to all registered users.
921
+ * Requires service key (server-side only).
922
+ *
923
+ * @example
924
+ * await krisspy.notifications.broadcast({
925
+ * title: 'App Update',
926
+ * body: 'Version 2.0 is now available!',
927
+ * })
928
+ */
929
+ broadcast(params: SendNotificationParams): Promise<{
930
+ data: {
931
+ tickets: NotificationTicket[];
932
+ count: number;
933
+ } | null;
934
+ error: KrisspyError | null;
935
+ }>;
936
+ }
937
+
830
938
  /**
831
939
  * Query Builder - Supabase-style fluent API for database queries
832
940
  */
@@ -981,6 +1089,7 @@ declare class KrisspyClient {
981
1089
  private _storage;
982
1090
  private _realtime;
983
1091
  private _analytics;
1092
+ private _notifications;
984
1093
  private dataMode;
985
1094
  private debug;
986
1095
  constructor(options: KrisspyClientOptions);
@@ -1057,6 +1166,27 @@ declare class KrisspyClient {
1057
1166
  * krisspy.analytics.destroy()
1058
1167
  */
1059
1168
  get analytics(): KrisspyAnalytics;
1169
+ /**
1170
+ * Notifications module for Expo push notifications
1171
+ *
1172
+ * @example
1173
+ * // Register push token (client-side, after getting token from expo-notifications)
1174
+ * await krisspy.notifications.register(expoPushToken, 'ios')
1175
+ *
1176
+ * // Unregister on sign out
1177
+ * await krisspy.notifications.unregister(expoPushToken)
1178
+ *
1179
+ * // Send to user (server-side, requires serviceKey)
1180
+ * await krisspy.notifications.send({
1181
+ * userId: 'user123',
1182
+ * title: 'Hello',
1183
+ * body: 'You have a new message',
1184
+ * })
1185
+ *
1186
+ * // Broadcast to all (server-side, requires serviceKey)
1187
+ * await krisspy.notifications.broadcast({ title: 'Update', body: 'v2.0 released!' })
1188
+ */
1189
+ get notifications(): KrisspyNotifications;
1060
1190
  /**
1061
1191
  * Create a realtime channel for subscribing to database changes
1062
1192
  *
@@ -1249,4 +1379,4 @@ declare class KrisspyClient {
1249
1379
  */
1250
1380
  declare function createClient(options: KrisspyClientOptions): KrisspyClient;
1251
1381
 
1252
- export { type AnalyticsEvent, type AnalyticsInitOptions, type AuthChangeEvent, type AuthResponse, type ChannelState, type FileObject, type FileUploadOptions, type Filter, type FilterOperator, type FunctionInvokeOptions, type FunctionResponse, HttpClient, KrisspyAnalytics, KrisspyAuth, KrisspyClient, type KrisspyClientOptions, type KrisspyError, KrisspyRealtime, KrisspyStorage, type MutationResponse, type OAuthProvider, type OrderBy, type PostgresChangesConfig, QueryBuilder, type QueryOptions, type QueryResponse, RealtimeChannel, type RealtimeEvent, type RealtimePayload, type Session, type SignInCredentials, type SignUpCredentials, type SingleResponse, type StorageAdapter, StorageBucket, type UploadAndLinkOptions, type UploadAndLinkResponse, type User, createClient, isBrowser, isReactNative };
1382
+ export { type AnalyticsEvent, type AnalyticsInitOptions, type AuthChangeEvent, type AuthResponse, type ChannelState, type FileObject, type FileUploadOptions, type Filter, type FilterOperator, type FunctionInvokeOptions, type FunctionResponse, HttpClient, KrisspyAnalytics, KrisspyAuth, KrisspyClient, type KrisspyClientOptions, type KrisspyError, KrisspyNotifications, KrisspyRealtime, KrisspyStorage, type MutationResponse, type NotificationTicket, type OAuthProvider, type OrderBy, type PostgresChangesConfig, type PushToken, QueryBuilder, type QueryOptions, type QueryResponse, RealtimeChannel, type RealtimeEvent, type RealtimePayload, type SendNotificationParams, type Session, type SignInCredentials, type SignUpCredentials, type SingleResponse, type StorageAdapter, StorageBucket, type UploadAndLinkOptions, type UploadAndLinkResponse, type User, createClient, isBrowser, isReactNative };
package/dist/index.d.ts CHANGED
@@ -827,6 +827,114 @@ declare class KrisspyAnalytics {
827
827
  private setupNavigationTracking;
828
828
  }
829
829
 
830
+ /**
831
+ * Notifications Module - Expo Push Notifications for Krisspy backends
832
+ * Compatible with Expo Go, EAS builds, and bare React Native.
833
+ */
834
+
835
+ interface PushToken {
836
+ id: string;
837
+ token: string;
838
+ platform?: string;
839
+ device_id?: string;
840
+ created_at: string;
841
+ updated_at: string;
842
+ }
843
+ interface NotificationTicket {
844
+ id?: string;
845
+ status: 'ok' | 'error';
846
+ message?: string;
847
+ details?: {
848
+ error?: string;
849
+ };
850
+ }
851
+ interface SendNotificationParams {
852
+ /** Target user ID (required for send, omit for broadcast) */
853
+ userId?: string;
854
+ /** Notification title */
855
+ title: string;
856
+ /** Notification body text */
857
+ body: string;
858
+ /** Custom data payload (available in notification handler) */
859
+ data?: Record<string, any>;
860
+ /** Sound: 'default' or null to disable */
861
+ sound?: 'default' | null;
862
+ /** iOS badge count */
863
+ badge?: number;
864
+ /** Priority: 'default', 'normal', or 'high' */
865
+ priority?: 'default' | 'normal' | 'high';
866
+ }
867
+ declare class KrisspyNotifications {
868
+ private http;
869
+ private backendId;
870
+ constructor(http: HttpClient, backendId: string);
871
+ /**
872
+ * Register a push token for the current user.
873
+ * Call this after getting the ExpoPushToken from expo-notifications.
874
+ *
875
+ * @example
876
+ * import * as Notifications from 'expo-notifications'
877
+ * const { data: token } = await Notifications.getExpoPushTokenAsync()
878
+ * await krisspy.notifications.register(token, 'ios')
879
+ */
880
+ register(token: string, platform?: 'ios' | 'android' | 'web'): Promise<{
881
+ data: PushToken | null;
882
+ error: KrisspyError | null;
883
+ }>;
884
+ /**
885
+ * Unregister a push token (e.g., on sign out or when user disables notifications).
886
+ */
887
+ unregister(token: string): Promise<{
888
+ error: KrisspyError | null;
889
+ }>;
890
+ /**
891
+ * List all push tokens registered for the current user.
892
+ */
893
+ getTokens(): Promise<{
894
+ data: PushToken[] | null;
895
+ error: KrisspyError | null;
896
+ }>;
897
+ /**
898
+ * Send a push notification to a specific user.
899
+ * Requires service key (server-side only).
900
+ *
901
+ * @example
902
+ * // In an Azure Function / serverless function:
903
+ * const krisspy = createClient({ backendId: '...', apiKey: '...', serviceKey: process.env.SERVICE_KEY })
904
+ * await krisspy.notifications.send({
905
+ * userId: 'user123',
906
+ * title: 'New message',
907
+ * body: 'You have a new message from Alice',
908
+ * data: { screen: 'chat', chatId: '456' },
909
+ * })
910
+ */
911
+ send(params: SendNotificationParams & {
912
+ userId: string;
913
+ }): Promise<{
914
+ data: {
915
+ tickets: NotificationTicket[];
916
+ } | null;
917
+ error: KrisspyError | null;
918
+ }>;
919
+ /**
920
+ * Broadcast a push notification to all registered users.
921
+ * Requires service key (server-side only).
922
+ *
923
+ * @example
924
+ * await krisspy.notifications.broadcast({
925
+ * title: 'App Update',
926
+ * body: 'Version 2.0 is now available!',
927
+ * })
928
+ */
929
+ broadcast(params: SendNotificationParams): Promise<{
930
+ data: {
931
+ tickets: NotificationTicket[];
932
+ count: number;
933
+ } | null;
934
+ error: KrisspyError | null;
935
+ }>;
936
+ }
937
+
830
938
  /**
831
939
  * Query Builder - Supabase-style fluent API for database queries
832
940
  */
@@ -981,6 +1089,7 @@ declare class KrisspyClient {
981
1089
  private _storage;
982
1090
  private _realtime;
983
1091
  private _analytics;
1092
+ private _notifications;
984
1093
  private dataMode;
985
1094
  private debug;
986
1095
  constructor(options: KrisspyClientOptions);
@@ -1057,6 +1166,27 @@ declare class KrisspyClient {
1057
1166
  * krisspy.analytics.destroy()
1058
1167
  */
1059
1168
  get analytics(): KrisspyAnalytics;
1169
+ /**
1170
+ * Notifications module for Expo push notifications
1171
+ *
1172
+ * @example
1173
+ * // Register push token (client-side, after getting token from expo-notifications)
1174
+ * await krisspy.notifications.register(expoPushToken, 'ios')
1175
+ *
1176
+ * // Unregister on sign out
1177
+ * await krisspy.notifications.unregister(expoPushToken)
1178
+ *
1179
+ * // Send to user (server-side, requires serviceKey)
1180
+ * await krisspy.notifications.send({
1181
+ * userId: 'user123',
1182
+ * title: 'Hello',
1183
+ * body: 'You have a new message',
1184
+ * })
1185
+ *
1186
+ * // Broadcast to all (server-side, requires serviceKey)
1187
+ * await krisspy.notifications.broadcast({ title: 'Update', body: 'v2.0 released!' })
1188
+ */
1189
+ get notifications(): KrisspyNotifications;
1060
1190
  /**
1061
1191
  * Create a realtime channel for subscribing to database changes
1062
1192
  *
@@ -1249,4 +1379,4 @@ declare class KrisspyClient {
1249
1379
  */
1250
1380
  declare function createClient(options: KrisspyClientOptions): KrisspyClient;
1251
1381
 
1252
- export { type AnalyticsEvent, type AnalyticsInitOptions, type AuthChangeEvent, type AuthResponse, type ChannelState, type FileObject, type FileUploadOptions, type Filter, type FilterOperator, type FunctionInvokeOptions, type FunctionResponse, HttpClient, KrisspyAnalytics, KrisspyAuth, KrisspyClient, type KrisspyClientOptions, type KrisspyError, KrisspyRealtime, KrisspyStorage, type MutationResponse, type OAuthProvider, type OrderBy, type PostgresChangesConfig, QueryBuilder, type QueryOptions, type QueryResponse, RealtimeChannel, type RealtimeEvent, type RealtimePayload, type Session, type SignInCredentials, type SignUpCredentials, type SingleResponse, type StorageAdapter, StorageBucket, type UploadAndLinkOptions, type UploadAndLinkResponse, type User, createClient, isBrowser, isReactNative };
1382
+ export { type AnalyticsEvent, type AnalyticsInitOptions, type AuthChangeEvent, type AuthResponse, type ChannelState, type FileObject, type FileUploadOptions, type Filter, type FilterOperator, type FunctionInvokeOptions, type FunctionResponse, HttpClient, KrisspyAnalytics, KrisspyAuth, KrisspyClient, type KrisspyClientOptions, type KrisspyError, KrisspyNotifications, KrisspyRealtime, KrisspyStorage, type MutationResponse, type NotificationTicket, type OAuthProvider, type OrderBy, type PostgresChangesConfig, type PushToken, QueryBuilder, type QueryOptions, type QueryResponse, RealtimeChannel, type RealtimeEvent, type RealtimePayload, type SendNotificationParams, type Session, type SignInCredentials, type SignUpCredentials, type SingleResponse, type StorageAdapter, StorageBucket, type UploadAndLinkOptions, type UploadAndLinkResponse, type User, createClient, isBrowser, isReactNative };
package/dist/index.js CHANGED
@@ -41,6 +41,7 @@ __export(index_exports, {
41
41
  KrisspyAnalytics: () => KrisspyAnalytics,
42
42
  KrisspyAuth: () => KrisspyAuth,
43
43
  KrisspyClient: () => KrisspyClient,
44
+ KrisspyNotifications: () => KrisspyNotifications,
44
45
  KrisspyRealtime: () => KrisspyRealtime,
45
46
  KrisspyStorage: () => KrisspyStorage,
46
47
  QueryBuilder: () => QueryBuilder,
@@ -1471,6 +1472,96 @@ var KrisspyAnalytics = class {
1471
1472
  }
1472
1473
  };
1473
1474
 
1475
+ // src/notifications.ts
1476
+ var KrisspyNotifications = class {
1477
+ constructor(http, backendId) {
1478
+ this.http = http;
1479
+ this.backendId = backendId;
1480
+ }
1481
+ // ── Client-side methods (app user JWT) ──
1482
+ /**
1483
+ * Register a push token for the current user.
1484
+ * Call this after getting the ExpoPushToken from expo-notifications.
1485
+ *
1486
+ * @example
1487
+ * import * as Notifications from 'expo-notifications'
1488
+ * const { data: token } = await Notifications.getExpoPushTokenAsync()
1489
+ * await krisspy.notifications.register(token, 'ios')
1490
+ */
1491
+ async register(token, platform) {
1492
+ var _a, _b;
1493
+ const path = `/api/v1/cloud-backends/${this.backendId}/notifications/token`;
1494
+ const response = await this.http.post(path, { token, platform });
1495
+ if (response.error) {
1496
+ return { data: null, error: response.error };
1497
+ }
1498
+ return { data: (_b = (_a = response.data) == null ? void 0 : _a.data) != null ? _b : null, error: null };
1499
+ }
1500
+ /**
1501
+ * Unregister a push token (e.g., on sign out or when user disables notifications).
1502
+ */
1503
+ async unregister(token) {
1504
+ const path = `/api/v1/cloud-backends/${this.backendId}/notifications/unregister`;
1505
+ const response = await this.http.post(path, { token });
1506
+ return { error: response.error };
1507
+ }
1508
+ /**
1509
+ * List all push tokens registered for the current user.
1510
+ */
1511
+ async getTokens() {
1512
+ var _a, _b;
1513
+ const path = `/api/v1/cloud-backends/${this.backendId}/notifications/tokens`;
1514
+ const response = await this.http.get(path);
1515
+ if (response.error) {
1516
+ return { data: null, error: response.error };
1517
+ }
1518
+ return { data: (_b = (_a = response.data) == null ? void 0 : _a.data) != null ? _b : [], error: null };
1519
+ }
1520
+ // ── Server-side methods (service key required) ──
1521
+ /**
1522
+ * Send a push notification to a specific user.
1523
+ * Requires service key (server-side only).
1524
+ *
1525
+ * @example
1526
+ * // In an Azure Function / serverless function:
1527
+ * const krisspy = createClient({ backendId: '...', apiKey: '...', serviceKey: process.env.SERVICE_KEY })
1528
+ * await krisspy.notifications.send({
1529
+ * userId: 'user123',
1530
+ * title: 'New message',
1531
+ * body: 'You have a new message from Alice',
1532
+ * data: { screen: 'chat', chatId: '456' },
1533
+ * })
1534
+ */
1535
+ async send(params) {
1536
+ var _a, _b;
1537
+ const path = `/api/v1/cloud-backends/${this.backendId}/notifications/send`;
1538
+ const response = await this.http.post(path, params);
1539
+ if (response.error) {
1540
+ return { data: null, error: response.error };
1541
+ }
1542
+ return { data: (_b = (_a = response.data) == null ? void 0 : _a.data) != null ? _b : { tickets: [] }, error: null };
1543
+ }
1544
+ /**
1545
+ * Broadcast a push notification to all registered users.
1546
+ * Requires service key (server-side only).
1547
+ *
1548
+ * @example
1549
+ * await krisspy.notifications.broadcast({
1550
+ * title: 'App Update',
1551
+ * body: 'Version 2.0 is now available!',
1552
+ * })
1553
+ */
1554
+ async broadcast(params) {
1555
+ var _a, _b;
1556
+ const path = `/api/v1/cloud-backends/${this.backendId}/notifications/send-broadcast`;
1557
+ const response = await this.http.post(path, params);
1558
+ if (response.error) {
1559
+ return { data: null, error: response.error };
1560
+ }
1561
+ return { data: (_b = (_a = response.data) == null ? void 0 : _a.data) != null ? _b : { tickets: [], count: 0 }, error: null };
1562
+ }
1563
+ };
1564
+
1474
1565
  // src/query-builder.ts
1475
1566
  var QueryBuilder = class {
1476
1567
  constructor(http, backendId, tableName, dataMode = "rls") {
@@ -1786,6 +1877,7 @@ var KrisspyClient = class {
1786
1877
  this._storage = new KrisspyStorage(this.http, this.backendId);
1787
1878
  this._realtime = new KrisspyRealtime(this.baseUrl, this.backendId, this.debug);
1788
1879
  this._analytics = new KrisspyAnalytics(this.http, this.backendId);
1880
+ this._notifications = new KrisspyNotifications(this.http, this.backendId);
1789
1881
  this._auth.onAuthStateChange((event) => {
1790
1882
  if (event === "SIGNED_IN") {
1791
1883
  const session = this._auth.session();
@@ -1878,6 +1970,29 @@ var KrisspyClient = class {
1878
1970
  get analytics() {
1879
1971
  return this._analytics;
1880
1972
  }
1973
+ /**
1974
+ * Notifications module for Expo push notifications
1975
+ *
1976
+ * @example
1977
+ * // Register push token (client-side, after getting token from expo-notifications)
1978
+ * await krisspy.notifications.register(expoPushToken, 'ios')
1979
+ *
1980
+ * // Unregister on sign out
1981
+ * await krisspy.notifications.unregister(expoPushToken)
1982
+ *
1983
+ * // Send to user (server-side, requires serviceKey)
1984
+ * await krisspy.notifications.send({
1985
+ * userId: 'user123',
1986
+ * title: 'Hello',
1987
+ * body: 'You have a new message',
1988
+ * })
1989
+ *
1990
+ * // Broadcast to all (server-side, requires serviceKey)
1991
+ * await krisspy.notifications.broadcast({ title: 'Update', body: 'v2.0 released!' })
1992
+ */
1993
+ get notifications() {
1994
+ return this._notifications;
1995
+ }
1881
1996
  /**
1882
1997
  * Create a realtime channel for subscribing to database changes
1883
1998
  *
@@ -1989,37 +2104,32 @@ var KrisspyClient = class {
1989
2104
  get functions() {
1990
2105
  return {
1991
2106
  invoke: async (functionName, options) => {
1992
- if (this.functionsUrl) {
1993
- const url = `${this.functionsUrl}/api/${functionName}`;
1994
- try {
1995
- const res = await fetch(url, {
1996
- method: "POST",
1997
- headers: __spreadValues({
1998
- "Content-Type": "application/json"
1999
- }, options == null ? void 0 : options.headers),
2000
- body: (options == null ? void 0 : options.body) ? JSON.stringify(options.body) : void 0
2001
- });
2002
- const contentType = res.headers.get("content-type");
2003
- let data = null;
2004
- if (contentType == null ? void 0 : contentType.includes("application/json")) {
2005
- data = await res.json();
2006
- } else {
2007
- data = await res.text();
2008
- }
2009
- if (!res.ok) {
2010
- return { data: null, error: { message: (data == null ? void 0 : data.error) || (data == null ? void 0 : data.message) || `Request failed with status ${res.status}`, status: res.status } };
2011
- }
2012
- return { data, error: null };
2013
- } catch (err) {
2014
- return { data: null, error: { message: err.message || "Network error", code: "NETWORK_ERROR", status: 0 } };
2015
- }
2107
+ if (!this.functionsUrl) {
2108
+ return { data: null, error: { message: 'functionsUrl is required. Pass it in createClient({ functionsUrl: "https://your-func-app.azurewebsites.net" })', code: "CONFIG_ERROR", status: 0 } };
2016
2109
  }
2017
- const path = `/api/v1/cloud-backends/${this.backendId}/functions/${functionName}/invoke`;
2018
- const response = await this.http.post(path, options == null ? void 0 : options.body, void 0);
2019
- if (response.error) {
2020
- return { data: null, error: response.error };
2110
+ const url = `${this.functionsUrl}/api/${functionName}`;
2111
+ try {
2112
+ const res = await fetch(url, {
2113
+ method: "POST",
2114
+ headers: __spreadValues({
2115
+ "Content-Type": "application/json"
2116
+ }, options == null ? void 0 : options.headers),
2117
+ body: (options == null ? void 0 : options.body) ? JSON.stringify(options.body) : void 0
2118
+ });
2119
+ const contentType = res.headers.get("content-type");
2120
+ let data = null;
2121
+ if (contentType == null ? void 0 : contentType.includes("application/json")) {
2122
+ data = await res.json();
2123
+ } else {
2124
+ data = await res.text();
2125
+ }
2126
+ if (!res.ok) {
2127
+ return { data: null, error: { message: (data == null ? void 0 : data.error) || (data == null ? void 0 : data.message) || `Request failed with status ${res.status}`, status: res.status } };
2128
+ }
2129
+ return { data, error: null };
2130
+ } catch (err) {
2131
+ return { data: null, error: { message: err.message || "Network error", code: "NETWORK_ERROR", status: 0 } };
2021
2132
  }
2022
- return { data: response.data, error: null };
2023
2133
  },
2024
2134
  list: async () => {
2025
2135
  var _a, _b;
package/dist/index.mjs CHANGED
@@ -1437,6 +1437,96 @@ var KrisspyAnalytics = class {
1437
1437
  }
1438
1438
  };
1439
1439
 
1440
+ // src/notifications.ts
1441
+ var KrisspyNotifications = class {
1442
+ constructor(http, backendId) {
1443
+ this.http = http;
1444
+ this.backendId = backendId;
1445
+ }
1446
+ // ── Client-side methods (app user JWT) ──
1447
+ /**
1448
+ * Register a push token for the current user.
1449
+ * Call this after getting the ExpoPushToken from expo-notifications.
1450
+ *
1451
+ * @example
1452
+ * import * as Notifications from 'expo-notifications'
1453
+ * const { data: token } = await Notifications.getExpoPushTokenAsync()
1454
+ * await krisspy.notifications.register(token, 'ios')
1455
+ */
1456
+ async register(token, platform) {
1457
+ var _a, _b;
1458
+ const path = `/api/v1/cloud-backends/${this.backendId}/notifications/token`;
1459
+ const response = await this.http.post(path, { token, platform });
1460
+ if (response.error) {
1461
+ return { data: null, error: response.error };
1462
+ }
1463
+ return { data: (_b = (_a = response.data) == null ? void 0 : _a.data) != null ? _b : null, error: null };
1464
+ }
1465
+ /**
1466
+ * Unregister a push token (e.g., on sign out or when user disables notifications).
1467
+ */
1468
+ async unregister(token) {
1469
+ const path = `/api/v1/cloud-backends/${this.backendId}/notifications/unregister`;
1470
+ const response = await this.http.post(path, { token });
1471
+ return { error: response.error };
1472
+ }
1473
+ /**
1474
+ * List all push tokens registered for the current user.
1475
+ */
1476
+ async getTokens() {
1477
+ var _a, _b;
1478
+ const path = `/api/v1/cloud-backends/${this.backendId}/notifications/tokens`;
1479
+ const response = await this.http.get(path);
1480
+ if (response.error) {
1481
+ return { data: null, error: response.error };
1482
+ }
1483
+ return { data: (_b = (_a = response.data) == null ? void 0 : _a.data) != null ? _b : [], error: null };
1484
+ }
1485
+ // ── Server-side methods (service key required) ──
1486
+ /**
1487
+ * Send a push notification to a specific user.
1488
+ * Requires service key (server-side only).
1489
+ *
1490
+ * @example
1491
+ * // In an Azure Function / serverless function:
1492
+ * const krisspy = createClient({ backendId: '...', apiKey: '...', serviceKey: process.env.SERVICE_KEY })
1493
+ * await krisspy.notifications.send({
1494
+ * userId: 'user123',
1495
+ * title: 'New message',
1496
+ * body: 'You have a new message from Alice',
1497
+ * data: { screen: 'chat', chatId: '456' },
1498
+ * })
1499
+ */
1500
+ async send(params) {
1501
+ var _a, _b;
1502
+ const path = `/api/v1/cloud-backends/${this.backendId}/notifications/send`;
1503
+ const response = await this.http.post(path, params);
1504
+ if (response.error) {
1505
+ return { data: null, error: response.error };
1506
+ }
1507
+ return { data: (_b = (_a = response.data) == null ? void 0 : _a.data) != null ? _b : { tickets: [] }, error: null };
1508
+ }
1509
+ /**
1510
+ * Broadcast a push notification to all registered users.
1511
+ * Requires service key (server-side only).
1512
+ *
1513
+ * @example
1514
+ * await krisspy.notifications.broadcast({
1515
+ * title: 'App Update',
1516
+ * body: 'Version 2.0 is now available!',
1517
+ * })
1518
+ */
1519
+ async broadcast(params) {
1520
+ var _a, _b;
1521
+ const path = `/api/v1/cloud-backends/${this.backendId}/notifications/send-broadcast`;
1522
+ const response = await this.http.post(path, params);
1523
+ if (response.error) {
1524
+ return { data: null, error: response.error };
1525
+ }
1526
+ return { data: (_b = (_a = response.data) == null ? void 0 : _a.data) != null ? _b : { tickets: [], count: 0 }, error: null };
1527
+ }
1528
+ };
1529
+
1440
1530
  // src/query-builder.ts
1441
1531
  var QueryBuilder = class {
1442
1532
  constructor(http, backendId, tableName, dataMode = "rls") {
@@ -1752,6 +1842,7 @@ var KrisspyClient = class {
1752
1842
  this._storage = new KrisspyStorage(this.http, this.backendId);
1753
1843
  this._realtime = new KrisspyRealtime(this.baseUrl, this.backendId, this.debug);
1754
1844
  this._analytics = new KrisspyAnalytics(this.http, this.backendId);
1845
+ this._notifications = new KrisspyNotifications(this.http, this.backendId);
1755
1846
  this._auth.onAuthStateChange((event) => {
1756
1847
  if (event === "SIGNED_IN") {
1757
1848
  const session = this._auth.session();
@@ -1844,6 +1935,29 @@ var KrisspyClient = class {
1844
1935
  get analytics() {
1845
1936
  return this._analytics;
1846
1937
  }
1938
+ /**
1939
+ * Notifications module for Expo push notifications
1940
+ *
1941
+ * @example
1942
+ * // Register push token (client-side, after getting token from expo-notifications)
1943
+ * await krisspy.notifications.register(expoPushToken, 'ios')
1944
+ *
1945
+ * // Unregister on sign out
1946
+ * await krisspy.notifications.unregister(expoPushToken)
1947
+ *
1948
+ * // Send to user (server-side, requires serviceKey)
1949
+ * await krisspy.notifications.send({
1950
+ * userId: 'user123',
1951
+ * title: 'Hello',
1952
+ * body: 'You have a new message',
1953
+ * })
1954
+ *
1955
+ * // Broadcast to all (server-side, requires serviceKey)
1956
+ * await krisspy.notifications.broadcast({ title: 'Update', body: 'v2.0 released!' })
1957
+ */
1958
+ get notifications() {
1959
+ return this._notifications;
1960
+ }
1847
1961
  /**
1848
1962
  * Create a realtime channel for subscribing to database changes
1849
1963
  *
@@ -1955,37 +2069,32 @@ var KrisspyClient = class {
1955
2069
  get functions() {
1956
2070
  return {
1957
2071
  invoke: async (functionName, options) => {
1958
- if (this.functionsUrl) {
1959
- const url = `${this.functionsUrl}/api/${functionName}`;
1960
- try {
1961
- const res = await fetch(url, {
1962
- method: "POST",
1963
- headers: __spreadValues({
1964
- "Content-Type": "application/json"
1965
- }, options == null ? void 0 : options.headers),
1966
- body: (options == null ? void 0 : options.body) ? JSON.stringify(options.body) : void 0
1967
- });
1968
- const contentType = res.headers.get("content-type");
1969
- let data = null;
1970
- if (contentType == null ? void 0 : contentType.includes("application/json")) {
1971
- data = await res.json();
1972
- } else {
1973
- data = await res.text();
1974
- }
1975
- if (!res.ok) {
1976
- return { data: null, error: { message: (data == null ? void 0 : data.error) || (data == null ? void 0 : data.message) || `Request failed with status ${res.status}`, status: res.status } };
1977
- }
1978
- return { data, error: null };
1979
- } catch (err) {
1980
- return { data: null, error: { message: err.message || "Network error", code: "NETWORK_ERROR", status: 0 } };
1981
- }
2072
+ if (!this.functionsUrl) {
2073
+ return { data: null, error: { message: 'functionsUrl is required. Pass it in createClient({ functionsUrl: "https://your-func-app.azurewebsites.net" })', code: "CONFIG_ERROR", status: 0 } };
1982
2074
  }
1983
- const path = `/api/v1/cloud-backends/${this.backendId}/functions/${functionName}/invoke`;
1984
- const response = await this.http.post(path, options == null ? void 0 : options.body, void 0);
1985
- if (response.error) {
1986
- return { data: null, error: response.error };
2075
+ const url = `${this.functionsUrl}/api/${functionName}`;
2076
+ try {
2077
+ const res = await fetch(url, {
2078
+ method: "POST",
2079
+ headers: __spreadValues({
2080
+ "Content-Type": "application/json"
2081
+ }, options == null ? void 0 : options.headers),
2082
+ body: (options == null ? void 0 : options.body) ? JSON.stringify(options.body) : void 0
2083
+ });
2084
+ const contentType = res.headers.get("content-type");
2085
+ let data = null;
2086
+ if (contentType == null ? void 0 : contentType.includes("application/json")) {
2087
+ data = await res.json();
2088
+ } else {
2089
+ data = await res.text();
2090
+ }
2091
+ if (!res.ok) {
2092
+ return { data: null, error: { message: (data == null ? void 0 : data.error) || (data == null ? void 0 : data.message) || `Request failed with status ${res.status}`, status: res.status } };
2093
+ }
2094
+ return { data, error: null };
2095
+ } catch (err) {
2096
+ return { data: null, error: { message: err.message || "Network error", code: "NETWORK_ERROR", status: 0 } };
1987
2097
  }
1988
- return { data: response.data, error: null };
1989
2098
  },
1990
2099
  list: async () => {
1991
2100
  var _a, _b;
@@ -2044,6 +2153,7 @@ export {
2044
2153
  KrisspyAnalytics,
2045
2154
  KrisspyAuth,
2046
2155
  KrisspyClient,
2156
+ KrisspyNotifications,
2047
2157
  KrisspyRealtime,
2048
2158
  KrisspyStorage,
2049
2159
  QueryBuilder,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "krisspy-sdk",
3
- "version": "0.7.0",
3
+ "version": "0.8.1",
4
4
  "description": "Krisspy Cloud SDK - Database, Auth, Storage, and Functions for your apps",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",