@objectstack/client 3.0.8 → 3.0.10

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.js CHANGED
@@ -27,6 +27,7 @@ __export(index_exports, {
27
27
  createQuery: () => createQuery
28
28
  });
29
29
  module.exports = __toCommonJS(index_exports);
30
+ var import_data = require("@objectstack/spec/data");
30
31
  var import_core = require("@objectstack/core");
31
32
 
32
33
  // src/query-builder.ts
@@ -614,12 +615,80 @@ var ObjectStackClient = class {
614
615
  const res = await this.fetch(`${this.baseUrl}${route}/files/${fileId}/url`);
615
616
  const data = await res.json();
616
617
  return data.url;
618
+ },
619
+ /**
620
+ * Get a presigned URL for direct-to-cloud upload
621
+ */
622
+ getPresignedUrl: async (req) => {
623
+ const route = this.getRoute("storage");
624
+ const res = await this.fetch(`${this.baseUrl}${route}/upload/presigned`, {
625
+ method: "POST",
626
+ body: JSON.stringify(req)
627
+ });
628
+ return res.json();
629
+ },
630
+ /**
631
+ * Initiate a chunked (multipart) upload session
632
+ */
633
+ initChunkedUpload: async (req) => {
634
+ const route = this.getRoute("storage");
635
+ const res = await this.fetch(`${this.baseUrl}${route}/upload/chunked`, {
636
+ method: "POST",
637
+ body: JSON.stringify(req)
638
+ });
639
+ return res.json();
640
+ },
641
+ /**
642
+ * Upload a single chunk/part of a multipart upload
643
+ */
644
+ uploadPart: async (uploadId, chunkIndex, resumeToken, data) => {
645
+ const route = this.getRoute("storage");
646
+ const res = await this.fetch(`${this.baseUrl}${route}/upload/chunked/${uploadId}/chunk/${chunkIndex}`, {
647
+ method: "PUT",
648
+ headers: { "x-resume-token": resumeToken },
649
+ body: data
650
+ });
651
+ return res.json();
652
+ },
653
+ /**
654
+ * Complete a chunked upload by assembling all parts
655
+ */
656
+ completeChunkedUpload: async (req) => {
657
+ const route = this.getRoute("storage");
658
+ const res = await this.fetch(`${this.baseUrl}${route}/upload/chunked/${req.uploadId}/complete`, {
659
+ method: "POST",
660
+ body: JSON.stringify(req)
661
+ });
662
+ return res.json();
663
+ },
664
+ /**
665
+ * Resume an interrupted chunked upload.
666
+ * Fetches current progress, then uploads remaining chunks and completes.
667
+ */
668
+ resumeUpload: async (uploadId, file, chunkSize, resumeToken) => {
669
+ const route = this.getRoute("storage");
670
+ const progressRes = await this.fetch(`${this.baseUrl}${route}/upload/chunked/${uploadId}/progress`);
671
+ const progress = await progressRes.json();
672
+ const { totalChunks, uploadedChunks } = progress.data;
673
+ const parts = [];
674
+ const fileBuffer = file instanceof ArrayBuffer ? file : await file.arrayBuffer();
675
+ for (let i = uploadedChunks; i < totalChunks; i++) {
676
+ const start = i * chunkSize;
677
+ const end = Math.min(start + chunkSize, fileBuffer.byteLength);
678
+ const chunk = new Blob([fileBuffer.slice(start, end)]);
679
+ const chunkRes = await this.storage.uploadPart(uploadId, i, resumeToken, chunk);
680
+ parts.push({ chunkIndex: i, eTag: chunkRes.data.eTag });
681
+ }
682
+ return this.storage.completeChunkedUpload({ uploadId, parts });
617
683
  }
618
684
  };
619
685
  /**
620
686
  * Automation Services
621
687
  */
622
688
  this.automation = {
689
+ /**
690
+ * Trigger a named automation flow (legacy endpoint)
691
+ */
623
692
  trigger: async (triggerName, payload) => {
624
693
  const route = this.getRoute("automation");
625
694
  const res = await this.fetch(`${this.baseUrl}${route}/trigger/${triggerName}`, {
@@ -627,6 +696,90 @@ var ObjectStackClient = class {
627
696
  body: JSON.stringify(payload)
628
697
  });
629
698
  return res.json();
699
+ },
700
+ /**
701
+ * List all registered automation flows
702
+ */
703
+ list: async () => {
704
+ const route = this.getRoute("automation");
705
+ const res = await this.fetch(`${this.baseUrl}${route}`);
706
+ return this.unwrapResponse(res);
707
+ },
708
+ /**
709
+ * Get a flow definition by name
710
+ */
711
+ get: async (name) => {
712
+ const route = this.getRoute("automation");
713
+ const res = await this.fetch(`${this.baseUrl}${route}/${name}`);
714
+ return this.unwrapResponse(res);
715
+ },
716
+ /**
717
+ * Create (register) a new flow
718
+ */
719
+ create: async (name, definition) => {
720
+ const route = this.getRoute("automation");
721
+ const res = await this.fetch(`${this.baseUrl}${route}`, {
722
+ method: "POST",
723
+ body: JSON.stringify({ name, ...definition })
724
+ });
725
+ return this.unwrapResponse(res);
726
+ },
727
+ /**
728
+ * Update an existing flow
729
+ */
730
+ update: async (name, definition) => {
731
+ const route = this.getRoute("automation");
732
+ const res = await this.fetch(`${this.baseUrl}${route}/${name}`, {
733
+ method: "PUT",
734
+ body: JSON.stringify({ definition })
735
+ });
736
+ return this.unwrapResponse(res);
737
+ },
738
+ /**
739
+ * Delete (unregister) a flow
740
+ */
741
+ delete: async (name) => {
742
+ const route = this.getRoute("automation");
743
+ const res = await this.fetch(`${this.baseUrl}${route}/${name}`, {
744
+ method: "DELETE"
745
+ });
746
+ return this.unwrapResponse(res);
747
+ },
748
+ /**
749
+ * Enable or disable a flow
750
+ */
751
+ toggle: async (name, enabled) => {
752
+ const route = this.getRoute("automation");
753
+ const res = await this.fetch(`${this.baseUrl}${route}/${name}/toggle`, {
754
+ method: "POST",
755
+ body: JSON.stringify({ enabled })
756
+ });
757
+ return this.unwrapResponse(res);
758
+ },
759
+ /**
760
+ * Execution run history
761
+ */
762
+ runs: {
763
+ /**
764
+ * List execution runs for a flow
765
+ */
766
+ list: async (flowName, options) => {
767
+ const route = this.getRoute("automation");
768
+ const params = new URLSearchParams();
769
+ if (options?.limit) params.set("limit", String(options.limit));
770
+ if (options?.cursor) params.set("cursor", options.cursor);
771
+ const qs = params.toString();
772
+ const res = await this.fetch(`${this.baseUrl}${route}/${flowName}/runs${qs ? `?${qs}` : ""}`);
773
+ return this.unwrapResponse(res);
774
+ },
775
+ /**
776
+ * Get a single execution run
777
+ */
778
+ get: async (flowName, runId) => {
779
+ const route = this.getRoute("automation");
780
+ const res = await this.fetch(`${this.baseUrl}${route}/${flowName}/runs/${runId}`);
781
+ return this.unwrapResponse(res);
782
+ }
630
783
  }
631
784
  };
632
785
  /**
@@ -1007,6 +1160,174 @@ var ObjectStackClient = class {
1007
1160
  return this.unwrapResponse(res);
1008
1161
  }
1009
1162
  };
1163
+ /**
1164
+ * Feed / Chatter Services
1165
+ *
1166
+ * Provides access to the activity timeline (comments, field changes, tasks),
1167
+ * emoji reactions, pin/star, search, changelog, and record subscriptions.
1168
+ * Base path: /api/data/{object}/{recordId}/feed
1169
+ */
1170
+ this.feed = {
1171
+ /**
1172
+ * List feed items for a record
1173
+ */
1174
+ list: async (object, recordId, options) => {
1175
+ const route = this.getRoute("feed");
1176
+ const params = new URLSearchParams();
1177
+ if (options?.type) params.set("type", options.type);
1178
+ if (options?.limit) params.set("limit", String(options.limit));
1179
+ if (options?.cursor) params.set("cursor", options.cursor);
1180
+ const qs = params.toString();
1181
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed${qs ? `?${qs}` : ""}`);
1182
+ return this.unwrapResponse(res);
1183
+ },
1184
+ /**
1185
+ * Create a new feed item (comment, note, task, etc.)
1186
+ */
1187
+ create: async (object, recordId, data) => {
1188
+ const route = this.getRoute("feed");
1189
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed`, {
1190
+ method: "POST",
1191
+ body: JSON.stringify(data)
1192
+ });
1193
+ return this.unwrapResponse(res);
1194
+ },
1195
+ /**
1196
+ * Update an existing feed item
1197
+ */
1198
+ update: async (object, recordId, feedId, data) => {
1199
+ const route = this.getRoute("feed");
1200
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}`, {
1201
+ method: "PUT",
1202
+ body: JSON.stringify(data)
1203
+ });
1204
+ return this.unwrapResponse(res);
1205
+ },
1206
+ /**
1207
+ * Delete a feed item
1208
+ */
1209
+ delete: async (object, recordId, feedId) => {
1210
+ const route = this.getRoute("feed");
1211
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}`, {
1212
+ method: "DELETE"
1213
+ });
1214
+ return this.unwrapResponse(res);
1215
+ },
1216
+ /**
1217
+ * Add an emoji reaction to a feed item
1218
+ */
1219
+ addReaction: async (object, recordId, feedId, emoji) => {
1220
+ const route = this.getRoute("feed");
1221
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/reactions`, {
1222
+ method: "POST",
1223
+ body: JSON.stringify({ emoji })
1224
+ });
1225
+ return this.unwrapResponse(res);
1226
+ },
1227
+ /**
1228
+ * Remove an emoji reaction from a feed item
1229
+ */
1230
+ removeReaction: async (object, recordId, feedId, emoji) => {
1231
+ const route = this.getRoute("feed");
1232
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/reactions/${encodeURIComponent(emoji)}`, {
1233
+ method: "DELETE"
1234
+ });
1235
+ return this.unwrapResponse(res);
1236
+ },
1237
+ /**
1238
+ * Pin a feed item to the top of the timeline
1239
+ */
1240
+ pin: async (object, recordId, feedId) => {
1241
+ const route = this.getRoute("feed");
1242
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/pin`, {
1243
+ method: "POST"
1244
+ });
1245
+ return this.unwrapResponse(res);
1246
+ },
1247
+ /**
1248
+ * Unpin a feed item
1249
+ */
1250
+ unpin: async (object, recordId, feedId) => {
1251
+ const route = this.getRoute("feed");
1252
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/pin`, {
1253
+ method: "DELETE"
1254
+ });
1255
+ return this.unwrapResponse(res);
1256
+ },
1257
+ /**
1258
+ * Star (bookmark) a feed item
1259
+ */
1260
+ star: async (object, recordId, feedId) => {
1261
+ const route = this.getRoute("feed");
1262
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/star`, {
1263
+ method: "POST"
1264
+ });
1265
+ return this.unwrapResponse(res);
1266
+ },
1267
+ /**
1268
+ * Unstar a feed item
1269
+ */
1270
+ unstar: async (object, recordId, feedId) => {
1271
+ const route = this.getRoute("feed");
1272
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/star`, {
1273
+ method: "DELETE"
1274
+ });
1275
+ return this.unwrapResponse(res);
1276
+ },
1277
+ /**
1278
+ * Search feed items
1279
+ */
1280
+ search: async (object, recordId, query, options) => {
1281
+ const route = this.getRoute("feed");
1282
+ const params = new URLSearchParams();
1283
+ params.set("query", query);
1284
+ if (options?.type) params.set("type", options.type);
1285
+ if (options?.actorId) params.set("actorId", options.actorId);
1286
+ if (options?.dateFrom) params.set("dateFrom", options.dateFrom);
1287
+ if (options?.dateTo) params.set("dateTo", options.dateTo);
1288
+ if (options?.limit) params.set("limit", String(options.limit));
1289
+ if (options?.cursor) params.set("cursor", options.cursor);
1290
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/search?${params.toString()}`);
1291
+ return this.unwrapResponse(res);
1292
+ },
1293
+ /**
1294
+ * Get field-level changelog for a record
1295
+ */
1296
+ getChangelog: async (object, recordId, options) => {
1297
+ const route = this.getRoute("feed");
1298
+ const params = new URLSearchParams();
1299
+ if (options?.field) params.set("field", options.field);
1300
+ if (options?.actorId) params.set("actorId", options.actorId);
1301
+ if (options?.dateFrom) params.set("dateFrom", options.dateFrom);
1302
+ if (options?.dateTo) params.set("dateTo", options.dateTo);
1303
+ if (options?.limit) params.set("limit", String(options.limit));
1304
+ if (options?.cursor) params.set("cursor", options.cursor);
1305
+ const qs = params.toString();
1306
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/changelog${qs ? `?${qs}` : ""}`);
1307
+ return this.unwrapResponse(res);
1308
+ },
1309
+ /**
1310
+ * Subscribe to record notifications
1311
+ */
1312
+ subscribe: async (object, recordId, options) => {
1313
+ const route = this.getRoute("feed");
1314
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/subscribe`, {
1315
+ method: "POST",
1316
+ body: JSON.stringify(options || {})
1317
+ });
1318
+ return this.unwrapResponse(res);
1319
+ },
1320
+ /**
1321
+ * Unsubscribe from record notifications
1322
+ */
1323
+ unsubscribe: async (object, recordId) => {
1324
+ const route = this.getRoute("feed");
1325
+ const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/subscribe`, {
1326
+ method: "DELETE"
1327
+ });
1328
+ return this.unwrapResponse(res);
1329
+ }
1330
+ };
1010
1331
  /**
1011
1332
  * Data Operations
1012
1333
  */
@@ -1039,11 +1360,12 @@ var ObjectStackClient = class {
1039
1360
  if (options.select) {
1040
1361
  queryParams.set("select", options.select.join(","));
1041
1362
  }
1042
- if (options.filters) {
1043
- if (this.isFilterAST(options.filters)) {
1044
- queryParams.set("filters", JSON.stringify(options.filters));
1045
- } else {
1046
- Object.entries(options.filters).forEach(([k, v]) => {
1363
+ const filterValue = options.filter ?? options.filters;
1364
+ if (filterValue) {
1365
+ if (this.isFilterAST(filterValue) || Array.isArray(filterValue)) {
1366
+ queryParams.set("filter", JSON.stringify(filterValue));
1367
+ } else if (typeof filterValue === "object" && filterValue !== null) {
1368
+ Object.entries(filterValue).forEach(([k, v]) => {
1047
1369
  if (v !== void 0 && v !== null) {
1048
1370
  queryParams.append(k, String(v));
1049
1371
  }
@@ -1198,11 +1520,19 @@ var ObjectStackClient = class {
1198
1520
  throw e;
1199
1521
  }
1200
1522
  }
1523
+ /**
1524
+ * Well-known capability flags discovered from the server.
1525
+ * Returns undefined if the client has not yet connected or the server
1526
+ * did not include capabilities in its discovery response.
1527
+ */
1528
+ get capabilities() {
1529
+ return this.discoveryInfo?.capabilities;
1530
+ }
1201
1531
  /**
1202
1532
  * Private Helpers
1203
1533
  */
1204
1534
  isFilterAST(filter) {
1205
- return Array.isArray(filter);
1535
+ return (0, import_data.isFilterAST)(filter);
1206
1536
  }
1207
1537
  /**
1208
1538
  * Unwrap the standard REST API response envelope.
@@ -1287,7 +1617,8 @@ var ObjectStackClient = class {
1287
1617
  views: "/api/v1/ui/views",
1288
1618
  notifications: "/api/v1/notifications",
1289
1619
  ai: "/api/v1/ai",
1290
- i18n: "/api/v1/i18n"
1620
+ i18n: "/api/v1/i18n",
1621
+ feed: "/api/v1/data"
1291
1622
  };
1292
1623
  return routeMap[type] || `/api/v1/${type}`;
1293
1624
  }