@decohere/create 0.0.1-dev.19 → 0.0.1-dev.20261105.46

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 (96) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +254 -0
  3. package/commonjs/src/cli.js +1 -34
  4. package/commonjs/src/index.js +26 -0
  5. package/commonjs/src/resolve-launch.js +100 -0
  6. package/commonjs/src/runner.js +63 -14
  7. package/commonjs/src/version.js +25 -9
  8. package/es6/src/cli.js +1 -34
  9. package/es6/src/index.js +2 -0
  10. package/es6/src/resolve-launch.js +87 -0
  11. package/es6/src/runner.js +67 -24
  12. package/es6/src/version.js +14 -6
  13. package/pack-builder-tgz.mjs +177 -0
  14. package/package.json +23 -11
  15. package/types/src/index.d.ts +3 -0
  16. package/types/src/resolve-launch.d.ts +20 -0
  17. package/types/src/runner.d.ts +3 -11
  18. package/types/src/version.d.ts +1 -1
  19. package/commonjs/src/domain/prompt.js +0 -55
  20. package/commonjs/src/models/compose.model.js +0 -82
  21. package/commonjs/src/models/config-yaml.model.js +0 -34
  22. package/commonjs/src/models/const.js +0 -29
  23. package/commonjs/src/models/container-image.model.js +0 -88
  24. package/commonjs/src/models/dev-container.model.js +0 -73
  25. package/commonjs/src/models/env-file.model.js +0 -33
  26. package/commonjs/src/models/gitignore.model.js +0 -32
  27. package/commonjs/src/models/launch-json.model.js +0 -52
  28. package/commonjs/src/models/package-json.model.js +0 -64
  29. package/commonjs/src/models/project.model.js +0 -126
  30. package/commonjs/src/models/source-file.js +0 -6
  31. package/commonjs/src/models/tsconfig.model.js +0 -34
  32. package/commonjs/src/models/vscode-settings.model.js +0 -31
  33. package/commonjs/src/services/scaffold.service.js +0 -62
  34. package/commonjs/src/webapi/create-api.js +0 -169
  35. package/commonjs/src/webapi/features/auth.feature.js +0 -166
  36. package/commonjs/src/webapi/features/auth0-cli.js +0 -151
  37. package/commonjs/src/webapi/features/database.feature.js +0 -151
  38. package/commonjs/src/webapi/features/finalize.feature.js +0 -51
  39. package/commonjs/src/webapi/features/postman.feature.js +0 -309
  40. package/commonjs/src/webapi/models/api-package-json.model.js +0 -57
  41. package/commonjs/src/webapi/models/auth-config.model.js +0 -23
  42. package/commonjs/src/webapi/models/controller-source-file.model.js +0 -180
  43. package/commonjs/src/webapi/models/main-source-file.model.js +0 -132
  44. package/commonjs/src/webapi/models/sample-files.model.js +0 -60
  45. package/es6/src/domain/prompt.js +0 -37
  46. package/es6/src/models/compose.model.js +0 -77
  47. package/es6/src/models/config-yaml.model.js +0 -30
  48. package/es6/src/models/const.js +0 -19
  49. package/es6/src/models/container-image.model.js +0 -84
  50. package/es6/src/models/dev-container.model.js +0 -68
  51. package/es6/src/models/env-file.model.js +0 -29
  52. package/es6/src/models/gitignore.model.js +0 -22
  53. package/es6/src/models/launch-json.model.js +0 -42
  54. package/es6/src/models/package-json.model.js +0 -60
  55. package/es6/src/models/project.model.js +0 -111
  56. package/es6/src/models/source-file.js +0 -3
  57. package/es6/src/models/tsconfig.model.js +0 -24
  58. package/es6/src/models/vscode-settings.model.js +0 -21
  59. package/es6/src/services/scaffold.service.js +0 -52
  60. package/es6/src/webapi/create-api.js +0 -151
  61. package/es6/src/webapi/features/auth.feature.js +0 -115
  62. package/es6/src/webapi/features/auth0-cli.js +0 -116
  63. package/es6/src/webapi/features/database.feature.js +0 -141
  64. package/es6/src/webapi/features/finalize.feature.js +0 -41
  65. package/es6/src/webapi/features/postman.feature.js +0 -258
  66. package/es6/src/webapi/models/api-package-json.model.js +0 -52
  67. package/es6/src/webapi/models/auth-config.model.js +0 -19
  68. package/es6/src/webapi/models/controller-source-file.model.js +0 -171
  69. package/es6/src/webapi/models/main-source-file.model.js +0 -128
  70. package/es6/src/webapi/models/sample-files.model.js +0 -42
  71. package/types/src/domain/prompt.d.ts +0 -2
  72. package/types/src/models/compose.model.d.ts +0 -32
  73. package/types/src/models/config-yaml.model.d.ts +0 -16
  74. package/types/src/models/const.d.ts +0 -17
  75. package/types/src/models/container-image.model.d.ts +0 -15
  76. package/types/src/models/dev-container.model.d.ts +0 -21
  77. package/types/src/models/env-file.model.d.ts +0 -14
  78. package/types/src/models/gitignore.model.d.ts +0 -6
  79. package/types/src/models/launch-json.model.d.ts +0 -5
  80. package/types/src/models/package-json.model.d.ts +0 -18
  81. package/types/src/models/project.model.d.ts +0 -55
  82. package/types/src/models/source-file.d.ts +0 -11
  83. package/types/src/models/tsconfig.model.d.ts +0 -5
  84. package/types/src/models/vscode-settings.model.d.ts +0 -7
  85. package/types/src/services/scaffold.service.d.ts +0 -22
  86. package/types/src/webapi/create-api.d.ts +0 -7
  87. package/types/src/webapi/features/auth.feature.d.ts +0 -7
  88. package/types/src/webapi/features/auth0-cli.d.ts +0 -22
  89. package/types/src/webapi/features/database.feature.d.ts +0 -5
  90. package/types/src/webapi/features/finalize.feature.d.ts +0 -2
  91. package/types/src/webapi/features/postman.feature.d.ts +0 -7
  92. package/types/src/webapi/models/api-package-json.model.d.ts +0 -17
  93. package/types/src/webapi/models/auth-config.model.d.ts +0 -18
  94. package/types/src/webapi/models/controller-source-file.model.d.ts +0 -42
  95. package/types/src/webapi/models/main-source-file.model.d.ts +0 -36
  96. package/types/src/webapi/models/sample-files.model.d.ts +0 -7
@@ -0,0 +1,87 @@
1
+ import { basename, resolve } from "node:path";
2
+ const HOST_FLAGS_WITH_VALUE = new Set([
3
+ "--config-file",
4
+ "--log-level"
5
+ ]);
6
+ /**
7
+ * Map host / npm-create argv onto the product CLI (`create webapi` / `add api`).
8
+ * `npm create @decohere webapi` already says “create”, so we prepend `create`
9
+ * rather than making users type `create create webapi`.
10
+ */ export function toProductArgv(tokens) {
11
+ if (tokens[0] === "webapi" && tokens[1] === "add") {
12
+ return [
13
+ "add",
14
+ "api",
15
+ ...tokens.slice(2)
16
+ ];
17
+ }
18
+ if (tokens[0] === "webapi") {
19
+ return [
20
+ "create",
21
+ "webapi",
22
+ ...tokens.slice(1)
23
+ ];
24
+ }
25
+ return tokens;
26
+ }
27
+ export function containerArgvFromHost(argv) {
28
+ const user = argv[0]?.includes("node") || argv[0]?.endsWith("node") ? argv.slice(2) : argv;
29
+ const stripped = [];
30
+ for(let i = 0; i < user.length; i++){
31
+ const arg = user[i];
32
+ if (arg != null && HOST_FLAGS_WITH_VALUE.has(arg)) {
33
+ i++;
34
+ continue;
35
+ }
36
+ if (arg != null) stripped.push(arg);
37
+ }
38
+ return toProductArgv(stripped);
39
+ }
40
+ /**
41
+ * Host argv → directory to mount and argv for the in-image `decohere` entrypoint.
42
+ * Positional `webapi my-app` / `create webapi my-app` wins over `-d` (npm treats `-d`
43
+ * as its own loglevel).
44
+ */ export function resolveLaunch(argv, cwd = process.cwd()) {
45
+ if (argv.length === 1 && (argv[0] === "--help" || argv[0] === "-h" || argv[0] === "--version")) {
46
+ return {
47
+ targetDir: cwd,
48
+ containerArgv: argv,
49
+ projectName: basename(cwd),
50
+ kind: "scaffold"
51
+ };
52
+ }
53
+ let directoryFlag;
54
+ const withoutD = [];
55
+ for(let i = 0; i < argv.length; i++){
56
+ const arg = argv[i];
57
+ if (arg === "-d") {
58
+ directoryFlag = argv[i + 1];
59
+ i++;
60
+ continue;
61
+ }
62
+ if (arg != null) withoutD.push(arg);
63
+ }
64
+ const product = toProductArgv(withoutD);
65
+ const isAdd = product[0] === "add";
66
+ let positional;
67
+ let withoutPositional = product;
68
+ if (!isAdd && product[0] === "create" && product[1] === "webapi" && product[2] != null && !product[2].startsWith("-")) {
69
+ positional = product[2];
70
+ withoutPositional = [
71
+ product[0],
72
+ product[1],
73
+ ...product.slice(3)
74
+ ];
75
+ }
76
+ const targetDir = resolve(cwd, positional?.trim() || directoryFlag?.trim() || ".");
77
+ return {
78
+ targetDir,
79
+ containerArgv: [
80
+ ...withoutPositional,
81
+ "-d",
82
+ "."
83
+ ],
84
+ projectName: basename(targetDir),
85
+ kind: product[0] === "auth0" ? "auth0" : "scaffold"
86
+ };
87
+ }
package/es6/src/runner.js CHANGED
@@ -1,23 +1,23 @@
1
1
  import { execFileSync, spawnSync } from "node:child_process";
2
- import { mkdirSync } from "node:fs";
2
+ import { existsSync, mkdirSync } from "node:fs";
3
+ import { homedir } from "node:os";
3
4
  import { resolve } from "node:path";
4
- import { CREATE_VERSION } from "./version.js";
5
- const BUILDER_IMAGE = `ghcr.io/decohere/scaffold:${CREATE_VERSION}`;
5
+ import { resolveLaunch } from "./resolve-launch.js";
6
+ import { CREATE_IMAGE_REPOSITORY, CREATE_VERSION } from "./version.js";
7
+ const BUILDER_IMAGE = `${CREATE_IMAGE_REPOSITORY}:${CREATE_VERSION}`;
6
8
  /**
7
- * Run the scaffold inside a pre-built Docker builder image.
8
- *
9
- * Called when `DECOHERE_BUILDER` is not set — i.e. the CLI is running on the
10
- * host, outside the container. Mounts the target directory as `/workspace` so
11
- * the scaffolded files are written back to the host filesystem.
12
- *
13
- * If `~/.config/auth0` exists on the host the directory is also mounted so
14
- * the auth0 CLI inside the container can reuse existing credentials silently.
15
- */ export function runInBuilder(opts) {
16
- const targetDir = resolve(opts.d ?? ".");
9
+ * Pull/run the Decohere scaffolder image. Host `@decohere/create` does not load
10
+ * the `@decohere/scaffold` dependency tree — that lives in the image only.
11
+ */ export function runInBuilder(argv) {
12
+ const { targetDir, containerArgv, projectName, kind } = resolveLaunch(argv);
17
13
  mkdirSync(targetDir, {
18
14
  recursive: true
19
15
  });
20
- // Ensure Docker is available.
16
+ if (kind === "scaffold") {
17
+ mkdirSync(resolve(targetDir, ".devcontainer", "npm-auth"), {
18
+ recursive: true
19
+ });
20
+ }
21
21
  try {
22
22
  execFileSync("docker", [
23
23
  "info"
@@ -28,28 +28,71 @@ const BUILDER_IMAGE = `ghcr.io/decohere/scaffold:${CREATE_VERSION}`;
28
28
  console.error("\n Docker is required to scaffold a Decohere project.\n" + " Install Docker Desktop: https://www.docker.com/products/docker-desktop\n");
29
29
  process.exit(1);
30
30
  }
31
+ const hostUid = process.getuid?.();
32
+ const hostGid = process.getgid?.() ?? hostUid;
33
+ const runAsHostUser = hostUid != null && hostGid != null;
34
+ const npmCacheDir = resolve(homedir(), ".cache/decohere-create/npm");
35
+ mkdirSync(npmCacheDir, {
36
+ recursive: true
37
+ });
38
+ const builderHome = "/tmp/decohere-create-home";
31
39
  const volumes = [
32
40
  "-v",
33
- `${targetDir}:/workspace`
41
+ `${targetDir}:/workspace`,
42
+ "-v",
43
+ `${npmCacheDir}:/npm-cache`
44
+ ];
45
+ const envArgs = [
46
+ "-e",
47
+ "DECOHERE_BUILDER=1",
48
+ "-e",
49
+ `DECOHERE_PROJECT_NAME=${projectName}`,
50
+ "-e",
51
+ "NPM_CONFIG_CACHE=/npm-cache",
52
+ "-e",
53
+ "NPM_CONFIG_PREFER_OFFLINE=true",
54
+ "-e",
55
+ `HOME=${builderHome}`
56
+ ];
57
+ const npmrcPath = resolve(homedir(), ".npmrc");
58
+ if (existsSync(npmrcPath)) {
59
+ volumes.push("-v", `${npmrcPath}:/tmp/host-npmrc:ro`);
60
+ envArgs.push("-e", "NPM_CONFIG_USERCONFIG=/tmp/host-npmrc");
61
+ }
62
+ const auth0ConfigDir = resolve(homedir(), ".config/auth0");
63
+ mkdirSync(auth0ConfigDir, {
64
+ recursive: true
65
+ });
66
+ volumes.push("-v", `${auth0ConfigDir}:${builderHome}/.config/auth0`);
67
+ const userArgs = [];
68
+ if (runAsHostUser) {
69
+ userArgs.push("--user", `${hostUid}:${hostGid}`);
70
+ } else if (hostUid != null) {
71
+ envArgs.push("-e", `HOST_UID=${hostUid}`);
72
+ if (hostGid != null) envArgs.push("-e", `HOST_GID=${hostGid}`);
73
+ }
74
+ console.log(kind === "auth0" ? " Running Auth0 CLI in the scaffolder image...\n" : " Running scaffolding in a sandboxed container...\n");
75
+ const ttyFlags = process.stdin.isTTY ? [
76
+ "-it"
77
+ ] : [
78
+ "-i"
34
79
  ];
35
- console.log(" Running scaffolding in a sandboxed container...\n");
36
80
  const result = spawnSync("docker", [
37
81
  "run",
38
82
  "--rm",
39
- "-it",
83
+ ...ttyFlags,
84
+ ...userArgs,
40
85
  ...volumes,
41
- "-e",
42
- "DECOHERE_BUILDER=1",
86
+ ...envArgs,
43
87
  "-w",
44
88
  "/workspace",
45
89
  BUILDER_IMAGE,
46
- "webapi",
47
- "-d",
48
- "."
90
+ ...containerArgv
49
91
  ], {
50
92
  stdio: "inherit"
51
93
  });
94
+ if (result.status === 0 && kind === "scaffold") {
95
+ console.log(`\n Scaffold complete!\n\n Open your project:\n\n code ${targetDir}\n`);
96
+ }
52
97
  process.exit(result.status ?? 0);
53
98
  }
54
-
55
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9ob21lL3J1bm5lci93b3JrL2RlY29oZXJlL2RlY29oZXJlL3Byb2plY3RzL2NyZWF0ZS9zcmMvcnVubmVyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGV4ZWNGaWxlU3luYywgc3Bhd25TeW5jIH0gZnJvbSBcIm5vZGU6Y2hpbGRfcHJvY2Vzc1wiO1xuaW1wb3J0IHsgbWtkaXJTeW5jIH0gZnJvbSBcIm5vZGU6ZnNcIjtcbmltcG9ydCB7IHJlc29sdmUgfSBmcm9tIFwibm9kZTpwYXRoXCI7XG5pbXBvcnQgeyBDUkVBVEVfVkVSU0lPTiB9IGZyb20gXCIuL3ZlcnNpb24uanNcIjtcblxuY29uc3QgQlVJTERFUl9JTUFHRSA9IGBnaGNyLmlvL2RlY29oZXJlL3NjYWZmb2xkOiR7Q1JFQVRFX1ZFUlNJT059YDtcblxuLyoqXG4gKiBSdW4gdGhlIHNjYWZmb2xkIGluc2lkZSBhIHByZS1idWlsdCBEb2NrZXIgYnVpbGRlciBpbWFnZS5cbiAqXG4gKiBDYWxsZWQgd2hlbiBgREVDT0hFUkVfQlVJTERFUmAgaXMgbm90IHNldCDigJQgaS5lLiB0aGUgQ0xJIGlzIHJ1bm5pbmcgb24gdGhlXG4gKiBob3N0LCBvdXRzaWRlIHRoZSBjb250YWluZXIuIE1vdW50cyB0aGUgdGFyZ2V0IGRpcmVjdG9yeSBhcyBgL3dvcmtzcGFjZWAgc29cbiAqIHRoZSBzY2FmZm9sZGVkIGZpbGVzIGFyZSB3cml0dGVuIGJhY2sgdG8gdGhlIGhvc3QgZmlsZXN5c3RlbS5cbiAqXG4gKiBJZiBgfi8uY29uZmlnL2F1dGgwYCBleGlzdHMgb24gdGhlIGhvc3QgdGhlIGRpcmVjdG9yeSBpcyBhbHNvIG1vdW50ZWQgc29cbiAqIHRoZSBhdXRoMCBDTEkgaW5zaWRlIHRoZSBjb250YWluZXIgY2FuIHJldXNlIGV4aXN0aW5nIGNyZWRlbnRpYWxzIHNpbGVudGx5LlxuICovXG5leHBvcnQgZnVuY3Rpb24gcnVuSW5CdWlsZGVyKG9wdHM6IHsgZD86IHN0cmluZyB9KTogbmV2ZXIge1xuXHRjb25zdCB0YXJnZXREaXIgPSByZXNvbHZlKG9wdHMuZCA/PyBcIi5cIik7XG5cdG1rZGlyU3luYyh0YXJnZXREaXIsIHsgcmVjdXJzaXZlOiB0cnVlIH0pO1xuXG5cdC8vIEVuc3VyZSBEb2NrZXIgaXMgYXZhaWxhYmxlLlxuXHR0cnkge1xuXHRcdGV4ZWNGaWxlU3luYyhcImRvY2tlclwiLCBbXCJpbmZvXCJdLCB7IHN0ZGlvOiBcInBpcGVcIiB9KTtcblx0fSBjYXRjaCB7XG5cdFx0Y29uc29sZS5lcnJvcihcblx0XHRcdFwiXFxuICBEb2NrZXIgaXMgcmVxdWlyZWQgdG8gc2NhZmZvbGQgYSBEZWNvaGVyZSBwcm9qZWN0LlxcblwiICtcblx0XHRcdFx0XCIgIEluc3RhbGwgRG9ja2VyIERlc2t0b3A6IGh0dHBzOi8vd3d3LmRvY2tlci5jb20vcHJvZHVjdHMvZG9ja2VyLWRlc2t0b3BcXG5cIixcblx0XHQpO1xuXHRcdHByb2Nlc3MuZXhpdCgxKTtcblx0fVxuXG5cdGNvbnN0IHZvbHVtZXM6IHN0cmluZ1tdID0gW1wiLXZcIiwgYCR7dGFyZ2V0RGlyfTovd29ya3NwYWNlYF07XG5cblx0Y29uc29sZS5sb2coXCIgIFJ1bm5pbmcgc2NhZmZvbGRpbmcgaW4gYSBzYW5kYm94ZWQgY29udGFpbmVyLi4uXFxuXCIpO1xuXG5cdGNvbnN0IHJlc3VsdCA9IHNwYXduU3luYyhcblx0XHRcImRvY2tlclwiLFxuXHRcdFtcblx0XHRcdFwicnVuXCIsXG5cdFx0XHRcIi0tcm1cIixcblx0XHRcdFwiLWl0XCIsXG5cdFx0XHQuLi52b2x1bWVzLFxuXHRcdFx0XCItZVwiLFxuXHRcdFx0XCJERUNPSEVSRV9CVUlMREVSPTFcIixcblx0XHRcdFwiLXdcIixcblx0XHRcdFwiL3dvcmtzcGFjZVwiLFxuXHRcdFx0QlVJTERFUl9JTUFHRSxcblx0XHRcdFwid2ViYXBpXCIsXG5cdFx0XHRcIi1kXCIsXG5cdFx0XHRcIi5cIixcblx0XHRdLFxuXHRcdHsgc3RkaW86IFwiaW5oZXJpdFwiIH0sXG5cdCk7XG5cblx0cHJvY2Vzcy5leGl0KHJlc3VsdC5zdGF0dXMgPz8gMCk7XG59XG4iXSwibmFtZXMiOlsiZXhlY0ZpbGVTeW5jIiwic3Bhd25TeW5jIiwibWtkaXJTeW5jIiwicmVzb2x2ZSIsIkNSRUFURV9WRVJTSU9OIiwiQlVJTERFUl9JTUFHRSIsInJ1bkluQnVpbGRlciIsIm9wdHMiLCJ0YXJnZXREaXIiLCJkIiwicmVjdXJzaXZlIiwic3RkaW8iLCJjb25zb2xlIiwiZXJyb3IiLCJwcm9jZXNzIiwiZXhpdCIsInZvbHVtZXMiLCJsb2ciLCJyZXN1bHQiLCJzdGF0dXMiXSwibWFwcGluZ3MiOiJBQUFBLFNBQVNBLFlBQVksRUFBRUMsU0FBUyxRQUFRLHFCQUFxQjtBQUM3RCxTQUFTQyxTQUFTLFFBQVEsVUFBVTtBQUNwQyxTQUFTQyxPQUFPLFFBQVEsWUFBWTtBQUNwQyxTQUFTQyxjQUFjLFFBQVEsZUFBZTtBQUU5QyxNQUFNQyxnQkFBZ0IsQ0FBQywwQkFBMEIsRUFBRUQsZ0JBQWdCO0FBRW5FOzs7Ozs7Ozs7Q0FTQyxHQUNELE9BQU8sU0FBU0UsYUFBYUMsSUFBb0I7SUFDaEQsTUFBTUMsWUFBWUwsUUFBUUksS0FBS0UsQ0FBQyxJQUFJO0lBQ3BDUCxVQUFVTSxXQUFXO1FBQUVFLFdBQVc7SUFBSztJQUV2Qyw4QkFBOEI7SUFDOUIsSUFBSTtRQUNIVixhQUFhLFVBQVU7WUFBQztTQUFPLEVBQUU7WUFBRVcsT0FBTztRQUFPO0lBQ2xELEVBQUUsT0FBTTtRQUNQQyxRQUFRQyxLQUFLLENBQ1osNkRBQ0M7UUFFRkMsUUFBUUMsSUFBSSxDQUFDO0lBQ2Q7SUFFQSxNQUFNQyxVQUFvQjtRQUFDO1FBQU0sR0FBR1IsVUFBVSxXQUFXLENBQUM7S0FBQztJQUUzREksUUFBUUssR0FBRyxDQUFDO0lBRVosTUFBTUMsU0FBU2pCLFVBQ2QsVUFDQTtRQUNDO1FBQ0E7UUFDQTtXQUNHZTtRQUNIO1FBQ0E7UUFDQTtRQUNBO1FBQ0FYO1FBQ0E7UUFDQTtRQUNBO0tBQ0EsRUFDRDtRQUFFTSxPQUFPO0lBQVU7SUFHcEJHLFFBQVFDLElBQUksQ0FBQ0csT0FBT0MsTUFBTSxJQUFJO0FBQy9CIn0=
@@ -2,21 +2,29 @@ import { createRequire } from "node:module";
2
2
  import { dirname, join } from "node:path";
3
3
  import { fileURLToPath } from "node:url";
4
4
  const _require = createRequire(import.meta.url);
5
- function resolveCreateVersion() {
5
+ const DEFAULT_CREATE_VERSION = "0.0.1-dev";
6
+ const DEFAULT_CREATE_IMAGE_REPOSITORY = "ghcr.io/typesmiths/decohere/create";
7
+ function resolveCreateMetadata() {
6
8
  let dir = dirname(fileURLToPath(import.meta.url));
7
9
  for(let i = 0; i < 5; i++){
8
10
  try {
9
11
  const pkg = _require(join(dir, "package.json"));
10
12
  if (pkg.name === "@decohere/create") {
11
- return pkg.version ?? "0.0.1-dev";
13
+ return {
14
+ version: pkg.version ?? DEFAULT_CREATE_VERSION,
15
+ imageRepository: pkg.decohere?.dockerImageRepository ?? DEFAULT_CREATE_IMAGE_REPOSITORY
16
+ };
12
17
  }
13
18
  } catch {}
14
19
  const parent = dirname(dir);
15
20
  if (parent === dir) break;
16
21
  dir = parent;
17
22
  }
18
- return "0.0.1-dev";
23
+ return {
24
+ version: DEFAULT_CREATE_VERSION,
25
+ imageRepository: DEFAULT_CREATE_IMAGE_REPOSITORY
26
+ };
19
27
  }
20
- /** Version of @decohere/create, used to pin scaffolded @decohere/* dependencies. */ export const CREATE_VERSION = resolveCreateVersion();
21
-
22
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9ob21lL3J1bm5lci93b3JrL2RlY29oZXJlL2RlY29oZXJlL3Byb2plY3RzL2NyZWF0ZS9zcmMvdmVyc2lvbi50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBjcmVhdGVSZXF1aXJlIH0gZnJvbSBcIm5vZGU6bW9kdWxlXCI7XG5pbXBvcnQgeyBkaXJuYW1lLCBqb2luIH0gZnJvbSBcIm5vZGU6cGF0aFwiO1xuaW1wb3J0IHsgZmlsZVVSTFRvUGF0aCB9IGZyb20gXCJub2RlOnVybFwiO1xuXG5jb25zdCBfcmVxdWlyZSA9IGNyZWF0ZVJlcXVpcmUoaW1wb3J0Lm1ldGEudXJsKTtcblxuZnVuY3Rpb24gcmVzb2x2ZUNyZWF0ZVZlcnNpb24oKTogc3RyaW5nIHtcblx0bGV0IGRpciA9IGRpcm5hbWUoZmlsZVVSTFRvUGF0aChpbXBvcnQubWV0YS51cmwpKTtcblx0Zm9yIChsZXQgaSA9IDA7IGkgPCA1OyBpKyspIHtcblx0XHR0cnkge1xuXHRcdFx0Y29uc3QgcGtnID0gX3JlcXVpcmUoam9pbihkaXIsIFwicGFja2FnZS5qc29uXCIpKSBhcyB7XG5cdFx0XHRcdG5hbWU/OiBzdHJpbmc7XG5cdFx0XHRcdHZlcnNpb24/OiBzdHJpbmc7XG5cdFx0XHR9O1xuXHRcdFx0aWYgKHBrZy5uYW1lID09PSBcIkBkZWNvaGVyZS9jcmVhdGVcIikge1xuXHRcdFx0XHRyZXR1cm4gcGtnLnZlcnNpb24gPz8gXCIwLjAuMS1kZXZcIjtcblx0XHRcdH1cblx0XHR9IGNhdGNoIHt9XG5cdFx0Y29uc3QgcGFyZW50ID0gZGlybmFtZShkaXIpO1xuXHRcdGlmIChwYXJlbnQgPT09IGRpcikgYnJlYWs7XG5cdFx0ZGlyID0gcGFyZW50O1xuXHR9XG5cdHJldHVybiBcIjAuMC4xLWRldlwiO1xufVxuXG4vKiogVmVyc2lvbiBvZiBAZGVjb2hlcmUvY3JlYXRlLCB1c2VkIHRvIHBpbiBzY2FmZm9sZGVkIEBkZWNvaGVyZS8qIGRlcGVuZGVuY2llcy4gKi9cbmV4cG9ydCBjb25zdCBDUkVBVEVfVkVSU0lPTiA9IHJlc29sdmVDcmVhdGVWZXJzaW9uKCk7XG4iXSwibmFtZXMiOlsiY3JlYXRlUmVxdWlyZSIsImRpcm5hbWUiLCJqb2luIiwiZmlsZVVSTFRvUGF0aCIsIl9yZXF1aXJlIiwidXJsIiwicmVzb2x2ZUNyZWF0ZVZlcnNpb24iLCJkaXIiLCJpIiwicGtnIiwibmFtZSIsInZlcnNpb24iLCJwYXJlbnQiLCJDUkVBVEVfVkVSU0lPTiJdLCJtYXBwaW5ncyI6IkFBQUEsU0FBU0EsYUFBYSxRQUFRLGNBQWM7QUFDNUMsU0FBU0MsT0FBTyxFQUFFQyxJQUFJLFFBQVEsWUFBWTtBQUMxQyxTQUFTQyxhQUFhLFFBQVEsV0FBVztBQUV6QyxNQUFNQyxXQUFXSixjQUFjLFlBQVlLLEdBQUc7QUFFOUMsU0FBU0M7SUFDUixJQUFJQyxNQUFNTixRQUFRRSxjQUFjLFlBQVlFLEdBQUc7SUFDL0MsSUFBSyxJQUFJRyxJQUFJLEdBQUdBLElBQUksR0FBR0EsSUFBSztRQUMzQixJQUFJO1lBQ0gsTUFBTUMsTUFBTUwsU0FBU0YsS0FBS0ssS0FBSztZQUkvQixJQUFJRSxJQUFJQyxJQUFJLEtBQUssb0JBQW9CO2dCQUNwQyxPQUFPRCxJQUFJRSxPQUFPLElBQUk7WUFDdkI7UUFDRCxFQUFFLE9BQU0sQ0FBQztRQUNULE1BQU1DLFNBQVNYLFFBQVFNO1FBQ3ZCLElBQUlLLFdBQVdMLEtBQUs7UUFDcEJBLE1BQU1LO0lBQ1A7SUFDQSxPQUFPO0FBQ1I7QUFFQSxrRkFBa0YsR0FDbEYsT0FBTyxNQUFNQyxpQkFBaUJQLHVCQUF1QiJ9
28
+ const createMetadata = resolveCreateMetadata();
29
+ export const CREATE_VERSION = createMetadata.version;
30
+ export const CREATE_IMAGE_REPOSITORY = createMetadata.imageRepository;
@@ -0,0 +1,177 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Vendor `@decohere/scaffold` for the builder image: resolve `@decohere/*` from
4
+ * dist/release via `file:` (not `overrides`), `npm install --install-links`,
5
+ * then tar the installed tree including `node_modules`. `--install-links`
6
+ * materializes `file:` packages (and their deps) instead of leaving symlinks
7
+ * whose nested installs live outside the packed directory.
8
+ */
9
+ import { spawnSync } from "node:child_process";
10
+ import { readdirSync, readFileSync, writeFileSync } from "node:fs";
11
+ import { dirname, join, relative, resolve } from "node:path";
12
+ import { fileURLToPath } from "node:url";
13
+
14
+ /**
15
+ * @typedef {{ name: string, dir: string }} WorkspacePackage
16
+ */
17
+
18
+ /**
19
+ * @param {string} rootDir
20
+ * @returns {WorkspacePackage[]}
21
+ */
22
+ export function collectWorkspacePackages(rootDir) {
23
+ /** @type {WorkspacePackage[]} */
24
+ const found = [];
25
+ const walk = (dir) => {
26
+ let entries;
27
+ try {
28
+ entries = readdirSync(dir, { withFileTypes: true });
29
+ } catch {
30
+ return;
31
+ }
32
+ for (const entry of entries) {
33
+ if (entry.name === "node_modules" || entry.name === ".git") continue;
34
+ const path = join(dir, entry.name);
35
+ if (entry.isDirectory()) {
36
+ walk(path);
37
+ continue;
38
+ }
39
+ if (entry.name !== "package.json") continue;
40
+ const pkg = JSON.parse(readFileSync(path, "utf8"));
41
+ if (typeof pkg.name === "string" && pkg.name.startsWith("@decohere/")) {
42
+ found.push({ name: pkg.name, dir });
43
+ }
44
+ }
45
+ };
46
+ walk(rootDir);
47
+ return found;
48
+ }
49
+
50
+ /**
51
+ * @param {string} fromDir
52
+ * @param {string} pkgDir
53
+ */
54
+ export function fileSpec(fromDir, pkgDir) {
55
+ let rel = relative(fromDir, pkgDir) || ".";
56
+ rel = rel.replaceAll("\\", "/");
57
+ if (!rel.startsWith(".")) rel = `./${rel}`;
58
+ return `file:${rel}`;
59
+ }
60
+
61
+ /**
62
+ * Rewrite `@decohere/*` dependency specs to local `file:` paths.
63
+ * Must mutate `dependencies` (not `overrides`) — npm throws EOVERRIDE when an
64
+ * override conflicts with a direct dependency.
65
+ *
66
+ * @param {Record<string, unknown>} pkg
67
+ * @param {string} fromDir
68
+ * @param {WorkspacePackage[]} packages
69
+ */
70
+ export function rewriteDecohereDepsToFile(pkg, fromDir, packages) {
71
+ const byName = new Map(packages.map((item) => [item.name, item.dir]));
72
+ const from = resolve(fromDir);
73
+ const rewrite = (deps) => {
74
+ if (deps == null || typeof deps !== "object" || Array.isArray(deps)) return deps;
75
+ const next = { .../** @type {Record<string, unknown>} */ (deps) };
76
+ for (const name of Object.keys(next)) {
77
+ const dir = byName.get(name);
78
+ if (dir == null || resolve(dir) === from) continue;
79
+ next[name] = fileSpec(fromDir, dir);
80
+ }
81
+ return next;
82
+ };
83
+ return {
84
+ ...pkg,
85
+ dependencies: rewrite(pkg.dependencies),
86
+ optionalDependencies: rewrite(pkg.optionalDependencies),
87
+ };
88
+ }
89
+
90
+ function writeJson(path, value) {
91
+ writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`);
92
+ }
93
+
94
+ function runNpm(cwd, args) {
95
+ const result = spawnSync("npm", args, { cwd, encoding: "utf8" });
96
+ if (result.status !== 0) {
97
+ throw new Error(
98
+ `npm ${args.join(" ")} failed (exit ${result.status}):\n${result.stderr}${result.stdout}`,
99
+ );
100
+ }
101
+ return result.stdout.trim();
102
+ }
103
+
104
+ function packInstalledTree(outDir, dest) {
105
+ const result = spawnSync(
106
+ "tar",
107
+ ["--create", "--gzip", "--dereference", "--file", dest, "-C", outDir, "."],
108
+ { encoding: "utf8" },
109
+ );
110
+ if (result.status !== 0) {
111
+ throw new Error(`tar failed (exit ${result.status}):\n${result.stderr}${result.stdout}`);
112
+ }
113
+ }
114
+
115
+ export function packBuilderTgz(options) {
116
+ const outDir = resolve(options.outDir);
117
+ const workspaceOut = resolve(options.workspaceOut);
118
+ const dest = resolve(options.dest);
119
+ const packageJsonPath = join(outDir, "package.json");
120
+ const packages = collectWorkspacePackages(workspaceOut);
121
+ const originals = packages.map((pkg) => {
122
+ const path = join(pkg.dir, "package.json");
123
+ return { path, original: JSON.parse(readFileSync(path, "utf8")) };
124
+ });
125
+ const createOriginal = originals.find((entry) => entry.path === packageJsonPath)?.original;
126
+ if (createOriginal == null) {
127
+ throw new Error(`No @decohere package.json at ${packageJsonPath}`);
128
+ }
129
+
130
+ try {
131
+ for (const { path, original } of originals) {
132
+ writeJson(path, rewriteDecohereDepsToFile(original, dirname(path), packages));
133
+ }
134
+ runNpm(outDir, ["install", "--omit=dev", "--ignore-scripts", "--install-links"]);
135
+ } finally {
136
+ for (const { path, original } of originals) {
137
+ writeJson(path, original);
138
+ }
139
+ }
140
+
141
+ packInstalledTree(outDir, dest);
142
+ return dest;
143
+ }
144
+
145
+ function parseArgs(argv) {
146
+ /** @type {Record<string, string>} */
147
+ const parsed = {};
148
+ for (let i = 0; i < argv.length; i++) {
149
+ const arg = argv[i];
150
+ if (arg == null || !arg.startsWith("--")) continue;
151
+ const key = arg.slice(2);
152
+ const value = argv[i + 1];
153
+ if (value == null || value.startsWith("--")) {
154
+ throw new Error(`Missing value for --${key}`);
155
+ }
156
+ parsed[key] = value;
157
+ i++;
158
+ }
159
+ return parsed;
160
+ }
161
+
162
+ function main() {
163
+ const args = parseArgs(process.argv.slice(2));
164
+ const outDir = args["out-dir"];
165
+ const workspaceOut = args["workspace-out"];
166
+ const dest = args.dest;
167
+ if (outDir == null || workspaceOut == null || dest == null) {
168
+ throw new Error(
169
+ "Usage: pack-builder-tgz.mjs --out-dir <dir> --workspace-out <dir> --dest <create.tgz>",
170
+ );
171
+ }
172
+ packBuilderTgz({ outDir, workspaceOut, dest });
173
+ }
174
+
175
+ if (process.argv[1] != null && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
176
+ main();
177
+ }
package/package.json CHANGED
@@ -1,28 +1,40 @@
1
1
  {
2
2
  "name": "@decohere/create",
3
- "version": "0.0.1-dev.19",
4
- "description": "Scaffold a new Decohere project — build with Forge, serve with Flux",
3
+ "version": "0.0.1-dev.20261105.46",
4
+ "description": "Download the Decohere scaffolder image and run it (npm create @decohere)",
5
5
  "keywords": [
6
6
  "decohere",
7
7
  "create",
8
8
  "scaffold",
9
- "cli",
10
- "forge",
11
- "flux",
12
- "typescript"
9
+ "cli"
13
10
  ],
11
+ "homepage": "https://github.com/typesmiths/decohere/tree/main/projects/create#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/typesmiths/decohere/issues"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "git+https://github.com/typesmiths/decohere.git",
18
+ "directory": "projects/create"
19
+ },
14
20
  "license": "ISC",
15
21
  "author": "Typesmiths",
16
22
  "type": "module",
23
+ "exports": {
24
+ ".": {
25
+ "types": "./types/src/index.d.ts",
26
+ "import": "./es6/src/index.js"
27
+ }
28
+ },
17
29
  "bin": {
18
30
  "create-decohere": "./es6/src/cli.js"
19
31
  },
20
32
  "scripts": {
21
33
  "test": "echo \"Error: no test specified\" && exit 1"
22
34
  },
23
- "dependencies": {
24
- "@decohere/nexus": "0.0.1-dev.19",
25
- "commander": "^13.1.0"
26
- },
27
- "devDependencies": {}
35
+ "dependencies": {},
36
+ "devDependencies": {},
37
+ "decohere": {
38
+ "dockerImageRepository": "ghcr.io/typesmiths/decohere/create"
39
+ }
28
40
  }
@@ -0,0 +1,3 @@
1
+ export type { LaunchKind, LaunchPlan } from "./resolve-launch.js";
2
+ export { containerArgvFromHost, resolveLaunch, toProductArgv } from "./resolve-launch.js";
3
+ export { runInBuilder } from "./runner.js";
@@ -0,0 +1,20 @@
1
+ export type LaunchKind = "scaffold" | "auth0";
2
+ export type LaunchPlan = {
3
+ targetDir: string;
4
+ containerArgv: string[];
5
+ projectName: string;
6
+ kind: LaunchKind;
7
+ };
8
+ /**
9
+ * Map host / npm-create argv onto the product CLI (`create webapi` / `add api`).
10
+ * `npm create @decohere webapi` already says “create”, so we prepend `create`
11
+ * rather than making users type `create create webapi`.
12
+ */
13
+ export declare function toProductArgv(tokens: string[]): string[];
14
+ export declare function containerArgvFromHost(argv: string[]): string[];
15
+ /**
16
+ * Host argv → directory to mount and argv for the in-image `decohere` entrypoint.
17
+ * Positional `webapi my-app` / `create webapi my-app` wins over `-d` (npm treats `-d`
18
+ * as its own loglevel).
19
+ */
20
+ export declare function resolveLaunch(argv: string[], cwd?: string): LaunchPlan;
@@ -1,13 +1,5 @@
1
1
  /**
2
- * Run the scaffold inside a pre-built Docker builder image.
3
- *
4
- * Called when `DECOHERE_BUILDER` is not set — i.e. the CLI is running on the
5
- * host, outside the container. Mounts the target directory as `/workspace` so
6
- * the scaffolded files are written back to the host filesystem.
7
- *
8
- * If `~/.config/auth0` exists on the host the directory is also mounted so
9
- * the auth0 CLI inside the container can reuse existing credentials silently.
2
+ * Pull/run the Decohere scaffolder image. Host `@decohere/create` does not load
3
+ * the `@decohere/scaffold` dependency tree — that lives in the image only.
10
4
  */
11
- export declare function runInBuilder(opts: {
12
- d?: string;
13
- }): never;
5
+ export declare function runInBuilder(argv: string[]): never;
@@ -1,2 +1,2 @@
1
- /** Version of @decohere/create, used to pin scaffolded @decohere/* dependencies. */
2
1
  export declare const CREATE_VERSION: string;
2
+ export declare const CREATE_IMAGE_REPOSITORY: string;
@@ -1,55 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- function _export(target, all) {
6
- for(var name in all)Object.defineProperty(target, name, {
7
- enumerable: true,
8
- get: Object.getOwnPropertyDescriptor(all, name).get
9
- });
10
- }
11
- _export(exports, {
12
- get ask () {
13
- return ask;
14
- },
15
- get pick () {
16
- return pick;
17
- }
18
- });
19
- const _nodereadline = require("node:readline");
20
- // readline/promises rl.question() has a race condition with piped (non-TTY) stdin
21
- // in Node ≥ 22: after the first line is read, stdin emits 'end', readline closes,
22
- // and all subsequent question() calls resolve to "".
23
- // Fix: use the async-iterator API, which buffers all lines before the close event fires.
24
- let _lines = null;
25
- function getLines() {
26
- if (_lines == null) {
27
- const rl = (0, _nodereadline.createInterface)({
28
- input: process.stdin,
29
- terminal: false
30
- });
31
- _lines = rl[Symbol.asyncIterator]();
32
- }
33
- return _lines;
34
- }
35
- async function readLine(prompt) {
36
- process.stdout.write(prompt);
37
- const { value, done } = await getLines().next();
38
- return done ? "" : value.trim();
39
- }
40
- async function ask(question, fallback = "") {
41
- const answer = await readLine(question);
42
- return answer || fallback;
43
- }
44
- async function pick(label, items, display) {
45
- console.log(label.trimEnd());
46
- for (const [i, item] of items.entries()){
47
- console.log(` ${i + 1}. ${display(item)}`);
48
- }
49
- const answer = await readLine(" > ");
50
- const idx = Number.parseInt(answer, 10);
51
- if (Number.isNaN(idx) || idx < 1 || idx > items.length) return null;
52
- return items[idx - 1];
53
- }
54
-
55
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi9ob21lL3J1bm5lci93b3JrL2RlY29oZXJlL2RlY29oZXJlL3Byb2plY3RzL2NyZWF0ZS9zcmMvZG9tYWluL3Byb21wdC50cyJdLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBjcmVhdGVJbnRlcmZhY2UgfSBmcm9tIFwibm9kZTpyZWFkbGluZVwiO1xuXG4vLyByZWFkbGluZS9wcm9taXNlcyBybC5xdWVzdGlvbigpIGhhcyBhIHJhY2UgY29uZGl0aW9uIHdpdGggcGlwZWQgKG5vbi1UVFkpIHN0ZGluXG4vLyBpbiBOb2RlIOKJpSAyMjogYWZ0ZXIgdGhlIGZpcnN0IGxpbmUgaXMgcmVhZCwgc3RkaW4gZW1pdHMgJ2VuZCcsIHJlYWRsaW5lIGNsb3Nlcyxcbi8vIGFuZCBhbGwgc3Vic2VxdWVudCBxdWVzdGlvbigpIGNhbGxzIHJlc29sdmUgdG8gXCJcIi5cbi8vIEZpeDogdXNlIHRoZSBhc3luYy1pdGVyYXRvciBBUEksIHdoaWNoIGJ1ZmZlcnMgYWxsIGxpbmVzIGJlZm9yZSB0aGUgY2xvc2UgZXZlbnQgZmlyZXMuXG5cbmxldCBfbGluZXM6IEFzeW5jSXRlcmF0b3I8c3RyaW5nPiB8IG51bGwgPSBudWxsO1xuXG5mdW5jdGlvbiBnZXRMaW5lcygpOiBBc3luY0l0ZXJhdG9yPHN0cmluZz4ge1xuXHRpZiAoX2xpbmVzID09IG51bGwpIHtcblx0XHRjb25zdCBybCA9IGNyZWF0ZUludGVyZmFjZSh7IGlucHV0OiBwcm9jZXNzLnN0ZGluLCB0ZXJtaW5hbDogZmFsc2UgfSk7XG5cdFx0X2xpbmVzID0gcmxbU3ltYm9sLmFzeW5jSXRlcmF0b3JdKCk7XG5cdH1cblx0cmV0dXJuIF9saW5lcztcbn1cblxuYXN5bmMgZnVuY3Rpb24gcmVhZExpbmUocHJvbXB0OiBzdHJpbmcpOiBQcm9taXNlPHN0cmluZz4ge1xuXHRwcm9jZXNzLnN0ZG91dC53cml0ZShwcm9tcHQpO1xuXHRjb25zdCB7IHZhbHVlLCBkb25lIH0gPSBhd2FpdCBnZXRMaW5lcygpLm5leHQoKTtcblx0cmV0dXJuIGRvbmUgPyBcIlwiIDogdmFsdWUudHJpbSgpO1xufVxuXG5leHBvcnQgYXN5bmMgZnVuY3Rpb24gYXNrKHF1ZXN0aW9uOiBzdHJpbmcsIGZhbGxiYWNrID0gXCJcIik6IFByb21pc2U8c3RyaW5nPiB7XG5cdGNvbnN0IGFuc3dlciA9IGF3YWl0IHJlYWRMaW5lKHF1ZXN0aW9uKTtcblx0cmV0dXJuIGFuc3dlciB8fCBmYWxsYmFjaztcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHBpY2s8VD4oXG5cdGxhYmVsOiBzdHJpbmcsXG5cdGl0ZW1zOiBUW10sXG5cdGRpc3BsYXk6IChpdGVtOiBUKSA9PiBzdHJpbmcsXG4pOiBQcm9taXNlPFQgfCBudWxsPiB7XG5cdGNvbnNvbGUubG9nKGxhYmVsLnRyaW1FbmQoKSk7XG5cdGZvciAoY29uc3QgW2ksIGl0ZW1dIG9mIGl0ZW1zLmVudHJpZXMoKSkge1xuXHRcdGNvbnNvbGUubG9nKGAgICAgJHtpICsgMX0uICR7ZGlzcGxheShpdGVtKX1gKTtcblx0fVxuXHRjb25zdCBhbnN3ZXIgPSBhd2FpdCByZWFkTGluZShcIiAgPiBcIik7XG5cdGNvbnN0IGlkeCA9IE51bWJlci5wYXJzZUludChhbnN3ZXIsIDEwKTtcblx0aWYgKE51bWJlci5pc05hTihpZHgpIHx8IGlkeCA8IDEgfHwgaWR4ID4gaXRlbXMubGVuZ3RoKSByZXR1cm4gbnVsbDtcblx0cmV0dXJuIGl0ZW1zW2lkeCAtIDFdO1xufVxuIl0sIm5hbWVzIjpbImFzayIsInBpY2siLCJfbGluZXMiLCJnZXRMaW5lcyIsInJsIiwiY3JlYXRlSW50ZXJmYWNlIiwiaW5wdXQiLCJwcm9jZXNzIiwic3RkaW4iLCJ0ZXJtaW5hbCIsIlN5bWJvbCIsImFzeW5jSXRlcmF0b3IiLCJyZWFkTGluZSIsInByb21wdCIsInN0ZG91dCIsIndyaXRlIiwidmFsdWUiLCJkb25lIiwibmV4dCIsInRyaW0iLCJxdWVzdGlvbiIsImZhbGxiYWNrIiwiYW5zd2VyIiwibGFiZWwiLCJpdGVtcyIsImRpc3BsYXkiLCJjb25zb2xlIiwibG9nIiwidHJpbUVuZCIsImkiLCJpdGVtIiwiZW50cmllcyIsImlkeCIsIk51bWJlciIsInBhcnNlSW50IiwiaXNOYU4iLCJsZW5ndGgiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7O1FBdUJzQkE7ZUFBQUE7O1FBS0FDO2VBQUFBOzs7OEJBNUJVO0FBRWhDLGtGQUFrRjtBQUNsRixrRkFBa0Y7QUFDbEYscURBQXFEO0FBQ3JELHlGQUF5RjtBQUV6RixJQUFJQyxTQUF1QztBQUUzQyxTQUFTQztJQUNSLElBQUlELFVBQVUsTUFBTTtRQUNuQixNQUFNRSxLQUFLQyxJQUFBQSw2QkFBZSxFQUFDO1lBQUVDLE9BQU9DLFFBQVFDLEtBQUs7WUFBRUMsVUFBVTtRQUFNO1FBQ25FUCxTQUFTRSxFQUFFLENBQUNNLE9BQU9DLGFBQWEsQ0FBQztJQUNsQztJQUNBLE9BQU9UO0FBQ1I7QUFFQSxlQUFlVSxTQUFTQyxNQUFjO0lBQ3JDTixRQUFRTyxNQUFNLENBQUNDLEtBQUssQ0FBQ0Y7SUFDckIsTUFBTSxFQUFFRyxLQUFLLEVBQUVDLElBQUksRUFBRSxHQUFHLE1BQU1kLFdBQVdlLElBQUk7SUFDN0MsT0FBT0QsT0FBTyxLQUFLRCxNQUFNRyxJQUFJO0FBQzlCO0FBRU8sZUFBZW5CLElBQUlvQixRQUFnQixFQUFFQyxXQUFXLEVBQUU7SUFDeEQsTUFBTUMsU0FBUyxNQUFNVixTQUFTUTtJQUM5QixPQUFPRSxVQUFVRDtBQUNsQjtBQUVPLGVBQWVwQixLQUNyQnNCLEtBQWEsRUFDYkMsS0FBVSxFQUNWQyxPQUE0QjtJQUU1QkMsUUFBUUMsR0FBRyxDQUFDSixNQUFNSyxPQUFPO0lBQ3pCLEtBQUssTUFBTSxDQUFDQyxHQUFHQyxLQUFLLElBQUlOLE1BQU1PLE9BQU8sR0FBSTtRQUN4Q0wsUUFBUUMsR0FBRyxDQUFDLENBQUMsSUFBSSxFQUFFRSxJQUFJLEVBQUUsRUFBRSxFQUFFSixRQUFRSyxPQUFPO0lBQzdDO0lBQ0EsTUFBTVIsU0FBUyxNQUFNVixTQUFTO0lBQzlCLE1BQU1vQixNQUFNQyxPQUFPQyxRQUFRLENBQUNaLFFBQVE7SUFDcEMsSUFBSVcsT0FBT0UsS0FBSyxDQUFDSCxRQUFRQSxNQUFNLEtBQUtBLE1BQU1SLE1BQU1ZLE1BQU0sRUFBRSxPQUFPO0lBQy9ELE9BQU9aLEtBQUssQ0FBQ1EsTUFBTSxFQUFFO0FBQ3RCIn0=