@object-ui/data-objectstack 17.5.0 → 17.6.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 +219 -0
- package/dist/index.cjs +262 -357
- package/dist/index.d.cts +385 -448
- package/dist/index.d.ts +385 -448
- package/dist/index.js +258 -350
- package/package.json +5 -6
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/src/aggregate-capability.test.ts +0 -177
- package/src/apiAccessDenied.test.ts +0 -233
- package/src/appAccessProbe.test.ts +0 -147
- package/src/bulkDelete.test.ts +0 -75
- package/src/bulkUpdate.test.ts +0 -93
- package/src/cache/MetadataCache.test.ts +0 -477
- package/src/cache/MetadataCache.ts +0 -289
- package/src/cloud-surface-retired-4152.pin.test.ts +0 -171
- package/src/connection.test.ts +0 -188
- package/src/contracts.ts +0 -115
- package/src/deleteView.homes.test.ts +0 -389
- package/src/deleteViewContract.types.test.ts +0 -246
- package/src/errors.test.ts +0 -426
- package/src/errors.ts +0 -293
- package/src/exportDownload.test.ts +0 -141
- package/src/filter-entry-translation.test.ts +0 -360
- package/src/filter-operator-ast-parity.test.ts +0 -135
- package/src/getObjectSchema.test.ts +0 -145
- package/src/httpFailureLogging.test.ts +0 -109
- package/src/importJob.test.ts +0 -71
- package/src/index.ts +0 -4442
- package/src/integration.ts +0 -200
- package/src/listViewOverrides.test.ts +0 -241
- package/src/listViews.test.ts +0 -175
- package/src/metadata-client.get-envelope.test.ts +0 -171
- package/src/metadata-client.saveAdvisories.test.ts +0 -226
- package/src/metadata-client.test.ts +0 -303
- package/src/metadata-client.ts +0 -1046
- package/src/occ.test.ts +0 -101
- package/src/onMutation.test.ts +0 -589
- package/src/onSaveAdvisory.test.ts +0 -311
- package/src/onWriteWarning.test.ts +0 -273
- package/src/orderby-serialization.test.ts +0 -115
- package/src/queryDataset.test.ts +0 -543
- package/src/searchAll.test.ts +0 -120
- package/src/security.ts +0 -243
- package/src/spec-symbol-batch6.test.ts +0 -326
- package/src/studio.ts +0 -152
- package/src/updateView.draft.test.ts +0 -242
- package/src/userState.test.ts +0 -315
- package/src/userState.ts +0 -238
- package/src/v3-compat.test.ts +0 -229
- package/src/validation-error.test.ts +0 -94
- package/src/viewCacheInvalidation.pin.test.ts +0 -260
- package/src/viewInvalidationSeam.pin.test.ts +0 -162
package/dist/index.cjs
CHANGED
|
@@ -24,31 +24,29 @@ var index_exports = {};
|
|
|
24
24
|
__export(index_exports, {
|
|
25
25
|
API_ACCESS_DENIED_CODES: () => API_ACCESS_DENIED_CODES,
|
|
26
26
|
APP_PERMISSION_DENIED_CODE: () => APP_PERMISSION_DENIED_CODE,
|
|
27
|
+
AnalyticsDatasetNotFoundError: () => AnalyticsDatasetNotFoundError,
|
|
27
28
|
AnalyticsNotInstalledError: () => AnalyticsNotInstalledError,
|
|
28
29
|
AnalyticsQueryRejectedError: () => AnalyticsQueryRejectedError,
|
|
30
|
+
AnalyticsUnauthenticatedError: () => AnalyticsUnauthenticatedError,
|
|
29
31
|
AuthenticationError: () => AuthenticationError,
|
|
30
32
|
BulkOperationError: () => BulkOperationError,
|
|
31
33
|
ConcurrentUpdateError: () => ConcurrentUpdateError,
|
|
32
34
|
ConnectionError: () => ConnectionError,
|
|
33
35
|
DataApiValidationError: () => DataApiValidationError,
|
|
34
36
|
FILTER_OPERATOR_ALIASES: () => FILTER_OPERATOR_ALIASES,
|
|
35
|
-
IntegrationManager: () => IntegrationManager,
|
|
36
37
|
MalformedFilterError: () => MalformedFilterError,
|
|
37
38
|
MetadataClient: () => MetadataClient,
|
|
38
39
|
MetadataNotFoundError: () => MetadataNotFoundError,
|
|
39
40
|
ObjectStackAdapter: () => ObjectStackAdapter,
|
|
40
41
|
ObjectStackError: () => ObjectStackError,
|
|
41
|
-
|
|
42
|
-
calculateAutoLayout: () => calculateAutoLayout,
|
|
42
|
+
VIEW_OVERLAY_OWNED_KEYS: () => VIEW_OVERLAY_OWNED_KEYS,
|
|
43
43
|
classifyAnalyticsFailure: () => classifyAnalyticsFailure,
|
|
44
44
|
clearSharedDiscoveryCache: () => clearSharedDiscoveryCache,
|
|
45
|
-
createDefaultCanvasConfig: () => createDefaultCanvasConfig,
|
|
46
45
|
createErrorFromResponse: () => createErrorFromResponse,
|
|
47
46
|
createObjectStackAdapter: () => createObjectStackAdapter,
|
|
48
47
|
createObjectStackUserStateAdapter: () => createObjectStackUserStateAdapter,
|
|
49
48
|
createQuietHttpLogger: () => createQuietHttpLogger,
|
|
50
49
|
formatHttpFailureMessage: () => formatHttpFailureMessage,
|
|
51
|
-
generateContractManifest: () => generateContractManifest,
|
|
52
50
|
getSharedDiscovery: () => getSharedDiscovery,
|
|
53
51
|
is404Error: () => is404Error,
|
|
54
52
|
isAnalyticsNotInstalledError: () => isAnalyticsNotInstalledError,
|
|
@@ -58,12 +56,11 @@ __export(index_exports, {
|
|
|
58
56
|
isErrorType: () => isErrorType,
|
|
59
57
|
isMalformedFilterError: () => isMalformedFilterError,
|
|
60
58
|
isObjectStackError: () => isObjectStackError,
|
|
59
|
+
narrowPersonalizationOverlay: () => narrowPersonalizationOverlay,
|
|
61
60
|
normaliseClientError: () => normaliseClientError,
|
|
62
61
|
readSaveAdvisories: () => readSaveAdvisories,
|
|
63
62
|
readTransactionalBatchCapability: () => readTransactionalBatchCapability,
|
|
64
63
|
serializeOrderBy: () => serializeOrderBy,
|
|
65
|
-
snapToGrid: () => snapToGrid,
|
|
66
|
-
validatePluginContract: () => validatePluginContract,
|
|
67
64
|
viewItemObjectName: () => viewItemObjectName
|
|
68
65
|
});
|
|
69
66
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -310,7 +307,6 @@ var MetadataClient = class _MetadataClient {
|
|
|
310
307
|
"Content-Type": "application/json"
|
|
311
308
|
};
|
|
312
309
|
if (options.ifMatch) headers["If-Match"] = options.ifMatch;
|
|
313
|
-
if (options.actor) headers["X-Actor"] = options.actor;
|
|
314
310
|
const res = await this.fetchImpl(url, {
|
|
315
311
|
method: "PUT",
|
|
316
312
|
headers,
|
|
@@ -359,13 +355,39 @@ var MetadataClient = class _MetadataClient {
|
|
|
359
355
|
return inner;
|
|
360
356
|
}
|
|
361
357
|
/**
|
|
362
|
-
* Get the 3-state layered view of a metadata item (
|
|
363
|
-
*
|
|
364
|
-
*
|
|
358
|
+
* Get the 3-state layered view of a metadata item: `code` (the packaged
|
|
359
|
+
* artifact baseline), `overlay` (the tenant customisation row alone) and
|
|
360
|
+
* `effective` (the merged value the runtime sees).
|
|
361
|
+
*
|
|
362
|
+
* Reads **`GET /meta/:type/:name/layers`** — the path the framework declares
|
|
363
|
+
* for this projection, with a response schema of its own
|
|
364
|
+
* (`GetMetaItemLayeredResponseSchema`, objectstack#5882 ruling B). It used to
|
|
365
|
+
* be reached by hanging a `layers` flag on the ordinary item read, which made
|
|
366
|
+
* one route answer two unrelated representations while `packages/spec`
|
|
367
|
+
* declared only one of them. That spelling still answers this same body
|
|
368
|
+
* inside its deprecation window (the response carries RFC 9745
|
|
369
|
+
* `Deprecation: true` and an RFC 8288 `Link: rel="successor-version"` back to
|
|
370
|
+
* this path) and is scheduled for removal upstream, so nothing here may
|
|
371
|
+
* depend on it — the repo-wide ratchet is
|
|
372
|
+
* `scripts/__tests__/layered-read-declared-path-4016.test.ts`.
|
|
373
|
+
*
|
|
374
|
+
* The request is built here rather than delegated to `@objectstack/client`
|
|
375
|
+
* because the SDK expresses no layered read in EITHER spelling: the
|
|
376
|
+
* framework's REST route ledger records this route as `server-only`,
|
|
377
|
+
* "consumed by objectui over plain HTTP", and whether the SDK should express
|
|
378
|
+
* it is an open upstream product call.
|
|
379
|
+
*
|
|
380
|
+
* One behaviour delta rides along with the path, and it is the server's
|
|
381
|
+
* choice rather than ours: the retired flag FELL THROUGH to the plain item
|
|
382
|
+
* read on a backend whose protocol implementation had no layered support,
|
|
383
|
+
* answering the `{ type, name, item }` envelope. A dedicated path refuses to
|
|
384
|
+
* answer a different resource under this one's declared shape, so it returns
|
|
385
|
+
* 501 `NOT_IMPLEMENTED` instead — which surfaces here as a thrown error
|
|
386
|
+
* rather than a view with `code` and `overlay` silently blank.
|
|
365
387
|
*/
|
|
366
388
|
async layered(type, name, options = {}) {
|
|
367
|
-
const
|
|
368
|
-
const url = `${this.base}/${encodeURIComponent(type)}/${encodeURIComponent(name)}
|
|
389
|
+
const qs = options.packageId ? `?package=${encodeURIComponent(options.packageId)}` : "";
|
|
390
|
+
const url = `${this.base}/${encodeURIComponent(type)}/${encodeURIComponent(name)}/layers${qs}`;
|
|
369
391
|
const res = await this.fetchImpl(url, { method: "GET", headers: this.headers, cache: "no-store" });
|
|
370
392
|
if (res.status === 404) {
|
|
371
393
|
return { code: null, overlay: null, overlayScope: null, effective: null };
|
|
@@ -455,7 +477,6 @@ var MetadataClient = class _MetadataClient {
|
|
|
455
477
|
const url = `${this.base}/${encodeURIComponent(type)}/${encodeURIComponent(name)}${qs}`;
|
|
456
478
|
const headers = { ...this.headers };
|
|
457
479
|
if (options.ifMatch) headers["If-Match"] = options.ifMatch;
|
|
458
|
-
if (options.actor) headers["X-Actor"] = options.actor;
|
|
459
480
|
const res = await this.fetchImpl(url, { method: "DELETE", headers });
|
|
460
481
|
if (!res.ok) throw await parseError(res);
|
|
461
482
|
return await res.json();
|
|
@@ -473,7 +494,6 @@ var MetadataClient = class _MetadataClient {
|
|
|
473
494
|
...this.headers,
|
|
474
495
|
"Content-Type": "application/json"
|
|
475
496
|
};
|
|
476
|
-
if (options.actor) headers["X-Actor"] = options.actor;
|
|
477
497
|
const res = await this.fetchImpl(url, {
|
|
478
498
|
method: "POST",
|
|
479
499
|
headers,
|
|
@@ -496,7 +516,6 @@ var MetadataClient = class _MetadataClient {
|
|
|
496
516
|
...this.headers,
|
|
497
517
|
"Content-Type": "application/json"
|
|
498
518
|
};
|
|
499
|
-
if (options.actor) headers["X-Actor"] = options.actor;
|
|
500
519
|
const res = await this.fetchImpl(url, {
|
|
501
520
|
method: "POST",
|
|
502
521
|
headers,
|
|
@@ -1097,52 +1116,6 @@ function isErrorType(error, errorClass) {
|
|
|
1097
1116
|
return error instanceof errorClass;
|
|
1098
1117
|
}
|
|
1099
1118
|
|
|
1100
|
-
// src/contracts.ts
|
|
1101
|
-
function validatePluginContract(contract) {
|
|
1102
|
-
const errors = [];
|
|
1103
|
-
const warnings = [];
|
|
1104
|
-
if (!contract.name || contract.name.trim().length === 0) {
|
|
1105
|
-
errors.push({ field: "name", message: "Plugin name is required", code: "MISSING_NAME" });
|
|
1106
|
-
}
|
|
1107
|
-
if (!contract.version || !/^\d+\.\d+\.\d+/.test(contract.version)) {
|
|
1108
|
-
errors.push({ field: "version", message: "Valid semver version is required", code: "INVALID_VERSION" });
|
|
1109
|
-
}
|
|
1110
|
-
if (!contract.exports || contract.exports.length === 0) {
|
|
1111
|
-
errors.push({ field: "exports", message: "At least one export is required", code: "NO_EXPORTS" });
|
|
1112
|
-
}
|
|
1113
|
-
if (contract.exports) {
|
|
1114
|
-
const validTypes = ["component", "hook", "utility", "provider"];
|
|
1115
|
-
for (const exp of contract.exports) {
|
|
1116
|
-
if (!exp.name) {
|
|
1117
|
-
errors.push({ field: "exports.name", message: "Export name is required", code: "MISSING_EXPORT_NAME" });
|
|
1118
|
-
}
|
|
1119
|
-
if (!validTypes.includes(exp.type)) {
|
|
1120
|
-
errors.push({ field: "exports.type", message: `Invalid export type: ${exp.type}`, code: "INVALID_EXPORT_TYPE" });
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
}
|
|
1124
|
-
if (!contract.permissions || contract.permissions.length === 0) {
|
|
1125
|
-
warnings.push("No permissions declared \u2014 plugin will have minimal access");
|
|
1126
|
-
}
|
|
1127
|
-
return { valid: errors.length === 0, errors, warnings };
|
|
1128
|
-
}
|
|
1129
|
-
function generateContractManifest(contract) {
|
|
1130
|
-
return {
|
|
1131
|
-
$schema: "https://objectui.org/schemas/plugin-contract-v1.json",
|
|
1132
|
-
name: contract.name,
|
|
1133
|
-
version: contract.version,
|
|
1134
|
-
peerDependencies: contract.peerDependencies ?? {},
|
|
1135
|
-
exports: contract.exports.map((exp) => ({
|
|
1136
|
-
name: exp.name,
|
|
1137
|
-
type: exp.type,
|
|
1138
|
-
description: exp.description ?? ""
|
|
1139
|
-
})),
|
|
1140
|
-
permissions: contract.permissions ?? [],
|
|
1141
|
-
api: contract.api ?? {},
|
|
1142
|
-
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1143
|
-
};
|
|
1144
|
-
}
|
|
1145
|
-
|
|
1146
1119
|
// src/userState.ts
|
|
1147
1120
|
var DEFAULT_RESOURCE = "sys_user_preference";
|
|
1148
1121
|
function createObjectStackUserStateAdapter(options) {
|
|
@@ -1245,267 +1218,6 @@ function createObjectStackUserStateAdapter(options) {
|
|
|
1245
1218
|
};
|
|
1246
1219
|
}
|
|
1247
1220
|
|
|
1248
|
-
// src/integration.ts
|
|
1249
|
-
var IntegrationManager = class {
|
|
1250
|
-
constructor() {
|
|
1251
|
-
__publicField(this, "integrations", /* @__PURE__ */ new Map());
|
|
1252
|
-
}
|
|
1253
|
-
/**
|
|
1254
|
-
* Register a new integration.
|
|
1255
|
-
*/
|
|
1256
|
-
register(id, config) {
|
|
1257
|
-
this.integrations.set(id, config);
|
|
1258
|
-
}
|
|
1259
|
-
/**
|
|
1260
|
-
* Remove an integration.
|
|
1261
|
-
*/
|
|
1262
|
-
unregister(id) {
|
|
1263
|
-
this.integrations.delete(id);
|
|
1264
|
-
}
|
|
1265
|
-
/**
|
|
1266
|
-
* Get all registered integrations.
|
|
1267
|
-
*/
|
|
1268
|
-
getAll() {
|
|
1269
|
-
return new Map(this.integrations);
|
|
1270
|
-
}
|
|
1271
|
-
/**
|
|
1272
|
-
* Get integrations that match a specific event.
|
|
1273
|
-
*/
|
|
1274
|
-
getForEvent(event) {
|
|
1275
|
-
return Array.from(this.integrations.values()).filter(
|
|
1276
|
-
(integration) => integration.enabled && integration.triggers?.some((t) => t.event === event)
|
|
1277
|
-
);
|
|
1278
|
-
}
|
|
1279
|
-
/**
|
|
1280
|
-
* Dispatch an event to all matching integrations.
|
|
1281
|
-
* Returns results for each integration.
|
|
1282
|
-
*/
|
|
1283
|
-
async dispatch(event, payload) {
|
|
1284
|
-
const matching = this.getForEvent(event);
|
|
1285
|
-
const results = [];
|
|
1286
|
-
for (const [id, integration] of this.integrations) {
|
|
1287
|
-
if (!matching.includes(integration)) continue;
|
|
1288
|
-
try {
|
|
1289
|
-
await this.send(integration, payload);
|
|
1290
|
-
results.push({ id, success: true });
|
|
1291
|
-
} catch (err) {
|
|
1292
|
-
results.push({ id, success: false, error: err.message });
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
return results;
|
|
1296
|
-
}
|
|
1297
|
-
/**
|
|
1298
|
-
* Send payload to a specific integration.
|
|
1299
|
-
*/
|
|
1300
|
-
async send(integration, payload) {
|
|
1301
|
-
switch (integration.provider) {
|
|
1302
|
-
case "webhook": {
|
|
1303
|
-
const cfg = integration.config;
|
|
1304
|
-
const url = cfg.url;
|
|
1305
|
-
try {
|
|
1306
|
-
const parsed = new URL(url);
|
|
1307
|
-
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
1308
|
-
throw new Error(`Invalid URL protocol: ${parsed.protocol}`);
|
|
1309
|
-
}
|
|
1310
|
-
} catch (e) {
|
|
1311
|
-
if (e instanceof TypeError) {
|
|
1312
|
-
throw new Error(`Invalid webhook URL: ${url}`);
|
|
1313
|
-
}
|
|
1314
|
-
throw e;
|
|
1315
|
-
}
|
|
1316
|
-
await fetch(url, {
|
|
1317
|
-
method: cfg.method,
|
|
1318
|
-
headers: {
|
|
1319
|
-
"Content-Type": "application/json",
|
|
1320
|
-
...cfg.headers
|
|
1321
|
-
},
|
|
1322
|
-
body: JSON.stringify(payload)
|
|
1323
|
-
});
|
|
1324
|
-
break;
|
|
1325
|
-
}
|
|
1326
|
-
case "slack": {
|
|
1327
|
-
const cfg = integration.config;
|
|
1328
|
-
const url = cfg.webhookUrl;
|
|
1329
|
-
try {
|
|
1330
|
-
const parsed = new URL(url);
|
|
1331
|
-
if (parsed.protocol !== "https:") {
|
|
1332
|
-
throw new Error(`Invalid Slack webhook URL protocol: ${parsed.protocol}`);
|
|
1333
|
-
}
|
|
1334
|
-
} catch (e) {
|
|
1335
|
-
if (e instanceof TypeError) {
|
|
1336
|
-
throw new Error(`Invalid Slack webhook URL: ${url}`);
|
|
1337
|
-
}
|
|
1338
|
-
throw e;
|
|
1339
|
-
}
|
|
1340
|
-
await fetch(url, {
|
|
1341
|
-
method: "POST",
|
|
1342
|
-
headers: { "Content-Type": "application/json" },
|
|
1343
|
-
body: JSON.stringify({
|
|
1344
|
-
channel: cfg.channel,
|
|
1345
|
-
username: cfg.username,
|
|
1346
|
-
icon_emoji: cfg.iconEmoji,
|
|
1347
|
-
text: JSON.stringify(payload)
|
|
1348
|
-
})
|
|
1349
|
-
});
|
|
1350
|
-
break;
|
|
1351
|
-
}
|
|
1352
|
-
// Email and other providers would require server-side implementation
|
|
1353
|
-
default:
|
|
1354
|
-
break;
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
};
|
|
1358
|
-
|
|
1359
|
-
// src/security.ts
|
|
1360
|
-
var SecurityManager = class {
|
|
1361
|
-
constructor(policy = {}) {
|
|
1362
|
-
__publicField(this, "policy");
|
|
1363
|
-
__publicField(this, "auditLog", []);
|
|
1364
|
-
this.policy = policy;
|
|
1365
|
-
}
|
|
1366
|
-
/**
|
|
1367
|
-
* Generate a CSP header string from the configuration.
|
|
1368
|
-
*/
|
|
1369
|
-
generateCSPHeader() {
|
|
1370
|
-
const csp = this.policy.csp;
|
|
1371
|
-
if (!csp) return "";
|
|
1372
|
-
const directives = [];
|
|
1373
|
-
if (csp.scriptSrc?.length) directives.push(`script-src ${csp.scriptSrc.join(" ")}`);
|
|
1374
|
-
if (csp.styleSrc?.length) directives.push(`style-src ${csp.styleSrc.join(" ")}`);
|
|
1375
|
-
if (csp.imgSrc?.length) directives.push(`img-src ${csp.imgSrc.join(" ")}`);
|
|
1376
|
-
if (csp.connectSrc?.length) directives.push(`connect-src ${csp.connectSrc.join(" ")}`);
|
|
1377
|
-
if (csp.fontSrc?.length) directives.push(`font-src ${csp.fontSrc.join(" ")}`);
|
|
1378
|
-
if (csp.frameSrc?.length) directives.push(`frame-src ${csp.frameSrc.join(" ")}`);
|
|
1379
|
-
if (csp.reportUri) directives.push(`report-uri ${csp.reportUri}`);
|
|
1380
|
-
return directives.join("; ");
|
|
1381
|
-
}
|
|
1382
|
-
/**
|
|
1383
|
-
* Record an audit log entry.
|
|
1384
|
-
*/
|
|
1385
|
-
recordAudit(entry) {
|
|
1386
|
-
if (!this.policy.auditLog?.enabled) return;
|
|
1387
|
-
if (this.policy.auditLog.events && !this.policy.auditLog.events.includes(entry.event)) return;
|
|
1388
|
-
const fullEntry = {
|
|
1389
|
-
...entry,
|
|
1390
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1391
|
-
};
|
|
1392
|
-
this.auditLog.push(fullEntry);
|
|
1393
|
-
const dest = this.policy.auditLog.destination ?? "console";
|
|
1394
|
-
if (dest === "console" || dest === "both") {
|
|
1395
|
-
console.info("[AUDIT]", fullEntry.event, fullEntry.userId, fullEntry.resource ?? "");
|
|
1396
|
-
}
|
|
1397
|
-
}
|
|
1398
|
-
/**
|
|
1399
|
-
* Get audit log entries.
|
|
1400
|
-
*/
|
|
1401
|
-
getAuditLog(filter) {
|
|
1402
|
-
let entries = [...this.auditLog];
|
|
1403
|
-
if (filter?.event) entries = entries.filter((e) => e.event === filter.event);
|
|
1404
|
-
if (filter?.userId) entries = entries.filter((e) => e.userId === filter.userId);
|
|
1405
|
-
if (filter?.since) entries = entries.filter((e) => e.timestamp >= filter.since);
|
|
1406
|
-
return entries;
|
|
1407
|
-
}
|
|
1408
|
-
/**
|
|
1409
|
-
* Apply data masking to a record.
|
|
1410
|
-
*/
|
|
1411
|
-
maskRecord(record, userRoles = []) {
|
|
1412
|
-
if (!this.policy.dataMasking?.rules?.length) return record;
|
|
1413
|
-
const masked = { ...record };
|
|
1414
|
-
const maskChar = this.policy.dataMasking.maskChar ?? "*";
|
|
1415
|
-
for (const rule of this.policy.dataMasking.rules) {
|
|
1416
|
-
if (!(rule.field in masked) || masked[rule.field] == null) continue;
|
|
1417
|
-
if (rule.exemptRoles?.some((role) => userRoles.includes(role))) continue;
|
|
1418
|
-
const value = String(masked[rule.field]);
|
|
1419
|
-
switch (rule.strategy) {
|
|
1420
|
-
case "full":
|
|
1421
|
-
masked[rule.field] = maskChar.repeat(value.length);
|
|
1422
|
-
break;
|
|
1423
|
-
case "partial": {
|
|
1424
|
-
const visible = rule.visibleChars ?? 4;
|
|
1425
|
-
if (value.length <= visible) {
|
|
1426
|
-
masked[rule.field] = maskChar.repeat(value.length);
|
|
1427
|
-
} else {
|
|
1428
|
-
masked[rule.field] = value.slice(0, visible) + maskChar.repeat(value.length - visible);
|
|
1429
|
-
}
|
|
1430
|
-
break;
|
|
1431
|
-
}
|
|
1432
|
-
case "hash":
|
|
1433
|
-
masked[rule.field] = `[HASHED:${simpleHash(value)}]`;
|
|
1434
|
-
break;
|
|
1435
|
-
case "redact":
|
|
1436
|
-
masked[rule.field] = "[REDACTED]";
|
|
1437
|
-
break;
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
return masked;
|
|
1441
|
-
}
|
|
1442
|
-
/**
|
|
1443
|
-
* Update the security policy.
|
|
1444
|
-
*/
|
|
1445
|
-
updatePolicy(policy) {
|
|
1446
|
-
this.policy = { ...this.policy, ...policy };
|
|
1447
|
-
}
|
|
1448
|
-
/**
|
|
1449
|
-
* Get current security policy.
|
|
1450
|
-
*/
|
|
1451
|
-
getPolicy() {
|
|
1452
|
-
return { ...this.policy };
|
|
1453
|
-
}
|
|
1454
|
-
};
|
|
1455
|
-
function simpleHash(str) {
|
|
1456
|
-
let hash = 0;
|
|
1457
|
-
for (let i = 0; i < str.length; i++) {
|
|
1458
|
-
const char = str.charCodeAt(i);
|
|
1459
|
-
hash = (hash << 5) - hash + char;
|
|
1460
|
-
hash = hash & hash;
|
|
1461
|
-
}
|
|
1462
|
-
return Math.abs(hash).toString(36);
|
|
1463
|
-
}
|
|
1464
|
-
|
|
1465
|
-
// src/studio.ts
|
|
1466
|
-
function createDefaultCanvasConfig(overrides) {
|
|
1467
|
-
return {
|
|
1468
|
-
width: 1200,
|
|
1469
|
-
height: 800,
|
|
1470
|
-
background: "grid",
|
|
1471
|
-
gridSize: 8,
|
|
1472
|
-
snapToGrid: true,
|
|
1473
|
-
zoom: {
|
|
1474
|
-
min: 0.25,
|
|
1475
|
-
max: 3,
|
|
1476
|
-
step: 0.1,
|
|
1477
|
-
current: 1
|
|
1478
|
-
},
|
|
1479
|
-
panOffset: { x: 0, y: 0 },
|
|
1480
|
-
showMinimap: false,
|
|
1481
|
-
minimapPosition: "bottom-right",
|
|
1482
|
-
...overrides
|
|
1483
|
-
};
|
|
1484
|
-
}
|
|
1485
|
-
function snapToGrid(x, y, gridSize) {
|
|
1486
|
-
return {
|
|
1487
|
-
x: Math.round(x / gridSize) * gridSize,
|
|
1488
|
-
y: Math.round(y / gridSize) * gridSize
|
|
1489
|
-
};
|
|
1490
|
-
}
|
|
1491
|
-
function calculateAutoLayout(items, canvasWidth, padding = 40, gap = 40) {
|
|
1492
|
-
const positions = [];
|
|
1493
|
-
let currentX = padding;
|
|
1494
|
-
let currentY = padding;
|
|
1495
|
-
let rowMaxHeight = 0;
|
|
1496
|
-
for (const item of items) {
|
|
1497
|
-
if (currentX + item.width + padding > canvasWidth) {
|
|
1498
|
-
currentX = padding;
|
|
1499
|
-
currentY += rowMaxHeight + gap;
|
|
1500
|
-
rowMaxHeight = 0;
|
|
1501
|
-
}
|
|
1502
|
-
positions.push({ id: item.id, x: currentX, y: currentY });
|
|
1503
|
-
currentX += item.width + gap;
|
|
1504
|
-
rowMaxHeight = Math.max(rowMaxHeight, item.height);
|
|
1505
|
-
}
|
|
1506
|
-
return positions;
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
1221
|
// src/index.ts
|
|
1510
1222
|
var FILTER_OPERATOR_ALIASES = {
|
|
1511
1223
|
equals: "=",
|
|
@@ -1533,6 +1245,15 @@ var FILTER_OPERATOR_ALIASES = {
|
|
|
1533
1245
|
notin: "nin",
|
|
1534
1246
|
nin: "nin",
|
|
1535
1247
|
contains: "contains",
|
|
1248
|
+
// Case-insensitive contains. A canonical `VIEW_FILTER_OPERATORS` member that
|
|
1249
|
+
// arrived with `@objectstack/spec` 17.1.0 (objectui#5328), and an IDENTITY row
|
|
1250
|
+
// like `contains` above: `icontains` is itself a member of
|
|
1251
|
+
// `VALID_AST_OPERATORS`, so the spelling the author writes is the spelling the
|
|
1252
|
+
// AST takes — nothing is translated and no case-sensitivity is lost.
|
|
1253
|
+
// Declared rather than left to the `?? op` fall-through on the rule this
|
|
1254
|
+
// table's test states: the AST gate accepting a spelling is not the driver
|
|
1255
|
+
// compiling it into a WHERE clause.
|
|
1256
|
+
icontains: "icontains",
|
|
1536
1257
|
not_contains: "notcontains",
|
|
1537
1258
|
notcontains: "notcontains",
|
|
1538
1259
|
starts_with: "startswith",
|
|
@@ -1693,21 +1414,83 @@ function isAppPermissionDeniedError(error) {
|
|
|
1693
1414
|
return errorCodeIs(error, APP_PERMISSION_DENIED_CODE);
|
|
1694
1415
|
}
|
|
1695
1416
|
var AnalyticsNotInstalledError = class extends Error {
|
|
1696
|
-
constructor(surface, detail) {
|
|
1417
|
+
constructor(surface, detail, serverCode) {
|
|
1697
1418
|
super(
|
|
1698
1419
|
`Analytics capability is not installed on this deployment \u2014 ${surface} is unavailable. Install @objectstack/service-analytics and mount AnalyticsServicePlugin to enable it.` + (detail ? ` (server said: ${detail})` : "")
|
|
1699
1420
|
);
|
|
1700
1421
|
__publicField(this, "code", "ANALYTICS_NOT_INSTALLED");
|
|
1701
1422
|
/** The surface that was unavailable, for the message a host renders. */
|
|
1702
1423
|
__publicField(this, "surface");
|
|
1424
|
+
/**
|
|
1425
|
+
* The server's own ADR-0112 code — the field this branch was chosen BY, when
|
|
1426
|
+
* a producer named one (`NOT_IMPLEMENTED` from the mounted route with no
|
|
1427
|
+
* service behind it, `ROUTE_NOT_FOUND` from the dispatcher when the route is
|
|
1428
|
+
* not mounted at all). Absent for the no-code residual, where a bare
|
|
1429
|
+
* transport 404/501 is all there was to read.
|
|
1430
|
+
*
|
|
1431
|
+
* Additive (objectui#5663): carried so a reader can audit that the headline
|
|
1432
|
+
* and the quoted `detail` below came off the same answer.
|
|
1433
|
+
*/
|
|
1434
|
+
__publicField(this, "serverCode");
|
|
1703
1435
|
this.name = "AnalyticsNotInstalledError";
|
|
1704
1436
|
this.surface = surface;
|
|
1437
|
+
this.serverCode = serverCode;
|
|
1705
1438
|
}
|
|
1706
1439
|
};
|
|
1707
1440
|
function isAnalyticsNotInstalledError(error) {
|
|
1708
1441
|
if (!error || typeof error !== "object") return false;
|
|
1709
1442
|
return error.code === "ANALYTICS_NOT_INSTALLED";
|
|
1710
1443
|
}
|
|
1444
|
+
var AnalyticsDatasetNotFoundError = class extends Error {
|
|
1445
|
+
constructor(opts) {
|
|
1446
|
+
const subject = opts.datasetName ? `Dataset "${opts.datasetName}"` : "The requested dataset";
|
|
1447
|
+
super(
|
|
1448
|
+
`${subject} is not defined in this environment \u2014 the app that defines it is missing or out of date. Analytics itself is installed and answering, so the fix is to upgrade the installed app, not to install a server plugin.` + (opts.serverMessage ? ` (server said: ${opts.serverMessage})` : "")
|
|
1449
|
+
);
|
|
1450
|
+
__publicField(this, "code", "ANALYTICS_DATASET_NOT_FOUND");
|
|
1451
|
+
/**
|
|
1452
|
+
* The dataset this query asked for, taken from the REQUEST rather than parsed
|
|
1453
|
+
* back out of the server's prose: the request is what we know for certain,
|
|
1454
|
+
* and re-reading the name out of a message would make the headline depend on
|
|
1455
|
+
* that message's WORDING — the coupling framework#5367 spent a whole issue
|
|
1456
|
+
* removing from the producing route.
|
|
1457
|
+
*/
|
|
1458
|
+
__publicField(this, "datasetName");
|
|
1459
|
+
/** The server's own ADR-0112 code — the field this branch was chosen BY. */
|
|
1460
|
+
__publicField(this, "serverCode");
|
|
1461
|
+
/** The server's own message, quoted verbatim in the parenthetical. */
|
|
1462
|
+
__publicField(this, "serverMessage");
|
|
1463
|
+
this.name = "AnalyticsDatasetNotFoundError";
|
|
1464
|
+
this.datasetName = opts.datasetName;
|
|
1465
|
+
this.serverCode = opts.serverCode;
|
|
1466
|
+
this.serverMessage = opts.serverMessage;
|
|
1467
|
+
}
|
|
1468
|
+
};
|
|
1469
|
+
var AnalyticsUnauthenticatedError = class extends Error {
|
|
1470
|
+
constructor(opts = {}) {
|
|
1471
|
+
super(
|
|
1472
|
+
"Analytics query refused: this session is not authenticated. Sign in again and retry \u2014 the request was refused before it ran, so it says nothing about whether the analytics capability is installed." + (opts.serverMessage ? ` (server said: ${opts.serverMessage})` : "")
|
|
1473
|
+
);
|
|
1474
|
+
__publicField(this, "code", "ANALYTICS_UNAUTHENTICATED");
|
|
1475
|
+
/** The server's own ADR-0112 code — the field this branch was chosen BY. */
|
|
1476
|
+
__publicField(this, "serverCode");
|
|
1477
|
+
/** The server's own message, quoted verbatim in the parenthetical. */
|
|
1478
|
+
__publicField(this, "serverMessage");
|
|
1479
|
+
this.name = "AnalyticsUnauthenticatedError";
|
|
1480
|
+
this.serverCode = opts.serverCode;
|
|
1481
|
+
this.serverMessage = opts.serverMessage;
|
|
1482
|
+
}
|
|
1483
|
+
};
|
|
1484
|
+
function readAnalyticsErrorEnvelope(body) {
|
|
1485
|
+
if (!body || typeof body !== "object") return {};
|
|
1486
|
+
const flat = body;
|
|
1487
|
+
const wrapped = flat.error && typeof flat.error === "object" ? flat.error : void 0;
|
|
1488
|
+
const source = wrapped ?? flat;
|
|
1489
|
+
const code = typeof source.code === "string" && source.code.length > 0 ? source.code : void 0;
|
|
1490
|
+
const message = typeof source.message === "string" && source.message.length > 0 ? source.message : typeof flat.error === "string" && flat.error.length > 0 ? flat.error : void 0;
|
|
1491
|
+
return { code, message };
|
|
1492
|
+
}
|
|
1493
|
+
var ANALYTICS_ABSENT_STATUSES = [404, 501];
|
|
1711
1494
|
var AnalyticsQueryRejectedError = class extends Error {
|
|
1712
1495
|
constructor(detail, serverCode) {
|
|
1713
1496
|
super(
|
|
@@ -1722,13 +1505,22 @@ var AnalyticsQueryRejectedError = class extends Error {
|
|
|
1722
1505
|
};
|
|
1723
1506
|
function classifyAnalyticsFailure(error) {
|
|
1724
1507
|
const err = error ?? {};
|
|
1725
|
-
const code = typeof err.code === "string" ? err.code : void 0;
|
|
1508
|
+
const code = typeof err.code === "string" && err.code.length > 0 ? err.code : void 0;
|
|
1726
1509
|
const message = typeof err.message === "string" ? err.message : void 0;
|
|
1727
1510
|
const status = typeof err.httpStatus === "number" ? err.httpStatus : typeof err.status === "number" ? err.status : typeof err.statusCode === "number" ? err.statusCode : void 0;
|
|
1728
|
-
if (code
|
|
1729
|
-
if (status === 404 || status === 501 || code === "NOT_IMPLEMENTED" || code === "ROUTE_NOT_FOUND") {
|
|
1511
|
+
if (errorCodeIsAnyOf({ code }, ["NOT_IMPLEMENTED", "ROUTE_NOT_FOUND"])) {
|
|
1730
1512
|
return { kind: "not-installed", code, message };
|
|
1731
1513
|
}
|
|
1514
|
+
if (errorCodeIs({ code }, "VALIDATION_FAILED")) return { kind: "rejected", code, message };
|
|
1515
|
+
if (errorCodeIs({ code }, "UNAUTHENTICATED")) return { kind: "unauthenticated", code, message };
|
|
1516
|
+
if (errorCodeIs({ code }, "CUBE_NOT_FOUND")) return { kind: "cube-not-found", code, message };
|
|
1517
|
+
if (code === void 0) {
|
|
1518
|
+
if (status !== void 0 && ANALYTICS_ABSENT_STATUSES.includes(status)) {
|
|
1519
|
+
return { kind: "not-installed", code, message };
|
|
1520
|
+
}
|
|
1521
|
+
if (status === 400) return { kind: "rejected", code, message };
|
|
1522
|
+
if (status === 401) return { kind: "unauthenticated", code, message };
|
|
1523
|
+
}
|
|
1732
1524
|
return { kind: "unknown", code, message };
|
|
1733
1525
|
}
|
|
1734
1526
|
var ConcurrentUpdateError = class extends Error {
|
|
@@ -1801,8 +1593,14 @@ function createQuietHttpLogger() {
|
|
|
1801
1593
|
return false;
|
|
1802
1594
|
};
|
|
1803
1595
|
const logger = {
|
|
1804
|
-
debug: (message, meta) =>
|
|
1805
|
-
|
|
1596
|
+
debug: (message, meta) => (
|
|
1597
|
+
// eslint-disable-next-line no-console -- objectui#4029, see comment above
|
|
1598
|
+
console.debug(message, meta ?? "")
|
|
1599
|
+
),
|
|
1600
|
+
info: (message, meta) => (
|
|
1601
|
+
// eslint-disable-next-line no-console -- objectui#4029, see comment above
|
|
1602
|
+
console.info(message, meta ?? "")
|
|
1603
|
+
),
|
|
1806
1604
|
warn: (message, meta) => console.warn(message, meta ?? ""),
|
|
1807
1605
|
error: (message, error, meta) => {
|
|
1808
1606
|
if (isExpected404(meta)) {
|
|
@@ -1815,6 +1613,7 @@ function createQuietHttpLogger() {
|
|
|
1815
1613
|
console.error(formatHttpFailureMessage(message, meta) ?? message, error ?? "", meta ?? "");
|
|
1816
1614
|
},
|
|
1817
1615
|
fatal: (message, error, meta) => console.error(formatHttpFailureMessage(message, meta) ?? message, error ?? "", meta ?? ""),
|
|
1616
|
+
// eslint-disable-next-line no-console -- objectui#4029, see comment above
|
|
1818
1617
|
log: (message, ...args) => console.log(message, ...args),
|
|
1819
1618
|
child: () => logger,
|
|
1820
1619
|
withTrace: () => logger
|
|
@@ -1857,6 +1656,44 @@ function viewItemObjectName(item) {
|
|
|
1857
1656
|
const spec = item?.list ?? item;
|
|
1858
1657
|
return spec?.data?.object ?? spec?.object ?? spec?.objectName;
|
|
1859
1658
|
}
|
|
1659
|
+
var VIEW_OVERLAY_MARKER = "_isOverride";
|
|
1660
|
+
function isLegacyOverlayRow(item, spec) {
|
|
1661
|
+
if (spec && spec.config && typeof spec.config === "object") return false;
|
|
1662
|
+
const viewKind = item?.viewKind ?? spec?.viewKind;
|
|
1663
|
+
return viewKind === "list";
|
|
1664
|
+
}
|
|
1665
|
+
function isPersonalizationOverlayRow(item, spec) {
|
|
1666
|
+
if (item?.[VIEW_OVERLAY_MARKER] === true) return true;
|
|
1667
|
+
if (spec?.[VIEW_OVERLAY_MARKER] === true) return true;
|
|
1668
|
+
return isLegacyOverlayRow(item, spec);
|
|
1669
|
+
}
|
|
1670
|
+
var VIEW_OVERLAY_OWNED_KEYS = Object.freeze([
|
|
1671
|
+
"rowHeight",
|
|
1672
|
+
"sort",
|
|
1673
|
+
"hiddenFields",
|
|
1674
|
+
"columnState",
|
|
1675
|
+
"inlineEdit"
|
|
1676
|
+
]);
|
|
1677
|
+
var VIEW_OVERLAY_IDENTITY_KEYS = Object.freeze([
|
|
1678
|
+
"name",
|
|
1679
|
+
"object",
|
|
1680
|
+
"viewKind",
|
|
1681
|
+
VIEW_OVERLAY_MARKER
|
|
1682
|
+
]);
|
|
1683
|
+
function narrowPersonalizationOverlay(row) {
|
|
1684
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) return row;
|
|
1685
|
+
const item = row;
|
|
1686
|
+
const spec = item.list ?? item;
|
|
1687
|
+
if (!isPersonalizationOverlayRow(item, spec)) return row;
|
|
1688
|
+
const narrowed = {};
|
|
1689
|
+
for (const key of VIEW_OVERLAY_IDENTITY_KEYS) {
|
|
1690
|
+
if (item[key] !== void 0) narrowed[key] = item[key];
|
|
1691
|
+
}
|
|
1692
|
+
for (const key of VIEW_OVERLAY_OWNED_KEYS) {
|
|
1693
|
+
if (item[key] !== void 0) narrowed[key] = item[key];
|
|
1694
|
+
}
|
|
1695
|
+
return narrowed;
|
|
1696
|
+
}
|
|
1860
1697
|
function unwrapViewDraft(resp) {
|
|
1861
1698
|
if (!resp || typeof resp !== "object") return null;
|
|
1862
1699
|
const env = resp;
|
|
@@ -3426,25 +3263,59 @@ var ObjectStackAdapter = class {
|
|
|
3426
3263
|
}
|
|
3427
3264
|
}
|
|
3428
3265
|
/**
|
|
3429
|
-
* Persist a view
|
|
3266
|
+
* Persist a toolbar-driven view config patch — density, column widths,
|
|
3267
|
+
* sort, hidden columns, inline edit. Symmetric counterpart to
|
|
3268
|
+
* {@link getView}: writes the row to the server metadata store via
|
|
3269
|
+
* `client.meta.saveItem`, then invalidates the matching cache entry so the
|
|
3270
|
+
* next {@link getView} reflects the new payload. Returns the persisted item
|
|
3271
|
+
* when the server echoes it, otherwise undefined.
|
|
3272
|
+
*
|
|
3273
|
+
* Called from exactly ONE production site — `ObjectView`'s
|
|
3274
|
+
* `persistViewPatch`, for the toolbar toggle — but that ONE call site
|
|
3275
|
+
* fires for BOTH kinds of active tab: a code-defined **system** view (no
|
|
3276
|
+
* row of its own yet) and a genuinely user-created **saved** view (already
|
|
3277
|
+
* has a row — the toggle is editing ITS OWN definition, not laying an
|
|
3278
|
+
* overlay on top of it). Which one a given call means is NOT re-derived
|
|
3279
|
+
* here from the write's shape (objectui#4227's own lesson: shape inference
|
|
3280
|
+
* on this namespace is exactly what let a system view masquerade as
|
|
3281
|
+
* saved) — the caller already knows, via the same `isSavedViewId`
|
|
3282
|
+
* classification that gates the switcher's readonly flag and its five
|
|
3283
|
+
* mutating handlers, and passes it as {@link opts.isSavedView}.
|
|
3430
3284
|
*
|
|
3431
|
-
*
|
|
3432
|
-
*
|
|
3433
|
-
*
|
|
3434
|
-
*
|
|
3435
|
-
*
|
|
3285
|
+
* - `isSavedView` false/omitted (system-view target, the common case and
|
|
3286
|
+
* the default for backward compatibility): stamps
|
|
3287
|
+
* {@link VIEW_OVERLAY_MARKER} so {@link listViews} excludes the row —
|
|
3288
|
+
* the original objectui#4227 fix.
|
|
3289
|
+
* - `isSavedView` true: the marker is withheld. Stamping it here would
|
|
3290
|
+
* flag the saved view's OWN row as a personalization overlay, and
|
|
3291
|
+
* `listViews()` would exclude it on the very next read — the user's own
|
|
3292
|
+
* view would vanish from the switcher the moment they toggled its
|
|
3293
|
+
* density (objectui#4227 follow-up, PM review on PR #4713, measured:
|
|
3294
|
+
* `persistViewPatch` has no gate on which kind of tab is active, and
|
|
3295
|
+
* this method writes to the exact same `(type='view', name=viewId)` key
|
|
3296
|
+
* {@link createView}/the ADR-0034 `viewEnvelope` seam already used for
|
|
3297
|
+
* that view, so the write is an upsert onto the saved view's row, not a
|
|
3298
|
+
* new one).
|
|
3436
3299
|
*
|
|
3437
|
-
*
|
|
3438
|
-
*
|
|
3439
|
-
*
|
|
3300
|
+
* Per objectstack#7494's ruling, the overlay this writes (system-view
|
|
3301
|
+
* case) is ORG-WIDE shared view settings, not a per-user preference — a
|
|
3302
|
+
* true per-user scope is a parked v18 direction on the platform side.
|
|
3440
3303
|
*
|
|
3441
3304
|
* @param objectName - Object name (e.g. 'lead')
|
|
3442
3305
|
* @param viewId - View identifier (e.g. 'all_leads')
|
|
3443
3306
|
* @param config - Full view definition to persist
|
|
3307
|
+
* @param opts.isSavedView - Whether `viewId` already names a saved view
|
|
3308
|
+
* (vs. a system view being personalized for the first time). Omit /
|
|
3309
|
+
* `false` for the default overlay-marking behavior.
|
|
3444
3310
|
*/
|
|
3445
|
-
async updateViewConfig(objectName, viewId, config) {
|
|
3311
|
+
async updateViewConfig(objectName, viewId, config, opts) {
|
|
3446
3312
|
await this.connect();
|
|
3447
|
-
const merged = {
|
|
3313
|
+
const merged = {
|
|
3314
|
+
...config || {},
|
|
3315
|
+
object: config?.object || objectName,
|
|
3316
|
+
name: viewId,
|
|
3317
|
+
...opts?.isSavedView ? {} : { [VIEW_OVERLAY_MARKER]: true }
|
|
3318
|
+
};
|
|
3448
3319
|
const result = await this.client.meta.saveItem(
|
|
3449
3320
|
"view",
|
|
3450
3321
|
viewId,
|
|
@@ -3483,7 +3354,9 @@ var ObjectStackAdapter = class {
|
|
|
3483
3354
|
const spec = v.list ?? v;
|
|
3484
3355
|
if (viewItemObjectName(v) !== objectName) return false;
|
|
3485
3356
|
const viewKind = v.viewKind ?? spec?.viewKind;
|
|
3486
|
-
|
|
3357
|
+
if (viewKind && FORM_FAMILY.has(viewKind)) return false;
|
|
3358
|
+
if (isPersonalizationOverlayRow(v, spec)) return false;
|
|
3359
|
+
return true;
|
|
3487
3360
|
}).map((v) => {
|
|
3488
3361
|
const spec = v.list ?? v;
|
|
3489
3362
|
const isDraft = v._draft === true || spec?._draft === true;
|
|
@@ -3758,7 +3631,7 @@ var ObjectStackAdapter = class {
|
|
|
3758
3631
|
try {
|
|
3759
3632
|
const cacheKey = `app:${appId}`;
|
|
3760
3633
|
return await this.metadataCache.get(cacheKey, async () => {
|
|
3761
|
-
const result = await this.client.meta.getItem("
|
|
3634
|
+
const result = await this.client.meta.getItem("app", appId);
|
|
3762
3635
|
if (result && result.item) return result.item;
|
|
3763
3636
|
return result ?? null;
|
|
3764
3637
|
});
|
|
@@ -3770,8 +3643,13 @@ var ObjectStackAdapter = class {
|
|
|
3770
3643
|
* Ask the by-name meta app route WHY an app is not in this session's app list
|
|
3771
3644
|
* (objectui#4252).
|
|
3772
3645
|
*
|
|
3773
|
-
* `GET /api/v1/meta
|
|
3774
|
-
*
|
|
3646
|
+
* The app LIST is the generic metadata list route `GET /api/v1/meta/:type`,
|
|
3647
|
+
* requested with the singular type segment `app` — the same address this
|
|
3648
|
+
* method appends a name to below, and the one `MetadataProvider` reads its
|
|
3649
|
+
* items from. The server filters that list per session in `filterAppForUser`
|
|
3650
|
+
* (`packages/rest/src/rest-server.ts`, applied inside the `:type` list handler
|
|
3651
|
+
* once the type segment resolves to `app`), so an app withheld by
|
|
3652
|
+
* `requiredPermissions` and an app
|
|
3775
3653
|
* that does not exist are byte-identical there: both are simply absent. A
|
|
3776
3654
|
* console reading only that list has one fact and two conditions, and it
|
|
3777
3655
|
* renders its copy for the wrong one — "it may still be publishing" over a
|
|
@@ -3821,7 +3699,7 @@ var ObjectStackAdapter = class {
|
|
|
3821
3699
|
try {
|
|
3822
3700
|
const cacheKey = `page:${pageId}`;
|
|
3823
3701
|
return await this.metadataCache.get(cacheKey, async () => {
|
|
3824
|
-
const result = await this.client.meta.getItem("
|
|
3702
|
+
const result = await this.client.meta.getItem("page", pageId);
|
|
3825
3703
|
if (result && result.item) return result.item;
|
|
3826
3704
|
return result ?? null;
|
|
3827
3705
|
});
|
|
@@ -3911,6 +3789,15 @@ var ObjectStackAdapter = class {
|
|
|
3911
3789
|
if (failure.kind === "rejected") {
|
|
3912
3790
|
throw new AnalyticsQueryRejectedError(failure.message, failure.code);
|
|
3913
3791
|
}
|
|
3792
|
+
if (failure.kind === "unauthenticated") {
|
|
3793
|
+
throw new AnalyticsUnauthenticatedError({
|
|
3794
|
+
serverCode: failure.code,
|
|
3795
|
+
serverMessage: failure.message
|
|
3796
|
+
});
|
|
3797
|
+
}
|
|
3798
|
+
if (failure.kind === "cube-not-found") {
|
|
3799
|
+
throw e;
|
|
3800
|
+
}
|
|
3914
3801
|
if (failure.kind === "not-installed") {
|
|
3915
3802
|
this.warnAnalyticsCapabilityOnce(failure.message);
|
|
3916
3803
|
}
|
|
@@ -3990,14 +3877,35 @@ var ObjectStackAdapter = class {
|
|
|
3990
3877
|
body: JSON.stringify(requestBody)
|
|
3991
3878
|
});
|
|
3992
3879
|
if (!res.ok) {
|
|
3993
|
-
let
|
|
3880
|
+
let errBody;
|
|
3994
3881
|
try {
|
|
3995
|
-
|
|
3996
|
-
detail = errBody?.message || errBody?.error || JSON.stringify(errBody);
|
|
3882
|
+
errBody = await res.json();
|
|
3997
3883
|
} catch {
|
|
3998
3884
|
}
|
|
3999
|
-
|
|
4000
|
-
|
|
3885
|
+
const { code: serverCode, message: serverMessage } = readAnalyticsErrorEnvelope(errBody);
|
|
3886
|
+
const detail = serverMessage || (errBody === void 0 ? "" : JSON.stringify(errBody));
|
|
3887
|
+
const surface = "POST /analytics/dataset/query";
|
|
3888
|
+
const requestedDatasetName = typeof dataset === "string" ? dataset : typeof dataset?.name === "string" ? String(dataset.name) : void 0;
|
|
3889
|
+
if (errorCodeIsAnyOf({ code: serverCode }, ["NOT_IMPLEMENTED", "ROUTE_NOT_FOUND"])) {
|
|
3890
|
+
throw new AnalyticsNotInstalledError(surface, serverMessage, serverCode);
|
|
3891
|
+
}
|
|
3892
|
+
if (errorCodeIs({ code: serverCode }, "NOT_FOUND")) {
|
|
3893
|
+
throw new AnalyticsDatasetNotFoundError({
|
|
3894
|
+
datasetName: requestedDatasetName,
|
|
3895
|
+
serverCode,
|
|
3896
|
+
serverMessage
|
|
3897
|
+
});
|
|
3898
|
+
}
|
|
3899
|
+
if (errorCodeIs({ code: serverCode }, "UNAUTHENTICATED")) {
|
|
3900
|
+
throw new AnalyticsUnauthenticatedError({ serverCode, serverMessage });
|
|
3901
|
+
}
|
|
3902
|
+
if (serverCode === void 0) {
|
|
3903
|
+
if (ANALYTICS_ABSENT_STATUSES.includes(res.status)) {
|
|
3904
|
+
throw new AnalyticsNotInstalledError(surface, detail || void 0);
|
|
3905
|
+
}
|
|
3906
|
+
if (res.status === 401) {
|
|
3907
|
+
throw new AnalyticsUnauthenticatedError({ serverMessage });
|
|
3908
|
+
}
|
|
4001
3909
|
}
|
|
4002
3910
|
throw new Error(`Dataset query failed: ${res.status} ${res.statusText}${detail ? ` \u2014 ${detail}` : ""}`);
|
|
4003
3911
|
}
|
|
@@ -4257,31 +4165,29 @@ function createObjectStackAdapter(config) {
|
|
|
4257
4165
|
0 && (module.exports = {
|
|
4258
4166
|
API_ACCESS_DENIED_CODES,
|
|
4259
4167
|
APP_PERMISSION_DENIED_CODE,
|
|
4168
|
+
AnalyticsDatasetNotFoundError,
|
|
4260
4169
|
AnalyticsNotInstalledError,
|
|
4261
4170
|
AnalyticsQueryRejectedError,
|
|
4171
|
+
AnalyticsUnauthenticatedError,
|
|
4262
4172
|
AuthenticationError,
|
|
4263
4173
|
BulkOperationError,
|
|
4264
4174
|
ConcurrentUpdateError,
|
|
4265
4175
|
ConnectionError,
|
|
4266
4176
|
DataApiValidationError,
|
|
4267
4177
|
FILTER_OPERATOR_ALIASES,
|
|
4268
|
-
IntegrationManager,
|
|
4269
4178
|
MalformedFilterError,
|
|
4270
4179
|
MetadataClient,
|
|
4271
4180
|
MetadataNotFoundError,
|
|
4272
4181
|
ObjectStackAdapter,
|
|
4273
4182
|
ObjectStackError,
|
|
4274
|
-
|
|
4275
|
-
calculateAutoLayout,
|
|
4183
|
+
VIEW_OVERLAY_OWNED_KEYS,
|
|
4276
4184
|
classifyAnalyticsFailure,
|
|
4277
4185
|
clearSharedDiscoveryCache,
|
|
4278
|
-
createDefaultCanvasConfig,
|
|
4279
4186
|
createErrorFromResponse,
|
|
4280
4187
|
createObjectStackAdapter,
|
|
4281
4188
|
createObjectStackUserStateAdapter,
|
|
4282
4189
|
createQuietHttpLogger,
|
|
4283
4190
|
formatHttpFailureMessage,
|
|
4284
|
-
generateContractManifest,
|
|
4285
4191
|
getSharedDiscovery,
|
|
4286
4192
|
is404Error,
|
|
4287
4193
|
isAnalyticsNotInstalledError,
|
|
@@ -4291,11 +4197,10 @@ function createObjectStackAdapter(config) {
|
|
|
4291
4197
|
isErrorType,
|
|
4292
4198
|
isMalformedFilterError,
|
|
4293
4199
|
isObjectStackError,
|
|
4200
|
+
narrowPersonalizationOverlay,
|
|
4294
4201
|
normaliseClientError,
|
|
4295
4202
|
readSaveAdvisories,
|
|
4296
4203
|
readTransactionalBatchCapability,
|
|
4297
4204
|
serializeOrderBy,
|
|
4298
|
-
snapToGrid,
|
|
4299
|
-
validatePluginContract,
|
|
4300
4205
|
viewItemObjectName
|
|
4301
4206
|
});
|