@openhi/constructs 0.0.85 → 0.0.86

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
@@ -94,6 +94,7 @@ var require_lib = __commonJS({
94
94
  var src_exports = {};
95
95
  __export(src_exports, {
96
96
  ChildHostedZone: () => ChildHostedZone,
97
+ CognitoFixtureSeederClient: () => CognitoFixtureSeederClient,
97
98
  CognitoUserPool: () => CognitoUserPool,
98
99
  CognitoUserPoolClient: () => CognitoUserPoolClient,
99
100
  CognitoUserPoolDomain: () => CognitoUserPoolDomain,
@@ -103,6 +104,7 @@ __export(src_exports, {
103
104
  DATA_STORE_CHANGE_EVENT_SOURCE: () => DATA_STORE_CHANGE_EVENT_SOURCE,
104
105
  DataEventBus: () => DataEventBus,
105
106
  DataStoreHistoricalArchive: () => DataStoreHistoricalArchive,
107
+ DataStorePostgresReplica: () => DataStorePostgresReplica,
106
108
  DiscoverableStringParameter: () => DiscoverableStringParameter,
107
109
  DynamoDbDataStore: () => DynamoDbDataStore,
108
110
  OpenHiApp: () => OpenHiApp,
@@ -115,6 +117,9 @@ __export(src_exports, {
115
117
  OpenHiService: () => OpenHiService,
116
118
  OpenHiStage: () => OpenHiStage,
117
119
  OpsEventBus: () => OpsEventBus,
120
+ POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME: () => POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME,
121
+ POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME: () => POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME,
122
+ POSTGRES_REPLICA_SECRET_ARN_SSM_NAME: () => POSTGRES_REPLICA_SECRET_ARN_SSM_NAME,
118
123
  PreTokenGenerationLambda: () => PreTokenGenerationLambda,
119
124
  REST_API_BASE_URL_SSM_NAME: () => REST_API_BASE_URL_SSM_NAME,
120
125
  RootGraphqlApi: () => RootGraphqlApi,
@@ -124,7 +129,8 @@ __export(src_exports, {
124
129
  STATIC_HOSTING_SERVICE_TYPE: () => STATIC_HOSTING_SERVICE_TYPE,
125
130
  StaticHosting: () => StaticHosting,
126
131
  buildFhirCurrentResourceChangeDetail: () => buildFhirCurrentResourceChangeDetail,
127
- getDynamoDbDataStoreTableName: () => getDynamoDbDataStoreTableName
132
+ getDynamoDbDataStoreTableName: () => getDynamoDbDataStoreTableName,
133
+ getPostgresReplicaSchemaName: () => getPostgresReplicaSchemaName
128
134
  });
129
135
  module.exports = __toCommonJS(src_exports);
130
136
 
@@ -402,6 +408,10 @@ var OpenHiService = class extends import_aws_cdk_lib4.Stack {
402
408
  this.environmentHash = environmentHash;
403
409
  this.branchHash = branchHash;
404
410
  this.stackHash = stackHash;
411
+ this.node.setContext(
412
+ `availability-zones:account=${account}:region=${region}`,
413
+ [`${region}a`, `${region}b`, `${region}c`]
414
+ );
405
415
  import_aws_cdk_lib4.Tags.of(this).add(`${appName}:repo-name`, repoName.slice(0, 255));
406
416
  import_aws_cdk_lib4.Tags.of(this).add(`${appName}:branch-name`, branchName.slice(0, 255));
407
417
  import_aws_cdk_lib4.Tags.of(this).add(`${appName}:service-type`, id.slice(0, 255));
@@ -578,9 +588,47 @@ var _RootGraphqlApi = class _RootGraphqlApi extends import_aws_appsync.GraphqlAp
578
588
  _RootGraphqlApi.SSM_PARAM_NAME = "ROOT_GRAPHQL_API";
579
589
  var RootGraphqlApi = _RootGraphqlApi;
580
590
 
581
- // src/components/cognito/cognito-user-pool.ts
591
+ // src/components/cognito/cognito-fixture-seeder-client.ts
592
+ var import_aws_cdk_lib6 = require("aws-cdk-lib");
582
593
  var import_aws_cognito = require("aws-cdk-lib/aws-cognito");
583
- var CognitoUserPool = class extends import_aws_cognito.UserPool {
594
+ var CognitoFixtureSeederClient = class extends import_aws_cognito.UserPoolClient {
595
+ constructor(scope, props) {
596
+ const { userPool, ...rest } = props;
597
+ super(scope, "fixture-seeder-client", {
598
+ userPool,
599
+ generateSecret: false,
600
+ authFlows: {
601
+ userPassword: true
602
+ },
603
+ // No OAuth flows — the seeder calls Cognito's `InitiateAuth`
604
+ // directly with USER_PASSWORD_AUTH, not through the hosted-UI
605
+ // OAuth grant flows the SPA client uses. `disableOAuth: true`
606
+ // causes CDK to omit `AllowedOAuthFlowsUserPoolClient` entirely;
607
+ // passing an empty `oAuth` block instead still flips that flag on
608
+ // and Cognito rejects the create call for missing flows/scopes.
609
+ disableOAuth: true,
610
+ // Short-lived tokens: a seeder run takes seconds, not hours.
611
+ // 1h access-token validity is the minimum Cognito permits and is
612
+ // plenty for a fixture run.
613
+ accessTokenValidity: import_aws_cdk_lib6.Duration.hours(1),
614
+ idTokenValidity: import_aws_cdk_lib6.Duration.hours(1),
615
+ refreshTokenValidity: import_aws_cdk_lib6.Duration.days(1),
616
+ preventUserExistenceErrors: true,
617
+ ...rest
618
+ });
619
+ }
620
+ };
621
+ /**
622
+ * SSM parameter name suffix used to publish this client's ID for
623
+ * cross-stack lookups. Built into a full parameter name via
624
+ * `buildParameterName` with `serviceType` AUTH (since the auth stack
625
+ * owns this resource).
626
+ */
627
+ CognitoFixtureSeederClient.SSM_PARAM_NAME = "COGNITO_FIXTURE_SEEDER_CLIENT";
628
+
629
+ // src/components/cognito/cognito-user-pool.ts
630
+ var import_aws_cognito2 = require("aws-cdk-lib/aws-cognito");
631
+ var CognitoUserPool = class extends import_aws_cognito2.UserPool {
584
632
  constructor(scope, props = {}) {
585
633
  const service = OpenHiService.of(scope);
586
634
  super(scope, "user-pool", {
@@ -594,7 +642,7 @@ var CognitoUserPool = class extends import_aws_cognito.UserPool {
594
642
  userVerification: {
595
643
  emailSubject: "Verify your email!",
596
644
  emailBody: "Your verification code is {####}.",
597
- emailStyle: import_aws_cognito.VerificationEmailStyle.CODE
645
+ emailStyle: import_aws_cognito2.VerificationEmailStyle.CODE
598
646
  },
599
647
  removalPolicy: props.removalPolicy ?? service.removalPolicy,
600
648
  /**
@@ -614,8 +662,8 @@ var CognitoUserPool = class extends import_aws_cognito.UserPool {
614
662
  CognitoUserPool.SSM_PARAM_NAME = "COGNITO_USER_POOL";
615
663
 
616
664
  // src/components/cognito/cognito-user-pool-client.ts
617
- var import_aws_cognito2 = require("aws-cdk-lib/aws-cognito");
618
- var CognitoUserPoolClient = class extends import_aws_cognito2.UserPoolClient {
665
+ var import_aws_cognito3 = require("aws-cdk-lib/aws-cognito");
666
+ var CognitoUserPoolClient = class extends import_aws_cognito3.UserPoolClient {
619
667
  constructor(scope, props) {
620
668
  super(scope, "user-pool-client", {
621
669
  /**
@@ -642,8 +690,8 @@ var CognitoUserPoolClient = class extends import_aws_cognito2.UserPoolClient {
642
690
  CognitoUserPoolClient.SSM_PARAM_NAME = "COGNITO_USER_POOL_CLIENT";
643
691
 
644
692
  // src/components/cognito/cognito-user-pool-domain.ts
645
- var import_aws_cognito3 = require("aws-cdk-lib/aws-cognito");
646
- var CognitoUserPoolDomain = class extends import_aws_cognito3.UserPoolDomain {
693
+ var import_aws_cognito4 = require("aws-cdk-lib/aws-cognito");
694
+ var CognitoUserPoolDomain = class extends import_aws_cognito4.UserPoolDomain {
647
695
  constructor(scope, props) {
648
696
  const id = props.cognitoDomain?.domainPrefix ? "cognito-domain" : "custom-domain";
649
697
  super(scope, id, {
@@ -749,10 +797,6 @@ var EXCLUDED_CHANGE_DETAIL_KEYS = /* @__PURE__ */ new Set([
749
797
  "GSI1SK",
750
798
  "GSI2PK",
751
799
  "GSI2SK",
752
- "GSI3PK",
753
- "GSI3SK",
754
- "GSI4PK",
755
- "GSI4SK",
756
800
  /** Full FHIR JSON may contain PII; never list or ship in the bus payload. */
757
801
  "resource"
758
802
  ]);
@@ -828,7 +872,7 @@ function buildFhirCurrentResourceChangeDetail(record, keys) {
828
872
  // src/components/dynamodb/data-store-historical-archive.ts
829
873
  var import_node_fs2 = __toESM(require("fs"));
830
874
  var import_node_path2 = __toESM(require("path"));
831
- var import_aws_cdk_lib6 = require("aws-cdk-lib");
875
+ var import_aws_cdk_lib7 = require("aws-cdk-lib");
832
876
  var kinesisfirehose = __toESM(require("aws-cdk-lib/aws-kinesisfirehose"));
833
877
  var import_aws_lambda2 = require("aws-cdk-lib/aws-lambda");
834
878
  var import_aws_lambda_nodejs2 = require("aws-cdk-lib/aws-lambda-nodejs");
@@ -850,7 +894,7 @@ var DataStoreHistoricalArchive = class extends import_constructs2.Construct {
850
894
  encryption: s3.BucketEncryption.S3_MANAGED,
851
895
  enforceSSL: true,
852
896
  removalPolicy: props.removalPolicy,
853
- autoDeleteObjects: props.removalPolicy === import_aws_cdk_lib6.RemovalPolicy.DESTROY,
897
+ autoDeleteObjects: props.removalPolicy === import_aws_cdk_lib7.RemovalPolicy.DESTROY,
854
898
  versioned: true
855
899
  });
856
900
  const putEventsFailureDlqBucket = props.dataEventBus ? new s3.Bucket(this, "PutEventsFailureDlq", {
@@ -858,7 +902,7 @@ var DataStoreHistoricalArchive = class extends import_constructs2.Construct {
858
902
  encryption: s3.BucketEncryption.S3_MANAGED,
859
903
  enforceSSL: true,
860
904
  removalPolicy: props.removalPolicy,
861
- autoDeleteObjects: props.removalPolicy === import_aws_cdk_lib6.RemovalPolicy.DESTROY,
905
+ autoDeleteObjects: props.removalPolicy === import_aws_cdk_lib7.RemovalPolicy.DESTROY,
862
906
  versioned: false
863
907
  }) : void 0;
864
908
  this.putEventsFailureDlqBucket = putEventsFailureDlqBucket;
@@ -866,7 +910,7 @@ var DataStoreHistoricalArchive = class extends import_constructs2.Construct {
866
910
  entry: resolveHandlerEntry2(__dirname),
867
911
  runtime: import_aws_lambda2.Runtime.NODEJS_LATEST,
868
912
  memorySize: 512,
869
- timeout: import_aws_cdk_lib6.Duration.minutes(1),
913
+ timeout: import_aws_cdk_lib7.Duration.minutes(1),
870
914
  description: "Firehose transform: filter CURRENT resource rows, S3 keys, EventBridge PutEvents",
871
915
  environment: props.dataEventBus && putEventsFailureDlqBucket ? {
872
916
  DATA_EVENT_BUS_NAME: props.dataEventBus.eventBusName,
@@ -882,16 +926,16 @@ var DataStoreHistoricalArchive = class extends import_constructs2.Construct {
882
926
  const processor = new kinesisfirehose.LambdaFunctionProcessor(
883
927
  this.transformFunction,
884
928
  {
885
- bufferInterval: import_aws_cdk_lib6.Duration.seconds(60),
886
- bufferSize: import_aws_cdk_lib6.Size.mebibytes(3),
929
+ bufferInterval: import_aws_cdk_lib7.Duration.seconds(60),
930
+ bufferSize: import_aws_cdk_lib7.Size.mebibytes(3),
887
931
  retries: 3
888
932
  }
889
933
  );
890
934
  const destination = new kinesisfirehose.S3Bucket(this.archiveBucket, {
891
935
  compression: kinesisfirehose.Compression.GZIP,
892
- bufferingInterval: import_aws_cdk_lib6.Duration.seconds(300),
936
+ bufferingInterval: import_aws_cdk_lib7.Duration.seconds(300),
893
937
  // Firehose requires SizeInMBs ≥ 64 when dynamic partitioning is enabled.
894
- bufferingSize: import_aws_cdk_lib6.Size.mebibytes(64),
938
+ bufferingSize: import_aws_cdk_lib7.Size.mebibytes(64),
895
939
  processors: [processor],
896
940
  errorOutputPrefix: "errors/!{firehose:error-output-type}/!{timestamp:yyyy/MM/dd/HH}/",
897
941
  loggingConfig: new kinesisfirehose.EnableLogging()
@@ -954,7 +998,15 @@ var DynamoDbDataStore = class extends import_aws_dynamodb.Table {
954
998
  type: import_aws_dynamodb.AttributeType.STRING
955
999
  },
956
1000
  projectionType: import_aws_dynamodb.ProjectionType.INCLUDE,
957
- nonKeyAttributes: ["srcType", "srcId", "path", "srcPk", "srcSk", "ts"]
1001
+ nonKeyAttributes: [
1002
+ "summary",
1003
+ "vid",
1004
+ "lastUpdated",
1005
+ "createdDate",
1006
+ "modifiedDate",
1007
+ "createdById",
1008
+ "modifiedById"
1009
+ ]
958
1010
  });
959
1011
  this.addGlobalSecondaryIndex({
960
1012
  indexName: "GSI2",
@@ -967,32 +1019,12 @@ var DynamoDbDataStore = class extends import_aws_dynamodb.Table {
967
1019
  type: import_aws_dynamodb.AttributeType.STRING
968
1020
  },
969
1021
  projectionType: import_aws_dynamodb.ProjectionType.INCLUDE,
970
- nonKeyAttributes: ["resourcePk", "resourceSk", "display", "status"]
971
- });
972
- this.addGlobalSecondaryIndex({
973
- indexName: "GSI3",
974
- partitionKey: {
975
- name: "GSI3PK",
976
- type: import_aws_dynamodb.AttributeType.STRING
977
- },
978
- sortKey: {
979
- name: "GSI3SK",
980
- type: import_aws_dynamodb.AttributeType.STRING
981
- },
982
- projectionType: import_aws_dynamodb.ProjectionType.INCLUDE,
983
- nonKeyAttributes: ["resourcePk", "resourceSk"]
984
- });
985
- this.addGlobalSecondaryIndex({
986
- indexName: "GSI4",
987
- partitionKey: {
988
- name: "GSI4PK",
989
- type: import_aws_dynamodb.AttributeType.STRING
990
- },
991
- sortKey: {
992
- name: "GSI4SK",
993
- type: import_aws_dynamodb.AttributeType.STRING
994
- },
995
- projectionType: import_aws_dynamodb.ProjectionType.ALL
1022
+ nonKeyAttributes: [
1023
+ "id",
1024
+ "currentTenant",
1025
+ "currentWorkspace",
1026
+ "displayName"
1027
+ ]
996
1028
  });
997
1029
  }
998
1030
  };
@@ -1041,8 +1073,172 @@ var OpsEventBus = class _OpsEventBus extends import_aws_events2.EventBus {
1041
1073
  }
1042
1074
  };
1043
1075
 
1076
+ // src/components/postgres/data-store-postgres-replica.ts
1077
+ var import_node_fs3 = __toESM(require("fs"));
1078
+ var import_node_path3 = __toESM(require("path"));
1079
+ var import_aws_cdk_lib8 = require("aws-cdk-lib");
1080
+ var ec2 = __toESM(require("aws-cdk-lib/aws-ec2"));
1081
+ var import_aws_lambda3 = require("aws-cdk-lib/aws-lambda");
1082
+ var import_aws_lambda_event_sources = require("aws-cdk-lib/aws-lambda-event-sources");
1083
+ var import_aws_lambda_nodejs3 = require("aws-cdk-lib/aws-lambda-nodejs");
1084
+ var rds = __toESM(require("aws-cdk-lib/aws-rds"));
1085
+ var import_constructs3 = require("constructs");
1086
+ var HANDLER_NAME3 = "data-store-postgres-replication.handler.js";
1087
+ var DEFAULT_DATABASE_NAME = "openhi";
1088
+ var SCHEMA_NAME_PATTERN = /^[a-z_][a-z0-9_]{0,62}$/;
1089
+ var POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME = "POSTGRES_REPLICA_CLUSTER_ARN";
1090
+ var POSTGRES_REPLICA_SECRET_ARN_SSM_NAME = "POSTGRES_REPLICA_SECRET_ARN";
1091
+ var POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME = "POSTGRES_REPLICA_DATABASE_NAME";
1092
+ function resolveHandlerEntry3(dirname) {
1093
+ const sameDir = import_node_path3.default.join(dirname, HANDLER_NAME3);
1094
+ if (import_node_fs3.default.existsSync(sameDir)) {
1095
+ return sameDir;
1096
+ }
1097
+ return import_node_path3.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME3);
1098
+ }
1099
+ function getPostgresReplicaSchemaName(branchHash) {
1100
+ const candidate = `b_${branchHash.toLowerCase()}`;
1101
+ if (!SCHEMA_NAME_PATTERN.test(candidate)) {
1102
+ throw new Error(
1103
+ `Branch hash ${JSON.stringify(branchHash)} produces an invalid Postgres schema name ${JSON.stringify(candidate)}; expected /[a-z_][a-z0-9_]{0,62}/.`
1104
+ );
1105
+ }
1106
+ return candidate;
1107
+ }
1108
+ var DataStorePostgresReplica = class extends import_constructs3.Construct {
1109
+ /**
1110
+ * Resolve the cluster ARN published by an upstream {@link DataStorePostgresReplica}.
1111
+ * Use from any stack that needs to grant `rds-data:ExecuteStatement` against
1112
+ * the cluster.
1113
+ */
1114
+ static clusterArnFromConstruct(scope) {
1115
+ return DiscoverableStringParameter.valueForLookupName(scope, {
1116
+ ssmParamName: POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME,
1117
+ serviceType: "data"
1118
+ });
1119
+ }
1120
+ /**
1121
+ * Resolve the credentials secret ARN published by an upstream
1122
+ * {@link DataStorePostgresReplica}. Use from any stack that needs to grant
1123
+ * `secretsmanager:GetSecretValue` against the secret.
1124
+ */
1125
+ static secretArnFromConstruct(scope) {
1126
+ return DiscoverableStringParameter.valueForLookupName(scope, {
1127
+ ssmParamName: POSTGRES_REPLICA_SECRET_ARN_SSM_NAME,
1128
+ serviceType: "data"
1129
+ });
1130
+ }
1131
+ /**
1132
+ * Resolve the database name published by an upstream
1133
+ * {@link DataStorePostgresReplica}.
1134
+ */
1135
+ static databaseNameFromConstruct(scope) {
1136
+ return DiscoverableStringParameter.valueForLookupName(scope, {
1137
+ ssmParamName: POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME,
1138
+ serviceType: "data"
1139
+ });
1140
+ }
1141
+ constructor(scope, id, props) {
1142
+ super(scope, id);
1143
+ this.databaseName = props.databaseName ?? DEFAULT_DATABASE_NAME;
1144
+ this.schemaName = getPostgresReplicaSchemaName(props.branchHash);
1145
+ const region = import_aws_cdk_lib8.Stack.of(this).region;
1146
+ this.vpc = props.vpc ?? new ec2.Vpc(this, "Vpc", {
1147
+ availabilityZones: [`${region}a`, `${region}b`],
1148
+ natGateways: 0,
1149
+ subnetConfiguration: [
1150
+ {
1151
+ name: "isolated",
1152
+ subnetType: ec2.SubnetType.PRIVATE_ISOLATED,
1153
+ cidrMask: 24
1154
+ }
1155
+ ]
1156
+ });
1157
+ this.cluster = new rds.DatabaseCluster(this, "Cluster", {
1158
+ clusterIdentifier: `openhi-dstore-pg-${props.stackHash}`,
1159
+ engine: rds.DatabaseClusterEngine.auroraPostgres({
1160
+ version: rds.AuroraPostgresEngineVersion.VER_16_4
1161
+ }),
1162
+ vpc: this.vpc,
1163
+ vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
1164
+ writer: rds.ClusterInstance.serverlessV2("writer"),
1165
+ serverlessV2MinCapacity: props.minCapacity ?? 1,
1166
+ serverlessV2MaxCapacity: props.maxCapacity ?? 2,
1167
+ defaultDatabaseName: this.databaseName,
1168
+ credentials: rds.Credentials.fromGeneratedSecret("openhi_admin"),
1169
+ storageEncrypted: true,
1170
+ removalPolicy: props.removalPolicy,
1171
+ // Phase 2 of ADR 2026-04-17-01: the REST API Lambda queries Postgres
1172
+ // via the RDS Data API (HTTPS) so it can stay out of the cluster's VPC.
1173
+ // Direct `pg` from the replication Lambda continues to work in parallel.
1174
+ enableDataApi: true
1175
+ });
1176
+ this.publishCoordinatesToSsm();
1177
+ this.replicationFunction = new import_aws_lambda_nodejs3.NodejsFunction(this, "ReplicationFunction", {
1178
+ entry: resolveHandlerEntry3(__dirname),
1179
+ runtime: import_aws_lambda3.Runtime.NODEJS_LATEST,
1180
+ memorySize: 512,
1181
+ timeout: import_aws_cdk_lib8.Duration.minutes(1),
1182
+ vpc: this.vpc,
1183
+ vpcSubnets: { subnetType: ec2.SubnetType.PRIVATE_ISOLATED },
1184
+ description: "Replicates DynamoDB current-resource changes into the Postgres `resources` JSONB table (ADR 2026-04-17-01).",
1185
+ environment: {
1186
+ OPENHI_PG_HOST: this.cluster.clusterEndpoint.hostname,
1187
+ OPENHI_PG_PORT: this.cluster.clusterEndpoint.port.toString(),
1188
+ OPENHI_PG_DATABASE: this.databaseName,
1189
+ OPENHI_PG_SCHEMA: this.schemaName,
1190
+ OPENHI_PG_SECRET_ARN: this.cluster.secret.secretArn,
1191
+ OPENHI_PG_SSL: "true"
1192
+ },
1193
+ bundling: {
1194
+ minify: true,
1195
+ sourceMap: false,
1196
+ // pg has conditional/optional deps (pg-native, pg-cloudflare) that
1197
+ // historically misbehave when bundled by esbuild; keep it as a real
1198
+ // node_module in the Lambda zip instead.
1199
+ nodeModules: ["pg"]
1200
+ }
1201
+ });
1202
+ this.cluster.secret.grantRead(this.replicationFunction);
1203
+ this.cluster.connections.allowDefaultPortFrom(this.replicationFunction);
1204
+ this.replicationFunction.addEventSource(
1205
+ new import_aws_lambda_event_sources.KinesisEventSource(props.kinesisStream, {
1206
+ startingPosition: import_aws_lambda3.StartingPosition.LATEST,
1207
+ batchSize: 100,
1208
+ maxBatchingWindow: import_aws_cdk_lib8.Duration.seconds(5),
1209
+ retryAttempts: 10,
1210
+ bisectBatchOnError: true,
1211
+ parallelizationFactor: 2,
1212
+ reportBatchItemFailures: true
1213
+ })
1214
+ );
1215
+ }
1216
+ /**
1217
+ * Publishes the cluster ARN, secret ARN, and database name as discoverable
1218
+ * SSM parameters so the REST API stack (and any future read-side consumer)
1219
+ * can wire RDS Data API access without a direct CDK cross-stack reference.
1220
+ */
1221
+ publishCoordinatesToSsm() {
1222
+ new DiscoverableStringParameter(this, "cluster-arn-param", {
1223
+ ssmParamName: POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME,
1224
+ stringValue: this.cluster.clusterArn,
1225
+ description: "ARN of the Aurora Serverless v2 cluster backing the Postgres replication tier (ADR 2026-04-17-01)."
1226
+ });
1227
+ new DiscoverableStringParameter(this, "secret-arn-param", {
1228
+ ssmParamName: POSTGRES_REPLICA_SECRET_ARN_SSM_NAME,
1229
+ stringValue: this.cluster.secret.secretArn,
1230
+ description: "ARN of the Secrets Manager secret with credentials for the Postgres replication tier."
1231
+ });
1232
+ new DiscoverableStringParameter(this, "database-name-param", {
1233
+ ssmParamName: POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME,
1234
+ stringValue: this.databaseName,
1235
+ description: "Database name within the Postgres replication cluster."
1236
+ });
1237
+ }
1238
+ };
1239
+
1044
1240
  // src/components/route-53/child-hosted-zone.ts
1045
- var import_aws_cdk_lib7 = require("aws-cdk-lib");
1241
+ var import_aws_cdk_lib9 = require("aws-cdk-lib");
1046
1242
  var import_aws_route53 = require("aws-cdk-lib/aws-route53");
1047
1243
  var ChildHostedZone = class extends import_aws_route53.HostedZone {
1048
1244
  constructor(scope, id, props) {
@@ -1051,7 +1247,7 @@ var ChildHostedZone = class extends import_aws_route53.HostedZone {
1051
1247
  zone: props.parentHostedZone,
1052
1248
  recordName: this.zoneName,
1053
1249
  values: this.hostedZoneNameServers || [],
1054
- ttl: import_aws_cdk_lib7.Duration.minutes(5)
1250
+ ttl: import_aws_cdk_lib9.Duration.minutes(5)
1055
1251
  });
1056
1252
  }
1057
1253
  };
@@ -1061,8 +1257,8 @@ var ChildHostedZone = class extends import_aws_route53.HostedZone {
1061
1257
  ChildHostedZone.SSM_PARAM_NAME = "CHILDHOSTEDZONE";
1062
1258
 
1063
1259
  // src/components/route-53/root-hosted-zone.ts
1064
- var import_constructs3 = require("constructs");
1065
- var RootHostedZone = class extends import_constructs3.Construct {
1260
+ var import_constructs4 = require("constructs");
1261
+ var RootHostedZone = class extends import_constructs4.Construct {
1066
1262
  };
1067
1263
 
1068
1264
  // src/components/static-hosting/static-hosting.ts
@@ -1070,9 +1266,9 @@ var import_aws_cloudfront = require("aws-cdk-lib/aws-cloudfront");
1070
1266
  var import_aws_cloudfront_origins = require("aws-cdk-lib/aws-cloudfront-origins");
1071
1267
  var import_aws_s3 = require("aws-cdk-lib/aws-s3");
1072
1268
  var import_core = require("aws-cdk-lib/core");
1073
- var import_constructs4 = require("constructs");
1269
+ var import_constructs5 = require("constructs");
1074
1270
  var STATIC_HOSTING_SERVICE_TYPE = "website";
1075
- var _StaticHosting = class _StaticHosting extends import_constructs4.Construct {
1271
+ var _StaticHosting = class _StaticHosting extends import_constructs5.Construct {
1076
1272
  constructor(scope, id, props = {}) {
1077
1273
  super(scope, id);
1078
1274
  const stack = OpenHiService.of(scope);
@@ -1124,7 +1320,8 @@ _StaticHosting.SSM_PARAM_NAME_DISTRIBUTION_ARN = "STATIC_HOSTING_DISTRIBUTION_AR
1124
1320
  var StaticHosting = _StaticHosting;
1125
1321
 
1126
1322
  // src/services/open-hi-auth-service.ts
1127
- var import_aws_cognito4 = require("aws-cdk-lib/aws-cognito");
1323
+ var import_config4 = __toESM(require_lib());
1324
+ var import_aws_cognito5 = require("aws-cdk-lib/aws-cognito");
1128
1325
  var import_aws_kms2 = require("aws-cdk-lib/aws-kms");
1129
1326
  var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
1130
1327
  /**
@@ -1135,7 +1332,7 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
1135
1332
  ssmParamName: CognitoUserPool.SSM_PARAM_NAME,
1136
1333
  serviceType: _OpenHiAuthService.SERVICE_TYPE
1137
1334
  });
1138
- return import_aws_cognito4.UserPool.fromUserPoolId(scope, "user-pool", userPoolId);
1335
+ return import_aws_cognito5.UserPool.fromUserPoolId(scope, "user-pool", userPoolId);
1139
1336
  }
1140
1337
  /**
1141
1338
  * Returns an IUserPoolClient by looking up the Auth stack's User Pool Client ID from SSM.
@@ -1148,12 +1345,33 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
1148
1345
  serviceType: _OpenHiAuthService.SERVICE_TYPE
1149
1346
  }
1150
1347
  );
1151
- return import_aws_cognito4.UserPoolClient.fromUserPoolClientId(
1348
+ return import_aws_cognito5.UserPoolClient.fromUserPoolClientId(
1152
1349
  scope,
1153
1350
  "user-pool-client",
1154
1351
  userPoolClientId
1155
1352
  );
1156
1353
  }
1354
+ /**
1355
+ * Returns the dedicated fixture-seeder IUserPoolClient by looking up
1356
+ * its ID from SSM. Only non-prod auth stacks publish this parameter
1357
+ * (per the conditional in {@link createFixtureSeederClient}); calling
1358
+ * this against a prod-deployed stack will fail at lookup time.
1359
+ *
1360
+ * Consumed by `OpenHiRestApiService` (in non-prod) so the authorizer
1361
+ * accepts tokens issued by this client, and by the seed-fixtures CLI
1362
+ * to drive USER_PASSWORD_AUTH against this client's ID.
1363
+ */
1364
+ static fixtureSeederClientFromConstruct(scope) {
1365
+ const clientId = DiscoverableStringParameter.valueForLookupName(scope, {
1366
+ ssmParamName: CognitoFixtureSeederClient.SSM_PARAM_NAME,
1367
+ serviceType: _OpenHiAuthService.SERVICE_TYPE
1368
+ });
1369
+ return import_aws_cognito5.UserPoolClient.fromUserPoolClientId(
1370
+ scope,
1371
+ "fixture-seeder-client",
1372
+ clientId
1373
+ );
1374
+ }
1157
1375
  /**
1158
1376
  * Returns an IUserPoolDomain by looking up the Auth stack's User Pool Domain from SSM.
1159
1377
  */
@@ -1162,7 +1380,7 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
1162
1380
  ssmParamName: CognitoUserPoolDomain.SSM_PARAM_NAME,
1163
1381
  serviceType: _OpenHiAuthService.SERVICE_TYPE
1164
1382
  });
1165
- return import_aws_cognito4.UserPoolDomain.fromDomainName(scope, "user-pool-domain", domainName);
1383
+ return import_aws_cognito5.UserPoolDomain.fromDomainName(scope, "user-pool-domain", domainName);
1166
1384
  }
1167
1385
  /**
1168
1386
  * Returns an IKey (KMS) by looking up the Auth stack's User Pool KMS Key ARN from SSM.
@@ -1185,6 +1403,7 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
1185
1403
  this.userPool = this.createUserPool();
1186
1404
  this.userPoolClient = this.createUserPoolClient();
1187
1405
  this.userPoolDomain = this.createUserPoolDomain();
1406
+ this.fixtureSeederClient = this.createFixtureSeederClient();
1188
1407
  }
1189
1408
  /**
1190
1409
  * Creates the KMS key for the Cognito User Pool and exports its ARN to SSM.
@@ -1219,9 +1438,9 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
1219
1438
  customSenderKmsKey: this.userPoolKmsKey
1220
1439
  });
1221
1440
  userPool.addTrigger(
1222
- import_aws_cognito4.UserPoolOperation.PRE_TOKEN_GENERATION_CONFIG,
1441
+ import_aws_cognito5.UserPoolOperation.PRE_TOKEN_GENERATION_CONFIG,
1223
1442
  this.preTokenGenerationLambda,
1224
- import_aws_cognito4.LambdaVersion.V2_0
1443
+ import_aws_cognito5.LambdaVersion.V2_0
1225
1444
  );
1226
1445
  new DiscoverableStringParameter(this, "user-pool-param", {
1227
1446
  ssmParamName: CognitoUserPool.SSM_PARAM_NAME,
@@ -1246,6 +1465,31 @@ var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
1246
1465
  });
1247
1466
  return client;
1248
1467
  }
1468
+ /**
1469
+ * Creates the dedicated USER_PASSWORD_AUTH app client for the
1470
+ * `@openhi/seed-fixtures` CLI, **only** in non-prod environments.
1471
+ * Returns `undefined` when this stack is being deployed to a prod
1472
+ * stage so the prod auth stack carries no fixture-seeder code path.
1473
+ *
1474
+ * Operator post-deploy: create a `fixture-seeder` Cognito user with
1475
+ * a service password (manually via console or scripted with
1476
+ * `aws cognito-idp admin-create-user`); the CLI consumes those creds
1477
+ * via env vars to drive `InitiateAuth`.
1478
+ */
1479
+ createFixtureSeederClient() {
1480
+ if (this.ohEnv.ohStage.stageType === import_config4.OPEN_HI_STAGE.PROD) {
1481
+ return void 0;
1482
+ }
1483
+ const client = new CognitoFixtureSeederClient(this, {
1484
+ userPool: this.userPool
1485
+ });
1486
+ new DiscoverableStringParameter(this, "fixture-seeder-client-param", {
1487
+ ssmParamName: CognitoFixtureSeederClient.SSM_PARAM_NAME,
1488
+ stringValue: client.userPoolClientId,
1489
+ description: "Cognito User Pool Client ID for the OpenHI fixture-seeder CLI (USER_PASSWORD_AUTH; non-prod only); cross-stack reference"
1490
+ });
1491
+ return client;
1492
+ }
1249
1493
  /**
1250
1494
  * Creates the User Pool Domain (Cognito hosted UI) and exports domain name to SSM.
1251
1495
  * Look up via {@link OpenHiAuthService.userPoolDomainFromConstruct}.
@@ -1372,6 +1616,7 @@ _OpenHiGlobalService.SERVICE_TYPE = "global";
1372
1616
  var OpenHiGlobalService = _OpenHiGlobalService;
1373
1617
 
1374
1618
  // src/services/open-hi-rest-api-service.ts
1619
+ var import_config5 = __toESM(require_lib());
1375
1620
  var import_aws_apigatewayv22 = require("aws-cdk-lib/aws-apigatewayv2");
1376
1621
  var import_aws_apigatewayv2_authorizers = require("aws-cdk-lib/aws-apigatewayv2-authorizers");
1377
1622
  var import_aws_apigatewayv2_integrations = require("aws-cdk-lib/aws-apigatewayv2-integrations");
@@ -1424,7 +1669,11 @@ var _OpenHiDataService = class _OpenHiDataService extends OpenHiService {
1424
1669
  "data-store-change-stream",
1425
1670
  {
1426
1671
  streamName: `openhi-dstore-cdc-${this.branchHash}`,
1427
- streamMode: kinesis.StreamMode.ON_DEMAND
1672
+ streamMode: kinesis.StreamMode.ON_DEMAND,
1673
+ // CDK default for kinesis.Stream is RETAIN, which strands the stream
1674
+ // when a non-prod stack is destroyed. Use the service's policy so
1675
+ // non-prod tears down cleanly while prod retains.
1676
+ removalPolicy: this.removalPolicy
1428
1677
  }
1429
1678
  );
1430
1679
  this.dataStore = this.createDataStore();
@@ -1438,6 +1687,16 @@ var _OpenHiDataService = class _OpenHiDataService extends OpenHiService {
1438
1687
  dataEventBus: this.dataEventBus
1439
1688
  }
1440
1689
  );
1690
+ this.dataStorePostgresReplica = new DataStorePostgresReplica(
1691
+ this,
1692
+ "data-store-postgres-replica",
1693
+ {
1694
+ kinesisStream: this.dataStoreChangeStream,
1695
+ removalPolicy: this.removalPolicy,
1696
+ stackHash: this.stackHash,
1697
+ branchHash: this.branchHash
1698
+ }
1699
+ );
1441
1700
  }
1442
1701
  /**
1443
1702
  * Creates the data event bus.
@@ -1468,57 +1727,61 @@ _OpenHiDataService.SERVICE_TYPE = "data";
1468
1727
  var OpenHiDataService = _OpenHiDataService;
1469
1728
 
1470
1729
  // src/data/lambda/cors-options-lambda.ts
1471
- var import_node_fs3 = __toESM(require("fs"));
1472
- var import_node_path3 = __toESM(require("path"));
1473
- var import_aws_lambda3 = require("aws-cdk-lib/aws-lambda");
1474
- var import_aws_lambda_nodejs3 = require("aws-cdk-lib/aws-lambda-nodejs");
1475
- var import_constructs5 = require("constructs");
1476
- var HANDLER_NAME3 = "cors-options-lambda.handler.js";
1477
- function resolveHandlerEntry3(dirname) {
1478
- const sameDir = import_node_path3.default.join(dirname, HANDLER_NAME3);
1479
- if (import_node_fs3.default.existsSync(sameDir)) {
1730
+ var import_node_fs4 = __toESM(require("fs"));
1731
+ var import_node_path4 = __toESM(require("path"));
1732
+ var import_aws_lambda4 = require("aws-cdk-lib/aws-lambda");
1733
+ var import_aws_lambda_nodejs4 = require("aws-cdk-lib/aws-lambda-nodejs");
1734
+ var import_constructs6 = require("constructs");
1735
+ var HANDLER_NAME4 = "cors-options-lambda.handler.js";
1736
+ function resolveHandlerEntry4(dirname) {
1737
+ const sameDir = import_node_path4.default.join(dirname, HANDLER_NAME4);
1738
+ if (import_node_fs4.default.existsSync(sameDir)) {
1480
1739
  return sameDir;
1481
1740
  }
1482
- const fromLib = import_node_path3.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME3);
1741
+ const fromLib = import_node_path4.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME4);
1483
1742
  return fromLib;
1484
1743
  }
1485
- var CorsOptionsLambda = class extends import_constructs5.Construct {
1744
+ var CorsOptionsLambda = class extends import_constructs6.Construct {
1486
1745
  constructor(scope, id = "cors-options-lambda") {
1487
1746
  super(scope, id);
1488
- this.lambda = new import_aws_lambda_nodejs3.NodejsFunction(this, "handler", {
1489
- entry: resolveHandlerEntry3(__dirname),
1490
- runtime: import_aws_lambda3.Runtime.NODEJS_LATEST,
1747
+ this.lambda = new import_aws_lambda_nodejs4.NodejsFunction(this, "handler", {
1748
+ entry: resolveHandlerEntry4(__dirname),
1749
+ runtime: import_aws_lambda4.Runtime.NODEJS_LATEST,
1491
1750
  memorySize: 128
1492
1751
  });
1493
1752
  }
1494
1753
  };
1495
1754
 
1496
1755
  // src/data/lambda/rest-api-lambda.ts
1497
- var import_node_fs4 = __toESM(require("fs"));
1498
- var import_node_path4 = __toESM(require("path"));
1499
- var import_aws_lambda4 = require("aws-cdk-lib/aws-lambda");
1500
- var import_aws_lambda_nodejs4 = require("aws-cdk-lib/aws-lambda-nodejs");
1501
- var import_constructs6 = require("constructs");
1502
- var HANDLER_NAME4 = "rest-api-lambda.handler.js";
1503
- function resolveHandlerEntry4(dirname) {
1504
- const sameDir = import_node_path4.default.join(dirname, HANDLER_NAME4);
1505
- if (import_node_fs4.default.existsSync(sameDir)) {
1756
+ var import_node_fs5 = __toESM(require("fs"));
1757
+ var import_node_path5 = __toESM(require("path"));
1758
+ var import_aws_lambda5 = require("aws-cdk-lib/aws-lambda");
1759
+ var import_aws_lambda_nodejs5 = require("aws-cdk-lib/aws-lambda-nodejs");
1760
+ var import_constructs7 = require("constructs");
1761
+ var HANDLER_NAME5 = "rest-api-lambda.handler.js";
1762
+ function resolveHandlerEntry5(dirname) {
1763
+ const sameDir = import_node_path5.default.join(dirname, HANDLER_NAME5);
1764
+ if (import_node_fs5.default.existsSync(sameDir)) {
1506
1765
  return sameDir;
1507
1766
  }
1508
- const fromLib = import_node_path4.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME4);
1767
+ const fromLib = import_node_path5.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME5);
1509
1768
  return fromLib;
1510
1769
  }
1511
- var RestApiLambda = class extends import_constructs6.Construct {
1770
+ var RestApiLambda = class extends import_constructs7.Construct {
1512
1771
  constructor(scope, props) {
1513
1772
  super(scope, "rest-api-lambda");
1514
- this.lambda = new import_aws_lambda_nodejs4.NodejsFunction(this, "handler", {
1515
- entry: resolveHandlerEntry4(__dirname),
1516
- runtime: import_aws_lambda4.Runtime.NODEJS_LATEST,
1773
+ this.lambda = new import_aws_lambda_nodejs5.NodejsFunction(this, "handler", {
1774
+ entry: resolveHandlerEntry5(__dirname),
1775
+ runtime: import_aws_lambda5.Runtime.NODEJS_LATEST,
1517
1776
  memorySize: 1024,
1518
1777
  environment: {
1519
1778
  DYNAMO_TABLE_NAME: props.dynamoTableName,
1520
1779
  BRANCH_TAG_VALUE: props.branchTagValue,
1521
- HTTP_API_TAG_VALUE: props.httpApiTagValue
1780
+ HTTP_API_TAG_VALUE: props.httpApiTagValue,
1781
+ OPENHI_PG_CLUSTER_ARN: props.postgresClusterArn,
1782
+ OPENHI_PG_SECRET_ARN: props.postgresSecretArn,
1783
+ OPENHI_PG_DATABASE: props.postgresDatabase,
1784
+ OPENHI_PG_SCHEMA: props.postgresSchema
1522
1785
  },
1523
1786
  bundling: {
1524
1787
  minify: true,
@@ -1637,11 +1900,36 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
1637
1900
  */
1638
1901
  createRestApiLambdaAndRoutes(hostedZone, domainName) {
1639
1902
  const dataStoreTable = OpenHiDataService.dynamoDbDataStoreFromConstruct(this);
1903
+ const postgresClusterArn = DataStorePostgresReplica.clusterArnFromConstruct(this);
1904
+ const postgresSecretArn = DataStorePostgresReplica.secretArnFromConstruct(this);
1905
+ const postgresDatabase = DataStorePostgresReplica.databaseNameFromConstruct(this);
1906
+ const postgresSchema = getPostgresReplicaSchemaName(this.branchHash);
1640
1907
  const { lambda } = new RestApiLambda(this, {
1641
1908
  dynamoTableName: dataStoreTable.tableName,
1642
1909
  branchTagValue: this.branchName,
1643
- httpApiTagValue: RootHttpApi.SSM_PARAM_NAME
1910
+ httpApiTagValue: RootHttpApi.SSM_PARAM_NAME,
1911
+ postgresClusterArn,
1912
+ postgresSecretArn,
1913
+ postgresDatabase,
1914
+ postgresSchema
1644
1915
  });
1916
+ lambda.addToRolePolicy(
1917
+ new import_aws_iam.PolicyStatement({
1918
+ effect: import_aws_iam.Effect.ALLOW,
1919
+ actions: [
1920
+ "rds-data:ExecuteStatement",
1921
+ "rds-data:BatchExecuteStatement"
1922
+ ],
1923
+ resources: [postgresClusterArn]
1924
+ })
1925
+ );
1926
+ lambda.addToRolePolicy(
1927
+ new import_aws_iam.PolicyStatement({
1928
+ effect: import_aws_iam.Effect.ALLOW,
1929
+ actions: ["secretsmanager:GetSecretValue"],
1930
+ resources: [postgresSecretArn]
1931
+ })
1932
+ );
1645
1933
  const dynamoActions = [
1646
1934
  "dynamodb:GetItem",
1647
1935
  "dynamodb:Query",
@@ -1721,10 +2009,16 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
1721
2009
  createRootHttpApi(domainName) {
1722
2010
  const userPool = OpenHiAuthService.userPoolFromConstruct(this);
1723
2011
  const userPoolClient = OpenHiAuthService.userPoolClientFromConstruct(this);
2012
+ const userPoolClients = [userPoolClient];
2013
+ if (this.ohEnv.ohStage.stageType !== import_config5.OPEN_HI_STAGE.PROD) {
2014
+ userPoolClients.push(
2015
+ OpenHiAuthService.fixtureSeederClientFromConstruct(this)
2016
+ );
2017
+ }
1724
2018
  const cognitoAuthorizer = new import_aws_apigatewayv2_authorizers.HttpUserPoolAuthorizer(
1725
2019
  "cognito-authorizer",
1726
2020
  userPool,
1727
- { userPoolClients: [userPoolClient] }
2021
+ { userPoolClients }
1728
2022
  );
1729
2023
  const { corsPreflight: cors, ...restRootHttpApiProps } = this.props.rootHttpApiProps ?? {};
1730
2024
  const corsPreflight = cors !== void 0 ? {
@@ -1807,6 +2101,7 @@ var OpenHiGraphqlService = _OpenHiGraphqlService;
1807
2101
  // Annotate the CommonJS export names for ESM import in node:
1808
2102
  0 && (module.exports = {
1809
2103
  ChildHostedZone,
2104
+ CognitoFixtureSeederClient,
1810
2105
  CognitoUserPool,
1811
2106
  CognitoUserPoolClient,
1812
2107
  CognitoUserPoolDomain,
@@ -1816,6 +2111,7 @@ var OpenHiGraphqlService = _OpenHiGraphqlService;
1816
2111
  DATA_STORE_CHANGE_EVENT_SOURCE,
1817
2112
  DataEventBus,
1818
2113
  DataStoreHistoricalArchive,
2114
+ DataStorePostgresReplica,
1819
2115
  DiscoverableStringParameter,
1820
2116
  DynamoDbDataStore,
1821
2117
  OpenHiApp,
@@ -1828,6 +2124,9 @@ var OpenHiGraphqlService = _OpenHiGraphqlService;
1828
2124
  OpenHiService,
1829
2125
  OpenHiStage,
1830
2126
  OpsEventBus,
2127
+ POSTGRES_REPLICA_CLUSTER_ARN_SSM_NAME,
2128
+ POSTGRES_REPLICA_DATABASE_NAME_SSM_NAME,
2129
+ POSTGRES_REPLICA_SECRET_ARN_SSM_NAME,
1831
2130
  PreTokenGenerationLambda,
1832
2131
  REST_API_BASE_URL_SSM_NAME,
1833
2132
  RootGraphqlApi,
@@ -1837,6 +2136,7 @@ var OpenHiGraphqlService = _OpenHiGraphqlService;
1837
2136
  STATIC_HOSTING_SERVICE_TYPE,
1838
2137
  StaticHosting,
1839
2138
  buildFhirCurrentResourceChangeDetail,
1840
- getDynamoDbDataStoreTableName
2139
+ getDynamoDbDataStoreTableName,
2140
+ getPostgresReplicaSchemaName
1841
2141
  });
1842
2142
  //# sourceMappingURL=index.js.map