@objectstack/client 2.0.0 → 2.0.2

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
@@ -83,6 +83,41 @@ var FilterBuilder = class {
83
83
  this.conditions.push([field, "is_not_null", null]);
84
84
  return this;
85
85
  }
86
+ /**
87
+ * BETWEEN filter: field BETWEEN min AND max
88
+ */
89
+ between(field, min, max) {
90
+ this.conditions.push(["and", [field, ">=", min], [field, "<=", max]]);
91
+ return this;
92
+ }
93
+ /**
94
+ * CONTAINS filter: field contains value (case-insensitive LIKE %value%)
95
+ */
96
+ contains(field, value) {
97
+ this.conditions.push([field, "like", `%${value}%`]);
98
+ return this;
99
+ }
100
+ /**
101
+ * STARTS WITH filter: field starts with value (LIKE value%)
102
+ */
103
+ startsWith(field, value) {
104
+ this.conditions.push([field, "like", `${value}%`]);
105
+ return this;
106
+ }
107
+ /**
108
+ * ENDS WITH filter: field ends with value (LIKE %value)
109
+ */
110
+ endsWith(field, value) {
111
+ this.conditions.push([field, "like", `%${value}`]);
112
+ return this;
113
+ }
114
+ /**
115
+ * EXISTS filter: field is not null (alias for isNotNull)
116
+ */
117
+ exists(field) {
118
+ this.conditions.push([field, "is_not_null", null]);
119
+ return this;
120
+ }
86
121
  /**
87
122
  * Build the filter condition
88
123
  */
@@ -178,6 +213,37 @@ var QueryBuilder = class {
178
213
  this.query.groupBy = fields;
179
214
  return this;
180
215
  }
216
+ /**
217
+ * Expand (eager-load) a related object with an optional sub-query
218
+ */
219
+ expand(relation, subQuery) {
220
+ if (!this.query.expand) {
221
+ this.query.expand = {};
222
+ }
223
+ this.query.expand[relation] = subQuery || {};
224
+ return this;
225
+ }
226
+ /**
227
+ * Add full-text search
228
+ */
229
+ search(query, options) {
230
+ this.query.search = { query, ...options };
231
+ return this;
232
+ }
233
+ /**
234
+ * Set cursor for keyset pagination
235
+ */
236
+ cursor(cursor) {
237
+ this.query.cursor = cursor;
238
+ return this;
239
+ }
240
+ /**
241
+ * Enable SELECT DISTINCT
242
+ */
243
+ distinct() {
244
+ this.query.distinct = true;
245
+ return this;
246
+ }
181
247
  /**
182
248
  * Build the final query AST
183
249
  */
@@ -335,36 +401,6 @@ var ObjectStackClient = class {
335
401
  return res.json();
336
402
  }
337
403
  };
338
- /**
339
- * Hub Management Services
340
- */
341
- this.hub = {
342
- spaces: {
343
- list: async () => {
344
- const route = this.getRoute("hub");
345
- const res = await this.fetch(`${this.baseUrl}${route}/spaces`);
346
- return res.json();
347
- },
348
- create: async (payload) => {
349
- const route = this.getRoute("hub");
350
- const res = await this.fetch(`${this.baseUrl}${route}/spaces`, {
351
- method: "POST",
352
- body: JSON.stringify(payload)
353
- });
354
- return res.json();
355
- }
356
- },
357
- plugins: {
358
- install: async (pkg, version) => {
359
- const route = this.getRoute("hub");
360
- const res = await this.fetch(`${this.baseUrl}${route}/plugins/install`, {
361
- method: "POST",
362
- body: JSON.stringify({ pkg, version })
363
- });
364
- return res.json();
365
- }
366
- }
367
- };
368
404
  /**
369
405
  * Package Management Services
370
406
  *
@@ -475,6 +511,36 @@ var ObjectStackClient = class {
475
511
  const route = this.getRoute("auth");
476
512
  const res = await this.fetch(`${this.baseUrl}${route}/me`);
477
513
  return res.json();
514
+ },
515
+ /**
516
+ * Register a new user account
517
+ */
518
+ register: async (request) => {
519
+ const route = this.getRoute("auth");
520
+ const res = await this.fetch(`${this.baseUrl}${route}/register`, {
521
+ method: "POST",
522
+ body: JSON.stringify(request)
523
+ });
524
+ const data = await res.json();
525
+ if (data.data?.token) {
526
+ this.token = data.data.token;
527
+ }
528
+ return data;
529
+ },
530
+ /**
531
+ * Refresh an authentication token
532
+ */
533
+ refreshToken: async (refreshToken) => {
534
+ const route = this.getRoute("auth");
535
+ const res = await this.fetch(`${this.baseUrl}${route}/refresh`, {
536
+ method: "POST",
537
+ body: JSON.stringify({ refreshToken })
538
+ });
539
+ const data = await res.json();
540
+ if (data.data?.token) {
541
+ this.token = data.data.token;
542
+ }
543
+ return data;
478
544
  }
479
545
  };
480
546
  /**
@@ -531,6 +597,384 @@ var ObjectStackClient = class {
531
597
  return res.json();
532
598
  }
533
599
  };
600
+ /**
601
+ * Permissions Services
602
+ */
603
+ this.permissions = {
604
+ /**
605
+ * Check if current user has permission for an action on an object
606
+ */
607
+ check: async (request) => {
608
+ const route = this.getRoute("permissions");
609
+ const res = await this.fetch(`${this.baseUrl}${route}/check`, {
610
+ method: "POST",
611
+ body: JSON.stringify(request)
612
+ });
613
+ return this.unwrapResponse(res);
614
+ },
615
+ /**
616
+ * Get all permissions for a specific object
617
+ */
618
+ getObjectPermissions: async (object) => {
619
+ const route = this.getRoute("permissions");
620
+ const res = await this.fetch(`${this.baseUrl}${route}/permissions/${encodeURIComponent(object)}`);
621
+ return this.unwrapResponse(res);
622
+ },
623
+ /**
624
+ * Get effective permissions for the current user
625
+ */
626
+ getEffectivePermissions: async () => {
627
+ const route = this.getRoute("permissions");
628
+ const res = await this.fetch(`${this.baseUrl}${route}/permissions/effective`);
629
+ return this.unwrapResponse(res);
630
+ }
631
+ };
632
+ /**
633
+ * Realtime Services
634
+ */
635
+ this.realtime = {
636
+ /**
637
+ * Establish a realtime connection
638
+ */
639
+ connect: async (request) => {
640
+ const route = this.getRoute("realtime");
641
+ const res = await this.fetch(`${this.baseUrl}${route}/connect`, {
642
+ method: "POST",
643
+ body: JSON.stringify(request || {})
644
+ });
645
+ return this.unwrapResponse(res);
646
+ },
647
+ /**
648
+ * Disconnect from realtime services
649
+ */
650
+ disconnect: async () => {
651
+ const route = this.getRoute("realtime");
652
+ await this.fetch(`${this.baseUrl}${route}/disconnect`, {
653
+ method: "POST"
654
+ });
655
+ },
656
+ /**
657
+ * Subscribe to a channel
658
+ */
659
+ subscribe: async (request) => {
660
+ const route = this.getRoute("realtime");
661
+ const res = await this.fetch(`${this.baseUrl}${route}/subscribe`, {
662
+ method: "POST",
663
+ body: JSON.stringify(request)
664
+ });
665
+ return this.unwrapResponse(res);
666
+ },
667
+ /**
668
+ * Unsubscribe from a channel
669
+ */
670
+ unsubscribe: async (subscriptionId) => {
671
+ const route = this.getRoute("realtime");
672
+ await this.fetch(`${this.baseUrl}${route}/unsubscribe`, {
673
+ method: "POST",
674
+ body: JSON.stringify({ subscriptionId })
675
+ });
676
+ },
677
+ /**
678
+ * Set presence state on a channel
679
+ */
680
+ setPresence: async (channel, state) => {
681
+ const route = this.getRoute("realtime");
682
+ await this.fetch(`${this.baseUrl}${route}/presence`, {
683
+ method: "PUT",
684
+ body: JSON.stringify({ channel, state })
685
+ });
686
+ },
687
+ /**
688
+ * Get presence information for a channel
689
+ */
690
+ getPresence: async (channel) => {
691
+ const route = this.getRoute("realtime");
692
+ const res = await this.fetch(`${this.baseUrl}${route}/presence/${encodeURIComponent(channel)}`);
693
+ return this.unwrapResponse(res);
694
+ }
695
+ };
696
+ /**
697
+ * Workflow Services
698
+ */
699
+ this.workflow = {
700
+ /**
701
+ * Get workflow configuration for an object
702
+ */
703
+ getConfig: async (object) => {
704
+ const route = this.getRoute("workflow");
705
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/config`);
706
+ return this.unwrapResponse(res);
707
+ },
708
+ /**
709
+ * Get current workflow state for a record
710
+ */
711
+ getState: async (object, recordId) => {
712
+ const route = this.getRoute("workflow");
713
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/state`);
714
+ return this.unwrapResponse(res);
715
+ },
716
+ /**
717
+ * Execute a workflow state transition
718
+ */
719
+ transition: async (request) => {
720
+ const route = this.getRoute("workflow");
721
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(request.object)}/${encodeURIComponent(request.recordId)}/transition`, {
722
+ method: "POST",
723
+ body: JSON.stringify({
724
+ transition: request.transition,
725
+ comment: request.comment,
726
+ data: request.data
727
+ })
728
+ });
729
+ return this.unwrapResponse(res);
730
+ },
731
+ /**
732
+ * Approve a workflow step
733
+ */
734
+ approve: async (request) => {
735
+ const route = this.getRoute("workflow");
736
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(request.object)}/${encodeURIComponent(request.recordId)}/approve`, {
737
+ method: "POST",
738
+ body: JSON.stringify({
739
+ comment: request.comment,
740
+ data: request.data
741
+ })
742
+ });
743
+ return this.unwrapResponse(res);
744
+ },
745
+ /**
746
+ * Reject a workflow step
747
+ */
748
+ reject: async (request) => {
749
+ const route = this.getRoute("workflow");
750
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(request.object)}/${encodeURIComponent(request.recordId)}/reject`, {
751
+ method: "POST",
752
+ body: JSON.stringify({
753
+ reason: request.reason,
754
+ comment: request.comment
755
+ })
756
+ });
757
+ return this.unwrapResponse(res);
758
+ }
759
+ };
760
+ /**
761
+ * Views CRUD Services
762
+ */
763
+ this.views = {
764
+ /**
765
+ * List views for an object
766
+ */
767
+ list: async (object, type) => {
768
+ const route = this.getRoute("views");
769
+ const params = new URLSearchParams();
770
+ if (type) params.set("type", type);
771
+ const qs = params.toString();
772
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}${qs ? `?${qs}` : ""}`);
773
+ return this.unwrapResponse(res);
774
+ },
775
+ /**
776
+ * Get a specific view
777
+ */
778
+ get: async (object, viewId) => {
779
+ const route = this.getRoute("views");
780
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(viewId)}`);
781
+ return this.unwrapResponse(res);
782
+ },
783
+ /**
784
+ * Create a new view
785
+ */
786
+ create: async (object, data) => {
787
+ const route = this.getRoute("views");
788
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}`, {
789
+ method: "POST",
790
+ body: JSON.stringify({ object, data })
791
+ });
792
+ return this.unwrapResponse(res);
793
+ },
794
+ /**
795
+ * Update an existing view
796
+ */
797
+ update: async (object, viewId, data) => {
798
+ const route = this.getRoute("views");
799
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(viewId)}`, {
800
+ method: "PUT",
801
+ body: JSON.stringify({ object, viewId, data })
802
+ });
803
+ return this.unwrapResponse(res);
804
+ },
805
+ /**
806
+ * Delete a view
807
+ */
808
+ delete: async (object, viewId) => {
809
+ const route = this.getRoute("views");
810
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(viewId)}`, {
811
+ method: "DELETE"
812
+ });
813
+ return this.unwrapResponse(res);
814
+ }
815
+ };
816
+ /**
817
+ * Notification Services
818
+ */
819
+ this.notifications = {
820
+ /**
821
+ * Register a device for push notifications
822
+ */
823
+ registerDevice: async (request) => {
824
+ const route = this.getRoute("notifications");
825
+ const res = await this.fetch(`${this.baseUrl}${route}/devices`, {
826
+ method: "POST",
827
+ body: JSON.stringify(request)
828
+ });
829
+ return this.unwrapResponse(res);
830
+ },
831
+ /**
832
+ * Unregister a device from push notifications
833
+ */
834
+ unregisterDevice: async (deviceId) => {
835
+ const route = this.getRoute("notifications");
836
+ const res = await this.fetch(`${this.baseUrl}${route}/devices/${encodeURIComponent(deviceId)}`, {
837
+ method: "DELETE"
838
+ });
839
+ return this.unwrapResponse(res);
840
+ },
841
+ /**
842
+ * Get notification preferences for the current user
843
+ */
844
+ getPreferences: async () => {
845
+ const route = this.getRoute("notifications");
846
+ const res = await this.fetch(`${this.baseUrl}${route}/preferences`);
847
+ return this.unwrapResponse(res);
848
+ },
849
+ /**
850
+ * Update notification preferences
851
+ */
852
+ updatePreferences: async (preferences) => {
853
+ const route = this.getRoute("notifications");
854
+ const res = await this.fetch(`${this.baseUrl}${route}/preferences`, {
855
+ method: "PUT",
856
+ body: JSON.stringify({ preferences })
857
+ });
858
+ return this.unwrapResponse(res);
859
+ },
860
+ /**
861
+ * List notifications for the current user
862
+ */
863
+ list: async (options) => {
864
+ const route = this.getRoute("notifications");
865
+ const params = new URLSearchParams();
866
+ if (options?.read !== void 0) params.set("read", String(options.read));
867
+ if (options?.type) params.set("type", options.type);
868
+ if (options?.limit) params.set("limit", String(options.limit));
869
+ if (options?.cursor) params.set("cursor", options.cursor);
870
+ const qs = params.toString();
871
+ const res = await this.fetch(`${this.baseUrl}${route}${qs ? `?${qs}` : ""}`);
872
+ return this.unwrapResponse(res);
873
+ },
874
+ /**
875
+ * Mark specific notifications as read
876
+ */
877
+ markRead: async (ids) => {
878
+ const route = this.getRoute("notifications");
879
+ const res = await this.fetch(`${this.baseUrl}${route}/read`, {
880
+ method: "POST",
881
+ body: JSON.stringify({ ids })
882
+ });
883
+ return this.unwrapResponse(res);
884
+ },
885
+ /**
886
+ * Mark all notifications as read
887
+ */
888
+ markAllRead: async () => {
889
+ const route = this.getRoute("notifications");
890
+ const res = await this.fetch(`${this.baseUrl}${route}/read/all`, {
891
+ method: "POST"
892
+ });
893
+ return this.unwrapResponse(res);
894
+ }
895
+ };
896
+ /**
897
+ * AI Services
898
+ */
899
+ this.ai = {
900
+ /**
901
+ * Natural language query — converts natural language to structured query
902
+ */
903
+ nlq: async (request) => {
904
+ const route = this.getRoute("ai");
905
+ const res = await this.fetch(`${this.baseUrl}${route}/nlq`, {
906
+ method: "POST",
907
+ body: JSON.stringify(request)
908
+ });
909
+ return this.unwrapResponse(res);
910
+ },
911
+ /**
912
+ * Multi-turn AI chat
913
+ */
914
+ chat: async (request) => {
915
+ const route = this.getRoute("ai");
916
+ const res = await this.fetch(`${this.baseUrl}${route}/chat`, {
917
+ method: "POST",
918
+ body: JSON.stringify(request)
919
+ });
920
+ return this.unwrapResponse(res);
921
+ },
922
+ /**
923
+ * AI-powered field value suggestions
924
+ */
925
+ suggest: async (request) => {
926
+ const route = this.getRoute("ai");
927
+ const res = await this.fetch(`${this.baseUrl}${route}/suggest`, {
928
+ method: "POST",
929
+ body: JSON.stringify(request)
930
+ });
931
+ return this.unwrapResponse(res);
932
+ },
933
+ /**
934
+ * AI-powered data insights
935
+ */
936
+ insights: async (request) => {
937
+ const route = this.getRoute("ai");
938
+ const res = await this.fetch(`${this.baseUrl}${route}/insights`, {
939
+ method: "POST",
940
+ body: JSON.stringify(request)
941
+ });
942
+ return this.unwrapResponse(res);
943
+ }
944
+ };
945
+ /**
946
+ * Internationalization Services
947
+ */
948
+ this.i18n = {
949
+ /**
950
+ * Get available locales
951
+ */
952
+ getLocales: async () => {
953
+ const route = this.getRoute("i18n");
954
+ const res = await this.fetch(`${this.baseUrl}${route}/locales`);
955
+ return this.unwrapResponse(res);
956
+ },
957
+ /**
958
+ * Get translations for a locale
959
+ */
960
+ getTranslations: async (locale, options) => {
961
+ const route = this.getRoute("i18n");
962
+ const params = new URLSearchParams();
963
+ params.set("locale", locale);
964
+ if (options?.namespace) params.set("namespace", options.namespace);
965
+ if (options?.keys) params.set("keys", options.keys.join(","));
966
+ const res = await this.fetch(`${this.baseUrl}${route}/translations?${params.toString()}`);
967
+ return this.unwrapResponse(res);
968
+ },
969
+ /**
970
+ * Get translated field labels for an object
971
+ */
972
+ getFieldLabels: async (object, locale) => {
973
+ const route = this.getRoute("i18n");
974
+ const res = await this.fetch(`${this.baseUrl}${route}/labels/${encodeURIComponent(object)}?locale=${encodeURIComponent(locale)}`);
975
+ return this.unwrapResponse(res);
976
+ }
977
+ };
534
978
  /**
535
979
  * Data Operations
536
980
  */
@@ -801,10 +1245,17 @@ var ObjectStackClient = class {
801
1245
  ui: "/api/v1/ui",
802
1246
  auth: "/api/v1/auth",
803
1247
  analytics: "/api/v1/analytics",
804
- hub: "/api/v1/hub",
805
1248
  storage: "/api/v1/storage",
806
1249
  automation: "/api/v1/automation",
807
- packages: "/api/v1/packages"
1250
+ packages: "/api/v1/packages",
1251
+ permissions: "/api/v1/auth",
1252
+ // Permission endpoints are under /api/v1/auth per spec
1253
+ realtime: "/api/v1/realtime",
1254
+ workflow: "/api/v1/workflow",
1255
+ views: "/api/v1/ui/views",
1256
+ notifications: "/api/v1/notifications",
1257
+ ai: "/api/v1/ai",
1258
+ i18n: "/api/v1/i18n"
808
1259
  };
809
1260
  return routeMap[type] || `/api/v1/${type}`;
810
1261
  }