@praxisui/core 9.0.57 → 9.0.58

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
@@ -167,6 +167,28 @@ Component-owned config editors, AI authoring manifests, and runtime profiles are
167
167
 
168
168
  The validator performs no rendering, dispatch, HTTP call or persistence. Pass the same `ComponentMetadataRegistry` that the target host uses for dynamic materialization. Registry presence is the materializability evidence supported by the current contract; component packages remain responsible for publishing and testing their own metadata providers.
169
169
 
170
+ ## Remote Configuration Persistence
171
+
172
+ `providePraxisGlobalConfigBootstrap` is the official host entry point for
173
+ `ApiConfigStorage`. Set `scope` explicitly whenever ownership is a product
174
+ decision instead of a lookup preference:
175
+
176
+ ```ts
177
+ providePraxisGlobalConfigBootstrap({
178
+ remote: { apiPath: "/api" },
179
+ scope: "tenant",
180
+ headersFactory: () => enterpriseRuntimeContext.headers(),
181
+ errorPolicy: "fail",
182
+ });
183
+ ```
184
+
185
+ Use `scope: "tenant"` for a shared governed document such as an application
186
+ page composition, and `scope: "user"` for personal preferences. Omitting the
187
+ option deliberately preserves the `praxis-config-starter` resolution rule:
188
+ requests carrying `X-User-ID` resolve user config first, while tenant config is
189
+ the fallback for reads. Scope selects persistence ownership; it never replaces
190
+ server-side identity resolution or write authorization.
191
+
170
192
  ## Dynamic Page Runtime
171
193
 
172
194
  `DynamicWidgetPageComponent` renders `WidgetPageDefinition` documents.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "schemaVersion": "1.0.0",
3
- "generatedAt": "2026-09-01T14:12:48.929Z",
3
+ "generatedAt": "2026-09-03T03:04:37.933Z",
4
4
  "packageName": "@praxisui/core",
5
- "packageVersion": "9.0.57",
5
+ "packageVersion": "9.0.58",
6
6
  "sourceRegistry": "praxis-component-registry-ingestion",
7
7
  "sourceRegistryVersion": "1.0.0",
8
8
  "componentCount": 4,
@@ -16296,6 +16296,7 @@ class ResourceActionOpenAdapterService {
16296
16296
  if (options.initialValue) {
16297
16297
  payload.widget.inputs['initialValue'] = this.clone(options.initialValue);
16298
16298
  }
16299
+ this.applyInteractionPresentation(payload, action);
16299
16300
  if (action.scope === 'ITEM') {
16300
16301
  if (options.resourceId != null) {
16301
16302
  payload.widget.inputs['resourceId'] = options.resourceId;
@@ -16313,6 +16314,43 @@ class ResourceActionOpenAdapterService {
16313
16314
  this.applyExecutionInputs(payload, action, options);
16314
16315
  return payload;
16315
16316
  }
16317
+ applyInteractionPresentation(payload, action) {
16318
+ const requiresFormConfirmation = action.execution?.interaction.mode === 'FORM'
16319
+ && action.execution.interaction.confirmationRequired;
16320
+ const confirmationMessage = requiresFormConfirmation
16321
+ ? String(action.description ?? '').trim() || String(action.title ?? '').trim()
16322
+ : '';
16323
+ if (requiresFormConfirmation && !confirmationMessage) {
16324
+ throw new Error(`ResourceActionOpenAdapterService requires description or title for confirmed FORM action "${action.id}".`);
16325
+ }
16326
+ const successMessage = String(action.successMessage ?? '').trim();
16327
+ if (!confirmationMessage && !successMessage) {
16328
+ return;
16329
+ }
16330
+ const inputs = payload.widget.inputs;
16331
+ const currentConfig = this.clone(inputs['config'] ?? {});
16332
+ const currentMessages = this.clone(currentConfig['messages'] ?? {});
16333
+ if (confirmationMessage) {
16334
+ currentMessages['confirmations'] = {
16335
+ ...(currentMessages['confirmations'] ?? {}),
16336
+ submit: confirmationMessage,
16337
+ };
16338
+ }
16339
+ if (successMessage) {
16340
+ const customActions = {
16341
+ ...(currentMessages['customActions'] ?? {}),
16342
+ };
16343
+ customActions['submit'] = {
16344
+ ...(customActions['submit'] ?? {}),
16345
+ success: successMessage,
16346
+ };
16347
+ currentMessages['customActions'] = customActions;
16348
+ }
16349
+ inputs['config'] = {
16350
+ ...currentConfig,
16351
+ messages: currentMessages,
16352
+ };
16353
+ }
16316
16354
  resolveDynamicFormPreset() {
16317
16355
  const preset = SURFACE_OPEN_PRESETS.find((candidate) => candidate.id === 'praxis-dynamic-form');
16318
16356
  if (!preset) {
@@ -19802,6 +19840,7 @@ function providePraxisGlobalConfigBootstrap(options) {
19802
19840
  const baseUrl = resolveBaseUrl(normalized.remote);
19803
19841
  const apiOptions = {
19804
19842
  ...(baseUrl ? { baseUrl } : {}),
19843
+ ...(normalized.scope ? { scope: normalized.scope } : {}),
19805
19844
  headersFactory,
19806
19845
  defaultHeaders: normalized.includeDefaultHeaders ? undefined : {},
19807
19846
  errorPolicy: normalized.errorPolicy,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@praxisui/core",
3
- "version": "9.0.57",
3
+ "version": "9.0.58",
4
4
  "description": "Core library for Praxis UI Workspace: types, tokens, services and utilities shared across @praxisui/* packages.",
5
5
  "peerDependencies": {
6
6
  "@angular/common": "^21.0.0",
@@ -6191,8 +6191,13 @@ declare const CONFIG_STORAGE: InjectionToken<ConfigStorage>;
6191
6191
  */
6192
6192
  interface ApiConfigStorageOptions {
6193
6193
  baseUrl?: string;
6194
- /** Optional ui_user_config scope query param, e.g. "user" for user-owned configs. */
6195
- scope?: string;
6194
+ /**
6195
+ * Explicit ui_user_config ownership.
6196
+ *
6197
+ * Omit only when the host deliberately wants the backend resolution rule
6198
+ * (user first when X-User-ID is present, then tenant on reads).
6199
+ */
6200
+ scope?: 'user' | 'tenant';
6196
6201
  /** Factory to supply headers (tenant/user/env/updatedBy) per request. */
6197
6202
  headersFactory?: () => Record<string, string | undefined>;
6198
6203
  /** Default headers applied when the headersFactory omits them. */
@@ -10863,6 +10868,7 @@ interface ResourceActionOpenAdapterOptions {
10863
10868
  declare class ResourceActionOpenAdapterService {
10864
10869
  private readonly discovery;
10865
10870
  toPayload(action: ResourceActionCatalogItem, options: ResourceActionOpenAdapterOptions): SurfaceOpenPayload;
10871
+ private applyInteractionPresentation;
10866
10872
  private resolveDynamicFormPreset;
10867
10873
  private buildStableInstanceId;
10868
10874
  private applyExecutionInputs;
@@ -11867,6 +11873,13 @@ interface PraxisGlobalConfigBootstrapOptions {
11867
11873
  baseUrl?: string;
11868
11874
  apiPath?: string;
11869
11875
  };
11876
+ /**
11877
+ * Explicit ownership for every config key handled by this bootstrap.
11878
+ * Use `tenant` for shared governed configuration and `user` for personal
11879
+ * preferences. When omitted, praxis-config-starter applies its canonical
11880
+ * header-based resolution rule.
11881
+ */
11882
+ scope?: 'user' | 'tenant';
11870
11883
  headers?: Record<string, string>;
11871
11884
  headersFactory?: () => Record<string, string>;
11872
11885
  includeDefaultHeaders?: boolean;