@object-ui/data-objectstack 13.1.0 → 14.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/CHANGELOG.md +82 -0
- package/dist/index.cjs +68 -0
- package/dist/index.d.cts +20 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +68 -0
- package/package.json +4 -4
- package/src/index.ts +90 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,87 @@
|
|
|
1
1
|
# @object-ui/data-objectstack
|
|
2
2
|
|
|
3
|
+
## 14.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 6a74160: Sharing-rule form: pick, don't type. Three new widget-hint field components make
|
|
8
|
+
the generic object form render pickers where an admin previously had to type
|
|
9
|
+
machine data (driven by the framework `widget` hints on `sys_sharing_rule`;
|
|
10
|
+
generalizes the `capability-multiselect` pattern). All degrade to the underlying
|
|
11
|
+
`type` renderer when a widget is unregistered.
|
|
12
|
+
|
|
13
|
+
- **`object-ref`** — choose a registered object by name (searchable `Combobox`),
|
|
14
|
+
backed by the new `DataSource.getObjects()` (`ObjectStackAdapter` lists code-
|
|
15
|
+
and DB-defined objects via `/api/v1/meta/object`), falling back to a
|
|
16
|
+
`sys_metadata` query. Stores the object's `name`.
|
|
17
|
+
- **`filter-condition`** — a visual criteria builder (`FilterBuilder`) scoped to
|
|
18
|
+
the fields of the object chosen in a sibling field (via `getObjectSchema`),
|
|
19
|
+
round-tripping the stored **MongoDB-style** FilterCondition JSON. Criteria the
|
|
20
|
+
builder can't represent (or invalid JSON) fall back to a raw-JSON editor, with
|
|
21
|
+
an always-available "Edit as JSON" toggle — nothing is hidden or lost.
|
|
22
|
+
- **`recipient-picker`** — a record picker whose target object follows a sibling
|
|
23
|
+
`recipient_type` (`user`→sys_user, `team`→sys_team, `business_unit`/
|
|
24
|
+
`unit_and_subordinates`→sys_business_unit, `position`→sys_position), storing the
|
|
25
|
+
value the evaluator matches on (a record id, or the position **name**). Resets
|
|
26
|
+
the stored id when the type changes.
|
|
27
|
+
|
|
28
|
+
Wiring: the three keys join `DATA_SOURCE_FIELD_TYPES` (form.tsx) so the form
|
|
29
|
+
threads `dataSource` + `dependentValues` to them, and `INLINE_EXCLUDED_FIELD_TYPES`
|
|
30
|
+
(they're authored in the record form, not a grid cell). `DataSource.getObjects()`
|
|
31
|
+
is optional on the interface; the ObjectStack adapter implements it.
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- Updated dependencies [443360a]
|
|
36
|
+
- Updated dependencies [86c69c3]
|
|
37
|
+
- Updated dependencies [05e56ca]
|
|
38
|
+
- Updated dependencies [6a74160]
|
|
39
|
+
- @object-ui/core@14.0.0
|
|
40
|
+
- @object-ui/types@14.0.0
|
|
41
|
+
|
|
42
|
+
## 13.2.0
|
|
43
|
+
|
|
44
|
+
### Patch Changes
|
|
45
|
+
|
|
46
|
+
- e492b9d: Permission sets — pure separation of **design** (Studio) and **assignment**
|
|
47
|
+
(Setup), per ADR-0056 / epic #2398. A `sys_permission_set` used to render its six
|
|
48
|
+
authorization facets in Setup as raw `[Object]` / JSON textareas, and only
|
|
49
|
+
objects+fields were editable in Studio; this reworks both surfaces.
|
|
50
|
+
|
|
51
|
+
**Setup (assign + read-only):**
|
|
52
|
+
- The six facets (`object_permissions`, `field_permissions`, `system_permissions`,
|
|
53
|
+
`row_level_security`, `tab_permissions`, `admin_scope`) now render read-only on
|
|
54
|
+
the `sys_permission_set` record page as a compact summary (counts, or capability
|
|
55
|
+
chips) plus a **“Design in Studio →”** deep-link into the structured editor
|
|
56
|
+
(`/apps/:appName/metadata/permission/:setName`, env scope). No `[Object]`, no
|
|
57
|
+
JSON — in the record view, inline edit, and the create/edit form. Implemented as
|
|
58
|
+
a `permission-facet-link` field widget stamped onto the six fields via the single
|
|
59
|
+
`ObjectStackAdapter.getObjectSchema` choke point and honored by DetailSection +
|
|
60
|
+
the record form.
|
|
61
|
+
- User assignment (add/remove via `sys_user_permission_set`) is surfaced directly
|
|
62
|
+
on the Setup record page.
|
|
63
|
+
|
|
64
|
+
**Studio (design every facet):** the permission matrix editor gains structured
|
|
65
|
+
editors for the facets that were JSON-only —
|
|
66
|
+
- **System Capabilities**: a multi-select over the live `sys_capability` registry
|
|
67
|
+
(scope-grouped, labelled chips).
|
|
68
|
+
- **Row-Level Security**: per-policy rows (object · operation · enabled) with CEL
|
|
69
|
+
USING/CHECK.
|
|
70
|
+
- **Tab Visibility**: per-tab `visible | hidden | default_on | default_off`.
|
|
71
|
+
- **Delegated Admin Scope**: business-unit + subtree, manage-assignments /
|
|
72
|
+
-bindings / author-env-sets toggles, and an assignable-permission-sets allowlist.
|
|
73
|
+
Assignment was moved out of the editor (it is now a Setup act) — the editor is
|
|
74
|
+
purely a design surface.
|
|
75
|
+
|
|
76
|
+
Storage/types are unchanged; editors read/write the draft’s existing parsed
|
|
77
|
+
fields and tolerate legacy JSON strings on load. Note: env-scope metadata saves of
|
|
78
|
+
these facets do not yet project onto the queryable `sys_permission_set` data
|
|
79
|
+
record the Setup summary reads, so a fresh Studio edit isn’t reflected in Setup’s
|
|
80
|
+
read-only view until the projection refreshes — tracked as a framework follow-up
|
|
81
|
+
(enforcement reads the authoritative metadata).
|
|
82
|
+
- @object-ui/types@13.2.0
|
|
83
|
+
- @object-ui/core@13.2.0
|
|
84
|
+
|
|
3
85
|
## 13.1.0
|
|
4
86
|
|
|
5
87
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -2432,6 +2432,7 @@ var ObjectStackAdapter = class {
|
|
|
2432
2432
|
objectName,
|
|
2433
2433
|
() => this.fetchObjectSchemaFresh(objectName)
|
|
2434
2434
|
);
|
|
2435
|
+
this.applyFieldWidgetOverrides(objectName, schema);
|
|
2435
2436
|
return schema;
|
|
2436
2437
|
} catch (error) {
|
|
2437
2438
|
const errorObj = error;
|
|
@@ -2444,6 +2445,44 @@ var ObjectStackAdapter = class {
|
|
|
2444
2445
|
throw createErrorFromResponse(errorObj, `getObjectSchema(${objectName})`);
|
|
2445
2446
|
}
|
|
2446
2447
|
}
|
|
2448
|
+
/**
|
|
2449
|
+
* ADR-0056 P2 (epic #2398) — stamp structured-widget hints onto platform
|
|
2450
|
+
* fields whose framework type is a storage primitive (e.g. `textarea`) but
|
|
2451
|
+
* whose authoring UX should be a structured editor. Only the render `widget`
|
|
2452
|
+
* is added; the field's `type` (the storage contract) is untouched. Applied
|
|
2453
|
+
* idempotently to the cached schema so form + detail both honor it. Widget
|
|
2454
|
+
* components are registered as `field:<widget>` in `@object-ui/fields`.
|
|
2455
|
+
*/
|
|
2456
|
+
applyFieldWidgetOverrides(objectName, schema) {
|
|
2457
|
+
const OVERRIDES = {
|
|
2458
|
+
// ADR-0056 pure model — a permission set's six authorization facets are
|
|
2459
|
+
// *designed* in Studio's structured editors and only *assigned* (to
|
|
2460
|
+
// users) in Setup. In Setup they render read-only as a summary + a
|
|
2461
|
+
// "Design in Studio →" deep-link (the `permission-facet-link` widget),
|
|
2462
|
+
// never as raw [Object]/JSON. The capability *editor* itself lives in
|
|
2463
|
+
// Studio (epic #2398 P2).
|
|
2464
|
+
sys_permission_set: {
|
|
2465
|
+
object_permissions: "permission-facet-link",
|
|
2466
|
+
field_permissions: "permission-facet-link",
|
|
2467
|
+
system_permissions: "permission-facet-link",
|
|
2468
|
+
row_level_security: "permission-facet-link",
|
|
2469
|
+
tab_permissions: "permission-facet-link",
|
|
2470
|
+
admin_scope: "permission-facet-link"
|
|
2471
|
+
}
|
|
2472
|
+
};
|
|
2473
|
+
const overrides = OVERRIDES[objectName];
|
|
2474
|
+
const fields = schema && typeof schema === "object" ? schema.fields : null;
|
|
2475
|
+
if (!overrides || !fields) return;
|
|
2476
|
+
for (const [fname, widget] of Object.entries(overrides)) {
|
|
2477
|
+
if (Array.isArray(fields)) {
|
|
2478
|
+
const f = fields.find((x) => x?.name === fname);
|
|
2479
|
+
if (f && !f.widget) f.widget = widget;
|
|
2480
|
+
} else {
|
|
2481
|
+
const f = fields[fname];
|
|
2482
|
+
if (f && !f.widget) f.widget = widget;
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
}
|
|
2447
2486
|
/**
|
|
2448
2487
|
* Fetch a single object's schema while always revalidating the browser cache.
|
|
2449
2488
|
*
|
|
@@ -2486,6 +2525,35 @@ var ObjectStackAdapter = class {
|
|
|
2486
2525
|
const data = body && typeof body === "object" && "success" in body && "data" in body ? body.data : body;
|
|
2487
2526
|
return data && typeof data === "object" && "item" in data ? data.item : data;
|
|
2488
2527
|
}
|
|
2528
|
+
/**
|
|
2529
|
+
* List every registered object (code- and DB-defined) from the metadata
|
|
2530
|
+
* registry — `GET /api/v1/meta/object`. Returns lightweight `{ name, label }`
|
|
2531
|
+
* headers for object-picker widgets (e.g. the sharing-rule `object-ref`
|
|
2532
|
+
* field). The list endpoint is uncached server-side, so no cache-busting
|
|
2533
|
+
* dance is needed. Returns `[]` on any failure so callers degrade gracefully.
|
|
2534
|
+
*/
|
|
2535
|
+
async getObjects() {
|
|
2536
|
+
try {
|
|
2537
|
+
await this.connect();
|
|
2538
|
+
const baseUrl = (this.baseUrl || "").replace(/\/$/, "");
|
|
2539
|
+
const hasApiVersionSuffix = /\/api\/v\d+$/i.test(baseUrl);
|
|
2540
|
+
const metaPath = hasApiVersionSuffix ? "/meta" : "/api/v1/meta";
|
|
2541
|
+
const url = `${baseUrl}${metaPath}/object`;
|
|
2542
|
+
const headers = { "Content-Type": "application/json" };
|
|
2543
|
+
if (this.token) headers["Authorization"] = `Bearer ${this.token}`;
|
|
2544
|
+
const res = await this.fetchImpl(url, { method: "GET", headers });
|
|
2545
|
+
if (!res.ok) return [];
|
|
2546
|
+
const body = await res.json();
|
|
2547
|
+
const data = body && typeof body === "object" && "success" in body && "data" in body ? body.data : body;
|
|
2548
|
+
const items = Array.isArray(data) ? data : Array.isArray(data?.items) ? data.items : [];
|
|
2549
|
+
return items.map((it) => ({
|
|
2550
|
+
name: String(it?.name ?? ""),
|
|
2551
|
+
label: it?.label != null ? String(it.label) : void 0
|
|
2552
|
+
})).filter((it) => it.name);
|
|
2553
|
+
} catch {
|
|
2554
|
+
return [];
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2489
2557
|
/**
|
|
2490
2558
|
* Get access to the underlying ObjectStack client for advanced operations.
|
|
2491
2559
|
*/
|
package/dist/index.d.cts
CHANGED
|
@@ -1530,6 +1530,15 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1530
1530
|
* @returns Promise resolving to the object schema
|
|
1531
1531
|
*/
|
|
1532
1532
|
getObjectSchema(objectName: string): Promise<unknown>;
|
|
1533
|
+
/**
|
|
1534
|
+
* ADR-0056 P2 (epic #2398) — stamp structured-widget hints onto platform
|
|
1535
|
+
* fields whose framework type is a storage primitive (e.g. `textarea`) but
|
|
1536
|
+
* whose authoring UX should be a structured editor. Only the render `widget`
|
|
1537
|
+
* is added; the field's `type` (the storage contract) is untouched. Applied
|
|
1538
|
+
* idempotently to the cached schema so form + detail both honor it. Widget
|
|
1539
|
+
* components are registered as `field:<widget>` in `@object-ui/fields`.
|
|
1540
|
+
*/
|
|
1541
|
+
private applyFieldWidgetOverrides;
|
|
1533
1542
|
/**
|
|
1534
1543
|
* Fetch a single object's schema while always revalidating the browser cache.
|
|
1535
1544
|
*
|
|
@@ -1548,6 +1557,17 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1548
1557
|
* not expose the request cache mode.
|
|
1549
1558
|
*/
|
|
1550
1559
|
private fetchObjectSchemaFresh;
|
|
1560
|
+
/**
|
|
1561
|
+
* List every registered object (code- and DB-defined) from the metadata
|
|
1562
|
+
* registry — `GET /api/v1/meta/object`. Returns lightweight `{ name, label }`
|
|
1563
|
+
* headers for object-picker widgets (e.g. the sharing-rule `object-ref`
|
|
1564
|
+
* field). The list endpoint is uncached server-side, so no cache-busting
|
|
1565
|
+
* dance is needed. Returns `[]` on any failure so callers degrade gracefully.
|
|
1566
|
+
*/
|
|
1567
|
+
getObjects(): Promise<Array<{
|
|
1568
|
+
name: string;
|
|
1569
|
+
label?: string;
|
|
1570
|
+
}>>;
|
|
1551
1571
|
/**
|
|
1552
1572
|
* Get access to the underlying ObjectStack client for advanced operations.
|
|
1553
1573
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -1530,6 +1530,15 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1530
1530
|
* @returns Promise resolving to the object schema
|
|
1531
1531
|
*/
|
|
1532
1532
|
getObjectSchema(objectName: string): Promise<unknown>;
|
|
1533
|
+
/**
|
|
1534
|
+
* ADR-0056 P2 (epic #2398) — stamp structured-widget hints onto platform
|
|
1535
|
+
* fields whose framework type is a storage primitive (e.g. `textarea`) but
|
|
1536
|
+
* whose authoring UX should be a structured editor. Only the render `widget`
|
|
1537
|
+
* is added; the field's `type` (the storage contract) is untouched. Applied
|
|
1538
|
+
* idempotently to the cached schema so form + detail both honor it. Widget
|
|
1539
|
+
* components are registered as `field:<widget>` in `@object-ui/fields`.
|
|
1540
|
+
*/
|
|
1541
|
+
private applyFieldWidgetOverrides;
|
|
1533
1542
|
/**
|
|
1534
1543
|
* Fetch a single object's schema while always revalidating the browser cache.
|
|
1535
1544
|
*
|
|
@@ -1548,6 +1557,17 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1548
1557
|
* not expose the request cache mode.
|
|
1549
1558
|
*/
|
|
1550
1559
|
private fetchObjectSchemaFresh;
|
|
1560
|
+
/**
|
|
1561
|
+
* List every registered object (code- and DB-defined) from the metadata
|
|
1562
|
+
* registry — `GET /api/v1/meta/object`. Returns lightweight `{ name, label }`
|
|
1563
|
+
* headers for object-picker widgets (e.g. the sharing-rule `object-ref`
|
|
1564
|
+
* field). The list endpoint is uncached server-side, so no cache-busting
|
|
1565
|
+
* dance is needed. Returns `[]` on any failure so callers degrade gracefully.
|
|
1566
|
+
*/
|
|
1567
|
+
getObjects(): Promise<Array<{
|
|
1568
|
+
name: string;
|
|
1569
|
+
label?: string;
|
|
1570
|
+
}>>;
|
|
1551
1571
|
/**
|
|
1552
1572
|
* Get access to the underlying ObjectStack client for advanced operations.
|
|
1553
1573
|
*/
|
package/dist/index.js
CHANGED
|
@@ -2384,6 +2384,7 @@ var ObjectStackAdapter = class {
|
|
|
2384
2384
|
objectName,
|
|
2385
2385
|
() => this.fetchObjectSchemaFresh(objectName)
|
|
2386
2386
|
);
|
|
2387
|
+
this.applyFieldWidgetOverrides(objectName, schema);
|
|
2387
2388
|
return schema;
|
|
2388
2389
|
} catch (error) {
|
|
2389
2390
|
const errorObj = error;
|
|
@@ -2396,6 +2397,44 @@ var ObjectStackAdapter = class {
|
|
|
2396
2397
|
throw createErrorFromResponse(errorObj, `getObjectSchema(${objectName})`);
|
|
2397
2398
|
}
|
|
2398
2399
|
}
|
|
2400
|
+
/**
|
|
2401
|
+
* ADR-0056 P2 (epic #2398) — stamp structured-widget hints onto platform
|
|
2402
|
+
* fields whose framework type is a storage primitive (e.g. `textarea`) but
|
|
2403
|
+
* whose authoring UX should be a structured editor. Only the render `widget`
|
|
2404
|
+
* is added; the field's `type` (the storage contract) is untouched. Applied
|
|
2405
|
+
* idempotently to the cached schema so form + detail both honor it. Widget
|
|
2406
|
+
* components are registered as `field:<widget>` in `@object-ui/fields`.
|
|
2407
|
+
*/
|
|
2408
|
+
applyFieldWidgetOverrides(objectName, schema) {
|
|
2409
|
+
const OVERRIDES = {
|
|
2410
|
+
// ADR-0056 pure model — a permission set's six authorization facets are
|
|
2411
|
+
// *designed* in Studio's structured editors and only *assigned* (to
|
|
2412
|
+
// users) in Setup. In Setup they render read-only as a summary + a
|
|
2413
|
+
// "Design in Studio →" deep-link (the `permission-facet-link` widget),
|
|
2414
|
+
// never as raw [Object]/JSON. The capability *editor* itself lives in
|
|
2415
|
+
// Studio (epic #2398 P2).
|
|
2416
|
+
sys_permission_set: {
|
|
2417
|
+
object_permissions: "permission-facet-link",
|
|
2418
|
+
field_permissions: "permission-facet-link",
|
|
2419
|
+
system_permissions: "permission-facet-link",
|
|
2420
|
+
row_level_security: "permission-facet-link",
|
|
2421
|
+
tab_permissions: "permission-facet-link",
|
|
2422
|
+
admin_scope: "permission-facet-link"
|
|
2423
|
+
}
|
|
2424
|
+
};
|
|
2425
|
+
const overrides = OVERRIDES[objectName];
|
|
2426
|
+
const fields = schema && typeof schema === "object" ? schema.fields : null;
|
|
2427
|
+
if (!overrides || !fields) return;
|
|
2428
|
+
for (const [fname, widget] of Object.entries(overrides)) {
|
|
2429
|
+
if (Array.isArray(fields)) {
|
|
2430
|
+
const f = fields.find((x) => x?.name === fname);
|
|
2431
|
+
if (f && !f.widget) f.widget = widget;
|
|
2432
|
+
} else {
|
|
2433
|
+
const f = fields[fname];
|
|
2434
|
+
if (f && !f.widget) f.widget = widget;
|
|
2435
|
+
}
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2399
2438
|
/**
|
|
2400
2439
|
* Fetch a single object's schema while always revalidating the browser cache.
|
|
2401
2440
|
*
|
|
@@ -2438,6 +2477,35 @@ var ObjectStackAdapter = class {
|
|
|
2438
2477
|
const data = body && typeof body === "object" && "success" in body && "data" in body ? body.data : body;
|
|
2439
2478
|
return data && typeof data === "object" && "item" in data ? data.item : data;
|
|
2440
2479
|
}
|
|
2480
|
+
/**
|
|
2481
|
+
* List every registered object (code- and DB-defined) from the metadata
|
|
2482
|
+
* registry — `GET /api/v1/meta/object`. Returns lightweight `{ name, label }`
|
|
2483
|
+
* headers for object-picker widgets (e.g. the sharing-rule `object-ref`
|
|
2484
|
+
* field). The list endpoint is uncached server-side, so no cache-busting
|
|
2485
|
+
* dance is needed. Returns `[]` on any failure so callers degrade gracefully.
|
|
2486
|
+
*/
|
|
2487
|
+
async getObjects() {
|
|
2488
|
+
try {
|
|
2489
|
+
await this.connect();
|
|
2490
|
+
const baseUrl = (this.baseUrl || "").replace(/\/$/, "");
|
|
2491
|
+
const hasApiVersionSuffix = /\/api\/v\d+$/i.test(baseUrl);
|
|
2492
|
+
const metaPath = hasApiVersionSuffix ? "/meta" : "/api/v1/meta";
|
|
2493
|
+
const url = `${baseUrl}${metaPath}/object`;
|
|
2494
|
+
const headers = { "Content-Type": "application/json" };
|
|
2495
|
+
if (this.token) headers["Authorization"] = `Bearer ${this.token}`;
|
|
2496
|
+
const res = await this.fetchImpl(url, { method: "GET", headers });
|
|
2497
|
+
if (!res.ok) return [];
|
|
2498
|
+
const body = await res.json();
|
|
2499
|
+
const data = body && typeof body === "object" && "success" in body && "data" in body ? body.data : body;
|
|
2500
|
+
const items = Array.isArray(data) ? data : Array.isArray(data?.items) ? data.items : [];
|
|
2501
|
+
return items.map((it) => ({
|
|
2502
|
+
name: String(it?.name ?? ""),
|
|
2503
|
+
label: it?.label != null ? String(it.label) : void 0
|
|
2504
|
+
})).filter((it) => it.name);
|
|
2505
|
+
} catch {
|
|
2506
|
+
return [];
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2441
2509
|
/**
|
|
2442
2510
|
* Get access to the underlying ObjectStack client for advanced operations.
|
|
2443
2511
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/data-objectstack",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "14.0.0",
|
|
4
4
|
"description": "ObjectStack Data Adapter for Object UI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -22,9 +22,9 @@
|
|
|
22
22
|
"LICENSE"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@objectstack/client": "^
|
|
26
|
-
"@object-ui/core": "
|
|
27
|
-
"@object-ui/types": "
|
|
25
|
+
"@objectstack/client": "^14.6.0",
|
|
26
|
+
"@object-ui/core": "14.0.0",
|
|
27
|
+
"@object-ui/types": "14.0.0"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"tsup": "^8.5.1",
|
package/src/index.ts
CHANGED
|
@@ -1608,7 +1608,13 @@ export class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1608
1608
|
const schema = await this.metadataCache.get(objectName, () =>
|
|
1609
1609
|
this.fetchObjectSchemaFresh(objectName),
|
|
1610
1610
|
);
|
|
1611
|
-
|
|
1611
|
+
|
|
1612
|
+
// ADR-0056 P2 (epic #2398): stamp structured-widget hints onto specific
|
|
1613
|
+
// platform fields. This is the single choke point both the record form
|
|
1614
|
+
// (ObjectForm) and the detail view (DetailView/DetailSection) read the
|
|
1615
|
+
// schema through, so one pass here reaches every edit surface.
|
|
1616
|
+
this.applyFieldWidgetOverrides(objectName, schema);
|
|
1617
|
+
|
|
1612
1618
|
return schema;
|
|
1613
1619
|
} catch (error: unknown) {
|
|
1614
1620
|
// Check if it's a 404 error
|
|
@@ -1626,6 +1632,46 @@ export class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1626
1632
|
}
|
|
1627
1633
|
}
|
|
1628
1634
|
|
|
1635
|
+
/**
|
|
1636
|
+
* ADR-0056 P2 (epic #2398) — stamp structured-widget hints onto platform
|
|
1637
|
+
* fields whose framework type is a storage primitive (e.g. `textarea`) but
|
|
1638
|
+
* whose authoring UX should be a structured editor. Only the render `widget`
|
|
1639
|
+
* is added; the field's `type` (the storage contract) is untouched. Applied
|
|
1640
|
+
* idempotently to the cached schema so form + detail both honor it. Widget
|
|
1641
|
+
* components are registered as `field:<widget>` in `@object-ui/fields`.
|
|
1642
|
+
*/
|
|
1643
|
+
private applyFieldWidgetOverrides(objectName: string, schema: unknown): void {
|
|
1644
|
+
const OVERRIDES: Record<string, Record<string, string>> = {
|
|
1645
|
+
// ADR-0056 pure model — a permission set's six authorization facets are
|
|
1646
|
+
// *designed* in Studio's structured editors and only *assigned* (to
|
|
1647
|
+
// users) in Setup. In Setup they render read-only as a summary + a
|
|
1648
|
+
// "Design in Studio →" deep-link (the `permission-facet-link` widget),
|
|
1649
|
+
// never as raw [Object]/JSON. The capability *editor* itself lives in
|
|
1650
|
+
// Studio (epic #2398 P2).
|
|
1651
|
+
sys_permission_set: {
|
|
1652
|
+
object_permissions: 'permission-facet-link',
|
|
1653
|
+
field_permissions: 'permission-facet-link',
|
|
1654
|
+
system_permissions: 'permission-facet-link',
|
|
1655
|
+
row_level_security: 'permission-facet-link',
|
|
1656
|
+
tab_permissions: 'permission-facet-link',
|
|
1657
|
+
admin_scope: 'permission-facet-link',
|
|
1658
|
+
},
|
|
1659
|
+
};
|
|
1660
|
+
const overrides = OVERRIDES[objectName];
|
|
1661
|
+
const fields =
|
|
1662
|
+
schema && typeof schema === 'object' ? (schema as { fields?: unknown }).fields : null;
|
|
1663
|
+
if (!overrides || !fields) return;
|
|
1664
|
+
for (const [fname, widget] of Object.entries(overrides)) {
|
|
1665
|
+
if (Array.isArray(fields)) {
|
|
1666
|
+
const f = fields.find((x: any) => x?.name === fname);
|
|
1667
|
+
if (f && !f.widget) f.widget = widget;
|
|
1668
|
+
} else {
|
|
1669
|
+
const f = (fields as Record<string, any>)[fname];
|
|
1670
|
+
if (f && !f.widget) f.widget = widget;
|
|
1671
|
+
}
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
|
|
1629
1675
|
/**
|
|
1630
1676
|
* Fetch a single object's schema while always revalidating the browser cache.
|
|
1631
1677
|
*
|
|
@@ -1679,6 +1725,49 @@ export class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1679
1725
|
return data && typeof data === 'object' && 'item' in data ? data.item : data;
|
|
1680
1726
|
}
|
|
1681
1727
|
|
|
1728
|
+
/**
|
|
1729
|
+
* List every registered object (code- and DB-defined) from the metadata
|
|
1730
|
+
* registry — `GET /api/v1/meta/object`. Returns lightweight `{ name, label }`
|
|
1731
|
+
* headers for object-picker widgets (e.g. the sharing-rule `object-ref`
|
|
1732
|
+
* field). The list endpoint is uncached server-side, so no cache-busting
|
|
1733
|
+
* dance is needed. Returns `[]` on any failure so callers degrade gracefully.
|
|
1734
|
+
*/
|
|
1735
|
+
async getObjects(): Promise<Array<{ name: string; label?: string }>> {
|
|
1736
|
+
try {
|
|
1737
|
+
await this.connect();
|
|
1738
|
+
const baseUrl = (this.baseUrl || '').replace(/\/$/, '');
|
|
1739
|
+
// Avoid doubling /api/v1 when baseUrl already carries the version suffix
|
|
1740
|
+
// (mirrors fetchObjectSchemaFresh).
|
|
1741
|
+
const hasApiVersionSuffix = /\/api\/v\d+$/i.test(baseUrl);
|
|
1742
|
+
const metaPath = hasApiVersionSuffix ? '/meta' : '/api/v1/meta';
|
|
1743
|
+
const url = `${baseUrl}${metaPath}/object`;
|
|
1744
|
+
|
|
1745
|
+
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
1746
|
+
if (this.token) headers['Authorization'] = `Bearer ${this.token}`;
|
|
1747
|
+
|
|
1748
|
+
const res = await this.fetchImpl(url, { method: 'GET', headers });
|
|
1749
|
+
if (!res.ok) return [];
|
|
1750
|
+
const body: any = await res.json();
|
|
1751
|
+
// Unwrap the `{ success, data }` envelope, the `{ type, items }` list
|
|
1752
|
+
// shape, or a bare array.
|
|
1753
|
+
const data =
|
|
1754
|
+
body && typeof body === 'object' && 'success' in body && 'data' in body ? body.data : body;
|
|
1755
|
+
const items: any[] = Array.isArray(data)
|
|
1756
|
+
? data
|
|
1757
|
+
: Array.isArray(data?.items)
|
|
1758
|
+
? data.items
|
|
1759
|
+
: [];
|
|
1760
|
+
return items
|
|
1761
|
+
.map((it: any) => ({
|
|
1762
|
+
name: String(it?.name ?? ''),
|
|
1763
|
+
label: it?.label != null ? String(it.label) : undefined,
|
|
1764
|
+
}))
|
|
1765
|
+
.filter((it) => it.name);
|
|
1766
|
+
} catch {
|
|
1767
|
+
return [];
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1682
1771
|
/**
|
|
1683
1772
|
* Get access to the underlying ObjectStack client for advanced operations.
|
|
1684
1773
|
*/
|