declapract-typescript-ehmpathy 0.49.2 → 0.49.4

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 (71) hide show
  1. package/dist/getVariables.ts +6 -2
  2. package/dist/practices/cicd-app-react-native-expo/best-practice/.github/workflows/.deploy-expo.yml +1 -1
  3. package/dist/practices/cicd-app-react-native-expo/best-practice/.github/workflows/deploy.yml +8 -8
  4. package/dist/practices/cicd-app-react-native-expo/best-practice/.github/workflows/test.yml +3 -3
  5. package/dist/practices/cicd-app-react-native-expo/best-practice/package.json +9 -9
  6. package/dist/practices/cicd-common/best-practice/.github/workflows/.test.yml +4 -4
  7. package/dist/practices/cicd-common/best-practice/.github/workflows/test.yml +1 -1
  8. package/dist/practices/cicd-package/best-practice/.github/workflows/publish.yml +1 -1
  9. package/dist/practices/cicd-service/best-practice/.agent/repo=.this/skills/use.rds.capacity.sh +1 -1
  10. package/dist/practices/cicd-service/best-practice/.declapract.readme.md +1 -1
  11. package/dist/practices/cicd-service/best-practice/.github/workflows/deploy.yml +3 -3
  12. package/dist/practices/cicd-service/best-practice/.github/workflows/provision.yml +9 -9
  13. package/dist/practices/config/bad-practices/configs-contain-wrong-account-id/config/prod.json.declapract.ts +2 -2
  14. package/dist/practices/config/bad-practices/divergent-config-shapes/.declapract.readme.md +1 -1
  15. package/dist/practices/config/bad-practices/divergent-config-shapes/config/{dev.json.declapract.ts → prep.json.declapract.ts} +7 -7
  16. package/dist/practices/config/bad-practices/old-dev-config-location/config/dev.json.declapract.ts +4 -1
  17. package/dist/practices/config/best-practice/config/prep.json +1 -1
  18. package/dist/practices/config/best-practice/config/test.json +1 -1
  19. package/dist/practices/environments/bad-practices/old-dev-scripts/package.json.declapract.ts +57 -0
  20. package/dist/practices/environments/best-practice/.declapract.readme.md +1 -1
  21. package/dist/practices/environments/best-practice/src/utils/environment.ts +2 -2
  22. package/dist/practices/husky/best-practice/.husky/check.timestamps.sh +48 -0
  23. package/dist/practices/husky/best-practice/.husky/pre-commit +2 -0
  24. package/dist/practices/husky/check.timestamps.play.declapract.integration.test.ts +148 -0
  25. package/dist/practices/node-service/bad-practices/simple-lambda-client/.declapract.readme.md +55 -0
  26. package/dist/practices/node-service/bad-practices/simple-lambda-client/package.json +5 -0
  27. package/dist/practices/node-service/bad-practices/simple-lambda-client/package.json.declapract.ts +27 -0
  28. package/dist/practices/node-service/bad-practices/simple-lambda-client/src/<star><star>/<star>.ts.declapract.ts +44 -0
  29. package/dist/practices/node-service/bad-practices/simple-lambda-handlers/.declapract.readme.md +52 -0
  30. package/dist/practices/node-service/bad-practices/simple-lambda-handlers/package.json +5 -0
  31. package/dist/practices/node-service/bad-practices/simple-lambda-handlers/package.json.declapract.ts +27 -0
  32. package/dist/practices/node-service/bad-practices/simple-lambda-handlers/src/<star><star>/<star>.ts.declapract.ts +50 -0
  33. package/dist/practices/node-service/best-practice/package.json +4 -1
  34. package/dist/practices/package-json-order/best-practice/package.json.declapract.ts +11 -11
  35. package/dist/practices/persist-with-rds/best-practice/.declapract.readme.md +21 -1
  36. package/dist/practices/persist-with-rds/best-practice/provision/aws/product/parameter-store.tf +39 -0
  37. package/dist/practices/persist-with-rds/best-practice/provision/schema/deploy.database.sh +9 -9
  38. package/dist/practices/provision-github/best-practice/.declapract.readme.md +7 -0
  39. package/dist/practices/rhachet/bad-practices/review-peer-artifacts/.behavior/<star><star>/.reviews/<star><star>/<star>peer-review<star>.md.declapract.ts +16 -0
  40. package/dist/practices/rhachet/bad-practices/review-peer-artifacts/.behavior/<star><star>/.reviews/peer/<star><star>/<star>.md.declapract.ts +16 -0
  41. package/dist/practices/rhachet/bad-practices/review-peer-artifacts/.declapract.readme.md +12 -0
  42. package/dist/practices/{runtime-type-checking → runtime-schemas}/bad-practices/joi/package.json.declapract.ts +4 -0
  43. package/dist/practices/runtime-schemas/bad-practices/joi/src/<star><star>/<star>.ts.declapract.ts +50 -0
  44. package/dist/practices/serverless/best-practice/package.json +2 -2
  45. package/dist/practices/serverless/best-practice/serverless.yml +3 -3
  46. package/dist/practices/serverless/best-practice/serverless.yml.declapract.ts +2 -2
  47. package/dist/practices/terraform-aws/bad-practices/old-dev-env-dir/provision/aws/environments/dev/<star>.declapract.ts +14 -0
  48. package/dist/practices/terraform-aws/best-practice/provision/aws/environments/{dev → prep}/main.tf +2 -2
  49. package/dist/practices/terraform-aws/best-practice/provision/aws/environments/test/main.tf +1 -1
  50. package/dist/useCases.yml +1 -3
  51. package/package.json +6 -5
  52. package/dist/practices/lambda-clients/bad-practices/lambda-service-client-package/.declapract.readme.md +0 -1
  53. package/dist/practices/lambda-clients/bad-practices/lambda-service-client-package/package.json +0 -5
  54. package/dist/practices/lambda-clients/bad-practices/lambda-service-client-package/package.json.declapract.ts +0 -16
  55. package/dist/practices/lambda-clients/best-practice/package.json +0 -5
  56. package/dist/practices/lambda-clients/best-practice/src/data/clients/<star>.ts +0 -1
  57. package/dist/practices/lambda-clients/best-practice/src/data/clients/<star>.ts.declapract.ts +0 -3
  58. package/dist/practices/lambda-handlers/best-practice/package.json +0 -6
  59. package/dist/practices/lambda-handlers/best-practice/package.json.declapract.ts +0 -3
  60. package/dist/practices/lambda-handlers/best-practice/src/contract/handlers/<star><star>/<star>.declapract.ts +0 -3
  61. package/dist/practices/runtime-type-checking/bad-practices/joi-types/package.json +0 -5
  62. package/dist/practices/runtime-type-checking/bad-practices/joi-types/package.json.declapract.ts +0 -18
  63. package/dist/practices/runtime-type-checking/bad-practices/old-joi-syntax-valid-input-cant-be-array/.declapract.readme.md +0 -1
  64. package/dist/practices/runtime-type-checking/bad-practices/old-joi-syntax-valid-input-cant-be-array/src/<star><star>/<star>.ts.declapract.ts +0 -15
  65. package/dist/practices/runtime-type-checking/best-practice/package.json.declapract.ts +0 -3
  66. /package/dist/practices/config/bad-practices/configs-contain-wrong-account-id/config/{dev.json.declapract.ts → prep.json.declapract.ts} +0 -0
  67. /package/dist/practices/{runtime-type-checking → runtime-schemas}/bad-practices/joi/.declapract.readme.md +0 -0
  68. /package/dist/practices/{runtime-type-checking → runtime-schemas}/bad-practices/joi/package.json +0 -0
  69. /package/dist/practices/{runtime-type-checking → runtime-schemas}/best-practice/package.json +0 -0
  70. /package/dist/practices/{lambda-clients → runtime-schemas}/best-practice/package.json.declapract.ts +0 -0
  71. /package/dist/practices/terraform-aws/best-practice/provision/aws/environments/{dev → prep}/versions.tf +0 -0
@@ -0,0 +1,55 @@
1
+ # simple-lambda-client
2
+
3
+ ## what
4
+
5
+ detects and transforms simple-lambda-client usage to sdk-aws-lambda.
6
+
7
+ ## why
8
+
9
+ sdk-aws-lambda provides modern lambda client patterns:
10
+ - context-based configuration
11
+ - trail.exid auto-propagation through call chain
12
+ - better cache support
13
+
14
+ ## transforms
15
+
16
+ | old | new |
17
+ |-----|-----|
18
+ | `import { invokeLambdaFunction } from 'simple-lambda-client'` | `import { askLambdaEndpoint } from 'sdk-aws-lambda'` |
19
+ | `invokeLambdaFunction(...)` | `askLambdaEndpoint(...)` |
20
+
21
+ ## limitations
22
+
23
+ these patterns require manual migration:
24
+
25
+ - `{ service, function, stage, event }` → `{ which: { service, function }, event }, context`
26
+ - `stage: string` → `context.env.access: string`
27
+ - `logDebug?: LogMethod` → `context.log: LogMethods` (required)
28
+ - `cache?: SimpleCache<O>` → `context.cache?: { response?, dedupe? }`
29
+
30
+ ## example
31
+
32
+ before:
33
+ ```ts
34
+ import { invokeLambdaFunction } from 'simple-lambda-client';
35
+
36
+ const result = await invokeLambdaFunction({
37
+ service: 'svc-users',
38
+ function: 'getUser',
39
+ stage: 'prod',
40
+ event: { userId: '123' },
41
+ });
42
+ ```
43
+
44
+ after (requires manual refinement):
45
+ ```ts
46
+ import { askLambdaEndpoint } from 'sdk-aws-lambda';
47
+
48
+ const result = await askLambdaEndpoint(
49
+ {
50
+ which: { service: 'svc-users', function: 'getUser' },
51
+ event: { userId: '123' },
52
+ },
53
+ { log, env: { access: 'prod' } },
54
+ );
55
+ ```
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "simple-lambda-client": "@declapract{check.minVersion('0.0.0')}"
4
+ }
5
+ }
@@ -0,0 +1,27 @@
1
+ import { FileCheckType, type FileFixFunction } from 'declapract';
2
+
3
+ export const check = FileCheckType.CONTAINS;
4
+
5
+ /**
6
+ * .what = removes simple-lambda-client and adds sdk-aws-lambda
7
+ * .why = sdk-aws-lambda is the modern replacement with better patterns
8
+ */
9
+ export const fix: FileFixFunction = (contents) => {
10
+ if (!contents) return { contents };
11
+
12
+ const packageJSON = JSON.parse(contents);
13
+
14
+ const updatedPackageJSON = {
15
+ ...packageJSON,
16
+ dependencies: {
17
+ ...packageJSON.dependencies,
18
+ 'simple-lambda-client': undefined,
19
+ 'sdk-aws-lambda':
20
+ packageJSON.dependencies?.['sdk-aws-lambda'] ?? '^0.1.0',
21
+ },
22
+ };
23
+
24
+ return {
25
+ contents: JSON.stringify(updatedPackageJSON, null, 2),
26
+ };
27
+ };
@@ -0,0 +1,44 @@
1
+ import type { FileCheckFunction, FileFixFunction } from 'declapract';
2
+
3
+ /**
4
+ * .what = detects simple-lambda-client imports in source files
5
+ * .why = simple-lambda-client should be replaced with sdk-aws-lambda
6
+ */
7
+ export const check: FileCheckFunction = (contents) => {
8
+ // match if file imports from simple-lambda-client
9
+ if (contents?.includes("from 'simple-lambda-client'")) return;
10
+ if (contents?.includes('from "simple-lambda-client"')) return;
11
+
12
+ // no match
13
+ throw new Error('does not import from simple-lambda-client');
14
+ };
15
+
16
+ /**
17
+ * .what = transforms simple-lambda-client imports to sdk-aws-lambda
18
+ * .why = automated migration reduces manual toil
19
+ *
20
+ * .note = basic transforms only; complex patterns require manual fix
21
+ *
22
+ * API map:
23
+ * - invokeLambdaFunction → askLambdaEndpoint
24
+ */
25
+ export const fix: FileFixFunction = (contents) => {
26
+ if (!contents) return {};
27
+
28
+ const updated = contents
29
+ // transform imports
30
+ .replace(
31
+ /import\s+\{([^}]*)\}\s+from\s+['"]simple-lambda-client['"]/g,
32
+ (match, imports) => {
33
+ const updatedImports = imports.replace(
34
+ /invokeLambdaFunction/g,
35
+ 'askLambdaEndpoint',
36
+ );
37
+ return `import {${updatedImports}} from 'sdk-aws-lambda'`;
38
+ },
39
+ )
40
+ // transform function calls
41
+ .replace(/invokeLambdaFunction/g, 'askLambdaEndpoint');
42
+
43
+ return { contents: updated };
44
+ };
@@ -0,0 +1,52 @@
1
+ # simple-lambda-handlers
2
+
3
+ ## what
4
+
5
+ detects and transforms simple-lambda-handlers usage to sdk-aws-lambda.
6
+
7
+ ## why
8
+
9
+ sdk-aws-lambda provides modern lambda handler patterns:
10
+ - better schema validation with zod (input + output)
11
+ - log context passed to invoke function
12
+ - trail.exid auto-propagation
13
+
14
+ ## transforms
15
+
16
+ | old | new |
17
+ |-----|-----|
18
+ | `import { createStandardHandler } from 'simple-lambda-handlers'` | `import { genLambdaEndpoint } from 'sdk-aws-lambda'` |
19
+ | `import { createApiGatewayHandler } from 'simple-lambda-handlers'` | `import { genLambdaEndpoint } from 'sdk-aws-lambda'` |
20
+ | `createStandardHandler(...)` | `genLambdaEndpoint(...)` |
21
+ | `createApiGatewayHandler(...)` | `genLambdaEndpoint.for.apiGateway(...)` |
22
+
23
+ ## limitations
24
+
25
+ these patterns require manual migration:
26
+
27
+ - `schema` (single joi) → `schema: { input, output }` (split zod)
28
+ - `logic: (event) => O` → `invoke: ({ event }, { log }) => O`
29
+ - `log` passed at creation → `log` also received in invoke context
30
+
31
+ ## example
32
+
33
+ before:
34
+ ```ts
35
+ import { createStandardHandler } from 'simple-lambda-handlers';
36
+
37
+ export const handler = createStandardHandler({
38
+ log,
39
+ schema,
40
+ logic: async (event) => doStuff(event),
41
+ });
42
+ ```
43
+
44
+ after (requires manual refinement):
45
+ ```ts
46
+ import { genLambdaEndpoint } from 'sdk-aws-lambda';
47
+
48
+ export const handler = genLambdaEndpoint({
49
+ schema: { input: inputSchema, output: outputSchema },
50
+ invoke: async ({ event }, { log }) => doStuff(event),
51
+ }, { log });
52
+ ```
@@ -0,0 +1,5 @@
1
+ {
2
+ "dependencies": {
3
+ "simple-lambda-handlers": "@declapract{check.minVersion('0.0.0')}"
4
+ }
5
+ }
@@ -0,0 +1,27 @@
1
+ import { FileCheckType, type FileFixFunction } from 'declapract';
2
+
3
+ export const check = FileCheckType.CONTAINS;
4
+
5
+ /**
6
+ * .what = removes simple-lambda-handlers and adds sdk-aws-lambda
7
+ * .why = sdk-aws-lambda is the modern replacement with better patterns
8
+ */
9
+ export const fix: FileFixFunction = (contents) => {
10
+ if (!contents) return { contents };
11
+
12
+ const packageJSON = JSON.parse(contents);
13
+
14
+ const updatedPackageJSON = {
15
+ ...packageJSON,
16
+ dependencies: {
17
+ ...packageJSON.dependencies,
18
+ 'simple-lambda-handlers': undefined,
19
+ 'sdk-aws-lambda':
20
+ packageJSON.dependencies?.['sdk-aws-lambda'] ?? '^0.1.0',
21
+ },
22
+ };
23
+
24
+ return {
25
+ contents: JSON.stringify(updatedPackageJSON, null, 2),
26
+ };
27
+ };
@@ -0,0 +1,50 @@
1
+ import type { FileCheckFunction, FileFixFunction } from 'declapract';
2
+
3
+ /**
4
+ * .what = detects simple-lambda-handlers imports in source files
5
+ * .why = simple-lambda-handlers should be replaced with sdk-aws-lambda
6
+ */
7
+ export const check: FileCheckFunction = (contents) => {
8
+ // match if file imports from simple-lambda-handlers
9
+ if (contents?.includes("from 'simple-lambda-handlers'")) return;
10
+ if (contents?.includes('from "simple-lambda-handlers"')) return;
11
+
12
+ // no match
13
+ throw new Error('does not import from simple-lambda-handlers');
14
+ };
15
+
16
+ /**
17
+ * .what = transforms simple-lambda-handlers imports to sdk-aws-lambda
18
+ * .why = automated migration reduces manual toil
19
+ *
20
+ * .note = basic transforms only; complex patterns require manual fix
21
+ *
22
+ * API map:
23
+ * - createStandardHandler → genLambdaEndpoint
24
+ * - createApiGatewayHandler → genLambdaEndpoint.for.apiGateway
25
+ */
26
+ export const fix: FileFixFunction = (contents) => {
27
+ if (!contents) return {};
28
+
29
+ const updated = contents
30
+ // transform imports (dedupe to avoid duplicate genLambdaEndpoint)
31
+ .replace(
32
+ /import\s+\{([^}]*)\}\s+from\s+['"]simple-lambda-handlers['"]/g,
33
+ (match, imports) => {
34
+ // split, replace, dedupe, join
35
+ const importList = imports.split(',').map((s: string) => s.trim());
36
+ const transformed = importList.map((name: string) => {
37
+ if (name === 'createStandardHandler') return 'genLambdaEndpoint';
38
+ if (name === 'createApiGatewayHandler') return 'genLambdaEndpoint';
39
+ return name;
40
+ });
41
+ const deduped = [...new Set(transformed)];
42
+ return `import { ${deduped.join(', ')} } from 'sdk-aws-lambda'`;
43
+ },
44
+ )
45
+ // transform function calls
46
+ .replace(/createStandardHandler/g, 'genLambdaEndpoint')
47
+ .replace(/createApiGatewayHandler/g, 'genLambdaEndpoint.for.apiGateway');
48
+
49
+ return { contents: updated };
50
+ };
@@ -1,3 +1,6 @@
1
1
  {
2
- "private": true
2
+ "private": true,
3
+ "dependencies": {
4
+ "sdk-aws-lambda": "@declapract{check.minVersion('0.0.1')}"
5
+ }
3
6
  }
@@ -53,7 +53,7 @@ export const desiredRelativeKeyOrder = {
53
53
  'provision:testdb:docker:down',
54
54
  'provision:testdb',
55
55
  'start:testdb',
56
- 'start:livedb:dev',
56
+ 'start:livedb:prep',
57
57
  'test:auth',
58
58
  'test:commits',
59
59
  'test:types',
@@ -82,34 +82,34 @@ export const desiredRelativeKeyOrder = {
82
82
  'deploy:prune',
83
83
  'deploy:release',
84
84
  'deploy:send-notification',
85
- 'deploy:dev',
85
+ 'deploy:prep',
86
86
  'deploy:prod',
87
87
  'deploy',
88
88
 
89
89
  // from app-react-native-expo
90
90
  'start:hot:proxy',
91
- 'start:hot:dev',
92
- 'start:hot:dev:link',
93
- 'start:hot:dev:proxy',
91
+ 'start:hot:prep',
92
+ 'start:hot:prep:link',
93
+ 'start:hot:prep:proxy',
94
94
  'start:hot:prod',
95
95
  'start:hot:prod:link',
96
96
  'start:hot:prod:proxy',
97
97
  'start:cold:proxy',
98
- 'start:cold:dev',
99
- 'start:cold:dev:link',
100
- 'start:cold:dev:proxy',
98
+ 'start:cold:prep',
99
+ 'start:cold:prep:link',
100
+ 'start:cold:prep:proxy',
101
101
  'start:cold:prod',
102
102
  'start:cold:prod:link',
103
103
  'register:device:apple:info',
104
104
  'register:device:apple:inclusions:rebuild:prod',
105
- 'register:device:apple:inclusions:rebuild:dev',
105
+ 'register:device:apple:inclusions:rebuild:prep',
106
106
  'register:device:apple:add',
107
107
  'register:device:apple:list',
108
108
  'register:device:apple:delete',
109
109
  'register:device:apple:rename',
110
110
  'register:profile:apple:list',
111
- 'build:dev:ios',
112
- 'build:dev:android',
111
+ 'build:prep:ios',
112
+ 'build:prep:android',
113
113
  'build:web',
114
114
  'deploy:prod:ios:metadata',
115
115
  'open:store:android',
@@ -1,6 +1,26 @@
1
1
  we use:
2
2
  - postgres database
3
- - provisioned by aws rds aurora when in dev and prod env
3
+ - provisioned by aws rds aurora when in prep and prod env
4
4
  - provisioned by docker when in test env
5
5
  - sql-schema-control to manage sql schema
6
6
  - sql-dao-generator to generate the sql-schemas + daos
7
+
8
+ ## required variables
9
+
10
+ | variable | description | example |
11
+ |----------|-------------|---------|
12
+ | `databaseTunnelHost.{prep,prod}` | ssm proxy host for database tunnel | `aws.ssmproxy.ahbodedb.prep` |
13
+ | `databaseClusterHost.{prep,prod}` | rds cluster endpoint | `ahbodedb.cluster-xxx.us-east-1.rds.amazonaws.com` |
14
+
15
+ ## ssm parameters
16
+
17
+ terraform provisions these ssm parameters for database credentials:
18
+
19
+ | parameter name | description | environments |
20
+ |----------------|-------------|--------------|
21
+ | `{namespace}.database.role.cicd.password` | password for cicd database user | prep, prod |
22
+ | `{namespace}.database.role.crud.password` | password for crud database user | prep, prod |
23
+
24
+ legacy parameters (kept for backwards compatibility):
25
+ - `{namespace}.database.admin.password`
26
+ - `{namespace}.database.service.password`
@@ -1,3 +1,6 @@
1
+ # ====================
2
+ # legacy params (kept for backwards compatibility)
3
+ # ====================
1
4
  resource "aws_ssm_parameter" "secret_database_admin_password" {
2
5
  name = "${local.parameter_store_namespace}.database.admin.password"
3
6
  type = "SecureString"
@@ -22,3 +25,39 @@ resource "aws_ssm_parameter" "secret_database_service_password" {
22
25
  ]
23
26
  }
24
27
  }
28
+
29
+ # ====================
30
+ # new role-based params (standard naming)
31
+ # ====================
32
+ # read legacy params to copy values (prod only)
33
+ data "aws_ssm_parameter" "legacy_admin_password" {
34
+ name = "${local.parameter_store_namespace}.database.admin.password"
35
+ count = var.environment == "prod" ? 1 : 0
36
+ with_decryption = true
37
+ }
38
+ data "aws_ssm_parameter" "legacy_service_password" {
39
+ name = "${local.parameter_store_namespace}.database.service.password"
40
+ count = var.environment == "prod" ? 1 : 0
41
+ with_decryption = true
42
+ }
43
+
44
+ resource "aws_ssm_parameter" "secret_database_role_cicd_password" {
45
+ name = "${local.parameter_store_namespace}.database.role.cicd.password"
46
+ type = "SecureString"
47
+ value = var.environment == "prod" ? data.aws_ssm_parameter.legacy_admin_password[0].value : "__CHANG3_ME__"
48
+ tags = local.tags
49
+ count = var.environment != "test" ? 1 : 0
50
+ lifecycle {
51
+ ignore_changes = [value]
52
+ }
53
+ }
54
+ resource "aws_ssm_parameter" "secret_database_role_crud_password" {
55
+ name = "${local.parameter_store_namespace}.database.role.crud.password"
56
+ type = "SecureString"
57
+ value = var.environment == "prod" ? data.aws_ssm_parameter.legacy_service_password[0].value : "__CHANG3_ME__"
58
+ tags = local.tags
59
+ count = var.environment != "test" ? 1 : 0
60
+ lifecycle {
61
+ ignore_changes = [value]
62
+ }
63
+ }
@@ -13,20 +13,20 @@
13
13
  ##
14
14
  ## usage example:
15
15
  ## ```sh
16
- ## ./provision/schema/deploy.database.sh dev $(op item get ahbodedb.dev.postgres --fields label=password --format json | jq -r .value)
16
+ ## ./provision/schema/deploy.database.sh prep $(op item get ahbodedb.prep.postgres --fields label=password --format json | jq -r .value)
17
17
  ## ```
18
18
  #####################################################
19
19
 
20
20
  # check that user has defined the environment that they want this key created for correctly
21
21
  ENVIRONMENT=$1;
22
- if [ "$ENVIRONMENT" != "prod" ] && [ "$ENVIRONMENT" != "dev" ]; then
23
- echo "\nerror: Environment, the first argument, must be specified as either 'prod' or 'dev'. You specified '$ENVIRONMENT'";
22
+ if [ "$ENVIRONMENT" != "prod" ] && [ "$ENVIRONMENT" != "prep" ]; then
23
+ echo "\nerror: Environment, the first argument, must be specified as either 'prod' or 'prep'. You specified '$ENVIRONMENT'";
24
24
  exit 1;
25
25
  fi
26
26
 
27
27
  # check that user is authed into correct account
28
28
  AWS_ACCOUNT_ID=$(aws sts get-caller-identity | jq -r '.Account');
29
- EXPECTED_AWS_ACCOUNT_ID=$([ "$ENVIRONMENT" = 'prod' ] && echo "@declapract{variable.awsAccountId.prod}" || echo "@declapract{variable.awsAccountId.dev}");
29
+ EXPECTED_AWS_ACCOUNT_ID=$([ "$ENVIRONMENT" = 'prod' ] && echo "@declapract{variable.awsAccountId.prod}" || echo "@declapract{variable.awsAccountId.prep}");
30
30
  if [ "$AWS_ACCOUNT_ID" != "$EXPECTED_AWS_ACCOUNT_ID" ]; then
31
31
  echo "\nerror: the AWS_ACCOUNT that you are signed into is not correct for the environment you specified. You are authed into account '$AWS_ACCOUNT_ID' but the correct account id for '$ENVIRONMENT' IS '$EXPECTED_AWS_ACCOUNT_ID'";
32
32
  exit 1;
@@ -41,20 +41,20 @@ fi;
41
41
 
42
42
  # check that the cicd password was provisioned, if in prod, since this is used to create the cicd user
43
43
  if [ "$ENVIRONMENT" = "prod" ]; then
44
- CICD_USER_PASSWORD=$(aws ssm get-parameter --name "@declapract{variable.organizationName}.@declapract{variable.projectName}.$ENVIRONMENT.database.admin.password" --with-decryption --output text --query Parameter.Value)
44
+ CICD_USER_PASSWORD=$(aws ssm get-parameter --name "@declapract{variable.organizationName}.@declapract{variable.projectName}.$ENVIRONMENT.database.role.cicd.password" --with-decryption --output text --query Parameter.Value)
45
45
  if [ -z "$CICD_USER_PASSWORD" ]; then
46
46
  echo "\nerror: CICD_USER_PASSWORD must be provisioned with terraform before running this"
47
47
  exit 1;
48
48
  fi;
49
- if [ "$CICD_USER_PASSWORD" = "__IGNORED__" ]; then
50
- echo "\nerror: CICD_USER_PASSWORD must be set to a value other than the default of '__IGNORED__'"
49
+ if [ "$CICD_USER_PASSWORD" = "__IGNORED__" ] || [ "$CICD_USER_PASSWORD" = "__CHANG3_ME__" ]; then
50
+ echo "\nerror: CICD_USER_PASSWORD must be set to a value other than the default placeholder"
51
51
  exit 1;
52
52
  fi;
53
53
  fi;
54
54
 
55
55
 
56
56
  # define the postgres connecition string
57
- CLUSTER_HOST=$([ "$ENVIRONMENT" = 'prod' ] && echo "@declapract{variable.databaseTunnelHost.prod}" || echo "@declapract{variable.databaseTunnelHost.dev}");
57
+ CLUSTER_HOST=$([ "$ENVIRONMENT" = 'prod' ] && echo "@declapract{variable.databaseTunnelHost.prod}" || echo "@declapract{variable.databaseTunnelHost.prep}");
58
58
  CLUSTER_CONNECTION_STRING=postgresql://postgres:$POSTGRES_ADMIN_PASSWORD@$CLUSTER_HOST:5432
59
59
  ROOT_DB_CONNECTION_STRING=$CLUSTER_CONNECTION_STRING/postgres
60
60
  SVC_DB_CONNECTION_STRING=$CLUSTER_CONNECTION_STRING/@declapract{variable.databaseName}
@@ -75,7 +75,7 @@ echo "\n 🔨 creating the schema..."
75
75
  psql $SVC_DB_CONNECTION_STRING -f $INIT_SQLS_DIR/.schema.sql
76
76
 
77
77
  if [ "$ENVIRONMENT" = "prod" ]; then
78
- echo "\n 🔨 granting reads to the datalakedb user..." # only in prod env; we dont want dev's testing data in our datalake
78
+ echo "\n 🔨 granting reads to the datalakedb user..." # only in prod env; we dont want prep's test data in our datalake
79
79
  psql $SVC_DB_CONNECTION_STRING -f $INIT_SQLS_DIR/.user.datalakedb.sql
80
80
  fi;
81
81
 
@@ -0,0 +1,7 @@
1
+ provisions github repository settings and environments via declastruct-github-sdk.
2
+
3
+ ## required variables
4
+
5
+ | variable | description | example |
6
+ |----------|-------------|---------|
7
+ | `reviewers.users` | github usernames for production deployment reviewers | `['whodisio', 'ehmpathy']` |
@@ -0,0 +1,16 @@
1
+ import { FileCheckType, type FileFixFunction } from 'declapract';
2
+
3
+ /**
4
+ * .what = detects committed peer-review artifacts under .behavior/**\/.reviews
5
+ * .why = peer-review files are noise; far less important than the root behavior
6
+ * files, so they should not be tracked in git
7
+ *
8
+ * .note = matches the old flat shape, where peer-review files sit directly in
9
+ * .reviews with `peer-review` in the filename
10
+ */
11
+
12
+ export const check = FileCheckType.EXISTS;
13
+
14
+ export const fix: FileFixFunction = () => {
15
+ return { contents: null }; // delete the file
16
+ };
@@ -0,0 +1,16 @@
1
+ import { FileCheckType, type FileFixFunction } from 'declapract';
2
+
3
+ /**
4
+ * .what = detects committed peer-review artifacts under .behavior/**\/.reviews/peer
5
+ * .why = peer-review files are noise; far less important than the root behavior
6
+ * files, so they should not be tracked in git
7
+ *
8
+ * .note = matches the new shape, where all peer-review files live under a
9
+ * dedicated `peer` directory
10
+ */
11
+
12
+ export const check = FileCheckType.EXISTS;
13
+
14
+ export const fix: FileFixFunction = () => {
15
+ return { contents: null }; // delete the file
16
+ };
@@ -0,0 +1,12 @@
1
+ peer review artifacts should not be committed to git
2
+
3
+ these files live under `.behavior/**/.reviews/` and capture peer-review feedback
4
+ from the behavior build lifecycle. they are noise for the most part — far less
5
+ important than the root behavior files themselves.
6
+
7
+ two path shapes are removed:
8
+
9
+ - old (flat): `.behavior/**/.reviews/**/*peer-review*.md`
10
+ - new (peer dir): `.behavior/**/.reviews/peer/**/*.md`
11
+
12
+ self-reviews and other non-peer artifacts under `.reviews/` are intentionally left alone.
@@ -12,6 +12,10 @@ export const fix: FileFixFunction = (contents) => {
12
12
  joi: undefined,
13
13
  zod: packageJSON.dependencies?.zod ?? '^3.24.0',
14
14
  },
15
+ devDependencies: {
16
+ ...packageJSON.devDependencies,
17
+ '@types/joi': undefined,
18
+ },
15
19
  };
16
20
  return {
17
21
  contents: JSON.stringify(updatedPackageJSON, null, 2),
@@ -0,0 +1,50 @@
1
+ import type { FileCheckFunction, FileFixFunction } from 'declapract';
2
+
3
+ /**
4
+ * .what = detects joi imports in source files
5
+ * .why = joi should be replaced with zod for better typescript integration
6
+ */
7
+ export const check: FileCheckFunction = (contents) => {
8
+ // match if file imports from joi
9
+ if (contents?.includes("from 'joi'")) return;
10
+ if (contents?.includes('from "joi"')) return;
11
+
12
+ // no match
13
+ throw new Error('does not import from joi');
14
+ };
15
+
16
+ /**
17
+ * .what = transforms joi imports and basic schema patterns to zod
18
+ * .why = automated migration reduces manual toil
19
+ *
20
+ * .note = basic transforms only; complex patterns require manual fix
21
+ */
22
+ export const fix: FileFixFunction = (contents) => {
23
+ if (!contents) return {};
24
+
25
+ const updated = contents
26
+ // transform imports: import Joi from 'joi' → import { z } from 'zod'
27
+ .replace(/import\s+Joi\s+from\s+['"]joi['"]/g, "import { z } from 'zod'")
28
+ // transform imports: import * as Joi from 'joi' → import { z } from 'zod'
29
+ .replace(
30
+ /import\s+\*\s+as\s+Joi\s+from\s+['"]joi['"]/g,
31
+ "import { z } from 'zod'",
32
+ )
33
+ // transform imports: import { ... } from 'joi' → import { z } from 'zod'
34
+ .replace(
35
+ /import\s+\{[^}]*\}\s+from\s+['"]joi['"]/g,
36
+ "import { z } from 'zod'",
37
+ )
38
+ // transform basic schema patterns: Joi.* → z.*
39
+ .replace(/Joi\.object/g, 'z.object')
40
+ .replace(/Joi\.string/g, 'z.string')
41
+ .replace(/Joi\.number/g, 'z.number')
42
+ .replace(/Joi\.boolean/g, 'z.boolean')
43
+ .replace(/Joi\.array/g, 'z.array')
44
+ .replace(/Joi\.date/g, 'z.date')
45
+ .replace(/Joi\.any/g, 'z.any')
46
+ // transform .required() → remove (zod is required by default)
47
+ .replace(/\.required\(\)/g, '');
48
+
49
+ return { contents: updated };
50
+ };
@@ -5,8 +5,8 @@
5
5
  "if-env": "@declapract{check.minVersion('1.0.4')}"
6
6
  },
7
7
  "scripts": {
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",
8
+ "deploy:prune": "export COMMIT=$(npx sdk-environment get commit) && export SLS_STAGE=$([ \"$ACCESS\" = 'prep' ] && echo 'dev' || echo \"$ACCESS\") && npx sls prune -n 7 --stage $SLS_STAGE",
9
+ "deploy:release": "export COMMIT=$(npx sdk-environment get commit) && export SLS_STAGE=$([ \"$ACCESS\" = 'prep' ] && echo 'dev' || echo \"$ACCESS\") && 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
11
  "deploy:prep": "ACCESS=prep npm run deploy:release",
12
12
  "deploy:prod": "ACCESS=prod npm run deploy:release && npm run deploy:send-notification",
@@ -10,7 +10,7 @@ plugins:
10
10
 
11
11
  custom:
12
12
  accessByStage:
13
- dev: prep
13
+ dev: prep # stage=dev deploys to $service-dev but uses ACCESS=prep (config/prep.json)
14
14
  prod: prod
15
15
 
16
16
  provider:
@@ -25,8 +25,8 @@ provider:
25
25
  product: ${self:service}
26
26
  environment:
27
27
  TZ: UTC # guarantee that utc timezone will be used explicitly, to facilitate a pit of success
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)
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)
28
+ NODE_ENV: production # deploy with production optimizations of all resources, to make `prep` and `prod` stage deployments equivalent functionally (i.e., the same code paths in prep and prod)
29
+ ACCESS: ${self:custom.accessByStage.${opt:stage}, 'prep'} # sdk-environment access tier, to target the correct config + resources (e.g., hit prep db -vs- prod db)
30
30
  COMMIT: ${env:COMMIT} # sdk-environment commit slug, must be set by deploy command
31
31
  AWS_NODEJS_CONNECTION_REUSE_ENABLED: true # https://docs.aws.amazon.com/sdk-for-javascript/v2/developer-guide/node-reusing-connections.html
32
32
  deploymentBucket: serverless-deployment-@declapract{variable.infrastructureNamespaceId}-${self:provider.stage}
@@ -11,7 +11,7 @@ const accessInferencePolicy = ` # allow access inference from account name
11
11
 
12
12
  const accessByStageCustom = `custom:
13
13
  accessByStage:
14
- dev: prep
14
+ dev: prep # stage=dev deploys to $service-dev but uses ACCESS=prep (config/prep.json)
15
15
  prod: prod
16
16
 
17
17
  `;
@@ -70,7 +70,7 @@ export const fix: FileFixFunction = (contents) => {
70
70
  ) {
71
71
  fixed = fixed.replace(
72
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",
73
+ "ACCESS: ${self:custom.accessByStage.${opt:stage}, 'prep'} # sdk-environment access tier, to target the correct config + resources (e.g., hit prep db -vs- prod db)\n COMMIT: ${env:COMMIT} # sdk-environment commit slug, must be set by deploy command",
74
74
  );
75
75
  }
76
76