@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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +17 -0
- package/CLIENT_SERVER_INTEGRATION_TESTS.md +939 -0
- package/CLIENT_SPEC_COMPLIANCE.md +361 -0
- package/QUICK_REFERENCE.md +206 -0
- package/README.md +129 -0
- package/dist/index.d.mts +235 -27
- package/dist/index.d.ts +235 -27
- package/dist/index.js +483 -32
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +483 -32
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -11
- package/src/client.test.ts +563 -2
- package/src/index.ts +540 -37
- package/src/query-builder.ts +75 -0
- package/tests/integration/01-discovery.test.ts +68 -0
- package/tests/integration/README.md +72 -0
- package/vitest.integration.config.ts +18 -0
package/dist/index.js
CHANGED
|
@@ -111,6 +111,41 @@ var FilterBuilder = class {
|
|
|
111
111
|
this.conditions.push([field, "is_not_null", null]);
|
|
112
112
|
return this;
|
|
113
113
|
}
|
|
114
|
+
/**
|
|
115
|
+
* BETWEEN filter: field BETWEEN min AND max
|
|
116
|
+
*/
|
|
117
|
+
between(field, min, max) {
|
|
118
|
+
this.conditions.push(["and", [field, ">=", min], [field, "<=", max]]);
|
|
119
|
+
return this;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* CONTAINS filter: field contains value (case-insensitive LIKE %value%)
|
|
123
|
+
*/
|
|
124
|
+
contains(field, value) {
|
|
125
|
+
this.conditions.push([field, "like", `%${value}%`]);
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* STARTS WITH filter: field starts with value (LIKE value%)
|
|
130
|
+
*/
|
|
131
|
+
startsWith(field, value) {
|
|
132
|
+
this.conditions.push([field, "like", `${value}%`]);
|
|
133
|
+
return this;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* ENDS WITH filter: field ends with value (LIKE %value)
|
|
137
|
+
*/
|
|
138
|
+
endsWith(field, value) {
|
|
139
|
+
this.conditions.push([field, "like", `%${value}`]);
|
|
140
|
+
return this;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* EXISTS filter: field is not null (alias for isNotNull)
|
|
144
|
+
*/
|
|
145
|
+
exists(field) {
|
|
146
|
+
this.conditions.push([field, "is_not_null", null]);
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
114
149
|
/**
|
|
115
150
|
* Build the filter condition
|
|
116
151
|
*/
|
|
@@ -206,6 +241,37 @@ var QueryBuilder = class {
|
|
|
206
241
|
this.query.groupBy = fields;
|
|
207
242
|
return this;
|
|
208
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Expand (eager-load) a related object with an optional sub-query
|
|
246
|
+
*/
|
|
247
|
+
expand(relation, subQuery) {
|
|
248
|
+
if (!this.query.expand) {
|
|
249
|
+
this.query.expand = {};
|
|
250
|
+
}
|
|
251
|
+
this.query.expand[relation] = subQuery || {};
|
|
252
|
+
return this;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Add full-text search
|
|
256
|
+
*/
|
|
257
|
+
search(query, options) {
|
|
258
|
+
this.query.search = { query, ...options };
|
|
259
|
+
return this;
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Set cursor for keyset pagination
|
|
263
|
+
*/
|
|
264
|
+
cursor(cursor) {
|
|
265
|
+
this.query.cursor = cursor;
|
|
266
|
+
return this;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Enable SELECT DISTINCT
|
|
270
|
+
*/
|
|
271
|
+
distinct() {
|
|
272
|
+
this.query.distinct = true;
|
|
273
|
+
return this;
|
|
274
|
+
}
|
|
209
275
|
/**
|
|
210
276
|
* Build the final query AST
|
|
211
277
|
*/
|
|
@@ -363,36 +429,6 @@ var ObjectStackClient = class {
|
|
|
363
429
|
return res.json();
|
|
364
430
|
}
|
|
365
431
|
};
|
|
366
|
-
/**
|
|
367
|
-
* Hub Management Services
|
|
368
|
-
*/
|
|
369
|
-
this.hub = {
|
|
370
|
-
spaces: {
|
|
371
|
-
list: async () => {
|
|
372
|
-
const route = this.getRoute("hub");
|
|
373
|
-
const res = await this.fetch(`${this.baseUrl}${route}/spaces`);
|
|
374
|
-
return res.json();
|
|
375
|
-
},
|
|
376
|
-
create: async (payload) => {
|
|
377
|
-
const route = this.getRoute("hub");
|
|
378
|
-
const res = await this.fetch(`${this.baseUrl}${route}/spaces`, {
|
|
379
|
-
method: "POST",
|
|
380
|
-
body: JSON.stringify(payload)
|
|
381
|
-
});
|
|
382
|
-
return res.json();
|
|
383
|
-
}
|
|
384
|
-
},
|
|
385
|
-
plugins: {
|
|
386
|
-
install: async (pkg, version) => {
|
|
387
|
-
const route = this.getRoute("hub");
|
|
388
|
-
const res = await this.fetch(`${this.baseUrl}${route}/plugins/install`, {
|
|
389
|
-
method: "POST",
|
|
390
|
-
body: JSON.stringify({ pkg, version })
|
|
391
|
-
});
|
|
392
|
-
return res.json();
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
};
|
|
396
432
|
/**
|
|
397
433
|
* Package Management Services
|
|
398
434
|
*
|
|
@@ -503,6 +539,36 @@ var ObjectStackClient = class {
|
|
|
503
539
|
const route = this.getRoute("auth");
|
|
504
540
|
const res = await this.fetch(`${this.baseUrl}${route}/me`);
|
|
505
541
|
return res.json();
|
|
542
|
+
},
|
|
543
|
+
/**
|
|
544
|
+
* Register a new user account
|
|
545
|
+
*/
|
|
546
|
+
register: async (request) => {
|
|
547
|
+
const route = this.getRoute("auth");
|
|
548
|
+
const res = await this.fetch(`${this.baseUrl}${route}/register`, {
|
|
549
|
+
method: "POST",
|
|
550
|
+
body: JSON.stringify(request)
|
|
551
|
+
});
|
|
552
|
+
const data = await res.json();
|
|
553
|
+
if (data.data?.token) {
|
|
554
|
+
this.token = data.data.token;
|
|
555
|
+
}
|
|
556
|
+
return data;
|
|
557
|
+
},
|
|
558
|
+
/**
|
|
559
|
+
* Refresh an authentication token
|
|
560
|
+
*/
|
|
561
|
+
refreshToken: async (refreshToken) => {
|
|
562
|
+
const route = this.getRoute("auth");
|
|
563
|
+
const res = await this.fetch(`${this.baseUrl}${route}/refresh`, {
|
|
564
|
+
method: "POST",
|
|
565
|
+
body: JSON.stringify({ refreshToken })
|
|
566
|
+
});
|
|
567
|
+
const data = await res.json();
|
|
568
|
+
if (data.data?.token) {
|
|
569
|
+
this.token = data.data.token;
|
|
570
|
+
}
|
|
571
|
+
return data;
|
|
506
572
|
}
|
|
507
573
|
};
|
|
508
574
|
/**
|
|
@@ -559,6 +625,384 @@ var ObjectStackClient = class {
|
|
|
559
625
|
return res.json();
|
|
560
626
|
}
|
|
561
627
|
};
|
|
628
|
+
/**
|
|
629
|
+
* Permissions Services
|
|
630
|
+
*/
|
|
631
|
+
this.permissions = {
|
|
632
|
+
/**
|
|
633
|
+
* Check if current user has permission for an action on an object
|
|
634
|
+
*/
|
|
635
|
+
check: async (request) => {
|
|
636
|
+
const route = this.getRoute("permissions");
|
|
637
|
+
const res = await this.fetch(`${this.baseUrl}${route}/check`, {
|
|
638
|
+
method: "POST",
|
|
639
|
+
body: JSON.stringify(request)
|
|
640
|
+
});
|
|
641
|
+
return this.unwrapResponse(res);
|
|
642
|
+
},
|
|
643
|
+
/**
|
|
644
|
+
* Get all permissions for a specific object
|
|
645
|
+
*/
|
|
646
|
+
getObjectPermissions: async (object) => {
|
|
647
|
+
const route = this.getRoute("permissions");
|
|
648
|
+
const res = await this.fetch(`${this.baseUrl}${route}/permissions/${encodeURIComponent(object)}`);
|
|
649
|
+
return this.unwrapResponse(res);
|
|
650
|
+
},
|
|
651
|
+
/**
|
|
652
|
+
* Get effective permissions for the current user
|
|
653
|
+
*/
|
|
654
|
+
getEffectivePermissions: async () => {
|
|
655
|
+
const route = this.getRoute("permissions");
|
|
656
|
+
const res = await this.fetch(`${this.baseUrl}${route}/permissions/effective`);
|
|
657
|
+
return this.unwrapResponse(res);
|
|
658
|
+
}
|
|
659
|
+
};
|
|
660
|
+
/**
|
|
661
|
+
* Realtime Services
|
|
662
|
+
*/
|
|
663
|
+
this.realtime = {
|
|
664
|
+
/**
|
|
665
|
+
* Establish a realtime connection
|
|
666
|
+
*/
|
|
667
|
+
connect: async (request) => {
|
|
668
|
+
const route = this.getRoute("realtime");
|
|
669
|
+
const res = await this.fetch(`${this.baseUrl}${route}/connect`, {
|
|
670
|
+
method: "POST",
|
|
671
|
+
body: JSON.stringify(request || {})
|
|
672
|
+
});
|
|
673
|
+
return this.unwrapResponse(res);
|
|
674
|
+
},
|
|
675
|
+
/**
|
|
676
|
+
* Disconnect from realtime services
|
|
677
|
+
*/
|
|
678
|
+
disconnect: async () => {
|
|
679
|
+
const route = this.getRoute("realtime");
|
|
680
|
+
await this.fetch(`${this.baseUrl}${route}/disconnect`, {
|
|
681
|
+
method: "POST"
|
|
682
|
+
});
|
|
683
|
+
},
|
|
684
|
+
/**
|
|
685
|
+
* Subscribe to a channel
|
|
686
|
+
*/
|
|
687
|
+
subscribe: async (request) => {
|
|
688
|
+
const route = this.getRoute("realtime");
|
|
689
|
+
const res = await this.fetch(`${this.baseUrl}${route}/subscribe`, {
|
|
690
|
+
method: "POST",
|
|
691
|
+
body: JSON.stringify(request)
|
|
692
|
+
});
|
|
693
|
+
return this.unwrapResponse(res);
|
|
694
|
+
},
|
|
695
|
+
/**
|
|
696
|
+
* Unsubscribe from a channel
|
|
697
|
+
*/
|
|
698
|
+
unsubscribe: async (subscriptionId) => {
|
|
699
|
+
const route = this.getRoute("realtime");
|
|
700
|
+
await this.fetch(`${this.baseUrl}${route}/unsubscribe`, {
|
|
701
|
+
method: "POST",
|
|
702
|
+
body: JSON.stringify({ subscriptionId })
|
|
703
|
+
});
|
|
704
|
+
},
|
|
705
|
+
/**
|
|
706
|
+
* Set presence state on a channel
|
|
707
|
+
*/
|
|
708
|
+
setPresence: async (channel, state) => {
|
|
709
|
+
const route = this.getRoute("realtime");
|
|
710
|
+
await this.fetch(`${this.baseUrl}${route}/presence`, {
|
|
711
|
+
method: "PUT",
|
|
712
|
+
body: JSON.stringify({ channel, state })
|
|
713
|
+
});
|
|
714
|
+
},
|
|
715
|
+
/**
|
|
716
|
+
* Get presence information for a channel
|
|
717
|
+
*/
|
|
718
|
+
getPresence: async (channel) => {
|
|
719
|
+
const route = this.getRoute("realtime");
|
|
720
|
+
const res = await this.fetch(`${this.baseUrl}${route}/presence/${encodeURIComponent(channel)}`);
|
|
721
|
+
return this.unwrapResponse(res);
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
/**
|
|
725
|
+
* Workflow Services
|
|
726
|
+
*/
|
|
727
|
+
this.workflow = {
|
|
728
|
+
/**
|
|
729
|
+
* Get workflow configuration for an object
|
|
730
|
+
*/
|
|
731
|
+
getConfig: async (object) => {
|
|
732
|
+
const route = this.getRoute("workflow");
|
|
733
|
+
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/config`);
|
|
734
|
+
return this.unwrapResponse(res);
|
|
735
|
+
},
|
|
736
|
+
/**
|
|
737
|
+
* Get current workflow state for a record
|
|
738
|
+
*/
|
|
739
|
+
getState: async (object, recordId) => {
|
|
740
|
+
const route = this.getRoute("workflow");
|
|
741
|
+
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/state`);
|
|
742
|
+
return this.unwrapResponse(res);
|
|
743
|
+
},
|
|
744
|
+
/**
|
|
745
|
+
* Execute a workflow state transition
|
|
746
|
+
*/
|
|
747
|
+
transition: async (request) => {
|
|
748
|
+
const route = this.getRoute("workflow");
|
|
749
|
+
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(request.object)}/${encodeURIComponent(request.recordId)}/transition`, {
|
|
750
|
+
method: "POST",
|
|
751
|
+
body: JSON.stringify({
|
|
752
|
+
transition: request.transition,
|
|
753
|
+
comment: request.comment,
|
|
754
|
+
data: request.data
|
|
755
|
+
})
|
|
756
|
+
});
|
|
757
|
+
return this.unwrapResponse(res);
|
|
758
|
+
},
|
|
759
|
+
/**
|
|
760
|
+
* Approve a workflow step
|
|
761
|
+
*/
|
|
762
|
+
approve: async (request) => {
|
|
763
|
+
const route = this.getRoute("workflow");
|
|
764
|
+
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(request.object)}/${encodeURIComponent(request.recordId)}/approve`, {
|
|
765
|
+
method: "POST",
|
|
766
|
+
body: JSON.stringify({
|
|
767
|
+
comment: request.comment,
|
|
768
|
+
data: request.data
|
|
769
|
+
})
|
|
770
|
+
});
|
|
771
|
+
return this.unwrapResponse(res);
|
|
772
|
+
},
|
|
773
|
+
/**
|
|
774
|
+
* Reject a workflow step
|
|
775
|
+
*/
|
|
776
|
+
reject: async (request) => {
|
|
777
|
+
const route = this.getRoute("workflow");
|
|
778
|
+
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(request.object)}/${encodeURIComponent(request.recordId)}/reject`, {
|
|
779
|
+
method: "POST",
|
|
780
|
+
body: JSON.stringify({
|
|
781
|
+
reason: request.reason,
|
|
782
|
+
comment: request.comment
|
|
783
|
+
})
|
|
784
|
+
});
|
|
785
|
+
return this.unwrapResponse(res);
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
/**
|
|
789
|
+
* Views CRUD Services
|
|
790
|
+
*/
|
|
791
|
+
this.views = {
|
|
792
|
+
/**
|
|
793
|
+
* List views for an object
|
|
794
|
+
*/
|
|
795
|
+
list: async (object, type) => {
|
|
796
|
+
const route = this.getRoute("views");
|
|
797
|
+
const params = new URLSearchParams();
|
|
798
|
+
if (type) params.set("type", type);
|
|
799
|
+
const qs = params.toString();
|
|
800
|
+
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}${qs ? `?${qs}` : ""}`);
|
|
801
|
+
return this.unwrapResponse(res);
|
|
802
|
+
},
|
|
803
|
+
/**
|
|
804
|
+
* Get a specific view
|
|
805
|
+
*/
|
|
806
|
+
get: async (object, viewId) => {
|
|
807
|
+
const route = this.getRoute("views");
|
|
808
|
+
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(viewId)}`);
|
|
809
|
+
return this.unwrapResponse(res);
|
|
810
|
+
},
|
|
811
|
+
/**
|
|
812
|
+
* Create a new view
|
|
813
|
+
*/
|
|
814
|
+
create: async (object, data) => {
|
|
815
|
+
const route = this.getRoute("views");
|
|
816
|
+
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}`, {
|
|
817
|
+
method: "POST",
|
|
818
|
+
body: JSON.stringify({ object, data })
|
|
819
|
+
});
|
|
820
|
+
return this.unwrapResponse(res);
|
|
821
|
+
},
|
|
822
|
+
/**
|
|
823
|
+
* Update an existing view
|
|
824
|
+
*/
|
|
825
|
+
update: async (object, viewId, data) => {
|
|
826
|
+
const route = this.getRoute("views");
|
|
827
|
+
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(viewId)}`, {
|
|
828
|
+
method: "PUT",
|
|
829
|
+
body: JSON.stringify({ object, viewId, data })
|
|
830
|
+
});
|
|
831
|
+
return this.unwrapResponse(res);
|
|
832
|
+
},
|
|
833
|
+
/**
|
|
834
|
+
* Delete a view
|
|
835
|
+
*/
|
|
836
|
+
delete: async (object, viewId) => {
|
|
837
|
+
const route = this.getRoute("views");
|
|
838
|
+
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(viewId)}`, {
|
|
839
|
+
method: "DELETE"
|
|
840
|
+
});
|
|
841
|
+
return this.unwrapResponse(res);
|
|
842
|
+
}
|
|
843
|
+
};
|
|
844
|
+
/**
|
|
845
|
+
* Notification Services
|
|
846
|
+
*/
|
|
847
|
+
this.notifications = {
|
|
848
|
+
/**
|
|
849
|
+
* Register a device for push notifications
|
|
850
|
+
*/
|
|
851
|
+
registerDevice: async (request) => {
|
|
852
|
+
const route = this.getRoute("notifications");
|
|
853
|
+
const res = await this.fetch(`${this.baseUrl}${route}/devices`, {
|
|
854
|
+
method: "POST",
|
|
855
|
+
body: JSON.stringify(request)
|
|
856
|
+
});
|
|
857
|
+
return this.unwrapResponse(res);
|
|
858
|
+
},
|
|
859
|
+
/**
|
|
860
|
+
* Unregister a device from push notifications
|
|
861
|
+
*/
|
|
862
|
+
unregisterDevice: async (deviceId) => {
|
|
863
|
+
const route = this.getRoute("notifications");
|
|
864
|
+
const res = await this.fetch(`${this.baseUrl}${route}/devices/${encodeURIComponent(deviceId)}`, {
|
|
865
|
+
method: "DELETE"
|
|
866
|
+
});
|
|
867
|
+
return this.unwrapResponse(res);
|
|
868
|
+
},
|
|
869
|
+
/**
|
|
870
|
+
* Get notification preferences for the current user
|
|
871
|
+
*/
|
|
872
|
+
getPreferences: async () => {
|
|
873
|
+
const route = this.getRoute("notifications");
|
|
874
|
+
const res = await this.fetch(`${this.baseUrl}${route}/preferences`);
|
|
875
|
+
return this.unwrapResponse(res);
|
|
876
|
+
},
|
|
877
|
+
/**
|
|
878
|
+
* Update notification preferences
|
|
879
|
+
*/
|
|
880
|
+
updatePreferences: async (preferences) => {
|
|
881
|
+
const route = this.getRoute("notifications");
|
|
882
|
+
const res = await this.fetch(`${this.baseUrl}${route}/preferences`, {
|
|
883
|
+
method: "PUT",
|
|
884
|
+
body: JSON.stringify({ preferences })
|
|
885
|
+
});
|
|
886
|
+
return this.unwrapResponse(res);
|
|
887
|
+
},
|
|
888
|
+
/**
|
|
889
|
+
* List notifications for the current user
|
|
890
|
+
*/
|
|
891
|
+
list: async (options) => {
|
|
892
|
+
const route = this.getRoute("notifications");
|
|
893
|
+
const params = new URLSearchParams();
|
|
894
|
+
if (options?.read !== void 0) params.set("read", String(options.read));
|
|
895
|
+
if (options?.type) params.set("type", options.type);
|
|
896
|
+
if (options?.limit) params.set("limit", String(options.limit));
|
|
897
|
+
if (options?.cursor) params.set("cursor", options.cursor);
|
|
898
|
+
const qs = params.toString();
|
|
899
|
+
const res = await this.fetch(`${this.baseUrl}${route}${qs ? `?${qs}` : ""}`);
|
|
900
|
+
return this.unwrapResponse(res);
|
|
901
|
+
},
|
|
902
|
+
/**
|
|
903
|
+
* Mark specific notifications as read
|
|
904
|
+
*/
|
|
905
|
+
markRead: async (ids) => {
|
|
906
|
+
const route = this.getRoute("notifications");
|
|
907
|
+
const res = await this.fetch(`${this.baseUrl}${route}/read`, {
|
|
908
|
+
method: "POST",
|
|
909
|
+
body: JSON.stringify({ ids })
|
|
910
|
+
});
|
|
911
|
+
return this.unwrapResponse(res);
|
|
912
|
+
},
|
|
913
|
+
/**
|
|
914
|
+
* Mark all notifications as read
|
|
915
|
+
*/
|
|
916
|
+
markAllRead: async () => {
|
|
917
|
+
const route = this.getRoute("notifications");
|
|
918
|
+
const res = await this.fetch(`${this.baseUrl}${route}/read/all`, {
|
|
919
|
+
method: "POST"
|
|
920
|
+
});
|
|
921
|
+
return this.unwrapResponse(res);
|
|
922
|
+
}
|
|
923
|
+
};
|
|
924
|
+
/**
|
|
925
|
+
* AI Services
|
|
926
|
+
*/
|
|
927
|
+
this.ai = {
|
|
928
|
+
/**
|
|
929
|
+
* Natural language query — converts natural language to structured query
|
|
930
|
+
*/
|
|
931
|
+
nlq: async (request) => {
|
|
932
|
+
const route = this.getRoute("ai");
|
|
933
|
+
const res = await this.fetch(`${this.baseUrl}${route}/nlq`, {
|
|
934
|
+
method: "POST",
|
|
935
|
+
body: JSON.stringify(request)
|
|
936
|
+
});
|
|
937
|
+
return this.unwrapResponse(res);
|
|
938
|
+
},
|
|
939
|
+
/**
|
|
940
|
+
* Multi-turn AI chat
|
|
941
|
+
*/
|
|
942
|
+
chat: async (request) => {
|
|
943
|
+
const route = this.getRoute("ai");
|
|
944
|
+
const res = await this.fetch(`${this.baseUrl}${route}/chat`, {
|
|
945
|
+
method: "POST",
|
|
946
|
+
body: JSON.stringify(request)
|
|
947
|
+
});
|
|
948
|
+
return this.unwrapResponse(res);
|
|
949
|
+
},
|
|
950
|
+
/**
|
|
951
|
+
* AI-powered field value suggestions
|
|
952
|
+
*/
|
|
953
|
+
suggest: async (request) => {
|
|
954
|
+
const route = this.getRoute("ai");
|
|
955
|
+
const res = await this.fetch(`${this.baseUrl}${route}/suggest`, {
|
|
956
|
+
method: "POST",
|
|
957
|
+
body: JSON.stringify(request)
|
|
958
|
+
});
|
|
959
|
+
return this.unwrapResponse(res);
|
|
960
|
+
},
|
|
961
|
+
/**
|
|
962
|
+
* AI-powered data insights
|
|
963
|
+
*/
|
|
964
|
+
insights: async (request) => {
|
|
965
|
+
const route = this.getRoute("ai");
|
|
966
|
+
const res = await this.fetch(`${this.baseUrl}${route}/insights`, {
|
|
967
|
+
method: "POST",
|
|
968
|
+
body: JSON.stringify(request)
|
|
969
|
+
});
|
|
970
|
+
return this.unwrapResponse(res);
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
/**
|
|
974
|
+
* Internationalization Services
|
|
975
|
+
*/
|
|
976
|
+
this.i18n = {
|
|
977
|
+
/**
|
|
978
|
+
* Get available locales
|
|
979
|
+
*/
|
|
980
|
+
getLocales: async () => {
|
|
981
|
+
const route = this.getRoute("i18n");
|
|
982
|
+
const res = await this.fetch(`${this.baseUrl}${route}/locales`);
|
|
983
|
+
return this.unwrapResponse(res);
|
|
984
|
+
},
|
|
985
|
+
/**
|
|
986
|
+
* Get translations for a locale
|
|
987
|
+
*/
|
|
988
|
+
getTranslations: async (locale, options) => {
|
|
989
|
+
const route = this.getRoute("i18n");
|
|
990
|
+
const params = new URLSearchParams();
|
|
991
|
+
params.set("locale", locale);
|
|
992
|
+
if (options?.namespace) params.set("namespace", options.namespace);
|
|
993
|
+
if (options?.keys) params.set("keys", options.keys.join(","));
|
|
994
|
+
const res = await this.fetch(`${this.baseUrl}${route}/translations?${params.toString()}`);
|
|
995
|
+
return this.unwrapResponse(res);
|
|
996
|
+
},
|
|
997
|
+
/**
|
|
998
|
+
* Get translated field labels for an object
|
|
999
|
+
*/
|
|
1000
|
+
getFieldLabels: async (object, locale) => {
|
|
1001
|
+
const route = this.getRoute("i18n");
|
|
1002
|
+
const res = await this.fetch(`${this.baseUrl}${route}/labels/${encodeURIComponent(object)}?locale=${encodeURIComponent(locale)}`);
|
|
1003
|
+
return this.unwrapResponse(res);
|
|
1004
|
+
}
|
|
1005
|
+
};
|
|
562
1006
|
/**
|
|
563
1007
|
* Data Operations
|
|
564
1008
|
*/
|
|
@@ -829,10 +1273,17 @@ var ObjectStackClient = class {
|
|
|
829
1273
|
ui: "/api/v1/ui",
|
|
830
1274
|
auth: "/api/v1/auth",
|
|
831
1275
|
analytics: "/api/v1/analytics",
|
|
832
|
-
hub: "/api/v1/hub",
|
|
833
1276
|
storage: "/api/v1/storage",
|
|
834
1277
|
automation: "/api/v1/automation",
|
|
835
|
-
packages: "/api/v1/packages"
|
|
1278
|
+
packages: "/api/v1/packages",
|
|
1279
|
+
permissions: "/api/v1/auth",
|
|
1280
|
+
// Permission endpoints are under /api/v1/auth per spec
|
|
1281
|
+
realtime: "/api/v1/realtime",
|
|
1282
|
+
workflow: "/api/v1/workflow",
|
|
1283
|
+
views: "/api/v1/ui/views",
|
|
1284
|
+
notifications: "/api/v1/notifications",
|
|
1285
|
+
ai: "/api/v1/ai",
|
|
1286
|
+
i18n: "/api/v1/i18n"
|
|
836
1287
|
};
|
|
837
1288
|
return routeMap[type] || `/api/v1/${type}`;
|
|
838
1289
|
}
|