@jaypie/constructs 1.2.57 → 1.2.59

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 ADDED
@@ -0,0 +1,9 @@
1
+ # Jaypie Constructs 🐦‍⬛
2
+
3
+ CDK constructs for Jaypie applications.
4
+
5
+ See [jaypie.net](https://jaypie.net) for documentation.
6
+
7
+ ## 📜 License
8
+
9
+ [MIT License](./LICENSE.txt). Published by Finlayson Studio.
@@ -1,43 +1,18 @@
1
1
  import { Construct } from "constructs";
2
- import { SecretValue, RemovalPolicy, Stack } from "aws-cdk-lib";
3
2
  import * as secretsmanager from "aws-cdk-lib/aws-secretsmanager";
4
- import { ISecret, ISecretAttachmentTarget, RotationSchedule, RotationScheduleOptions } from "aws-cdk-lib/aws-secretsmanager";
5
- import { IKey } from "aws-cdk-lib/aws-kms";
6
- import { Grant, IGrantable, PolicyStatement, AddToResourcePolicyResult } from "aws-cdk-lib/aws-iam";
7
- export interface JaypieEnvSecretProps {
3
+ import { BuildSecretContext, JaypieSecret, JaypieSecretProps } from "./JaypieSecret";
4
+ export interface JaypieEnvSecretProps extends JaypieSecretProps {
8
5
  consumer?: boolean;
9
- envKey?: string;
10
6
  export?: string;
11
- generateSecretString?: secretsmanager.SecretStringGenerator;
12
7
  provider?: boolean;
13
- removalPolicy?: boolean | RemovalPolicy;
14
- roleTag?: string;
15
- vendorTag?: string;
16
- value?: string;
17
8
  }
18
- export declare class JaypieEnvSecret extends Construct implements ISecret {
19
- private readonly _envKey?;
20
- private readonly _secret;
9
+ /**
10
+ * @deprecated Use {@link JaypieSecret}. JaypieEnvSecret layers an
11
+ * environment-driven provider/consumer cross-stack pattern on top of
12
+ * JaypieSecret and will be removed in 2.0.
13
+ */
14
+ export declare class JaypieEnvSecret extends JaypieSecret {
15
+ protected static readonly shorthandPrefix: string;
21
16
  constructor(scope: Construct, idOrEnvKey: string, props?: JaypieEnvSecretProps);
22
- get stack(): Stack;
23
- get env(): {
24
- account: string;
25
- region: string;
26
- };
27
- applyRemovalPolicy(policy: RemovalPolicy): void;
28
- get secretArn(): string;
29
- get secretFullArn(): string | undefined;
30
- get secretName(): string;
31
- get secretRef(): secretsmanager.SecretReference;
32
- get encryptionKey(): IKey | undefined;
33
- get secretValue(): SecretValue;
34
- secretValueFromJson(key: string): SecretValue;
35
- grantRead(grantee: IGrantable, versionStages?: string[]): Grant;
36
- grantWrite(grantee: IGrantable): Grant;
37
- addRotationSchedule(id: string, options: RotationScheduleOptions): RotationSchedule;
38
- addToResourcePolicy(statement: PolicyStatement): AddToResourcePolicyResult;
39
- denyAccountRootDelete(): void;
40
- attach(target: ISecretAttachmentTarget): ISecret;
41
- cfnDynamicReferenceKey(options?: Parameters<ISecret["cfnDynamicReferenceKey"]>[0]): string;
42
- get envKey(): string | undefined;
17
+ protected buildSecret(context: BuildSecretContext): secretsmanager.ISecret;
43
18
  }
@@ -64,13 +64,14 @@ export interface JaypieLambdaProps {
64
64
  /**
65
65
  * Secrets to make available to the Lambda function.
66
66
  *
67
- * Supports both JaypieEnvSecret instances and strings:
68
- * - JaypieEnvSecret: used directly
67
+ * Supports both JaypieSecret instances and strings:
68
+ * - JaypieSecret (including JaypieEnvSecret): used directly
69
69
  * - String: creates a JaypieEnvSecret with the string as envKey
70
70
  * (reuses existing secrets within the same scope)
71
71
  */
72
72
  secrets?: SecretsArrayItem[];
73
73
  securityGroups?: ec2.ISecurityGroup[];
74
+ serviceTag?: string;
74
75
  timeout?: Duration | number;
75
76
  tracing?: lambda.Tracing;
76
77
  vendorTag?: string;
@@ -44,8 +44,8 @@ export interface JaypieNextjsProps {
44
44
  /**
45
45
  * Secrets to make available to the Next.js application.
46
46
  *
47
- * Supports both JaypieEnvSecret instances and strings:
48
- * - JaypieEnvSecret: used directly
47
+ * Supports both JaypieSecret instances and strings:
48
+ * - JaypieSecret (including JaypieEnvSecret): used directly
49
49
  * - String: creates a JaypieEnvSecret with the string as envKey
50
50
  * (reuses existing secrets within the same scope)
51
51
  */
@@ -0,0 +1,59 @@
1
+ import { Construct } from "constructs";
2
+ import { RemovalPolicy, SecretValue, Stack } from "aws-cdk-lib";
3
+ import * as secretsmanager from "aws-cdk-lib/aws-secretsmanager";
4
+ import { ISecret, ISecretAttachmentTarget, RotationSchedule, RotationScheduleOptions } from "aws-cdk-lib/aws-secretsmanager";
5
+ import { IKey } from "aws-cdk-lib/aws-kms";
6
+ import { AddToResourcePolicyResult, Grant, IGrantable, PolicyStatement } from "aws-cdk-lib/aws-iam";
7
+ export interface JaypieSecretProps {
8
+ envKey?: string;
9
+ generateSecretString?: secretsmanager.SecretStringGenerator;
10
+ removalPolicy?: boolean | RemovalPolicy;
11
+ roleTag?: string;
12
+ vendorTag?: string;
13
+ value?: string;
14
+ }
15
+ /**
16
+ * Context handed to {@link JaypieSecret.buildSecret} so subclasses can build the
17
+ * underlying secret differently (e.g. import vs. create) while reusing the
18
+ * shared id/envKey resolution and the full ISecret passthrough.
19
+ */
20
+ export interface BuildSecretContext {
21
+ envKey?: string;
22
+ id: string;
23
+ props: JaypieSecretProps;
24
+ treatAsEnvKey: boolean;
25
+ }
26
+ export declare class JaypieSecret extends Construct implements ISecret {
27
+ protected static readonly shorthandPrefix: string;
28
+ protected readonly _envKey?: string;
29
+ protected readonly _secret: secretsmanager.ISecret;
30
+ constructor(scope: Construct, idOrEnvKey: string, props?: JaypieSecretProps);
31
+ /**
32
+ * Builds the underlying secret. The base implementation always creates a new
33
+ * Secrets Manager secret from an envKey value, an explicit value, or a
34
+ * generated string. Subclasses may override to import an existing secret or
35
+ * emit cross-stack outputs.
36
+ */
37
+ protected buildSecret(context: BuildSecretContext): secretsmanager.ISecret;
38
+ get stack(): Stack;
39
+ get env(): {
40
+ account: string;
41
+ region: string;
42
+ };
43
+ applyRemovalPolicy(policy: RemovalPolicy): void;
44
+ get secretArn(): string;
45
+ get secretFullArn(): string | undefined;
46
+ get secretName(): string;
47
+ get secretRef(): secretsmanager.SecretReference;
48
+ get encryptionKey(): IKey | undefined;
49
+ get secretValue(): SecretValue;
50
+ secretValueFromJson(key: string): SecretValue;
51
+ grantRead(grantee: IGrantable, versionStages?: string[]): Grant;
52
+ grantWrite(grantee: IGrantable): Grant;
53
+ addRotationSchedule(id: string, options: RotationScheduleOptions): RotationSchedule;
54
+ addToResourcePolicy(statement: PolicyStatement): AddToResourcePolicyResult;
55
+ denyAccountRootDelete(): void;
56
+ attach(target: ISecretAttachmentTarget): ISecret;
57
+ cfnDynamicReferenceKey(options?: Parameters<ISecret["cfnDynamicReferenceKey"]>[0]): string;
58
+ get envKey(): string | undefined;
59
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,17 +1,17 @@
1
1
  import { Construct } from "constructs";
2
- import { JaypieEnvSecret } from "../JaypieEnvSecret.js";
2
+ import { JaypieSecret } from "../JaypieSecret.js";
3
3
  /**
4
- * Secrets input type that supports both JaypieEnvSecret instances and strings
5
- * - JaypieEnvSecret: passed through as-is
6
- * - string: converted to JaypieEnvSecret with the string as envKey
4
+ * Secrets input type that supports both JaypieSecret instances and strings
5
+ * - JaypieSecret (including JaypieEnvSecret subclasses): passed through as-is
6
+ * - string: converted to a JaypieEnvSecret with the string as envKey
7
7
  */
8
- export type SecretsArrayItem = JaypieEnvSecret | string;
8
+ export type SecretsArrayItem = JaypieSecret | string;
9
9
  /**
10
- * Resolves secrets input to an array of JaypieEnvSecret instances.
10
+ * Resolves secrets input to an array of JaypieSecret instances.
11
11
  *
12
- * When an item is already a JaypieEnvSecret, it's passed through as-is.
13
- * When an item is a string, a JaypieEnvSecret is created (or reused from cache)
14
- * with the string as the envKey.
12
+ * When an item is already a JaypieSecret (including a JaypieEnvSecret), it's
13
+ * passed through as-is. When an item is a string, a JaypieEnvSecret is created
14
+ * (or reused from cache) with the string as the envKey.
15
15
  *
16
16
  * Secrets are cached per scope to avoid creating duplicate secrets when
17
17
  * multiple constructs in the same scope reference the same secret.
@@ -39,7 +39,7 @@ export type SecretsArrayItem = JaypieEnvSecret | string;
39
39
  * const secrets2 = resolveSecrets(scope, ["SHARED_SECRET"]);
40
40
  * // secrets1[0] === secrets2[0] (same instance)
41
41
  */
42
- export declare function resolveSecrets(scope: Construct, secrets?: SecretsArrayItem[]): JaypieEnvSecret[];
42
+ export declare function resolveSecrets(scope: Construct, secrets?: SecretsArrayItem[]): JaypieSecret[];
43
43
  /**
44
44
  * Clears the secrets cache for a given scope.
45
45
  * Primarily useful for testing.
@@ -973,115 +973,67 @@ const resolveParamsAndSecrets = ({ paramsAndSecrets, options, } = {}) => {
973
973
  return resolvedParamsAndSecrets;
974
974
  };
975
975
 
976
- // It is a consumer if the environment is ephemeral
977
- function checkEnvIsConsumer$1(env = process.env) {
978
- return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
979
- !!env.CDK_ENV_PERSONAL ||
980
- /** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
981
- /** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
982
- }
983
- function checkEnvIsProvider$1(env = process.env) {
984
- return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
985
- }
986
- function cleanName$1(name) {
987
- return name.replace(/[^a-zA-Z0-9:-]/g, "");
988
- }
989
- function exportEnvName$1(name, env = process.env, consumer = false) {
990
- let rawName;
991
- if (checkEnvIsProvider$1(env)) {
992
- rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
993
- // Clean the entire name to only allow alphanumeric, colons, and hyphens
994
- return cleanName$1(rawName);
995
- }
996
- else {
997
- if (consumer || checkEnvIsConsumer$1(env)) {
998
- rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
999
- }
1000
- else {
1001
- rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
1002
- }
1003
- }
1004
- return cleanName$1(rawName);
1005
- }
1006
- class JaypieEnvSecret extends constructs.Construct {
976
+ class JaypieSecret extends constructs.Construct {
1007
977
  constructor(scope, idOrEnvKey, props) {
1008
978
  // Shorthand detection: treat idOrEnvKey as envKey when envKey prop is
1009
979
  // not set and idOrEnvKey either looks like a SCREAMING_SNAKE_CASE env
1010
980
  // var name or is already present in process.env. Convention-based
1011
981
  // detection ensures missing env vars still go through envKey validation
1012
982
  // instead of silently creating an empty secret.
983
+ const prefix = new.target.shorthandPrefix;
1013
984
  const looksLikeEnvKey = /^[A-Z][A-Z0-9_]*$/.test(idOrEnvKey);
1014
985
  const treatAsEnvKey = (!props || props.envKey === undefined) &&
1015
986
  (looksLikeEnvKey ||
1016
987
  (typeof process.env[idOrEnvKey] === "string" &&
1017
988
  process.env[idOrEnvKey] !== ""));
1018
- const id = treatAsEnvKey ? `EnvSecret_${idOrEnvKey}` : idOrEnvKey;
989
+ const id = treatAsEnvKey ? `${prefix}${idOrEnvKey}` : idOrEnvKey;
1019
990
  super(scope, id);
1020
- const { consumer = checkEnvIsConsumer$1(), envKey: envKeyProp, export: exportParam, generateSecretString, provider = checkEnvIsProvider$1(), removalPolicy, roleTag, vendorTag, value, } = props || {};
1021
- const envKey = treatAsEnvKey ? idOrEnvKey : envKeyProp;
991
+ const envKey = treatAsEnvKey ? idOrEnvKey : props?.envKey;
1022
992
  this._envKey = envKey;
1023
- let exportName;
1024
- if (!exportParam) {
1025
- // When shorthand detection is active, use the full construct id (which
1026
- // includes the "EnvSecret_" prefix) so the export name matches what was
1027
- // produced by earlier versions of this construct. Using the raw envKey
1028
- // here produces a shorter name that breaks existing cross-stack imports.
1029
- const exportSource = treatAsEnvKey ? id : envKey || id;
1030
- exportName = exportEnvName$1(exportSource, process.env, consumer);
1031
- }
1032
- else {
1033
- exportName = cleanName$1(exportParam);
1034
- }
1035
- if (!consumer &&
1036
- envKey &&
993
+ this._secret = this.buildSecret({
994
+ envKey,
995
+ id,
996
+ props: props || {},
997
+ treatAsEnvKey,
998
+ });
999
+ }
1000
+ /**
1001
+ * Builds the underlying secret. The base implementation always creates a new
1002
+ * Secrets Manager secret from an envKey value, an explicit value, or a
1003
+ * generated string. Subclasses may override to import an existing secret or
1004
+ * emit cross-stack outputs.
1005
+ */
1006
+ buildSecret(context) {
1007
+ const { envKey, id, props } = context;
1008
+ const { generateSecretString, removalPolicy, roleTag, vendorTag, value } = props;
1009
+ if (envKey &&
1037
1010
  !process.env[envKey] &&
1038
1011
  value === undefined &&
1039
1012
  !generateSecretString) {
1040
- throw new errors.ConfigurationError(`JaypieEnvSecret(${id}): envKey "${envKey}" is empty in process.env and no value or generateSecretString was provided`);
1013
+ throw new errors.ConfigurationError(`JaypieSecret(${id}): envKey "${envKey}" is empty in process.env and no value or generateSecretString was provided`);
1041
1014
  }
1042
- if (consumer) {
1043
- const secretName = cdk.Fn.importValue(exportName);
1044
- this._secret = secretsmanager__namespace.Secret.fromSecretNameV2(this, id, secretName);
1045
- // Add CfnOutput for consumer secrets
1046
- new cdk.CfnOutput(this, `ConsumedName`, {
1047
- value: this._secret.secretName,
1048
- });
1015
+ const secretValue = envKey && process.env[envKey] ? process.env[envKey] : value;
1016
+ const secret = new secretsmanager__namespace.Secret(this, id, {
1017
+ generateSecretString,
1018
+ secretStringValue: !generateSecretString && secretValue
1019
+ ? cdk.SecretValue.unsafePlainText(secretValue)
1020
+ : undefined,
1021
+ });
1022
+ if (removalPolicy !== undefined) {
1023
+ const policy = typeof removalPolicy === "boolean"
1024
+ ? removalPolicy
1025
+ ? cdk.RemovalPolicy.RETAIN
1026
+ : cdk.RemovalPolicy.DESTROY
1027
+ : removalPolicy;
1028
+ secret.applyRemovalPolicy(policy);
1049
1029
  }
1050
- else {
1051
- const secretValue = envKey && process.env[envKey] ? process.env[envKey] : value;
1052
- const secretProps = {
1053
- generateSecretString,
1054
- secretStringValue: !generateSecretString && secretValue
1055
- ? cdk.SecretValue.unsafePlainText(secretValue)
1056
- : undefined,
1057
- };
1058
- this._secret = new secretsmanager__namespace.Secret(this, id, secretProps);
1059
- if (removalPolicy !== undefined) {
1060
- const policy = typeof removalPolicy === "boolean"
1061
- ? removalPolicy
1062
- ? cdk.RemovalPolicy.RETAIN
1063
- : cdk.RemovalPolicy.DESTROY
1064
- : removalPolicy;
1065
- this._secret.applyRemovalPolicy(policy);
1066
- }
1067
- if (roleTag) {
1068
- cdk.Tags.of(this._secret).add(CDK$2.TAG.ROLE, roleTag);
1069
- }
1070
- if (vendorTag) {
1071
- cdk.Tags.of(this._secret).add(CDK$2.TAG.VENDOR, vendorTag);
1072
- }
1073
- if (provider) {
1074
- new cdk.CfnOutput(this, `ProvidedName`, {
1075
- value: this._secret.secretName,
1076
- exportName,
1077
- });
1078
- }
1079
- else {
1080
- new cdk.CfnOutput(this, `CreatedName`, {
1081
- value: this._secret.secretName,
1082
- });
1083
- }
1030
+ if (roleTag) {
1031
+ cdk.Tags.of(secret).add(CDK$2.TAG.ROLE, roleTag);
1032
+ }
1033
+ if (vendorTag) {
1034
+ cdk.Tags.of(secret).add(CDK$2.TAG.VENDOR, vendorTag);
1084
1035
  }
1036
+ return secret;
1085
1037
  }
1086
1038
  // IResource implementation
1087
1039
  get stack() {
@@ -1143,6 +1095,117 @@ class JaypieEnvSecret extends constructs.Construct {
1143
1095
  return this._envKey;
1144
1096
  }
1145
1097
  }
1098
+ // Construct id prefix used when an envKey is detected via shorthand.
1099
+ // Subclasses override this to preserve their own naming conventions.
1100
+ JaypieSecret.shorthandPrefix = "Secret_";
1101
+
1102
+ // It is a consumer if the environment is ephemeral
1103
+ function checkEnvIsConsumer$1(env = process.env) {
1104
+ return (env.PROJECT_ENV === CDK$2.ENV.PERSONAL ||
1105
+ !!env.CDK_ENV_PERSONAL ||
1106
+ /** @deprecated */ env.PROJECT_ENV === "ephemeral" ||
1107
+ /** @deprecated */ !!env.CDK_ENV_EPHEMERAL);
1108
+ }
1109
+ function checkEnvIsProvider$1(env = process.env) {
1110
+ return env.PROJECT_ENV === CDK$2.ENV.SANDBOX;
1111
+ }
1112
+ function cleanName$1(name) {
1113
+ return name.replace(/[^a-zA-Z0-9:-]/g, "");
1114
+ }
1115
+ function exportEnvName$1(name, env = process.env, consumer = false) {
1116
+ let rawName;
1117
+ if (checkEnvIsProvider$1(env)) {
1118
+ rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
1119
+ // Clean the entire name to only allow alphanumeric, colons, and hyphens
1120
+ return cleanName$1(rawName);
1121
+ }
1122
+ else {
1123
+ if (consumer || checkEnvIsConsumer$1(env)) {
1124
+ rawName = `env-${CDK$2.ENV.SANDBOX}-${env.PROJECT_KEY}-${name}`;
1125
+ }
1126
+ else {
1127
+ rawName = `env-${env.PROJECT_ENV}-${env.PROJECT_KEY}-${name}`;
1128
+ }
1129
+ }
1130
+ return cleanName$1(rawName);
1131
+ }
1132
+ /**
1133
+ * @deprecated Use {@link JaypieSecret}. JaypieEnvSecret layers an
1134
+ * environment-driven provider/consumer cross-stack pattern on top of
1135
+ * JaypieSecret and will be removed in 2.0.
1136
+ */
1137
+ class JaypieEnvSecret extends JaypieSecret {
1138
+ constructor(scope, idOrEnvKey, props) {
1139
+ super(scope, idOrEnvKey, props);
1140
+ }
1141
+ buildSecret(context) {
1142
+ const { envKey, id, treatAsEnvKey } = context;
1143
+ const props = context.props;
1144
+ const { consumer = checkEnvIsConsumer$1(), export: exportParam, generateSecretString, provider = checkEnvIsProvider$1(), removalPolicy, roleTag, vendorTag, value, } = props;
1145
+ let exportName;
1146
+ if (!exportParam) {
1147
+ // When shorthand detection is active, use the full construct id (which
1148
+ // includes the "EnvSecret_" prefix) so the export name matches what was
1149
+ // produced by earlier versions of this construct. Using the raw envKey
1150
+ // here produces a shorter name that breaks existing cross-stack imports.
1151
+ const exportSource = treatAsEnvKey ? id : envKey || id;
1152
+ exportName = exportEnvName$1(exportSource, process.env, consumer);
1153
+ }
1154
+ else {
1155
+ exportName = cleanName$1(exportParam);
1156
+ }
1157
+ if (!consumer &&
1158
+ envKey &&
1159
+ !process.env[envKey] &&
1160
+ value === undefined &&
1161
+ !generateSecretString) {
1162
+ throw new errors.ConfigurationError(`JaypieEnvSecret(${id}): envKey "${envKey}" is empty in process.env and no value or generateSecretString was provided`);
1163
+ }
1164
+ if (consumer) {
1165
+ const secretName = cdk.Fn.importValue(exportName);
1166
+ const secret = secretsmanager__namespace.Secret.fromSecretNameV2(this, id, secretName);
1167
+ // Add CfnOutput for consumer secrets
1168
+ new cdk.CfnOutput(this, `ConsumedName`, {
1169
+ value: secret.secretName,
1170
+ });
1171
+ return secret;
1172
+ }
1173
+ const secretValue = envKey && process.env[envKey] ? process.env[envKey] : value;
1174
+ const secret = new secretsmanager__namespace.Secret(this, id, {
1175
+ generateSecretString,
1176
+ secretStringValue: !generateSecretString && secretValue
1177
+ ? cdk.SecretValue.unsafePlainText(secretValue)
1178
+ : undefined,
1179
+ });
1180
+ if (removalPolicy !== undefined) {
1181
+ const policy = typeof removalPolicy === "boolean"
1182
+ ? removalPolicy
1183
+ ? cdk.RemovalPolicy.RETAIN
1184
+ : cdk.RemovalPolicy.DESTROY
1185
+ : removalPolicy;
1186
+ secret.applyRemovalPolicy(policy);
1187
+ }
1188
+ if (roleTag) {
1189
+ cdk.Tags.of(secret).add(CDK$2.TAG.ROLE, roleTag);
1190
+ }
1191
+ if (vendorTag) {
1192
+ cdk.Tags.of(secret).add(CDK$2.TAG.VENDOR, vendorTag);
1193
+ }
1194
+ if (provider) {
1195
+ new cdk.CfnOutput(this, `ProvidedName`, {
1196
+ value: secret.secretName,
1197
+ exportName,
1198
+ });
1199
+ }
1200
+ else {
1201
+ new cdk.CfnOutput(this, `CreatedName`, {
1202
+ value: secret.secretName,
1203
+ });
1204
+ }
1205
+ return secret;
1206
+ }
1207
+ }
1208
+ JaypieEnvSecret.shorthandPrefix = "EnvSecret_";
1146
1209
 
1147
1210
  /**
1148
1211
  * Cache for secrets by scope to avoid creating duplicates.
@@ -1179,11 +1242,11 @@ function getOrCreateSecret(scope, envKey, props) {
1179
1242
  return secret;
1180
1243
  }
1181
1244
  /**
1182
- * Resolves secrets input to an array of JaypieEnvSecret instances.
1245
+ * Resolves secrets input to an array of JaypieSecret instances.
1183
1246
  *
1184
- * When an item is already a JaypieEnvSecret, it's passed through as-is.
1185
- * When an item is a string, a JaypieEnvSecret is created (or reused from cache)
1186
- * with the string as the envKey.
1247
+ * When an item is already a JaypieSecret (including a JaypieEnvSecret), it's
1248
+ * passed through as-is. When an item is a string, a JaypieEnvSecret is created
1249
+ * (or reused from cache) with the string as the envKey.
1187
1250
  *
1188
1251
  * Secrets are cached per scope to avoid creating duplicate secrets when
1189
1252
  * multiple constructs in the same scope reference the same secret.
@@ -1219,7 +1282,7 @@ function resolveSecrets(scope, secrets) {
1219
1282
  if (typeof item === "string") {
1220
1283
  return getOrCreateSecret(scope, item);
1221
1284
  }
1222
- // Already a JaypieEnvSecret instance
1285
+ // Already a JaypieSecret (or JaypieEnvSecret) instance
1223
1286
  return item;
1224
1287
  });
1225
1288
  }
@@ -1429,12 +1492,12 @@ class JaypieLambda extends constructs.Construct {
1429
1492
  super(scope, id);
1430
1493
  const { allowAllOutbound, allowPublicSubnet, architecture = lambda__namespace.Architecture.X86_64, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, tables = [], environment: environmentInput, envSecrets = {}, ephemeralStorageSize, filesystem, handler = "index.handler", initialPolicy, layers = [], logGroup, logRetention = CDK$2.LAMBDA.LOG_RETENTION, maxEventAge, memorySize = CDK$2.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, profiling, profilingGroup, provisionedConcurrentExecutions, reservedConcurrentExecutions, retryAttempts, roleTag = CDK$2.ROLE.PROCESSING, runtime = new lambda__namespace.Runtime("nodejs24.x", lambda__namespace.RuntimeFamily.NODEJS, {
1431
1494
  supportsInlineCode: true,
1432
- }), runtimeManagementMode, secrets: secretsInput = [], securityGroups, timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, vpc, vpcSubnets, } = props;
1495
+ }), runtimeManagementMode, secrets: secretsInput = [], securityGroups, serviceTag, timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, vpc, vpcSubnets, } = props;
1433
1496
  // Resolve environment from array or object syntax
1434
1497
  const initialEnvironment = resolveEnvironment(environmentInput);
1435
1498
  // Get base environment with defaults
1436
1499
  const environment = jaypieLambdaEnv({ initialEnvironment });
1437
- // Resolve secrets from mixed array (strings and JaypieEnvSecret instances)
1500
+ // Resolve secrets from mixed array (strings and JaypieSecret instances)
1438
1501
  // Use Stack.of(this) to ensure secrets are shared at stack level across all constructs
1439
1502
  const secrets = resolveSecrets(cdk.Stack.of(this), secretsInput);
1440
1503
  const codeAsset = typeof code === "string" ? lambda__namespace.Code.fromAsset(code) : code;
@@ -1445,7 +1508,7 @@ class JaypieLambda extends constructs.Construct {
1445
1508
  ...acc,
1446
1509
  [`SECRET_${key}`]: secret.secretName,
1447
1510
  }), {});
1448
- // Process JaypieEnvSecret array
1511
+ // Process JaypieSecret array
1449
1512
  const jaypieSecretsEnvironment = secrets.reduce((acc, secret) => {
1450
1513
  if (secret.envKey) {
1451
1514
  return {
@@ -1515,7 +1578,7 @@ class JaypieLambda extends constructs.Construct {
1515
1578
  Object.values(envSecrets).forEach((secret) => {
1516
1579
  secret.grantRead(this._lambda);
1517
1580
  });
1518
- // Grant read permissions for JaypieEnvSecrets
1581
+ // Grant read permissions for JaypieSecrets
1519
1582
  secrets.forEach((secret) => {
1520
1583
  secret.grantRead(this._lambda);
1521
1584
  });
@@ -1543,6 +1606,9 @@ class JaypieLambda extends constructs.Construct {
1543
1606
  if (roleTag) {
1544
1607
  cdk.Tags.of(this._lambda).add(CDK$2.TAG.ROLE, roleTag);
1545
1608
  }
1609
+ if (serviceTag) {
1610
+ cdk.Tags.of(this._lambda).add(CDK$2.TAG.SERVICE, serviceTag);
1611
+ }
1546
1612
  if (vendorTag) {
1547
1613
  cdk.Tags.of(this._lambda).add(CDK$2.TAG.VENDOR, vendorTag);
1548
1614
  }
@@ -1667,7 +1733,7 @@ class JaypieQueuedLambda extends constructs.Construct {
1667
1733
  super(scope, id);
1668
1734
  const { allowAllOutbound, allowPublicSubnet, architecture, batchSize = 1, code, datadogApiKeyArn, deadLetterQueue, deadLetterQueueEnabled, deadLetterTopic, description, environment = {}, envSecrets = {}, ephemeralStorageSize, fifo = true, filesystem, handler = "index.handler", initialPolicy, layers = [], logGroup, logRetention = CDK$2.LAMBDA.LOG_RETENTION, maxEventAge, memorySize = CDK$2.LAMBDA.MEMORY_SIZE, paramsAndSecrets, paramsAndSecretsOptions, profiling, profilingGroup, provisionedConcurrentExecutions, reservedConcurrentExecutions, retryAttempts, roleTag, runtime = new lambda__namespace.Runtime("nodejs24.x", lambda__namespace.RuntimeFamily.NODEJS, {
1669
1735
  supportsInlineCode: true,
1670
- }), runtimeManagementMode, secrets = [], securityGroups, tables = [], timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, visibilityTimeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), vpc, vpcSubnets, } = props;
1736
+ }), runtimeManagementMode, secrets = [], securityGroups, serviceTag, tables = [], timeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), tracing, vendorTag, visibilityTimeout = cdk.Duration.seconds(CDK$2.DURATION.LAMBDA_WORKER), vpc, vpcSubnets, } = props;
1671
1737
  // Create SQS Queue
1672
1738
  this._queue = new sqs__namespace.Queue(this, "Queue", {
1673
1739
  fifo,
@@ -1678,6 +1744,9 @@ class JaypieQueuedLambda extends constructs.Construct {
1678
1744
  if (roleTag) {
1679
1745
  cdk.Tags.of(this._queue).add(CDK$2.TAG.ROLE, roleTag);
1680
1746
  }
1747
+ if (serviceTag) {
1748
+ cdk.Tags.of(this._queue).add(CDK$2.TAG.SERVICE, serviceTag);
1749
+ }
1681
1750
  if (vendorTag) {
1682
1751
  cdk.Tags.of(this._queue).add(CDK$2.TAG.VENDOR, vendorTag);
1683
1752
  }
@@ -1715,6 +1784,7 @@ class JaypieQueuedLambda extends constructs.Construct {
1715
1784
  runtimeManagementMode,
1716
1785
  secrets,
1717
1786
  securityGroups,
1787
+ serviceTag,
1718
1788
  tables,
1719
1789
  timeout,
1720
1790
  tracing,
@@ -1909,7 +1979,7 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
1909
1979
  constructor(scope, id, props) {
1910
1980
  props.fifo = false; // S3 event notifications are not supported for FIFO queues
1911
1981
  super(scope, id, props);
1912
- const { bucketName, roleTag, vendorTag, bucketOptions = {} } = props;
1982
+ const { bucketName, roleTag, serviceTag, vendorTag, bucketOptions = {}, } = props;
1913
1983
  // Create S3 Bucket
1914
1984
  this._bucket = new s3__namespace.Bucket(this, "Bucket", {
1915
1985
  bucketName: bucketOptions.bucketName || bucketName,
@@ -1920,6 +1990,9 @@ class JaypieBucketQueuedLambda extends JaypieQueuedLambda {
1920
1990
  if (roleTag) {
1921
1991
  cdk.Tags.of(this._bucket).add(CDK$2.TAG.ROLE, roleTag);
1922
1992
  }
1993
+ if (serviceTag) {
1994
+ cdk.Tags.of(this._bucket).add(CDK$2.TAG.SERVICE, serviceTag);
1995
+ }
1923
1996
  if (vendorTag) {
1924
1997
  cdk.Tags.of(this._bucket).add(CDK$2.TAG.VENDOR, vendorTag);
1925
1998
  }
@@ -3757,7 +3830,7 @@ class JaypieNextJs extends constructs.Construct {
3757
3830
  ? path__namespace.join(process.cwd(), props.nextjsPath)
3758
3831
  : props?.nextjsPath || path__namespace.join(process.cwd(), "..", "nextjs");
3759
3832
  const paramsAndSecrets = resolveParamsAndSecrets();
3760
- // Resolve secrets from mixed array (strings and JaypieEnvSecret instances)
3833
+ // Resolve secrets from mixed array (strings and JaypieSecret instances)
3761
3834
  // Use Stack.of(this) to ensure secrets are shared at stack level across all constructs
3762
3835
  const secrets = resolveSecrets(cdk.Stack.of(this), props?.secrets);
3763
3836
  // Process secrets environment variables
@@ -3765,7 +3838,7 @@ class JaypieNextJs extends constructs.Construct {
3765
3838
  ...acc,
3766
3839
  [`SECRET_${key}`]: secret.secretName,
3767
3840
  }), {});
3768
- // Process JaypieEnvSecret array
3841
+ // Process JaypieSecret array
3769
3842
  const jaypieSecretsEnvironment = secrets.reduce((acc, secret) => {
3770
3843
  if (secret.envKey) {
3771
3844
  return {
@@ -3840,7 +3913,7 @@ class JaypieNextJs extends constructs.Construct {
3840
3913
  Object.values(envSecrets).forEach((secret) => {
3841
3914
  secret.grantRead(nextjs.serverFunction.lambdaFunction);
3842
3915
  });
3843
- // Grant read permissions for JaypieEnvSecrets
3916
+ // Grant read permissions for JaypieSecrets
3844
3917
  secrets.forEach((secret) => {
3845
3918
  secret.grantRead(nextjs.serverFunction.lambdaFunction);
3846
3919
  });
@@ -4204,6 +4277,9 @@ class JaypieSsoPermissions extends constructs.Construct {
4204
4277
  {
4205
4278
  Effect: "Allow",
4206
4279
  Action: [
4280
+ "bedrock-agent:*",
4281
+ "bedrock-agentcore:*",
4282
+ "bedrock:*",
4207
4283
  "budgets:*",
4208
4284
  "ce:*",
4209
4285
  "cloudformation:*",
@@ -5404,6 +5480,7 @@ exports.JaypieNextJs = JaypieNextJs;
5404
5480
  exports.JaypieOpenAiSecret = JaypieOpenAiSecret;
5405
5481
  exports.JaypieOrganizationTrail = JaypieOrganizationTrail;
5406
5482
  exports.JaypieQueuedLambda = JaypieQueuedLambda;
5483
+ exports.JaypieSecret = JaypieSecret;
5407
5484
  exports.JaypieSsoPermissions = JaypieSsoPermissions;
5408
5485
  exports.JaypieSsoSyncApplication = JaypieSsoSyncApplication;
5409
5486
  exports.JaypieStack = JaypieStack;