@object-ui/data-objectstack 7.0.0 → 7.2.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/CHANGELOG.md +19 -0
- package/dist/index.cjs +46 -7
- package/dist/index.d.cts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +46 -7
- package/package.json +3 -3
- package/src/getObjectSchema.test.ts +77 -0
- package/src/index.ts +63 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @object-ui/data-objectstack
|
|
2
2
|
|
|
3
|
+
## 7.2.0
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [d23db5c]
|
|
8
|
+
- @object-ui/types@7.2.0
|
|
9
|
+
- @object-ui/core@7.2.0
|
|
10
|
+
|
|
11
|
+
## 7.1.0
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Updated dependencies [677f7ed]
|
|
16
|
+
- Updated dependencies [08c47da]
|
|
17
|
+
- Updated dependencies [a71be60]
|
|
18
|
+
- Updated dependencies [cb03bc3]
|
|
19
|
+
- @object-ui/types@7.1.0
|
|
20
|
+
- @object-ui/core@7.1.0
|
|
21
|
+
|
|
3
22
|
## 7.0.0
|
|
4
23
|
|
|
5
24
|
### Minor Changes
|
package/dist/index.cjs
CHANGED
|
@@ -2175,13 +2175,10 @@ var ObjectStackAdapter = class {
|
|
|
2175
2175
|
async getObjectSchema(objectName) {
|
|
2176
2176
|
await this.connect();
|
|
2177
2177
|
try {
|
|
2178
|
-
const schema = await this.metadataCache.get(
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
}
|
|
2183
|
-
return result;
|
|
2184
|
-
});
|
|
2178
|
+
const schema = await this.metadataCache.get(
|
|
2179
|
+
objectName,
|
|
2180
|
+
() => this.fetchObjectSchemaFresh(objectName)
|
|
2181
|
+
);
|
|
2185
2182
|
return schema;
|
|
2186
2183
|
} catch (error) {
|
|
2187
2184
|
const errorObj = error;
|
|
@@ -2194,6 +2191,48 @@ var ObjectStackAdapter = class {
|
|
|
2194
2191
|
throw createErrorFromResponse(errorObj, `getObjectSchema(${objectName})`);
|
|
2195
2192
|
}
|
|
2196
2193
|
}
|
|
2194
|
+
/**
|
|
2195
|
+
* Fetch a single object's schema while always revalidating the browser cache.
|
|
2196
|
+
*
|
|
2197
|
+
* The server serves `GET /api/v1/meta/object/:name` with
|
|
2198
|
+
* `Cache-Control: public, max-age=3600`, so the default `fetch` the SDK uses
|
|
2199
|
+
* keeps returning the same response from the browser HTTP cache for up to an
|
|
2200
|
+
* hour without contacting the origin. Because the create/edit form reads the
|
|
2201
|
+
* object schema through {@link getObjectSchema}, a field added + published in
|
|
2202
|
+
* the same session never appears in the form even though it is live (the LIST
|
|
2203
|
+
* endpoint, `/meta/object`, is uncached — which is why list views update).
|
|
2204
|
+
*
|
|
2205
|
+
* Issuing the read with `cache: 'no-cache'` forces a conditional revalidation
|
|
2206
|
+
* (`If-None-Match`): a changed ETag returns the fresh schema, an unchanged one
|
|
2207
|
+
* still gets a cheap `304`. We go through `fetchImpl` (the adapter's
|
|
2208
|
+
* authenticated fetch) rather than `client.meta.getItem` because the SDK does
|
|
2209
|
+
* not expose the request cache mode.
|
|
2210
|
+
*/
|
|
2211
|
+
async fetchObjectSchemaFresh(objectName) {
|
|
2212
|
+
const baseUrl = (this.baseUrl || "").replace(/\/$/, "");
|
|
2213
|
+
const hasApiVersionSuffix = /\/api\/v\d+$/i.test(baseUrl);
|
|
2214
|
+
const metaPath = hasApiVersionSuffix ? "/meta" : "/api/v1/meta";
|
|
2215
|
+
const url = `${baseUrl}${metaPath}/object/${encodeURIComponent(objectName)}`;
|
|
2216
|
+
const headers = { "Content-Type": "application/json" };
|
|
2217
|
+
if (this.token) {
|
|
2218
|
+
headers["Authorization"] = `Bearer ${this.token}`;
|
|
2219
|
+
}
|
|
2220
|
+
const res = await this.fetchImpl(url, {
|
|
2221
|
+
method: "GET",
|
|
2222
|
+
headers,
|
|
2223
|
+
// Revalidate instead of serving the stale `max-age` body (see doc above).
|
|
2224
|
+
cache: "no-cache"
|
|
2225
|
+
});
|
|
2226
|
+
if (!res.ok) {
|
|
2227
|
+
const errBody = await res.json().catch(() => ({ message: res.statusText }));
|
|
2228
|
+
const err = new Error(errBody?.error?.message || errBody?.message || res.statusText);
|
|
2229
|
+
err.status = res.status;
|
|
2230
|
+
throw err;
|
|
2231
|
+
}
|
|
2232
|
+
const body = await res.json();
|
|
2233
|
+
const data = body && typeof body === "object" && "success" in body && "data" in body ? body.data : body;
|
|
2234
|
+
return data && typeof data === "object" && "item" in data ? data.item : data;
|
|
2235
|
+
}
|
|
2197
2236
|
/**
|
|
2198
2237
|
* Get access to the underlying ObjectStack client for advanced operations.
|
|
2199
2238
|
*/
|
package/dist/index.d.cts
CHANGED
|
@@ -1456,6 +1456,24 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1456
1456
|
* @returns Promise resolving to the object schema
|
|
1457
1457
|
*/
|
|
1458
1458
|
getObjectSchema(objectName: string): Promise<unknown>;
|
|
1459
|
+
/**
|
|
1460
|
+
* Fetch a single object's schema while always revalidating the browser cache.
|
|
1461
|
+
*
|
|
1462
|
+
* The server serves `GET /api/v1/meta/object/:name` with
|
|
1463
|
+
* `Cache-Control: public, max-age=3600`, so the default `fetch` the SDK uses
|
|
1464
|
+
* keeps returning the same response from the browser HTTP cache for up to an
|
|
1465
|
+
* hour without contacting the origin. Because the create/edit form reads the
|
|
1466
|
+
* object schema through {@link getObjectSchema}, a field added + published in
|
|
1467
|
+
* the same session never appears in the form even though it is live (the LIST
|
|
1468
|
+
* endpoint, `/meta/object`, is uncached — which is why list views update).
|
|
1469
|
+
*
|
|
1470
|
+
* Issuing the read with `cache: 'no-cache'` forces a conditional revalidation
|
|
1471
|
+
* (`If-None-Match`): a changed ETag returns the fresh schema, an unchanged one
|
|
1472
|
+
* still gets a cheap `304`. We go through `fetchImpl` (the adapter's
|
|
1473
|
+
* authenticated fetch) rather than `client.meta.getItem` because the SDK does
|
|
1474
|
+
* not expose the request cache mode.
|
|
1475
|
+
*/
|
|
1476
|
+
private fetchObjectSchemaFresh;
|
|
1459
1477
|
/**
|
|
1460
1478
|
* Get access to the underlying ObjectStack client for advanced operations.
|
|
1461
1479
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -1456,6 +1456,24 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1456
1456
|
* @returns Promise resolving to the object schema
|
|
1457
1457
|
*/
|
|
1458
1458
|
getObjectSchema(objectName: string): Promise<unknown>;
|
|
1459
|
+
/**
|
|
1460
|
+
* Fetch a single object's schema while always revalidating the browser cache.
|
|
1461
|
+
*
|
|
1462
|
+
* The server serves `GET /api/v1/meta/object/:name` with
|
|
1463
|
+
* `Cache-Control: public, max-age=3600`, so the default `fetch` the SDK uses
|
|
1464
|
+
* keeps returning the same response from the browser HTTP cache for up to an
|
|
1465
|
+
* hour without contacting the origin. Because the create/edit form reads the
|
|
1466
|
+
* object schema through {@link getObjectSchema}, a field added + published in
|
|
1467
|
+
* the same session never appears in the form even though it is live (the LIST
|
|
1468
|
+
* endpoint, `/meta/object`, is uncached — which is why list views update).
|
|
1469
|
+
*
|
|
1470
|
+
* Issuing the read with `cache: 'no-cache'` forces a conditional revalidation
|
|
1471
|
+
* (`If-None-Match`): a changed ETag returns the fresh schema, an unchanged one
|
|
1472
|
+
* still gets a cheap `304`. We go through `fetchImpl` (the adapter's
|
|
1473
|
+
* authenticated fetch) rather than `client.meta.getItem` because the SDK does
|
|
1474
|
+
* not expose the request cache mode.
|
|
1475
|
+
*/
|
|
1476
|
+
private fetchObjectSchemaFresh;
|
|
1459
1477
|
/**
|
|
1460
1478
|
* Get access to the underlying ObjectStack client for advanced operations.
|
|
1461
1479
|
*/
|
package/dist/index.js
CHANGED
|
@@ -2127,13 +2127,10 @@ var ObjectStackAdapter = class {
|
|
|
2127
2127
|
async getObjectSchema(objectName) {
|
|
2128
2128
|
await this.connect();
|
|
2129
2129
|
try {
|
|
2130
|
-
const schema = await this.metadataCache.get(
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
}
|
|
2135
|
-
return result;
|
|
2136
|
-
});
|
|
2130
|
+
const schema = await this.metadataCache.get(
|
|
2131
|
+
objectName,
|
|
2132
|
+
() => this.fetchObjectSchemaFresh(objectName)
|
|
2133
|
+
);
|
|
2137
2134
|
return schema;
|
|
2138
2135
|
} catch (error) {
|
|
2139
2136
|
const errorObj = error;
|
|
@@ -2146,6 +2143,48 @@ var ObjectStackAdapter = class {
|
|
|
2146
2143
|
throw createErrorFromResponse(errorObj, `getObjectSchema(${objectName})`);
|
|
2147
2144
|
}
|
|
2148
2145
|
}
|
|
2146
|
+
/**
|
|
2147
|
+
* Fetch a single object's schema while always revalidating the browser cache.
|
|
2148
|
+
*
|
|
2149
|
+
* The server serves `GET /api/v1/meta/object/:name` with
|
|
2150
|
+
* `Cache-Control: public, max-age=3600`, so the default `fetch` the SDK uses
|
|
2151
|
+
* keeps returning the same response from the browser HTTP cache for up to an
|
|
2152
|
+
* hour without contacting the origin. Because the create/edit form reads the
|
|
2153
|
+
* object schema through {@link getObjectSchema}, a field added + published in
|
|
2154
|
+
* the same session never appears in the form even though it is live (the LIST
|
|
2155
|
+
* endpoint, `/meta/object`, is uncached — which is why list views update).
|
|
2156
|
+
*
|
|
2157
|
+
* Issuing the read with `cache: 'no-cache'` forces a conditional revalidation
|
|
2158
|
+
* (`If-None-Match`): a changed ETag returns the fresh schema, an unchanged one
|
|
2159
|
+
* still gets a cheap `304`. We go through `fetchImpl` (the adapter's
|
|
2160
|
+
* authenticated fetch) rather than `client.meta.getItem` because the SDK does
|
|
2161
|
+
* not expose the request cache mode.
|
|
2162
|
+
*/
|
|
2163
|
+
async fetchObjectSchemaFresh(objectName) {
|
|
2164
|
+
const baseUrl = (this.baseUrl || "").replace(/\/$/, "");
|
|
2165
|
+
const hasApiVersionSuffix = /\/api\/v\d+$/i.test(baseUrl);
|
|
2166
|
+
const metaPath = hasApiVersionSuffix ? "/meta" : "/api/v1/meta";
|
|
2167
|
+
const url = `${baseUrl}${metaPath}/object/${encodeURIComponent(objectName)}`;
|
|
2168
|
+
const headers = { "Content-Type": "application/json" };
|
|
2169
|
+
if (this.token) {
|
|
2170
|
+
headers["Authorization"] = `Bearer ${this.token}`;
|
|
2171
|
+
}
|
|
2172
|
+
const res = await this.fetchImpl(url, {
|
|
2173
|
+
method: "GET",
|
|
2174
|
+
headers,
|
|
2175
|
+
// Revalidate instead of serving the stale `max-age` body (see doc above).
|
|
2176
|
+
cache: "no-cache"
|
|
2177
|
+
});
|
|
2178
|
+
if (!res.ok) {
|
|
2179
|
+
const errBody = await res.json().catch(() => ({ message: res.statusText }));
|
|
2180
|
+
const err = new Error(errBody?.error?.message || errBody?.message || res.statusText);
|
|
2181
|
+
err.status = res.status;
|
|
2182
|
+
throw err;
|
|
2183
|
+
}
|
|
2184
|
+
const body = await res.json();
|
|
2185
|
+
const data = body && typeof body === "object" && "success" in body && "data" in body ? body.data : body;
|
|
2186
|
+
return data && typeof data === "object" && "item" in data ? data.item : data;
|
|
2187
|
+
}
|
|
2149
2188
|
/**
|
|
2150
2189
|
* Get access to the underlying ObjectStack client for advanced operations.
|
|
2151
2190
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/data-objectstack",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.2.0",
|
|
4
4
|
"description": "ObjectStack Data Adapter for Object UI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@objectstack/client": "^10.0.0",
|
|
26
|
-
"@object-ui/core": "7.
|
|
27
|
-
"@object-ui/types": "7.
|
|
26
|
+
"@object-ui/core": "7.2.0",
|
|
27
|
+
"@object-ui/types": "7.2.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"tsup": "^8.5.1",
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ObjectUI
|
|
3
|
+
* Copyright (c) 2024-present ObjectStack Inc.
|
|
4
|
+
*
|
|
5
|
+
* This source code is licensed under the MIT license found in the
|
|
6
|
+
* LICENSE file in the root directory of this source tree.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
10
|
+
import { ObjectStackAdapter, clearSharedDiscoveryCache } from './index';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A fetch mock that answers discovery + records the `GET /meta/object/:name`
|
|
14
|
+
* read so we can assert how it was issued.
|
|
15
|
+
*/
|
|
16
|
+
function makeFetch(objectBody: unknown) {
|
|
17
|
+
const calls: Array<{ url: string; init?: any }> = [];
|
|
18
|
+
const fetchImpl = vi.fn(async (url: any, init?: any) => {
|
|
19
|
+
const u = String(url);
|
|
20
|
+
calls.push({ url: u, init });
|
|
21
|
+
if (u.includes('/api/v1/discovery')) {
|
|
22
|
+
return { ok: true, status: 200, statusText: 'OK', json: async () => ({ success: true, data: { version: 'v1', routes: {} } }) } as any;
|
|
23
|
+
}
|
|
24
|
+
if (u.includes('/api/v1/meta/object/')) {
|
|
25
|
+
return { ok: true, status: 200, statusText: 'OK', json: async () => objectBody } as any;
|
|
26
|
+
}
|
|
27
|
+
return { ok: true, status: 200, statusText: 'OK', json: async () => ({}) } as any;
|
|
28
|
+
});
|
|
29
|
+
return { fetchImpl, calls };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe('ObjectStackAdapter.getObjectSchema', () => {
|
|
33
|
+
beforeEach(() => clearSharedDiscoveryCache());
|
|
34
|
+
|
|
35
|
+
it('reads with cache: "no-cache" so a freshly published field is not masked by the server max-age', async () => {
|
|
36
|
+
// The server marks single-object metadata `public, max-age=3600`; without
|
|
37
|
+
// revalidation the browser HTTP cache would keep serving the pre-publish
|
|
38
|
+
// schema and the create/edit form would miss fields published this session.
|
|
39
|
+
const { fetchImpl, calls } = makeFetch({
|
|
40
|
+
name: 'leave_request',
|
|
41
|
+
fields: { employee_name: { type: 'text' }, department: { type: 'text' } },
|
|
42
|
+
});
|
|
43
|
+
const adapter = new ObjectStackAdapter({
|
|
44
|
+
baseUrl: 'http://localhost:3000',
|
|
45
|
+
token: 'tok_123',
|
|
46
|
+
autoReconnect: false,
|
|
47
|
+
fetch: fetchImpl as any,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const schema: any = await adapter.getObjectSchema('leave_request');
|
|
51
|
+
|
|
52
|
+
// Returns the (unwrapped) object schema with its fields.
|
|
53
|
+
expect(Object.keys(schema.fields)).toEqual(['employee_name', 'department']);
|
|
54
|
+
|
|
55
|
+
const get = calls.find((c) => c.url.includes('/api/v1/meta/object/'))!;
|
|
56
|
+
expect(get.url).toBe('http://localhost:3000/api/v1/meta/object/leave_request');
|
|
57
|
+
expect(get.init.method).toBe('GET');
|
|
58
|
+
// The crux of the fix: revalidate the HTTP cache instead of serving the
|
|
59
|
+
// stale `max-age` body.
|
|
60
|
+
expect(get.init.cache).toBe('no-cache');
|
|
61
|
+
expect(get.init.headers.Authorization).toBe('Bearer tok_123');
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('unwraps the { item } envelope when the server wraps the object', async () => {
|
|
65
|
+
const { fetchImpl } = makeFetch({ item: { name: 'account', fields: { x: { type: 'text' } } } });
|
|
66
|
+
const adapter = new ObjectStackAdapter({
|
|
67
|
+
baseUrl: 'http://localhost:3000',
|
|
68
|
+
autoReconnect: false,
|
|
69
|
+
fetch: fetchImpl as any,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
const schema: any = await adapter.getObjectSchema('account');
|
|
73
|
+
|
|
74
|
+
expect(schema.name).toBe('account');
|
|
75
|
+
expect(Object.keys(schema.fields)).toEqual(['x']);
|
|
76
|
+
});
|
|
77
|
+
});
|
package/src/index.ts
CHANGED
|
@@ -1296,16 +1296,16 @@ export class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1296
1296
|
// shell's locale remount (issue #1319). Keeping the key locale-free here
|
|
1297
1297
|
// means a metadata *write* still invalidates the single entry it knows
|
|
1298
1298
|
// about, without having to fan out across every cached locale.
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1299
|
+
// Read through a cache-revalidating fetch (see fetchObjectSchemaFresh):
|
|
1300
|
+
// the server marks single-object metadata `public, max-age=3600`, so a
|
|
1301
|
+
// plain fetch would keep serving the pre-publish schema from the browser
|
|
1302
|
+
// HTTP cache for up to an hour — and the create/edit form (which reads
|
|
1303
|
+
// getObjectSchema) would never show a field added + published in this
|
|
1304
|
+
// session. The list endpoint is uncached, which is why list views already
|
|
1305
|
+
// refresh on publish.
|
|
1306
|
+
const schema = await this.metadataCache.get(objectName, () =>
|
|
1307
|
+
this.fetchObjectSchemaFresh(objectName),
|
|
1308
|
+
);
|
|
1309
1309
|
|
|
1310
1310
|
return schema;
|
|
1311
1311
|
} catch (error: unknown) {
|
|
@@ -1324,6 +1324,59 @@ export class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1324
1324
|
}
|
|
1325
1325
|
}
|
|
1326
1326
|
|
|
1327
|
+
/**
|
|
1328
|
+
* Fetch a single object's schema while always revalidating the browser cache.
|
|
1329
|
+
*
|
|
1330
|
+
* The server serves `GET /api/v1/meta/object/:name` with
|
|
1331
|
+
* `Cache-Control: public, max-age=3600`, so the default `fetch` the SDK uses
|
|
1332
|
+
* keeps returning the same response from the browser HTTP cache for up to an
|
|
1333
|
+
* hour without contacting the origin. Because the create/edit form reads the
|
|
1334
|
+
* object schema through {@link getObjectSchema}, a field added + published in
|
|
1335
|
+
* the same session never appears in the form even though it is live (the LIST
|
|
1336
|
+
* endpoint, `/meta/object`, is uncached — which is why list views update).
|
|
1337
|
+
*
|
|
1338
|
+
* Issuing the read with `cache: 'no-cache'` forces a conditional revalidation
|
|
1339
|
+
* (`If-None-Match`): a changed ETag returns the fresh schema, an unchanged one
|
|
1340
|
+
* still gets a cheap `304`. We go through `fetchImpl` (the adapter's
|
|
1341
|
+
* authenticated fetch) rather than `client.meta.getItem` because the SDK does
|
|
1342
|
+
* not expose the request cache mode.
|
|
1343
|
+
*/
|
|
1344
|
+
private async fetchObjectSchemaFresh(objectName: string): Promise<unknown> {
|
|
1345
|
+
const baseUrl = (this.baseUrl || '').replace(/\/$/, '');
|
|
1346
|
+
// Avoid doubling /api/v1 when baseUrl already carries the version suffix
|
|
1347
|
+
// (mirrors rawFindWithPopulate).
|
|
1348
|
+
const hasApiVersionSuffix = /\/api\/v\d+$/i.test(baseUrl);
|
|
1349
|
+
const metaPath = hasApiVersionSuffix ? '/meta' : '/api/v1/meta';
|
|
1350
|
+
const url = `${baseUrl}${metaPath}/object/${encodeURIComponent(objectName)}`;
|
|
1351
|
+
|
|
1352
|
+
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
1353
|
+
// Bearer (server-to-server) callers configure `this.token`; cookie/console
|
|
1354
|
+
// auth is injected by `fetchImpl` (the authenticated fetch wrapper).
|
|
1355
|
+
if (this.token) {
|
|
1356
|
+
headers['Authorization'] = `Bearer ${this.token}`;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
const res = await this.fetchImpl(url, {
|
|
1360
|
+
method: 'GET',
|
|
1361
|
+
headers,
|
|
1362
|
+
// Revalidate instead of serving the stale `max-age` body (see doc above).
|
|
1363
|
+
cache: 'no-cache',
|
|
1364
|
+
});
|
|
1365
|
+
|
|
1366
|
+
if (!res.ok) {
|
|
1367
|
+
const errBody: any = await res.json().catch(() => ({ message: res.statusText }));
|
|
1368
|
+
const err: any = new Error(errBody?.error?.message || errBody?.message || res.statusText);
|
|
1369
|
+
err.status = res.status;
|
|
1370
|
+
throw err;
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
const body: any = await res.json();
|
|
1374
|
+
// Unwrap defensively across server/SDK response shapes: the standard
|
|
1375
|
+
// `{ success, data }` envelope, an `{ item }` wrapper, or the bare item.
|
|
1376
|
+
const data = body && typeof body === 'object' && 'success' in body && 'data' in body ? body.data : body;
|
|
1377
|
+
return data && typeof data === 'object' && 'item' in data ? data.item : data;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1327
1380
|
/**
|
|
1328
1381
|
* Get access to the underlying ObjectStack client for advanced operations.
|
|
1329
1382
|
*/
|