@scion/workbench 20.0.0-beta.6 → 20.0.0-beta.7

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.
@@ -19,7 +19,7 @@ import * as i1 from '@angular/forms';
19
19
  import { NonNullableFormBuilder, ReactiveFormsModule, NG_VALUE_ACCESSOR } from '@angular/forms';
20
20
  import { FocusMonitor, CdkTrapFocus } from '@angular/cdk/a11y';
21
21
  import { trigger, transition, style, animate } from '@angular/animations';
22
- import { ManifestService, MicrofrontendPlatform, PlatformState, MessageHeaders, MessageClient, ResponseStatusCodes, OutletRouter, MicrofrontendPlatformConfig, APP_IDENTITY, mapToBody, HostManifestInterceptor, ObservableDecorator, IntentInterceptor, CapabilityInterceptor, MicrofrontendPlatformHost, IntentClient, PlatformPropertyService } from '@scion/microfrontend-platform';
22
+ import { ManifestService, MicrofrontendPlatform, PlatformState, MessageHeaders, MessageClient, ResponseStatusCodes, mapToBody, OutletRouter, MicrofrontendPlatformConfig, APP_IDENTITY, HostManifestInterceptor, ObservableDecorator, IntentInterceptor, CapabilityInterceptor, MicrofrontendPlatformHost, IntentClient, PlatformPropertyService } from '@scion/microfrontend-platform';
23
23
  import { Beans } from '@scion/toolkit/bean-manager';
24
24
  import { ɵMicrofrontendRouteParams as _MicrofrontendRouteParams, WorkbenchCapabilities, WorkbenchTextService, WorkbenchMessageBox, eMESSAGE_BOX_MESSAGE_PARAM, ɵTHEME_CONTEXT_KEY as _THEME_CONTEXT_KEY, ɵWorkbenchCommands as _WorkbenchCommands, ɵPOPUP_CONTEXT as _POPUP_CONTEXT, ɵWorkbenchPopupMessageHeaders as _WorkbenchPopupMessageHeaders, WorkbenchPopup, ɵMESSAGE_BOX_CONTEXT as _MESSAGE_BOX_CONTEXT, ɵDIALOG_CONTEXT as _DIALOG_CONTEXT, ɵWorkbenchDialogMessageHeaders as _WorkbenchDialogMessageHeaders, WorkbenchDialog as WorkbenchDialog$1, WorkbenchRouter as WorkbenchRouter$1, WorkbenchPopupService, WorkbenchMessageBoxService as WorkbenchMessageBoxService$1, ɵWorkbenchMessageBoxService as _WorkbenchMessageBoxService, WorkbenchDialogService as WorkbenchDialogService$1, ɵWorkbenchDialogService as _WorkbenchDialogService, WorkbenchNotificationService, ɵWorkbenchTextService as _WorkbenchTextService, ɵVIEW_ID_CONTEXT_KEY as _VIEW_ID_CONTEXT_KEY, WorkbenchClient } from '@scion/workbench-client';
25
25
  import { SciThrobberComponent } from '@scion/components/throbber';
@@ -5570,22 +5570,22 @@ function parseMatrixParams(matrixParams) {
5570
5570
  return {};
5571
5571
  }
5572
5572
  const params = {};
5573
- for (const match of escapeSemicolon(matrixParams).matchAll(/(?<paramName>[^=;]+)=(?<paramValue>[^;]*)/g)) {
5573
+ for (const match of encodeEscapedSemicolons(matrixParams).matchAll(/(?<paramName>[^=;]+)=(?<paramValue>[^;]*)/g)) {
5574
5574
  const { paramName, paramValue } = match.groups;
5575
- params[unescapeSemicolon(paramName)] = unescapeSemicolon(paramValue);
5575
+ params[paramName] = decodeSemicolons(paramValue);
5576
5576
  }
5577
5577
  return params;
5578
5578
  /**
5579
- * Replaces escaped semicolons (`\;`) with the placeholder (`ɵ`) to prevent interpretation as key-value separators.
5579
+ * Encodes escaped semicolons (`\\;`) as `&#x3b` (Unicode) to prevent interpretation as interpolation parameter separators.
5580
5580
  */
5581
- function escapeSemicolon(value) {
5582
- return value.replaceAll('\\;', 'ɵ');
5581
+ function encodeEscapedSemicolons(value) {
5582
+ return value.replaceAll('\\;', '&#x3b');
5583
5583
  }
5584
5584
  /**
5585
- * Restores escaped semicolons by replacing the placeholder (`ɵ`) back to semicolons (`;`).
5585
+ * Decodes encoded semicolons (`&#x3b`) back to semicolons (`;`).
5586
5586
  */
5587
- function unescapeSemicolon(value) {
5588
- return value.replaceAll('ɵ', ';');
5587
+ function decodeSemicolons(value) {
5588
+ return value.replaceAll('&#x3b', ';');
5589
5589
  }
5590
5590
  }
5591
5591
 
@@ -14359,47 +14359,72 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.2.3", ngImpor
14359
14359
  /**
14360
14360
  * Registers a text provider for the SCION Workbench to get texts from micro apps.
14361
14361
  *
14362
- * If the key matches the format of a remote key, text is requested via intent from the respective 'text-provider' capability.
14363
- * If not, the key is ignored and `undefined` is returned.
14364
- *
14365
- * Remote key format: "workbench.external.~<APP_SYMBOLIC_NAME>~.<TEXT_KEY>".
14362
+ * This text provider provides texts for keys matching the format: "workbench.external.scion-workbench-client.<APP_SYMBOLIC_NAME>.<TRANSLATABLE>".
14366
14363
  *
14367
14364
  * @see createRemoteTranslatable
14368
14365
  */
14369
14366
  function provideRemoteTextProvider() {
14370
- const REMOTE_KEY = /^workbench\.external\.~(?<provider>[^\\~]+)~\.(?<key>.+)$/;
14367
+ const REMOTE_TRANSLATION_KEY = /^workbench\.external\.scion-workbench-client\.(?<provider>[^\\.]+)\.%(?<key>.+)$/;
14368
+ const REMOTE_TEXT = /^workbench\.external\.scion-workbench-client\.(?<provider>[^\\.]+)\.(?<text>[^%].+)$/;
14371
14369
  return makeEnvironmentProviders([
14372
14370
  {
14373
14371
  provide: WORKBENCH_TEXT_PROVIDER,
14374
- useValue: remoteTextProvider,
14372
+ useValue: provideRemoteText,
14373
+ multi: true,
14374
+ },
14375
+ {
14376
+ provide: WORKBENCH_TEXT_PROVIDER,
14377
+ useValue: interpolateRemoteText,
14375
14378
  multi: true,
14376
14379
  },
14377
14380
  ]);
14378
- function remoteTextProvider(remoteKey, params) {
14379
- // Test if the key matches a remote key.
14380
- const match = REMOTE_KEY.exec(remoteKey);
14381
+ /**
14382
+ * Provides text from a remote app.
14383
+ */
14384
+ function provideRemoteText(translationKey, params) {
14385
+ // Test if the key matches a remote translation key.
14386
+ const match = REMOTE_TRANSLATION_KEY.exec(translationKey);
14381
14387
  if (!match) {
14382
- return undefined; // ignore key
14388
+ return undefined;
14383
14389
  }
14384
- // Parse key and provider from the remote key.
14390
+ // Parse key and provider from the remote translation key.
14385
14391
  const { key, provider } = match.groups;
14386
14392
  // Request the text by intent. Parameters starting with the topic protocol 'topic://' are resolved via messaging.
14387
- const text$ = observeParams$(params)
14388
- .pipe(map$1(params => params.reduce((translatable, [name, value]) => `${translatable};${name}=${value}`, `%${key}`)), switchMap(translatable => Beans.get(WorkbenchTextService).text$(translatable, { provider: provider })), map$1(text => text ?? key));
14393
+ const text$ = observeParams$(params, { provider })
14394
+ .pipe(map$1(params => params.reduce((translatable, [param, value]) => `${translatable};${param}=${encodeSemicolons$1(value)}`, `%${key}`)), switchMap(translatable => Beans.get(WorkbenchTextService).text$(translatable, { provider })), map$1(text => text ?? key));
14389
14395
  return toSignal(text$, { initialValue: '' });
14390
14396
  }
14391
14397
  /**
14392
- * Creates an Observable that emits tuples of name/value pairs from the passed parameters.
14398
+ * Substitutes named parameters in a remote text.
14399
+ */
14400
+ function interpolateRemoteText(translationKey, params) {
14401
+ // Test if the key matches a remote text.
14402
+ const match = REMOTE_TEXT.exec(translationKey);
14403
+ if (!match) {
14404
+ return undefined;
14405
+ }
14406
+ // Parse text and provider from the remote text.
14407
+ const { text, provider } = match.groups;
14408
+ // Substitute params. Parameters starting with the topic protocol 'topic://' are resolved via messaging.
14409
+ const text$ = observeParams$(params, { provider })
14410
+ .pipe(map$1(params => params.reduce((text, [param, value]) => text.replaceAll(`:${param}`, value), decodeSemicolons(text))));
14411
+ return toSignal(text$, { initialValue: '' });
14412
+ }
14413
+ /**
14414
+ * Creates an Observable that emits tuples of name-value pairs from the passed parameters.
14393
14415
  *
14394
14416
  * Parameters starting with the topic protocol 'topic://' are resolved via topic-based messaging.
14395
14417
  */
14396
- function observeParams$(params) {
14397
- const observableParams = Object.entries(params).map(([name, value]) => {
14418
+ function observeParams$(params, options) {
14419
+ const observableParams = Object.entries(params).map(([param, value]) => {
14398
14420
  if (!value.startsWith(TOPIC_PROTOCOL)) {
14399
- return of([name, value]);
14421
+ return of([param, value]);
14400
14422
  }
14401
14423
  const topic = value.substring(TOPIC_PROTOCOL.length);
14402
- return Beans.get(MessageClient).request$(topic, undefined, { retain: true }).pipe(map$1(({ body: resolved }) => ([name, resolved ?? ''])));
14424
+ return Beans.get(MessageClient).request$(topic, undefined, { retain: true })
14425
+ .pipe(mapToBody(),
14426
+ // Resolve text if the resolver returns a translatable.
14427
+ switchMap(resolved => resolved?.startsWith('%') ? Beans.get(WorkbenchTextService).text$(resolved, options) : of(resolved)), map$1(resolved => [param, resolved ?? '']));
14403
14428
  });
14404
14429
  return observableParams.length ? combineLatest(observableParams) : of([]);
14405
14430
  }
@@ -14407,38 +14432,77 @@ function provideRemoteTextProvider() {
14407
14432
  /**
14408
14433
  * Creates a translatable for the SCION Workbench to request the text from a micro app.
14409
14434
  *
14410
- * Passed parameters are used to substitute named interpolation parameters. A named interpolation parameter starts with a colon (`:`) followed by a name.
14435
+ * Passed parameters are used to substitute named parameters in the text or interpolation parameters of the translation key.
14411
14436
  *
14412
- * Example: %translationKey;param1=value1;param2=:value2 // :value2 is a named interpolation parameter
14437
+ * A named parameter starts with a colon (`:`) followed by the parameter name and can be substituted by an explicit value (passed as value param) or topic (passed as topic param).
14438
+ * Topic params define a topic with the actual value requested when resolving the translatable. A topic can also reference value params as named params in topic segments.
14413
14439
  *
14414
- * Named interpolation parameters can be substituted by explicit values (passed as value params) or topics (passed as topic params).
14415
- * Unlike value params, topic params define a topic and the actual value will be requested when resolving the translatable.
14416
- * Like the translatable's interpolation params, a topic can reference value params as named parameters in topic segments.
14440
+ * @example - Translation Key
14441
+ * `%translationKey;param1=:namedParam1;param2=:namedParam2`
14442
+ *
14443
+ * @example - Text
14444
+ * `Text with :namedParam1 and :namedParam2`
14417
14445
  *
14418
14446
  * @param translatable - Specifies the translatable.
14419
- * @param config - Specifies the text provider and values for substituting named interpolation parameters.
14447
+ * @param config - Specifies the text provider and values for substituting named parameters.
14420
14448
  * @return Translatable that can be passed to the workbench's {@link text()} function for translation.
14421
14449
  *
14422
14450
  * @see provideRemoteTextProvider
14423
14451
  */
14424
14452
  function createRemoteTranslatable(translatable, config) {
14425
- if (!translatable?.startsWith('%')) {
14453
+ if (!translatable) {
14454
+ return translatable;
14455
+ }
14456
+ // Create Map of value params.
14457
+ const valueParams = new Map(Object.entries(Dictionaries.coerce(config.valueParams)).map(([param, value]) => [param, encodeSemicolons$1(value)]));
14458
+ // Create Map of topic params, substituting referenced value params.
14459
+ const topicParams = new Map(Object.entries(Dictionaries.coerce(config.topicParams)).map(([param, topic]) => [param, toTopicParam(topic)]));
14460
+ // Return text as-is if not a translation key nor referencing parameters.
14461
+ if (!translatable.startsWith('%') && !containsParam(translatable, valueParams) && !containsParam(translatable, topicParams)) {
14426
14462
  return translatable;
14427
14463
  }
14428
- const remoteTranslatable = `%workbench.external.~${config.appSymbolicName}~.${translatable.substring(1)}`;
14429
- const valueParams = Dictionaries.coerce(config.valueParams);
14430
- const topicParams = Object.fromEntries(Object.entries(Dictionaries.coerce(config.topicParams))
14431
- // Replace named params in topic segments.
14432
- .map(([name, topic]) => [name, topic.replace(/(?<=\/|^):(?<namedParam>[^/]+)/g, (match, namedParam) => `${valueParams[namedParam] ?? match}`)])
14433
- // Add topic protocol to indicate resolution via topic-based messaging.
14434
- .map(([paramName, topic]) => [paramName, `${TOPIC_PROTOCOL}${topic}`]));
14435
- // Replace named params in matrix param values.
14436
- return remoteTranslatable.replace(/(?<==):(?<namedParam>[^;]+)/g, (match, namedParam) => `${valueParams[namedParam] ?? topicParams[namedParam] ?? match}`);
14464
+ const remoteTranslatablePrefix = `%workbench.external.scion-workbench-client.${config.appSymbolicName}`;
14465
+ if (translatable.startsWith('%')) {
14466
+ // Substitute named parameters in interpolation params.
14467
+ return `${remoteTranslatablePrefix}.${translatable}`.replace(/(?<==):(?<namedParam>[^;]+)/g, (match, param) => valueParams.get(param) ?? topicParams.get(param) ?? match);
14468
+ }
14469
+ else {
14470
+ // Append referenced parameters in matrix notation.
14471
+ return [...valueParams, ...topicParams]
14472
+ .filter(([param]) => translatable.includes(`:${param}`))
14473
+ .reduce((translatable, [param, value]) => `${translatable};${param}=${value}`, `${remoteTranslatablePrefix}.${encodeSemicolons$1(translatable)}`);
14474
+ }
14475
+ /**
14476
+ * Adds the topic protocol to indicate resolution via topic-based messaging and substitutes named topic segments.
14477
+ */
14478
+ function toTopicParam(topic) {
14479
+ return `${TOPIC_PROTOCOL}${topic.replace(/(?<=\/|^):(?<namedParam>[^/]+)/g, (match, namedParam) => {
14480
+ return valueParams.get(namedParam) ?? match;
14481
+ })}`;
14482
+ }
14483
+ /**
14484
+ * Tests whether the passed text references any of the passed params.
14485
+ */
14486
+ function containsParam(text, params) {
14487
+ return Array.from(params.keys()).some(param => text.includes(`:${param}`));
14488
+ }
14437
14489
  }
14438
14490
  /**
14439
14491
  * Prefix of topic params.
14440
14492
  */
14441
14493
  const TOPIC_PROTOCOL = 'topic://';
14494
+ /**
14495
+ * Encodes semicolons (`;`) as `&#x3b` (Unicode) to prevent interpretation as interpolation parameter separators.
14496
+ */
14497
+ function encodeSemicolons$1(value) {
14498
+ return `${value}`.replaceAll(';', '&#x3b');
14499
+ }
14500
+ /**
14501
+ * Decodes encoded semicolons (`&#x3b`) back to semicolons (`;`).
14502
+ */
14503
+ function decodeSemicolons(value) {
14504
+ return value.replaceAll('&#x3b', ';');
14505
+ }
14442
14506
 
14443
14507
  /*
14444
14508
  * Copyright (c) 2018-2024 Swiss Federal Railways
@@ -17055,7 +17119,7 @@ function provideHostTextProvider() {
17055
17119
  provideMicrofrontendPlatformInitializer(() => {
17056
17120
  const injector = inject(Injector);
17057
17121
  WorkbenchClient.registerTextProvider((key, params) => {
17058
- const translatable = Object.entries(params).reduce((translatable, [name, value]) => `${translatable};${name}=${value}`, `%${key}`);
17122
+ const translatable = Object.entries(params).reduce((translatable, [name, value]) => `${translatable};${name}=${encodeSemicolons(value)}`, `%${key}`);
17059
17123
  const environmentInjector = createEnvironmentInjector([], injector.get(EnvironmentInjector));
17060
17124
  return toObservable(text(translatable, { injector: environmentInjector }), { injector: environmentInjector })
17061
17125
  .pipe(map$1(text => text !== '' && text !== key ? text : undefined), // emit `undefined` if not found the text
@@ -17064,6 +17128,14 @@ function provideHostTextProvider() {
17064
17128
  }),
17065
17129
  ]);
17066
17130
  }
17131
+ /**
17132
+ * Encodes semicolons (`;`) as `\\;` to prevent interpretation as interpolation parameter separators.
17133
+ *
17134
+ * @see Translatable
17135
+ */
17136
+ function encodeSemicolons(value) {
17137
+ return value.replaceAll(';', '\\;');
17138
+ }
17067
17139
 
17068
17140
  /*
17069
17141
  * Copyright (c) 2018-2024 Swiss Federal Railways