@objectstack/client 3.3.1 → 4.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/.turbo/turbo-build.log +10 -10
- package/CHANGELOG.md +45 -0
- package/README.md +9 -6
- package/dist/index.d.mts +83 -13
- package/dist/index.d.ts +83 -13
- package/dist/index.js +74 -34
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +74 -34
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -11
- package/src/client.hono.test.ts +2 -2
- package/src/client.msw.test.ts +2 -2
- package/src/client.test.ts +70 -0
- package/src/index.ts +127 -40
- package/src/query-builder.ts +10 -1
- package/tsconfig.json +2 -1
package/dist/index.js
CHANGED
|
@@ -221,12 +221,20 @@ var QueryBuilder = class {
|
|
|
221
221
|
return this;
|
|
222
222
|
}
|
|
223
223
|
/**
|
|
224
|
-
* Skip records (for pagination)
|
|
224
|
+
* Skip records (for pagination).
|
|
225
|
+
* @deprecated Prefer `.offset()` for alignment with Spec canonical field names.
|
|
225
226
|
*/
|
|
226
227
|
skip(count) {
|
|
227
228
|
this.query.offset = count;
|
|
228
229
|
return this;
|
|
229
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* Offset records (for pagination) — canonical alias for `.skip()`
|
|
233
|
+
*/
|
|
234
|
+
offset(count) {
|
|
235
|
+
this.query.offset = count;
|
|
236
|
+
return this;
|
|
237
|
+
}
|
|
230
238
|
/**
|
|
231
239
|
* Paginate results
|
|
232
240
|
*/
|
|
@@ -1172,7 +1180,7 @@ var ObjectStackClient = class {
|
|
|
1172
1180
|
* List feed items for a record
|
|
1173
1181
|
*/
|
|
1174
1182
|
list: async (object, recordId, options) => {
|
|
1175
|
-
const route = this.getRoute("
|
|
1183
|
+
const route = this.getRoute("data");
|
|
1176
1184
|
const params = new URLSearchParams();
|
|
1177
1185
|
if (options?.type) params.set("type", options.type);
|
|
1178
1186
|
if (options?.limit) params.set("limit", String(options.limit));
|
|
@@ -1185,7 +1193,7 @@ var ObjectStackClient = class {
|
|
|
1185
1193
|
* Create a new feed item (comment, note, task, etc.)
|
|
1186
1194
|
*/
|
|
1187
1195
|
create: async (object, recordId, data) => {
|
|
1188
|
-
const route = this.getRoute("
|
|
1196
|
+
const route = this.getRoute("data");
|
|
1189
1197
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed`, {
|
|
1190
1198
|
method: "POST",
|
|
1191
1199
|
body: JSON.stringify(data)
|
|
@@ -1196,7 +1204,7 @@ var ObjectStackClient = class {
|
|
|
1196
1204
|
* Update an existing feed item
|
|
1197
1205
|
*/
|
|
1198
1206
|
update: async (object, recordId, feedId, data) => {
|
|
1199
|
-
const route = this.getRoute("
|
|
1207
|
+
const route = this.getRoute("data");
|
|
1200
1208
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}`, {
|
|
1201
1209
|
method: "PUT",
|
|
1202
1210
|
body: JSON.stringify(data)
|
|
@@ -1207,7 +1215,7 @@ var ObjectStackClient = class {
|
|
|
1207
1215
|
* Delete a feed item
|
|
1208
1216
|
*/
|
|
1209
1217
|
delete: async (object, recordId, feedId) => {
|
|
1210
|
-
const route = this.getRoute("
|
|
1218
|
+
const route = this.getRoute("data");
|
|
1211
1219
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}`, {
|
|
1212
1220
|
method: "DELETE"
|
|
1213
1221
|
});
|
|
@@ -1217,7 +1225,7 @@ var ObjectStackClient = class {
|
|
|
1217
1225
|
* Add an emoji reaction to a feed item
|
|
1218
1226
|
*/
|
|
1219
1227
|
addReaction: async (object, recordId, feedId, emoji) => {
|
|
1220
|
-
const route = this.getRoute("
|
|
1228
|
+
const route = this.getRoute("data");
|
|
1221
1229
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/reactions`, {
|
|
1222
1230
|
method: "POST",
|
|
1223
1231
|
body: JSON.stringify({ emoji })
|
|
@@ -1228,7 +1236,7 @@ var ObjectStackClient = class {
|
|
|
1228
1236
|
* Remove an emoji reaction from a feed item
|
|
1229
1237
|
*/
|
|
1230
1238
|
removeReaction: async (object, recordId, feedId, emoji) => {
|
|
1231
|
-
const route = this.getRoute("
|
|
1239
|
+
const route = this.getRoute("data");
|
|
1232
1240
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/reactions/${encodeURIComponent(emoji)}`, {
|
|
1233
1241
|
method: "DELETE"
|
|
1234
1242
|
});
|
|
@@ -1238,7 +1246,7 @@ var ObjectStackClient = class {
|
|
|
1238
1246
|
* Pin a feed item to the top of the timeline
|
|
1239
1247
|
*/
|
|
1240
1248
|
pin: async (object, recordId, feedId) => {
|
|
1241
|
-
const route = this.getRoute("
|
|
1249
|
+
const route = this.getRoute("data");
|
|
1242
1250
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/pin`, {
|
|
1243
1251
|
method: "POST"
|
|
1244
1252
|
});
|
|
@@ -1248,7 +1256,7 @@ var ObjectStackClient = class {
|
|
|
1248
1256
|
* Unpin a feed item
|
|
1249
1257
|
*/
|
|
1250
1258
|
unpin: async (object, recordId, feedId) => {
|
|
1251
|
-
const route = this.getRoute("
|
|
1259
|
+
const route = this.getRoute("data");
|
|
1252
1260
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/pin`, {
|
|
1253
1261
|
method: "DELETE"
|
|
1254
1262
|
});
|
|
@@ -1258,7 +1266,7 @@ var ObjectStackClient = class {
|
|
|
1258
1266
|
* Star (bookmark) a feed item
|
|
1259
1267
|
*/
|
|
1260
1268
|
star: async (object, recordId, feedId) => {
|
|
1261
|
-
const route = this.getRoute("
|
|
1269
|
+
const route = this.getRoute("data");
|
|
1262
1270
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/star`, {
|
|
1263
1271
|
method: "POST"
|
|
1264
1272
|
});
|
|
@@ -1268,7 +1276,7 @@ var ObjectStackClient = class {
|
|
|
1268
1276
|
* Unstar a feed item
|
|
1269
1277
|
*/
|
|
1270
1278
|
unstar: async (object, recordId, feedId) => {
|
|
1271
|
-
const route = this.getRoute("
|
|
1279
|
+
const route = this.getRoute("data");
|
|
1272
1280
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/star`, {
|
|
1273
1281
|
method: "DELETE"
|
|
1274
1282
|
});
|
|
@@ -1278,7 +1286,7 @@ var ObjectStackClient = class {
|
|
|
1278
1286
|
* Search feed items
|
|
1279
1287
|
*/
|
|
1280
1288
|
search: async (object, recordId, query, options) => {
|
|
1281
|
-
const route = this.getRoute("
|
|
1289
|
+
const route = this.getRoute("data");
|
|
1282
1290
|
const params = new URLSearchParams();
|
|
1283
1291
|
params.set("query", query);
|
|
1284
1292
|
if (options?.type) params.set("type", options.type);
|
|
@@ -1294,7 +1302,7 @@ var ObjectStackClient = class {
|
|
|
1294
1302
|
* Get field-level changelog for a record
|
|
1295
1303
|
*/
|
|
1296
1304
|
getChangelog: async (object, recordId, options) => {
|
|
1297
|
-
const route = this.getRoute("
|
|
1305
|
+
const route = this.getRoute("data");
|
|
1298
1306
|
const params = new URLSearchParams();
|
|
1299
1307
|
if (options?.field) params.set("field", options.field);
|
|
1300
1308
|
if (options?.actorId) params.set("actorId", options.actorId);
|
|
@@ -1310,7 +1318,7 @@ var ObjectStackClient = class {
|
|
|
1310
1318
|
* Subscribe to record notifications
|
|
1311
1319
|
*/
|
|
1312
1320
|
subscribe: async (object, recordId, options) => {
|
|
1313
|
-
const route = this.getRoute("
|
|
1321
|
+
const route = this.getRoute("data");
|
|
1314
1322
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/subscribe`, {
|
|
1315
1323
|
method: "POST",
|
|
1316
1324
|
body: JSON.stringify(options || {})
|
|
@@ -1321,7 +1329,7 @@ var ObjectStackClient = class {
|
|
|
1321
1329
|
* Unsubscribe from record notifications
|
|
1322
1330
|
*/
|
|
1323
1331
|
unsubscribe: async (object, recordId) => {
|
|
1324
|
-
const route = this.getRoute("
|
|
1332
|
+
const route = this.getRoute("data");
|
|
1325
1333
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/subscribe`, {
|
|
1326
1334
|
method: "DELETE"
|
|
1327
1335
|
});
|
|
@@ -1344,23 +1352,40 @@ var ObjectStackClient = class {
|
|
|
1344
1352
|
});
|
|
1345
1353
|
return this.unwrapResponse(res);
|
|
1346
1354
|
},
|
|
1355
|
+
/**
|
|
1356
|
+
* @deprecated Use `data.query()` with standard QueryAST parameters instead.
|
|
1357
|
+
* This method uses legacy parameter names. Internally adapts to HTTP GET params.
|
|
1358
|
+
*/
|
|
1347
1359
|
find: async (object, options = {}) => {
|
|
1348
1360
|
const route = this.getRoute("data");
|
|
1349
1361
|
const queryParams = new URLSearchParams();
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
if (options
|
|
1353
|
-
if (
|
|
1354
|
-
|
|
1362
|
+
const v2 = options;
|
|
1363
|
+
const normalizedOptions = {};
|
|
1364
|
+
if ("where" in options || "fields" in options || "orderBy" in options || "offset" in options) {
|
|
1365
|
+
if (v2.where) normalizedOptions.filter = v2.where;
|
|
1366
|
+
if (v2.fields) normalizedOptions.select = v2.fields;
|
|
1367
|
+
if (v2.orderBy) normalizedOptions.sort = v2.orderBy;
|
|
1368
|
+
if (v2.limit != null) normalizedOptions.top = v2.limit;
|
|
1369
|
+
if (v2.offset != null) normalizedOptions.skip = v2.offset;
|
|
1370
|
+
if (v2.aggregations) normalizedOptions.aggregations = v2.aggregations;
|
|
1371
|
+
if (v2.groupBy) normalizedOptions.groupBy = v2.groupBy;
|
|
1372
|
+
} else {
|
|
1373
|
+
Object.assign(normalizedOptions, options);
|
|
1374
|
+
}
|
|
1375
|
+
if (normalizedOptions.top) queryParams.set("top", normalizedOptions.top.toString());
|
|
1376
|
+
if (normalizedOptions.skip) queryParams.set("skip", normalizedOptions.skip.toString());
|
|
1377
|
+
if (normalizedOptions.sort) {
|
|
1378
|
+
if (Array.isArray(normalizedOptions.sort) && typeof normalizedOptions.sort[0] === "object") {
|
|
1379
|
+
queryParams.set("sort", JSON.stringify(normalizedOptions.sort));
|
|
1355
1380
|
} else {
|
|
1356
|
-
const sortVal = Array.isArray(
|
|
1381
|
+
const sortVal = Array.isArray(normalizedOptions.sort) ? normalizedOptions.sort.join(",") : normalizedOptions.sort;
|
|
1357
1382
|
queryParams.set("sort", sortVal);
|
|
1358
1383
|
}
|
|
1359
1384
|
}
|
|
1360
|
-
if (
|
|
1361
|
-
queryParams.set("select",
|
|
1385
|
+
if (normalizedOptions.select) {
|
|
1386
|
+
queryParams.set("select", normalizedOptions.select.join(","));
|
|
1362
1387
|
}
|
|
1363
|
-
const filterValue =
|
|
1388
|
+
const filterValue = normalizedOptions.filter ?? normalizedOptions.filters;
|
|
1364
1389
|
if (filterValue) {
|
|
1365
1390
|
if (this.isFilterAST(filterValue) || Array.isArray(filterValue)) {
|
|
1366
1391
|
queryParams.set("filter", JSON.stringify(filterValue));
|
|
@@ -1372,11 +1397,11 @@ var ObjectStackClient = class {
|
|
|
1372
1397
|
});
|
|
1373
1398
|
}
|
|
1374
1399
|
}
|
|
1375
|
-
if (
|
|
1376
|
-
queryParams.set("aggregations", JSON.stringify(
|
|
1400
|
+
if (normalizedOptions.aggregations) {
|
|
1401
|
+
queryParams.set("aggregations", JSON.stringify(normalizedOptions.aggregations));
|
|
1377
1402
|
}
|
|
1378
|
-
if (
|
|
1379
|
-
queryParams.set("groupBy",
|
|
1403
|
+
if (normalizedOptions.groupBy) {
|
|
1404
|
+
queryParams.set("groupBy", normalizedOptions.groupBy.join(","));
|
|
1380
1405
|
}
|
|
1381
1406
|
const res = await this.fetch(`${this.baseUrl}${route}/${object}?${queryParams.toString()}`);
|
|
1382
1407
|
return this.unwrapResponse(res);
|
|
@@ -1496,8 +1521,8 @@ var ObjectStackClient = class {
|
|
|
1496
1521
|
this.logger.debug("Standard discovery probe failed", { error: e.message });
|
|
1497
1522
|
}
|
|
1498
1523
|
if (!data) {
|
|
1499
|
-
const fallbackUrl = `${this.baseUrl}/api/v1`;
|
|
1500
|
-
this.logger.debug("Falling back to
|
|
1524
|
+
const fallbackUrl = `${this.baseUrl}/api/v1/discovery`;
|
|
1525
|
+
this.logger.debug("Falling back to standard discovery endpoint", { url: fallbackUrl });
|
|
1501
1526
|
const res = await this.fetchImpl(fallbackUrl);
|
|
1502
1527
|
if (!res.ok) {
|
|
1503
1528
|
throw new Error(`Failed to connect to ${fallbackUrl}: ${res.statusText}`);
|
|
@@ -1524,9 +1549,19 @@ var ObjectStackClient = class {
|
|
|
1524
1549
|
* Well-known capability flags discovered from the server.
|
|
1525
1550
|
* Returns undefined if the client has not yet connected or the server
|
|
1526
1551
|
* did not include capabilities in its discovery response.
|
|
1552
|
+
*
|
|
1553
|
+
* The server may return capabilities in hierarchical format
|
|
1554
|
+
* `{ key: { enabled: boolean } }` or flat boolean format `{ key: boolean }`.
|
|
1555
|
+
* This getter normalizes both to flat `WellKnownCapabilities`.
|
|
1527
1556
|
*/
|
|
1528
1557
|
get capabilities() {
|
|
1529
|
-
|
|
1558
|
+
const raw = this.discoveryInfo?.capabilities;
|
|
1559
|
+
if (!raw) return void 0;
|
|
1560
|
+
const result = {};
|
|
1561
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
1562
|
+
result[key] = typeof value === "object" && value !== null ? !!value.enabled : !!value;
|
|
1563
|
+
}
|
|
1564
|
+
return result;
|
|
1530
1565
|
}
|
|
1531
1566
|
/**
|
|
1532
1567
|
* Private Helpers
|
|
@@ -1598,12 +1633,16 @@ var ObjectStackClient = class {
|
|
|
1598
1633
|
* ObjectStack uses standard conventions: /api/v1/data, /api/v1/meta, /api/v1/ui
|
|
1599
1634
|
*/
|
|
1600
1635
|
getRoute(type) {
|
|
1601
|
-
|
|
1602
|
-
|
|
1636
|
+
const routes = this.discoveryInfo?.routes;
|
|
1637
|
+
if (routes) {
|
|
1638
|
+
const key = type;
|
|
1639
|
+
const discovered = routes[key];
|
|
1640
|
+
if (discovered) return discovered;
|
|
1603
1641
|
}
|
|
1604
1642
|
const routeMap = {
|
|
1605
1643
|
data: "/api/v1/data",
|
|
1606
1644
|
metadata: "/api/v1/meta",
|
|
1645
|
+
discovery: "/api/v1/discovery",
|
|
1607
1646
|
ui: "/api/v1/ui",
|
|
1608
1647
|
auth: "/api/v1/auth",
|
|
1609
1648
|
analytics: "/api/v1/analytics",
|
|
@@ -1617,7 +1656,8 @@ var ObjectStackClient = class {
|
|
|
1617
1656
|
notifications: "/api/v1/notifications",
|
|
1618
1657
|
ai: "/api/v1/ai",
|
|
1619
1658
|
i18n: "/api/v1/i18n",
|
|
1620
|
-
feed: "/api/v1/
|
|
1659
|
+
feed: "/api/v1/feed",
|
|
1660
|
+
graphql: "/graphql"
|
|
1621
1661
|
};
|
|
1622
1662
|
return routeMap[type] || `/api/v1/${type}`;
|
|
1623
1663
|
}
|