nodestack-client 1.2.0 → 2.0.0

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.mjs CHANGED
@@ -1,3 +1,10 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
1
8
  // src/auth/BaseAuthStore.ts
2
9
  function decodeJwtPayload(token) {
3
10
  if (!token || typeof token !== "string") return null;
@@ -424,7 +431,8 @@ var FileService = class extends BaseService {
424
431
  const cleanCol = encodeURIComponent(collectionName);
425
432
  const cleanId = encodeURIComponent(recordId);
426
433
  const cleanFile = encodeURIComponent(filename);
427
- let url = `${cleanBase}/api/files/${cleanCol}/${cleanId}/${cleanFile}`;
434
+ const appSlug = options.app || this.client.appSlug;
435
+ let url = appSlug && appSlug !== "default" ? `${cleanBase}/api/apps/${encodeURIComponent(appSlug)}/files/${cleanCol}/${cleanId}/${cleanFile}` : `${cleanBase}/api/files/${cleanCol}/${cleanId}/${cleanFile}`;
428
436
  const queryParts = [];
429
437
  if (options.download) {
430
438
  queryParts.push("download=1");
@@ -497,8 +505,16 @@ var RealtimeService = class extends BaseService {
497
505
  const EventSourceClass = this.getEventSourceConstructor();
498
506
  const base = (this.client.baseUrl || "").replace(/\/+$/, "");
499
507
  let url = `${base}/api/realtime`;
508
+ const queryParams = new URLSearchParams();
500
509
  if (this.client.authStore.token) {
501
- url += `?token=${encodeURIComponent(this.client.authStore.token)}`;
510
+ queryParams.append("token", this.client.authStore.token);
511
+ }
512
+ if (this.client.appSlug) {
513
+ queryParams.append("app", this.client.appSlug);
514
+ }
515
+ const qs = queryParams.toString();
516
+ if (qs) {
517
+ url += `?${qs}`;
502
518
  }
503
519
  this.eventSource = new EventSourceClass(url);
504
520
  const handleConnect = async (event) => {
@@ -730,6 +746,8 @@ var AdminService = class extends BaseService {
730
746
 
731
747
  // src/services/CollectionService.ts
732
748
  var CollectionService = class extends BaseService {
749
+ list = this.getList.bind(this);
750
+ get = this.getOne.bind(this);
733
751
  /**
734
752
  * Retrieves all collection schema models.
735
753
  */
@@ -836,15 +854,288 @@ var DemoService = class extends BaseService {
836
854
  }
837
855
  };
838
856
 
857
+ // src/services/SnapshotService.ts
858
+ var SnapshotService = class extends BaseService {
859
+ /**
860
+ * Retrieves all snapshots, active baseline, and live drift information.
861
+ */
862
+ async list() {
863
+ return this.send("/api/snapshots", {
864
+ method: "GET"
865
+ });
866
+ }
867
+ /**
868
+ * Alias for list() to retrieve status.
869
+ */
870
+ async getStatus() {
871
+ return this.list();
872
+ }
873
+ /**
874
+ * Creates a new application state snapshot.
875
+ * If snapshot limit (10) is reached, evictOldest: true can be passed to confirm eviction of the oldest.
876
+ */
877
+ async create(options) {
878
+ return this.send("/api/snapshots", {
879
+ method: "POST",
880
+ body: options || {}
881
+ });
882
+ }
883
+ /**
884
+ * Retrieves a specific snapshot by ID.
885
+ */
886
+ async get(id) {
887
+ return this.send(`/api/snapshots/${encodeURIComponent(id)}`, {
888
+ method: "GET"
889
+ });
890
+ }
891
+ /**
892
+ * Restores application state to a specific snapshot.
893
+ * Note: Future snapshots are NOT deleted; history remains intact.
894
+ */
895
+ async restore(id) {
896
+ return this.send(`/api/snapshots/${encodeURIComponent(id)}/restore`, {
897
+ method: "POST"
898
+ });
899
+ }
900
+ /**
901
+ * Deletes a specific snapshot by ID.
902
+ */
903
+ async delete(id) {
904
+ return this.send(`/api/snapshots/${encodeURIComponent(id)}`, {
905
+ method: "DELETE"
906
+ });
907
+ }
908
+ /**
909
+ * Clears all saved snapshots.
910
+ */
911
+ async clear() {
912
+ return this.send("/api/snapshots", {
913
+ method: "DELETE"
914
+ });
915
+ }
916
+ };
917
+
918
+ // src/services/WebhookService.ts
919
+ function computeWebhookSignature(payload, secret) {
920
+ const str = typeof payload === "string" ? payload : JSON.stringify(payload);
921
+ try {
922
+ const nodeCrypto = typeof __require !== "undefined" ? __require("crypto") : typeof globalThis !== "undefined" ? globalThis.require?.("crypto") || globalThis.crypto : null;
923
+ if (nodeCrypto && typeof nodeCrypto.createHmac === "function") {
924
+ return nodeCrypto.createHmac("sha256", secret).update(str, "utf8").digest("hex");
925
+ }
926
+ } catch {
927
+ }
928
+ throw new Error("[NodeStackClient] HMAC-SHA256 computation requires crypto module.");
929
+ }
930
+ function verifyWebhookSignature(payload, signatureHeader, secret) {
931
+ if (!signatureHeader || !secret) return false;
932
+ let expected = "";
933
+ try {
934
+ expected = computeWebhookSignature(payload, secret);
935
+ } catch {
936
+ return false;
937
+ }
938
+ let cleanHeader = signatureHeader.trim();
939
+ if (cleanHeader.startsWith("sha256=")) {
940
+ cleanHeader = cleanHeader.substring(7);
941
+ } else if (cleanHeader.startsWith("sha256:")) {
942
+ cleanHeader = cleanHeader.substring(7);
943
+ }
944
+ if (cleanHeader.length !== expected.length) return false;
945
+ try {
946
+ const nodeCrypto = typeof __require !== "undefined" ? __require("crypto") : typeof globalThis !== "undefined" ? globalThis.require?.("crypto") : null;
947
+ const buf = typeof Buffer !== "undefined" ? Buffer : typeof globalThis !== "undefined" ? globalThis.Buffer : null;
948
+ if (nodeCrypto && typeof nodeCrypto.timingSafeEqual === "function" && buf) {
949
+ return nodeCrypto.timingSafeEqual(buf.from(cleanHeader, "hex"), buf.from(expected, "hex"));
950
+ }
951
+ } catch {
952
+ }
953
+ let diff = 0;
954
+ for (let i = 0; i < cleanHeader.length; i++) {
955
+ diff |= cleanHeader.charCodeAt(i) ^ expected.charCodeAt(i);
956
+ }
957
+ return diff === 0;
958
+ }
959
+ var WebhookService = class extends BaseService {
960
+ /**
961
+ * Retrieves all configured outbound webhooks.
962
+ */
963
+ async getList(options = {}) {
964
+ const res = await this.send("/api/webhooks", {
965
+ method: "GET",
966
+ query: options
967
+ });
968
+ return Array.isArray(res) ? res : [];
969
+ }
970
+ /**
971
+ * Retrieves a single webhook by ID.
972
+ */
973
+ async getOne(id) {
974
+ return this.send(`/api/webhooks/${encodeURIComponent(id)}`, {
975
+ method: "GET"
976
+ });
977
+ }
978
+ /**
979
+ * Creates a new outbound webhook configuration.
980
+ */
981
+ async create(data) {
982
+ return this.send("/api/webhooks", {
983
+ method: "POST",
984
+ body: data
985
+ });
986
+ }
987
+ /**
988
+ * Updates an existing webhook configuration.
989
+ */
990
+ async update(id, data) {
991
+ return this.send(`/api/webhooks/${encodeURIComponent(id)}`, {
992
+ method: "PATCH",
993
+ body: data
994
+ });
995
+ }
996
+ /**
997
+ * Deletes a configured webhook by ID.
998
+ */
999
+ async delete(id) {
1000
+ await this.send(`/api/webhooks/${encodeURIComponent(id)}`, {
1001
+ method: "DELETE"
1002
+ });
1003
+ return true;
1004
+ }
1005
+ /**
1006
+ * Dispatches a live test event to the target webhook endpoint with an optional payload.
1007
+ */
1008
+ async test(id, payload) {
1009
+ return this.send(`/api/webhooks/${encodeURIComponent(id)}/test`, {
1010
+ method: "POST",
1011
+ body: payload !== void 0 ? { payload } : {}
1012
+ });
1013
+ }
1014
+ /**
1015
+ * Retrieves overall webhook health and delivery statistics.
1016
+ */
1017
+ async getStats() {
1018
+ return this.send("/api/webhooks/stats", {
1019
+ method: "GET"
1020
+ });
1021
+ }
1022
+ /**
1023
+ * Queries paginated webhook delivery logs with optional filtering by webhook ID, collection, status, or search.
1024
+ */
1025
+ async getDeliveries(options = {}) {
1026
+ return this.send("/api/webhooks/deliveries", {
1027
+ method: "GET",
1028
+ query: options
1029
+ });
1030
+ }
1031
+ /**
1032
+ * Retrieves complete delivery log details including headers and request/response payloads.
1033
+ */
1034
+ async getDelivery(id) {
1035
+ return this.send(`/api/webhooks/deliveries/${encodeURIComponent(id)}`, {
1036
+ method: "GET"
1037
+ });
1038
+ }
1039
+ /**
1040
+ * Manually retries a failed delivery attempt to its target endpoint.
1041
+ */
1042
+ async retryDelivery(id) {
1043
+ return this.send(`/api/webhooks/deliveries/${encodeURIComponent(id)}/retry`, {
1044
+ method: "POST"
1045
+ });
1046
+ }
1047
+ /**
1048
+ * Clears delivery history logs. If olderThanDays is specified, only deletes logs older than that many days.
1049
+ */
1050
+ async clearDeliveries(olderThanDays) {
1051
+ const query = {};
1052
+ if (olderThanDays !== void 0 && olderThanDays !== null) {
1053
+ query.olderThanDays = olderThanDays;
1054
+ }
1055
+ return this.send("/api/webhooks/deliveries", {
1056
+ method: "DELETE",
1057
+ query
1058
+ });
1059
+ }
1060
+ /**
1061
+ * Verifies an incoming X-NodeStack-Signature header against the expected secret.
1062
+ */
1063
+ verifySignature(payload, signatureHeader, secret) {
1064
+ return verifyWebhookSignature(payload, signatureHeader, secret);
1065
+ }
1066
+ /**
1067
+ * Computes an HMAC-SHA256 signature for a payload and secret string.
1068
+ */
1069
+ computeSignature(payload, secret) {
1070
+ return computeWebhookSignature(payload, secret);
1071
+ }
1072
+ static verifySignature = verifyWebhookSignature;
1073
+ static computeSignature = computeWebhookSignature;
1074
+ };
1075
+
1076
+ // src/services/AppService.ts
1077
+ var AppService = class extends BaseService {
1078
+ list = this.getList.bind(this);
1079
+ get = this.getOne.bind(this);
1080
+ /**
1081
+ * Retrieves all configured applications.
1082
+ */
1083
+ async getList() {
1084
+ const res = await this.send("/api/apps", {
1085
+ method: "GET"
1086
+ });
1087
+ return Array.isArray(res) ? res : res?.items || [];
1088
+ }
1089
+ /**
1090
+ * Retrieves a single application by ID or slug.
1091
+ */
1092
+ async getOne(appId) {
1093
+ return this.send(`/api/apps/${encodeURIComponent(appId)}`, {
1094
+ method: "GET"
1095
+ });
1096
+ }
1097
+ /**
1098
+ * Creates a new application.
1099
+ */
1100
+ async create(data) {
1101
+ return this.send("/api/apps", {
1102
+ method: "POST",
1103
+ body: data
1104
+ });
1105
+ }
1106
+ /**
1107
+ * Updates an existing application.
1108
+ */
1109
+ async update(appId, data) {
1110
+ return this.send(`/api/apps/${encodeURIComponent(appId)}`, {
1111
+ method: "PATCH",
1112
+ body: data
1113
+ });
1114
+ }
1115
+ /**
1116
+ * Deletes an application by ID or slug.
1117
+ */
1118
+ async delete(appId) {
1119
+ await this.send(`/api/apps/${encodeURIComponent(appId)}`, {
1120
+ method: "DELETE"
1121
+ });
1122
+ return true;
1123
+ }
1124
+ };
1125
+
839
1126
  // src/Client.ts
840
- var NodeStackClient = class {
1127
+ var NodeStackClient = class _NodeStackClient {
841
1128
  baseUrl;
842
1129
  authStore;
843
1130
  files;
844
1131
  realtime;
845
1132
  admins;
846
1133
  collections;
1134
+ snapshots;
847
1135
  demo;
1136
+ webhooks;
1137
+ apps;
1138
+ appSlug;
848
1139
  customFetch;
849
1140
  defaultHeaders;
850
1141
  defaultTimeout;
@@ -857,11 +1148,15 @@ var NodeStackClient = class {
857
1148
  this.defaultHeaders = options.headers || {};
858
1149
  this.defaultTimeout = options.timeout || 12e4;
859
1150
  this.chaosOptions = options.chaos ? { ...options.chaos } : null;
1151
+ this.appSlug = options.app;
860
1152
  this.files = new FileService(this);
861
1153
  this.realtime = new RealtimeService(this, options.EventSource);
862
1154
  this.admins = new AdminService(this);
863
1155
  this.collections = new CollectionService(this);
1156
+ this.snapshots = new SnapshotService(this);
864
1157
  this.demo = new DemoService(this);
1158
+ this.webhooks = new WebhookService(this);
1159
+ this.apps = new AppService(this);
865
1160
  }
866
1161
  /**
867
1162
  * Dynamically configure latency & chaos simulation options for all future requests.
@@ -915,6 +1210,9 @@ var NodeStackClient = class {
915
1210
  if (this.authStore.token && !headers["Authorization"] && !headers["authorization"]) {
916
1211
  headers["Authorization"] = `Bearer ${this.authStore.token}`;
917
1212
  }
1213
+ if (this.appSlug && !headers["x-nodestack-app"] && !headers["X-NodeStack-App"]) {
1214
+ headers["x-nodestack-app"] = this.appSlug;
1215
+ }
918
1216
  const effectiveChaos = {
919
1217
  ...this.chaosOptions || {},
920
1218
  ...options.chaos || {},
@@ -1031,6 +1329,21 @@ var NodeStackClient = class {
1031
1329
  async metrics() {
1032
1330
  return this.send("/api/metrics", { method: "GET" });
1033
1331
  }
1332
+ /**
1333
+ * Returns a new NodeStackClient instance scoped to a specific application/MVP.
1334
+ * Inherits baseUrl, authStore, customFetch, defaultHeaders, and defaultTimeout.
1335
+ */
1336
+ app(slug, options = {}) {
1337
+ return new _NodeStackClient(this.baseUrl, {
1338
+ authStore: options.authStore || this.authStore,
1339
+ fetch: options.fetch || this.customFetch,
1340
+ headers: { ...this.defaultHeaders, ...options.headers || {} },
1341
+ timeout: options.timeout !== void 0 ? options.timeout : this.defaultTimeout,
1342
+ chaos: options.chaos !== void 0 ? options.chaos ? { ...options.chaos } : void 0 : this.chaosOptions ? { ...this.chaosOptions } : void 0,
1343
+ app: slug,
1344
+ EventSource: options.EventSource || this.realtime.customEventSource
1345
+ });
1346
+ }
1034
1347
  };
1035
1348
 
1036
1349
  // src/auth/MemoryAuthStore.ts
@@ -1039,6 +1352,7 @@ var MemoryAuthStore = class extends BaseAuthStore {
1039
1352
  };
1040
1353
  export {
1041
1354
  AdminService,
1355
+ AppService,
1042
1356
  BaseAuthStore,
1043
1357
  BaseService,
1044
1358
  ClientResponseError,
@@ -1050,7 +1364,11 @@ export {
1050
1364
  NodeStackClient,
1051
1365
  RealtimeService,
1052
1366
  RecordService,
1367
+ SnapshotService,
1368
+ WebhookService,
1369
+ computeWebhookSignature,
1053
1370
  decodeJwtPayload,
1054
- NodeStackClient as default
1371
+ NodeStackClient as default,
1372
+ verifyWebhookSignature
1055
1373
  };
1056
1374
  //# sourceMappingURL=index.mjs.map