@objectstack/client 3.3.1 → 4.0.1
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.mjs
CHANGED
|
@@ -193,12 +193,20 @@ var QueryBuilder = class {
|
|
|
193
193
|
return this;
|
|
194
194
|
}
|
|
195
195
|
/**
|
|
196
|
-
* Skip records (for pagination)
|
|
196
|
+
* Skip records (for pagination).
|
|
197
|
+
* @deprecated Prefer `.offset()` for alignment with Spec canonical field names.
|
|
197
198
|
*/
|
|
198
199
|
skip(count) {
|
|
199
200
|
this.query.offset = count;
|
|
200
201
|
return this;
|
|
201
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* Offset records (for pagination) — canonical alias for `.skip()`
|
|
205
|
+
*/
|
|
206
|
+
offset(count) {
|
|
207
|
+
this.query.offset = count;
|
|
208
|
+
return this;
|
|
209
|
+
}
|
|
202
210
|
/**
|
|
203
211
|
* Paginate results
|
|
204
212
|
*/
|
|
@@ -1144,7 +1152,7 @@ var ObjectStackClient = class {
|
|
|
1144
1152
|
* List feed items for a record
|
|
1145
1153
|
*/
|
|
1146
1154
|
list: async (object, recordId, options) => {
|
|
1147
|
-
const route = this.getRoute("
|
|
1155
|
+
const route = this.getRoute("data");
|
|
1148
1156
|
const params = new URLSearchParams();
|
|
1149
1157
|
if (options?.type) params.set("type", options.type);
|
|
1150
1158
|
if (options?.limit) params.set("limit", String(options.limit));
|
|
@@ -1157,7 +1165,7 @@ var ObjectStackClient = class {
|
|
|
1157
1165
|
* Create a new feed item (comment, note, task, etc.)
|
|
1158
1166
|
*/
|
|
1159
1167
|
create: async (object, recordId, data) => {
|
|
1160
|
-
const route = this.getRoute("
|
|
1168
|
+
const route = this.getRoute("data");
|
|
1161
1169
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed`, {
|
|
1162
1170
|
method: "POST",
|
|
1163
1171
|
body: JSON.stringify(data)
|
|
@@ -1168,7 +1176,7 @@ var ObjectStackClient = class {
|
|
|
1168
1176
|
* Update an existing feed item
|
|
1169
1177
|
*/
|
|
1170
1178
|
update: async (object, recordId, feedId, data) => {
|
|
1171
|
-
const route = this.getRoute("
|
|
1179
|
+
const route = this.getRoute("data");
|
|
1172
1180
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}`, {
|
|
1173
1181
|
method: "PUT",
|
|
1174
1182
|
body: JSON.stringify(data)
|
|
@@ -1179,7 +1187,7 @@ var ObjectStackClient = class {
|
|
|
1179
1187
|
* Delete a feed item
|
|
1180
1188
|
*/
|
|
1181
1189
|
delete: async (object, recordId, feedId) => {
|
|
1182
|
-
const route = this.getRoute("
|
|
1190
|
+
const route = this.getRoute("data");
|
|
1183
1191
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}`, {
|
|
1184
1192
|
method: "DELETE"
|
|
1185
1193
|
});
|
|
@@ -1189,7 +1197,7 @@ var ObjectStackClient = class {
|
|
|
1189
1197
|
* Add an emoji reaction to a feed item
|
|
1190
1198
|
*/
|
|
1191
1199
|
addReaction: async (object, recordId, feedId, emoji) => {
|
|
1192
|
-
const route = this.getRoute("
|
|
1200
|
+
const route = this.getRoute("data");
|
|
1193
1201
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/reactions`, {
|
|
1194
1202
|
method: "POST",
|
|
1195
1203
|
body: JSON.stringify({ emoji })
|
|
@@ -1200,7 +1208,7 @@ var ObjectStackClient = class {
|
|
|
1200
1208
|
* Remove an emoji reaction from a feed item
|
|
1201
1209
|
*/
|
|
1202
1210
|
removeReaction: async (object, recordId, feedId, emoji) => {
|
|
1203
|
-
const route = this.getRoute("
|
|
1211
|
+
const route = this.getRoute("data");
|
|
1204
1212
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/reactions/${encodeURIComponent(emoji)}`, {
|
|
1205
1213
|
method: "DELETE"
|
|
1206
1214
|
});
|
|
@@ -1210,7 +1218,7 @@ var ObjectStackClient = class {
|
|
|
1210
1218
|
* Pin a feed item to the top of the timeline
|
|
1211
1219
|
*/
|
|
1212
1220
|
pin: async (object, recordId, feedId) => {
|
|
1213
|
-
const route = this.getRoute("
|
|
1221
|
+
const route = this.getRoute("data");
|
|
1214
1222
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/pin`, {
|
|
1215
1223
|
method: "POST"
|
|
1216
1224
|
});
|
|
@@ -1220,7 +1228,7 @@ var ObjectStackClient = class {
|
|
|
1220
1228
|
* Unpin a feed item
|
|
1221
1229
|
*/
|
|
1222
1230
|
unpin: async (object, recordId, feedId) => {
|
|
1223
|
-
const route = this.getRoute("
|
|
1231
|
+
const route = this.getRoute("data");
|
|
1224
1232
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/pin`, {
|
|
1225
1233
|
method: "DELETE"
|
|
1226
1234
|
});
|
|
@@ -1230,7 +1238,7 @@ var ObjectStackClient = class {
|
|
|
1230
1238
|
* Star (bookmark) a feed item
|
|
1231
1239
|
*/
|
|
1232
1240
|
star: async (object, recordId, feedId) => {
|
|
1233
|
-
const route = this.getRoute("
|
|
1241
|
+
const route = this.getRoute("data");
|
|
1234
1242
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/star`, {
|
|
1235
1243
|
method: "POST"
|
|
1236
1244
|
});
|
|
@@ -1240,7 +1248,7 @@ var ObjectStackClient = class {
|
|
|
1240
1248
|
* Unstar a feed item
|
|
1241
1249
|
*/
|
|
1242
1250
|
unstar: async (object, recordId, feedId) => {
|
|
1243
|
-
const route = this.getRoute("
|
|
1251
|
+
const route = this.getRoute("data");
|
|
1244
1252
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/feed/${encodeURIComponent(feedId)}/star`, {
|
|
1245
1253
|
method: "DELETE"
|
|
1246
1254
|
});
|
|
@@ -1250,7 +1258,7 @@ var ObjectStackClient = class {
|
|
|
1250
1258
|
* Search feed items
|
|
1251
1259
|
*/
|
|
1252
1260
|
search: async (object, recordId, query, options) => {
|
|
1253
|
-
const route = this.getRoute("
|
|
1261
|
+
const route = this.getRoute("data");
|
|
1254
1262
|
const params = new URLSearchParams();
|
|
1255
1263
|
params.set("query", query);
|
|
1256
1264
|
if (options?.type) params.set("type", options.type);
|
|
@@ -1266,7 +1274,7 @@ var ObjectStackClient = class {
|
|
|
1266
1274
|
* Get field-level changelog for a record
|
|
1267
1275
|
*/
|
|
1268
1276
|
getChangelog: async (object, recordId, options) => {
|
|
1269
|
-
const route = this.getRoute("
|
|
1277
|
+
const route = this.getRoute("data");
|
|
1270
1278
|
const params = new URLSearchParams();
|
|
1271
1279
|
if (options?.field) params.set("field", options.field);
|
|
1272
1280
|
if (options?.actorId) params.set("actorId", options.actorId);
|
|
@@ -1282,7 +1290,7 @@ var ObjectStackClient = class {
|
|
|
1282
1290
|
* Subscribe to record notifications
|
|
1283
1291
|
*/
|
|
1284
1292
|
subscribe: async (object, recordId, options) => {
|
|
1285
|
-
const route = this.getRoute("
|
|
1293
|
+
const route = this.getRoute("data");
|
|
1286
1294
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/subscribe`, {
|
|
1287
1295
|
method: "POST",
|
|
1288
1296
|
body: JSON.stringify(options || {})
|
|
@@ -1293,7 +1301,7 @@ var ObjectStackClient = class {
|
|
|
1293
1301
|
* Unsubscribe from record notifications
|
|
1294
1302
|
*/
|
|
1295
1303
|
unsubscribe: async (object, recordId) => {
|
|
1296
|
-
const route = this.getRoute("
|
|
1304
|
+
const route = this.getRoute("data");
|
|
1297
1305
|
const res = await this.fetch(`${this.baseUrl}${route}/${encodeURIComponent(object)}/${encodeURIComponent(recordId)}/subscribe`, {
|
|
1298
1306
|
method: "DELETE"
|
|
1299
1307
|
});
|
|
@@ -1316,23 +1324,40 @@ var ObjectStackClient = class {
|
|
|
1316
1324
|
});
|
|
1317
1325
|
return this.unwrapResponse(res);
|
|
1318
1326
|
},
|
|
1327
|
+
/**
|
|
1328
|
+
* @deprecated Use `data.query()` with standard QueryAST parameters instead.
|
|
1329
|
+
* This method uses legacy parameter names. Internally adapts to HTTP GET params.
|
|
1330
|
+
*/
|
|
1319
1331
|
find: async (object, options = {}) => {
|
|
1320
1332
|
const route = this.getRoute("data");
|
|
1321
1333
|
const queryParams = new URLSearchParams();
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
if (options
|
|
1325
|
-
if (
|
|
1326
|
-
|
|
1334
|
+
const v2 = options;
|
|
1335
|
+
const normalizedOptions = {};
|
|
1336
|
+
if ("where" in options || "fields" in options || "orderBy" in options || "offset" in options) {
|
|
1337
|
+
if (v2.where) normalizedOptions.filter = v2.where;
|
|
1338
|
+
if (v2.fields) normalizedOptions.select = v2.fields;
|
|
1339
|
+
if (v2.orderBy) normalizedOptions.sort = v2.orderBy;
|
|
1340
|
+
if (v2.limit != null) normalizedOptions.top = v2.limit;
|
|
1341
|
+
if (v2.offset != null) normalizedOptions.skip = v2.offset;
|
|
1342
|
+
if (v2.aggregations) normalizedOptions.aggregations = v2.aggregations;
|
|
1343
|
+
if (v2.groupBy) normalizedOptions.groupBy = v2.groupBy;
|
|
1344
|
+
} else {
|
|
1345
|
+
Object.assign(normalizedOptions, options);
|
|
1346
|
+
}
|
|
1347
|
+
if (normalizedOptions.top) queryParams.set("top", normalizedOptions.top.toString());
|
|
1348
|
+
if (normalizedOptions.skip) queryParams.set("skip", normalizedOptions.skip.toString());
|
|
1349
|
+
if (normalizedOptions.sort) {
|
|
1350
|
+
if (Array.isArray(normalizedOptions.sort) && typeof normalizedOptions.sort[0] === "object") {
|
|
1351
|
+
queryParams.set("sort", JSON.stringify(normalizedOptions.sort));
|
|
1327
1352
|
} else {
|
|
1328
|
-
const sortVal = Array.isArray(
|
|
1353
|
+
const sortVal = Array.isArray(normalizedOptions.sort) ? normalizedOptions.sort.join(",") : normalizedOptions.sort;
|
|
1329
1354
|
queryParams.set("sort", sortVal);
|
|
1330
1355
|
}
|
|
1331
1356
|
}
|
|
1332
|
-
if (
|
|
1333
|
-
queryParams.set("select",
|
|
1357
|
+
if (normalizedOptions.select) {
|
|
1358
|
+
queryParams.set("select", normalizedOptions.select.join(","));
|
|
1334
1359
|
}
|
|
1335
|
-
const filterValue =
|
|
1360
|
+
const filterValue = normalizedOptions.filter ?? normalizedOptions.filters;
|
|
1336
1361
|
if (filterValue) {
|
|
1337
1362
|
if (this.isFilterAST(filterValue) || Array.isArray(filterValue)) {
|
|
1338
1363
|
queryParams.set("filter", JSON.stringify(filterValue));
|
|
@@ -1344,11 +1369,11 @@ var ObjectStackClient = class {
|
|
|
1344
1369
|
});
|
|
1345
1370
|
}
|
|
1346
1371
|
}
|
|
1347
|
-
if (
|
|
1348
|
-
queryParams.set("aggregations", JSON.stringify(
|
|
1372
|
+
if (normalizedOptions.aggregations) {
|
|
1373
|
+
queryParams.set("aggregations", JSON.stringify(normalizedOptions.aggregations));
|
|
1349
1374
|
}
|
|
1350
|
-
if (
|
|
1351
|
-
queryParams.set("groupBy",
|
|
1375
|
+
if (normalizedOptions.groupBy) {
|
|
1376
|
+
queryParams.set("groupBy", normalizedOptions.groupBy.join(","));
|
|
1352
1377
|
}
|
|
1353
1378
|
const res = await this.fetch(`${this.baseUrl}${route}/${object}?${queryParams.toString()}`);
|
|
1354
1379
|
return this.unwrapResponse(res);
|
|
@@ -1468,8 +1493,8 @@ var ObjectStackClient = class {
|
|
|
1468
1493
|
this.logger.debug("Standard discovery probe failed", { error: e.message });
|
|
1469
1494
|
}
|
|
1470
1495
|
if (!data) {
|
|
1471
|
-
const fallbackUrl = `${this.baseUrl}/api/v1`;
|
|
1472
|
-
this.logger.debug("Falling back to
|
|
1496
|
+
const fallbackUrl = `${this.baseUrl}/api/v1/discovery`;
|
|
1497
|
+
this.logger.debug("Falling back to standard discovery endpoint", { url: fallbackUrl });
|
|
1473
1498
|
const res = await this.fetchImpl(fallbackUrl);
|
|
1474
1499
|
if (!res.ok) {
|
|
1475
1500
|
throw new Error(`Failed to connect to ${fallbackUrl}: ${res.statusText}`);
|
|
@@ -1496,9 +1521,19 @@ var ObjectStackClient = class {
|
|
|
1496
1521
|
* Well-known capability flags discovered from the server.
|
|
1497
1522
|
* Returns undefined if the client has not yet connected or the server
|
|
1498
1523
|
* did not include capabilities in its discovery response.
|
|
1524
|
+
*
|
|
1525
|
+
* The server may return capabilities in hierarchical format
|
|
1526
|
+
* `{ key: { enabled: boolean } }` or flat boolean format `{ key: boolean }`.
|
|
1527
|
+
* This getter normalizes both to flat `WellKnownCapabilities`.
|
|
1499
1528
|
*/
|
|
1500
1529
|
get capabilities() {
|
|
1501
|
-
|
|
1530
|
+
const raw = this.discoveryInfo?.capabilities;
|
|
1531
|
+
if (!raw) return void 0;
|
|
1532
|
+
const result = {};
|
|
1533
|
+
for (const [key, value] of Object.entries(raw)) {
|
|
1534
|
+
result[key] = typeof value === "object" && value !== null ? !!value.enabled : !!value;
|
|
1535
|
+
}
|
|
1536
|
+
return result;
|
|
1502
1537
|
}
|
|
1503
1538
|
/**
|
|
1504
1539
|
* Private Helpers
|
|
@@ -1570,12 +1605,16 @@ var ObjectStackClient = class {
|
|
|
1570
1605
|
* ObjectStack uses standard conventions: /api/v1/data, /api/v1/meta, /api/v1/ui
|
|
1571
1606
|
*/
|
|
1572
1607
|
getRoute(type) {
|
|
1573
|
-
|
|
1574
|
-
|
|
1608
|
+
const routes = this.discoveryInfo?.routes;
|
|
1609
|
+
if (routes) {
|
|
1610
|
+
const key = type;
|
|
1611
|
+
const discovered = routes[key];
|
|
1612
|
+
if (discovered) return discovered;
|
|
1575
1613
|
}
|
|
1576
1614
|
const routeMap = {
|
|
1577
1615
|
data: "/api/v1/data",
|
|
1578
1616
|
metadata: "/api/v1/meta",
|
|
1617
|
+
discovery: "/api/v1/discovery",
|
|
1579
1618
|
ui: "/api/v1/ui",
|
|
1580
1619
|
auth: "/api/v1/auth",
|
|
1581
1620
|
analytics: "/api/v1/analytics",
|
|
@@ -1589,7 +1628,8 @@ var ObjectStackClient = class {
|
|
|
1589
1628
|
notifications: "/api/v1/notifications",
|
|
1590
1629
|
ai: "/api/v1/ai",
|
|
1591
1630
|
i18n: "/api/v1/i18n",
|
|
1592
|
-
feed: "/api/v1/
|
|
1631
|
+
feed: "/api/v1/feed",
|
|
1632
|
+
graphql: "/graphql"
|
|
1593
1633
|
};
|
|
1594
1634
|
return routeMap[type] || `/api/v1/${type}`;
|
|
1595
1635
|
}
|