declapract-typescript-ehmpathy 0.47.67 → 0.47.69

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.
Files changed (43) hide show
  1. package/dist/practices/cicd-service/best-practice/.agent/repo=.this/skills/use.rds.capacity.sh +4 -4
  2. package/dist/practices/cicd-service/best-practice/.github/workflows/.deploy-sls.yml +4 -4
  3. package/dist/practices/cicd-service/best-practice/.github/workflows/.sql-schema-control.yml +4 -4
  4. package/dist/practices/cicd-service/best-practice/.github/workflows/provision.yml +1 -1
  5. package/dist/practices/commands/best-practice/src/contract/commands/sayHello.ts +1 -1
  6. package/dist/practices/config/bad-practices/old-config-with-paramstore-dep/package.json +5 -0
  7. package/dist/practices/config/bad-practices/old-config-with-paramstore-dep/package.json.declapract.ts +18 -0
  8. package/dist/practices/config/bad-practices/old-config-with-paramstore-dts/nontyped_modules/config-with-paramstore.d.ts.declapract.ts +8 -0
  9. package/dist/practices/config/bad-practices/old-dev-config-location/config/dev.json.declapract.ts +18 -0
  10. package/dist/practices/config/bad-practices/old-parameterstorenamespace-key/config/prep.json +3 -0
  11. package/dist/practices/config/bad-practices/old-parameterstorenamespace-key/config/prep.json.declapract.ts +12 -0
  12. package/dist/practices/config/bad-practices/old-parameterstorenamespace-key/config/prod.json +3 -0
  13. package/dist/practices/config/bad-practices/old-parameterstorenamespace-key/config/prod.json.declapract.ts +12 -0
  14. package/dist/practices/config/best-practice/config/{dev.json → prep.json} +1 -2
  15. package/dist/practices/config/best-practice/config/prod.json +0 -1
  16. package/dist/practices/config/best-practice/config/test.json +0 -1
  17. package/dist/practices/config/best-practice/package.json +3 -1
  18. package/dist/practices/config/best-practice/src/utils/config/Config.ts +1 -1
  19. package/dist/practices/config/best-practice/src/utils/config/config.schema.ts +15 -0
  20. package/dist/practices/config/best-practice/src/utils/config/getConfig.ts +17 -7
  21. package/dist/practices/environments/bad-practices/old-stage-enum/src/<star><star>/<star>.ts.declapract.ts +37 -0
  22. package/dist/practices/environments/best-practice/package.json +1 -3
  23. package/dist/practices/environments/best-practice/src/utils/environment.ts +5 -163
  24. package/dist/practices/persist-with-dynamodb/best-practice/jest.integration.env.ts +3 -1
  25. package/dist/practices/persist-with-rds/bad-practices/old-dev-config-location/config/dev.json.declapract.ts +19 -0
  26. package/dist/practices/persist-with-rds/bad-practices/old-param-placeholder/config/<star>.json.declapract.ts +14 -0
  27. package/dist/practices/persist-with-rds/best-practice/config/{dev.json → prep.json} +4 -4
  28. package/dist/practices/persist-with-rds/best-practice/config/prod.json +2 -2
  29. package/dist/practices/persist-with-rds/best-practice/package.json +1 -1
  30. package/dist/practices/persist-with-rds/best-practice/provision/schema/connection.config.js +7 -6
  31. package/dist/practices/persist-with-rds/best-practice/src/utils/config/config.schema.ts +43 -0
  32. package/dist/practices/persist-with-rds/best-practice/src/utils/config/config.schema.ts.declapract.ts +47 -0
  33. package/dist/practices/persist-with-rds/best-practice/src/utils/database/getDatabaseConnection.ts +1 -1
  34. package/dist/practices/serverless/best-practice/package.json +5 -5
  35. package/dist/practices/serverless/best-practice/serverless.yml +14 -4
  36. package/dist/practices/serverless/best-practice/serverless.yml.declapract.ts +57 -7
  37. package/dist/practices/tests/best-practice/jest.integration.env.ts +35 -24
  38. package/dist/practices/tests/best-practice/jest.unit.env.ts +7 -8
  39. package/dist/practices/tests/best-practice/package.json +4 -4
  40. package/package.json +9 -9
  41. package/dist/practices/config/best-practice/nontyped_modules/config-with-paramstore.d.ts +0 -1
  42. /package/dist/practices/config/best-practice/config/{dev.json.declapract.ts → prep.json.declapract.ts} +0 -0
  43. /package/dist/practices/persist-with-rds/best-practice/config/{dev.json.declapract.ts → prep.json.declapract.ts} +0 -0
@@ -15,18 +15,18 @@
15
15
  # - Any time you need to ensure the database is ready before proceeding
16
16
  #
17
17
  # Usage:
18
- # STAGE=dev ./.agent/repo=.this/skills/use.rds.capacity.sh
18
+ # ACCESS=prep ./.agent/repo=.this/skills/use.rds.capacity.sh
19
19
  #
20
20
  # Prerequisites:
21
- # - STAGE environment variable must be set
21
+ # - ACCESS environment variable must be set
22
22
  # - AWS credentials configured with SSM access
23
23
  # - sudo access (for /etc/hosts modification via vpc tunnel)
24
24
  # - pg_isready command available (postgresql-client)
25
25
  #
26
26
  set -eo pipefail
27
27
 
28
- # failfast if STAGE is not declared
29
- [[ -z "${STAGE:-}" ]] && echo "STAGE is not set" && exit 1
28
+ # failfast if ACCESS is not declared
29
+ [[ -z "${ACCESS:-}" ]] && echo "ACCESS is not set" && exit 1
30
30
 
31
31
  set -u
32
32
 
@@ -63,7 +63,7 @@ jobs:
63
63
  fail-on-cache-miss: true
64
64
 
65
65
  - name: deploy
66
- run: STAGE=${{ inputs.stage }} DEPLOYER_NAME=$GITHUB_ACTOR npm run deploy
66
+ run: ACCESS=${{ inputs.stage }} DEPLOYER_NAME=$GITHUB_ACTOR npm run deploy
67
67
 
68
68
  assure:
69
69
  runs-on: ubuntu-24.04
@@ -92,10 +92,10 @@ jobs:
92
92
 
93
93
  - name: vpc:tunnel:open
94
94
  if: inputs.needs-vpn-for-acceptance
95
- run: STAGE=${{ inputs.stage }} .agent/repo=.this/skills/use.vpc.tunnel.ts
95
+ run: ACCESS=${{ inputs.stage }} .agent/repo=.this/skills/use.vpc.tunnel.ts
96
96
 
97
97
  - name: test:acceptance
98
- run: STAGE=${{ inputs.stage }} npm run test:acceptance
98
+ run: ACCESS=${{ inputs.stage }} npm run test:acceptance
99
99
 
100
100
  - name: alarm on failure
101
101
  env:
@@ -132,4 +132,4 @@ jobs:
132
132
  fail-on-cache-miss: true
133
133
 
134
134
  - name: prune
135
- run: STAGE=${{ inputs.stage }} DEPLOYER_NAME=$GITHUB_ACTOR npm run deploy:prune
135
+ run: ACCESS=${{ inputs.stage }} DEPLOYER_NAME=$GITHUB_ACTOR npm run deploy:prune
@@ -60,10 +60,10 @@ jobs:
60
60
  aws-region: ${{ inputs.creds-aws-region }}
61
61
 
62
62
  - name: vpc:tunnel:open
63
- run: STAGE=${{ inputs.stage }} .agent/repo=.this/skills/use.vpc.tunnel.ts
63
+ run: ACCESS=${{ inputs.stage }} .agent/repo=.this/skills/use.vpc.tunnel.ts
64
64
 
65
65
  - name: plan
66
- run: STAGE=${{ inputs.stage }} npm run provision:schema:plan | tee ./plan.log
66
+ run: ACCESS=${{ inputs.stage }} npm run provision:schema:plan | tee ./plan.log
67
67
 
68
68
  - name: evaluate plan
69
69
  id: evaluate-plan
@@ -115,7 +115,7 @@ jobs:
115
115
  aws-region: ${{ inputs.creds-aws-region }}
116
116
 
117
117
  - name: vpc:tunnel:open
118
- run: STAGE=${{ inputs.stage }} .agent/repo=.this/skills/use.vpc.tunnel.ts
118
+ run: ACCESS=${{ inputs.stage }} .agent/repo=.this/skills/use.vpc.tunnel.ts
119
119
 
120
120
  - name: apply
121
- run: STAGE=${{ inputs.stage }} npm run provision:schema:apply
121
+ run: ACCESS=${{ inputs.stage }} npm run provision:schema:apply
@@ -53,7 +53,7 @@ jobs:
53
53
  sql-schema-dev:
54
54
  uses: ./.github/workflows/.sql-schema-control.yml
55
55
  with:
56
- stage: dev
56
+ stage: prep
57
57
  github-environment: dev
58
58
  creds-aws-region: us-east-1
59
59
  creds-aws-role-arn: ${{ vars.CREDS_CICD_AWS_DEV_OIDC_ROLE_ARN }}
@@ -15,5 +15,5 @@ const command = asCommand(
15
15
  async () => console.log('hello world'),
16
16
  );
17
17
 
18
- // STAGE=prod npx tsx src/contract/commands/sayHello.ts
18
+ // ACCESS=prod npx tsx src/contract/commands/sayHello.ts
19
19
  if (require.main === module) void command({});
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "config-with-paramstore": "@declapract{check.minVersion('0.0.0')}"
4
+ }
5
+ }
@@ -0,0 +1,18 @@
1
+ import { FileCheckType, type FileFixFunction } from 'declapract';
2
+
3
+ export const check = FileCheckType.CONTAINS;
4
+
5
+ export const fix: FileFixFunction = (contents) => {
6
+ if (!contents) return { contents };
7
+ const packageJSON = JSON.parse(contents);
8
+ const updatedPackageJSON = {
9
+ ...packageJSON,
10
+ dependencies: {
11
+ ...packageJSON.dependencies,
12
+ 'config-with-paramstore': undefined,
13
+ },
14
+ };
15
+ return {
16
+ contents: JSON.stringify(updatedPackageJSON, null, 2),
17
+ };
18
+ };
@@ -0,0 +1,8 @@
1
+ import { FileCheckType, type FileFixFunction } from 'declapract';
2
+
3
+ export const check = FileCheckType.EXISTS;
4
+
5
+ export const fix: FileFixFunction = () => {
6
+ // delete the file - sdk-config replaces config-with-paramstore
7
+ return { contents: null };
8
+ };
@@ -0,0 +1,18 @@
1
+ import { FileCheckType, type FileFixFunction } from 'declapract';
2
+
3
+ export const check = FileCheckType.EXISTS;
4
+
5
+ export const fix: FileFixFunction = (contents, context) => {
6
+ // move config/dev.json → config/prep.json and update access: dev → prep
7
+ let fixed = contents;
8
+ if (fixed) {
9
+ fixed = fixed.replace(/"access":\s*"dev"/, '"access": "prep"');
10
+ }
11
+ return {
12
+ contents: fixed ?? null,
13
+ relativeFilePath: context.relativeFilePath.replace(
14
+ /^config\/dev\.json$/,
15
+ 'config/prep.json',
16
+ ),
17
+ };
18
+ };
@@ -0,0 +1,3 @@
1
+ {
2
+ "parameterStoreNamespace": "@declapract{check.minVersion('0.0.0')}"
3
+ }
@@ -0,0 +1,12 @@
1
+ import { FileCheckType, type FileFixFunction } from 'declapract';
2
+
3
+ export const check = FileCheckType.CONTAINS;
4
+
5
+ export const fix: FileFixFunction = (contents) => {
6
+ if (!contents) return { contents };
7
+ const config = JSON.parse(contents);
8
+ const { parameterStoreNamespace: _, ...rest } = config;
9
+ return {
10
+ contents: JSON.stringify(rest, null, 2),
11
+ };
12
+ };
@@ -0,0 +1,3 @@
1
+ {
2
+ "parameterStoreNamespace": "@declapract{check.minVersion('0.0.0')}"
3
+ }
@@ -0,0 +1,12 @@
1
+ import { FileCheckType, type FileFixFunction } from 'declapract';
2
+
3
+ export const check = FileCheckType.CONTAINS;
4
+
5
+ export const fix: FileFixFunction = (contents) => {
6
+ if (!contents) return { contents };
7
+ const config = JSON.parse(contents);
8
+ const { parameterStoreNamespace: _, ...rest } = config;
9
+ return {
10
+ contents: JSON.stringify(rest, null, 2),
11
+ };
12
+ };
@@ -1,9 +1,8 @@
1
1
  {
2
- "parameterStoreNamespace": "@declapract{variable.organizationName}.@declapract{variable.projectName}.dev",
3
2
  "organization": "@declapract{variable.organizationName}",
4
3
  "project": "@declapract{variable.projectName}",
5
4
  "environment": {
6
- "access": "dev"
5
+ "access": "prep"
7
6
  },
8
7
  "aws": {
9
8
  "account": "@declapract{variable.awsAccountId.dev}",
@@ -1,5 +1,4 @@
1
1
  {
2
- "parameterStoreNamespace": "@declapract{variable.organizationName}.@declapract{variable.projectName}.prod",
3
2
  "organization": "@declapract{variable.organizationName}",
4
3
  "project": "@declapract{variable.projectName}",
5
4
  "environment": {
@@ -1,5 +1,4 @@
1
1
  {
2
- "parameterStoreNamespace": "@declapract{variable.organizationName}.@declapract{variable.projectName}.test",
3
2
  "organization": "@declapract{variable.organizationName}",
4
3
  "project": "@declapract{variable.projectName}",
5
4
  "environment": {
@@ -1,5 +1,7 @@
1
1
  {
2
2
  "dependencies": {
3
- "config-with-paramstore": "@declapract{check.minVersion('1.1.1')}"
3
+ "sdk-config": "@declapract{check.minVersion('0.1.1')}",
4
+ "simple-in-memory-cache": "@declapract{check.minVersion('0.4.0')}",
5
+ "zod": "@declapract{check.minVersion('4.0.0')}"
4
6
  }
5
7
  }
@@ -2,7 +2,7 @@ export interface Config {
2
2
  organization: string;
3
3
  project: string;
4
4
  environment: {
5
- access: 'test' | 'dev' | 'prod';
5
+ access: 'test' | 'prep' | 'prod';
6
6
  };
7
7
  aws: {
8
8
  account: string;
@@ -0,0 +1,15 @@
1
+ import { z } from 'zod';
2
+
3
+ export const schema = z.object({
4
+ organization: z.string(),
5
+ project: z.string(),
6
+ environment: z.object({
7
+ access: z.enum(['test', 'prep', 'prod']),
8
+ }),
9
+ aws: z.object({
10
+ account: z.string(),
11
+ namespace: z.string(),
12
+ }),
13
+ });
14
+
15
+ export type Config = z.infer<typeof schema>;
@@ -1,9 +1,19 @@
1
- // export a default instance of the config object
2
- import ConfigCache from 'config-with-paramstore';
1
+ import {
2
+ genGetConfig,
3
+ genSdkConfigSupplierAwsParameterStore,
4
+ } from 'sdk-config';
5
+ import { createCache } from 'simple-in-memory-cache';
3
6
 
4
- import { getEnvironment } from '../environment';
5
- import type { Config } from './Config';
7
+ import { envStatic } from '../environment';
8
+ import { schema } from './config.schema';
6
9
 
7
- export const configInstance = new ConfigCache();
8
- export const getConfig = async (): Promise<Config> =>
9
- configInstance.get((await getEnvironment()).config);
10
+ export const getConfig = genGetConfig({
11
+ schema,
12
+ statics: 'config/*.json',
13
+ cache: createCache({ expiration: { minutes: 5 } }),
14
+ suppliers: [genSdkConfigSupplierAwsParameterStore()],
15
+ environment: {
16
+ config: envStatic.config,
17
+ server: envStatic.server,
18
+ },
19
+ });
@@ -0,0 +1,37 @@
1
+ import type { FileCheckFunction, FileFixFunction } from 'declapract';
2
+
3
+ export const check: FileCheckFunction = (contents) => {
4
+ if (!contents) throw new Error('does not match bad practice');
5
+
6
+ // check for Stage enum usage
7
+ if (
8
+ contents.includes('Stage.PRODUCTION') ||
9
+ contents.includes('Stage.DEVELOPMENT') ||
10
+ contents.includes('Stage.TEST')
11
+ )
12
+ return; // bad practice detected
13
+
14
+ throw new Error('does not match bad practice');
15
+ };
16
+
17
+ export const fix: FileFixFunction = (contents) => {
18
+ if (!contents) return { contents };
19
+
20
+ let fixed = contents
21
+ // replace enum values with string literals
22
+ .replace(/Stage\.PRODUCTION/g, "'prod'")
23
+ .replace(/Stage\.DEVELOPMENT/g, "'prep'")
24
+ .replace(/Stage\.TEST/g, "'test'")
25
+ // remove Stage import if no longer used
26
+ .replace(/import\s*\{\s*Stage\s*\}\s*from\s*['"][^'"]+['"];\n?/g, '')
27
+ .replace(
28
+ /import\s*\{\s*([^}]*),\s*Stage\s*\}\s*from/g,
29
+ 'import { $1 } from',
30
+ )
31
+ .replace(
32
+ /import\s*\{\s*Stage\s*,\s*([^}]*)\}\s*from/g,
33
+ 'import { $1 } from',
34
+ );
35
+
36
+ return { contents: fixed };
37
+ };
@@ -1,7 +1,5 @@
1
1
  {
2
2
  "dependencies": {
3
- "@aws-sdk/client-iam": "@declapract{check.minVersion('3.940.0')}",
4
- "with-simple-cache": "@declapract{check.minVersion('0.15.1')}",
5
- "simple-in-memory-cache": "@declapract{check.minVersion('0.4.0')}"
3
+ "sdk-environment": "@declapract{check.minVersion('0.1.3')}"
6
4
  }
7
5
  }
@@ -1,166 +1,8 @@
1
- import {
2
- IAMClient,
3
- ListAccountAliasesCommand,
4
- type ListAccountAliasesCommandOutput,
5
- } from '@aws-sdk/client-iam';
6
- import { UnexpectedCodePathError } from 'helpful-errors';
7
- import { createCache } from 'simple-in-memory-cache';
8
- import { createIsOfEnum, type Literalize } from 'type-fns';
9
- import { withSimpleCache } from 'with-simple-cache';
1
+ import { getEnvironment } from 'sdk-environment';
10
2
 
11
- import { log } from './logger';
3
+ export { getEnvironment };
12
4
 
13
- export enum ConfigChoice {
14
- PRODUCTION = 'prod',
15
- DEVELOPMENT = 'dev',
16
- TEST = 'test',
17
- }
18
- export const isOfConfigChoice = createIsOfEnum(ConfigChoice);
5
+ export const envStatic = getEnvironment.static();
19
6
 
20
- export enum Access {
21
- PRODUCTION = 'prod',
22
- DEVELOPMENT = 'dev',
23
- }
24
- export const isOfAccess = createIsOfEnum(Access);
25
-
26
- export enum Stage { // todo: deprecate stage
27
- PRODUCTION = 'prod',
28
- DEVELOPMENT = 'dev',
29
- TEST = 'test',
30
- }
31
- export const isOfStage = createIsOfEnum(Stage);
32
-
33
- /**
34
- * ensure that the server is on UTC timezone
35
- *
36
- * why?
37
- * - non UTC timezone usage causes consistency problems and takes a while to track down
38
- * - by warning and correcting if the server the code runs on in is not in UTC, we avoid these issues
39
- * - instead, users should push down converting from UTC into the users TZ as far as possible
40
- */
41
- const TIMEZONE = process.env.TZ;
42
- if (TIMEZONE !== 'UTC') {
43
- log.debug(
44
- 'env.TZ is not set to UTC. this can cause issues. updating this on your behalf',
45
- { found: TIMEZONE, desire: 'UTC' },
46
- );
47
- process.env.TZ = 'UTC';
48
- }
49
-
50
- /**
51
- * this allows us to infer what the stage should be in environments that do not have STAGE specified
52
- * - e.g., when running locally
53
- * - e.g., when running tests
54
- */
55
- const inferStageFromNodeEnv = () => {
56
- const nodeEnv = process.env.NODE_ENV; // default to test if not defined
57
- if (!nodeEnv) throw new Error('process.env.NODE_ENV must be defined');
58
- if (nodeEnv === 'production') return Stage.PRODUCTION;
59
- if (nodeEnv === 'development') return Stage.DEVELOPMENT;
60
- if (nodeEnv === 'test') return Stage.TEST;
61
- throw new Error(`unexpected nodeEnv '${nodeEnv}'`);
62
- };
63
-
64
- /**
65
- * a method that exposes relevant environmental variables in a standard way
66
- */
67
- export const getStage = (): Stage => {
68
- const stage = process.env.STAGE ?? inferStageFromNodeEnv(); // figure it out from NODE_ENV if not explicitly defined
69
- if (!stage) throw new Error('process.env.STAGE must be defined');
70
- if (!isOfStage(stage)) throw new Error(`invalid stage defined '${stage}'`);
71
- return stage;
72
- };
73
- export const stage: Stage = getStage(); // todo: deprecate
74
-
75
- // export service client stage // todo: deprecate
76
- export const serviceClientStage =
77
- stage === Stage.PRODUCTION ? Stage.PRODUCTION : Stage.DEVELOPMENT; // i.e., if its prod, hit prod. otherwise, dev
78
-
79
- /**
80
- * .what = infer access (prod/dev) from curernt credentials
81
- *
82
- * .why?
83
- * - allows detection of the access grain we are currently authenticated into, if authenticated
84
- * - account aliases typically contain the environment (e.g., 'ahbode-dev', 'ahbode-prod')
85
- */
86
- export const inferAccess = withSimpleCache(
87
- async (): Promise<Access | null> => {
88
- // skip aws api calls if no credentials are configured
89
- if (!process.env.AWS_PROFILE && !process.env.AWS_ACCESS_KEY_ID) return null;
90
-
91
- // grab the alias of the current account
92
- const iam = new IAMClient({});
93
- const response: ListAccountAliasesCommandOutput = await iam.send(
94
- new ListAccountAliasesCommand({}),
95
- );
96
-
97
- // infer access from alias
98
- const alias = response.AccountAliases?.[0];
99
- if (alias?.includes('prod')) return Access.PRODUCTION;
100
- if (alias?.includes('dev')) return Access.DEVELOPMENT;
101
- throw new Error(`Could not infer access from account alias '${alias}'`);
102
- },
103
- { cache: createCache() },
104
- );
105
-
106
- /**
107
- * infer stage from access (prod/dev)
108
- *
109
- * logic:
110
- * - if NODE_ENV === 'test', return test (test environment always uses test config)
111
- * - otherwise, defer to access from AWS credentials
112
- */
113
- const inferConfigChoice = async (): Promise<ConfigChoice> => {
114
- // grab some facts
115
- const access = await inferAccess();
116
- const envarNode = process.env.NODE_ENV;
117
- const envarConfig = process.env.CONFIG ?? process.env.STAGE; // fallback to stage as alias to config choice
118
-
119
- // if access is against prod, then must use prod config; no exceptions
120
- if (access === Access.PRODUCTION) return ConfigChoice.PRODUCTION;
121
-
122
- // if access is against prep and asked for dev, then use dev config
123
- if (access === Access.DEVELOPMENT && envarConfig === 'dev')
124
- return ConfigChoice.DEVELOPMENT;
125
-
126
- // if access is against prep and asked for test, then use test config
127
- if (access === Access.DEVELOPMENT && envarNode === 'test')
128
- return ConfigChoice.TEST;
129
-
130
- // if access is against prep and not asked for test, then must use dev config
131
- if (access === Access.DEVELOPMENT) return ConfigChoice.DEVELOPMENT;
132
-
133
- // otherwise, unsupported
134
- throw new UnexpectedCodePathError(
135
- 'Could not infer config choice: NODE_ENV is not test and no valid access could be determined',
136
- { access, envarNode, envarConfig },
137
- );
138
- };
139
-
140
- // export the v3 environmental variables
141
- export interface Environment {
142
- /**
143
- * .what = the choice of config to execute against
144
- */
145
- config: Literalize<ConfigChoice>;
146
-
147
- /**
148
- * .what = the resources accessible from this environment, if any
149
- */
150
- access: Literalize<Access> | null;
151
-
152
- /**
153
- * .what = the server that hosts this environment
154
- */
155
- server: 'CICD' | 'AWS:LAMBDA' | 'LOCAL';
156
- }
157
- export const getEnvironment = async (): Promise<Environment> => ({
158
- config: await inferConfigChoice(),
159
- access: await inferAccess(),
160
- server: (() => {
161
- if (process.env.CI) return 'CICD' as const;
162
- if (process.env.LAMBDA_TASK_ROOT) return 'AWS:LAMBDA' as const;
163
- return 'LOCAL' as const; // default to local
164
- })(),
165
- // region: // todo
166
- });
7
+ export const stage = envStatic.access === 'prep' ? 'dev' : envStatic.access;
8
+ export const serviceClientStage = stage === 'prod' ? 'prod' : 'dev';
@@ -1,5 +1,7 @@
1
+ import { stage } from './src/utils/environment';
2
+
1
3
  /**
2
4
  * specify that dynamodb should use the local dynamodb database, if running in test env
3
5
  */
4
- if (stage === Stage.TEST)
6
+ if (stage === 'test')
5
7
  process.env.USE_CUSTOM_DYNAMODB_ENDPOINT = 'http://localhost:7337';
@@ -0,0 +1,19 @@
1
+ import { FileCheckType, type FileFixFunction } from 'declapract';
2
+
3
+ export const check = FileCheckType.EXISTS;
4
+
5
+ export const fix: FileFixFunction = (contents, context) => {
6
+ // move config/dev.json → config/prep.json and update .dev → .prep references
7
+ const updatedContents = contents
8
+ ?.replace(/\.dev\b/g, '.prep')
9
+ .replace(/"access":\s*"dev"/, '"access": "prep"')
10
+ .replace(/"__CHANG3_ME__"/g, '"$.at(aws::param)"');
11
+
12
+ return {
13
+ contents: updatedContents ?? null,
14
+ relativeFilePath: context.relativeFilePath.replace(
15
+ /^config\/dev\.json$/,
16
+ 'config/prep.json',
17
+ ),
18
+ };
19
+ };
@@ -0,0 +1,14 @@
1
+ import type { FileCheckFunction, FileFixFunction } from 'declapract';
2
+
3
+ export const check: FileCheckFunction = (contents) => {
4
+ if (contents?.includes('__PARAM__')) return; // bad practice detected
5
+ throw new Error('does not match bad practice');
6
+ };
7
+
8
+ export const fix: FileFixFunction = (contents) => {
9
+ if (!contents) return { contents };
10
+
11
+ return {
12
+ contents: contents.replace(/"__PARAM__"/g, '"$.at(aws::param)"'),
13
+ };
14
+ };
@@ -7,20 +7,20 @@
7
7
  "role": {
8
8
  "cicd": {
9
9
  "username": "@declapract{variable.databaseUserName.cicdUser}",
10
- "password": "__CHANG3_ME__"
10
+ "password": "$.at(aws::param)"
11
11
  },
12
12
  "crud": {
13
13
  "username": "@declapract{variable.databaseUserName.serviceUser}",
14
- "password": "__CHANG3_ME__"
14
+ "password": "$.at(aws::param)"
15
15
  }
16
16
  },
17
17
  "tunnel": {
18
18
  "local": {
19
- "host": "@declapract{variable.databaseTunnelHost.dev}",
19
+ "host": "@declapract{variable.databaseTunnelHost.prep}",
20
20
  "port": 15432
21
21
  },
22
22
  "lambda": {
23
- "host": "@declapract{variable.databaseClusterHost.dev}",
23
+ "host": "@declapract{variable.databaseClusterHost.prep}",
24
24
  "port": 5432
25
25
  }
26
26
  }
@@ -7,11 +7,11 @@
7
7
  "role": {
8
8
  "cicd": {
9
9
  "username": "@declapract{variable.databaseUserName.cicdUser}",
10
- "password": "__PARAM__"
10
+ "password": "$.at(aws::param)"
11
11
  },
12
12
  "crud": {
13
13
  "username": "@declapract{variable.databaseUserName.serviceUser}",
14
- "password": "__PARAM__"
14
+ "password": "$.at(aws::param)"
15
15
  }
16
16
  },
17
17
  "tunnel": {
@@ -25,6 +25,6 @@
25
25
  "provision:testdb:docker:down": "docker compose -f ./provision/docker/testdb/docker-compose.yml down",
26
26
  "provision:testdb": "npm run provision:testdb:docker:clear && npm run provision:testdb:docker:prepare && npm run provision:testdb:docker:up && npm run provision:testdb:docker:await && npm run provision:schema:plan && npm run provision:schema:apply && npm run provision:schema:plan",
27
27
  "start:testdb": "npm run provision:testdb",
28
- "start:livedb:dev": "echo 'will ping the database until assured its not asleep' && STAGE=dev .agent/repo=.this/skills/use.rds.capacity.sh"
28
+ "start:livedb:prep": "echo 'will ping the database until assured its not asleep' && ACCESS=prep .agent/repo=.this/skills/use.rds.capacity.sh"
29
29
  }
30
30
  }
@@ -1,15 +1,16 @@
1
- const Config = require('config-with-paramstore').default;
2
-
3
- const configInstance = new Config();
4
- const getConfig = async () =>
5
- configInstance.get(process.env.STAGE || undefined);
1
+ /**
2
+ * .what = database credentials for sql-schema-control
3
+ * .why = reuses app's getConfig for consistent config resolution
4
+ */
5
+ require('esbuild-register');
6
+ const { getConfig } = require('../../src/utils/config/getConfig');
6
7
 
7
8
  const promiseSchemaControlCredentials = async () => {
8
9
  const config = await getConfig();
9
10
  const credentials = {
10
11
  host: config.database.tunnel.local.host,
11
12
  port: config.database.tunnel.local.port,
12
- database: config.database.target.database, // i.e., db = schema
13
+ database: config.database.target.database,
13
14
  schema: config.database.target.schema,
14
15
  username: config.database.role.cicd.username,
15
16
  password: config.database.role.cicd.password,
@@ -0,0 +1,43 @@
1
+ import { z } from 'zod';
2
+
3
+ export const schema = z.object({
4
+ organization: z.string(),
5
+ project: z.string(),
6
+ environment: z.object({
7
+ access: z.enum(['test', 'prep', 'prod']),
8
+ }),
9
+ aws: z.object({
10
+ account: z.string(),
11
+ namespace: z.string(),
12
+ }),
13
+ database: z.object({
14
+ target: z.object({
15
+ database: z.string(),
16
+ schema: z.string(),
17
+ }),
18
+ role: z.object({
19
+ cicd: z.object({
20
+ username: z.string(),
21
+ password: z.string(),
22
+ }),
23
+ crud: z.object({
24
+ username: z.string(),
25
+ password: z.string(),
26
+ }),
27
+ }),
28
+ tunnel: z.object({
29
+ local: z.object({
30
+ host: z.string(),
31
+ port: z.number(),
32
+ }),
33
+ lambda: z
34
+ .object({
35
+ host: z.string(),
36
+ port: z.number(),
37
+ })
38
+ .nullable(),
39
+ }),
40
+ }),
41
+ });
42
+
43
+ export type Config = z.infer<typeof schema>;
@@ -0,0 +1,47 @@
1
+ import { FileCheckType, type FileFixFunction } from 'declapract';
2
+
3
+ export const check = FileCheckType.CONTAINS;
4
+
5
+ const databaseSchema = ` database: z.object({
6
+ target: z.object({
7
+ database: z.string(),
8
+ schema: z.string(),
9
+ }),
10
+ role: z.object({
11
+ cicd: z.object({
12
+ username: z.string(),
13
+ password: z.string(),
14
+ }),
15
+ crud: z.object({
16
+ username: z.string(),
17
+ password: z.string(),
18
+ }),
19
+ }),
20
+ tunnel: z.object({
21
+ local: z.object({
22
+ host: z.string(),
23
+ port: z.number(),
24
+ }),
25
+ lambda: z
26
+ .object({
27
+ host: z.string(),
28
+ port: z.number(),
29
+ })
30
+ .nullable(),
31
+ }),
32
+ }),`;
33
+
34
+ export const fix: FileFixFunction = (contents) => {
35
+ if (!contents) return { contents };
36
+
37
+ // if database schema already present, return as-is
38
+ if (contents.includes('database: z.object(')) return { contents };
39
+
40
+ // add database schema before the close of the main object
41
+ const fixed = contents.replace(
42
+ /(\s*)\}\);(\s*\n\s*export type Config)/,
43
+ `$1${databaseSchema}\n});$2`,
44
+ );
45
+
46
+ return { contents: fixed };
47
+ };
@@ -47,7 +47,7 @@ export const getDatabaseConnection = async (): Promise<DatabaseConnection> => {
47
47
 
48
48
  // determine which tunnel to use based on environment.server
49
49
  const tunnel =
50
- environment.server === 'AWS:LAMBDA'
50
+ environment.server === 'cloud@aws.lambda'
51
51
  ? config.database.tunnel.lambda
52
52
  : config.database.tunnel.local;
53
53
 
@@ -5,11 +5,11 @@
5
5
  "if-env": "@declapract{check.minVersion('1.0.4')}"
6
6
  },
7
7
  "scripts": {
8
- "deploy:prune": "npx sls prune -n 7 --stage $STAGE",
9
- "deploy:release": "npm run build && sls deploy --verbose --stage $STAGE",
8
+ "deploy:prune": "SLS_STAGE=$(if [ \"$ACCESS\" = 'prep' ]; then echo 'dev'; else echo \"$ACCESS\"; fi) && export COMMIT=$(npx sdk-environment get commit) && npx sls prune -n 7 --stage $SLS_STAGE",
9
+ "deploy:release": "SLS_STAGE=$(if [ \"$ACCESS\" = 'prep' ]; then echo 'dev'; else echo \"$ACCESS\"; fi) && export COMMIT=$(npx sdk-environment get commit) && npm run build && sls deploy --verbose --stage $SLS_STAGE",
10
10
  "deploy:send-notification": "curl -X POST -H 'Content-type: application/json' --data \"{\\\"text\\\":\\\"$([ -z $DEPLOYER_NAME ] && git config user.name || echo $DEPLOYER_NAME) has deployed $npm_package_name@v$npm_package_version:\nhttps://github.com/@declapract{variable.organizationName}/$npm_package_name/tree/v$npm_package_version\\\"}\" @declapract{variable.slackWebhookUrl}",
11
- "deploy:dev": "STAGE=dev npm run deploy:release",
12
- "deploy:prod": "STAGE=prod npm run deploy:release && npm run deploy:send-notification",
13
- "deploy": "if-env STAGE=prod && npm run deploy:prod && exit 0 || if-env STAGE=dev && npm run deploy:dev && exit 0 || echo '🛑 invalid STAGE, must be prod or dev' && exit 1"
11
+ "deploy:prep": "ACCESS=prep npm run deploy:release",
12
+ "deploy:prod": "ACCESS=prod npm run deploy:release && npm run deploy:send-notification",
13
+ "deploy": "if-env ACCESS=prod && npm run deploy:prod && exit 0 || if-env ACCESS=prep && npm run deploy:prep && exit 0 || echo '🛑 invalid ACCESS, must be prod or prep' && exit 1"
14
14
  }
15
15
  }
@@ -1,11 +1,18 @@
1
1
  service: @declapract{variable.projectName}
2
2
 
3
+ variablesResolutionMode: 20210326
4
+
3
5
  package:
4
6
  artifact: .artifact/contents.zip
5
7
 
6
8
  plugins:
7
9
  - serverless-prune-plugin
8
10
 
11
+ custom:
12
+ accessByStage:
13
+ dev: prep
14
+ prod: prod
15
+
9
16
  provider:
10
17
  name: aws
11
18
  runtime: nodejs22.x
@@ -19,7 +26,8 @@ provider:
19
26
  environment:
20
27
  TZ: UTC # guarantee that utc timezone will be used explicitly, to facilitate a pit of success
21
28
  NODE_ENV: production # deploy with production optimizations of all resources, to make `dev` and `prod` stage deployments equivalent functionally (i.e., the same code paths in dev and prod)
22
- STAGE: ${self:provider.stage} # deploy specifying which stage we're targeting, to enable targeting the correct config + resources (e.g., hit dev db -vs- prod db)
29
+ ACCESS: ${self:custom.accessByStage.${opt:stage}, 'prep'} # sdk-environment access tier, to target the correct config + resources (e.g., hit dev db -vs- prod db)
30
+ COMMIT: ${env:COMMIT} # sdk-environment commit slug, must be set by deploy command
23
31
  AWS_NODEJS_CONNECTION_REUSE_ENABLED: true # https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/node-reusing-connections.html
24
32
  deploymentBucket: serverless-deployment-@declapract{variable.infrastructureNamespaceId}-${self:provider.stage}
25
33
  vpc:
@@ -35,7 +43,9 @@ provider:
35
43
  Action: 'ssm:DescribeParameters'
36
44
  Resource: '*'
37
45
  - Effect: 'Allow'
38
- Action: 'ssm:GetParameters'
46
+ Action:
47
+ - ssm:GetParameter
48
+ - ssm:GetParameters
39
49
  Resource: arn:aws:ssm:${aws:region}:${aws:accountId}:parameter/*
40
50
  - Effect: 'Allow'
41
51
  Action: 'kms:Decrypt'
@@ -110,8 +120,8 @@ provider:
110
120
  - athena:GetQueryExecution
111
121
  - athena:GetQueryResults
112
122
  Resource: '*'
113
- # allow inferring access from account alias
123
+ # allow access inference from account name
114
124
  - Effect: Allow
115
125
  Action:
116
- - iam:ListAccountAliases
126
+ - account:GetAccountInformation
117
127
  Resource: '*'
@@ -3,16 +3,23 @@ import { FileCheckType } from 'declapract';
3
3
 
4
4
  export const check = FileCheckType.CONTAINS; // i.e., check that the contents of the file contains what's declared (default is equals)
5
5
 
6
- const listAccountAliasesPolicy = ` # allow inferring access from account alias
6
+ const accessInferencePolicy = ` # allow access inference from account name
7
7
  - Effect: Allow
8
8
  Action:
9
- - iam:ListAccountAliases
9
+ - account:GetAccountInformation
10
10
  Resource: '*'`;
11
11
 
12
+ const accessByStageCustom = `custom:
13
+ accessByStage:
14
+ dev: prep
15
+ prod: prod
16
+
17
+ `;
18
+
12
19
  export const fix: FileFixFunction = (contents) => {
13
- if (!contents) return { contents }; // do nothing if file dne; // TODO: update to provision file from declared contents
20
+ if (!contents) return { contents }; // do nothing if file dne
14
21
  let fixed = contents
15
- .replace(/runtime: nodejs\d\d.x/, 'runtime: nodejs16.x')
22
+ .replace(/runtime: nodejs\d\d.x/, 'runtime: nodejs22.x')
16
23
  .replace(/ {2}- serverless-offline .*\n/, '') // a plugin we no longer use (never used it, no need to have it)
17
24
  .replace(/ {2}- serverless-pseudo-parameters .*\n/, '') // a plugin we no longer use (serverless supports variables natively now)
18
25
  .replace(/#\{AWS::Region\}/g, '${aws:region}') // use the serverless native variables, instead of the pseudo-parameters format
@@ -43,15 +50,58 @@ export const fix: FileFixFunction = (contents) => {
43
50
  ' timeout: 60 # default timeout to 1min, for resilience against increased cold start times; individual functions can override this', // bump the timeout
44
51
  );
45
52
 
46
- // Append ListAccountAliases policy if not already present
47
- if (!fixed.includes('iam:ListAccountAliases')) {
53
+ // add variablesResolutionMode after service line if not present
54
+ if (!fixed.includes('variablesResolutionMode:')) {
55
+ fixed = fixed.replace(
56
+ /^(service: [a-zA-Z0-9-]+)\n/m,
57
+ '$1\n\nvariablesResolutionMode: 20210326\n',
58
+ );
59
+ }
60
+
61
+ // add custom accessByStage block before provider if not present
62
+ if (!fixed.includes('accessByStage:')) {
63
+ fixed = fixed.replace(/\nprovider:/, `\n${accessByStageCustom}provider:`);
64
+ }
65
+
66
+ // replace STAGE env var with ACCESS + COMMIT pattern
67
+ if (
68
+ fixed.includes('STAGE: ${self:provider.stage}') &&
69
+ !fixed.includes('ACCESS:')
70
+ ) {
71
+ fixed = fixed.replace(
72
+ /STAGE: \$\{self:provider\.stage\}[^\n]*/,
73
+ "ACCESS: ${self:custom.accessByStage.${opt:stage}, 'prep'} # sdk-environment access tier, to target the correct config + resources (e.g., hit dev db -vs- prod db)\n COMMIT: ${env:COMMIT} # sdk-environment commit slug, must be set by deploy command",
74
+ );
75
+ }
76
+
77
+ // replace old iam:ListAccountAliases with account:GetAccountInformation
78
+ if (fixed.includes('iam:ListAccountAliases')) {
79
+ fixed = fixed.replace(
80
+ /# allow inferring access from account alias\n\s+- Effect: Allow\n\s+Action:\n\s+- iam:ListAccountAliases\n\s+Resource: '\*'/,
81
+ accessInferencePolicy.trim(),
82
+ );
83
+ }
84
+
85
+ // add account:GetAccountInformation policy if not present
86
+ if (!fixed.includes('account:GetAccountInformation')) {
48
87
  if (/iamRoleStatements:\s*\n/.test(fixed)) {
49
88
  fixed = fixed.replace(
50
89
  /iamRoleStatements:\s*\n/,
51
- `iamRoleStatements:\n${listAccountAliasesPolicy}\n`,
90
+ `iamRoleStatements:\n${accessInferencePolicy}\n`,
52
91
  );
53
92
  }
54
93
  }
55
94
 
95
+ // add ssm:GetParameter to SSM permissions if only GetParameters exists
96
+ if (
97
+ fixed.includes('ssm:GetParameters') &&
98
+ !fixed.includes('ssm:GetParameter\n')
99
+ ) {
100
+ fixed = fixed.replace(
101
+ /Action: 'ssm:GetParameters'/,
102
+ 'Action:\n - ssm:GetParameter\n - ssm:GetParameters',
103
+ );
104
+ }
105
+
56
106
  return { contents: fixed };
57
107
  };
@@ -27,29 +27,50 @@ if (!existsSync(join(process.cwd(), 'package.json')))
27
27
  */
28
28
  if (
29
29
  (process.env.NODE_ENV !== 'test' ||
30
- (process.env.STAGE && process.env.STAGE !== 'test')) &&
31
- process.env.I_KNOW_WHAT_IM_DOING !== 'true'
30
+ (process.env.CONFIG && process.env.CONFIG !== 'test')) &&
31
+ process.env.I_KNOW_THE_RISKS !== 'true'
32
32
  )
33
- throw new Error(`integration.test is not targeting stage 'test'`);
33
+ throw new Error(`integration.test config must be 'test'`);
34
34
 
35
35
  /**
36
- * .what = verify that the env has sufficient auth to run the tests if aws is used; otherwise, fail fast
37
- * .why =
38
- * - prevent time wasted waiting on tests to fail due to lack of credentials
39
- * - prevent time wasted debugging tests which are failing due to hard-to-read missed credential errors
36
+ * .what = source aws profile from keyrack if available
37
+ * .why = keyrack manages which profile to use per environment
38
+ */
39
+ const keyrackYmlPath = join(process.cwd(), '.agent/keyrack.yml');
40
+ if (existsSync(keyrackYmlPath) && !process.env.CI)
41
+ keyrack.source({ env: 'test', owner: 'ehmpath', mode: 'lenient' });
42
+
43
+ /**
44
+ * .what = export aws credentials from sso profile if aws is required
45
+ * .why = aws sdk v2 doesn't handle sso_session profiles natively
40
46
  */
41
47
  const declapractUsePath = join(process.cwd(), 'declapract.use.yml');
42
48
  const declapractUseContent = existsSync(declapractUsePath)
43
49
  ? readFileSync(declapractUsePath, 'utf8')
44
50
  : '';
45
51
  const requiresAwsAuth = declapractUseContent.includes('awsAccountId');
46
- if (
47
- requiresAwsAuth &&
48
- !(process.env.AWS_PROFILE || process.env.AWS_ACCESS_KEY_ID)
49
- )
50
- throw new Error(
51
- 'no aws credentials present. please authenticate with aws to run integration tests',
52
- );
52
+ if (requiresAwsAuth && !process.env.AWS_ACCESS_KEY_ID && !process.env.CI) {
53
+ const awsSsoProfile = process.env.AWS_PROFILE;
54
+ if (!awsSsoProfile)
55
+ throw new Error(
56
+ 'AWS_PROFILE not set. keyrack.source() should have set it.',
57
+ );
58
+ try {
59
+ const credOutput = execSync(
60
+ `aws configure export-credentials --profile ${awsSsoProfile} --format env`,
61
+ { encoding: 'utf8', timeout: 10000 },
62
+ );
63
+ // parse and set env vars from output like "export AWS_ACCESS_KEY_ID=..."
64
+ credOutput.split('\n').forEach((line) => {
65
+ const match = line.match(/^export\s+(\w+)=(.*)$/);
66
+ if (match) process.env[match[1]!] = match[2]!;
67
+ });
68
+ } catch {
69
+ throw new Error(
70
+ `failed to export aws credentials from sso profile '${awsSsoProfile}'. run: aws sso login --profile ${awsSsoProfile}`,
71
+ );
72
+ }
73
+ }
53
74
 
54
75
  /**
55
76
  * .what = verify that the testdb has been provisioned if a databaseUserName is declared
@@ -85,13 +106,3 @@ if (requiresTestDb) {
85
106
  );
86
107
  }
87
108
  }
88
-
89
- /**
90
- * .what = source credentials from keyrack for test env
91
- * .why =
92
- * - auto-inject keys into process.env
93
- * - fail fast with helpful error if keyrack locked or keys absent
94
- */
95
- const keyrackYmlPath = join(process.cwd(), '.agent/keyrack.yml');
96
- if (existsSync(keyrackYmlPath))
97
- keyrack.source({ env: 'test', owner: 'ehmpath', mode: 'strict' });
@@ -4,6 +4,8 @@ import util from 'node:util';
4
4
 
5
5
  import { jest } from '@jest/globals';
6
6
 
7
+ import { stage } from './src/utils/environment';
8
+
7
9
  // mock that getConfig just returns plaintext test env config in unit tests
8
10
  jest.mock('./src/utils/config/getConfig', () => ({
9
11
  getConfig: jest.fn().mockImplementation(() => require('./config/test.json')),
@@ -13,7 +15,7 @@ jest.mock('./src/utils/config/getConfig', () => ({
13
15
  util.inspect.defaultOptions.depth = 5;
14
16
 
15
17
  /**
16
- * .what = verify that we're running from a valid project directory; otherwise, fail fast
18
+ * .what = verify that we're at a valid project directory; otherwise, fail fast
17
19
  * .why = prevent confusion and hard-to-debug errors from running tests in the wrong directory
18
20
  */
19
21
  if (!existsSync(join(process.cwd(), 'package.json')))
@@ -23,11 +25,8 @@ if (!existsSync(join(process.cwd(), 'package.json')))
23
25
  * sanity check that unit tests are only run the 'test' environment
24
26
  *
25
27
  * usecases
26
- * - prevent polluting prod state with test data
27
- * - prevent executing financially impacting mutations
28
+ * - prevent prod state pollution with test data
29
+ * - prevent financial mutations
28
30
  */
29
- if (
30
- (process.env.NODE_ENV !== 'test' || process.env.STAGE) &&
31
- process.env.I_KNOW_WHAT_IM_DOING !== 'true'
32
- )
33
- throw new Error(`unit.test is not targeting stage 'test'`);
31
+ if (stage !== 'test' && process.env.I_KNOW_THE_RISKS !== 'true')
32
+ throw new Error(`unit-test does not target stage 'test'`);
@@ -10,10 +10,10 @@
10
10
  "@swc/jest": "@declapract{check.minVersion('0.2.39')}"
11
11
  },
12
12
  "scripts": {
13
- "test:unit": "set -eu && jest -c ./jest.unit.config.ts --forceExit --verbose --passWithNoTests $([ -n \"${CI:-}\" ] && echo '--ci') $([ -z \"${THOROUGH:-}\" ] && echo '--changedSince=main') $([ -n \"${RESNAP:-}\" ] && echo '--updateSnapshot')",
14
- "test:integration": "set -eu && jest -c ./jest.integration.config.ts --forceExit --verbose --passWithNoTests $([ -n \"${CI:-}\" ] && echo '--ci') $([ -z \"${THOROUGH:-}\" ] && echo '--changedSince=main') $([ -n \"${RESNAP:-}\" ] && echo '--updateSnapshot')",
15
- "test:acceptance:locally": "set -eu && npm run build && LOCALLY=true jest -c ./jest.acceptance.config.ts --forceExit --verbose --runInBand --passWithNoTests $([ -n \"${RESNAP:-}\" ] && echo '--updateSnapshot')",
13
+ "test:unit": "set -eu && jest -c ./jest.unit.config.ts --forceExit --verbose --passWithNoTests $([ -n \"${CI:-}\" ] && echo '--ci') $([ \"${THOROUGH:-}\" != \"true\" ] && echo '--changedSince=main') $([ \"${RESNAP:-}\" = \"true\" ] && echo '--updateSnapshot')",
14
+ "test:integration": "set -eu && jest -c ./jest.integration.config.ts --forceExit --verbose --passWithNoTests $([ -n \"${CI:-}\" ] && echo '--ci') $([ \"${THOROUGH:-}\" != \"true\" ] && echo '--changedSince=main') $([ \"${RESNAP:-}\" = \"true\" ] && echo '--updateSnapshot')",
15
+ "test:acceptance:locally": "set -eu && npm run build && LOCALLY=true jest -c ./jest.acceptance.config.ts --forceExit --verbose --runInBand --passWithNoTests $([ \"${RESNAP:-}\" = \"true\" ] && echo '--updateSnapshot')",
16
16
  "test": "set -eu && npm run test:commits && npm run test:types && npm run test:format && npm run test:lint && npm run test:unit && npm run test:integration && npm run test:acceptance:locally",
17
- "test:acceptance": "set -eu && npm run build && jest -c ./jest.acceptance.config.ts --forceExit --verbose --runInBand --passWithNoTests $([ -n \"${CI:-}\" ] && echo '--ci') $([ -n \"${RESNAP:-}\" ] && echo '--updateSnapshot')"
17
+ "test:acceptance": "set -eu && npm run build && jest -c ./jest.acceptance.config.ts --forceExit --verbose --runInBand --passWithNoTests $([ -n \"${CI:-}\" ] && echo '--ci') $([ \"${RESNAP:-}\" = \"true\" ] && echo '--updateSnapshot')"
18
18
  }
19
19
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "declapract-typescript-ehmpathy",
3
3
  "author": "ehmpathy",
4
4
  "description": "declapract best practices declarations for typescript",
5
- "version": "0.47.67",
5
+ "version": "0.47.69",
6
6
  "license": "MIT",
7
7
  "main": "src/index.js",
8
8
  "repository": "ehmpathy/declapract-typescript-ehmpathy",
@@ -33,11 +33,11 @@
33
33
  "test:lint:biome": "npm run biome:nested:hide && biome check --diagnostic-level=error; EXIT=$?; npm run biome:nested:restore; exit $EXIT",
34
34
  "test:lint:biome:all": "npm run biome:nested:hide && biome check; EXIT=$?; npm run biome:nested:restore; exit $EXIT",
35
35
  "test:lint": "npm run test:lint:biome && npm run test:lint:deps",
36
- "test:unit": "set -eu && jest -c ./jest.unit.config.ts --forceExit --verbose --passWithNoTests $([ -n \"${CI:-}\" ] && echo '--ci') $([ -z \"${THOROUGH:-}\" ] && echo '--changedSince=main') $([ -n \"${RESNAP:-}\" ] && echo '--updateSnapshot')",
37
- "test:integration": "set -eu && jest -c ./jest.integration.config.ts --forceExit --verbose --passWithNoTests $([ -n \"${CI:-}\" ] && echo '--ci') $([ -z \"${THOROUGH:-}\" ] && echo '--changedSince=main') $([ -n \"${RESNAP:-}\" ] && echo '--updateSnapshot')",
38
- "test:acceptance:locally": "set -eu && npm run build && LOCALLY=true jest -c ./jest.acceptance.config.ts --forceExit --verbose --runInBand --passWithNoTests $([ -n \"${RESNAP:-}\" ] && echo '--updateSnapshot')",
36
+ "test:unit": "set -eu && jest -c ./jest.unit.config.ts --forceExit --verbose --passWithNoTests $([ -n \"${CI:-}\" ] && echo '--ci') $([ \"${THOROUGH:-}\" != \"true\" ] && echo '--changedSince=main') $([ \"${RESNAP:-}\" = \"true\" ] && echo '--updateSnapshot')",
37
+ "test:integration": "set -eu && jest -c ./jest.integration.config.ts --forceExit --verbose --passWithNoTests $([ -n \"${CI:-}\" ] && echo '--ci') $([ \"${THOROUGH:-}\" != \"true\" ] && echo '--changedSince=main') $([ \"${RESNAP:-}\" = \"true\" ] && echo '--updateSnapshot')",
38
+ "test:acceptance:locally": "set -eu && npm run build && LOCALLY=true jest -c ./jest.acceptance.config.ts --forceExit --verbose --runInBand --passWithNoTests $([ \"${RESNAP:-}\" = \"true\" ] && echo '--updateSnapshot')",
39
39
  "test": "set -eu && npm run test:commits && npm run test:types && npm run test:format && npm run test:lint && npm run test:unit && npm run test:integration && npm run test:acceptance:locally && npm run test:validate",
40
- "test:acceptance": "set -eu && npm run build && jest -c ./jest.acceptance.config.ts --forceExit --verbose --runInBand --passWithNoTests $([ -n \"${CI:-}\" ] && echo '--ci') $([ -n \"${RESNAP:-}\" ] && echo '--updateSnapshot')",
40
+ "test:acceptance": "set -eu && npm run build && jest -c ./jest.acceptance.config.ts --forceExit --verbose --runInBand --passWithNoTests $([ -n \"${CI:-}\" ] && echo '--ci') $([ \"${RESNAP:-}\" = \"true\" ] && echo '--updateSnapshot')",
41
41
  "prepush": "npm run test && npm run build",
42
42
  "prepublish": "npm run build",
43
43
  "preversion": "npm run prepush",
@@ -75,12 +75,12 @@
75
75
  "esbuild-register": "3.6.0",
76
76
  "husky": "8.0.3",
77
77
  "jest": "30.2.0",
78
- "rhachet": "1.41.1",
78
+ "rhachet": "1.41.10",
79
79
  "rhachet-brains-anthropic": "0.4.1",
80
80
  "rhachet-brains-xai": "0.3.3",
81
- "rhachet-roles-bhrain": "0.27.5",
82
- "rhachet-roles-bhuild": "0.21.1",
83
- "rhachet-roles-ehmpathy": "1.35.0",
81
+ "rhachet-roles-bhrain": "0.27.6",
82
+ "rhachet-roles-bhuild": "0.21.9",
83
+ "rhachet-roles-ehmpathy": "1.35.5",
84
84
  "tsc-alias": "1.8.10",
85
85
  "tsx": "4.20.6",
86
86
  "type-fns": "1.21.2",
@@ -1 +0,0 @@
1
- declare module 'config-with-paramstore';