@decohere/scaffold 0.0.1-dev.18 → 0.0.1-dev.81

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 (240) hide show
  1. package/commonjs/src/add-api.action.js +39 -0
  2. package/commonjs/src/add-api.command.js +41 -0
  3. package/commonjs/src/add.command.js +24 -0
  4. package/commonjs/src/add.module.js +26 -0
  5. package/commonjs/src/cli.js +9 -0
  6. package/commonjs/src/create.command.js +24 -0
  7. package/commonjs/src/create.module.js +26 -0
  8. package/commonjs/src/domain/prompt.js +64 -0
  9. package/commonjs/src/index.js +38 -0
  10. package/commonjs/src/models/compose.model.js +150 -0
  11. package/commonjs/src/models/config-yaml.model.js +88 -0
  12. package/commonjs/src/models/const.js +32 -0
  13. package/commonjs/src/models/dev-container.model.js +116 -0
  14. package/commonjs/src/models/docker-image.model.js +259 -0
  15. package/commonjs/src/models/env-file.model.js +50 -0
  16. package/commonjs/src/models/gitignore.model.js +43 -0
  17. package/commonjs/src/models/launch-json.model.js +74 -0
  18. package/commonjs/src/models/package-json.model.js +149 -0
  19. package/commonjs/src/models/project.model.js +134 -0
  20. package/commonjs/src/models/source-file.js +6 -0
  21. package/commonjs/src/models/tsconfig.model.js +34 -0
  22. package/commonjs/src/models/vitest-config.model.js +35 -0
  23. package/commonjs/src/models/vscode-settings.model.js +31 -0
  24. package/commonjs/src/program.js +57 -0
  25. package/commonjs/src/resolve-target-dir.js +19 -0
  26. package/commonjs/src/services/scaffold.service.js +62 -0
  27. package/commonjs/src/version.js +57 -0
  28. package/commonjs/src/webapi/api-name.js +95 -0
  29. package/commonjs/src/webapi/cli-options.js +101 -0
  30. package/commonjs/src/webapi/const.js +50 -0
  31. package/commonjs/src/webapi/create-api.js +237 -0
  32. package/commonjs/src/webapi/devcontainer/codebase-rag-mcp.js +166 -0
  33. package/commonjs/src/webapi/devcontainer/codebase-rag-mcp.ts +182 -0
  34. package/commonjs/src/webapi/devcontainer/host-scripts.js +52 -0
  35. package/commonjs/src/webapi/devcontainer/templates/codebase-rag.env.example +3 -0
  36. package/commonjs/src/webapi/devcontainer/templates/initialize.sh +169 -0
  37. package/commonjs/src/webapi/devcontainer/templates/post-create.sh +35 -0
  38. package/commonjs/src/webapi/devcontainer/templates/post-start.sh +65 -0
  39. package/commonjs/src/webapi/devcontainer/templates/tty-prompt.sh +38 -0
  40. package/commonjs/src/webapi/features/auth.feature.js +441 -0
  41. package/commonjs/src/webapi/features/auth0-cli.js +435 -0
  42. package/commonjs/src/webapi/features/biome-format.js +49 -0
  43. package/commonjs/src/webapi/features/database.feature.js +433 -0
  44. package/commonjs/src/webapi/features/finalize.feature.js +95 -0
  45. package/commonjs/src/webapi/features/postman.feature.js +324 -0
  46. package/commonjs/src/webapi/models/api-package-json.model.js +60 -0
  47. package/commonjs/src/webapi/models/auth-config.model.js +26 -0
  48. package/commonjs/src/webapi/models/controller-source-file.model.js +193 -0
  49. package/commonjs/src/webapi/models/github-ci.model.js +193 -0
  50. package/commonjs/src/webapi/models/main-source-file.model.js +167 -0
  51. package/commonjs/src/webapi/models/production-dockerfile.model.js +157 -0
  52. package/commonjs/src/webapi/models/sample-files.model.js +60 -0
  53. package/commonjs/src/webapi/models/webapi-image.model.js +27 -0
  54. package/commonjs/src/webapi/postman/hello-openapi.js +175 -0
  55. package/commonjs/src/webapi/postman/local-view.js +184 -0
  56. package/commonjs/src/webapi/postman/openapi-to-postman.js +202 -0
  57. package/commonjs/src/webapi/workspace-readme.js +110 -0
  58. package/commonjs/src/webapi.action.js +30 -0
  59. package/commonjs/src/webapi.command.js +45 -0
  60. package/es6/src/add-api.action.js +29 -0
  61. package/es6/src/add-api.command.js +31 -0
  62. package/es6/src/add.command.js +14 -0
  63. package/es6/src/add.module.js +16 -0
  64. package/es6/src/cli.js +5 -0
  65. package/es6/src/create.command.js +14 -0
  66. package/es6/src/create.module.js +16 -0
  67. package/es6/src/domain/prompt.js +43 -0
  68. package/es6/src/index.js +8 -0
  69. package/es6/src/models/compose.model.js +144 -0
  70. package/es6/src/models/config-yaml.model.js +76 -0
  71. package/es6/src/models/const.js +22 -0
  72. package/es6/src/models/dev-container.model.js +106 -0
  73. package/es6/src/models/docker-image.model.js +217 -0
  74. package/es6/src/models/env-file.model.js +46 -0
  75. package/es6/src/models/gitignore.model.js +33 -0
  76. package/es6/src/models/launch-json.model.js +64 -0
  77. package/es6/src/models/package-json.model.js +137 -0
  78. package/es6/src/models/project.model.js +119 -0
  79. package/es6/src/models/source-file.js +3 -0
  80. package/es6/src/models/tsconfig.model.js +24 -0
  81. package/es6/src/models/vitest-config.model.js +25 -0
  82. package/es6/src/models/vscode-settings.model.js +21 -0
  83. package/es6/src/program.js +39 -0
  84. package/es6/src/resolve-target-dir.js +9 -0
  85. package/es6/src/services/scaffold.service.js +52 -0
  86. package/es6/src/version.js +36 -0
  87. package/es6/src/webapi/api-name.js +59 -0
  88. package/es6/src/webapi/cli-options.js +36 -0
  89. package/es6/src/webapi/const.js +20 -0
  90. package/es6/src/webapi/create-api.js +217 -0
  91. package/es6/src/webapi/devcontainer/codebase-rag-mcp.js +156 -0
  92. package/es6/src/webapi/devcontainer/codebase-rag-mcp.ts +182 -0
  93. package/es6/src/webapi/devcontainer/host-scripts.js +31 -0
  94. package/es6/src/webapi/devcontainer/templates/codebase-rag.env.example +3 -0
  95. package/es6/src/webapi/devcontainer/templates/initialize.sh +169 -0
  96. package/es6/src/webapi/devcontainer/templates/post-create.sh +35 -0
  97. package/es6/src/webapi/devcontainer/templates/post-start.sh +65 -0
  98. package/es6/src/webapi/devcontainer/templates/tty-prompt.sh +38 -0
  99. package/es6/src/webapi/features/auth.feature.js +390 -0
  100. package/es6/src/webapi/features/auth0-cli.js +375 -0
  101. package/es6/src/webapi/features/biome-format.js +42 -0
  102. package/es6/src/webapi/features/database.feature.js +423 -0
  103. package/es6/src/webapi/features/finalize.feature.js +85 -0
  104. package/es6/src/webapi/features/postman.feature.js +258 -0
  105. package/es6/src/webapi/models/api-package-json.model.js +55 -0
  106. package/es6/src/webapi/models/auth-config.model.js +22 -0
  107. package/es6/src/webapi/models/controller-source-file.model.js +184 -0
  108. package/es6/src/webapi/models/github-ci.model.js +186 -0
  109. package/es6/src/webapi/models/main-source-file.model.js +163 -0
  110. package/es6/src/webapi/models/production-dockerfile.model.js +148 -0
  111. package/es6/src/webapi/models/sample-files.model.js +42 -0
  112. package/es6/src/webapi/models/webapi-image.model.js +20 -0
  113. package/es6/src/webapi/postman/hello-openapi.js +168 -0
  114. package/es6/src/webapi/postman/local-view.js +169 -0
  115. package/es6/src/webapi/postman/openapi-to-postman.js +179 -0
  116. package/es6/src/webapi/workspace-readme.js +89 -0
  117. package/es6/src/webapi.action.js +20 -0
  118. package/es6/src/webapi.command.js +35 -0
  119. package/package.json +8 -8
  120. package/types/src/add-api.action.d.ts +13 -0
  121. package/types/src/add-api.command.d.ts +7 -0
  122. package/types/src/add.command.d.ts +6 -0
  123. package/types/src/add.module.d.ts +14 -0
  124. package/types/src/cli.d.ts +2 -0
  125. package/types/src/create.command.d.ts +6 -0
  126. package/types/src/create.module.d.ts +14 -0
  127. package/types/src/domain/prompt.d.ts +4 -0
  128. package/types/src/models/compose.model.d.ts +64 -0
  129. package/types/src/models/config-yaml.model.d.ts +27 -0
  130. package/types/src/models/const.d.ts +20 -0
  131. package/types/src/models/dev-container.model.d.ts +37 -0
  132. package/types/src/models/docker-image.model.d.ts +84 -0
  133. package/types/src/models/env-file.model.d.ts +16 -0
  134. package/types/src/models/gitignore.model.d.ts +7 -0
  135. package/types/src/models/launch-json.model.d.ts +7 -0
  136. package/types/src/models/package-json.model.d.ts +35 -0
  137. package/types/src/models/project.model.d.ts +60 -0
  138. package/{src/models/source-file.ts → types/src/models/source-file.d.ts} +4 -6
  139. package/types/src/models/tsconfig.model.d.ts +5 -0
  140. package/types/src/models/vitest-config.model.d.ts +5 -0
  141. package/types/src/models/vscode-settings.model.d.ts +7 -0
  142. package/types/src/program.d.ts +3 -0
  143. package/{src/resolve-target-dir.ts → types/src/resolve-target-dir.d.ts} +1 -4
  144. package/types/src/services/scaffold.service.d.ts +22 -0
  145. package/types/src/version.d.ts +6 -0
  146. package/types/src/webapi/api-name.d.ts +14 -0
  147. package/types/src/webapi/cli-options.d.ts +6 -0
  148. package/{src/webapi/const.ts → types/src/webapi/const.d.ts} +7 -21
  149. package/types/src/webapi/create-api.d.ts +15 -0
  150. package/types/src/webapi/devcontainer/codebase-rag-mcp.d.ts +32 -0
  151. package/types/src/webapi/devcontainer/codebase-rag-mcp.ts +182 -0
  152. package/types/src/webapi/devcontainer/host-scripts.d.ts +7 -0
  153. package/types/src/webapi/devcontainer/templates/codebase-rag.env.example +3 -0
  154. package/types/src/webapi/devcontainer/templates/initialize.sh +169 -0
  155. package/types/src/webapi/devcontainer/templates/post-create.sh +35 -0
  156. package/types/src/webapi/devcontainer/templates/post-start.sh +65 -0
  157. package/types/src/webapi/devcontainer/templates/tty-prompt.sh +38 -0
  158. package/types/src/webapi/features/auth.feature.d.ts +10 -0
  159. package/types/src/webapi/features/auth0-cli.d.ts +80 -0
  160. package/types/src/webapi/features/biome-format.d.ts +5 -0
  161. package/types/src/webapi/features/database.feature.d.ts +7 -0
  162. package/types/src/webapi/features/finalize.feature.d.ts +2 -0
  163. package/types/src/webapi/features/postman.feature.d.ts +16 -0
  164. package/types/src/webapi/models/api-package-json.model.d.ts +18 -0
  165. package/types/src/webapi/models/auth-config.model.d.ts +26 -0
  166. package/types/src/webapi/models/controller-source-file.model.d.ts +46 -0
  167. package/types/src/webapi/models/github-ci.model.d.ts +9 -0
  168. package/types/src/webapi/models/main-source-file.model.d.ts +54 -0
  169. package/types/src/webapi/models/production-dockerfile.model.d.ts +23 -0
  170. package/types/src/webapi/models/sample-files.model.d.ts +7 -0
  171. package/types/src/webapi/models/webapi-image.model.d.ts +12 -0
  172. package/types/src/webapi/postman/hello-openapi.d.ts +19 -0
  173. package/types/src/webapi/postman/local-view.d.ts +17 -0
  174. package/types/src/webapi/postman/openapi-to-postman.d.ts +62 -0
  175. package/types/src/webapi/workspace-readme.d.ts +18 -0
  176. package/types/src/webapi.action.d.ts +14 -0
  177. package/types/src/webapi.command.d.ts +11 -0
  178. package/src/add-api.action.ts +0 -25
  179. package/src/add-api.command.ts +0 -31
  180. package/src/add.command.ts +0 -20
  181. package/src/add.module.ts +0 -11
  182. package/src/cli.ts +0 -4
  183. package/src/create.command.ts +0 -20
  184. package/src/create.module.ts +0 -11
  185. package/src/domain/prompt.ts +0 -50
  186. package/src/models/compose.model.ts +0 -205
  187. package/src/models/config-yaml.model.ts +0 -83
  188. package/src/models/const.ts +0 -20
  189. package/src/models/dev-container.model.ts +0 -136
  190. package/src/models/docker-image.model.ts +0 -343
  191. package/src/models/env-file.model.ts +0 -45
  192. package/src/models/gitignore.model.ts +0 -35
  193. package/src/models/launch-json.model.ts +0 -60
  194. package/src/models/package-json.model.ts +0 -169
  195. package/src/models/project.model.ts +0 -141
  196. package/src/models/tsconfig.model.ts +0 -29
  197. package/src/models/vitest-config.model.ts +0 -26
  198. package/src/models/vscode-settings.model.ts +0 -24
  199. package/src/program.ts +0 -63
  200. package/src/services/scaffold.service.ts +0 -89
  201. package/src/version.ts +0 -56
  202. package/src/webapi/api-name.ts +0 -73
  203. package/src/webapi/cli-options.ts +0 -56
  204. package/src/webapi/create-api.ts +0 -275
  205. package/src/webapi/devcontainer/host-scripts.ts +0 -35
  206. package/src/webapi/features/auth.feature.ts +0 -454
  207. package/src/webapi/features/auth0-cli.ts +0 -421
  208. package/src/webapi/features/biome-format.ts +0 -34
  209. package/src/webapi/features/database.feature.ts +0 -522
  210. package/src/webapi/features/finalize.feature.ts +0 -112
  211. package/src/webapi/features/postman.feature.ts +0 -287
  212. package/src/webapi/models/api-package-json.model.ts +0 -60
  213. package/src/webapi/models/auth-config.model.ts +0 -36
  214. package/src/webapi/models/controller-source-file.model.ts +0 -232
  215. package/src/webapi/models/github-ci.model.ts +0 -188
  216. package/src/webapi/models/main-source-file.model.ts +0 -208
  217. package/src/webapi/models/production-dockerfile.model.ts +0 -162
  218. package/src/webapi/models/sample-files.model.ts +0 -45
  219. package/src/webapi/models/webapi-image.model.ts +0 -21
  220. package/src/webapi/postman/hello-openapi.ts +0 -92
  221. package/src/webapi/postman/local-view.ts +0 -195
  222. package/src/webapi/postman/openapi-to-postman.ts +0 -208
  223. package/src/webapi/workspace-readme.ts +0 -192
  224. package/src/webapi.action.ts +0 -22
  225. package/src/webapi.command.ts +0 -41
  226. package/tests/auth-postman.wiring.spec.ts +0 -263
  227. package/tests/auth0-cli.spec.ts +0 -232
  228. package/tests/biome-format.spec.ts +0 -49
  229. package/tests/cli.integration.spec.ts +0 -658
  230. package/tests/compose.model.spec.ts +0 -51
  231. package/tests/container-image.model.spec.ts +0 -162
  232. package/tests/dev-container.model.spec.ts +0 -29
  233. package/tests/devcontainer-base-image.spec.ts +0 -32
  234. package/tests/hello-di-inject.scaffold.spec.ts +0 -18
  235. package/tests/hello-route.contract.spec.ts +0 -94
  236. package/tests/package-json.order.spec.ts +0 -59
  237. package/tests/postman-from-openapi.spec.ts +0 -197
  238. package/tests/seed-devcontainer-templates.ts +0 -40
  239. package/tests/webapi/devcontainer/codebase-rag-mcp.spec.ts +0 -96
  240. /package/{src/index.ts → types/src/index.d.ts} +0 -0
@@ -0,0 +1,433 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "DatabaseFeature", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return DatabaseFeature;
9
+ }
10
+ });
11
+ const _nodecrypto = require("node:crypto");
12
+ const _nodefs = require("node:fs");
13
+ const _nodepath = require("node:path");
14
+ const _prompt = require("../../domain/prompt.js");
15
+ const _const = require("../../models/const.js");
16
+ const _dockerimagemodel = require("../../models/docker-image.model.js");
17
+ const _version = require("../../version.js");
18
+ const _apiname = require("../api-name.js");
19
+ /** Bind path relative to `.devcontainer/docker-compose.yml` (dies with the working tree). */ const POSTGRES_DATA_BIND = "./postgres-data:/var/lib/postgresql/data";
20
+ const POSTGRES_DATA_GITIGNORE = ".devcontainer/postgres-data";
21
+ const DB_RESET_SCRIPT = "rm -rf .devcontainer/postgres-data && docker compose -f .devcontainer/docker-compose.yml down && docker compose -f .devcontainer/docker-compose.yml up -d postgres";
22
+ /** Read a single KEY= from an existing target `.env` (if any). */ function readExistingEnvValue(workspace, key) {
23
+ try {
24
+ const raw = (0, _nodefs.readFileSync)((0, _nodepath.join)(workspace.targetDir, ".env"), "utf8");
25
+ const line = raw.split("\n").find((l)=>l.startsWith(`${key}=`));
26
+ const value = line?.slice(key.length + 1).trim();
27
+ return value || undefined;
28
+ } catch {
29
+ return undefined;
30
+ }
31
+ }
32
+ function randomPassword() {
33
+ return (0, _nodecrypto.randomBytes)(16).toString("hex");
34
+ }
35
+ /** Safe Postgres identifier fragment from the project name. */ function toSqlIdentFragment(name) {
36
+ const cleaned = name.replace(/[^A-Za-z0-9_]/g, "_").replace(/^([^A-Za-z_])/, "p_$1");
37
+ return cleaned.length > 0 ? cleaned.slice(0, 40) : "app";
38
+ }
39
+ function passwordOrExisting(workspace, key, fallbackExistingKeys = []) {
40
+ const existing = readExistingEnvValue(workspace, key);
41
+ if (existing) return existing;
42
+ for (const alt of fallbackExistingKeys){
43
+ const v = readExistingEnvValue(workspace, alt);
44
+ if (v) return v;
45
+ }
46
+ return randomPassword();
47
+ }
48
+ // Production: runtime app role only — no bootstrap, no admin/migrate secrets required.
49
+ function renderBaseDataSource(apiName) {
50
+ const p = (0, _apiname.apiEnvPrefix)(apiName);
51
+ return [
52
+ ` - name: ${apiName}`,
53
+ ` provider: postgres`,
54
+ ` host: \${${p}_DB_HOST}`,
55
+ ` port: \${${p}_DB_PORT}`,
56
+ ` database: \${${p}_DB_NAME}`,
57
+ ` username: \${${p}_DB_USER}`,
58
+ ` password: \${${p}_DB_PASSWORD}`,
59
+ ` synchronize: false`,
60
+ ` seed: false`,
61
+ ` bootstrap: false`
62
+ ].join("\n");
63
+ }
64
+ // Dev overlay (merged by list index onto config.yaml). Enable bootstrap + migrate role.
65
+ function renderDevDataSource(apiName) {
66
+ const p = (0, _apiname.apiEnvPrefix)(apiName);
67
+ return [
68
+ ` - name: ${apiName}`,
69
+ ` synchronize: true`,
70
+ ` seed: true`,
71
+ ` bootstrap: true`,
72
+ ` adminUser: \${DB_ADMIN_USER}`,
73
+ ` adminPassword: \${DB_ADMIN_PASSWORD}`,
74
+ ` migrateUsername: \${${p}_DB_MIGRATE_USER}`,
75
+ ` migratePassword: \${${p}_DB_MIGRATE_PASSWORD}`
76
+ ].join("\n");
77
+ }
78
+ function renderDatabaseSection(apiNames, items) {
79
+ return `database:\n dataSources:\n${apiNames.map(items).join("\n")}\n`;
80
+ }
81
+ /** Create-or-sync bootstrap role; runs on every Postgres container start (not only empty volume). */ const POSTGRES_ENSURE_BOOTSTRAP_SH = `#!/bin/bash
82
+ set -euo pipefail
83
+ # Superuser aligns decohere_bootstrap with DB_ADMIN_* from env_file on every start.
84
+ # Initdb.d alone is insufficient: passwords in .env can change while the volume persists.
85
+ : "\${POSTGRES_USER:?POSTGRES_USER is required}"
86
+ : "\${DB_ADMIN_USER:?DB_ADMIN_USER is required}"
87
+ : "\${DB_ADMIN_PASSWORD:?DB_ADMIN_PASSWORD is required}"
88
+
89
+ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname postgres \\
90
+ \t--set=bootstrap_user="$DB_ADMIN_USER" \\
91
+ \t--set=bootstrap_password="$DB_ADMIN_PASSWORD" <<'EOSQL'
92
+ SELECT format(
93
+ \t'CREATE ROLE %I LOGIN PASSWORD %L CREATEDB CREATEROLE NOSUPERUSER NOBYPASSRLS',
94
+ \t:'bootstrap_user',
95
+ \t:'bootstrap_password'
96
+ )
97
+ WHERE NOT EXISTS (
98
+ \tSELECT 1 FROM pg_catalog.pg_roles WHERE rolname = :'bootstrap_user'
99
+ )\\gexec
100
+
101
+ SELECT format(
102
+ \t'ALTER ROLE %I WITH LOGIN PASSWORD %L CREATEDB CREATEROLE NOSUPERUSER NOBYPASSRLS',
103
+ \t:'bootstrap_user',
104
+ \t:'bootstrap_password'
105
+ )\\gexec
106
+
107
+ GRANT CONNECT ON DATABASE postgres TO :"bootstrap_user";
108
+ EOSQL
109
+ `;
110
+ const POSTGRES_ENTRYPOINT_SH = `#!/bin/bash
111
+ set -euo pipefail
112
+ (
113
+ \tfor _ in $(seq 1 60); do
114
+ \t\tif pg_isready -U "\${POSTGRES_USER:-postgres}" >/dev/null 2>&1; then
115
+ \t\t\t/usr/local/bin/decohere-ensure-bootstrap.sh
116
+ \t\t\texit 0
117
+ \t\tfi
118
+ \t\tsleep 0.5
119
+ \tdone
120
+ \techo "decohere: timed out waiting for Postgres before ensuring bootstrap role" >&2
121
+ \texit 1
122
+ ) &
123
+ exec docker-entrypoint.sh "$@"
124
+ `;
125
+ const POSTGRES_IMAGE_DOCKERFILE = `FROM postgres:16
126
+ COPY ensure-bootstrap-role.sh /usr/local/bin/decohere-ensure-bootstrap.sh
127
+ COPY entrypoint.sh /usr/local/bin/decohere-postgres-entrypoint.sh
128
+ RUN chmod +x /usr/local/bin/decohere-ensure-bootstrap.sh /usr/local/bin/decohere-postgres-entrypoint.sh
129
+ ENTRYPOINT ["/usr/local/bin/decohere-postgres-entrypoint.sh"]
130
+ CMD ["postgres"]
131
+ `;
132
+ const SAMPLE_ENTITY_TS = `import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
133
+ import type { Entity as IEntity } from "@decohere/codex";
134
+
135
+ @Entity()
136
+ export class SampleEntity implements IEntity {
137
+ \t@PrimaryGeneratedColumn()
138
+ \tpublic id!: number;
139
+
140
+ \t@Column()
141
+ \tpublic name!: string;
142
+ }
143
+ `;
144
+ const SAMPLE_REPOSITORY_TS = `import { injectable } from "@decohere/crucible/decorators";
145
+ import { injectUnitOfWork } from "@decohere/codex";
146
+ import { TypeOrmRepository, TypeOrmUnitOfWork } from "@decohere/codex-typeorm";
147
+ import { SampleEntity } from "../entities/sample.entity.js";
148
+
149
+ @injectable()
150
+ export class SampleEntityRepository extends TypeOrmRepository<SampleEntity> {
151
+ \tpublic constructor(@injectUnitOfWork() unitOfWork: TypeOrmUnitOfWork) {
152
+ \t\tsuper(unitOfWork, SampleEntity);
153
+ \t}
154
+ }
155
+ `;
156
+ /** TypeORM CLI DataSource — connects as the migrate role; never enables synchronize. */ function renderTypeOrmDataSource(apiName) {
157
+ const p = (0, _apiname.apiEnvPrefix)(apiName);
158
+ return `import { dirname, join } from "node:path";
159
+ import { fileURLToPath } from "node:url";
160
+ import { DataSource } from "typeorm";
161
+
162
+ const root = dirname(fileURLToPath(import.meta.url));
163
+ const required = (key: string): string => {
164
+ \tconst value = process.env[key];
165
+ \tif (value == null || value === "") {
166
+ \t\tthrow new Error(\`Missing env \${key} (load .env via --env-file or export it)\`);
167
+ \t}
168
+ \treturn value;
169
+ };
170
+
171
+ /**
172
+ * CLI-only DataSource for \`pnpm db:migrate\` / \`pnpm db:migrate:generate\`.
173
+ * Uses the migrate role (DDL). Runtime API pods use the app role with synchronize: false.
174
+ */
175
+ export default new DataSource({
176
+ \ttype: "postgres",
177
+ \thost: required("${p}_DB_HOST"),
178
+ \tport: Number(required("${p}_DB_PORT")),
179
+ \tdatabase: required("${p}_DB_NAME"),
180
+ \tusername: required("${p}_DB_MIGRATE_USER"),
181
+ \tpassword: required("${p}_DB_MIGRATE_PASSWORD"),
182
+ \tsynchronize: false,
183
+ \tentities: [join(root, "entities", "*.{ts,js}")],
184
+ \tmigrations: [join(root, "migrations", "*.{ts,js}")],
185
+ });
186
+ `;
187
+ }
188
+ const DATABASE_DOCS_MD = `# Database (Postgres / TypeORM)
189
+
190
+ ## Roles
191
+
192
+ | Role | Env | Purpose |
193
+ |---|---|---|
194
+ | App | \`{PREFIX}_DB_USER\` | Runtime API (DML only in production) |
195
+ | Migrate | \`{PREFIX}_DB_MIGRATE_USER\` | Schema changes / TypeORM migrations (CI/deploy) |
196
+ | Bootstrap | \`DB_ADMIN_USER\` | Create DB + roles when \`bootstrap: true\` (dev) |
197
+
198
+ \`{PREFIX}\` is the API slug in SCREAMING_SNAKE (\`api\` → \`API\`, \`billing\` → \`BILLING\`).
199
+
200
+ ## Development (fast iteration)
201
+
202
+ \`config/config.dev.yaml\` sets \`synchronize: true\` and \`seed: true\` so the API can create tables on startup. You can keep using sync while prototyping.
203
+
204
+ Alternatively, turn sync off in the overlay and use migrations the same way as production:
205
+
206
+ \`\`\`bash
207
+ pnpm db:up
208
+ pnpm db:migrate
209
+ pnpm db:migrate:generate -- ./projects/api/src/migrations/AddSomething
210
+ \`\`\`
211
+
212
+ Do **not** mix sync and migrations for the same schema change (pick one path per change).
213
+
214
+ ## Production (migrations only)
215
+
216
+ \`config/config.yaml\` keeps \`synchronize: false\` / \`seed: false\` / \`bootstrap: false\`. Deploy steps:
217
+
218
+ 1. Ensure the app database and roles already exist (provisioner / ops — not the API pod).
219
+ 2. Run migrations with the migrate role: \`pnpm db:migrate\` (or \`pnpm db:migrate:<api>\` for multi-API).
220
+ 3. Start the API with the app role only — no migrate/admin secrets on the pod.
221
+
222
+ ## Scripts
223
+
224
+ | Script | What it does |
225
+ |---|---|
226
+ | \`db:up\` / \`db:down\` / \`db:reset\` | Local Compose Postgres |
227
+ | \`db:migrate\` | Run pending migrations (\`projects/api\` by default) |
228
+ | \`db:migrate:generate\` | Generate a migration from entity drift (pass path after \`--\`) |
229
+ | \`db:migrate:<api>\` | Same for a named API under \`projects/<api>\` |
230
+
231
+ CLI entry: \`projects/<api>/src/typeorm.data-source.ts\` (migrate credentials, \`synchronize: false\`).
232
+
233
+ ## Browse the database
234
+
235
+ Prefer the Dev Container **SQLTools** extension (Postgres driver) or any desktop client against \`127.0.0.1:5433\` (see \`.env\` for credentials).
236
+
237
+ Adminer is **not** scaffolded. If you want a browser UI in Compose, add this under \`services:\` in \`.devcontainer/docker-compose.yml\` (profile \`tools\` so it stays off by default):
238
+
239
+ \`\`\`yaml
240
+ adminer:
241
+ image: adminer
242
+ profiles:
243
+ - tools
244
+ ports:
245
+ - "127.0.0.1:8081:8080"
246
+ networks:
247
+ - <compose-network-key> # same key as postgres / devcontainer
248
+ \`\`\`
249
+
250
+ Then: \`docker compose -f .devcontainer/docker-compose.yml --profile tools up -d adminer\`
251
+ `;
252
+ function typeormCli(args) {
253
+ return `node --import tsx --env-file=.env ./node_modules/typeorm/cli.js ${args}`;
254
+ }
255
+ function migrateRunScript(apiName) {
256
+ return typeormCli(`migration:run -d ./projects/${apiName}/src/typeorm.data-source.ts`);
257
+ }
258
+ function migrateGenerateScript(apiName) {
259
+ return typeormCli(`migration:generate --esm -d ./projects/${apiName}/src/typeorm.data-source.ts`);
260
+ }
261
+ async function getOptions(_workspace, cliOptions) {
262
+ if (cliOptions.databaseProvider != null) return {};
263
+ const choices = [
264
+ {
265
+ label: "none"
266
+ },
267
+ {
268
+ label: "postgres/typeorm"
269
+ }
270
+ ];
271
+ const selected = await (0, _prompt.pick)("\n Database: ", choices, (c)=>c.label);
272
+ return {
273
+ databaseProvider: selected?.label ?? "none"
274
+ };
275
+ }
276
+ function scaffold(workspace, options) {
277
+ if (options.databaseProvider !== "postgres/typeorm") return;
278
+ const name = workspace.name;
279
+ const ident = toSqlIdentFragment(name);
280
+ const apiProjects = workspace.getProjects();
281
+ const apiNames = apiProjects.map((project)=>(0, _apiname.apiNameFromProjectDir)(project.dir));
282
+ // Container init superuser — never reused as an API runtime role.
283
+ const postgresSuperuser = "postgres";
284
+ const postgresPassword = passwordOrExisting(workspace, "POSTGRES_PASSWORD");
285
+ const bootstrapUser = "decohere_bootstrap";
286
+ const bootstrapPassword = passwordOrExisting(workspace, "DB_ADMIN_PASSWORD", [
287
+ "POSTGRES_PASSWORD"
288
+ ]);
289
+ const devContainer = workspace.getModel(_const.WELL_KNOWN_FILES.DEVCONTAINER_JSON);
290
+ const compose = workspace.getModel(_const.WELL_KNOWN_FILES.DOCKER_COMPOSE);
291
+ const networkKey = name;
292
+ if (compose) {
293
+ // One shared instance. Each API owns a database on it (created at bootstrap).
294
+ // Point `{PREFIX}_DB_HOST` / `{PREFIX}_DB_PORT` at another server to split later.
295
+ // Custom image: sync bootstrap role password on every start (initdb.d is once-only).
296
+ // Default data is a bind mount under the project so `rm -rf` the tree removes it.
297
+ compose.addHeaderComment("Postgres data defaults to ./postgres-data under this folder (bind mount; dies with the working tree).", `For data that outlives the checkout, replace the postgres volumes entry with a named volume`, `e.g. "${name}-postgres-data:/var/lib/postgresql/data" and declare it under top-level volumes:.`).addService("postgres", {
298
+ build: "./postgres",
299
+ container_name: `samples-${name}-postgres`,
300
+ restart: "unless-stopped",
301
+ ports: [
302
+ "127.0.0.1:5433:5432"
303
+ ],
304
+ volumes: [
305
+ POSTGRES_DATA_BIND
306
+ ],
307
+ networks: [
308
+ {
309
+ [networkKey]: {
310
+ aliases: [
311
+ "postgres"
312
+ ]
313
+ }
314
+ }
315
+ ],
316
+ healthcheck: {
317
+ // Ensure role/password match .env before reporting healthy.
318
+ test: [
319
+ "CMD-SHELL",
320
+ `pg_isready -U ${postgresSuperuser} && /usr/local/bin/decohere-ensure-bootstrap.sh`
321
+ ],
322
+ interval: "2s",
323
+ timeout: "5s",
324
+ retries: 15
325
+ }
326
+ }).addEnvFile("postgres", "../.env");
327
+ workspace.getModel(_const.WELL_KNOWN_FILES.GITIGNORE)?.add(POSTGRES_DATA_GITIGNORE);
328
+ workspace.addFile({
329
+ files: ()=>[
330
+ {
331
+ filePath: ".devcontainer/postgres/Dockerfile",
332
+ content: POSTGRES_IMAGE_DOCKERFILE
333
+ },
334
+ {
335
+ filePath: ".devcontainer/postgres/entrypoint.sh",
336
+ content: POSTGRES_ENTRYPOINT_SH
337
+ },
338
+ {
339
+ filePath: ".devcontainer/postgres/ensure-bootstrap-role.sh",
340
+ content: POSTGRES_ENSURE_BOOTSTRAP_SH
341
+ }
342
+ ]
343
+ });
344
+ compose.addEnvFile("devcontainer", "../.env");
345
+ }
346
+ devContainer?.addExtension("mtxr.sqltools", "mtxr.sqltools-driver-pg");
347
+ workspace.getModel(_const.WELL_KNOWN_FILES.DOCKERFILE)?.addLayer((0, _dockerimagemodel.withPostgresqlClient)());
348
+ const pkg = workspace.getModel(_const.WELL_KNOWN_FILES.PACKAGE_JSON)?.addDependency("@decohere/codex", _version.CREATE_VERSION).addDependency("typeorm", "^0.3.28").addDependency("@decohere/codex-typeorm", _version.CREATE_VERSION).addDependency("tsx", "^4.20.5").addScript("db:up", "docker compose -f .devcontainer/docker-compose.yml up -d postgres").addScript("db:down", "docker compose -f .devcontainer/docker-compose.yml down").addScript("db:reset", DB_RESET_SCRIPT);
349
+ const primaryApi = apiNames[0];
350
+ if (pkg != null) {
351
+ for (const apiName of apiNames){
352
+ pkg.addScript(`db:migrate:${apiName}`, migrateRunScript(apiName)).addScript(`db:migrate:generate:${apiName}`, migrateGenerateScript(apiName));
353
+ }
354
+ // Default aliases target the first API of a new workspace only — never overwrite on `webapi add`.
355
+ if (!options.add && primaryApi != null) {
356
+ pkg.addScript("db:migrate", migrateRunScript(primaryApi)).addScript("db:migrate:generate", migrateGenerateScript(primaryApi));
357
+ }
358
+ }
359
+ workspace.addFile({
360
+ files: ()=>[
361
+ {
362
+ filePath: "docs/database.md",
363
+ content: DATABASE_DOCS_MD
364
+ }
365
+ ]
366
+ });
367
+ writeDatabaseConfig(workspace, apiNames);
368
+ for (const project of apiProjects){
369
+ wireApiDatabase(project);
370
+ }
371
+ const env = workspace.getModel(_const.WELL_KNOWN_FILES.ENV_FILE);
372
+ env?.addEntry("POSTGRES_DB", ident).addEntry("POSTGRES_USER", postgresSuperuser).addEntry("POSTGRES_PASSWORD", postgresPassword).addEntry("DB_ADMIN_USER", bootstrapUser).addEntry("DB_ADMIN_PASSWORD", bootstrapPassword);
373
+ for (const apiName of apiNames){
374
+ writeApiDatabaseEnv(workspace, ident, apiName);
375
+ }
376
+ }
377
+ function writeDatabaseConfig(workspace, apiNames) {
378
+ const config = workspace.getModel(_const.WELL_KNOWN_FILES.CONFIG_YAML);
379
+ const devConfig = workspace.getModel(_const.WELL_KNOWN_FILES.CONFIG_DEV_YAML);
380
+ const pending = apiNames.filter((apiName)=>!config?.hasDataSource(apiName));
381
+ if (pending.length === 0) return;
382
+ if (config?.hasDataSources()) {
383
+ for (const apiName of pending){
384
+ config.appendDataSource(renderBaseDataSource(apiName));
385
+ devConfig?.appendDataSource(renderDevDataSource(apiName));
386
+ }
387
+ return;
388
+ }
389
+ config?.addSection(renderDatabaseSection(pending, renderBaseDataSource));
390
+ devConfig?.addSection(renderDatabaseSection(pending, renderDevDataSource));
391
+ }
392
+ function writeApiDatabaseEnv(workspace, ident, apiName) {
393
+ const prefix = (0, _apiname.apiEnvPrefix)(apiName);
394
+ const sqlApi = toSqlIdentFragment(apiName);
395
+ workspace.getModel(_const.WELL_KNOWN_FILES.ENV_FILE)?.addEntry(`${prefix}_DB_HOST`, "postgres").addEntry(`${prefix}_DB_PORT`, "5432").addEntry(`${prefix}_DB_NAME`, `${ident}_${sqlApi}_dev`).addEntry(`${prefix}_DB_USER`, `${ident}_${sqlApi}_app`).addEntry(`${prefix}_DB_PASSWORD`, passwordOrExisting(workspace, `${prefix}_DB_PASSWORD`)).addEntry(`${prefix}_DB_MIGRATE_USER`, `${ident}_${sqlApi}_migrate`).addEntry(`${prefix}_DB_MIGRATE_PASSWORD`, passwordOrExisting(workspace, `${prefix}_DB_MIGRATE_PASSWORD`));
396
+ }
397
+ function wireApiDatabase(project) {
398
+ const apiName = (0, _apiname.apiNameFromProjectDir)(project.dir);
399
+ project.getModel(_const.WELL_KNOWN_FILES.MAIN_TS)?.setDataSourceName(apiName).addSideEffectImport("@decohere/codex-typeorm").addEntity({
400
+ className: "SampleEntity",
401
+ importPath: "./entities/sample.entity.js"
402
+ }).addRepository({
403
+ className: "SampleEntityRepository",
404
+ importPath: "./repositories/sample.repository.js"
405
+ });
406
+ project.getModel(_const.WELL_KNOWN_FILES.API_PACKAGE_JSON)?.addDependency("@decohere/codex", _version.CREATE_VERSION).addDependency("@decohere/codex-typeorm", _version.CREATE_VERSION);
407
+ project.addFile({
408
+ files: ()=>[
409
+ {
410
+ filePath: "src/entities/sample.entity.ts",
411
+ content: SAMPLE_ENTITY_TS
412
+ },
413
+ {
414
+ filePath: "src/repositories/sample.repository.ts",
415
+ content: SAMPLE_REPOSITORY_TS
416
+ },
417
+ {
418
+ filePath: "src/typeorm.data-source.ts",
419
+ content: renderTypeOrmDataSource(apiName)
420
+ },
421
+ {
422
+ filePath: "src/migrations/.gitkeep",
423
+ content: ""
424
+ }
425
+ ]
426
+ });
427
+ }
428
+ const DatabaseFeature = {
429
+ getOptions,
430
+ scaffold
431
+ };
432
+
433
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9ob21lL3J1bm5lci93b3JrL2RlY29oZXJlL2RlY29oZXJlL3Byb2plY3RzL3NjYWZmb2xkL3NyYy93ZWJhcGkvZmVhdHVyZXMvZGF0YWJhc2UuZmVhdHVyZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyByYW5kb21CeXRlcyB9IGZyb20gXCJub2RlOmNyeXB0b1wiO1xuaW1wb3J0IHsgcmVhZEZpbGVTeW5jIH0gZnJvbSBcIm5vZGU6ZnNcIjtcbmltcG9ydCB7IGpvaW4gfSBmcm9tIFwibm9kZTpwYXRoXCI7XG5pbXBvcnQgeyBwaWNrIH0gZnJvbSBcIi4uLy4uL2RvbWFpbi9wcm9tcHQuanNcIjtcbmltcG9ydCB0eXBlIHsgRG9ja2VyQ29tcG9zZSB9IGZyb20gXCIuLi8uLi9tb2RlbHMvY29tcG9zZS5tb2RlbC5qc1wiO1xuaW1wb3J0IHR5cGUgeyBDb25maWdZYW1sIH0gZnJvbSBcIi4uLy4uL21vZGVscy9jb25maWcteWFtbC5tb2RlbC5qc1wiO1xuaW1wb3J0IHsgV0VMTF9LTk9XTl9GSUxFUyB9IGZyb20gXCIuLi8uLi9tb2RlbHMvY29uc3QuanNcIjtcbmltcG9ydCB0eXBlIHsgRGV2Q29udGFpbmVyIH0gZnJvbSBcIi4uLy4uL21vZGVscy9kZXYtY29udGFpbmVyLm1vZGVsLmpzXCI7XG5pbXBvcnQge1xuXHR0eXBlIERvY2tlcmZpbGVNb2RlbCxcblx0d2l0aFBvc3RncmVzcWxDbGllbnQsXG59IGZyb20gXCIuLi8uLi9tb2RlbHMvZG9ja2VyLWltYWdlLm1vZGVsLmpzXCI7XG5pbXBvcnQgdHlwZSB7IEVudkZpbGUgfSBmcm9tIFwiLi4vLi4vbW9kZWxzL2Vudi1maWxlLm1vZGVsLmpzXCI7XG5pbXBvcnQgdHlwZSB7IEdpdGlnbm9yZSB9IGZyb20gXCIuLi8uLi9tb2RlbHMvZ2l0aWdub3JlLm1vZGVsLmpzXCI7XG5pbXBvcnQgdHlwZSB7IFBhY2thZ2VKc29uIH0gZnJvbSBcIi4uLy4uL21vZGVscy9wYWNrYWdlLWpzb24ubW9kZWwuanNcIjtcbmltcG9ydCB0eXBlIHsgUHJvamVjdFNjYWZmb2xkLCBXb3Jrc3BhY2VTY2FmZm9sZCB9IGZyb20gXCIuLi8uLi9tb2RlbHMvcHJvamVjdC5tb2RlbC5qc1wiO1xuaW1wb3J0IHR5cGUgeyBGZWF0dXJlIH0gZnJvbSBcIi4uLy4uL3NlcnZpY2VzL3NjYWZmb2xkLnNlcnZpY2UuanNcIjtcbmltcG9ydCB7IENSRUFURV9WRVJTSU9OIH0gZnJvbSBcIi4uLy4uL3ZlcnNpb24uanNcIjtcbmltcG9ydCB7IGFwaUVudlByZWZpeCwgYXBpTmFtZUZyb21Qcm9qZWN0RGlyIH0gZnJvbSBcIi4uL2FwaS1uYW1lLmpzXCI7XG5pbXBvcnQgdHlwZSB7IEFwaVBhY2thZ2VKc29uIH0gZnJvbSBcIi4uL21vZGVscy9hcGktcGFja2FnZS1qc29uLm1vZGVsLmpzXCI7XG5pbXBvcnQgdHlwZSB7IE1haW5Tb3VyY2VGaWxlIH0gZnJvbSBcIi4uL21vZGVscy9tYWluLXNvdXJjZS1maWxlLm1vZGVsLmpzXCI7XG5cbi8qKiBCaW5kIHBhdGggcmVsYXRpdmUgdG8gYC5kZXZjb250YWluZXIvZG9ja2VyLWNvbXBvc2UueW1sYCAoZGllcyB3aXRoIHRoZSB3b3JraW5nIHRyZWUpLiAqL1xuY29uc3QgUE9TVEdSRVNfREFUQV9CSU5EID0gXCIuL3Bvc3RncmVzLWRhdGE6L3Zhci9saWIvcG9zdGdyZXNxbC9kYXRhXCI7XG5jb25zdCBQT1NUR1JFU19EQVRBX0dJVElHTk9SRSA9IFwiLmRldmNvbnRhaW5lci9wb3N0Z3Jlcy1kYXRhXCI7XG5jb25zdCBEQl9SRVNFVF9TQ1JJUFQgPVxuXHRcInJtIC1yZiAuZGV2Y29udGFpbmVyL3Bvc3RncmVzLWRhdGEgJiYgZG9ja2VyIGNvbXBvc2UgLWYgLmRldmNvbnRhaW5lci9kb2NrZXItY29tcG9zZS55bWwgZG93biAmJiBkb2NrZXIgY29tcG9zZSAtZiAuZGV2Y29udGFpbmVyL2RvY2tlci1jb21wb3NlLnltbCB1cCAtZCBwb3N0Z3Jlc1wiO1xuXG5leHBvcnQgdHlwZSBEYXRhYmFzZU9wdGlvbnMgPSB7XG5cdGRhdGFiYXNlUHJvdmlkZXI/OiBcIm5vbmVcIiB8IFwicG9zdGdyZXMvdHlwZW9ybVwiO1xuXHQvKiogV2hlbiB0cnVlLCBtZXJnZSBpbnRvIGFuIGV4aXN0aW5nIHdvcmtzcGFjZSAoXFxgd2ViYXBpIGFkZFxcYCkuICovXG5cdGFkZD86IGJvb2xlYW47XG59O1xuXG4vKiogUmVhZCBhIHNpbmdsZSBLRVk9IGZyb20gYW4gZXhpc3RpbmcgdGFyZ2V0IGAuZW52YCAoaWYgYW55KS4gKi9cbmZ1bmN0aW9uIHJlYWRFeGlzdGluZ0VudlZhbHVlKHdvcmtzcGFjZTogV29ya3NwYWNlU2NhZmZvbGQsIGtleTogc3RyaW5nKTogc3RyaW5nIHwgdW5kZWZpbmVkIHtcblx0dHJ5IHtcblx0XHRjb25zdCByYXcgPSByZWFkRmlsZVN5bmMoam9pbih3b3Jrc3BhY2UudGFyZ2V0RGlyLCBcIi5lbnZcIiksIFwidXRmOFwiKTtcblx0XHRjb25zdCBsaW5lID0gcmF3LnNwbGl0KFwiXFxuXCIpLmZpbmQoKGwpID0+IGwuc3RhcnRzV2l0aChgJHtrZXl9PWApKTtcblx0XHRjb25zdCB2YWx1ZSA9IGxpbmU/LnNsaWNlKGtleS5sZW5ndGggKyAxKS50cmltKCk7XG5cdFx0cmV0dXJuIHZhbHVlIHx8IHVuZGVmaW5lZDtcblx0fSBjYXRjaCB7XG5cdFx0cmV0dXJuIHVuZGVmaW5lZDtcblx0fVxufVxuXG5mdW5jdGlvbiByYW5kb21QYXNzd29yZCgpOiBzdHJpbmcge1xuXHRyZXR1cm4gcmFuZG9tQnl0ZXMoMTYpLnRvU3RyaW5nKFwiaGV4XCIpO1xufVxuXG4vKiogU2FmZSBQb3N0Z3JlcyBpZGVudGlmaWVyIGZyYWdtZW50IGZyb20gdGhlIHByb2plY3QgbmFtZS4gKi9cbmZ1bmN0aW9uIHRvU3FsSWRlbnRGcmFnbWVudChuYW1lOiBzdHJpbmcpOiBzdHJpbmcge1xuXHRjb25zdCBjbGVhbmVkID0gbmFtZS5yZXBsYWNlKC9bXkEtWmEtejAtOV9dL2csIFwiX1wiKS5yZXBsYWNlKC9eKFteQS1aYS16X10pLywgXCJwXyQxXCIpO1xuXHRyZXR1cm4gY2xlYW5lZC5sZW5ndGggPiAwID8gY2xlYW5lZC5zbGljZSgwLCA0MCkgOiBcImFwcFwiO1xufVxuXG5mdW5jdGlvbiBwYXNzd29yZE9yRXhpc3RpbmcoXG5cdHdvcmtzcGFjZTogV29ya3NwYWNlU2NhZmZvbGQsXG5cdGtleTogc3RyaW5nLFxuXHRmYWxsYmFja0V4aXN0aW5nS2V5czogc3RyaW5nW10gPSBbXSxcbik6IHN0cmluZyB7XG5cdGNvbnN0IGV4aXN0aW5nID0gcmVhZEV4aXN0aW5nRW52VmFsdWUod29ya3NwYWNlLCBrZXkpO1xuXHRpZiAoZXhpc3RpbmcpIHJldHVybiBleGlzdGluZztcblx0Zm9yIChjb25zdCBhbHQgb2YgZmFsbGJhY2tFeGlzdGluZ0tleXMpIHtcblx0XHRjb25zdCB2ID0gcmVhZEV4aXN0aW5nRW52VmFsdWUod29ya3NwYWNlLCBhbHQpO1xuXHRcdGlmICh2KSByZXR1cm4gdjtcblx0fVxuXHRyZXR1cm4gcmFuZG9tUGFzc3dvcmQoKTtcbn1cblxuLy8gUHJvZHVjdGlvbjogcnVudGltZSBhcHAgcm9sZSBvbmx5IOKAlCBubyBib290c3RyYXAsIG5vIGFkbWluL21pZ3JhdGUgc2VjcmV0cyByZXF1aXJlZC5cbmZ1bmN0aW9uIHJlbmRlckJhc2VEYXRhU291cmNlKGFwaU5hbWU6IHN0cmluZyk6IHN0cmluZyB7XG5cdGNvbnN0IHAgPSBhcGlFbnZQcmVmaXgoYXBpTmFtZSk7XG5cdHJldHVybiBbXG5cdFx0YCAgICAtIG5hbWU6ICR7YXBpTmFtZX1gLFxuXHRcdGAgICAgICBwcm92aWRlcjogcG9zdGdyZXNgLFxuXHRcdGAgICAgICBob3N0OiBcXCR7JHtwfV9EQl9IT1NUfWAsXG5cdFx0YCAgICAgIHBvcnQ6IFxcJHske3B9X0RCX1BPUlR9YCxcblx0XHRgICAgICAgZGF0YWJhc2U6IFxcJHske3B9X0RCX05BTUV9YCxcblx0XHRgICAgICAgdXNlcm5hbWU6IFxcJHske3B9X0RCX1VTRVJ9YCxcblx0XHRgICAgICAgcGFzc3dvcmQ6IFxcJHske3B9X0RCX1BBU1NXT1JEfWAsXG5cdFx0YCAgICAgIHN5bmNocm9uaXplOiBmYWxzZWAsXG5cdFx0YCAgICAgIHNlZWQ6IGZhbHNlYCxcblx0XHRgICAgICAgYm9vdHN0cmFwOiBmYWxzZWAsXG5cdF0uam9pbihcIlxcblwiKTtcbn1cblxuLy8gRGV2IG92ZXJsYXkgKG1lcmdlZCBieSBsaXN0IGluZGV4IG9udG8gY29uZmlnLnlhbWwpLiBFbmFibGUgYm9vdHN0cmFwICsgbWlncmF0ZSByb2xlLlxuZnVuY3Rpb24gcmVuZGVyRGV2RGF0YVNvdXJjZShhcGlOYW1lOiBzdHJpbmcpOiBzdHJpbmcge1xuXHRjb25zdCBwID0gYXBpRW52UHJlZml4KGFwaU5hbWUpO1xuXHRyZXR1cm4gW1xuXHRcdGAgICAgLSBuYW1lOiAke2FwaU5hbWV9YCxcblx0XHRgICAgICAgc3luY2hyb25pemU6IHRydWVgLFxuXHRcdGAgICAgICBzZWVkOiB0cnVlYCxcblx0XHRgICAgICAgYm9vdHN0cmFwOiB0cnVlYCxcblx0XHRgICAgICAgYWRtaW5Vc2VyOiBcXCR7REJfQURNSU5fVVNFUn1gLFxuXHRcdGAgICAgICBhZG1pblBhc3N3b3JkOiBcXCR7REJfQURNSU5fUEFTU1dPUkR9YCxcblx0XHRgICAgICAgbWlncmF0ZVVzZXJuYW1lOiBcXCR7JHtwfV9EQl9NSUdSQVRFX1VTRVJ9YCxcblx0XHRgICAgICAgbWlncmF0ZVBhc3N3b3JkOiBcXCR7JHtwfV9EQl9NSUdSQVRFX1BBU1NXT1JEfWAsXG5cdF0uam9pbihcIlxcblwiKTtcbn1cblxuZnVuY3Rpb24gcmVuZGVyRGF0YWJhc2VTZWN0aW9uKFxuXHRhcGlOYW1lczogcmVhZG9ubHkgc3RyaW5nW10sXG5cdGl0ZW1zOiAobmFtZTogc3RyaW5nKSA9PiBzdHJpbmcsXG4pOiBzdHJpbmcge1xuXHRyZXR1cm4gYGRhdGFiYXNlOlxcbiAgZGF0YVNvdXJjZXM6XFxuJHthcGlOYW1lcy5tYXAoaXRlbXMpLmpvaW4oXCJcXG5cIil9XFxuYDtcbn1cblxuLyoqIENyZWF0ZS1vci1zeW5jIGJvb3RzdHJhcCByb2xlOyBydW5zIG9uIGV2ZXJ5IFBvc3RncmVzIGNvbnRhaW5lciBzdGFydCAobm90IG9ubHkgZW1wdHkgdm9sdW1lKS4gKi9cbmNvbnN0IFBPU1RHUkVTX0VOU1VSRV9CT09UU1RSQVBfU0ggPSBgIyEvYmluL2Jhc2hcbnNldCAtZXVvIHBpcGVmYWlsXG4jIFN1cGVydXNlciBhbGlnbnMgZGVjb2hlcmVfYm9vdHN0cmFwIHdpdGggREJfQURNSU5fKiBmcm9tIGVudl9maWxlIG9uIGV2ZXJ5IHN0YXJ0LlxuIyBJbml0ZGIuZCBhbG9uZSBpcyBpbnN1ZmZpY2llbnQ6IHBhc3N3b3JkcyBpbiAuZW52IGNhbiBjaGFuZ2Ugd2hpbGUgdGhlIHZvbHVtZSBwZXJzaXN0cy5cbjogXCJcXCR7UE9TVEdSRVNfVVNFUjo/UE9TVEdSRVNfVVNFUiBpcyByZXF1aXJlZH1cIlxuOiBcIlxcJHtEQl9BRE1JTl9VU0VSOj9EQl9BRE1JTl9VU0VSIGlzIHJlcXVpcmVkfVwiXG46IFwiXFwke0RCX0FETUlOX1BBU1NXT1JEOj9EQl9BRE1JTl9QQVNTV09SRCBpcyByZXF1aXJlZH1cIlxuXG5wc3FsIC12IE9OX0VSUk9SX1NUT1A9MSAtLXVzZXJuYW1lIFwiJFBPU1RHUkVTX1VTRVJcIiAtLWRibmFtZSBwb3N0Z3JlcyBcXFxcXG5cXHQtLXNldD1ib290c3RyYXBfdXNlcj1cIiREQl9BRE1JTl9VU0VSXCIgXFxcXFxuXFx0LS1zZXQ9Ym9vdHN0cmFwX3Bhc3N3b3JkPVwiJERCX0FETUlOX1BBU1NXT1JEXCIgPDwnRU9TUUwnXG5TRUxFQ1QgZm9ybWF0KFxuXFx0J0NSRUFURSBST0xFICVJIExPR0lOIFBBU1NXT1JEICVMIENSRUFURURCIENSRUFURVJPTEUgTk9TVVBFUlVTRVIgTk9CWVBBU1NSTFMnLFxuXFx0Oidib290c3RyYXBfdXNlcicsXG5cXHQ6J2Jvb3RzdHJhcF9wYXNzd29yZCdcbilcbldIRVJFIE5PVCBFWElTVFMgKFxuXFx0U0VMRUNUIDEgRlJPTSBwZ19jYXRhbG9nLnBnX3JvbGVzIFdIRVJFIHJvbG5hbWUgPSA6J2Jvb3RzdHJhcF91c2VyJ1xuKVxcXFxnZXhlY1xuXG5TRUxFQ1QgZm9ybWF0KFxuXFx0J0FMVEVSIFJPTEUgJUkgV0lUSCBMT0dJTiBQQVNTV09SRCAlTCBDUkVBVEVEQiBDUkVBVEVST0xFIE5PU1VQRVJVU0VSIE5PQllQQVNTUkxTJyxcblxcdDonYm9vdHN0cmFwX3VzZXInLFxuXFx0Oidib290c3RyYXBfcGFzc3dvcmQnXG4pXFxcXGdleGVjXG5cbkdSQU5UIENPTk5FQ1QgT04gREFUQUJBU0UgcG9zdGdyZXMgVE8gOlwiYm9vdHN0cmFwX3VzZXJcIjtcbkVPU1FMXG5gO1xuXG5jb25zdCBQT1NUR1JFU19FTlRSWVBPSU5UX1NIID0gYCMhL2Jpbi9iYXNoXG5zZXQgLWV1byBwaXBlZmFpbFxuKFxuXFx0Zm9yIF8gaW4gJChzZXEgMSA2MCk7IGRvXG5cXHRcXHRpZiBwZ19pc3JlYWR5IC1VIFwiXFwke1BPU1RHUkVTX1VTRVI6LXBvc3RncmVzfVwiID4vZGV2L251bGwgMj4mMTsgdGhlblxuXFx0XFx0XFx0L3Vzci9sb2NhbC9iaW4vZGVjb2hlcmUtZW5zdXJlLWJvb3RzdHJhcC5zaFxuXFx0XFx0XFx0ZXhpdCAwXG5cXHRcXHRmaVxuXFx0XFx0c2xlZXAgMC41XG5cXHRkb25lXG5cXHRlY2hvIFwiZGVjb2hlcmU6IHRpbWVkIG91dCB3YWl0aW5nIGZvciBQb3N0Z3JlcyBiZWZvcmUgZW5zdXJpbmcgYm9vdHN0cmFwIHJvbGVcIiA+JjJcblxcdGV4aXQgMVxuKSAmXG5leGVjIGRvY2tlci1lbnRyeXBvaW50LnNoIFwiJEBcIlxuYDtcblxuY29uc3QgUE9TVEdSRVNfSU1BR0VfRE9DS0VSRklMRSA9IGBGUk9NIHBvc3RncmVzOjE2XG5DT1BZIGVuc3VyZS1ib290c3RyYXAtcm9sZS5zaCAvdXNyL2xvY2FsL2Jpbi9kZWNvaGVyZS1lbnN1cmUtYm9vdHN0cmFwLnNoXG5DT1BZIGVudHJ5cG9pbnQuc2ggL3Vzci9sb2NhbC9iaW4vZGVjb2hlcmUtcG9zdGdyZXMtZW50cnlwb2ludC5zaFxuUlVOIGNobW9kICt4IC91c3IvbG9jYWwvYmluL2RlY29oZXJlLWVuc3VyZS1ib290c3RyYXAuc2ggL3Vzci9sb2NhbC9iaW4vZGVjb2hlcmUtcG9zdGdyZXMtZW50cnlwb2ludC5zaFxuRU5UUllQT0lOVCBbXCIvdXNyL2xvY2FsL2Jpbi9kZWNvaGVyZS1wb3N0Z3Jlcy1lbnRyeXBvaW50LnNoXCJdXG5DTUQgW1wicG9zdGdyZXNcIl1cbmA7XG5cbmNvbnN0IFNBTVBMRV9FTlRJVFlfVFMgPSBgaW1wb3J0IHsgQ29sdW1uLCBFbnRpdHksIFByaW1hcnlHZW5lcmF0ZWRDb2x1bW4gfSBmcm9tIFwidHlwZW9ybVwiO1xuaW1wb3J0IHR5cGUgeyBFbnRpdHkgYXMgSUVudGl0eSB9IGZyb20gXCJAZGVjb2hlcmUvY29kZXhcIjtcblxuQEVudGl0eSgpXG5leHBvcnQgY2xhc3MgU2FtcGxlRW50aXR5IGltcGxlbWVudHMgSUVudGl0eSB7XG5cXHRAUHJpbWFyeUdlbmVyYXRlZENvbHVtbigpXG5cXHRwdWJsaWMgaWQhOiBudW1iZXI7XG5cblxcdEBDb2x1bW4oKVxuXFx0cHVibGljIG5hbWUhOiBzdHJpbmc7XG59XG5gO1xuXG5jb25zdCBTQU1QTEVfUkVQT1NJVE9SWV9UUyA9IGBpbXBvcnQgeyBpbmplY3RhYmxlIH0gZnJvbSBcIkBkZWNvaGVyZS9jcnVjaWJsZS9kZWNvcmF0b3JzXCI7XG5pbXBvcnQgeyBpbmplY3RVbml0T2ZXb3JrIH0gZnJvbSBcIkBkZWNvaGVyZS9jb2RleFwiO1xuaW1wb3J0IHsgVHlwZU9ybVJlcG9zaXRvcnksIFR5cGVPcm1Vbml0T2ZXb3JrIH0gZnJvbSBcIkBkZWNvaGVyZS9jb2RleC10eXBlb3JtXCI7XG5pbXBvcnQgeyBTYW1wbGVFbnRpdHkgfSBmcm9tIFwiLi4vZW50aXRpZXMvc2FtcGxlLmVudGl0eS5qc1wiO1xuXG5AaW5qZWN0YWJsZSgpXG5leHBvcnQgY2xhc3MgU2FtcGxlRW50aXR5UmVwb3NpdG9yeSBleHRlbmRzIFR5cGVPcm1SZXBvc2l0b3J5PFNhbXBsZUVudGl0eT4ge1xuXFx0cHVibGljIGNvbnN0cnVjdG9yKEBpbmplY3RVbml0T2ZXb3JrKCkgdW5pdE9mV29yazogVHlwZU9ybVVuaXRPZldvcmspIHtcblxcdFxcdHN1cGVyKHVuaXRPZldvcmssIFNhbXBsZUVudGl0eSk7XG5cXHR9XG59XG5gO1xuXG4vKiogVHlwZU9STSBDTEkgRGF0YVNvdXJjZSDigJQgY29ubmVjdHMgYXMgdGhlIG1pZ3JhdGUgcm9sZTsgbmV2ZXIgZW5hYmxlcyBzeW5jaHJvbml6ZS4gKi9cbmZ1bmN0aW9uIHJlbmRlclR5cGVPcm1EYXRhU291cmNlKGFwaU5hbWU6IHN0cmluZyk6IHN0cmluZyB7XG5cdGNvbnN0IHAgPSBhcGlFbnZQcmVmaXgoYXBpTmFtZSk7XG5cdHJldHVybiBgaW1wb3J0IHsgZGlybmFtZSwgam9pbiB9IGZyb20gXCJub2RlOnBhdGhcIjtcbmltcG9ydCB7IGZpbGVVUkxUb1BhdGggfSBmcm9tIFwibm9kZTp1cmxcIjtcbmltcG9ydCB7IERhdGFTb3VyY2UgfSBmcm9tIFwidHlwZW9ybVwiO1xuXG5jb25zdCByb290ID0gZGlybmFtZShmaWxlVVJMVG9QYXRoKGltcG9ydC5tZXRhLnVybCkpO1xuY29uc3QgcmVxdWlyZWQgPSAoa2V5OiBzdHJpbmcpOiBzdHJpbmcgPT4ge1xuXFx0Y29uc3QgdmFsdWUgPSBwcm9jZXNzLmVudltrZXldO1xuXFx0aWYgKHZhbHVlID09IG51bGwgfHwgdmFsdWUgPT09IFwiXCIpIHtcblxcdFxcdHRocm93IG5ldyBFcnJvcihcXGBNaXNzaW5nIGVudiBcXCR7a2V5fSAobG9hZCAuZW52IHZpYSAtLWVudi1maWxlIG9yIGV4cG9ydCBpdClcXGApO1xuXFx0fVxuXFx0cmV0dXJuIHZhbHVlO1xufTtcblxuLyoqXG4gKiBDTEktb25seSBEYXRhU291cmNlIGZvciBcXGBwbnBtIGRiOm1pZ3JhdGVcXGAgLyBcXGBwbnBtIGRiOm1pZ3JhdGU6Z2VuZXJhdGVcXGAuXG4gKiBVc2VzIHRoZSBtaWdyYXRlIHJvbGUgKERETCkuIFJ1bnRpbWUgQVBJIHBvZHMgdXNlIHRoZSBhcHAgcm9sZSB3aXRoIHN5bmNocm9uaXplOiBmYWxzZS5cbiAqL1xuZXhwb3J0IGRlZmF1bHQgbmV3IERhdGFTb3VyY2Uoe1xuXFx0dHlwZTogXCJwb3N0Z3Jlc1wiLFxuXFx0aG9zdDogcmVxdWlyZWQoXCIke3B9X0RCX0hPU1RcIiksXG5cXHRwb3J0OiBOdW1iZXIocmVxdWlyZWQoXCIke3B9X0RCX1BPUlRcIikpLFxuXFx0ZGF0YWJhc2U6IHJlcXVpcmVkKFwiJHtwfV9EQl9OQU1FXCIpLFxuXFx0dXNlcm5hbWU6IHJlcXVpcmVkKFwiJHtwfV9EQl9NSUdSQVRFX1VTRVJcIiksXG5cXHRwYXNzd29yZDogcmVxdWlyZWQoXCIke3B9X0RCX01JR1JBVEVfUEFTU1dPUkRcIiksXG5cXHRzeW5jaHJvbml6ZTogZmFsc2UsXG5cXHRlbnRpdGllczogW2pvaW4ocm9vdCwgXCJlbnRpdGllc1wiLCBcIioue3RzLGpzfVwiKV0sXG5cXHRtaWdyYXRpb25zOiBbam9pbihyb290LCBcIm1pZ3JhdGlvbnNcIiwgXCIqLnt0cyxqc31cIildLFxufSk7XG5gO1xufVxuXG5jb25zdCBEQVRBQkFTRV9ET0NTX01EID0gYCMgRGF0YWJhc2UgKFBvc3RncmVzIC8gVHlwZU9STSlcblxuIyMgUm9sZXNcblxufCBSb2xlIHwgRW52IHwgUHVycG9zZSB8XG58LS0tfC0tLXwtLS18XG58IEFwcCB8IFxcYHtQUkVGSVh9X0RCX1VTRVJcXGAgfCBSdW50aW1lIEFQSSAoRE1MIG9ubHkgaW4gcHJvZHVjdGlvbikgfFxufCBNaWdyYXRlIHwgXFxge1BSRUZJWH1fREJfTUlHUkFURV9VU0VSXFxgIHwgU2NoZW1hIGNoYW5nZXMgLyBUeXBlT1JNIG1pZ3JhdGlvbnMgKENJL2RlcGxveSkgfFxufCBCb290c3RyYXAgfCBcXGBEQl9BRE1JTl9VU0VSXFxgIHwgQ3JlYXRlIERCICsgcm9sZXMgd2hlbiBcXGBib290c3RyYXA6IHRydWVcXGAgKGRldikgfFxuXG5cXGB7UFJFRklYfVxcYCBpcyB0aGUgQVBJIHNsdWcgaW4gU0NSRUFNSU5HX1NOQUtFIChcXGBhcGlcXGAg4oaSIFxcYEFQSVxcYCwgXFxgYmlsbGluZ1xcYCDihpIgXFxgQklMTElOR1xcYCkuXG5cbiMjIERldmVsb3BtZW50IChmYXN0IGl0ZXJhdGlvbilcblxuXFxgY29uZmlnL2NvbmZpZy5kZXYueWFtbFxcYCBzZXRzIFxcYHN5bmNocm9uaXplOiB0cnVlXFxgIGFuZCBcXGBzZWVkOiB0cnVlXFxgIHNvIHRoZSBBUEkgY2FuIGNyZWF0ZSB0YWJsZXMgb24gc3RhcnR1cC4gWW91IGNhbiBrZWVwIHVzaW5nIHN5bmMgd2hpbGUgcHJvdG90eXBpbmcuXG5cbkFsdGVybmF0aXZlbHksIHR1cm4gc3luYyBvZmYgaW4gdGhlIG92ZXJsYXkgYW5kIHVzZSBtaWdyYXRpb25zIHRoZSBzYW1lIHdheSBhcyBwcm9kdWN0aW9uOlxuXG5cXGBcXGBcXGBiYXNoXG5wbnBtIGRiOnVwXG5wbnBtIGRiOm1pZ3JhdGVcbnBucG0gZGI6bWlncmF0ZTpnZW5lcmF0ZSAtLSAuL3Byb2plY3RzL2FwaS9zcmMvbWlncmF0aW9ucy9BZGRTb21ldGhpbmdcblxcYFxcYFxcYFxuXG5EbyAqKm5vdCoqIG1peCBzeW5jIGFuZCBtaWdyYXRpb25zIGZvciB0aGUgc2FtZSBzY2hlbWEgY2hhbmdlIChwaWNrIG9uZSBwYXRoIHBlciBjaGFuZ2UpLlxuXG4jIyBQcm9kdWN0aW9uIChtaWdyYXRpb25zIG9ubHkpXG5cblxcYGNvbmZpZy9jb25maWcueWFtbFxcYCBrZWVwcyBcXGBzeW5jaHJvbml6ZTogZmFsc2VcXGAgLyBcXGBzZWVkOiBmYWxzZVxcYCAvIFxcYGJvb3RzdHJhcDogZmFsc2VcXGAuIERlcGxveSBzdGVwczpcblxuMS4gRW5zdXJlIHRoZSBhcHAgZGF0YWJhc2UgYW5kIHJvbGVzIGFscmVhZHkgZXhpc3QgKHByb3Zpc2lvbmVyIC8gb3BzIOKAlCBub3QgdGhlIEFQSSBwb2QpLlxuMi4gUnVuIG1pZ3JhdGlvbnMgd2l0aCB0aGUgbWlncmF0ZSByb2xlOiBcXGBwbnBtIGRiOm1pZ3JhdGVcXGAgKG9yIFxcYHBucG0gZGI6bWlncmF0ZTo8YXBpPlxcYCBmb3IgbXVsdGktQVBJKS5cbjMuIFN0YXJ0IHRoZSBBUEkgd2l0aCB0aGUgYXBwIHJvbGUgb25seSDigJQgbm8gbWlncmF0ZS9hZG1pbiBzZWNyZXRzIG9uIHRoZSBwb2QuXG5cbiMjIFNjcmlwdHNcblxufCBTY3JpcHQgfCBXaGF0IGl0IGRvZXMgfFxufC0tLXwtLS18XG58IFxcYGRiOnVwXFxgIC8gXFxgZGI6ZG93blxcYCAvIFxcYGRiOnJlc2V0XFxgIHwgTG9jYWwgQ29tcG9zZSBQb3N0Z3JlcyB8XG58IFxcYGRiOm1pZ3JhdGVcXGAgfCBSdW4gcGVuZGluZyBtaWdyYXRpb25zIChcXGBwcm9qZWN0cy9hcGlcXGAgYnkgZGVmYXVsdCkgfFxufCBcXGBkYjptaWdyYXRlOmdlbmVyYXRlXFxgIHwgR2VuZXJhdGUgYSBtaWdyYXRpb24gZnJvbSBlbnRpdHkgZHJpZnQgKHBhc3MgcGF0aCBhZnRlciBcXGAtLVxcYCkgfFxufCBcXGBkYjptaWdyYXRlOjxhcGk+XFxgIHwgU2FtZSBmb3IgYSBuYW1lZCBBUEkgdW5kZXIgXFxgcHJvamVjdHMvPGFwaT5cXGAgfFxuXG5DTEkgZW50cnk6IFxcYHByb2plY3RzLzxhcGk+L3NyYy90eXBlb3JtLmRhdGEtc291cmNlLnRzXFxgIChtaWdyYXRlIGNyZWRlbnRpYWxzLCBcXGBzeW5jaHJvbml6ZTogZmFsc2VcXGApLlxuXG4jIyBCcm93c2UgdGhlIGRhdGFiYXNlXG5cblByZWZlciB0aGUgRGV2IENvbnRhaW5lciAqKlNRTFRvb2xzKiogZXh0ZW5zaW9uIChQb3N0Z3JlcyBkcml2ZXIpIG9yIGFueSBkZXNrdG9wIGNsaWVudCBhZ2FpbnN0IFxcYDEyNy4wLjAuMTo1NDMzXFxgIChzZWUgXFxgLmVudlxcYCBmb3IgY3JlZGVudGlhbHMpLlxuXG5BZG1pbmVyIGlzICoqbm90Kiogc2NhZmZvbGRlZC4gSWYgeW91IHdhbnQgYSBicm93c2VyIFVJIGluIENvbXBvc2UsIGFkZCB0aGlzIHVuZGVyIFxcYHNlcnZpY2VzOlxcYCBpbiBcXGAuZGV2Y29udGFpbmVyL2RvY2tlci1jb21wb3NlLnltbFxcYCAocHJvZmlsZSBcXGB0b29sc1xcYCBzbyBpdCBzdGF5cyBvZmYgYnkgZGVmYXVsdCk6XG5cblxcYFxcYFxcYHlhbWxcbiAgYWRtaW5lcjpcbiAgICBpbWFnZTogYWRtaW5lclxuICAgIHByb2ZpbGVzOlxuICAgICAgLSB0b29sc1xuICAgIHBvcnRzOlxuICAgICAgLSBcIjEyNy4wLjAuMTo4MDgxOjgwODBcIlxuICAgIG5ldHdvcmtzOlxuICAgICAgLSA8Y29tcG9zZS1uZXR3b3JrLWtleT4gIyBzYW1lIGtleSBhcyBwb3N0Z3JlcyAvIGRldmNvbnRhaW5lclxuXFxgXFxgXFxgXG5cblRoZW46IFxcYGRvY2tlciBjb21wb3NlIC1mIC5kZXZjb250YWluZXIvZG9ja2VyLWNvbXBvc2UueW1sIC0tcHJvZmlsZSB0b29scyB1cCAtZCBhZG1pbmVyXFxgXG5gO1xuXG5mdW5jdGlvbiB0eXBlb3JtQ2xpKGFyZ3M6IHN0cmluZyk6IHN0cmluZyB7XG5cdHJldHVybiBgbm9kZSAtLWltcG9ydCB0c3ggLS1lbnYtZmlsZT0uZW52IC4vbm9kZV9tb2R1bGVzL3R5cGVvcm0vY2xpLmpzICR7YXJnc31gO1xufVxuXG5mdW5jdGlvbiBtaWdyYXRlUnVuU2NyaXB0KGFwaU5hbWU6IHN0cmluZyk6IHN0cmluZyB7XG5cdHJldHVybiB0eXBlb3JtQ2xpKGBtaWdyYXRpb246cnVuIC1kIC4vcHJvamVjdHMvJHthcGlOYW1lfS9zcmMvdHlwZW9ybS5kYXRhLXNvdXJjZS50c2ApO1xufVxuXG5mdW5jdGlvbiBtaWdyYXRlR2VuZXJhdGVTY3JpcHQoYXBpTmFtZTogc3RyaW5nKTogc3RyaW5nIHtcblx0cmV0dXJuIHR5cGVvcm1DbGkoXG5cdFx0YG1pZ3JhdGlvbjpnZW5lcmF0ZSAtLWVzbSAtZCAuL3Byb2plY3RzLyR7YXBpTmFtZX0vc3JjL3R5cGVvcm0uZGF0YS1zb3VyY2UudHNgLFxuXHQpO1xufVxuXG5hc3luYyBmdW5jdGlvbiBnZXRPcHRpb25zKFxuXHRfd29ya3NwYWNlOiBXb3Jrc3BhY2VTY2FmZm9sZCxcblx0Y2xpT3B0aW9uczogUGFydGlhbDxEYXRhYmFzZU9wdGlvbnM+LFxuKTogUHJvbWlzZTxQYXJ0aWFsPERhdGFiYXNlT3B0aW9ucz4+IHtcblx0aWYgKGNsaU9wdGlvbnMuZGF0YWJhc2VQcm92aWRlciAhPSBudWxsKSByZXR1cm4ge307XG5cblx0Y29uc3QgY2hvaWNlcyA9IFt7IGxhYmVsOiBcIm5vbmVcIiBhcyBjb25zdCB9LCB7IGxhYmVsOiBcInBvc3RncmVzL3R5cGVvcm1cIiBhcyBjb25zdCB9XTtcblx0Y29uc3Qgc2VsZWN0ZWQgPSBhd2FpdCBwaWNrKFwiXFxuICBEYXRhYmFzZTogXCIsIGNob2ljZXMsIChjKSA9PiBjLmxhYmVsKTtcblx0cmV0dXJuIHsgZGF0YWJhc2VQcm92aWRlcjogc2VsZWN0ZWQ/LmxhYmVsID8/IFwibm9uZVwiIH07XG59XG5cbmZ1bmN0aW9uIHNjYWZmb2xkKHdvcmtzcGFjZTogV29ya3NwYWNlU2NhZmZvbGQsIG9wdGlvbnM6IERhdGFiYXNlT3B0aW9ucyk6IHZvaWQge1xuXHRpZiAob3B0aW9ucy5kYXRhYmFzZVByb3ZpZGVyICE9PSBcInBvc3RncmVzL3R5cGVvcm1cIikgcmV0dXJuO1xuXG5cdGNvbnN0IG5hbWUgPSB3b3Jrc3BhY2UubmFtZTtcblx0Y29uc3QgaWRlbnQgPSB0b1NxbElkZW50RnJhZ21lbnQobmFtZSk7XG5cdGNvbnN0IGFwaVByb2plY3RzID0gd29ya3NwYWNlLmdldFByb2plY3RzKCk7XG5cdGNvbnN0IGFwaU5hbWVzID0gYXBpUHJvamVjdHMubWFwKChwcm9qZWN0KSA9PiBhcGlOYW1lRnJvbVByb2plY3REaXIocHJvamVjdC5kaXIpKTtcblx0Ly8gQ29udGFpbmVyIGluaXQgc3VwZXJ1c2VyIOKAlCBuZXZlciByZXVzZWQgYXMgYW4gQVBJIHJ1bnRpbWUgcm9sZS5cblx0Y29uc3QgcG9zdGdyZXNTdXBlcnVzZXIgPSBcInBvc3RncmVzXCI7XG5cdGNvbnN0IHBvc3RncmVzUGFzc3dvcmQgPSBwYXNzd29yZE9yRXhpc3Rpbmcod29ya3NwYWNlLCBcIlBPU1RHUkVTX1BBU1NXT1JEXCIpO1xuXHRjb25zdCBib290c3RyYXBVc2VyID0gXCJkZWNvaGVyZV9ib290c3RyYXBcIjtcblx0Y29uc3QgYm9vdHN0cmFwUGFzc3dvcmQgPSBwYXNzd29yZE9yRXhpc3Rpbmcod29ya3NwYWNlLCBcIkRCX0FETUlOX1BBU1NXT1JEXCIsIFtcblx0XHRcIlBPU1RHUkVTX1BBU1NXT1JEXCIsXG5cdF0pO1xuXG5cdGNvbnN0IGRldkNvbnRhaW5lciA9IHdvcmtzcGFjZS5nZXRNb2RlbDxEZXZDb250YWluZXI+KFdFTExfS05PV05fRklMRVMuREVWQ09OVEFJTkVSX0pTT04pO1xuXHRjb25zdCBjb21wb3NlID0gd29ya3NwYWNlLmdldE1vZGVsPERvY2tlckNvbXBvc2U+KFdFTExfS05PV05fRklMRVMuRE9DS0VSX0NPTVBPU0UpO1xuXG5cdGNvbnN0IG5ldHdvcmtLZXkgPSBuYW1lO1xuXG5cdGlmIChjb21wb3NlKSB7XG5cdFx0Ly8gT25lIHNoYXJlZCBpbnN0YW5jZS4gRWFjaCBBUEkgb3ducyBhIGRhdGFiYXNlIG9uIGl0IChjcmVhdGVkIGF0IGJvb3RzdHJhcCkuXG5cdFx0Ly8gUG9pbnQgYHtQUkVGSVh9X0RCX0hPU1RgIC8gYHtQUkVGSVh9X0RCX1BPUlRgIGF0IGFub3RoZXIgc2VydmVyIHRvIHNwbGl0IGxhdGVyLlxuXHRcdC8vIEN1c3RvbSBpbWFnZTogc3luYyBib290c3RyYXAgcm9sZSBwYXNzd29yZCBvbiBldmVyeSBzdGFydCAoaW5pdGRiLmQgaXMgb25jZS1vbmx5KS5cblx0XHQvLyBEZWZhdWx0IGRhdGEgaXMgYSBiaW5kIG1vdW50IHVuZGVyIHRoZSBwcm9qZWN0IHNvIGBybSAtcmZgIHRoZSB0cmVlIHJlbW92ZXMgaXQuXG5cdFx0Y29tcG9zZVxuXHRcdFx0LmFkZEhlYWRlckNvbW1lbnQoXG5cdFx0XHRcdFwiUG9zdGdyZXMgZGF0YSBkZWZhdWx0cyB0byAuL3Bvc3RncmVzLWRhdGEgdW5kZXIgdGhpcyBmb2xkZXIgKGJpbmQgbW91bnQ7IGRpZXMgd2l0aCB0aGUgd29ya2luZyB0cmVlKS5cIixcblx0XHRcdFx0YEZvciBkYXRhIHRoYXQgb3V0bGl2ZXMgdGhlIGNoZWNrb3V0LCByZXBsYWNlIHRoZSBwb3N0Z3JlcyB2b2x1bWVzIGVudHJ5IHdpdGggYSBuYW1lZCB2b2x1bWVgLFxuXHRcdFx0XHRgZS5nLiBcIiR7bmFtZX0tcG9zdGdyZXMtZGF0YTovdmFyL2xpYi9wb3N0Z3Jlc3FsL2RhdGFcIiBhbmQgZGVjbGFyZSBpdCB1bmRlciB0b3AtbGV2ZWwgdm9sdW1lczouYCxcblx0XHRcdClcblx0XHRcdC5hZGRTZXJ2aWNlKFwicG9zdGdyZXNcIiwge1xuXHRcdFx0XHRidWlsZDogXCIuL3Bvc3RncmVzXCIsXG5cdFx0XHRcdGNvbnRhaW5lcl9uYW1lOiBgc2FtcGxlcy0ke25hbWV9LXBvc3RncmVzYCxcblx0XHRcdFx0cmVzdGFydDogXCJ1bmxlc3Mtc3RvcHBlZFwiLFxuXHRcdFx0XHRwb3J0czogW1wiMTI3LjAuMC4xOjU0MzM6NTQzMlwiXSxcblx0XHRcdFx0dm9sdW1lczogW1BPU1RHUkVTX0RBVEFfQklORF0sXG5cdFx0XHRcdG5ldHdvcmtzOiBbeyBbbmV0d29ya0tleV06IHsgYWxpYXNlczogW1wicG9zdGdyZXNcIl0gfSB9XSxcblx0XHRcdFx0aGVhbHRoY2hlY2s6IHtcblx0XHRcdFx0XHQvLyBFbnN1cmUgcm9sZS9wYXNzd29yZCBtYXRjaCAuZW52IGJlZm9yZSByZXBvcnRpbmcgaGVhbHRoeS5cblx0XHRcdFx0XHR0ZXN0OiBbXG5cdFx0XHRcdFx0XHRcIkNNRC1TSEVMTFwiLFxuXHRcdFx0XHRcdFx0YHBnX2lzcmVhZHkgLVUgJHtwb3N0Z3Jlc1N1cGVydXNlcn0gJiYgL3Vzci9sb2NhbC9iaW4vZGVjb2hlcmUtZW5zdXJlLWJvb3RzdHJhcC5zaGAsXG5cdFx0XHRcdFx0XSxcblx0XHRcdFx0XHRpbnRlcnZhbDogXCIyc1wiLFxuXHRcdFx0XHRcdHRpbWVvdXQ6IFwiNXNcIixcblx0XHRcdFx0XHRyZXRyaWVzOiAxNSxcblx0XHRcdFx0fSxcblx0XHRcdH0pXG5cdFx0XHQuYWRkRW52RmlsZShcInBvc3RncmVzXCIsIFwiLi4vLmVudlwiKTtcblxuXHRcdHdvcmtzcGFjZS5nZXRNb2RlbDxHaXRpZ25vcmU+KFdFTExfS05PV05fRklMRVMuR0lUSUdOT1JFKT8uYWRkKFBPU1RHUkVTX0RBVEFfR0lUSUdOT1JFKTtcblxuXHRcdHdvcmtzcGFjZS5hZGRGaWxlKHtcblx0XHRcdGZpbGVzOiAoKSA9PiBbXG5cdFx0XHRcdHtcblx0XHRcdFx0XHRmaWxlUGF0aDogXCIuZGV2Y29udGFpbmVyL3Bvc3RncmVzL0RvY2tlcmZpbGVcIixcblx0XHRcdFx0XHRjb250ZW50OiBQT1NUR1JFU19JTUFHRV9ET0NLRVJGSUxFLFxuXHRcdFx0XHR9LFxuXHRcdFx0XHR7XG5cdFx0XHRcdFx0ZmlsZVBhdGg6IFwiLmRldmNvbnRhaW5lci9wb3N0Z3Jlcy9lbnRyeXBvaW50LnNoXCIsXG5cdFx0XHRcdFx0Y29udGVudDogUE9TVEdSRVNfRU5UUllQT0lOVF9TSCxcblx0XHRcdFx0fSxcblx0XHRcdFx0e1xuXHRcdFx0XHRcdGZpbGVQYXRoOiBcIi5kZXZjb250YWluZXIvcG9zdGdyZXMvZW5zdXJlLWJvb3RzdHJhcC1yb2xlLnNoXCIsXG5cdFx0XHRcdFx0Y29udGVudDogUE9TVEdSRVNfRU5TVVJFX0JPT1RTVFJBUF9TSCxcblx0XHRcdFx0fSxcblx0XHRcdF0sXG5cdFx0fSk7XG5cblx0XHRjb21wb3NlLmFkZEVudkZpbGUoXCJkZXZjb250YWluZXJcIiwgXCIuLi8uZW52XCIpO1xuXHR9XG5cblx0ZGV2Q29udGFpbmVyPy5hZGRFeHRlbnNpb24oXCJtdHhyLnNxbHRvb2xzXCIsIFwibXR4ci5zcWx0b29scy1kcml2ZXItcGdcIik7XG5cdHdvcmtzcGFjZVxuXHRcdC5nZXRNb2RlbDxEb2NrZXJmaWxlTW9kZWw+KFdFTExfS05PV05fRklMRVMuRE9DS0VSRklMRSlcblx0XHQ/LmFkZExheWVyKHdpdGhQb3N0Z3Jlc3FsQ2xpZW50KCkpO1xuXG5cdGNvbnN0IHBrZyA9IHdvcmtzcGFjZVxuXHRcdC5nZXRNb2RlbDxQYWNrYWdlSnNvbj4oV0VMTF9LTk9XTl9GSUxFUy5QQUNLQUdFX0pTT04pXG5cdFx0Py5hZGREZXBlbmRlbmN5KFwiQGRlY29oZXJlL2NvZGV4XCIsIENSRUFURV9WRVJTSU9OKVxuXHRcdC5hZGREZXBlbmRlbmN5KFwidHlwZW9ybVwiLCBcIl4wLjMuMjhcIilcblx0XHQuYWRkRGVwZW5kZW5jeShcIkBkZWNvaGVyZS9jb2RleC10eXBlb3JtXCIsIENSRUFURV9WRVJTSU9OKVxuXHRcdC5hZGREZXBlbmRlbmN5KFwidHN4XCIsIFwiXjQuMjAuNVwiKVxuXHRcdC5hZGRTY3JpcHQoXCJkYjp1cFwiLCBcImRvY2tlciBjb21wb3NlIC1mIC5kZXZjb250YWluZXIvZG9ja2VyLWNvbXBvc2UueW1sIHVwIC1kIHBvc3RncmVzXCIpXG5cdFx0LmFkZFNjcmlwdChcImRiOmRvd25cIiwgXCJkb2NrZXIgY29tcG9zZSAtZiAuZGV2Y29udGFpbmVyL2RvY2tlci1jb21wb3NlLnltbCBkb3duXCIpXG5cdFx0LmFkZFNjcmlwdChcImRiOnJlc2V0XCIsIERCX1JFU0VUX1NDUklQVCk7XG5cblx0Y29uc3QgcHJpbWFyeUFwaSA9IGFwaU5hbWVzWzBdO1xuXHRpZiAocGtnICE9IG51bGwpIHtcblx0XHRmb3IgKGNvbnN0IGFwaU5hbWUgb2YgYXBpTmFtZXMpIHtcblx0XHRcdHBrZ1xuXHRcdFx0XHQuYWRkU2NyaXB0KGBkYjptaWdyYXRlOiR7YXBpTmFtZX1gLCBtaWdyYXRlUnVuU2NyaXB0KGFwaU5hbWUpKVxuXHRcdFx0XHQuYWRkU2NyaXB0KGBkYjptaWdyYXRlOmdlbmVyYXRlOiR7YXBpTmFtZX1gLCBtaWdyYXRlR2VuZXJhdGVTY3JpcHQoYXBpTmFtZSkpO1xuXHRcdH1cblx0XHQvLyBEZWZhdWx0IGFsaWFzZXMgdGFyZ2V0IHRoZSBmaXJzdCBBUEkgb2YgYSBuZXcgd29ya3NwYWNlIG9ubHkg4oCUIG5ldmVyIG92ZXJ3cml0ZSBvbiBgd2ViYXBpIGFkZGAuXG5cdFx0aWYgKCFvcHRpb25zLmFkZCAmJiBwcmltYXJ5QXBpICE9IG51bGwpIHtcblx0XHRcdHBrZ1xuXHRcdFx0XHQuYWRkU2NyaXB0KFwiZGI6bWlncmF0ZVwiLCBtaWdyYXRlUnVuU2NyaXB0KHByaW1hcnlBcGkpKVxuXHRcdFx0XHQuYWRkU2NyaXB0KFwiZGI6bWlncmF0ZTpnZW5lcmF0ZVwiLCBtaWdyYXRlR2VuZXJhdGVTY3JpcHQocHJpbWFyeUFwaSkpO1xuXHRcdH1cblx0fVxuXG5cdHdvcmtzcGFjZS5hZGRGaWxlKHtcblx0XHRmaWxlczogKCkgPT4gW1xuXHRcdFx0e1xuXHRcdFx0XHRmaWxlUGF0aDogXCJkb2NzL2RhdGFiYXNlLm1kXCIsXG5cdFx0XHRcdGNvbnRlbnQ6IERBVEFCQVNFX0RPQ1NfTUQsXG5cdFx0XHR9LFxuXHRcdF0sXG5cdH0pO1xuXG5cdHdyaXRlRGF0YWJhc2VDb25maWcod29ya3NwYWNlLCBhcGlOYW1lcyk7XG5cblx0Zm9yIChjb25zdCBwcm9qZWN0IG9mIGFwaVByb2plY3RzKSB7XG5cdFx0d2lyZUFwaURhdGFiYXNlKHByb2plY3QpO1xuXHR9XG5cblx0Y29uc3QgZW52ID0gd29ya3NwYWNlLmdldE1vZGVsPEVudkZpbGU+KFdFTExfS05PV05fRklMRVMuRU5WX0ZJTEUpO1xuXHRlbnZcblx0XHQ/LmFkZEVudHJ5KFwiUE9TVEdSRVNfREJcIiwgaWRlbnQpXG5cdFx0LmFkZEVudHJ5KFwiUE9TVEdSRVNfVVNFUlwiLCBwb3N0Z3Jlc1N1cGVydXNlcilcblx0XHQuYWRkRW50cnkoXCJQT1NUR1JFU19QQVNTV09SRFwiLCBwb3N0Z3Jlc1Bhc3N3b3JkKVxuXHRcdC5hZGRFbnRyeShcIkRCX0FETUlOX1VTRVJcIiwgYm9vdHN0cmFwVXNlcilcblx0XHQuYWRkRW50cnkoXCJEQl9BRE1JTl9QQVNTV09SRFwiLCBib290c3RyYXBQYXNzd29yZCk7XG5cblx0Zm9yIChjb25zdCBhcGlOYW1lIG9mIGFwaU5hbWVzKSB7XG5cdFx0d3JpdGVBcGlEYXRhYmFzZUVudih3b3Jrc3BhY2UsIGlkZW50LCBhcGlOYW1lKTtcblx0fVxufVxuXG5mdW5jdGlvbiB3cml0ZURhdGFiYXNlQ29uZmlnKHdvcmtzcGFjZTogV29ya3NwYWNlU2NhZmZvbGQsIGFwaU5hbWVzOiByZWFkb25seSBzdHJpbmdbXSk6IHZvaWQge1xuXHRjb25zdCBjb25maWcgPSB3b3Jrc3BhY2UuZ2V0TW9kZWw8Q29uZmlnWWFtbD4oV0VMTF9LTk9XTl9GSUxFUy5DT05GSUdfWUFNTCk7XG5cdGNvbnN0IGRldkNvbmZpZyA9IHdvcmtzcGFjZS5nZXRNb2RlbDxDb25maWdZYW1sPihXRUxMX0tOT1dOX0ZJTEVTLkNPTkZJR19ERVZfWUFNTCk7XG5cdGNvbnN0IHBlbmRpbmcgPSBhcGlOYW1lcy5maWx0ZXIoKGFwaU5hbWUpID0+ICFjb25maWc/Lmhhc0RhdGFTb3VyY2UoYXBpTmFtZSkpO1xuXHRpZiAocGVuZGluZy5sZW5ndGggPT09IDApIHJldHVybjtcblxuXHRpZiAoY29uZmlnPy5oYXNEYXRhU291cmNlcygpKSB7XG5cdFx0Zm9yIChjb25zdCBhcGlOYW1lIG9mIHBlbmRpbmcpIHtcblx0XHRcdGNvbmZpZy5hcHBlbmREYXRhU291cmNlKHJlbmRlckJhc2VEYXRhU291cmNlKGFwaU5hbWUpKTtcblx0XHRcdGRldkNvbmZpZz8uYXBwZW5kRGF0YVNvdXJjZShyZW5kZXJEZXZEYXRhU291cmNlKGFwaU5hbWUpKTtcblx0XHR9XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0Y29uZmlnPy5hZGRTZWN0aW9uKHJlbmRlckRhdGFiYXNlU2VjdGlvbihwZW5kaW5nLCByZW5kZXJCYXNlRGF0YVNvdXJjZSkpO1xuXHRkZXZDb25maWc/LmFkZFNlY3Rpb24ocmVuZGVyRGF0YWJhc2VTZWN0aW9uKHBlbmRpbmcsIHJlbmRlckRldkRhdGFTb3VyY2UpKTtcbn1cblxuZnVuY3Rpb24gd3JpdGVBcGlEYXRhYmFzZUVudihcblx0d29ya3NwYWNlOiBXb3Jrc3BhY2VTY2FmZm9sZCxcblx0aWRlbnQ6IHN0cmluZyxcblx0YXBpTmFtZTogc3RyaW5nLFxuKTogdm9pZCB7XG5cdGNvbnN0IHByZWZpeCA9IGFwaUVudlByZWZpeChhcGlOYW1lKTtcblx0Y29uc3Qgc3FsQXBpID0gdG9TcWxJZGVudEZyYWdtZW50KGFwaU5hbWUpO1xuXHR3b3Jrc3BhY2Vcblx0XHQuZ2V0TW9kZWw8RW52RmlsZT4oV0VMTF9LTk9XTl9GSUxFUy5FTlZfRklMRSlcblx0XHQ/LmFkZEVudHJ5KGAke3ByZWZpeH1fREJfSE9TVGAsIFwicG9zdGdyZXNcIilcblx0XHQuYWRkRW50cnkoYCR7cHJlZml4fV9EQl9QT1JUYCwgXCI1NDMyXCIpXG5cdFx0LmFkZEVudHJ5KGAke3ByZWZpeH1fREJfTkFNRWAsIGAke2lkZW50fV8ke3NxbEFwaX1fZGV2YClcblx0XHQuYWRkRW50cnkoYCR7cHJlZml4fV9EQl9VU0VSYCwgYCR7aWRlbnR9XyR7c3FsQXBpfV9hcHBgKVxuXHRcdC5hZGRFbnRyeShgJHtwcmVmaXh9X0RCX1BBU1NXT1JEYCwgcGFzc3dvcmRPckV4aXN0aW5nKHdvcmtzcGFjZSwgYCR7cHJlZml4fV9EQl9QQVNTV09SRGApKVxuXHRcdC5hZGRFbnRyeShgJHtwcmVmaXh9X0RCX01JR1JBVEVfVVNFUmAsIGAke2lkZW50fV8ke3NxbEFwaX1fbWlncmF0ZWApXG5cdFx0LmFkZEVudHJ5KFxuXHRcdFx0YCR7cHJlZml4fV9EQl9NSUdSQVRFX1BBU1NXT1JEYCxcblx0XHRcdHBhc3N3b3JkT3JFeGlzdGluZyh3b3Jrc3BhY2UsIGAke3ByZWZpeH1fREJfTUlHUkFURV9QQVNTV09SRGApLFxuXHRcdCk7XG59XG5cbmZ1bmN0aW9uIHdpcmVBcGlEYXRhYmFzZShwcm9qZWN0OiBQcm9qZWN0U2NhZmZvbGQpOiB2b2lkIHtcblx0Y29uc3QgYXBpTmFtZSA9IGFwaU5hbWVGcm9tUHJvamVjdERpcihwcm9qZWN0LmRpcik7XG5cdHByb2plY3Rcblx0XHQuZ2V0TW9kZWw8TWFpblNvdXJjZUZpbGU+KFdFTExfS05PV05fRklMRVMuTUFJTl9UUylcblx0XHQ/LnNldERhdGFTb3VyY2VOYW1lKGFwaU5hbWUpXG5cdFx0LmFkZFNpZGVFZmZlY3RJbXBvcnQoXCJAZGVjb2hlcmUvY29kZXgtdHlwZW9ybVwiKVxuXHRcdC5hZGRFbnRpdHkoeyBjbGFzc05hbWU6IFwiU2FtcGxlRW50aXR5XCIsIGltcG9ydFBhdGg6IFwiLi9lbnRpdGllcy9zYW1wbGUuZW50aXR5LmpzXCIgfSlcblx0XHQuYWRkUmVwb3NpdG9yeSh7XG5cdFx0XHRjbGFzc05hbWU6IFwiU2FtcGxlRW50aXR5UmVwb3NpdG9yeVwiLFxuXHRcdFx0aW1wb3J0UGF0aDogXCIuL3JlcG9zaXRvcmllcy9zYW1wbGUucmVwb3NpdG9yeS5qc1wiLFxuXHRcdH0pO1xuXHRwcm9qZWN0XG5cdFx0LmdldE1vZGVsPEFwaVBhY2thZ2VKc29uPihXRUxMX0tOT1dOX0ZJTEVTLkFQSV9QQUNLQUdFX0pTT04pXG5cdFx0Py5hZGREZXBlbmRlbmN5KFwiQGRlY29oZXJlL2NvZGV4XCIsIENSRUFURV9WRVJTSU9OKVxuXHRcdC5hZGREZXBlbmRlbmN5KFwiQGRlY29oZXJlL2NvZGV4LXR5cGVvcm1cIiwgQ1JFQVRFX1ZFUlNJT04pO1xuXHRwcm9qZWN0LmFkZEZpbGUoe1xuXHRcdGZpbGVzOiAoKSA9PiBbXG5cdFx0XHR7XG5cdFx0XHRcdGZpbGVQYXRoOiBcInNyYy9lbnRpdGllcy9zYW1wbGUuZW50aXR5LnRzXCIsXG5cdFx0XHRcdGNvbnRlbnQ6IFNBTVBMRV9FTlRJVFlfVFMsXG5cdFx0XHR9LFxuXHRcdFx0e1xuXHRcdFx0XHRmaWxlUGF0aDogXCJzcmMvcmVwb3NpdG9yaWVzL3NhbXBsZS5yZXBvc2l0b3J5LnRzXCIsXG5cdFx0XHRcdGNvbnRlbnQ6IFNBTVBMRV9SRVBPU0lUT1JZX1RTLFxuXHRcdFx0fSxcblx0XHRcdHtcblx0XHRcdFx0ZmlsZVBhdGg6IFwic3JjL3R5cGVvcm0uZGF0YS1zb3VyY2UudHNcIixcblx0XHRcdFx0Y29udGVudDogcmVuZGVyVHlwZU9ybURhdGFTb3VyY2UoYXBpTmFtZSksXG5cdFx0XHR9LFxuXHRcdFx0e1xuXHRcdFx0XHRmaWxlUGF0aDogXCJzcmMvbWlncmF0aW9ucy8uZ2l0a2VlcFwiLFxuXHRcdFx0XHRjb250ZW50OiBcIlwiLFxuXHRcdFx0fSxcblx0XHRdLFxuXHR9KTtcbn1cblxuZXhwb3J0IGNvbnN0IERhdGFiYXNlRmVhdHVyZTogRmVhdHVyZTxEYXRhYmFzZU9wdGlvbnM+ID0geyBnZXRPcHRpb25zLCBzY2FmZm9sZCB9O1xuIl0sIm5hbWVzIjpbIkRhdGFiYXNlRmVhdHVyZSIsIlBPU1RHUkVTX0RBVEFfQklORCIsIlBPU1RHUkVTX0RBVEFfR0lUSUdOT1JFIiwiREJfUkVTRVRfU0NSSVBUIiwicmVhZEV4aXN0aW5nRW52VmFsdWUiLCJ3b3Jrc3BhY2UiLCJrZXkiLCJyYXciLCJyZWFkRmlsZVN5bmMiLCJqb2luIiwidGFyZ2V0RGlyIiwibGluZSIsInNwbGl0IiwiZmluZCIsImwiLCJzdGFydHNXaXRoIiwidmFsdWUiLCJzbGljZSIsImxlbmd0aCIsInRyaW0iLCJ1bmRlZmluZWQiLCJyYW5kb21QYXNzd29yZCIsInJhbmRvbUJ5dGVzIiwidG9TdHJpbmciLCJ0b1NxbElkZW50RnJhZ21lbnQiLCJuYW1lIiwiY2xlYW5lZCIsInJlcGxhY2UiLCJwYXNzd29yZE9yRXhpc3RpbmciLCJmYWxsYmFja0V4aXN0aW5nS2V5cyIsImV4aXN0aW5nIiwiYWx0IiwidiIsInJlbmRlckJhc2VEYXRhU291cmNlIiwiYXBpTmFtZSIsInAiLCJhcGlFbnZQcmVmaXgiLCJyZW5kZXJEZXZEYXRhU291cmNlIiwicmVuZGVyRGF0YWJhc2VTZWN0aW9uIiwiYXBpTmFtZXMiLCJpdGVtcyIsIm1hcCIsIlBPU1RHUkVTX0VOU1VSRV9CT09UU1RSQVBfU0giLCJQT1NUR1JFU19FTlRSWVBPSU5UX1NIIiwiUE9TVEdSRVNfSU1BR0VfRE9DS0VSRklMRSIsIlNBTVBMRV9FTlRJVFlfVFMiLCJTQU1QTEVfUkVQT1NJVE9SWV9UUyIsInJlbmRlclR5cGVPcm1EYXRhU291cmNlIiwiREFUQUJBU0VfRE9DU19NRCIsInR5cGVvcm1DbGkiLCJhcmdzIiwibWlncmF0ZVJ1blNjcmlwdCIsIm1pZ3JhdGVHZW5lcmF0ZVNjcmlwdCIsImdldE9wdGlvbnMiLCJfd29ya3NwYWNlIiwiY2xpT3B0aW9ucyIsImRhdGFiYXNlUHJvdmlkZXIiLCJjaG9pY2VzIiwibGFiZWwiLCJzZWxlY3RlZCIsInBpY2siLCJjIiwic2NhZmZvbGQiLCJvcHRpb25zIiwiaWRlbnQiLCJhcGlQcm9qZWN0cyIsImdldFByb2plY3RzIiwicHJvamVjdCIsImFwaU5hbWVGcm9tUHJvamVjdERpciIsImRpciIsInBvc3RncmVzU3VwZXJ1c2VyIiwicG9zdGdyZXNQYXNzd29yZCIsImJvb3RzdHJhcFVzZXIiLCJib290c3RyYXBQYXNzd29yZCIsImRldkNvbnRhaW5lciIsImdldE1vZGVsIiwiV0VMTF9LTk9XTl9GSUxFUyIsIkRFVkNPTlRBSU5FUl9KU09OIiwiY29tcG9zZSIsIkRPQ0tFUl9DT01QT1NFIiwibmV0d29ya0tleSIsImFkZEhlYWRlckNvbW1lbnQiLCJhZGRTZXJ2aWNlIiwiYnVpbGQiLCJjb250YWluZXJfbmFtZSIsInJlc3RhcnQiLCJwb3J0cyIsInZvbHVtZXMiLCJuZXR3b3JrcyIsImFsaWFzZXMiLCJoZWFsdGhjaGVjayIsInRlc3QiLCJpbnRlcnZhbCIsInRpbWVvdXQiLCJyZXRyaWVzIiwiYWRkRW52RmlsZSIsIkdJVElHTk9SRSIsImFkZCIsImFkZEZpbGUiLCJmaWxlcyIsImZpbGVQYXRoIiwiY29udGVudCIsImFkZEV4dGVuc2lvbiIsIkRPQ0tFUkZJTEUiLCJhZGRMYXllciIsIndpdGhQb3N0Z3Jlc3FsQ2xpZW50IiwicGtnIiwiUEFDS0FHRV9KU09OIiwiYWRkRGVwZW5kZW5jeSIsIkNSRUFURV9WRVJTSU9OIiwiYWRkU2NyaXB0IiwicHJpbWFyeUFwaSIsIndyaXRlRGF0YWJhc2VDb25maWciLCJ3aXJlQXBpRGF0YWJhc2UiLCJlbnYiLCJFTlZfRklMRSIsImFkZEVudHJ5Iiwid3JpdGVBcGlEYXRhYmFzZUVudiIsImNvbmZpZyIsIkNPTkZJR19ZQU1MIiwiZGV2Q29uZmlnIiwiQ09ORklHX0RFVl9ZQU1MIiwicGVuZGluZyIsImZpbHRlciIsImhhc0RhdGFTb3VyY2UiLCJoYXNEYXRhU291cmNlcyIsImFwcGVuZERhdGFTb3VyY2UiLCJhZGRTZWN0aW9uIiwicHJlZml4Iiwic3FsQXBpIiwiTUFJTl9UUyIsInNldERhdGFTb3VyY2VOYW1lIiwiYWRkU2lkZUVmZmVjdEltcG9ydCIsImFkZEVudGl0eSIsImNsYXNzTmFtZSIsImltcG9ydFBhdGgiLCJhZGRSZXBvc2l0b3J5IiwiQVBJX1BBQ0tBR0VfSlNPTiJdLCJtYXBwaW5ncyI6Ijs7OzsrQkF5Z0JhQTs7O2VBQUFBOzs7NEJBemdCZTt3QkFDQzswQkFDUjt3QkFDQTt1QkFHWTtrQ0FLMUI7eUJBTXdCO3lCQUNxQjtBQUlwRCwyRkFBMkYsR0FDM0YsTUFBTUMscUJBQXFCO0FBQzNCLE1BQU1DLDBCQUEwQjtBQUNoQyxNQUFNQyxrQkFDTDtBQVFELGdFQUFnRSxHQUNoRSxTQUFTQyxxQkFBcUJDLFNBQTRCLEVBQUVDLEdBQVc7SUFDdEUsSUFBSTtRQUNILE1BQU1DLE1BQU1DLElBQUFBLG9CQUFZLEVBQUNDLElBQUFBLGNBQUksRUFBQ0osVUFBVUssU0FBUyxFQUFFLFNBQVM7UUFDNUQsTUFBTUMsT0FBT0osSUFBSUssS0FBSyxDQUFDLE1BQU1DLElBQUksQ0FBQyxDQUFDQyxJQUFNQSxFQUFFQyxVQUFVLENBQUMsR0FBR1QsSUFBSSxDQUFDLENBQUM7UUFDL0QsTUFBTVUsUUFBUUwsTUFBTU0sTUFBTVgsSUFBSVksTUFBTSxHQUFHLEdBQUdDO1FBQzFDLE9BQU9ILFNBQVNJO0lBQ2pCLEVBQUUsT0FBTTtRQUNQLE9BQU9B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
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "FinalizeFeature", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return FinalizeFeature;
9
+ }
10
+ });
11
+ const _nodefs = require("node:fs");
12
+ const _nodepath = require("node:path");
13
+ const _interop = require("@decohere/nexus/interop");
14
+ const _prompt = require("../../domain/prompt.js");
15
+ const _const = require("../../models/const.js");
16
+ const _workspacereadme = require("../workspace-readme.js");
17
+ const _biomeformat = require("./biome-format.js");
18
+ function readExistingEnvValue(workspace, key) {
19
+ try {
20
+ const raw = (0, _nodefs.readFileSync)((0, _nodepath.join)(workspace.targetDir, ".env"), "utf8");
21
+ const line = raw.split("\n").find((l)=>l.startsWith(`${key}=`));
22
+ const value = line?.slice(key.length + 1).trim();
23
+ return value || undefined;
24
+ } catch {
25
+ return undefined;
26
+ }
27
+ }
28
+ // State resolved during scaffold(), consumed by finalize().
29
+ let _pm = null;
30
+ let _shouldInstall = true;
31
+ async function getOptions(_workspace, cliOptions) {
32
+ if (cliOptions.installDependencies != null) {
33
+ _shouldInstall = cliOptions.installDependencies === "yes";
34
+ }
35
+ return {};
36
+ }
37
+ async function scaffold(workspace, options) {
38
+ // Local scaffold default — production deploys must set NODE_ENV explicitly.
39
+ const nodeEnv = readExistingEnvValue(workspace, "NODE_ENV") ?? "dev";
40
+ workspace.getModel(_const.WELL_KNOWN_FILES.ENV_FILE)?.addEntry("NODE_ENV", nodeEnv);
41
+ // Durable day-2 CLI examples (also refreshed on `webapi add`).
42
+ (0, _workspacereadme.writeWorkspaceReadme)(workspace, options);
43
+ if (process.env.DECOHERE_BUILDER === "1") {
44
+ _shouldInstall = false;
45
+ return;
46
+ }
47
+ _pm = (0, _interop.detectPackageManager)(workspace.targetDir) ?? await (0, _interop.promptForPackageManager)();
48
+ if (options.installDependencies != null) {
49
+ _shouldInstall = options.installDependencies === "yes";
50
+ return;
51
+ }
52
+ const answer = await (0, _prompt.ask)("\nInstall dependencies now? [yes/no] (yes): ", "yes");
53
+ _shouldInstall = answer.toLowerCase() !== "no";
54
+ }
55
+ async function finalize(workspace, options) {
56
+ const formatPaths = options.add ? workspace.getProjects().map((project)=>project.dir) : [
57
+ "."
58
+ ];
59
+ (0, _biomeformat.formatScaffoldedFiles)(workspace.targetDir, formatPaths.length > 0 ? formatPaths : [
60
+ "."
61
+ ]);
62
+ if (_shouldInstall) {
63
+ const pm = _pm;
64
+ console.log(`\nScaffolded ${workspace.name}. Installing dependencies...\n`);
65
+ await pm.install({
66
+ cwd: workspace.targetDir
67
+ });
68
+ }
69
+ const apiNames = workspace.getProjects().map((project)=>project.dir.replace(/^projects\//, ""));
70
+ console.log(`\nDone! ${_shouldInstall ? "Next" : "To get started:"} steps:\n`);
71
+ if (!options.add) {
72
+ console.log(` cd ${workspace.name}`);
73
+ }
74
+ if (!_shouldInstall && options.add) {
75
+ const pmName = _pm?.name ?? "pnpm";
76
+ console.log(` ${pmName} install`);
77
+ }
78
+ if (options.add && apiNames[0] != null) {
79
+ console.log(` pnpm run start:${apiNames[0]}`);
80
+ console.log(` docker build -f Dockerfile.${apiNames[0]} .`);
81
+ } else {
82
+ console.log(" code . # open in VS Code");
83
+ console.log(" # then: Reopen in Container (first create runs pnpm install)\n");
84
+ }
85
+ console.log(" See README.md for CLI examples.\n");
86
+ // Drop readline's stdin hold so the CLI / container can exit.
87
+ (0, _prompt.closePrompt)();
88
+ }
89
+ const FinalizeFeature = {
90
+ getOptions,
91
+ scaffold,
92
+ finalize
93
+ };
94
+
95
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9ob21lL3J1bm5lci93b3JrL2RlY29oZXJlL2RlY29oZXJlL3Byb2plY3RzL3NjYWZmb2xkL3NyYy93ZWJhcGkvZmVhdHVyZXMvZmluYWxpemUuZmVhdHVyZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyByZWFkRmlsZVN5bmMgfSBmcm9tIFwibm9kZTpmc1wiO1xuaW1wb3J0IHsgam9pbiB9IGZyb20gXCJub2RlOnBhdGhcIjtcbmltcG9ydCB0eXBlIHsgUGFja2FnZU1hbmFnZXIgfSBmcm9tIFwiQGRlY29oZXJlL25leHVzL2ludGVyb3BcIjtcbmltcG9ydCB7IGRldGVjdFBhY2thZ2VNYW5hZ2VyLCBwcm9tcHRGb3JQYWNrYWdlTWFuYWdlciB9IGZyb20gXCJAZGVjb2hlcmUvbmV4dXMvaW50ZXJvcFwiO1xuaW1wb3J0IHsgYXNrLCBjbG9zZVByb21wdCB9IGZyb20gXCIuLi8uLi9kb21haW4vcHJvbXB0LmpzXCI7XG5pbXBvcnQgeyBXRUxMX0tOT1dOX0ZJTEVTIH0gZnJvbSBcIi4uLy4uL21vZGVscy9jb25zdC5qc1wiO1xuaW1wb3J0IHR5cGUgeyBFbnZGaWxlIH0gZnJvbSBcIi4uLy4uL21vZGVscy9lbnYtZmlsZS5tb2RlbC5qc1wiO1xuaW1wb3J0IHR5cGUgeyBXb3Jrc3BhY2VTY2FmZm9sZCB9IGZyb20gXCIuLi8uLi9tb2RlbHMvcHJvamVjdC5tb2RlbC5qc1wiO1xuaW1wb3J0IHR5cGUgeyBGZWF0dXJlIH0gZnJvbSBcIi4uLy4uL3NlcnZpY2VzL3NjYWZmb2xkLnNlcnZpY2UuanNcIjtcbmltcG9ydCB7IHdyaXRlV29ya3NwYWNlUmVhZG1lIH0gZnJvbSBcIi4uL3dvcmtzcGFjZS1yZWFkbWUuanNcIjtcbmltcG9ydCB7IGZvcm1hdFNjYWZmb2xkZWRGaWxlcyB9IGZyb20gXCIuL2Jpb21lLWZvcm1hdC5qc1wiO1xuXG5mdW5jdGlvbiByZWFkRXhpc3RpbmdFbnZWYWx1ZSh3b3Jrc3BhY2U6IFdvcmtzcGFjZVNjYWZmb2xkLCBrZXk6IHN0cmluZyk6IHN0cmluZyB8IHVuZGVmaW5lZCB7XG5cdHRyeSB7XG5cdFx0Y29uc3QgcmF3ID0gcmVhZEZpbGVTeW5jKGpvaW4od29ya3NwYWNlLnRhcmdldERpciwgXCIuZW52XCIpLCBcInV0ZjhcIik7XG5cdFx0Y29uc3QgbGluZSA9IHJhdy5zcGxpdChcIlxcblwiKS5maW5kKChsKSA9PiBsLnN0YXJ0c1dpdGgoYCR7a2V5fT1gKSk7XG5cdFx0Y29uc3QgdmFsdWUgPSBsaW5lPy5zbGljZShrZXkubGVuZ3RoICsgMSkudHJpbSgpO1xuXHRcdHJldHVybiB2YWx1ZSB8fCB1bmRlZmluZWQ7XG5cdH0gY2F0Y2gge1xuXHRcdHJldHVybiB1bmRlZmluZWQ7XG5cdH1cbn1cblxuLy8gU3RhdGUgcmVzb2x2ZWQgZHVyaW5nIHNjYWZmb2xkKCksIGNvbnN1bWVkIGJ5IGZpbmFsaXplKCkuXG5sZXQgX3BtOiBQYWNrYWdlTWFuYWdlciB8IG51bGwgPSBudWxsO1xubGV0IF9zaG91bGRJbnN0YWxsID0gdHJ1ZTtcblxudHlwZSBGaW5hbGl6ZU9wdGlvbnMgPSB7XG5cdGluc3RhbGxEZXBlbmRlbmNpZXM/OiBcInllc1wiIHwgXCJub1wiO1xuXHRhZGQ/OiBib29sZWFuO1xuXHRkYXRhYmFzZVByb3ZpZGVyPzogXCJub25lXCIgfCBcInBvc3RncmVzL3R5cGVvcm1cIjtcblx0YXV0aFByb3ZpZGVyPzogXCJhdXRoMFwiIHwgXCJub25lXCI7XG5cdHBvc3RtYW5Qcm92aWRlcj86IFwiYXBpXCIgfCBcImxvY2FsXCIgfCBcIm5vbmVcIjtcbn07XG5cbmFzeW5jIGZ1bmN0aW9uIGdldE9wdGlvbnMoXG5cdF93b3Jrc3BhY2U6IFdvcmtzcGFjZVNjYWZmb2xkLFxuXHRjbGlPcHRpb25zOiBQYXJ0aWFsPEZpbmFsaXplT3B0aW9ucz4sXG4pOiBQcm9taXNlPG9iamVjdD4ge1xuXHRpZiAoY2xpT3B0aW9ucy5pbnN0YWxsRGVwZW5kZW5jaWVzICE9IG51bGwpIHtcblx0XHRfc2hvdWxkSW5zdGFsbCA9IGNsaU9wdGlvbnMuaW5zdGFsbERlcGVuZGVuY2llcyA9PT0gXCJ5ZXNcIjtcblx0fVxuXHRyZXR1cm4ge307XG59XG5cbmFzeW5jIGZ1bmN0aW9uIHNjYWZmb2xkKFxuXHR3b3Jrc3BhY2U6IFdvcmtzcGFjZVNjYWZmb2xkLFxuXHRvcHRpb25zOiBGaW5hbGl6ZU9wdGlvbnMsXG4pOiBQcm9taXNlPHZvaWQ+IHtcblx0Ly8gTG9jYWwgc2NhZmZvbGQgZGVmYXVsdCDigJQgcHJvZHVjdGlvbiBkZXBsb3lzIG11c3Qgc2V0IE5PREVfRU5WIGV4cGxpY2l0bHkuXG5cdGNvbnN0IG5vZGVFbnYgPSByZWFkRXhpc3RpbmdFbnZWYWx1ZSh3b3Jrc3BhY2UsIFwiTk9ERV9FTlZcIikgPz8gXCJkZXZcIjtcblx0d29ya3NwYWNlLmdldE1vZGVsPEVudkZpbGU+KFdFTExfS05PV05fRklMRVMuRU5WX0ZJTEUpPy5hZGRFbnRyeShcIk5PREVfRU5WXCIsIG5vZGVFbnYpO1xuXG5cdC8vIER1cmFibGUgZGF5LTIgQ0xJIGV4YW1wbGVzIChhbHNvIHJlZnJlc2hlZCBvbiBgd2ViYXBpIGFkZGApLlxuXHR3cml0ZVdvcmtzcGFjZVJlYWRtZSh3b3Jrc3BhY2UsIG9wdGlvbnMpO1xuXG5cdGlmIChwcm9jZXNzLmVudi5ERUNPSEVSRV9CVUlMREVSID09PSBcIjFcIikge1xuXHRcdF9zaG91bGRJbnN0YWxsID0gZmFsc2U7XG5cdFx0cmV0dXJuO1xuXHR9XG5cdF9wbSA9IGRldGVjdFBhY2thZ2VNYW5hZ2VyKHdvcmtzcGFjZS50YXJnZXREaXIpID8/IChhd2FpdCBwcm9tcHRGb3JQYWNrYWdlTWFuYWdlcigpKTtcblx0aWYgKG9wdGlvbnMuaW5zdGFsbERlcGVuZGVuY2llcyAhPSBudWxsKSB7XG5cdFx0X3Nob3VsZEluc3RhbGwgPSBvcHRpb25zLmluc3RhbGxEZXBlbmRlbmNpZXMgPT09IFwieWVzXCI7XG5cdFx0cmV0dXJuO1xuXHR9XG5cblx0Y29uc3QgYW5zd2VyID0gYXdhaXQgYXNrKFwiXFxuSW5zdGFsbCBkZXBlbmRlbmNpZXMgbm93PyBbeWVzL25vXSAoeWVzKTogXCIsIFwieWVzXCIpO1xuXHRfc2hvdWxkSW5zdGFsbCA9IGFuc3dlci50b0xvd2VyQ2FzZSgpICE9PSBcIm5vXCI7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGZpbmFsaXplKFxuXHR3b3Jrc3BhY2U6IFdvcmtzcGFjZVNjYWZmb2xkLFxuXHRvcHRpb25zOiBGaW5hbGl6ZU9wdGlvbnMsXG4pOiBQcm9taXNlPHZvaWQ+IHtcblx0Y29uc3QgZm9ybWF0UGF0aHMgPSBvcHRpb25zLmFkZFxuXHRcdD8gd29ya3NwYWNlLmdldFByb2plY3RzKCkubWFwKChwcm9qZWN0KSA9PiBwcm9qZWN0LmRpcilcblx0XHQ6IFtcIi5cIl07XG5cdGZvcm1hdFNjYWZmb2xkZWRGaWxlcyh3b3Jrc3BhY2UudGFyZ2V0RGlyLCBmb3JtYXRQYXRocy5sZW5ndGggPiAwID8gZm9ybWF0UGF0aHMgOiBbXCIuXCJdKTtcblxuXHRpZiAoX3Nob3VsZEluc3RhbGwpIHtcblx0XHRjb25zdCBwbSA9IF9wbSE7XG5cdFx0Y29uc29sZS5sb2coYFxcblNjYWZmb2xkZWQgJHt3b3Jrc3BhY2UubmFtZX0uIEluc3RhbGxpbmcgZGVwZW5kZW5jaWVzLi4uXFxuYCk7XG5cdFx0YXdhaXQgcG0uaW5zdGFsbCh7IGN3ZDogd29ya3NwYWNlLnRhcmdldERpciB9KTtcblx0fVxuXG5cdGNvbnN0IGFwaU5hbWVzID0gd29ya3NwYWNlXG5cdFx0LmdldFByb2plY3RzKClcblx0XHQubWFwKChwcm9qZWN0KSA9PiBwcm9qZWN0LmRpci5yZXBsYWNlKC9ecHJvamVjdHNcXC8vLCBcIlwiKSk7XG5cdGNvbnNvbGUubG9nKGBcXG5Eb25lISAke19zaG91bGRJbnN0YWxsID8gXCJOZXh0XCIgOiBcIlRvIGdldCBzdGFydGVkOlwifSBzdGVwczpcXG5gKTtcblx0aWYgKCFvcHRpb25zLmFkZCkge1xuXHRcdGNvbnNvbGUubG9nKGAgIGNkICR7d29ya3NwYWNlLm5hbWV9YCk7XG5cdH1cblx0aWYgKCFfc2hvdWxkSW5zdGFsbCAmJiBvcHRpb25zLmFkZCkge1xuXHRcdGNvbnN0IHBtTmFtZSA9IF9wbT8ubmFtZSA/PyBcInBucG1cIjtcblx0XHRjb25zb2xlLmxvZyhgICAke3BtTmFtZX0gaW5zdGFsbGApO1xuXHR9XG5cdGlmIChvcHRpb25zLmFkZCAmJiBhcGlOYW1lc1swXSAhPSBudWxsKSB7XG5cdFx0Y29uc29sZS5sb2coYCAgcG5wbSBydW4gc3RhcnQ6JHthcGlOYW1lc1swXX1gKTtcblx0XHRjb25zb2xlLmxvZyhgICBkb2NrZXIgYnVpbGQgLWYgRG9ja2VyZmlsZS4ke2FwaU5hbWVzWzBdfSAuYCk7XG5cdH0gZWxzZSB7XG5cdFx0Y29uc29sZS5sb2coXCIgIGNvZGUgLiAgICAgICAgICMgb3BlbiBpbiBWUyBDb2RlXCIpO1xuXHRcdGNvbnNvbGUubG9nKFxuXHRcdFx0XCIgICAgICAgICAgICAgICAgICMgdGhlbjogUmVvcGVuIGluIENvbnRhaW5lciAoZmlyc3QgY3JlYXRlIHJ1bnMgcG5wbSBpbnN0YWxsKVxcblwiLFxuXHRcdCk7XG5cdH1cblx0Y29uc29sZS5sb2coXCIgIFNlZSBSRUFETUUubWQgZm9yIENMSSBleGFtcGxlcy5cXG5cIik7XG5cblx0Ly8gRHJvcCByZWFkbGluZSdzIHN0ZGluIGhvbGQgc28gdGhlIENMSSAvIGNvbnRhaW5lciBjYW4gZXhpdC5cblx0Y2xvc2VQcm9tcHQoKTtcbn1cblxuZXhwb3J0IGNvbnN0IEZpbmFsaXplRmVhdHVyZTogRmVhdHVyZSA9IHsgZ2V0T3B0aW9ucywgc2NhZmZvbGQsIGZpbmFsaXplIH07XG4iXSwibmFtZXMiOlsiRmluYWxpemVGZWF0dXJlIiwicmVhZEV4aXN0aW5nRW52VmFsdWUiLCJ3b3Jrc3BhY2UiLCJrZXkiLCJyYXciLCJyZWFkRmlsZVN5bmMiLCJqb2luIiwidGFyZ2V0RGlyIiwibGluZSIsInNwbGl0IiwiZmluZCIsImwiLCJzdGFydHNXaXRoIiwidmFsdWUiLCJzbGljZSIsImxlbmd0aCIsInRyaW0iLCJ1bmRlZmluZWQiLCJfcG0iLCJfc2hvdWxkSW5zdGFsbCIsImdldE9wdGlvbnMiLCJfd29ya3NwYWNlIiwiY2xpT3B0aW9ucyIsImluc3RhbGxEZXBlbmRlbmNpZXMiLCJzY2FmZm9sZCIsIm9wdGlvbnMiLCJub2RlRW52IiwiZ2V0TW9kZWwiLCJXRUxMX0tOT1dOX0ZJTEVTIiwiRU5WX0ZJTEUiLCJhZGRFbnRyeSIsIndyaXRlV29ya3NwYWNlUmVhZG1lIiwicHJvY2VzcyIsImVudiIsIkRFQ09IRVJFX0JVSUxERVIiLCJkZXRlY3RQYWNrYWdlTWFuYWdlciIsInByb21wdEZvclBhY2thZ2VNYW5hZ2VyIiwiYW5zd2VyIiwiYXNrIiwidG9Mb3dlckNhc2UiLCJmaW5hbGl6ZSIsImZvcm1hdFBhdGhzIiwiYWRkIiwiZ2V0UHJvamVjdHMiLCJtYXAiLCJwcm9qZWN0IiwiZGlyIiwiZm9ybWF0U2NhZmZvbGRlZEZpbGVzIiwicG0iLCJjb25zb2xlIiwibG9nIiwibmFtZSIsImluc3RhbGwiLCJjd2QiLCJhcGlOYW1lcyIsInJlcGxhY2UiLCJwbU5hbWUiLCJjbG9zZVByb21wdCJdLCJtYXBwaW5ncyI6Ijs7OzsrQkErR2FBOzs7ZUFBQUE7Ozt3QkEvR2dCOzBCQUNSO3lCQUV5Qzt3QkFDN0I7dUJBQ0E7aUNBSUk7NkJBQ0M7QUFFdEMsU0FBU0MscUJBQXFCQyxTQUE0QixFQUFFQyxHQUFXO0lBQ3RFLElBQUk7UUFDSCxNQUFNQyxNQUFNQyxJQUFBQSxvQkFBWSxFQUFDQyxJQUFBQSxjQUFJLEVBQUNKLFVBQVVLLFNBQVMsRUFBRSxTQUFTO1FBQzVELE1BQU1DLE9BQU9KLElBQUlLLEtBQUssQ0FBQyxNQUFNQyxJQUFJLENBQUMsQ0FBQ0MsSUFBTUEsRUFBRUMsVUFBVSxDQUFDLEdBQUdULElBQUksQ0FBQyxDQUFDO1FBQy9ELE1BQU1VLFFBQVFMLE1BQU1NLE1BQU1YLElBQUlZLE1BQU0sR0FBRyxHQUFHQztRQUMxQyxPQUFPSCxTQUFTSTtJQUNqQixFQUFFLE9BQU07UUFDUCxPQUFPQTtJQUNSO0FBQ0Q7QUFFQSw0REFBNEQ7QUFDNUQsSUFBSUMsTUFBNkI7QUFDakMsSUFBSUMsaUJBQWlCO0FBVXJCLGVBQWVDLFdBQ2RDLFVBQTZCLEVBQzdCQyxVQUFvQztJQUVwQyxJQUFJQSxXQUFXQyxtQkFBbUIsSUFBSSxNQUFNO1FBQzNDSixpQkFBaUJHLFdBQVdDLG1CQUFtQixLQUFLO0lBQ3JEO0lBQ0EsT0FBTyxDQUFDO0FBQ1Q7QUFFQSxlQUFlQyxTQUNkdEIsU0FBNEIsRUFDNUJ1QixPQUF3QjtJQUV4Qiw0RUFBNEU7SUFDNUUsTUFBTUMsVUFBVXpCLHFCQUFxQkMsV0FBVyxlQUFlO0lBQy9EQSxVQUFVeUIsUUFBUSxDQUFVQyx1QkFBZ0IsQ0FBQ0MsUUFBUSxHQUFHQyxTQUFTLFlBQVlKO0lBRTdFLCtEQUErRDtJQUMvREssSUFBQUEscUNBQW9CLEVBQUM3QixXQUFXdUI7SUFFaEMsSUFBSU8sUUFBUUMsR0FBRyxDQUFDQyxnQkFBZ0IsS0FBSyxLQUFLO1FBQ3pDZixpQkFBaUI7UUFDakI7SUFDRDtJQUNBRCxNQUFNaUIsSUFBQUEsNkJBQW9CLEVBQUNqQyxVQUFVSyxTQUFTLEtBQU0sTUFBTTZCLElBQUFBLGdDQUF1QjtJQUNqRixJQUFJWCxRQUFRRixtQkFBbUIsSUFBSSxNQUFNO1FBQ3hDSixpQkFBaUJNLFFBQVFGLG1CQUFtQixLQUFLO1FBQ2pEO0lBQ0Q7SUFFQSxNQUFNYyxTQUFTLE1BQU1DLElBQUFBLFdBQUcsRUFBQyxnREFBZ0Q7SUFDekVuQixpQkFBaUJrQixPQUFPRSxXQUFXLE9BQU87QUFDM0M7QUFFQSxlQUFlQyxTQUNkdEMsU0FBNEIsRUFDNUJ1QixPQUF3QjtJQUV4QixNQUFNZ0IsY0FBY2hCLFFBQVFpQixHQUFHLEdBQzVCeEMsVUFBVXlDLFdBQVcsR0FBR0MsR0FBRyxDQUFDLENBQUNDLFVBQVlBLFFBQVFDLEdBQUcsSUFDcEQ7UUFBQztLQUFJO0lBQ1JDLElBQUFBLGtDQUFxQixFQUFDN0MsVUFBVUssU0FBUyxFQUFFa0MsWUFBWTFCLE1BQU0sR0FBRyxJQUFJMEIsY0FBYztRQUFDO0tBQUk7SUFFdkYsSUFBSXRCLGdCQUFnQjtRQUNuQixNQUFNNkIsS0FBSzlCO1FBQ1grQixRQUFRQyxHQUFHLENBQUMsQ0FBQyxhQUFhLEVBQUVoRCxVQUFVaUQsSUFBSSxDQUFDLDhCQUE4QixDQUFDO1FBQzFFLE1BQU1ILEdBQUdJLE9BQU8sQ0FBQztZQUFFQyxLQUFLbkQsVUFBVUssU0FBUztRQUFDO0lBQzdDO0lBRUEsTUFBTStDLFdBQVdwRCxVQUNmeUMsV0FBVyxHQUNYQyxHQUFHLENBQUMsQ0FBQ0MsVUFBWUEsUUFBUUMsR0FBRyxDQUFDUyxPQUFPLENBQUMsZUFBZTtJQUN0RE4sUUFBUUMsR0FBRyxDQUFDLENBQUMsUUFBUSxFQUFFL0IsaUJBQWlCLFNBQVMsa0JBQWtCLFNBQVMsQ0FBQztJQUM3RSxJQUFJLENBQUNNLFFBQVFpQixHQUFHLEVBQUU7UUFDakJPLFFBQVFDLEdBQUcsQ0FBQyxDQUFDLEtBQUssRUFBRWhELFVBQVVpRCxJQUFJLEVBQUU7SUFDckM7SUFDQSxJQUFJLENBQUNoQyxrQkFBa0JNLFFBQVFpQixHQUFHLEVBQUU7UUFDbkMsTUFBTWMsU0FBU3RDLEtBQUtpQyxRQUFRO1FBQzVCRixRQUFRQyxHQUFHLENBQUMsQ0FBQyxFQUFFLEVBQUVNLE9BQU8sUUFBUSxDQUFDO0lBQ2xDO0lBQ0EsSUFBSS9CLFFBQVFpQixHQUFHLElBQUlZLFFBQVEsQ0FBQyxFQUFFLElBQUksTUFBTTtRQUN2Q0wsUUFBUUMsR0FBRyxDQUFDLENBQUMsaUJBQWlCLEVBQUVJLFFBQVEsQ0FBQyxFQUFFLEVBQUU7UUFDN0NMLFFBQVFDLEdBQUcsQ0FBQyxDQUFDLDZCQUE2QixFQUFFSSxRQUFRLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQztJQUM1RCxPQUFPO1FBQ05MLFFBQVFDLEdBQUcsQ0FBQztRQUNaRCxRQUFRQyxHQUFHLENBQ1Y7SUFFRjtJQUNBRCxRQUFRQyxHQUFHLENBQUM7SUFFWiw4REFBOEQ7SUFDOURPLElBQUFBLG1CQUFXO0FBQ1o7QUFFTyxNQUFNekQsa0JBQTJCO0lBQUVvQjtJQUFZSTtJQUFVZ0I7QUFBUyJ9