@praxisui/page-builder 9.0.58 → 9.0.60

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 CHANGED
@@ -324,6 +324,16 @@ Every official local apply of a `UiCompositionPlan` now runs a fail-closed prefl
324
324
 
325
325
  `preflightUiCompositionPlan(plan, targetRegistry)` is exported by `@praxisui/page-builder` for official Dynamic Page hosts that compile plans outside `DynamicPageBuilderComponent`. Those hosts must inject their runtime `ComponentMetadataRegistry`; omitting it fails closed with `UI_COMPOSITION_TARGET_REGISTRY_REQUIRED`.
326
326
 
327
+ Resource-backed compositions must then run the exported asynchronous
328
+ `preflightUiCompositionPlanResources(...)`, or inject
329
+ `UiCompositionResourcePreflightService` in an Angular host. Component owners declare
330
+ the finite filter field source on the output port and map each `query-context` input
331
+ to its resource operation through `runtimeProfiles`. The service resolves canonical
332
+ request schemas from `/schemas/filtered`, verifies the concrete operation path and
333
+ proves that every declared source field is accepted by both the source filter and
334
+ every connected consumer request. Missing evidence, undeclared operations, path
335
+ divergence and incompatible fields block the Dynamic Page before mount or persistence.
336
+
327
337
  Workspace contributors can exercise this path in the reference host at
328
338
  `/dynamic-page-lab`. Its **Certificação do UiCompositionPlan** station displays
329
339
  the compact plan, registry-aware preflight, compiled `WidgetPageDefinition`, real
@@ -337,15 +347,17 @@ kind, unguarded feedback, incomplete master-detail layout and a stale governed
337
347
  template hash. The browser proof for the related-resource scenario requires a real
338
348
  parent selection, the relationship filter and visible child columns/data derived
339
349
  from the remote schema; request success alone is insufficient. The same route's
340
- People Operations dashboard is authored as a 9-widget/10-binding plan and proves
350
+ People Operations dashboard is authored as a 9-widget/11-binding plan and proves
341
351
  filter and governed decision propagation through the compiled graph. The lab also
342
352
  executes deterministic runtime reset, verifies JSON persistence invariants for
343
353
  every applicable scenario, and invokes the real Page Builder save output before
344
354
  reopening the rich People Operations page. Corrupted executable transforms and
345
355
  legacy `page.connections` remain Core/page-ingress adversarials because neither is
346
356
  a valid `UiCompositionPlan` input; the lab does not duplicate those boundary
347
- validators. The governed Config Starter + Quickstart gate remains tracked in
348
- issue #430; issue #431 closed the former global-action source limitation.
357
+ validators. The lab now proves the governed Config Starter + Quickstart
358
+ save/reload/delete transaction with strong ETag semantics; issue #430 still tracks
359
+ approval/publication and the remaining archetype-quality gates. Issue #431 closed
360
+ the former global-action source limitation.
349
361
 
350
362
  For multi-consumer interaction state, author one `event -> state` binding and
351
363
  project `state -> component-port` to each consumer. A direct event delivery is
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-09-03T03:04:37.933Z",
3
+ "generatedAt": "2026-09-04T15:26:31.194Z",
4
4
  "packageName": "@praxisui/page-builder",
5
- "packageVersion": "9.0.58",
5
+ "packageVersion": "9.0.60",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 1,
@@ -7,7 +7,7 @@ import { MAT_DIALOG_DATA, MatDialogModule } from '@angular/material/dialog';
7
7
  import * as i2$1 from '@angular/material/icon';
8
8
  import { MatIconModule } from '@angular/material/icon';
9
9
  import * as i2$2 from '@praxisui/core';
10
- import { PraxisIconDirective, providePraxisI18n, BUILTIN_SHELL_PRESETS, PraxisIconButtonComponent, PraxisI18nService, GLOBAL_ACTION_CATALOG, getGlobalActionCatalog, PRAXIS_GLOBAL_ACTION_CATALOG, SurfaceOpenActionEditorComponent, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, NestedWidgetConfigAccessor, CompositionValidatorService, deepMerge, generateId, ComponentMetadataRegistry, ObservabilityDashboardService, PraxisRuntimeComponentObservationRegistryService, domainKnowledgeTimelineToRichContentDocument, DomainRuleService, DomainKnowledgeService, SETTINGS_PANEL_BRIDGE, DynamicWidgetPageComponent, DYNAMIC_PAGE_SHELL_EDITOR } from '@praxisui/core';
10
+ import { PraxisIconDirective, providePraxisI18n, BUILTIN_SHELL_PRESETS, PraxisIconButtonComponent, PraxisI18nService, GLOBAL_ACTION_CATALOG, getGlobalActionCatalog, PRAXIS_GLOBAL_ACTION_CATALOG, SurfaceOpenActionEditorComponent, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, SETTINGS_PANEL_DATA as SETTINGS_PANEL_DATA$1, NestedWidgetConfigAccessor, CompositionValidatorService, ComponentMetadataRegistry, ResourceDiscoveryService, ComponentRuntimeProfileService, buildApiUrl, deepMerge, generateId, ObservabilityDashboardService, PraxisRuntimeComponentObservationRegistryService, domainKnowledgeTimelineToRichContentDocument, DomainRuleService, DomainKnowledgeService, SETTINGS_PANEL_BRIDGE, DynamicWidgetPageComponent, DYNAMIC_PAGE_SHELL_EDITOR } from '@praxisui/core';
11
11
  import * as i3 from '@angular/material/tooltip';
12
12
  import { MatTooltipModule } from '@angular/material/tooltip';
13
13
  import * as i3$1 from '@angular/material/form-field';
@@ -24,7 +24,7 @@ import * as i6 from '@angular/material/divider';
24
24
  import { MatDividerModule } from '@angular/material/divider';
25
25
  import * as i6$1 from '@angular/material/select';
26
26
  import { MatSelectModule } from '@angular/material/select';
27
- import { BehaviorSubject, merge, timeout, map, catchError, throwError, from, firstValueFrom, share, concatMap, of, timer, takeUntil, Subject, tap, filter, take } from 'rxjs';
27
+ import { BehaviorSubject, merge, timeout, map, catchError, throwError, firstValueFrom, from, share, concatMap, of, timer, takeUntil, Subject, tap, filter, take } from 'rxjs';
28
28
  import { SETTINGS_PANEL_DATA } from '@praxisui/settings-panel';
29
29
  import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
30
30
  import { CdkTrapFocus } from '@angular/cdk/a11y';
@@ -15977,6 +15977,347 @@ function createAuthoringDiagnostic(id, code, message, path, blocking, stage) {
15977
15977
  };
15978
15978
  }
15979
15979
 
15980
+ /**
15981
+ * Async, fail-closed resource conformance layered after the synchronous
15982
+ * compiler/registry preflight. It fetches schema evidence but never persists it
15983
+ * into the page definition.
15984
+ */
15985
+ async function preflightUiCompositionPlanResources(plan, registry, resolveSchema) {
15986
+ const structural = preflightUiCompositionPlan(plan, registry);
15987
+ if (!structural.valid)
15988
+ return structural;
15989
+ const diagnostics = await attestResourceQueries(structural.page.widgets, structural.page.composition?.links ?? [], registry, resolveSchema);
15990
+ const allDiagnostics = [...structural.diagnostics, ...diagnostics];
15991
+ const blocking = allDiagnostics.some(isBlockingDiagnostic);
15992
+ return blocking
15993
+ ? { valid: false, page: null, diagnostics: allDiagnostics }
15994
+ : { valid: true, page: structural.page, diagnostics: allDiagnostics };
15995
+ }
15996
+ /** Official HTTP-backed resource conformance service for Dynamic Page hosts. */
15997
+ class UiCompositionResourcePreflightService {
15998
+ registry = inject(ComponentMetadataRegistry);
15999
+ discovery = inject(ResourceDiscoveryService);
16000
+ preflight(plan, registry = this.registry) {
16001
+ return preflightUiCompositionPlanResources(plan, registry, (request) => this.resolveSchema(request));
16002
+ }
16003
+ async resolveSchema(request) {
16004
+ const apiUrlEntry = request.apiUrlEntry ?? this.discovery.resolveApiEntry();
16005
+ const expectedOperationPath = resolveOperationPath(request.resourcePath, request.pathSuffix, apiUrlEntry);
16006
+ const operationPath = request.pathInput
16007
+ ? normalizeAbsoluteOrRootPath(request.pathInput)
16008
+ : expectedOperationPath;
16009
+ if (operationPath !== expectedOperationPath) {
16010
+ return { schema: null, operationPath, expectedOperationPath };
16011
+ }
16012
+ const query = new URLSearchParams({
16013
+ path: operationPath,
16014
+ operation: request.method.toLowerCase(),
16015
+ schemaType: 'request',
16016
+ includeInternalSchemas: 'true',
16017
+ });
16018
+ const schema = await firstValueFrom(this.discovery.getSchemaByUrl(`/schemas/filtered?${query.toString()}`, { apiUrlEntry }));
16019
+ return { schema, operationPath, expectedOperationPath };
16020
+ }
16021
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: UiCompositionResourcePreflightService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
16022
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: UiCompositionResourcePreflightService, providedIn: 'any' });
16023
+ }
16024
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: UiCompositionResourcePreflightService, decorators: [{
16025
+ type: Injectable,
16026
+ args: [{ providedIn: 'any' }]
16027
+ }] });
16028
+ async function attestResourceQueries(widgets, links, registry, resolveSchema) {
16029
+ const diagnostics = [];
16030
+ const widgetsByKey = new Map(widgets.map((widget) => [widget.key, widget]));
16031
+ const profiles = new ComponentRuntimeProfileService();
16032
+ const schemaCache = new Map();
16033
+ for (const link of links) {
16034
+ if (link.to.kind !== 'component-port' || link.to.ref.direction !== 'input')
16035
+ continue;
16036
+ const targetWidget = widgetsByKey.get(link.to.ref.widget);
16037
+ if (!targetWidget)
16038
+ continue;
16039
+ const sources = traceResourceFieldSources(link.from, links, widgetsByKey, registry, new Set());
16040
+ if (!sources.length)
16041
+ continue;
16042
+ const targetMetadata = registry.get(targetWidget.definition.id);
16043
+ if (!targetMetadata)
16044
+ continue;
16045
+ const targetPortId = link.to.ref.port;
16046
+ const targetPort = targetMetadata.ports?.find((port) => port.id === targetPortId && port.direction === 'input');
16047
+ if (targetPort?.semanticKind !== 'query-context')
16048
+ continue;
16049
+ const profileResolution = profiles.resolve(targetMetadata, targetWidget.definition.inputs);
16050
+ if (!profileResolution.match) {
16051
+ diagnostics.push(resourceDiagnostic('UI_COMPOSITION_RESOURCE_QUERY_TARGET_PROFILE_MISMATCH', `Widget "${targetWidget.key}" has no owner-authored runtime profile matching its resource-query inputs.`, link, targetWidget.key, { componentId: targetWidget.definition.id }));
16052
+ continue;
16053
+ }
16054
+ const operations = queryContextOperations(profileResolution.match.profile.effects, link.to.ref.port);
16055
+ if (!operations.length) {
16056
+ diagnostics.push(resourceDiagnostic('UI_COMPOSITION_RESOURCE_QUERY_TARGET_OPERATION_UNDECLARED', `Widget "${targetWidget.key}" does not declare how port "${link.to.ref.port}" reaches a resource request.`, link, targetWidget.key, {
16057
+ componentId: targetWidget.definition.id,
16058
+ runtimeProfileId: profileResolution.match.profile.id,
16059
+ portId: link.to.ref.port,
16060
+ }));
16061
+ continue;
16062
+ }
16063
+ for (const source of sources) {
16064
+ const fields = readDeclaredFields(profiles, source.widget.definition.inputs, source.descriptor.fieldPaths);
16065
+ if (!fields.length) {
16066
+ diagnostics.push(resourceDiagnostic('UI_COMPOSITION_RESOURCE_QUERY_FIELD_SET_UNRESOLVED', `Widget "${source.widget.key}" does not expose a finite declared field set for resource-query conformance.`, link, source.widget.key, { componentId: source.widget.definition.id, portId: source.port.id }));
16067
+ continue;
16068
+ }
16069
+ const sourceRequest = sourceSchemaRequest(source, profiles);
16070
+ if (!sourceRequest) {
16071
+ diagnostics.push(resourceDiagnostic('UI_COMPOSITION_RESOURCE_QUERY_SOURCE_PATH_UNRESOLVED', `Widget "${source.widget.key}" does not resolve the resource path declared by port "${source.port.id}".`, link, source.widget.key, { componentId: source.widget.definition.id, portId: source.port.id }));
16072
+ continue;
16073
+ }
16074
+ await validateFieldsAgainstSchema(fields, sourceRequest, 'SOURCE', link, diagnostics, resolveSchema, schemaCache);
16075
+ for (const { effect, operation } of operations) {
16076
+ const targetRequest = targetSchemaRequest(targetWidget, effect, operation, profiles);
16077
+ if (!targetRequest) {
16078
+ diagnostics.push(resourceDiagnostic('UI_COMPOSITION_RESOURCE_QUERY_TARGET_PATH_UNRESOLVED', `Widget "${targetWidget.key}" does not resolve the resource operation declared for "${operation.operationId}".`, link, targetWidget.key, { componentId: targetWidget.definition.id, operationId: operation.operationId }));
16079
+ continue;
16080
+ }
16081
+ await validateFieldsAgainstSchema(fields, targetRequest, 'TARGET', link, diagnostics, resolveSchema, schemaCache);
16082
+ }
16083
+ }
16084
+ }
16085
+ return diagnostics;
16086
+ }
16087
+ function traceResourceFieldSources(endpoint, links, widgetsByKey, registry, visited) {
16088
+ if (endpoint.kind === 'component-port' && endpoint.ref.direction === 'output') {
16089
+ const widget = widgetsByKey.get(endpoint.ref.widget);
16090
+ const metadata = widget ? registry.get(widget.definition.id) : undefined;
16091
+ const port = metadata?.ports?.find((candidate) => candidate.id === endpoint.ref.port && candidate.direction === 'output');
16092
+ return widget && metadata && port?.resourceQueryFieldSource
16093
+ ? [{ widget, metadata, port, descriptor: port.resourceQueryFieldSource }]
16094
+ : [];
16095
+ }
16096
+ if (endpoint.kind !== 'state')
16097
+ return [];
16098
+ const stateKey = `${endpoint.ref.layer ?? 'values'}:${endpoint.ref.path}`;
16099
+ if (visited.has(stateKey))
16100
+ return [];
16101
+ const nextVisited = new Set(visited).add(stateKey);
16102
+ return links
16103
+ .filter((candidate) => candidate.to.kind === 'state'
16104
+ && candidate.to.ref.path === endpoint.ref.path
16105
+ && (candidate.to.ref.layer ?? 'values') === (endpoint.ref.layer ?? 'values'))
16106
+ .flatMap((candidate) => traceResourceFieldSources(candidate.from, links, widgetsByKey, registry, nextVisited));
16107
+ }
16108
+ function queryContextOperations(effects, portId) {
16109
+ return effects.flatMap((effect) => effect.kind === 'resource-read'
16110
+ ? effect.operations
16111
+ .filter((operation) => operation.queryContext?.portId === portId)
16112
+ .map((operation) => ({ effect, operation }))
16113
+ : []);
16114
+ }
16115
+ function sourceSchemaRequest(source, profiles) {
16116
+ const inputs = source.widget.definition.inputs ?? {};
16117
+ const resourcePath = readSingleString(profiles, inputs, source.descriptor.resourcePath);
16118
+ if (!resourcePath)
16119
+ return null;
16120
+ return {
16121
+ widgetKey: source.widget.key,
16122
+ componentId: source.widget.definition.id,
16123
+ operationId: source.descriptor.schemaOperation.operationId,
16124
+ method: source.descriptor.schemaOperation.method,
16125
+ resourcePath,
16126
+ pathSuffix: source.descriptor.schemaOperation.pathSuffix,
16127
+ requestFieldPath: source.descriptor.schemaOperation.requestFieldPath ?? '$',
16128
+ apiUrlEntry: readApiUrlEntry(profiles, inputs, source.descriptor.apiUrlEntry),
16129
+ };
16130
+ }
16131
+ function targetSchemaRequest(widget, effect, operation, profiles) {
16132
+ const inputs = widget.definition.inputs ?? {};
16133
+ const resourcePath = readSingleString(profiles, inputs, effect.resourcePath);
16134
+ if (!resourcePath)
16135
+ return null;
16136
+ const pathInput = operation.pathInput
16137
+ ? readSingleString(profiles, inputs, operation.pathInput)
16138
+ : undefined;
16139
+ if (operation.pathInput && !pathInput)
16140
+ return null;
16141
+ return {
16142
+ widgetKey: widget.key,
16143
+ componentId: widget.definition.id,
16144
+ operationId: operation.operationId,
16145
+ method: operation.method,
16146
+ resourcePath,
16147
+ pathSuffix: operation.pathSuffix,
16148
+ pathInput,
16149
+ requestFieldPath: operation.queryContext?.requestFieldPath ?? '$',
16150
+ apiUrlEntry: readApiUrlEntry(profiles, inputs, effect.apiUrlEntry),
16151
+ };
16152
+ }
16153
+ async function validateFieldsAgainstSchema(fields, request, side, link, diagnostics, resolveSchema, cache) {
16154
+ const cacheKey = JSON.stringify(request);
16155
+ let resolution;
16156
+ try {
16157
+ let pending = cache.get(cacheKey);
16158
+ if (!pending) {
16159
+ pending = resolveSchema(request);
16160
+ cache.set(cacheKey, pending);
16161
+ }
16162
+ resolution = await pending;
16163
+ }
16164
+ catch (error) {
16165
+ diagnostics.push(resourceDiagnostic(`UI_COMPOSITION_RESOURCE_QUERY_${side}_SCHEMA_UNAVAILABLE`, `Request schema for "${request.operationId}" could not be resolved.`, link, request.widgetKey, {
16166
+ componentId: request.componentId,
16167
+ operationId: request.operationId,
16168
+ error: error instanceof Error ? error.message : String(error),
16169
+ }));
16170
+ return;
16171
+ }
16172
+ if (resolution.operationPath !== resolution.expectedOperationPath) {
16173
+ diagnostics.push(resourceDiagnostic('UI_COMPOSITION_RESOURCE_OPERATION_PATH_MISMATCH', `Operation path "${resolution.operationPath}" escapes the owner-declared resource path "${resolution.expectedOperationPath}".`, link, request.widgetKey, {
16174
+ componentId: request.componentId,
16175
+ operationId: request.operationId,
16176
+ operationPath: resolution.operationPath,
16177
+ expectedOperationPath: resolution.expectedOperationPath,
16178
+ }));
16179
+ return;
16180
+ }
16181
+ const allowedFields = schemaPropertiesAtPath(resolution.schema, request.requestFieldPath);
16182
+ if (!allowedFields) {
16183
+ diagnostics.push(resourceDiagnostic(`UI_COMPOSITION_RESOURCE_QUERY_${side}_FIELD_CONTAINER_UNRESOLVED`, `Request schema "${request.operationId}" does not expose properties at "${request.requestFieldPath}".`, link, request.widgetKey, {
16184
+ componentId: request.componentId,
16185
+ operationId: request.operationId,
16186
+ requestFieldPath: request.requestFieldPath,
16187
+ operationPath: resolution.operationPath,
16188
+ }));
16189
+ return;
16190
+ }
16191
+ const missingFields = fields.filter((field) => !allowedFields.has(field));
16192
+ if (!missingFields.length)
16193
+ return;
16194
+ diagnostics.push(resourceDiagnostic(`UI_COMPOSITION_RESOURCE_QUERY_FIELD_NOT_IN_${side}_SCHEMA`, `Fields [${missingFields.join(', ')}] are not accepted by "${request.operationId}" at "${request.requestFieldPath}".`, link, request.widgetKey, {
16195
+ componentId: request.componentId,
16196
+ operationId: request.operationId,
16197
+ operationPath: resolution.operationPath,
16198
+ requestFieldPath: request.requestFieldPath,
16199
+ missingFields,
16200
+ allowedFields: [...allowedFields].sort(),
16201
+ }));
16202
+ }
16203
+ function readDeclaredFields(profiles, inputs, paths) {
16204
+ const fields = paths.flatMap((path) => profiles.readPathValues(inputs ?? {}, path).flatMap(flattenStrings));
16205
+ return [...new Set(fields.map((field) => field.trim()).filter(Boolean))].sort();
16206
+ }
16207
+ function flattenStrings(value) {
16208
+ if (typeof value === 'string')
16209
+ return [value];
16210
+ return Array.isArray(value) ? value.flatMap(flattenStrings) : [];
16211
+ }
16212
+ function readSingleString(profiles, inputs, path) {
16213
+ const values = profiles.readPathValues(inputs, path)
16214
+ .filter((value) => typeof value === 'string')
16215
+ .map((value) => value.trim())
16216
+ .filter(Boolean);
16217
+ return values.length === 1 ? values[0] : undefined;
16218
+ }
16219
+ function readApiUrlEntry(profiles, inputs, path) {
16220
+ if (!path)
16221
+ return undefined;
16222
+ const values = profiles.readPathValues(inputs, path);
16223
+ const value = values.length === 1 ? values[0] : undefined;
16224
+ return value && typeof value === 'object' && !Array.isArray(value)
16225
+ ? value
16226
+ : undefined;
16227
+ }
16228
+ function schemaPropertiesAtPath(schema, path) {
16229
+ const root = asRecord(schema);
16230
+ if (!root)
16231
+ return null;
16232
+ let node = dereference(root, root);
16233
+ const segments = path === '$'
16234
+ ? []
16235
+ : path.split('.').map((segment) => segment.trim()).filter(Boolean);
16236
+ for (const segment of segments) {
16237
+ const properties = mergedProperties(node, root);
16238
+ node = properties ? dereference(asRecord(properties[segment]), root) : null;
16239
+ if (!node)
16240
+ return null;
16241
+ }
16242
+ const properties = mergedProperties(node, root);
16243
+ return properties ? new Set(Object.keys(properties)) : null;
16244
+ }
16245
+ function mergedProperties(node, root) {
16246
+ if (!node)
16247
+ return null;
16248
+ const direct = asRecord(node['properties']) ?? {};
16249
+ const inherited = Array.isArray(node['allOf'])
16250
+ ? node['allOf'].reduce((acc, candidate) => {
16251
+ const resolved = dereference(asRecord(candidate), root);
16252
+ return { ...acc, ...(resolved ? mergedProperties(resolved, root) ?? {} : {}) };
16253
+ }, {})
16254
+ : {};
16255
+ const merged = { ...inherited, ...direct };
16256
+ return Object.keys(merged).length ? merged : null;
16257
+ }
16258
+ function dereference(node, root) {
16259
+ const ref = typeof node?.['$ref'] === 'string' ? node['$ref'] : '';
16260
+ if (!ref.startsWith('#/'))
16261
+ return node;
16262
+ const resolved = ref.slice(2).split('/').reduce((value, segment) => {
16263
+ const record = asRecord(value);
16264
+ return record?.[segment.replace(/~1/g, '/').replace(/~0/g, '~')];
16265
+ }, root);
16266
+ return asRecord(resolved);
16267
+ }
16268
+ function asRecord(value) {
16269
+ return value && typeof value === 'object' && !Array.isArray(value)
16270
+ ? value
16271
+ : null;
16272
+ }
16273
+ function resolveOperationPath(resourcePath, pathSuffix, apiUrlEntry) {
16274
+ const resource = normalizeAbsoluteOrRootPath(resourcePath);
16275
+ const base = normalizeAbsoluteOrRootPath(buildApiUrl(apiUrlEntry));
16276
+ const baseSegments = segments(base);
16277
+ const resourceSegments = segments(resource);
16278
+ const rootRelative = resourcePath.trim().startsWith('/')
16279
+ || /^https?:\/\//i.test(resourcePath.trim());
16280
+ const alreadyPrefixed = baseSegments.length > 0
16281
+ && baseSegments.every((segment, index) => resourceSegments[index] === segment);
16282
+ const combined = rootRelative || alreadyPrefixed
16283
+ ? resourceSegments
16284
+ : [...baseSegments, ...resourceSegments];
16285
+ return `/${[...combined, ...segments(pathSuffix)].join('/')}`;
16286
+ }
16287
+ function normalizeAbsoluteOrRootPath(value) {
16288
+ const normalized = String(value || '').trim();
16289
+ if (/^https?:\/\//i.test(normalized)) {
16290
+ return new URL(normalized).pathname.replace(/\/+$/, '') || '/';
16291
+ }
16292
+ return `/${segments(normalized).join('/')}`;
16293
+ }
16294
+ function segments(value) {
16295
+ return String(value || '').split('/').map((item) => item.trim()).filter(Boolean);
16296
+ }
16297
+ function resourceDiagnostic(code, message, link, widgetKey, details) {
16298
+ return {
16299
+ id: `ui-composition-resource:${link.id}:${widgetKey}:${code}`,
16300
+ code,
16301
+ severity: 'error',
16302
+ phase: 'page-lint',
16303
+ message,
16304
+ summary: message,
16305
+ subject: { kind: 'link', linkId: link.id, widgetKey },
16306
+ details: {
16307
+ provenance: 'ui-composition-resource-preflight',
16308
+ stage: 'resource-schema-conformance',
16309
+ ...details,
16310
+ },
16311
+ source: 'authoring',
16312
+ blocking: true,
16313
+ createdAt: new Date().toISOString(),
16314
+ };
16315
+ }
16316
+ function isBlockingDiagnostic(diagnostic) {
16317
+ return diagnostic.blocking !== false
16318
+ && (diagnostic.severity === 'error' || diagnostic.severity === 'fatal');
16319
+ }
16320
+
15980
16321
  const RUNTIME_AUTHORING_METADATA_INPUTS = [
15981
16322
  'schemaVerification',
15982
16323
  'schemaEvidenceSource',
@@ -26840,4 +27181,4 @@ function providePraxisPageBuilderMetadata() {
26840
27181
  * Generated bundle index. Do not edit.
26841
27182
  */
26842
27183
 
26843
- export { ComponentPaletteDialogComponent, ConfirmDialogComponent, ConnectionEditorComponent, DynamicPageBuilderComponent, DynamicPageConfigEditorComponent, FloatingToolbarComponent, PAGE_BUILDER_AGENTIC_AUTHORING_OPTIONS, PAGE_BUILDER_AI_CAPABILITIES, PAGE_BUILDER_COMPONENT_CONTEXT_PACK, PAGE_BUILDER_WIDGET_AI_CATALOGS, PLACEHOLDER, PRAXIS_PAGE_BUILDER_AUTHORING_MANIFEST, PRAXIS_PAGE_BUILDER_COMPONENT_METADATA, PageBuilderAgenticAuthoringService, PageConfigEditorComponent, TileToolbarComponent, WidgetShellEditorComponent, clearWidgetAiCatalogs, compileUiCompositionPlan, getPageAiCatalog, getWidgetAiCapabilities, preflightUiCompositionPlan, providePageBuilderWidgetAiCatalogs, providePraxisPageBuilderMetadata, registerWidgetAiCatalog, registerWidgetAiCatalogs, resolveUiCompositionPlanTemplate, validateUiCompositionPlan };
27184
+ export { ComponentPaletteDialogComponent, ConfirmDialogComponent, ConnectionEditorComponent, DynamicPageBuilderComponent, DynamicPageConfigEditorComponent, FloatingToolbarComponent, PAGE_BUILDER_AGENTIC_AUTHORING_OPTIONS, PAGE_BUILDER_AI_CAPABILITIES, PAGE_BUILDER_COMPONENT_CONTEXT_PACK, PAGE_BUILDER_WIDGET_AI_CATALOGS, PLACEHOLDER, PRAXIS_PAGE_BUILDER_AUTHORING_MANIFEST, PRAXIS_PAGE_BUILDER_COMPONENT_METADATA, PageBuilderAgenticAuthoringService, PageConfigEditorComponent, TileToolbarComponent, UiCompositionResourcePreflightService, WidgetShellEditorComponent, clearWidgetAiCatalogs, compileUiCompositionPlan, getPageAiCatalog, getWidgetAiCapabilities, preflightUiCompositionPlan, preflightUiCompositionPlanResources, providePageBuilderWidgetAiCatalogs, providePraxisPageBuilderMetadata, registerWidgetAiCatalog, registerWidgetAiCatalogs, resolveUiCompositionPlanTemplate, validateUiCompositionPlan };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praxisui/page-builder",
3
- "version": "9.0.58",
3
+ "version": "9.0.60",
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": "^9.0.58",
12
- "@praxisui/core": "^9.0.58",
13
- "@praxisui/settings-panel": "^9.0.58",
11
+ "@praxisui/ai": "^9.0.60",
12
+ "@praxisui/core": "^9.0.60",
13
+ "@praxisui/settings-panel": "^9.0.60",
14
14
  "rxjs": "~7.8.0"
15
15
  },
16
16
  "dependencies": {
@@ -1,7 +1,7 @@
1
1
  import { MatDialogRef, MatDialog } from '@angular/material/dialog';
2
2
  import * as _angular_core from '@angular/core';
3
3
  import { EventEmitter, OnInit, WritableSignal, InjectionToken, Provider, OnChanges, OnDestroy, Type, SimpleChanges } from '@angular/core';
4
- import { ComponentDocMeta, ComponentMetadataRegistry, PraxisI18nService, WidgetShellConfig, WidgetPageAuthoringCapabilities, WidgetShellAction, EndpointRef, ComponentPortPathSegment, LinkIntent, CompositionLink, WidgetPageDefinition, TransformKind, SurfaceOpenPayload, SettingsValueProvider as SettingsValueProvider$1, WidgetPageGroupingDefinition, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, PageIdentity, WidgetStateNode, WidgetDerivedStateNode, AiCapability, AiCapabilityCategory, AiCapabilityCatalog, AiValueKind, DomainKnowledgeChangeSetRequest, DomainKnowledgeChangeSet, DomainKnowledgeChangeSetTimelineResponse, DomainKnowledgeChangeSetFilters, DomainKnowledgeValidationResponse, DomainKnowledgeStatusTransitionRequest, DomainRuleIntakeRequest, DomainRuleIntakeResponse, DomainRuleDefinitionRequest, DomainRuleDefinition, DomainRuleSimulationRequest, DomainRuleSimulationResponse, DomainRuleStatusTransitionRequest, DomainRulePublicationRequest, DomainRulePublicationResponse, DomainRuleMaterializationFilters, DomainRuleMaterialization, DomainRuleTimelineResponse, PraxisRuntimeComponentObservationEnvelope, WidgetPageCanvasLayout, WidgetPageDeviceLayouts, WidgetPageSlotAssignments, WidgetPageStateInput, WidgetInstance, TransformOutputHint, TransformSemanticKind, ComponentContextPack, ComponentAuthoringManifest, DiagnosticRecord, NestedPortCatalogRegistry, CompositionValidatorService, DynamicWidgetPageComponent, WidgetEventEnvelope, ObservabilityAgenticTurnMetrics, SettingsPanelBridge, ObservabilityAgenticTurnMetricBucket, RichTimelineItem } from '@praxisui/core';
4
+ import { ComponentDocMeta, ComponentMetadataRegistry, PraxisI18nService, WidgetShellConfig, WidgetPageAuthoringCapabilities, WidgetShellAction, EndpointRef, ComponentPortPathSegment, LinkIntent, CompositionLink, WidgetPageDefinition, TransformKind, SurfaceOpenPayload, SettingsValueProvider as SettingsValueProvider$1, WidgetPageGroupingDefinition, BUILTIN_PAGE_LAYOUT_PRESETS, BUILTIN_PAGE_THEME_PRESETS, PageIdentity, WidgetStateNode, WidgetDerivedStateNode, AiCapability, AiCapabilityCategory, AiCapabilityCatalog, AiValueKind, DomainKnowledgeChangeSetRequest, DomainKnowledgeChangeSet, DomainKnowledgeChangeSetTimelineResponse, DomainKnowledgeChangeSetFilters, DomainKnowledgeValidationResponse, DomainKnowledgeStatusTransitionRequest, DomainRuleIntakeRequest, DomainRuleIntakeResponse, DomainRuleDefinitionRequest, DomainRuleDefinition, DomainRuleSimulationRequest, DomainRuleSimulationResponse, DomainRuleStatusTransitionRequest, DomainRulePublicationRequest, DomainRulePublicationResponse, DomainRuleMaterializationFilters, DomainRuleMaterialization, DomainRuleTimelineResponse, PraxisRuntimeComponentObservationEnvelope, WidgetPageCanvasLayout, WidgetPageDeviceLayouts, WidgetPageSlotAssignments, WidgetPageStateInput, WidgetInstance, TransformOutputHint, TransformSemanticKind, ComponentContextPack, ComponentAuthoringManifest, DiagnosticRecord, NestedPortCatalogRegistry, CompositionValidatorService, ApiUrlEntry, DynamicWidgetPageComponent, WidgetEventEnvelope, ObservabilityAgenticTurnMetrics, SettingsPanelBridge, ObservabilityAgenticTurnMetricBucket, RichTimelineItem } from '@praxisui/core';
5
5
  import * as rxjs from 'rxjs';
6
6
  import { BehaviorSubject, Observable } from 'rxjs';
7
7
  import { FormGroup, FormControl, FormArray } from '@angular/forms';
@@ -1813,6 +1813,39 @@ type UiCompositionPlanPreflightResult = {
1813
1813
  */
1814
1814
  declare function preflightUiCompositionPlan(plan: UiCompositionPlan, registry: NestedPortCatalogRegistry | undefined, validator?: CompositionValidatorService): UiCompositionPlanPreflightResult;
1815
1815
 
1816
+ interface UiCompositionResourceSchemaRequest {
1817
+ widgetKey: string;
1818
+ componentId: string;
1819
+ operationId: string;
1820
+ method: 'GET' | 'POST';
1821
+ resourcePath: string;
1822
+ pathSuffix: string;
1823
+ pathInput?: string;
1824
+ requestFieldPath: string;
1825
+ apiUrlEntry?: ApiUrlEntry;
1826
+ }
1827
+ interface UiCompositionResourceSchemaResolution {
1828
+ schema: unknown;
1829
+ operationPath: string;
1830
+ expectedOperationPath: string;
1831
+ }
1832
+ type UiCompositionResourceSchemaResolver = (request: UiCompositionResourceSchemaRequest) => Promise<UiCompositionResourceSchemaResolution>;
1833
+ /**
1834
+ * Async, fail-closed resource conformance layered after the synchronous
1835
+ * compiler/registry preflight. It fetches schema evidence but never persists it
1836
+ * into the page definition.
1837
+ */
1838
+ declare function preflightUiCompositionPlanResources(plan: UiCompositionPlan, registry: ComponentMetadataRegistry, resolveSchema: UiCompositionResourceSchemaResolver): Promise<UiCompositionPlanPreflightResult>;
1839
+ /** Official HTTP-backed resource conformance service for Dynamic Page hosts. */
1840
+ declare class UiCompositionResourcePreflightService {
1841
+ private readonly registry;
1842
+ private readonly discovery;
1843
+ preflight(plan: UiCompositionPlan, registry?: ComponentMetadataRegistry): Promise<UiCompositionPlanPreflightResult>;
1844
+ private resolveSchema;
1845
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<UiCompositionResourcePreflightService, never>;
1846
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<UiCompositionResourcePreflightService>;
1847
+ }
1848
+
1816
1849
  type PageBuilderGovernedContinuationFamily = 'shared-rule' | 'project-knowledge';
1817
1850
  type PageBuilderGovernedContinuationActionKind = 'open_definition' | 'simulate' | 'approve' | 'reject' | 'publish' | 'materialize' | 'validate_enforcement' | 'create_knowledge_change_set' | 'validate_knowledge_change_set' | 'apply_knowledge_change_set' | 'open_timeline';
1818
1851
  type PageBuilderGovernedContinuationBlockedReason = 'busy' | 'missing_handoff' | 'missing_authoring_target' | 'missing_definition' | 'definition_already_available' | 'missing_materialization' | 'missing_project_knowledge_audit' | 'missing_project_knowledge_candidate' | 'change_set_already_available' | 'missing_change_set' | 'validation_required' | 'validation_already_valid' | 'approval_required' | 'already_applied' | 'timeline_unavailable' | 'timeline_contract_unwired';
@@ -2279,5 +2312,5 @@ declare const PRAXIS_PAGE_BUILDER_COMPONENT_METADATA: ComponentDocMeta;
2279
2312
  /** Provider para auto-registrar metadados do componente Page Builder. */
2280
2313
  declare function providePraxisPageBuilderMetadata(): Provider;
2281
2314
 
2282
- export { ComponentPaletteDialogComponent, ConfirmDialogComponent, ConnectionEditorComponent, DynamicPageBuilderComponent, DynamicPageConfigEditorComponent, FloatingToolbarComponent, PAGE_BUILDER_AGENTIC_AUTHORING_OPTIONS, PAGE_BUILDER_AI_CAPABILITIES, PAGE_BUILDER_COMPONENT_CONTEXT_PACK, PAGE_BUILDER_WIDGET_AI_CATALOGS, PLACEHOLDER, PRAXIS_PAGE_BUILDER_AUTHORING_MANIFEST, PRAXIS_PAGE_BUILDER_COMPONENT_METADATA, PageBuilderAgenticAuthoringService, PageConfigEditorComponent, TileToolbarComponent, WidgetShellEditorComponent, clearWidgetAiCatalogs, compileUiCompositionPlan, getPageAiCatalog, getWidgetAiCapabilities, preflightUiCompositionPlan, providePageBuilderWidgetAiCatalogs, providePraxisPageBuilderMetadata, registerWidgetAiCatalog, registerWidgetAiCatalogs, resolveUiCompositionPlanTemplate, validateUiCompositionPlan };
2283
- export type { Capability, CapabilityCatalog, CapabilityCategory, ComponentPaletteData, ComponentPaletteSelection, ConfirmDialogData, PageBuilderAgenticAuthoringAttachmentSummary, PageBuilderAgenticAuthoringCandidate, PageBuilderAgenticAuthoringComponentCapabilitiesResult, PageBuilderAgenticAuthoringComponentCapability, PageBuilderAgenticAuthoringComponentCapabilityCatalog, PageBuilderAgenticAuthoringComponentCapabilityExample, PageBuilderAgenticAuthoringComponentFieldAlias, PageBuilderAgenticAuthoringConversationMessage, PageBuilderAgenticAuthoringGateResult, PageBuilderAgenticAuthoringIntentResolutionRequest, PageBuilderAgenticAuthoringIntentResolutionResult, PageBuilderAgenticAuthoringOptions, PageBuilderAgenticAuthoringPendingClarification, PageBuilderAgenticAuthoringPromptRequest, PageBuilderAgenticAuthoringProvider, PageBuilderAgenticAuthoringQuickReply, PageBuilderAgenticAuthoringResourceCandidatesRequest, PageBuilderAgenticAuthoringResourceCandidatesResult, PageBuilderAgenticAuthoringSharedRuleHandoff, PageBuilderAgenticAuthoringTarget, PageBuilderAgenticAuthoringTurnStreamConnectionError, PageBuilderAgenticAuthoringTurnStreamEvent, PageBuilderAgenticAuthoringTurnStreamRequest, PageBuilderAgenticAuthoringTurnStreamStartResponse, PageBuilderApplyRequest, PageBuilderApplyResult, PageBuilderAuthoringCapabilities, PageBuilderCompiledFormPatch, PageBuilderMinimalFormPlanResult, PageBuilderPreviewDiagnostics, PageBuilderPreviewResult, PageBuilderProjectKnowledgeAudit, UiCompositionPlan, UiCompositionPlanAuthoringInput, UiCompositionPlanBinding, UiCompositionPlanCompileResult, UiCompositionPlanComponentPortEndpoint, UiCompositionPlanContextScope, UiCompositionPlanContextScopeTarget, UiCompositionPlanContextValue, UiCompositionPlanDiagnostic, UiCompositionPlanEndpoint, UiCompositionPlanGlobalActionEndpoint, UiCompositionPlanGlobalActionSourceEndpoint, UiCompositionPlanInputSource, UiCompositionPlanPreflightResult, UiCompositionPlanSelectionSync, UiCompositionPlanSelectionSyncMapping, UiCompositionPlanSourceEndpoint, UiCompositionPlanStateEndpoint, UiCompositionPlanTargetEndpoint, UiCompositionPlanTemplateMaterialization, UiCompositionPlanTemplateRef, UiCompositionPlanTemplateReference, UiCompositionPlanTemplateResolutionResult, UiCompositionPlanTemplateRevision, UiCompositionPlanTransform, UiCompositionPlanTransformOutput, UiCompositionPlanWidget, ValueKind };
2315
+ export { ComponentPaletteDialogComponent, ConfirmDialogComponent, ConnectionEditorComponent, DynamicPageBuilderComponent, DynamicPageConfigEditorComponent, FloatingToolbarComponent, PAGE_BUILDER_AGENTIC_AUTHORING_OPTIONS, PAGE_BUILDER_AI_CAPABILITIES, PAGE_BUILDER_COMPONENT_CONTEXT_PACK, PAGE_BUILDER_WIDGET_AI_CATALOGS, PLACEHOLDER, PRAXIS_PAGE_BUILDER_AUTHORING_MANIFEST, PRAXIS_PAGE_BUILDER_COMPONENT_METADATA, PageBuilderAgenticAuthoringService, PageConfigEditorComponent, TileToolbarComponent, UiCompositionResourcePreflightService, WidgetShellEditorComponent, clearWidgetAiCatalogs, compileUiCompositionPlan, getPageAiCatalog, getWidgetAiCapabilities, preflightUiCompositionPlan, preflightUiCompositionPlanResources, providePageBuilderWidgetAiCatalogs, providePraxisPageBuilderMetadata, registerWidgetAiCatalog, registerWidgetAiCatalogs, resolveUiCompositionPlanTemplate, validateUiCompositionPlan };
2316
+ export type { Capability, CapabilityCatalog, CapabilityCategory, ComponentPaletteData, ComponentPaletteSelection, ConfirmDialogData, PageBuilderAgenticAuthoringAttachmentSummary, PageBuilderAgenticAuthoringCandidate, PageBuilderAgenticAuthoringComponentCapabilitiesResult, PageBuilderAgenticAuthoringComponentCapability, PageBuilderAgenticAuthoringComponentCapabilityCatalog, PageBuilderAgenticAuthoringComponentCapabilityExample, PageBuilderAgenticAuthoringComponentFieldAlias, PageBuilderAgenticAuthoringConversationMessage, PageBuilderAgenticAuthoringGateResult, PageBuilderAgenticAuthoringIntentResolutionRequest, PageBuilderAgenticAuthoringIntentResolutionResult, PageBuilderAgenticAuthoringOptions, PageBuilderAgenticAuthoringPendingClarification, PageBuilderAgenticAuthoringPromptRequest, PageBuilderAgenticAuthoringProvider, PageBuilderAgenticAuthoringQuickReply, PageBuilderAgenticAuthoringResourceCandidatesRequest, PageBuilderAgenticAuthoringResourceCandidatesResult, PageBuilderAgenticAuthoringSharedRuleHandoff, PageBuilderAgenticAuthoringTarget, PageBuilderAgenticAuthoringTurnStreamConnectionError, PageBuilderAgenticAuthoringTurnStreamEvent, PageBuilderAgenticAuthoringTurnStreamRequest, PageBuilderAgenticAuthoringTurnStreamStartResponse, PageBuilderApplyRequest, PageBuilderApplyResult, PageBuilderAuthoringCapabilities, PageBuilderCompiledFormPatch, PageBuilderMinimalFormPlanResult, PageBuilderPreviewDiagnostics, PageBuilderPreviewResult, PageBuilderProjectKnowledgeAudit, UiCompositionPlan, UiCompositionPlanAuthoringInput, UiCompositionPlanBinding, UiCompositionPlanCompileResult, UiCompositionPlanComponentPortEndpoint, UiCompositionPlanContextScope, UiCompositionPlanContextScopeTarget, UiCompositionPlanContextValue, UiCompositionPlanDiagnostic, UiCompositionPlanEndpoint, UiCompositionPlanGlobalActionEndpoint, UiCompositionPlanGlobalActionSourceEndpoint, UiCompositionPlanInputSource, UiCompositionPlanPreflightResult, UiCompositionPlanSelectionSync, UiCompositionPlanSelectionSyncMapping, UiCompositionPlanSourceEndpoint, UiCompositionPlanStateEndpoint, UiCompositionPlanTargetEndpoint, UiCompositionPlanTemplateMaterialization, UiCompositionPlanTemplateRef, UiCompositionPlanTemplateReference, UiCompositionPlanTemplateResolutionResult, UiCompositionPlanTemplateRevision, UiCompositionPlanTransform, UiCompositionPlanTransformOutput, UiCompositionPlanWidget, UiCompositionResourceSchemaRequest, UiCompositionResourceSchemaResolution, UiCompositionResourceSchemaResolver, ValueKind };