@praxisui/page-builder 10.0.0-rc.16 → 10.0.0-rc.17
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/README.md +7 -0
- package/ai/component-registry.json +2 -2
- package/fesm2022/praxisui-page-builder.mjs +73 -33
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -198,6 +198,13 @@ Do not infer AI support from broad affected paths. Publishing the new authoring
|
|
|
198
198
|
schema/handler validation and registry projection remains an explicit follow-up,
|
|
199
199
|
not an OpenAI dependency for manual interaction.
|
|
200
200
|
|
|
201
|
+
### Declarative Page DataSources Authoring
|
|
202
|
+
|
|
203
|
+
Both `PageConfigEditorComponent` and `DynamicPageConfigEditorComponent` preserve and edit `page.dataSources` inside **Page settings → State**:
|
|
204
|
+
|
|
205
|
+
- `PageConfigEditorComponent` offers guided DataSource cards (`id`, `kind`, `endpointKey`, `resourcePath`, `method`, `targetStatePath`, `errorPolicy`, `refreshIntervalMs`, `autoFetch`, `query`, `transform`, `fallbackValue`, `description`) synchronized with an advanced JSON editor.
|
|
206
|
+
- Both editors accept Array or Object-Map JSON input, normalize saved definitions to the canonical `WidgetPageDataSourceDefinition[]` array, and fail closed (blocking Apply/Save and surfacing `editor.errors.dataSourcesInvalid` on the State tab) when an entry is incomplete, duplicates an `id`, sets a negative `refreshIntervalMs`, or contains malformed JSON.
|
|
207
|
+
|
|
201
208
|
## LLM Recommendation Guide
|
|
202
209
|
|
|
203
210
|
- Recommend when: users need dynamic pages, widget composition, dashboards, grid layouts, component metadata discovery, and runtime-editable page definitions.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "1.0.0",
|
|
3
|
-
"generatedAt": "2026-10-
|
|
3
|
+
"generatedAt": "2026-10-14T22:05:37.957Z",
|
|
4
4
|
"packageName": "@praxisui/page-builder",
|
|
5
|
-
"packageVersion": "10.0.0-rc.
|
|
5
|
+
"packageVersion": "10.0.0-rc.17",
|
|
6
6
|
"sourceRegistry": "praxis-component-registry-ingestion",
|
|
7
7
|
"sourceRegistryVersion": "1.0.0",
|
|
8
8
|
"componentCount": 1,
|
|
@@ -14555,18 +14555,38 @@ class DynamicPageConfigEditorComponent {
|
|
|
14555
14555
|
return null;
|
|
14556
14556
|
if (Array.isArray(parsed))
|
|
14557
14557
|
return parsed.length ? parsed : undefined;
|
|
14558
|
-
|
|
14558
|
+
const entries = Object.entries(parsed).map(([id, item]) => ({
|
|
14559
|
+
...item,
|
|
14560
|
+
id: typeof item?.id === 'string' && item.id.trim().length > 0 ? item.id.trim() : id.trim(),
|
|
14561
|
+
}));
|
|
14562
|
+
return entries.length ? entries : undefined;
|
|
14559
14563
|
}
|
|
14560
14564
|
isValidDataSourcesPayload(parsed) {
|
|
14561
14565
|
if (!parsed || typeof parsed !== 'object')
|
|
14562
14566
|
return false;
|
|
14567
|
+
const isValidRefreshInterval = (val) => val === undefined || (typeof val === 'number' && Number.isFinite(val) && val >= 0);
|
|
14563
14568
|
if (Array.isArray(parsed)) {
|
|
14564
|
-
|
|
14565
|
-
|
|
14566
|
-
|
|
14569
|
+
const seenIds = new Set();
|
|
14570
|
+
for (const item of parsed) {
|
|
14571
|
+
if (!this.isPlainObject(item)
|
|
14572
|
+
|| typeof item.id !== 'string'
|
|
14573
|
+
|| !item.id.trim().length
|
|
14574
|
+
|| typeof item.resourcePath !== 'string'
|
|
14575
|
+
|| !item.resourcePath.trim().length
|
|
14576
|
+
|| !isValidRefreshInterval(item.refreshIntervalMs)) {
|
|
14577
|
+
return false;
|
|
14578
|
+
}
|
|
14579
|
+
const trimmedId = item.id.trim();
|
|
14580
|
+
if (seenIds.has(trimmedId))
|
|
14581
|
+
return false;
|
|
14582
|
+
seenIds.add(trimmedId);
|
|
14583
|
+
}
|
|
14584
|
+
return true;
|
|
14567
14585
|
}
|
|
14568
14586
|
return Object.entries(parsed).every(([id, item]) => id.trim().length > 0 && this.isPlainObject(item)
|
|
14569
|
-
&& typeof item.resourcePath === 'string'
|
|
14587
|
+
&& typeof item.resourcePath === 'string'
|
|
14588
|
+
&& item.resourcePath.trim().length > 0
|
|
14589
|
+
&& isValidRefreshInterval(item.refreshIntervalMs));
|
|
14570
14590
|
}
|
|
14571
14591
|
parseGroupingTabs(raw) { const parsed = this.parseJson(raw, []); if (!Array.isArray(parsed) || !parsed.every((tab) => this.isGroupingTab(tab)))
|
|
14572
14592
|
return null; return this.hasDuplicateIds(parsed) ? null : parsed; }
|
|
@@ -14935,7 +14955,7 @@ class PageConfigEditorComponent {
|
|
|
14935
14955
|
this.activeTab.set(this.tabOrder[index] || 'layout');
|
|
14936
14956
|
}
|
|
14937
14957
|
apply() {
|
|
14938
|
-
const updated = this.buildCurrentPage();
|
|
14958
|
+
const updated = this.buildCurrentPage(true);
|
|
14939
14959
|
if (!updated)
|
|
14940
14960
|
return;
|
|
14941
14961
|
this.pageChange.emit(updated);
|
|
@@ -15217,29 +15237,46 @@ class PageConfigEditorComponent {
|
|
|
15217
15237
|
isValidDataSourcesPayload(parsed) {
|
|
15218
15238
|
if (!parsed || typeof parsed !== 'object')
|
|
15219
15239
|
return false;
|
|
15240
|
+
const isValidRefreshInterval = (val) => val === undefined ||
|
|
15241
|
+
(typeof val === 'number' && Number.isFinite(val) && val >= 0);
|
|
15220
15242
|
if (Array.isArray(parsed)) {
|
|
15221
|
-
|
|
15222
|
-
|
|
15223
|
-
|
|
15224
|
-
|
|
15225
|
-
|
|
15226
|
-
|
|
15227
|
-
|
|
15228
|
-
|
|
15229
|
-
|
|
15230
|
-
|
|
15231
|
-
|
|
15232
|
-
|
|
15233
|
-
|
|
15234
|
-
|
|
15243
|
+
const seenIds = new Set();
|
|
15244
|
+
for (const item of parsed) {
|
|
15245
|
+
if (!item ||
|
|
15246
|
+
typeof item !== 'object' ||
|
|
15247
|
+
Array.isArray(item) ||
|
|
15248
|
+
typeof item.id !== 'string' ||
|
|
15249
|
+
!item.id.trim().length ||
|
|
15250
|
+
typeof item.resourcePath !== 'string' ||
|
|
15251
|
+
!item.resourcePath.trim().length ||
|
|
15252
|
+
!isValidRefreshInterval(item.refreshIntervalMs)) {
|
|
15253
|
+
return false;
|
|
15254
|
+
}
|
|
15255
|
+
const trimmedId = item.id.trim();
|
|
15256
|
+
if (seenIds.has(trimmedId)) {
|
|
15257
|
+
return false;
|
|
15258
|
+
}
|
|
15259
|
+
seenIds.add(trimmedId);
|
|
15260
|
+
}
|
|
15261
|
+
return true;
|
|
15262
|
+
}
|
|
15263
|
+
return Object.entries(parsed).every(([id, item]) => id.trim().length > 0 &&
|
|
15264
|
+
!!item &&
|
|
15265
|
+
typeof item === 'object' &&
|
|
15266
|
+
!Array.isArray(item) &&
|
|
15267
|
+
typeof item.resourcePath === 'string' &&
|
|
15268
|
+
item.resourcePath.trim().length > 0 &&
|
|
15269
|
+
isValidRefreshInterval(item.refreshIntervalMs));
|
|
15235
15270
|
}
|
|
15236
15271
|
buildDataSourcesFromNodes() {
|
|
15237
15272
|
const list = [];
|
|
15273
|
+
const seenIds = new Set();
|
|
15238
15274
|
for (const group of this.dataSourceNodes.controls) {
|
|
15239
15275
|
const id = group.controls.id.value.trim();
|
|
15240
15276
|
const resourcePath = group.controls.resourcePath.value.trim();
|
|
15241
|
-
if (!id || !resourcePath)
|
|
15277
|
+
if (!id || !resourcePath || seenIds.has(id))
|
|
15242
15278
|
return null;
|
|
15279
|
+
seenIds.add(id);
|
|
15243
15280
|
const node = { id, resourcePath };
|
|
15244
15281
|
const kind = group.controls.kind.value.trim();
|
|
15245
15282
|
const endpointKey = group.controls.endpointKey.value.trim();
|
|
@@ -15262,8 +15299,14 @@ class PageConfigEditorComponent {
|
|
|
15262
15299
|
node.targetStatePath = targetStatePath;
|
|
15263
15300
|
if (errorPolicy)
|
|
15264
15301
|
node.errorPolicy = errorPolicy;
|
|
15265
|
-
if (refreshIntervalMs !== null &&
|
|
15266
|
-
|
|
15302
|
+
if (refreshIntervalMs !== null &&
|
|
15303
|
+
refreshIntervalMs !== undefined &&
|
|
15304
|
+
String(refreshIntervalMs).trim() !== '') {
|
|
15305
|
+
const parsedRefresh = Number(refreshIntervalMs);
|
|
15306
|
+
if (!Number.isFinite(parsedRefresh) || parsedRefresh < 0) {
|
|
15307
|
+
return null;
|
|
15308
|
+
}
|
|
15309
|
+
node.refreshIntervalMs = parsedRefresh;
|
|
15267
15310
|
}
|
|
15268
15311
|
if (autoFetch === 'true')
|
|
15269
15312
|
node.autoFetch = true;
|
|
@@ -15307,16 +15350,13 @@ class PageConfigEditorComponent {
|
|
|
15307
15350
|
const normalized = this.normalizeDataSourcesArray(parsed);
|
|
15308
15351
|
return normalized.length ? normalized : undefined;
|
|
15309
15352
|
}
|
|
15310
|
-
|
|
15311
|
-
|
|
15312
|
-
|
|
15313
|
-
|
|
15314
|
-
|
|
15315
|
-
|
|
15316
|
-
|
|
15317
|
-
return fromNodes.length ? fromNodes : undefined;
|
|
15318
|
-
}
|
|
15319
|
-
return undefined;
|
|
15353
|
+
const fromNodes = this.buildDataSourcesFromNodes();
|
|
15354
|
+
if (fromNodes === null)
|
|
15355
|
+
return null;
|
|
15356
|
+
const json = fromNodes.length ? JSON.stringify(fromNodes, null, 2) : '';
|
|
15357
|
+
this.lastDataSources = json;
|
|
15358
|
+
this.dataSourcesControl.setValue(json, { emitEvent: false });
|
|
15359
|
+
return fromNodes.length ? fromNodes : undefined;
|
|
15320
15360
|
}
|
|
15321
15361
|
replaceSchemaNodes(schema) {
|
|
15322
15362
|
this.schemaNodes.clear({ emitEvent: false });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/page-builder",
|
|
3
|
-
"version": "10.0.0-rc.
|
|
3
|
+
"version": "10.0.0-rc.17",
|
|
4
4
|
"description": "Page and widget builder utilities for Praxis UI (grid, dynamic widgets, editors).",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/common": "^21.0.0",
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
"@angular/forms": "^21.0.0",
|
|
9
9
|
"@angular/cdk": "^21.0.0",
|
|
10
10
|
"@angular/material": "^21.0.0",
|
|
11
|
-
"@praxisui/ai": "^10.0.0-rc.
|
|
12
|
-
"@praxisui/core": "^10.0.0-rc.
|
|
13
|
-
"@praxisui/settings-panel": "^10.0.0-rc.
|
|
11
|
+
"@praxisui/ai": "^10.0.0-rc.17",
|
|
12
|
+
"@praxisui/core": "^10.0.0-rc.17",
|
|
13
|
+
"@praxisui/settings-panel": "^10.0.0-rc.17",
|
|
14
14
|
"rxjs": "~7.8.0"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|