@object-ui/data-objectstack 4.0.9 → 4.0.10
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 +7 -0
- package/dist/index.cjs +108 -2
- package/dist/index.d.cts +35 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +108 -2
- package/package.json +3 -3
- package/src/index.ts +137 -2
package/CHANGELOG.md
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1586,20 +1586,126 @@ var ObjectStackAdapter = class {
|
|
|
1586
1586
|
async updateViewConfig(objectName, viewId, config) {
|
|
1587
1587
|
await this.connect();
|
|
1588
1588
|
try {
|
|
1589
|
+
const merged = { ...config || {}, object: config?.object || objectName, name: viewId };
|
|
1589
1590
|
const result = await this.client.meta.saveItem(
|
|
1590
|
-
|
|
1591
|
+
"view",
|
|
1591
1592
|
viewId,
|
|
1592
|
-
|
|
1593
|
+
merged
|
|
1593
1594
|
);
|
|
1594
1595
|
const cacheKey = `view:${objectName}:${viewId}`;
|
|
1595
1596
|
this.metadataCache.invalidate?.(cacheKey);
|
|
1596
1597
|
this.metadataCache.invalidate?.(`view-overrides:${objectName}`);
|
|
1598
|
+
this.metadataCache.invalidate?.(`views:${objectName}`);
|
|
1597
1599
|
if (result && result.item) return result.item;
|
|
1598
1600
|
return result ?? void 0;
|
|
1599
1601
|
} catch (err) {
|
|
1600
1602
|
throw err;
|
|
1601
1603
|
}
|
|
1602
1604
|
}
|
|
1605
|
+
/**
|
|
1606
|
+
* List user-created views for a given object via the metadata overlay
|
|
1607
|
+
* API (ADR-0005). Replaces the legacy `find('sys_view', {...})` path
|
|
1608
|
+
* that wrote to a physical `sys_view` table whose columns no longer
|
|
1609
|
+
* match the view spec shape.
|
|
1610
|
+
*
|
|
1611
|
+
* Returns view spec objects with their canonical `name` as identifier.
|
|
1612
|
+
* Filters by `data.object === objectName` (or top-level `object`)
|
|
1613
|
+
* client-side because the metadata index is name-only, not field-typed.
|
|
1614
|
+
*/
|
|
1615
|
+
async listViews(objectName) {
|
|
1616
|
+
await this.connect();
|
|
1617
|
+
try {
|
|
1618
|
+
const result = await this.client.meta.getItems("view");
|
|
1619
|
+
const items = Array.isArray(result?.items) ? result.items : Array.isArray(result) ? result : [];
|
|
1620
|
+
return items.filter((v) => {
|
|
1621
|
+
if (!v) return false;
|
|
1622
|
+
const spec = v.list ?? v;
|
|
1623
|
+
const obj = spec?.data?.object ?? spec?.object ?? spec?.objectName;
|
|
1624
|
+
return obj === objectName;
|
|
1625
|
+
}).map((v) => v.list ?? v);
|
|
1626
|
+
} catch (err) {
|
|
1627
|
+
console.warn("[OBJECTSTACKDataSource] listViews failed:", err);
|
|
1628
|
+
return [];
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
/**
|
|
1632
|
+
* Create a new overlay view for an object. The view's `name` is the
|
|
1633
|
+
* stable identifier — must be unique within the project scope. Returns
|
|
1634
|
+
* the persisted view spec (or undefined when the server doesn't echo).
|
|
1635
|
+
*
|
|
1636
|
+
* Generates a snake_case name if `spec.name` is not provided by appending
|
|
1637
|
+
* a short timestamp suffix to the source-name hint.
|
|
1638
|
+
*/
|
|
1639
|
+
async createView(objectName, spec) {
|
|
1640
|
+
await this.connect();
|
|
1641
|
+
let name = String(spec?.name || "").trim();
|
|
1642
|
+
if (!name) {
|
|
1643
|
+
const base = String(spec?.label || objectName || "view").toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 40) || "view";
|
|
1644
|
+
const suffix = Date.now().toString(36);
|
|
1645
|
+
name = `${base}_${suffix}`;
|
|
1646
|
+
}
|
|
1647
|
+
const fullSpec = {
|
|
1648
|
+
...spec,
|
|
1649
|
+
name,
|
|
1650
|
+
object: spec?.object || objectName,
|
|
1651
|
+
data: spec?.data || { provider: "object", object: objectName }
|
|
1652
|
+
};
|
|
1653
|
+
try {
|
|
1654
|
+
const result = await this.client.meta.saveItem("view", name, fullSpec);
|
|
1655
|
+
this.metadataCache.invalidate?.(`views:${objectName}`);
|
|
1656
|
+
if (result && result.item) return result.item;
|
|
1657
|
+
return fullSpec;
|
|
1658
|
+
} catch (err) {
|
|
1659
|
+
throw err;
|
|
1660
|
+
}
|
|
1661
|
+
}
|
|
1662
|
+
/**
|
|
1663
|
+
* Apply a partial update to an existing overlay view. Reads the current
|
|
1664
|
+
* overlay (or seeds from artifact), merges, and writes back. ADR-0005
|
|
1665
|
+
* overlay rows store the *full* view document, so partial updates require
|
|
1666
|
+
* a read-merge-write cycle.
|
|
1667
|
+
*/
|
|
1668
|
+
async updateView(objectName, viewName, partial) {
|
|
1669
|
+
await this.connect();
|
|
1670
|
+
let current = {};
|
|
1671
|
+
try {
|
|
1672
|
+
const r = await this.client.meta.getItem("view", viewName);
|
|
1673
|
+
current = r && (r.item || r) || {};
|
|
1674
|
+
if (current?.list) current = current.list;
|
|
1675
|
+
} catch {
|
|
1676
|
+
}
|
|
1677
|
+
const merged = {
|
|
1678
|
+
...current,
|
|
1679
|
+
...partial,
|
|
1680
|
+
name: viewName,
|
|
1681
|
+
object: current?.object || current?.data?.object || objectName
|
|
1682
|
+
};
|
|
1683
|
+
try {
|
|
1684
|
+
const result = await this.client.meta.saveItem("view", viewName, merged);
|
|
1685
|
+
this.metadataCache.invalidate?.(`views:${objectName}`);
|
|
1686
|
+
this.metadataCache.invalidate?.(`view:${objectName}:${viewName}`);
|
|
1687
|
+
if (result && result.item) return result.item;
|
|
1688
|
+
return merged;
|
|
1689
|
+
} catch (err) {
|
|
1690
|
+
throw err;
|
|
1691
|
+
}
|
|
1692
|
+
}
|
|
1693
|
+
/**
|
|
1694
|
+
* Delete an overlay view (reset to artifact default if one exists, or
|
|
1695
|
+
* remove entirely if it was a user-created view). Routes to
|
|
1696
|
+
* `DELETE /api/v1/meta/view/:name`.
|
|
1697
|
+
*/
|
|
1698
|
+
async deleteView(objectName, viewName) {
|
|
1699
|
+
await this.connect();
|
|
1700
|
+
try {
|
|
1701
|
+
const result = await this.client.meta.deleteItem("view", viewName);
|
|
1702
|
+
this.metadataCache.invalidate?.(`views:${objectName}`);
|
|
1703
|
+
this.metadataCache.invalidate?.(`view:${objectName}:${viewName}`);
|
|
1704
|
+
return { deleted: !!(result?.deleted ?? result?.reset ?? true) };
|
|
1705
|
+
} catch (err) {
|
|
1706
|
+
throw err;
|
|
1707
|
+
}
|
|
1708
|
+
}
|
|
1603
1709
|
/**
|
|
1604
1710
|
* Get an application definition by name or ID.
|
|
1605
1711
|
* Attempts to fetch from the server metadata API.
|
package/dist/index.d.cts
CHANGED
|
@@ -896,6 +896,41 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
896
896
|
* @param config - Full view definition to persist
|
|
897
897
|
*/
|
|
898
898
|
updateViewConfig(objectName: string, viewId: string, config: Record<string, any>): Promise<Record<string, any> | void>;
|
|
899
|
+
/**
|
|
900
|
+
* List user-created views for a given object via the metadata overlay
|
|
901
|
+
* API (ADR-0005). Replaces the legacy `find('sys_view', {...})` path
|
|
902
|
+
* that wrote to a physical `sys_view` table whose columns no longer
|
|
903
|
+
* match the view spec shape.
|
|
904
|
+
*
|
|
905
|
+
* Returns view spec objects with their canonical `name` as identifier.
|
|
906
|
+
* Filters by `data.object === objectName` (or top-level `object`)
|
|
907
|
+
* client-side because the metadata index is name-only, not field-typed.
|
|
908
|
+
*/
|
|
909
|
+
listViews(objectName: string): Promise<any[]>;
|
|
910
|
+
/**
|
|
911
|
+
* Create a new overlay view for an object. The view's `name` is the
|
|
912
|
+
* stable identifier — must be unique within the project scope. Returns
|
|
913
|
+
* the persisted view spec (or undefined when the server doesn't echo).
|
|
914
|
+
*
|
|
915
|
+
* Generates a snake_case name if `spec.name` is not provided by appending
|
|
916
|
+
* a short timestamp suffix to the source-name hint.
|
|
917
|
+
*/
|
|
918
|
+
createView(objectName: string, spec: Record<string, any>): Promise<Record<string, any> | void>;
|
|
919
|
+
/**
|
|
920
|
+
* Apply a partial update to an existing overlay view. Reads the current
|
|
921
|
+
* overlay (or seeds from artifact), merges, and writes back. ADR-0005
|
|
922
|
+
* overlay rows store the *full* view document, so partial updates require
|
|
923
|
+
* a read-merge-write cycle.
|
|
924
|
+
*/
|
|
925
|
+
updateView(objectName: string, viewName: string, partial: Record<string, any>): Promise<Record<string, any> | void>;
|
|
926
|
+
/**
|
|
927
|
+
* Delete an overlay view (reset to artifact default if one exists, or
|
|
928
|
+
* remove entirely if it was a user-created view). Routes to
|
|
929
|
+
* `DELETE /api/v1/meta/view/:name`.
|
|
930
|
+
*/
|
|
931
|
+
deleteView(objectName: string, viewName: string): Promise<{
|
|
932
|
+
deleted: boolean;
|
|
933
|
+
}>;
|
|
899
934
|
/**
|
|
900
935
|
* Get an application definition by name or ID.
|
|
901
936
|
* Attempts to fetch from the server metadata API.
|
package/dist/index.d.ts
CHANGED
|
@@ -896,6 +896,41 @@ declare class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
896
896
|
* @param config - Full view definition to persist
|
|
897
897
|
*/
|
|
898
898
|
updateViewConfig(objectName: string, viewId: string, config: Record<string, any>): Promise<Record<string, any> | void>;
|
|
899
|
+
/**
|
|
900
|
+
* List user-created views for a given object via the metadata overlay
|
|
901
|
+
* API (ADR-0005). Replaces the legacy `find('sys_view', {...})` path
|
|
902
|
+
* that wrote to a physical `sys_view` table whose columns no longer
|
|
903
|
+
* match the view spec shape.
|
|
904
|
+
*
|
|
905
|
+
* Returns view spec objects with their canonical `name` as identifier.
|
|
906
|
+
* Filters by `data.object === objectName` (or top-level `object`)
|
|
907
|
+
* client-side because the metadata index is name-only, not field-typed.
|
|
908
|
+
*/
|
|
909
|
+
listViews(objectName: string): Promise<any[]>;
|
|
910
|
+
/**
|
|
911
|
+
* Create a new overlay view for an object. The view's `name` is the
|
|
912
|
+
* stable identifier — must be unique within the project scope. Returns
|
|
913
|
+
* the persisted view spec (or undefined when the server doesn't echo).
|
|
914
|
+
*
|
|
915
|
+
* Generates a snake_case name if `spec.name` is not provided by appending
|
|
916
|
+
* a short timestamp suffix to the source-name hint.
|
|
917
|
+
*/
|
|
918
|
+
createView(objectName: string, spec: Record<string, any>): Promise<Record<string, any> | void>;
|
|
919
|
+
/**
|
|
920
|
+
* Apply a partial update to an existing overlay view. Reads the current
|
|
921
|
+
* overlay (or seeds from artifact), merges, and writes back. ADR-0005
|
|
922
|
+
* overlay rows store the *full* view document, so partial updates require
|
|
923
|
+
* a read-merge-write cycle.
|
|
924
|
+
*/
|
|
925
|
+
updateView(objectName: string, viewName: string, partial: Record<string, any>): Promise<Record<string, any> | void>;
|
|
926
|
+
/**
|
|
927
|
+
* Delete an overlay view (reset to artifact default if one exists, or
|
|
928
|
+
* remove entirely if it was a user-created view). Routes to
|
|
929
|
+
* `DELETE /api/v1/meta/view/:name`.
|
|
930
|
+
*/
|
|
931
|
+
deleteView(objectName: string, viewName: string): Promise<{
|
|
932
|
+
deleted: boolean;
|
|
933
|
+
}>;
|
|
899
934
|
/**
|
|
900
935
|
* Get an application definition by name or ID.
|
|
901
936
|
* Attempts to fetch from the server metadata API.
|
package/dist/index.js
CHANGED
|
@@ -1543,20 +1543,126 @@ var ObjectStackAdapter = class {
|
|
|
1543
1543
|
async updateViewConfig(objectName, viewId, config) {
|
|
1544
1544
|
await this.connect();
|
|
1545
1545
|
try {
|
|
1546
|
+
const merged = { ...config || {}, object: config?.object || objectName, name: viewId };
|
|
1546
1547
|
const result = await this.client.meta.saveItem(
|
|
1547
|
-
|
|
1548
|
+
"view",
|
|
1548
1549
|
viewId,
|
|
1549
|
-
|
|
1550
|
+
merged
|
|
1550
1551
|
);
|
|
1551
1552
|
const cacheKey = `view:${objectName}:${viewId}`;
|
|
1552
1553
|
this.metadataCache.invalidate?.(cacheKey);
|
|
1553
1554
|
this.metadataCache.invalidate?.(`view-overrides:${objectName}`);
|
|
1555
|
+
this.metadataCache.invalidate?.(`views:${objectName}`);
|
|
1554
1556
|
if (result && result.item) return result.item;
|
|
1555
1557
|
return result ?? void 0;
|
|
1556
1558
|
} catch (err) {
|
|
1557
1559
|
throw err;
|
|
1558
1560
|
}
|
|
1559
1561
|
}
|
|
1562
|
+
/**
|
|
1563
|
+
* List user-created views for a given object via the metadata overlay
|
|
1564
|
+
* API (ADR-0005). Replaces the legacy `find('sys_view', {...})` path
|
|
1565
|
+
* that wrote to a physical `sys_view` table whose columns no longer
|
|
1566
|
+
* match the view spec shape.
|
|
1567
|
+
*
|
|
1568
|
+
* Returns view spec objects with their canonical `name` as identifier.
|
|
1569
|
+
* Filters by `data.object === objectName` (or top-level `object`)
|
|
1570
|
+
* client-side because the metadata index is name-only, not field-typed.
|
|
1571
|
+
*/
|
|
1572
|
+
async listViews(objectName) {
|
|
1573
|
+
await this.connect();
|
|
1574
|
+
try {
|
|
1575
|
+
const result = await this.client.meta.getItems("view");
|
|
1576
|
+
const items = Array.isArray(result?.items) ? result.items : Array.isArray(result) ? result : [];
|
|
1577
|
+
return items.filter((v) => {
|
|
1578
|
+
if (!v) return false;
|
|
1579
|
+
const spec = v.list ?? v;
|
|
1580
|
+
const obj = spec?.data?.object ?? spec?.object ?? spec?.objectName;
|
|
1581
|
+
return obj === objectName;
|
|
1582
|
+
}).map((v) => v.list ?? v);
|
|
1583
|
+
} catch (err) {
|
|
1584
|
+
console.warn("[OBJECTSTACKDataSource] listViews failed:", err);
|
|
1585
|
+
return [];
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
/**
|
|
1589
|
+
* Create a new overlay view for an object. The view's `name` is the
|
|
1590
|
+
* stable identifier — must be unique within the project scope. Returns
|
|
1591
|
+
* the persisted view spec (or undefined when the server doesn't echo).
|
|
1592
|
+
*
|
|
1593
|
+
* Generates a snake_case name if `spec.name` is not provided by appending
|
|
1594
|
+
* a short timestamp suffix to the source-name hint.
|
|
1595
|
+
*/
|
|
1596
|
+
async createView(objectName, spec) {
|
|
1597
|
+
await this.connect();
|
|
1598
|
+
let name = String(spec?.name || "").trim();
|
|
1599
|
+
if (!name) {
|
|
1600
|
+
const base = String(spec?.label || objectName || "view").toLowerCase().replace(/[^a-z0-9_]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 40) || "view";
|
|
1601
|
+
const suffix = Date.now().toString(36);
|
|
1602
|
+
name = `${base}_${suffix}`;
|
|
1603
|
+
}
|
|
1604
|
+
const fullSpec = {
|
|
1605
|
+
...spec,
|
|
1606
|
+
name,
|
|
1607
|
+
object: spec?.object || objectName,
|
|
1608
|
+
data: spec?.data || { provider: "object", object: objectName }
|
|
1609
|
+
};
|
|
1610
|
+
try {
|
|
1611
|
+
const result = await this.client.meta.saveItem("view", name, fullSpec);
|
|
1612
|
+
this.metadataCache.invalidate?.(`views:${objectName}`);
|
|
1613
|
+
if (result && result.item) return result.item;
|
|
1614
|
+
return fullSpec;
|
|
1615
|
+
} catch (err) {
|
|
1616
|
+
throw err;
|
|
1617
|
+
}
|
|
1618
|
+
}
|
|
1619
|
+
/**
|
|
1620
|
+
* Apply a partial update to an existing overlay view. Reads the current
|
|
1621
|
+
* overlay (or seeds from artifact), merges, and writes back. ADR-0005
|
|
1622
|
+
* overlay rows store the *full* view document, so partial updates require
|
|
1623
|
+
* a read-merge-write cycle.
|
|
1624
|
+
*/
|
|
1625
|
+
async updateView(objectName, viewName, partial) {
|
|
1626
|
+
await this.connect();
|
|
1627
|
+
let current = {};
|
|
1628
|
+
try {
|
|
1629
|
+
const r = await this.client.meta.getItem("view", viewName);
|
|
1630
|
+
current = r && (r.item || r) || {};
|
|
1631
|
+
if (current?.list) current = current.list;
|
|
1632
|
+
} catch {
|
|
1633
|
+
}
|
|
1634
|
+
const merged = {
|
|
1635
|
+
...current,
|
|
1636
|
+
...partial,
|
|
1637
|
+
name: viewName,
|
|
1638
|
+
object: current?.object || current?.data?.object || objectName
|
|
1639
|
+
};
|
|
1640
|
+
try {
|
|
1641
|
+
const result = await this.client.meta.saveItem("view", viewName, merged);
|
|
1642
|
+
this.metadataCache.invalidate?.(`views:${objectName}`);
|
|
1643
|
+
this.metadataCache.invalidate?.(`view:${objectName}:${viewName}`);
|
|
1644
|
+
if (result && result.item) return result.item;
|
|
1645
|
+
return merged;
|
|
1646
|
+
} catch (err) {
|
|
1647
|
+
throw err;
|
|
1648
|
+
}
|
|
1649
|
+
}
|
|
1650
|
+
/**
|
|
1651
|
+
* Delete an overlay view (reset to artifact default if one exists, or
|
|
1652
|
+
* remove entirely if it was a user-created view). Routes to
|
|
1653
|
+
* `DELETE /api/v1/meta/view/:name`.
|
|
1654
|
+
*/
|
|
1655
|
+
async deleteView(objectName, viewName) {
|
|
1656
|
+
await this.connect();
|
|
1657
|
+
try {
|
|
1658
|
+
const result = await this.client.meta.deleteItem("view", viewName);
|
|
1659
|
+
this.metadataCache.invalidate?.(`views:${objectName}`);
|
|
1660
|
+
this.metadataCache.invalidate?.(`view:${objectName}:${viewName}`);
|
|
1661
|
+
return { deleted: !!(result?.deleted ?? result?.reset ?? true) };
|
|
1662
|
+
} catch (err) {
|
|
1663
|
+
throw err;
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1560
1666
|
/**
|
|
1561
1667
|
* Get an application definition by name or ID.
|
|
1562
1668
|
* Attempts to fetch from the server metadata API.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/data-objectstack",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.10",
|
|
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": "^4.0.5",
|
|
26
|
-
"@object-ui/core": "4.0.
|
|
27
|
-
"@object-ui/types": "4.0.
|
|
26
|
+
"@object-ui/core": "4.0.10",
|
|
27
|
+
"@object-ui/types": "4.0.10"
|
|
28
28
|
},
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"tsup": "^8.5.1",
|
package/src/index.ts
CHANGED
|
@@ -1116,16 +1116,23 @@ export class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1116
1116
|
): Promise<Record<string, any> | void> {
|
|
1117
1117
|
await this.connect();
|
|
1118
1118
|
try {
|
|
1119
|
+
// ADR-0005 metadata customization overlay: persist views under
|
|
1120
|
+
// `type='view'` (NOT `type=<objectName>` — that was a pre-overlay
|
|
1121
|
+
// misuse that hit `/api/v1/meta/<objectName>/<viewId>`, which the
|
|
1122
|
+
// server never wired). The view's `data.object` field is what
|
|
1123
|
+
// associates it back to the object on read.
|
|
1124
|
+
const merged = { ...(config || {}), object: (config as any)?.object || objectName, name: viewId };
|
|
1119
1125
|
const result: any = await this.client.meta.saveItem(
|
|
1120
|
-
|
|
1126
|
+
'view',
|
|
1121
1127
|
viewId,
|
|
1122
|
-
|
|
1128
|
+
merged
|
|
1123
1129
|
);
|
|
1124
1130
|
// Invalidate cached read so next getView reflects the change
|
|
1125
1131
|
const cacheKey = `view:${objectName}:${viewId}`;
|
|
1126
1132
|
this.metadataCache.invalidate?.(cacheKey);
|
|
1127
1133
|
// Also invalidate the batch override map so listViewOverrides re-fetches
|
|
1128
1134
|
this.metadataCache.invalidate?.(`view-overrides:${objectName}`);
|
|
1135
|
+
this.metadataCache.invalidate?.(`views:${objectName}`);
|
|
1129
1136
|
if (result && result.item) return result.item;
|
|
1130
1137
|
return result ?? undefined;
|
|
1131
1138
|
} catch (err) {
|
|
@@ -1136,6 +1143,134 @@ export class ObjectStackAdapter<T = unknown> implements DataSource<T> {
|
|
|
1136
1143
|
}
|
|
1137
1144
|
}
|
|
1138
1145
|
|
|
1146
|
+
/**
|
|
1147
|
+
* List user-created views for a given object via the metadata overlay
|
|
1148
|
+
* API (ADR-0005). Replaces the legacy `find('sys_view', {...})` path
|
|
1149
|
+
* that wrote to a physical `sys_view` table whose columns no longer
|
|
1150
|
+
* match the view spec shape.
|
|
1151
|
+
*
|
|
1152
|
+
* Returns view spec objects with their canonical `name` as identifier.
|
|
1153
|
+
* Filters by `data.object === objectName` (or top-level `object`)
|
|
1154
|
+
* client-side because the metadata index is name-only, not field-typed.
|
|
1155
|
+
*/
|
|
1156
|
+
async listViews(objectName: string): Promise<any[]> {
|
|
1157
|
+
await this.connect();
|
|
1158
|
+
try {
|
|
1159
|
+
const result: any = await this.client.meta.getItems('view');
|
|
1160
|
+
const items: any[] = Array.isArray(result?.items)
|
|
1161
|
+
? result.items
|
|
1162
|
+
: Array.isArray(result) ? result : [];
|
|
1163
|
+
return items.filter((v: any) => {
|
|
1164
|
+
if (!v) return false;
|
|
1165
|
+
// Handle both bare view spec and `{list: {...}}` artifact wrapper
|
|
1166
|
+
const spec = v.list ?? v;
|
|
1167
|
+
const obj = spec?.data?.object ?? spec?.object ?? spec?.objectName;
|
|
1168
|
+
return obj === objectName;
|
|
1169
|
+
}).map((v: any) => v.list ?? v);
|
|
1170
|
+
} catch (err) {
|
|
1171
|
+
console.warn('[OBJECTSTACKDataSource] listViews failed:', err);
|
|
1172
|
+
return [];
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1176
|
+
/**
|
|
1177
|
+
* Create a new overlay view for an object. The view's `name` is the
|
|
1178
|
+
* stable identifier — must be unique within the project scope. Returns
|
|
1179
|
+
* the persisted view spec (or undefined when the server doesn't echo).
|
|
1180
|
+
*
|
|
1181
|
+
* Generates a snake_case name if `spec.name` is not provided by appending
|
|
1182
|
+
* a short timestamp suffix to the source-name hint.
|
|
1183
|
+
*/
|
|
1184
|
+
async createView(
|
|
1185
|
+
objectName: string,
|
|
1186
|
+
spec: Record<string, any>,
|
|
1187
|
+
): Promise<Record<string, any> | void> {
|
|
1188
|
+
await this.connect();
|
|
1189
|
+
let name = String(spec?.name || '').trim();
|
|
1190
|
+
if (!name) {
|
|
1191
|
+
const base = String(spec?.label || objectName || 'view')
|
|
1192
|
+
.toLowerCase()
|
|
1193
|
+
.replace(/[^a-z0-9_]+/g, '_')
|
|
1194
|
+
.replace(/^_+|_+$/g, '')
|
|
1195
|
+
.slice(0, 40) || 'view';
|
|
1196
|
+
const suffix = Date.now().toString(36);
|
|
1197
|
+
name = `${base}_${suffix}`;
|
|
1198
|
+
}
|
|
1199
|
+
const fullSpec = {
|
|
1200
|
+
...spec,
|
|
1201
|
+
name,
|
|
1202
|
+
object: spec?.object || objectName,
|
|
1203
|
+
data: spec?.data || { provider: 'object', object: objectName },
|
|
1204
|
+
};
|
|
1205
|
+
try {
|
|
1206
|
+
const result: any = await this.client.meta.saveItem('view', name, fullSpec);
|
|
1207
|
+
this.metadataCache.invalidate?.(`views:${objectName}`);
|
|
1208
|
+
if (result && result.item) return result.item;
|
|
1209
|
+
return fullSpec;
|
|
1210
|
+
} catch (err) {
|
|
1211
|
+
throw err;
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
/**
|
|
1216
|
+
* Apply a partial update to an existing overlay view. Reads the current
|
|
1217
|
+
* overlay (or seeds from artifact), merges, and writes back. ADR-0005
|
|
1218
|
+
* overlay rows store the *full* view document, so partial updates require
|
|
1219
|
+
* a read-merge-write cycle.
|
|
1220
|
+
*/
|
|
1221
|
+
async updateView(
|
|
1222
|
+
objectName: string,
|
|
1223
|
+
viewName: string,
|
|
1224
|
+
partial: Record<string, any>,
|
|
1225
|
+
): Promise<Record<string, any> | void> {
|
|
1226
|
+
await this.connect();
|
|
1227
|
+
let current: any = {};
|
|
1228
|
+
try {
|
|
1229
|
+
const r: any = await this.client.meta.getItem('view', viewName);
|
|
1230
|
+
current = (r && (r.item || r)) || {};
|
|
1231
|
+
// Some endpoints return the bare item; others wrap as {type,name,item}
|
|
1232
|
+
if (current?.list) current = current.list;
|
|
1233
|
+
} catch {
|
|
1234
|
+
// Treat missing as create-equivalent
|
|
1235
|
+
}
|
|
1236
|
+
const merged = {
|
|
1237
|
+
...current,
|
|
1238
|
+
...partial,
|
|
1239
|
+
name: viewName,
|
|
1240
|
+
object: current?.object || (current as any)?.data?.object || objectName,
|
|
1241
|
+
};
|
|
1242
|
+
try {
|
|
1243
|
+
const result: any = await this.client.meta.saveItem('view', viewName, merged);
|
|
1244
|
+
this.metadataCache.invalidate?.(`views:${objectName}`);
|
|
1245
|
+
this.metadataCache.invalidate?.(`view:${objectName}:${viewName}`);
|
|
1246
|
+
if (result && result.item) return result.item;
|
|
1247
|
+
return merged;
|
|
1248
|
+
} catch (err) {
|
|
1249
|
+
throw err;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
/**
|
|
1254
|
+
* Delete an overlay view (reset to artifact default if one exists, or
|
|
1255
|
+
* remove entirely if it was a user-created view). Routes to
|
|
1256
|
+
* `DELETE /api/v1/meta/view/:name`.
|
|
1257
|
+
*/
|
|
1258
|
+
async deleteView(
|
|
1259
|
+
objectName: string,
|
|
1260
|
+
viewName: string,
|
|
1261
|
+
): Promise<{ deleted: boolean }> {
|
|
1262
|
+
await this.connect();
|
|
1263
|
+
try {
|
|
1264
|
+
const result: any = await this.client.meta.deleteItem('view', viewName);
|
|
1265
|
+
this.metadataCache.invalidate?.(`views:${objectName}`);
|
|
1266
|
+
this.metadataCache.invalidate?.(`view:${objectName}:${viewName}`);
|
|
1267
|
+
return { deleted: !!(result?.deleted ?? result?.reset ?? true) };
|
|
1268
|
+
} catch (err) {
|
|
1269
|
+
throw err;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
|
|
1273
|
+
|
|
1139
1274
|
/**
|
|
1140
1275
|
* Get an application definition by name or ID.
|
|
1141
1276
|
* Attempts to fetch from the server metadata API.
|