@openhi/constructs 0.0.132 → 0.0.134

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/lib/index.js CHANGED
@@ -796,7 +796,6 @@ __export(src_exports, {
796
796
  DataStorePostgresReplica: () => DataStorePostgresReplica,
797
797
  DiscoverableStringParameter: () => DiscoverableStringParameter,
798
798
  DynamoDbDataStore: () => DynamoDbDataStore,
799
- OPENHI_PR_NUMBER_ENV_VAR: () => OPENHI_PR_NUMBER_ENV_VAR,
800
799
  OPENHI_REPO_TAG_KEY_ENV_VAR: () => OPENHI_REPO_TAG_KEY_ENV_VAR,
801
800
  OPENHI_RESOURCE_URN_SYSTEM: () => OPENHI_RESOURCE_URN_SYSTEM,
802
801
  OPENHI_TAG_KEY_PREFIX_ENV_VAR: () => OPENHI_TAG_KEY_PREFIX_ENV_VAR,
@@ -1207,10 +1206,14 @@ var OpenHiService = class extends import_aws_cdk_lib4.Stack {
1207
1206
  );
1208
1207
  }
1209
1208
  /**
1210
- * DNS prefix for this branche's child zone.
1209
+ * DNS prefix for this branche's child zone. Capped at 56 chars so
1210
+ * that a `<service>-<prefix>` hostname segment stays under the 63-byte
1211
+ * DNS label limit even for the longest current prefix (`admin-`, 6
1212
+ * bytes; the matching API uses `api-`, 4 bytes). 56 leaves 1 byte of
1213
+ * headroom on the longer side.
1211
1214
  */
1212
1215
  get childZonePrefix() {
1213
- return (0, import_change_case.paramCase)(this.branchName).slice(0, 200);
1216
+ return (0, import_change_case.paramCase)(this.branchName).slice(0, 56);
1214
1217
  }
1215
1218
  };
1216
1219
 
@@ -2355,7 +2358,7 @@ var import_aws_route53_targets = require("aws-cdk-lib/aws-route53-targets");
2355
2358
  var import_aws_s3 = require("aws-cdk-lib/aws-s3");
2356
2359
  var import_constructs8 = require("constructs");
2357
2360
  var STATIC_HOSTING_SERVICE_TYPE = "website";
2358
- var PER_BRANCH_PREVIEW_PREFIX = "admin-pr-";
2361
+ var PER_BRANCH_PREVIEW_PREFIX = "admin-";
2359
2362
  var DEFAULT_PREVIEW_EXPIRATION_DAYS = 14;
2360
2363
  var _StaticHosting = class _StaticHosting extends import_constructs8.Construct {
2361
2364
  /**
@@ -2434,10 +2437,14 @@ var _StaticHosting = class _StaticHosting extends import_constructs8.Construct {
2434
2437
  originAccessLevels: [import_aws_cloudfront.AccessLevel.READ]
2435
2438
  });
2436
2439
  const hasCustomDomain = props.certificate !== void 0 && props.hostedZone !== void 0 && props.domainNames !== void 0 && props.domainNames.length > 0;
2437
- const additionalBehaviors = this.buildRestApiBehaviors(
2440
+ const restApiWiring = this.buildRestApiBehaviors(
2438
2441
  stack.branchHash,
2439
2442
  props.restApi
2440
2443
  );
2444
+ const additionalBehaviors = restApiWiring?.behaviors;
2445
+ this.configJsonRewriteFunction = restApiWiring?.configJsonRewriteFunction;
2446
+ this.hostCopyFunction = restApiWiring?.hostCopyFunction;
2447
+ this.originRequestHandler = restApiWiring?.originRequestHandler;
2441
2448
  this.distribution = new import_aws_cloudfront.Distribution(this, "distribution", {
2442
2449
  comment: `Static hosting distribution for ${props.description ?? id}`,
2443
2450
  ...hasCustomDomain ? {
@@ -2466,7 +2473,7 @@ var _StaticHosting = class _StaticHosting extends import_constructs8.Construct {
2466
2473
  if (_StaticHosting.isWildcardDomain(domainName)) {
2467
2474
  return;
2468
2475
  }
2469
- new import_aws_route532.ARecord(this, `dns-record-${index}`, {
2476
+ new import_aws_route532.ARecord(this, `dns-record-${index}-${domainName}`, {
2470
2477
  zone: props.hostedZone,
2471
2478
  recordName: domainName,
2472
2479
  target: import_aws_route532.RecordTarget.fromAlias(
@@ -2501,14 +2508,20 @@ var _StaticHosting = class _StaticHosting extends import_constructs8.Construct {
2501
2508
  });
2502
2509
  }
2503
2510
  /**
2504
- * Builds the `/api/*` and `/api/control/runtime-config` behaviors backed
2505
- * by the REST API custom-domain origin. Returns `undefined` when no
2506
- * `restApi` prop is supplied so the Distribution stays S3-only.
2511
+ * Builds the `/config.json`, `/api/*`, and `/api/control/runtime-config`
2512
+ * behaviors backed by the REST API custom-domain origin, plus the
2513
+ * viewer-request CloudFront Functions and the origin-request
2514
+ * Lambda@Edge that route each request to the matching per-PR API
2515
+ * origin. Returns `undefined` when no `restApi` prop is supplied so
2516
+ * the Distribution stays S3-only.
2507
2517
  */
2508
2518
  buildRestApiBehaviors(branchHash, restApi) {
2509
2519
  if (restApi === void 0) {
2510
2520
  return void 0;
2511
2521
  }
2522
+ const runtimeConfigPath = restApi.runtimeConfigPath ?? "/control/runtime-config";
2523
+ const viewerHostPrefix = restApi.hostMapping?.viewerPrefix ?? "admin";
2524
+ const apiHostPrefix = restApi.hostMapping?.apiPrefix ?? "api";
2512
2525
  const apiOrigin = new import_aws_cloudfront_origins.HttpOrigin(restApi.domainName, {
2513
2526
  protocolPolicy: import_aws_cloudfront.OriginProtocolPolicy.HTTPS_ONLY
2514
2527
  });
@@ -2517,31 +2530,146 @@ var _StaticHosting = class _StaticHosting extends import_constructs8.Construct {
2517
2530
  "runtime-config-cache-policy",
2518
2531
  {
2519
2532
  cachePolicyName: `static-hosting-runtime-config-${branchHash}`,
2520
- comment: "/api/control/runtime-config: cache key includes only `v` so the bundle's deploy-hash bust works automatically.",
2533
+ comment: "/config.json: cache key keyed on Host + `v` so per-PR responses cannot leak across hosts.",
2521
2534
  defaultTtl: restApi.runtimeConfigCacheTtl?.defaultTtl ?? import_aws_cdk_lib11.Duration.minutes(5),
2522
2535
  minTtl: import_aws_cdk_lib11.Duration.seconds(0),
2523
2536
  maxTtl: restApi.runtimeConfigCacheTtl?.maxTtl ?? import_aws_cdk_lib11.Duration.hours(1),
2524
- headerBehavior: import_aws_cloudfront.CacheHeaderBehavior.none(),
2537
+ // `Host` keys the cache per-PR — the origin-request edge Lambda
2538
+ // forwards each PR's request to its own API origin, and two
2539
+ // PRs' /config.json payloads must not share a cache slot.
2540
+ headerBehavior: import_aws_cloudfront.CacheHeaderBehavior.allowList("Host"),
2525
2541
  queryStringBehavior: import_aws_cloudfront.CacheQueryStringBehavior.allowList("v"),
2526
2542
  cookieBehavior: import_aws_cloudfront.CacheCookieBehavior.none(),
2527
2543
  enableAcceptEncodingGzip: true,
2528
2544
  enableAcceptEncodingBrotli: true
2529
2545
  }
2530
2546
  );
2547
+ const runtimeConfigPathLiteral = JSON.stringify(runtimeConfigPath);
2548
+ const configJsonRewriteFunction = new import_aws_cloudfront.Function(
2549
+ this,
2550
+ "config-json-rewrite-function",
2551
+ {
2552
+ functionName: `static-hosting-config-json-rewrite-${branchHash}`,
2553
+ comment: "Copies Host into x-viewer-host and rewrites /config.json to the configured runtime-config path so the SPA can bootstrap same-origin against its own per-PR REST API.",
2554
+ code: import_aws_cloudfront.FunctionCode.fromInline(
2555
+ [
2556
+ "function handler(event) {",
2557
+ " var request = event.request;",
2558
+ " if (request.headers.host && request.headers.host.value) {",
2559
+ " request.headers['x-viewer-host'] = { value: request.headers.host.value };",
2560
+ " }",
2561
+ ` request.uri = ${runtimeConfigPathLiteral};`,
2562
+ " return request;",
2563
+ "}"
2564
+ ].join("\n")
2565
+ )
2566
+ }
2567
+ );
2568
+ const hostCopyFunction = new import_aws_cloudfront.Function(
2569
+ this,
2570
+ "host-copy-function",
2571
+ {
2572
+ functionName: `static-hosting-host-copy-${branchHash}`,
2573
+ comment: "Copies Host into x-viewer-host so the origin-request Lambda@Edge can pick the matching per-PR API origin (Host is stripped by ALL_VIEWER_EXCEPT_HOST_HEADER before reaching the origin-request stage).",
2574
+ code: import_aws_cloudfront.FunctionCode.fromInline(
2575
+ [
2576
+ "function handler(event) {",
2577
+ " var request = event.request;",
2578
+ " if (request.headers.host && request.headers.host.value) {",
2579
+ " request.headers['x-viewer-host'] = { value: request.headers.host.value };",
2580
+ " }",
2581
+ " return request;",
2582
+ "}"
2583
+ ].join("\n")
2584
+ )
2585
+ }
2586
+ );
2587
+ const originHandlerJs = path6.join(
2588
+ __dirname,
2589
+ "static-hosting.origin-request-handler.js"
2590
+ );
2591
+ const originHandlerTs = path6.join(
2592
+ __dirname,
2593
+ "static-hosting.origin-request-handler.ts"
2594
+ );
2595
+ const originHandlerEntry = fs6.existsSync(originHandlerJs) ? originHandlerJs : originHandlerTs;
2596
+ const originRequestHandler = new import_aws_lambda_nodejs6.NodejsFunction(
2597
+ this,
2598
+ "origin-request-handler",
2599
+ {
2600
+ entry: originHandlerEntry,
2601
+ handler: "originRequestHandler",
2602
+ memorySize: 128,
2603
+ runtime: import_aws_lambda6.Runtime.NODEJS_LATEST,
2604
+ logGroup: new import_aws_logs.LogGroup(this, "origin-request-handler-log-group", {
2605
+ retention: import_aws_logs.RetentionDays.ONE_MONTH
2606
+ }),
2607
+ // Lambda@Edge forbids runtime env vars, so the host-prefix
2608
+ // mapping is inlined into the bundle at synth time via esbuild
2609
+ // `define`. The handler reads `process.env.VIEWER_HOST_PREFIX`
2610
+ // and `process.env.API_HOST_PREFIX` at module load; esbuild
2611
+ // replaces those identifiers with literal strings during
2612
+ // bundling so the shipped code carries no env-var lookups.
2613
+ bundling: {
2614
+ define: {
2615
+ "process.env.VIEWER_HOST_PREFIX": JSON.stringify(viewerHostPrefix),
2616
+ "process.env.API_HOST_PREFIX": JSON.stringify(apiHostPrefix)
2617
+ }
2618
+ }
2619
+ }
2620
+ );
2621
+ const originRequestEdgeLambda = {
2622
+ functionVersion: originRequestHandler.currentVersion,
2623
+ eventType: import_aws_cloudfront.LambdaEdgeEventType.ORIGIN_REQUEST,
2624
+ includeBody: false
2625
+ };
2531
2626
  return {
2532
- "/api/control/runtime-config": {
2533
- origin: apiOrigin,
2534
- viewerProtocolPolicy: import_aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
2535
- allowedMethods: import_aws_cloudfront.AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
2536
- cachePolicy: runtimeConfigCachePolicy,
2537
- originRequestPolicy: import_aws_cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER
2538
- },
2539
- "/api/*": {
2540
- origin: apiOrigin,
2541
- viewerProtocolPolicy: import_aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
2542
- allowedMethods: import_aws_cloudfront.AllowedMethods.ALLOW_ALL,
2543
- cachePolicy: import_aws_cloudfront.CachePolicy.CACHING_DISABLED,
2544
- originRequestPolicy: import_aws_cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER
2627
+ configJsonRewriteFunction,
2628
+ hostCopyFunction,
2629
+ originRequestHandler,
2630
+ behaviors: {
2631
+ "/config.json": {
2632
+ origin: apiOrigin,
2633
+ viewerProtocolPolicy: import_aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
2634
+ allowedMethods: import_aws_cloudfront.AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
2635
+ cachePolicy: runtimeConfigCachePolicy,
2636
+ originRequestPolicy: import_aws_cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,
2637
+ functionAssociations: [
2638
+ {
2639
+ function: configJsonRewriteFunction,
2640
+ eventType: import_aws_cloudfront.FunctionEventType.VIEWER_REQUEST
2641
+ }
2642
+ ],
2643
+ edgeLambdas: [originRequestEdgeLambda]
2644
+ },
2645
+ "/api/control/runtime-config": {
2646
+ origin: apiOrigin,
2647
+ viewerProtocolPolicy: import_aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
2648
+ allowedMethods: import_aws_cloudfront.AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
2649
+ cachePolicy: runtimeConfigCachePolicy,
2650
+ originRequestPolicy: import_aws_cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,
2651
+ functionAssociations: [
2652
+ {
2653
+ function: hostCopyFunction,
2654
+ eventType: import_aws_cloudfront.FunctionEventType.VIEWER_REQUEST
2655
+ }
2656
+ ],
2657
+ edgeLambdas: [originRequestEdgeLambda]
2658
+ },
2659
+ "/api/*": {
2660
+ origin: apiOrigin,
2661
+ viewerProtocolPolicy: import_aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
2662
+ allowedMethods: import_aws_cloudfront.AllowedMethods.ALLOW_ALL,
2663
+ cachePolicy: import_aws_cloudfront.CachePolicy.CACHING_DISABLED,
2664
+ originRequestPolicy: import_aws_cloudfront.OriginRequestPolicy.ALL_VIEWER_EXCEPT_HOST_HEADER,
2665
+ functionAssociations: [
2666
+ {
2667
+ function: hostCopyFunction,
2668
+ eventType: import_aws_cloudfront.FunctionEventType.VIEWER_REQUEST
2669
+ }
2670
+ ],
2671
+ edgeLambdas: [originRequestEdgeLambda]
2672
+ }
2545
2673
  }
2546
2674
  };
2547
2675
  }
@@ -2595,7 +2723,7 @@ var PerBranchHostname = class extends import_constructs9.Construct {
2595
2723
  distributionId
2596
2724
  }
2597
2725
  );
2598
- this.record = new import_aws_route533.ARecord(this, "alias-record", {
2726
+ this.record = new import_aws_route533.ARecord(this, `alias-record-${props.hostname}`, {
2599
2727
  zone: props.hostedZone,
2600
2728
  recordName: props.hostname,
2601
2729
  target: import_aws_route533.RecordTarget.fromAlias(new import_aws_route53_targets2.CloudFrontTarget(distribution))
@@ -7301,7 +7429,7 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
7301
7429
  integration
7302
7430
  });
7303
7431
  const apiPrefix = this.branchName === "main" ? `api` : `api-${this.childZonePrefix}`;
7304
- new import_aws_route535.ARecord(this, "api-a-record", {
7432
+ new import_aws_route535.ARecord(this, `api-a-record-${apiPrefix}`, {
7305
7433
  zone: hostedZone,
7306
7434
  recordName: apiPrefix,
7307
7435
  target: import_aws_route535.RecordTarget.fromAlias(
@@ -7434,7 +7562,6 @@ var OpenHiGraphqlService = _OpenHiGraphqlService;
7434
7562
  // src/services/open-hi-website-service.ts
7435
7563
  var import_config5 = __toESM(require_lib());
7436
7564
  var import_aws_s32 = require("aws-cdk-lib/aws-s3");
7437
- var OPENHI_PR_NUMBER_ENV_VAR = "OPENHI_PR_NUMBER";
7438
7565
  var SSM_PARAM_NAME_FULL_DOMAIN = "WEBSITE_FULL_DOMAIN";
7439
7566
  var _OpenHiWebsiteService = class _OpenHiWebsiteService extends OpenHiService {
7440
7567
  /**
@@ -7495,12 +7622,6 @@ var _OpenHiWebsiteService = class _OpenHiWebsiteService extends OpenHiService {
7495
7622
  this.props = props;
7496
7623
  this.validateConfig(props);
7497
7624
  const isReleaseBranch = this.branchName === this.defaultReleaseBranch;
7498
- this.prNumber = this.resolvePrNumber(props);
7499
- if (!isReleaseBranch && this.prNumber === void 0) {
7500
- throw new Error(
7501
- `OpenHiWebsiteService: prNumber is required on non-release-branch deploys (branchName="${this.branchName}", defaultReleaseBranch="${this.defaultReleaseBranch}"). Pass the \`prNumber\` prop or set the ${OPENHI_PR_NUMBER_ENV_VAR} env var.`
7502
- );
7503
- }
7504
7625
  const hostedZone = this.createHostedZone();
7505
7626
  this.fullDomain = this.computeFullDomain(hostedZone);
7506
7627
  const shouldCreateHostingInfra = props.createHostingInfrastructure ?? isReleaseBranch;
@@ -7555,32 +7676,12 @@ var _OpenHiWebsiteService = class _OpenHiWebsiteService extends OpenHiService {
7555
7676
  return OpenHiGlobalService.rootWildcardCertificateFromConstruct(this);
7556
7677
  }
7557
7678
  /**
7558
- * Resolves the PR number from props or the `OPENHI_PR_NUMBER` env var.
7559
- * Returns `undefined` on release-branch deploys where no PR number is
7560
- * needed.
7561
- */
7562
- resolvePrNumber(props) {
7563
- if (props.prNumber !== void 0) {
7564
- return props.prNumber;
7565
- }
7566
- const raw = process.env[OPENHI_PR_NUMBER_ENV_VAR]?.trim();
7567
- if (!raw) {
7568
- return void 0;
7569
- }
7570
- const parsed = Number.parseInt(raw, 10);
7571
- if (!Number.isInteger(parsed) || parsed <= 0) {
7572
- throw new Error(
7573
- `${OPENHI_PR_NUMBER_ENV_VAR} must be a positive integer; got "${raw}".`
7574
- );
7575
- }
7576
- return parsed;
7577
- }
7578
- /**
7579
- * Computes the full website domain from `domainPrefix`, the PR number,
7580
- * and the child zone name. Release-branch deploys serve at
7581
- * `\<domainPrefix\>.\<zone\>` (e.g. `admin.dev.openhi.org`); every other
7582
- * deploy serves a per-PR preview at `\<domainPrefix\>-pr-\<N\>.\<zone\>`
7583
- * (e.g. `admin-pr-123.dev.openhi.org`).
7679
+ * Computes the full website domain from `domainPrefix`,
7680
+ * `childZonePrefix`, and the child zone name. Release-branch deploys
7681
+ * serve at `\<domainPrefix\>.\<zone\>` (e.g. `admin.dev.openhi.org`);
7682
+ * every other deploy serves a per-PR preview at
7683
+ * `\<domainPrefix\>-\<childZonePrefix\>.\<zone\>`
7684
+ * (e.g. `admin-feat-1093-patient-migration.dev.openhi.org`).
7584
7685
  */
7585
7686
  computeFullDomain(hostedZone) {
7586
7687
  const subDomain = this.computeSubDomain();
@@ -7592,16 +7693,20 @@ var _OpenHiWebsiteService = class _OpenHiWebsiteService extends OpenHiService {
7592
7693
  * key prefix passed to {@link StaticContent} so the upload prefix
7593
7694
  * always matches the served hostname.
7594
7695
  *
7595
- * Non-release deploys compose the per-PR slug from
7596
- * {@link PER_BRANCH_PREVIEW_PREFIX} so the per-PR S3 key prefix
7696
+ * Non-release deploys compose the per-PR slug as
7697
+ * `\<domainPrefix\>-\<childZonePrefix\>`, mirroring the REST API's
7698
+ * `api-\<childZonePrefix\>` convention. When `domainPrefix` is `admin`
7699
+ * (the only consumer today), the resulting sub-domain starts with
7700
+ * {@link PER_BRANCH_PREVIEW_PREFIX}, so the per-PR S3 key prefix
7597
7701
  * matches what `StaticHosting`'s lifecycle rule expires.
7598
7702
  */
7599
7703
  computeSubDomain() {
7600
7704
  const isReleaseBranch = this.branchName === this.defaultReleaseBranch;
7705
+ const domainPrefix = this.props.domainPrefix ?? "www";
7601
7706
  if (isReleaseBranch) {
7602
- return this.props.domainPrefix ?? "www";
7707
+ return domainPrefix;
7603
7708
  }
7604
- return `${PER_BRANCH_PREVIEW_PREFIX}${this.prNumber}`;
7709
+ return `${domainPrefix}-${this.childZonePrefix}`;
7605
7710
  }
7606
7711
  /**
7607
7712
  * Creates the StaticHosting infrastructure (bucket + distribution +
@@ -7662,8 +7767,9 @@ var _OpenHiWebsiteService = class _OpenHiWebsiteService extends OpenHiService {
7662
7767
  * The S3 key prefix is `\<sub-domain\>.\<zone\>/\<contentDest\>` so the
7663
7768
  * upload location matches the Host-header-derived folder the Lambda@Edge
7664
7769
  * viewer-request handler prepends. Passing the zone name (rather than
7665
- * `this.fullDomain`) for the `fullDomain` prop keeps the prefix flat
7666
- * `admin-pr-123.dev.openhi.org/`, not `admin-pr-123.admin.dev.openhi.org/`.
7770
+ * `this.fullDomain`) for the `fullDomain` prop keeps the prefix flat
7771
+ * `admin-feat-foo.dev.openhi.org/`, not
7772
+ * `admin-feat-foo.admin.dev.openhi.org/`.
7667
7773
  */
7668
7774
  createStaticContent(bucket) {
7669
7775
  const { contentSourceDirectory, contentDestinationDirectory } = this.props;
@@ -7677,9 +7783,10 @@ var _OpenHiWebsiteService = class _OpenHiWebsiteService extends OpenHiService {
7677
7783
  }
7678
7784
  /**
7679
7785
  * Creates the per-PR `PerBranchHostname` alias record on non-release
7680
- * branch deploys. The record points `\<domainPrefix\>-pr-\<N\>.\<zone\>`
7681
- * at the release-branch CloudFront distribution (resolved from SSM
7682
- * against {@link OpenHiService.releaseBranchHash}).
7786
+ * branch deploys. The record points
7787
+ * `\<domainPrefix\>-\<childZonePrefix\>.\<zone\>` at the release-branch
7788
+ * CloudFront distribution (resolved from SSM against
7789
+ * {@link OpenHiService.releaseBranchHash}).
7683
7790
  */
7684
7791
  createPerBranchHostname(hostedZone) {
7685
7792
  return new PerBranchHostname(this, "per-branch-hostname", {
@@ -8242,7 +8349,6 @@ var RenameCascadeWorkflow = class extends import_constructs25.Construct {
8242
8349
  DataStorePostgresReplica,
8243
8350
  DiscoverableStringParameter,
8244
8351
  DynamoDbDataStore,
8245
- OPENHI_PR_NUMBER_ENV_VAR,
8246
8352
  OPENHI_REPO_TAG_KEY_ENV_VAR,
8247
8353
  OPENHI_RESOURCE_URN_SYSTEM,
8248
8354
  OPENHI_TAG_KEY_PREFIX_ENV_VAR,