sst 2.1.26 → 2.1.28
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.
|
@@ -5,18 +5,15 @@ import * as fs from "fs/promises";
|
|
|
5
5
|
import { DatabaseMetadata, EnumCollection, PostgresDialect, MysqlDialect, Serializer, Transformer, } from "kysely-codegen";
|
|
6
6
|
import { Context } from "../../../context/context.js";
|
|
7
7
|
import { useBus } from "../../../bus.js";
|
|
8
|
-
import { useProject } from "../../../project.js";
|
|
9
8
|
import { Logger } from "../../../logger.js";
|
|
10
|
-
import { useAWSClient,
|
|
9
|
+
import { useAWSClient, } from "../../../credentials.js";
|
|
11
10
|
export const useKyselyTypeGenerator = Context.memo(async () => {
|
|
12
11
|
let databases = [];
|
|
13
12
|
const bus = useBus();
|
|
14
|
-
const project = useProject();
|
|
15
13
|
async function generate(db) {
|
|
16
14
|
if (!db.types)
|
|
17
15
|
return;
|
|
18
16
|
Logger.debug("Generating types for", db.migratorID);
|
|
19
|
-
const credentials = await useAWSCredentials();
|
|
20
17
|
const k = new Kysely({
|
|
21
18
|
dialect: new DataApiDialect({
|
|
22
19
|
mode: db.engine.includes("postgres") ? "postgres" : "mysql",
|
|
@@ -93,7 +90,7 @@ export const useKyselyTypeGenerator = Context.memo(async () => {
|
|
|
93
90
|
const db = databases.find((db) => db.migratorID === evt.properties.functionID);
|
|
94
91
|
if (!db)
|
|
95
92
|
return;
|
|
96
|
-
generate(db);
|
|
93
|
+
generate(db).catch(() => { });
|
|
97
94
|
});
|
|
98
95
|
Logger.debug("Loaded kyseley type generator");
|
|
99
96
|
});
|
package/constructs/Api.d.ts
CHANGED
|
@@ -2,10 +2,11 @@ import { Construct } from "constructs";
|
|
|
2
2
|
import * as acm from "aws-cdk-lib/aws-certificatemanager";
|
|
3
3
|
import * as apig from "@aws-cdk/aws-apigatewayv2-alpha";
|
|
4
4
|
import * as apigAuthorizers from "@aws-cdk/aws-apigatewayv2-authorizers-alpha";
|
|
5
|
-
import
|
|
5
|
+
import { HttpUrlIntegrationProps, HttpAlbIntegrationProps, HttpNlbIntegrationProps } from "@aws-cdk/aws-apigatewayv2-integrations-alpha";
|
|
6
6
|
import * as elb from "aws-cdk-lib/aws-elasticloadbalancingv2";
|
|
7
7
|
import * as lambda from "aws-cdk-lib/aws-lambda";
|
|
8
8
|
import * as logs from "aws-cdk-lib/aws-logs";
|
|
9
|
+
import { HttpAwsIntegrationProps } from "./cdk/HttpAwsIntegration.js";
|
|
9
10
|
import { SSTConstruct } from "./Construct.js";
|
|
10
11
|
import { Function as Fn, FunctionProps, FunctionInlineDefinition, FunctionDefinition } from "./Function.js";
|
|
11
12
|
import { FunctionBindingProps } from "./util/functionBinding.js";
|
|
@@ -16,6 +17,8 @@ import * as apigV2Domain from "./util/apiGatewayV2Domain.js";
|
|
|
16
17
|
import * as apigV2AccessLog from "./util/apiGatewayV2AccessLog.js";
|
|
17
18
|
declare const PayloadFormatVersions: readonly ["1.0", "2.0"];
|
|
18
19
|
export type ApiPayloadFormatVersion = (typeof PayloadFormatVersions)[number];
|
|
20
|
+
export interface CdkHttpAwsIntegrationProps extends HttpAwsIntegrationProps {
|
|
21
|
+
}
|
|
19
22
|
export type ApiAuthorizer = ApiUserPoolAuthorizer | ApiJwtAuthorizer | ApiLambdaAuthorizer;
|
|
20
23
|
interface ApiBaseAuthorizer {
|
|
21
24
|
/**
|
|
@@ -394,7 +397,7 @@ export interface ApiProps<Authorizers extends Record<string, ApiAuthorizer> = Re
|
|
|
394
397
|
httpStages?: Omit<apig.HttpStageProps, "httpApi">[];
|
|
395
398
|
};
|
|
396
399
|
}
|
|
397
|
-
export type ApiRouteProps<AuthorizerKeys> = FunctionInlineDefinition | ApiFunctionRouteProps<AuthorizerKeys> | ApiHttpRouteProps<AuthorizerKeys> | ApiAlbRouteProps<AuthorizerKeys> | ApiNlbRouteProps<AuthorizerKeys> | ApiGraphQLRouteProps<AuthorizerKeys>;
|
|
400
|
+
export type ApiRouteProps<AuthorizerKeys> = FunctionInlineDefinition | ApiFunctionRouteProps<AuthorizerKeys> | ApiAwsRouteProps<AuthorizerKeys> | ApiHttpRouteProps<AuthorizerKeys> | ApiAlbRouteProps<AuthorizerKeys> | ApiNlbRouteProps<AuthorizerKeys> | ApiGraphQLRouteProps<AuthorizerKeys>;
|
|
398
401
|
interface ApiBaseRouteProps<AuthorizerKeys = string> {
|
|
399
402
|
authorizer?: "none" | "iam" | (string extends AuthorizerKeys ? Omit<AuthorizerKeys, "none" | "iam"> : AuthorizerKeys);
|
|
400
403
|
authorizationScopes?: string[];
|
|
@@ -432,6 +435,37 @@ export interface ApiFunctionRouteProps<AuthorizersKeys = string> extends ApiBase
|
|
|
432
435
|
function?: lambda.IFunction;
|
|
433
436
|
};
|
|
434
437
|
}
|
|
438
|
+
/**
|
|
439
|
+
* Specify a function route handler and configure additional options
|
|
440
|
+
*
|
|
441
|
+
* @example
|
|
442
|
+
* ```js
|
|
443
|
+
* api.addRoutes(stack, {
|
|
444
|
+
* "GET /notes/{id}": {
|
|
445
|
+
* type: "aws",
|
|
446
|
+
* cdk: {
|
|
447
|
+
* integration: {
|
|
448
|
+
* subtype: "EventBridge-PutEvents",
|
|
449
|
+
* parameterMapping: ParameterMapping.fromObject({
|
|
450
|
+
* Source: MappingValue.custom("$request.body.source"),
|
|
451
|
+
* DetailType: MappingValue.custom("$request.body.detailType"),
|
|
452
|
+
* Detail: MappingValue.custom("$request.body.detail"),
|
|
453
|
+
* }),
|
|
454
|
+
* }
|
|
455
|
+
* }
|
|
456
|
+
* }
|
|
457
|
+
* });
|
|
458
|
+
* ```
|
|
459
|
+
*/
|
|
460
|
+
export interface ApiAwsRouteProps<AuthorizersKeys> extends ApiBaseRouteProps<AuthorizersKeys> {
|
|
461
|
+
/**
|
|
462
|
+
* This is a constant
|
|
463
|
+
*/
|
|
464
|
+
type: "aws";
|
|
465
|
+
cdk: {
|
|
466
|
+
integration: Omit<CdkHttpAwsIntegrationProps, "credentials">;
|
|
467
|
+
};
|
|
468
|
+
}
|
|
435
469
|
/**
|
|
436
470
|
* Specify a route handler that forwards to another URL
|
|
437
471
|
*
|
|
@@ -458,7 +492,7 @@ export interface ApiHttpRouteProps<AuthorizersKeys> extends ApiBaseRouteProps<Au
|
|
|
458
492
|
/**
|
|
459
493
|
* Override the underlying CDK integration
|
|
460
494
|
*/
|
|
461
|
-
integration:
|
|
495
|
+
integration: HttpUrlIntegrationProps;
|
|
462
496
|
};
|
|
463
497
|
}
|
|
464
498
|
/**
|
|
@@ -483,7 +517,7 @@ export interface ApiAlbRouteProps<AuthorizersKeys> extends ApiBaseRouteProps<Aut
|
|
|
483
517
|
* The listener to the application load balancer used for the integration.
|
|
484
518
|
*/
|
|
485
519
|
albListener: elb.IApplicationListener;
|
|
486
|
-
integration?:
|
|
520
|
+
integration?: HttpAlbIntegrationProps;
|
|
487
521
|
};
|
|
488
522
|
}
|
|
489
523
|
/**
|
|
@@ -508,7 +542,7 @@ export interface ApiNlbRouteProps<AuthorizersKeys> extends ApiBaseRouteProps<Aut
|
|
|
508
542
|
* The listener to the application load balancer used for the integration.
|
|
509
543
|
*/
|
|
510
544
|
nlbListener: elb.INetworkListener;
|
|
511
|
-
integration?:
|
|
545
|
+
integration?: HttpNlbIntegrationProps;
|
|
512
546
|
};
|
|
513
547
|
}
|
|
514
548
|
/**
|
|
@@ -732,7 +766,7 @@ export declare class Api<Authorizers extends Record<string, ApiAuthorizer> = Rec
|
|
|
732
766
|
output: string | undefined;
|
|
733
767
|
commands: string[] | undefined;
|
|
734
768
|
} | {
|
|
735
|
-
type: "lambda_function" | "url" | "alb" | "nlb";
|
|
769
|
+
type: "lambda_function" | "aws" | "url" | "alb" | "nlb";
|
|
736
770
|
route: string;
|
|
737
771
|
fn?: undefined;
|
|
738
772
|
schema?: undefined;
|
|
@@ -746,6 +780,7 @@ export declare class Api<Authorizers extends Record<string, ApiAuthorizer> = Rec
|
|
|
746
780
|
private createHttpApi;
|
|
747
781
|
private addAuthorizers;
|
|
748
782
|
private addRoute;
|
|
783
|
+
private createAwsProxyIntegration;
|
|
749
784
|
private createHttpIntegration;
|
|
750
785
|
private createAlbIntegration;
|
|
751
786
|
private createNlbIntegration;
|
package/constructs/Api.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { Construct } from "constructs";
|
|
2
2
|
import * as cdkLib from "aws-cdk-lib";
|
|
3
3
|
import * as cognito from "aws-cdk-lib/aws-cognito";
|
|
4
|
+
import { Role, ServicePrincipal, PolicyDocument, PolicyStatement, } from "aws-cdk-lib/aws-iam";
|
|
4
5
|
import * as apig from "@aws-cdk/aws-apigatewayv2-alpha";
|
|
5
6
|
import * as apigAuthorizers from "@aws-cdk/aws-apigatewayv2-authorizers-alpha";
|
|
6
|
-
import
|
|
7
|
+
import { HttpUrlIntegration, HttpAlbIntegration, HttpNlbIntegration, HttpLambdaIntegration, } from "@aws-cdk/aws-apigatewayv2-integrations-alpha";
|
|
8
|
+
import { HttpAwsIntegration, } from "./cdk/HttpAwsIntegration.js";
|
|
7
9
|
import { Stack } from "./Stack.js";
|
|
8
10
|
import { getFunctionRef, isCDKConstruct } from "./Construct.js";
|
|
9
11
|
import { Function as Fn, } from "./Function.js";
|
|
@@ -438,6 +440,12 @@ export class Api extends Construct {
|
|
|
438
440
|
this.createFunctionIntegration(scope, routeKey, routeProps, postfixName),
|
|
439
441
|
];
|
|
440
442
|
}
|
|
443
|
+
if (routeValue.type === "aws") {
|
|
444
|
+
return [
|
|
445
|
+
routeValue,
|
|
446
|
+
this.createAwsProxyIntegration(scope, routeKey, routeValue, postfixName),
|
|
447
|
+
];
|
|
448
|
+
}
|
|
441
449
|
if (routeValue.type === "alb") {
|
|
442
450
|
return [
|
|
443
451
|
routeValue,
|
|
@@ -476,7 +484,7 @@ export class Api extends Construct {
|
|
|
476
484
|
}
|
|
477
485
|
if ("handler" in routeValue)
|
|
478
486
|
throw new Error(`Function definition must be nested under the "function" key in the route props for "${routeKey}". ie. { function: { handler: "myfunc.handler" } }`);
|
|
479
|
-
throw new Error(`Invalid route type for "${routeKey}"
|
|
487
|
+
throw new Error(`Invalid route type "${routeValue.type}" for "${routeKey}".`);
|
|
480
488
|
})();
|
|
481
489
|
const { authorizationType, authorizer, authorizationScopes } = this.buildRouteAuth(routeProps);
|
|
482
490
|
const route = new apig.HttpRoute(scope, `Route_${postfixName}`, {
|
|
@@ -502,11 +510,45 @@ export class Api extends Construct {
|
|
|
502
510
|
cfnRoute.authorizationType = "NONE";
|
|
503
511
|
}
|
|
504
512
|
}
|
|
513
|
+
createAwsProxyIntegration(scope, routeKey, routeProps, postfixName) {
|
|
514
|
+
// Create IAM role for API Gateway to call the AWS services
|
|
515
|
+
const [service, serviceApi] = routeProps.cdk.integration.subtype.split("-");
|
|
516
|
+
const servicePrefix = {
|
|
517
|
+
EventBridge: "events",
|
|
518
|
+
SQS: "sqs",
|
|
519
|
+
AppConfig: "appconfig",
|
|
520
|
+
Kinesis: "kinesis",
|
|
521
|
+
StepFunctions: "states",
|
|
522
|
+
}[service];
|
|
523
|
+
const role = new Role(scope, `IntegrationRole_${postfixName}`, {
|
|
524
|
+
assumedBy: new ServicePrincipal("apigateway.amazonaws.com"),
|
|
525
|
+
inlinePolicies: {
|
|
526
|
+
Policy: new PolicyDocument({
|
|
527
|
+
statements: [
|
|
528
|
+
new PolicyStatement({
|
|
529
|
+
actions: [`${servicePrefix}:${serviceApi}`],
|
|
530
|
+
resources: ["*"],
|
|
531
|
+
}),
|
|
532
|
+
],
|
|
533
|
+
}),
|
|
534
|
+
},
|
|
535
|
+
});
|
|
536
|
+
// Create integration
|
|
537
|
+
const integration = new HttpAwsIntegration(`Integration_${postfixName}`, {
|
|
538
|
+
...routeProps.cdk.integration,
|
|
539
|
+
credentials: apig.IntegrationCredentials.fromRole(role),
|
|
540
|
+
});
|
|
541
|
+
// Store route
|
|
542
|
+
this.routesData[routeKey] = {
|
|
543
|
+
type: "aws",
|
|
544
|
+
};
|
|
545
|
+
return integration;
|
|
546
|
+
}
|
|
505
547
|
createHttpIntegration(scope, routeKey, routeProps, postfixName) {
|
|
506
548
|
///////////////////
|
|
507
549
|
// Create integration
|
|
508
550
|
///////////////////
|
|
509
|
-
const integration = new
|
|
551
|
+
const integration = new HttpUrlIntegration(`Integration_${postfixName}`, routeProps.url, routeProps.cdk?.integration);
|
|
510
552
|
// Store route
|
|
511
553
|
this.routesData[routeKey] = {
|
|
512
554
|
type: "url",
|
|
@@ -518,7 +560,7 @@ export class Api extends Construct {
|
|
|
518
560
|
///////////////////
|
|
519
561
|
// Create integration
|
|
520
562
|
///////////////////
|
|
521
|
-
const integration = new
|
|
563
|
+
const integration = new HttpAlbIntegration(`Integration_${postfixName}`, routeProps.cdk?.albListener, routeProps.cdk?.integration);
|
|
522
564
|
// Store route
|
|
523
565
|
this.routesData[routeKey] = {
|
|
524
566
|
type: "alb",
|
|
@@ -530,7 +572,7 @@ export class Api extends Construct {
|
|
|
530
572
|
///////////////////
|
|
531
573
|
// Create integration
|
|
532
574
|
///////////////////
|
|
533
|
-
const integration = new
|
|
575
|
+
const integration = new HttpNlbIntegration(`Integration_${postfixName}`, routeProps.cdk?.nlbListener, routeProps.cdk?.integration);
|
|
534
576
|
// Store route
|
|
535
577
|
this.routesData[routeKey] = {
|
|
536
578
|
type: "nlb",
|
|
@@ -576,7 +618,7 @@ export class Api extends Construct {
|
|
|
576
618
|
///////////////////
|
|
577
619
|
// Create integration
|
|
578
620
|
///////////////////
|
|
579
|
-
const integration = new
|
|
621
|
+
const integration = new HttpLambdaIntegration(`Integration_${postfixName}`, lambda, {
|
|
580
622
|
payloadFormatVersion: integrationPayloadFormatVersion,
|
|
581
623
|
});
|
|
582
624
|
// Store route
|
|
@@ -615,7 +657,7 @@ export class Api extends Construct {
|
|
|
615
657
|
///////////////////
|
|
616
658
|
// Create integration
|
|
617
659
|
///////////////////
|
|
618
|
-
const integration = new
|
|
660
|
+
const integration = new HttpLambdaIntegration(`Integration_${postfixName}`, lambda, {
|
|
619
661
|
payloadFormatVersion: integrationPayloadFormatVersion,
|
|
620
662
|
});
|
|
621
663
|
// Store route
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { HttpIntegrationSubtype, HttpRouteIntegrationBindOptions, HttpRouteIntegrationConfig, HttpRouteIntegration, ParameterMapping, IntegrationCredentials } from "@aws-cdk/aws-apigatewayv2-alpha";
|
|
2
|
+
/**
|
|
3
|
+
* Properties to initialize a new `HttpProxyIntegration`.
|
|
4
|
+
*/
|
|
5
|
+
export interface HttpAwsIntegrationProps {
|
|
6
|
+
/**
|
|
7
|
+
* Specifies the AWS service action to invoke
|
|
8
|
+
*/
|
|
9
|
+
readonly subtype: HttpIntegrationSubtype;
|
|
10
|
+
/**
|
|
11
|
+
* Specifies how to transform HTTP requests before sending them to the backend
|
|
12
|
+
*/
|
|
13
|
+
readonly parameterMapping: ParameterMapping;
|
|
14
|
+
/**
|
|
15
|
+
* The credentials with which to invoke the integration.
|
|
16
|
+
*
|
|
17
|
+
* @default - no credentials, use resource-based permissions on supported AWS services
|
|
18
|
+
*/
|
|
19
|
+
readonly credentials: IntegrationCredentials;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The HTTP Proxy integration resource for HTTP API
|
|
23
|
+
*/
|
|
24
|
+
export declare class HttpAwsIntegration extends HttpRouteIntegration {
|
|
25
|
+
private readonly props;
|
|
26
|
+
/**
|
|
27
|
+
* @param id id of the underlying integration construct
|
|
28
|
+
* @param props properties to configure the integration
|
|
29
|
+
*/
|
|
30
|
+
constructor(id: string, props: HttpAwsIntegrationProps);
|
|
31
|
+
bind(_: HttpRouteIntegrationBindOptions): HttpRouteIntegrationConfig;
|
|
32
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { HttpIntegrationType, HttpRouteIntegration, PayloadFormatVersion, } from "@aws-cdk/aws-apigatewayv2-alpha";
|
|
2
|
+
/**
|
|
3
|
+
* The HTTP Proxy integration resource for HTTP API
|
|
4
|
+
*/
|
|
5
|
+
export class HttpAwsIntegration extends HttpRouteIntegration {
|
|
6
|
+
props;
|
|
7
|
+
/**
|
|
8
|
+
* @param id id of the underlying integration construct
|
|
9
|
+
* @param props properties to configure the integration
|
|
10
|
+
*/
|
|
11
|
+
constructor(id, props) {
|
|
12
|
+
super(id);
|
|
13
|
+
this.props = props;
|
|
14
|
+
}
|
|
15
|
+
bind(_) {
|
|
16
|
+
return {
|
|
17
|
+
payloadFormatVersion: PayloadFormatVersion.VERSION_1_0,
|
|
18
|
+
type: HttpIntegrationType.AWS_PROXY,
|
|
19
|
+
subtype: this.props.subtype,
|
|
20
|
+
parameterMapping: this.props.parameterMapping,
|
|
21
|
+
credentials: this.props.credentials,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
}
|
package/package.json
CHANGED
package/sst.mjs
CHANGED
|
@@ -6329,18 +6329,15 @@ var init_kysely = __esm({
|
|
|
6329
6329
|
"use strict";
|
|
6330
6330
|
init_context();
|
|
6331
6331
|
init_bus();
|
|
6332
|
-
init_project();
|
|
6333
6332
|
init_logger();
|
|
6334
6333
|
init_credentials();
|
|
6335
6334
|
useKyselyTypeGenerator = Context.memo(async () => {
|
|
6336
6335
|
let databases = [];
|
|
6337
6336
|
const bus = useBus();
|
|
6338
|
-
const project = useProject();
|
|
6339
6337
|
async function generate2(db) {
|
|
6340
6338
|
if (!db.types)
|
|
6341
6339
|
return;
|
|
6342
6340
|
Logger.debug("Generating types for", db.migratorID);
|
|
6343
|
-
const credentials = await useAWSCredentials();
|
|
6344
6341
|
const k = new Kysely({
|
|
6345
6342
|
dialect: new DataApiDialect({
|
|
6346
6343
|
mode: db.engine.includes("postgres") ? "postgres" : "mysql",
|
|
@@ -6413,7 +6410,8 @@ var init_kysely = __esm({
|
|
|
6413
6410
|
);
|
|
6414
6411
|
if (!db)
|
|
6415
6412
|
return;
|
|
6416
|
-
generate2(db)
|
|
6413
|
+
generate2(db).catch(() => {
|
|
6414
|
+
});
|
|
6417
6415
|
});
|
|
6418
6416
|
Logger.debug("Loaded kyseley type generator");
|
|
6419
6417
|
});
|
|
@@ -6758,7 +6756,7 @@ var env = (program2) => program2.command(
|
|
|
6758
6756
|
const fs19 = await import("fs/promises");
|
|
6759
6757
|
const { SiteEnv } = await Promise.resolve().then(() => (init_site_env(), site_env_exports));
|
|
6760
6758
|
const { spawnSync } = await import("child_process");
|
|
6761
|
-
const { useAWSCredentials:
|
|
6759
|
+
const { useAWSCredentials: useAWSCredentials3 } = await Promise.resolve().then(() => (init_credentials(), credentials_exports));
|
|
6762
6760
|
const { useProject: useProject2 } = await Promise.resolve().then(() => (init_project(), project_exports));
|
|
6763
6761
|
let spinner;
|
|
6764
6762
|
while (true) {
|
|
@@ -6774,7 +6772,7 @@ var env = (program2) => program2.command(
|
|
|
6774
6772
|
const sites = await SiteEnv.values();
|
|
6775
6773
|
const env2 = sites[process.cwd()] || {};
|
|
6776
6774
|
const project = useProject2();
|
|
6777
|
-
const credentials = await
|
|
6775
|
+
const credentials = await useAWSCredentials3();
|
|
6778
6776
|
const joined = args.command?.join(" ");
|
|
6779
6777
|
if (!joined)
|
|
6780
6778
|
throw new VisibleError(
|
|
@@ -7139,11 +7137,11 @@ var bind = (program2) => program2.command(
|
|
|
7139
7137
|
async (args) => {
|
|
7140
7138
|
const { Config: Config2 } = await Promise.resolve().then(() => (init_config(), config_exports));
|
|
7141
7139
|
const { spawnSync } = await import("child_process");
|
|
7142
|
-
const { useAWSCredentials:
|
|
7140
|
+
const { useAWSCredentials: useAWSCredentials3 } = await Promise.resolve().then(() => (init_credentials(), credentials_exports));
|
|
7143
7141
|
const { useProject: useProject2 } = await Promise.resolve().then(() => (init_project(), project_exports));
|
|
7144
7142
|
const env2 = await Config2.env();
|
|
7145
7143
|
const project = useProject2();
|
|
7146
|
-
const credentials = await
|
|
7144
|
+
const credentials = await useAWSCredentials3();
|
|
7147
7145
|
const joined = args.command?.join(" ");
|
|
7148
7146
|
if (!joined)
|
|
7149
7147
|
throw new VisibleError("Command is required, e.g. sst bind env");
|