@decohere/create 0.0.1-dev.20 → 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.
- package/LICENSE +15 -0
- package/README.md +254 -0
- package/commonjs/src/cli.js +1 -34
- package/commonjs/src/index.js +26 -0
- package/commonjs/src/resolve-launch.js +100 -0
- package/commonjs/src/runner.js +63 -14
- package/commonjs/src/version.js +25 -9
- package/es6/src/cli.js +1 -34
- package/es6/src/index.js +2 -0
- package/es6/src/resolve-launch.js +87 -0
- package/es6/src/runner.js +67 -24
- package/es6/src/version.js +14 -6
- package/pack-builder-tgz.mjs +177 -0
- package/package.json +23 -11
- package/types/src/index.d.ts +3 -0
- package/types/src/resolve-launch.d.ts +20 -0
- package/types/src/runner.d.ts +3 -11
- package/types/src/version.d.ts +1 -1
- package/commonjs/src/domain/prompt.js +0 -55
- package/commonjs/src/models/compose.model.js +0 -82
- package/commonjs/src/models/config-yaml.model.js +0 -34
- package/commonjs/src/models/const.js +0 -29
- package/commonjs/src/models/container-image.model.js +0 -88
- package/commonjs/src/models/dev-container.model.js +0 -73
- package/commonjs/src/models/env-file.model.js +0 -33
- package/commonjs/src/models/gitignore.model.js +0 -32
- package/commonjs/src/models/launch-json.model.js +0 -52
- package/commonjs/src/models/package-json.model.js +0 -64
- package/commonjs/src/models/project.model.js +0 -126
- package/commonjs/src/models/source-file.js +0 -6
- package/commonjs/src/models/tsconfig.model.js +0 -34
- package/commonjs/src/models/vscode-settings.model.js +0 -31
- package/commonjs/src/services/scaffold.service.js +0 -62
- package/commonjs/src/webapi/create-api.js +0 -169
- package/commonjs/src/webapi/features/auth.feature.js +0 -166
- package/commonjs/src/webapi/features/auth0-cli.js +0 -151
- package/commonjs/src/webapi/features/database.feature.js +0 -151
- package/commonjs/src/webapi/features/finalize.feature.js +0 -51
- package/commonjs/src/webapi/features/postman.feature.js +0 -309
- package/commonjs/src/webapi/models/api-package-json.model.js +0 -57
- package/commonjs/src/webapi/models/auth-config.model.js +0 -23
- package/commonjs/src/webapi/models/controller-source-file.model.js +0 -180
- package/commonjs/src/webapi/models/main-source-file.model.js +0 -132
- package/commonjs/src/webapi/models/sample-files.model.js +0 -60
- package/es6/src/domain/prompt.js +0 -37
- package/es6/src/models/compose.model.js +0 -77
- package/es6/src/models/config-yaml.model.js +0 -30
- package/es6/src/models/const.js +0 -19
- package/es6/src/models/container-image.model.js +0 -84
- package/es6/src/models/dev-container.model.js +0 -68
- package/es6/src/models/env-file.model.js +0 -29
- package/es6/src/models/gitignore.model.js +0 -22
- package/es6/src/models/launch-json.model.js +0 -42
- package/es6/src/models/package-json.model.js +0 -60
- package/es6/src/models/project.model.js +0 -111
- package/es6/src/models/source-file.js +0 -3
- package/es6/src/models/tsconfig.model.js +0 -24
- package/es6/src/models/vscode-settings.model.js +0 -21
- package/es6/src/services/scaffold.service.js +0 -52
- package/es6/src/webapi/create-api.js +0 -151
- package/es6/src/webapi/features/auth.feature.js +0 -115
- package/es6/src/webapi/features/auth0-cli.js +0 -116
- package/es6/src/webapi/features/database.feature.js +0 -141
- package/es6/src/webapi/features/finalize.feature.js +0 -41
- package/es6/src/webapi/features/postman.feature.js +0 -258
- package/es6/src/webapi/models/api-package-json.model.js +0 -52
- package/es6/src/webapi/models/auth-config.model.js +0 -19
- package/es6/src/webapi/models/controller-source-file.model.js +0 -171
- package/es6/src/webapi/models/main-source-file.model.js +0 -128
- package/es6/src/webapi/models/sample-files.model.js +0 -42
- package/types/src/domain/prompt.d.ts +0 -2
- package/types/src/models/compose.model.d.ts +0 -32
- package/types/src/models/config-yaml.model.d.ts +0 -16
- package/types/src/models/const.d.ts +0 -17
- package/types/src/models/container-image.model.d.ts +0 -15
- package/types/src/models/dev-container.model.d.ts +0 -21
- package/types/src/models/env-file.model.d.ts +0 -14
- package/types/src/models/gitignore.model.d.ts +0 -6
- package/types/src/models/launch-json.model.d.ts +0 -5
- package/types/src/models/package-json.model.d.ts +0 -18
- package/types/src/models/project.model.d.ts +0 -55
- package/types/src/models/source-file.d.ts +0 -11
- package/types/src/models/tsconfig.model.d.ts +0 -5
- package/types/src/models/vscode-settings.model.d.ts +0 -7
- package/types/src/services/scaffold.service.d.ts +0 -22
- package/types/src/webapi/create-api.d.ts +0 -7
- package/types/src/webapi/features/auth.feature.d.ts +0 -7
- package/types/src/webapi/features/auth0-cli.d.ts +0 -22
- package/types/src/webapi/features/database.feature.d.ts +0 -5
- package/types/src/webapi/features/finalize.feature.d.ts +0 -2
- package/types/src/webapi/features/postman.feature.d.ts +0 -7
- package/types/src/webapi/models/api-package-json.model.d.ts +0 -17
- package/types/src/webapi/models/auth-config.model.d.ts +0 -18
- package/types/src/webapi/models/controller-source-file.model.d.ts +0 -42
- package/types/src/webapi/models/main-source-file.model.d.ts +0 -36
- 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 {
|
|
5
|
-
|
|
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
|
-
*
|
|
8
|
-
*
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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
|
-
|
|
83
|
+
...ttyFlags,
|
|
84
|
+
...userArgs,
|
|
40
85
|
...volumes,
|
|
41
|
-
|
|
42
|
-
"DECOHERE_BUILDER=1",
|
|
86
|
+
...envArgs,
|
|
43
87
|
"-w",
|
|
44
88
|
"/workspace",
|
|
45
89
|
BUILDER_IMAGE,
|
|
46
|
-
|
|
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=
|
package/es6/src/version.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
23
|
+
return {
|
|
24
|
+
version: DEFAULT_CREATE_VERSION,
|
|
25
|
+
imageRepository: DEFAULT_CREATE_IMAGE_REPOSITORY
|
|
26
|
+
};
|
|
19
27
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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.
|
|
4
|
-
"description": "
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
35
|
+
"dependencies": {},
|
|
36
|
+
"devDependencies": {},
|
|
37
|
+
"decohere": {
|
|
38
|
+
"dockerImageRepository": "ghcr.io/typesmiths/decohere/create"
|
|
39
|
+
}
|
|
28
40
|
}
|
|
@@ -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;
|
package/types/src/runner.d.ts
CHANGED
|
@@ -1,13 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
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(
|
|
12
|
-
d?: string;
|
|
13
|
-
}): never;
|
|
5
|
+
export declare function runInBuilder(argv: string[]): never;
|
package/types/src/version.d.ts
CHANGED
|
@@ -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=
|