@decohere/scaffold 0.0.1-dev.125 → 0.0.1-dev.18
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/package.json +9 -19
- package/src/add-api.action.ts +25 -0
- package/src/add-api.command.ts +31 -0
- package/src/add.command.ts +20 -0
- package/src/add.module.ts +11 -0
- package/src/cli.ts +4 -0
- package/src/create.command.ts +20 -0
- package/src/create.module.ts +11 -0
- package/src/domain/prompt.ts +50 -0
- package/{types/src/index.d.ts → src/index.ts} +0 -4
- package/src/models/compose.model.ts +205 -0
- package/src/models/config-yaml.model.ts +83 -0
- package/src/models/const.ts +20 -0
- package/src/models/dev-container.model.ts +136 -0
- package/src/models/docker-image.model.ts +343 -0
- package/src/models/env-file.model.ts +45 -0
- package/src/models/gitignore.model.ts +35 -0
- package/src/models/launch-json.model.ts +60 -0
- package/src/models/package-json.model.ts +169 -0
- package/src/models/project.model.ts +141 -0
- package/{types/src/models/source-file.d.ts → src/models/source-file.ts} +6 -4
- package/src/models/tsconfig.model.ts +29 -0
- package/src/models/vitest-config.model.ts +26 -0
- package/src/models/vscode-settings.model.ts +24 -0
- package/src/program.ts +63 -0
- package/{types/src/resolve-target-dir.d.ts → src/resolve-target-dir.ts} +4 -1
- package/src/services/scaffold.service.ts +89 -0
- package/src/version.ts +56 -0
- package/src/webapi/api-name.ts +73 -0
- package/src/webapi/cli-options.ts +56 -0
- package/{types/src/webapi/const.d.ts → src/webapi/const.ts} +21 -9
- package/src/webapi/create-api.ts +275 -0
- package/src/webapi/devcontainer/host-scripts.ts +35 -0
- package/src/webapi/features/auth.feature.ts +454 -0
- package/src/webapi/features/auth0-cli.ts +421 -0
- package/src/webapi/features/biome-format.ts +34 -0
- package/src/webapi/features/database.feature.ts +522 -0
- package/src/webapi/features/finalize.feature.ts +112 -0
- package/src/webapi/features/postman.feature.ts +287 -0
- package/src/webapi/models/api-package-json.model.ts +60 -0
- package/src/webapi/models/auth-config.model.ts +36 -0
- package/src/webapi/models/controller-source-file.model.ts +232 -0
- package/{es6/src/webapi/models/github-ci.model.js → src/webapi/models/github-ci.model.ts} +29 -27
- package/src/webapi/models/main-source-file.model.ts +208 -0
- package/src/webapi/models/production-dockerfile.model.ts +162 -0
- package/src/webapi/models/sample-files.model.ts +45 -0
- package/src/webapi/models/webapi-image.model.ts +21 -0
- package/src/webapi/postman/hello-openapi.ts +92 -0
- package/src/webapi/postman/local-view.ts +195 -0
- package/src/webapi/postman/openapi-to-postman.ts +208 -0
- package/src/webapi/workspace-readme.ts +192 -0
- package/src/webapi.action.ts +22 -0
- package/src/webapi.command.ts +41 -0
- package/tests/auth-postman.wiring.spec.ts +263 -0
- package/tests/auth0-cli.spec.ts +232 -0
- package/tests/biome-format.spec.ts +49 -0
- package/tests/cli.integration.spec.ts +658 -0
- package/tests/compose.model.spec.ts +51 -0
- package/tests/container-image.model.spec.ts +162 -0
- package/tests/dev-container.model.spec.ts +29 -0
- package/tests/devcontainer-base-image.spec.ts +32 -0
- package/tests/hello-di-inject.scaffold.spec.ts +18 -0
- package/tests/hello-route.contract.spec.ts +94 -0
- package/tests/package-json.order.spec.ts +59 -0
- package/tests/postman-from-openapi.spec.ts +197 -0
- package/tests/seed-devcontainer-templates.ts +40 -0
- package/tests/webapi/devcontainer/codebase-rag-mcp.spec.ts +96 -0
- package/LICENSE +0 -15
- package/commonjs/src/add-api.action.js +0 -37
- package/commonjs/src/add-api.command.js +0 -39
- package/commonjs/src/add.command.js +0 -22
- package/commonjs/src/add.module.js +0 -24
- package/commonjs/src/auth0-configure.action.js +0 -28
- package/commonjs/src/auth0-configure.command.js +0 -24
- package/commonjs/src/auth0-login.action.js +0 -28
- package/commonjs/src/auth0-login.command.js +0 -23
- package/commonjs/src/auth0.command.js +0 -22
- package/commonjs/src/auth0.module.js +0 -28
- package/commonjs/src/cli.js +0 -8
- package/commonjs/src/create.command.js +0 -22
- package/commonjs/src/create.module.js +0 -24
- package/commonjs/src/domain/prompt.js +0 -62
- package/commonjs/src/index.js +0 -48
- package/commonjs/src/models/compose.model.js +0 -258
- package/commonjs/src/models/config-yaml.model.js +0 -86
- package/commonjs/src/models/const.js +0 -30
- package/commonjs/src/models/dev-container.model.js +0 -172
- package/commonjs/src/models/docker-image.model.js +0 -277
- package/commonjs/src/models/env-file.model.js +0 -48
- package/commonjs/src/models/gitignore.model.js +0 -48
- package/commonjs/src/models/launch-json.model.js +0 -70
- package/commonjs/src/models/package-json.model.js +0 -156
- package/commonjs/src/models/project.model.js +0 -132
- package/commonjs/src/models/source-file.js +0 -4
- package/commonjs/src/models/tsconfig.model.js +0 -32
- package/commonjs/src/models/vitest-config.model.js +0 -33
- package/commonjs/src/models/vscode-settings.model.js +0 -29
- package/commonjs/src/program.js +0 -64
- package/commonjs/src/resolve-target-dir.js +0 -17
- package/commonjs/src/services/scaffold.service.js +0 -60
- package/commonjs/src/version.js +0 -55
- package/commonjs/src/webapi/api-name.js +0 -93
- package/commonjs/src/webapi/auth0-commands.js +0 -250
- package/commonjs/src/webapi/authorize-scopes.js +0 -79
- package/commonjs/src/webapi/cli-options.js +0 -99
- package/commonjs/src/webapi/const.js +0 -54
- package/commonjs/src/webapi/create-api.js +0 -265
- package/commonjs/src/webapi/devcontainer/codebase-rag-mcp.js +0 -164
- package/commonjs/src/webapi/devcontainer/codebase-rag-mcp.ts +0 -182
- package/commonjs/src/webapi/devcontainer/host-scripts.js +0 -50
- package/commonjs/src/webapi/devcontainer/templates/codebase-rag.env.example +0 -3
- package/commonjs/src/webapi/devcontainer/templates/initialize.sh +0 -169
- package/commonjs/src/webapi/devcontainer/templates/post-create.sh +0 -35
- package/commonjs/src/webapi/devcontainer/templates/post-start.sh +0 -65
- package/commonjs/src/webapi/devcontainer/templates/tty-prompt.sh +0 -38
- package/commonjs/src/webapi/features/auth.feature.js +0 -408
- package/commonjs/src/webapi/features/auth0-cli.js +0 -472
- package/commonjs/src/webapi/features/biome-format.js +0 -47
- package/commonjs/src/webapi/features/database.feature.js +0 -433
- package/commonjs/src/webapi/features/finalize.feature.js +0 -93
- package/commonjs/src/webapi/features/postman.feature.js +0 -322
- package/commonjs/src/webapi/models/api-package-json.model.js +0 -58
- package/commonjs/src/webapi/models/auth-config.model.js +0 -24
- package/commonjs/src/webapi/models/controller-source-file.model.js +0 -190
- package/commonjs/src/webapi/models/github-ci.model.js +0 -193
- package/commonjs/src/webapi/models/main-source-file.model.js +0 -165
- package/commonjs/src/webapi/models/production-dockerfile.model.js +0 -75
- package/commonjs/src/webapi/models/sample-files.model.js +0 -58
- package/commonjs/src/webapi/models/webapi-image.model.js +0 -25
- package/commonjs/src/webapi/postman/hello-openapi.js +0 -173
- package/commonjs/src/webapi/postman/local-view.js +0 -182
- package/commonjs/src/webapi/postman/openapi-to-postman.js +0 -238
- package/commonjs/src/webapi/workspace-readme.js +0 -109
- package/commonjs/src/webapi.action.js +0 -28
- package/commonjs/src/webapi.command.js +0 -43
- package/es6/src/add-api.action.js +0 -27
- package/es6/src/add-api.command.js +0 -29
- package/es6/src/add.command.js +0 -12
- package/es6/src/add.module.js +0 -14
- package/es6/src/auth0-configure.action.js +0 -18
- package/es6/src/auth0-configure.command.js +0 -14
- package/es6/src/auth0-login.action.js +0 -18
- package/es6/src/auth0-login.command.js +0 -13
- package/es6/src/auth0.command.js +0 -12
- package/es6/src/auth0.module.js +0 -18
- package/es6/src/cli.js +0 -4
- package/es6/src/create.command.js +0 -12
- package/es6/src/create.module.js +0 -14
- package/es6/src/domain/prompt.js +0 -41
- package/es6/src/index.js +0 -9
- package/es6/src/models/compose.model.js +0 -254
- package/es6/src/models/config-yaml.model.js +0 -74
- package/es6/src/models/const.js +0 -20
- package/es6/src/models/dev-container.model.js +0 -162
- package/es6/src/models/docker-image.model.js +0 -235
- package/es6/src/models/env-file.model.js +0 -44
- package/es6/src/models/gitignore.model.js +0 -38
- package/es6/src/models/launch-json.model.js +0 -60
- package/es6/src/models/package-json.model.js +0 -141
- package/es6/src/models/project.model.js +0 -117
- package/es6/src/models/source-file.js +0 -1
- package/es6/src/models/tsconfig.model.js +0 -22
- package/es6/src/models/vitest-config.model.js +0 -23
- package/es6/src/models/vscode-settings.model.js +0 -19
- package/es6/src/program.js +0 -50
- package/es6/src/resolve-target-dir.js +0 -7
- package/es6/src/services/scaffold.service.js +0 -50
- package/es6/src/version.js +0 -34
- package/es6/src/webapi/api-name.js +0 -57
- package/es6/src/webapi/auth0-commands.js +0 -185
- package/es6/src/webapi/authorize-scopes.js +0 -55
- package/es6/src/webapi/cli-options.js +0 -34
- package/es6/src/webapi/const.js +0 -21
- package/es6/src/webapi/create-api.js +0 -245
- package/es6/src/webapi/devcontainer/codebase-rag-mcp.js +0 -154
- package/es6/src/webapi/devcontainer/codebase-rag-mcp.ts +0 -182
- package/es6/src/webapi/devcontainer/host-scripts.js +0 -29
- package/es6/src/webapi/devcontainer/templates/codebase-rag.env.example +0 -3
- package/es6/src/webapi/devcontainer/templates/initialize.sh +0 -169
- package/es6/src/webapi/devcontainer/templates/post-create.sh +0 -35
- package/es6/src/webapi/devcontainer/templates/post-start.sh +0 -65
- package/es6/src/webapi/devcontainer/templates/tty-prompt.sh +0 -38
- package/es6/src/webapi/features/auth.feature.js +0 -357
- package/es6/src/webapi/features/auth0-cli.js +0 -406
- package/es6/src/webapi/features/biome-format.js +0 -40
- package/es6/src/webapi/features/database.feature.js +0 -423
- package/es6/src/webapi/features/finalize.feature.js +0 -83
- package/es6/src/webapi/features/postman.feature.js +0 -256
- package/es6/src/webapi/models/api-package-json.model.js +0 -53
- package/es6/src/webapi/models/auth-config.model.js +0 -20
- package/es6/src/webapi/models/controller-source-file.model.js +0 -181
- package/es6/src/webapi/models/main-source-file.model.js +0 -161
- package/es6/src/webapi/models/production-dockerfile.model.js +0 -66
- package/es6/src/webapi/models/sample-files.model.js +0 -40
- package/es6/src/webapi/models/webapi-image.model.js +0 -18
- package/es6/src/webapi/postman/hello-openapi.js +0 -166
- package/es6/src/webapi/postman/local-view.js +0 -167
- package/es6/src/webapi/postman/openapi-to-postman.js +0 -179
- package/es6/src/webapi/workspace-readme.js +0 -88
- package/es6/src/webapi.action.js +0 -18
- package/es6/src/webapi.command.js +0 -33
- package/types/src/add-api.action.d.ts +0 -13
- package/types/src/add-api.command.d.ts +0 -7
- package/types/src/add.command.d.ts +0 -6
- package/types/src/add.module.d.ts +0 -14
- package/types/src/auth0-configure.action.d.ts +0 -7
- package/types/src/auth0-configure.command.d.ts +0 -6
- package/types/src/auth0-login.action.d.ts +0 -4
- package/types/src/auth0-login.command.d.ts +0 -6
- package/types/src/auth0.command.d.ts +0 -6
- package/types/src/auth0.module.d.ts +0 -18
- package/types/src/cli.d.ts +0 -2
- package/types/src/create.command.d.ts +0 -6
- package/types/src/create.module.d.ts +0 -14
- package/types/src/domain/prompt.d.ts +0 -4
- package/types/src/models/compose.model.d.ts +0 -73
- package/types/src/models/config-yaml.model.d.ts +0 -27
- package/types/src/models/const.d.ts +0 -20
- package/types/src/models/dev-container.model.d.ts +0 -49
- package/types/src/models/docker-image.model.d.ts +0 -90
- package/types/src/models/env-file.model.d.ts +0 -16
- package/types/src/models/gitignore.model.d.ts +0 -9
- package/types/src/models/launch-json.model.d.ts +0 -7
- package/types/src/models/package-json.model.d.ts +0 -36
- package/types/src/models/project.model.d.ts +0 -60
- package/types/src/models/tsconfig.model.d.ts +0 -5
- package/types/src/models/vitest-config.model.d.ts +0 -5
- package/types/src/models/vscode-settings.model.d.ts +0 -7
- package/types/src/program.d.ts +0 -8
- package/types/src/services/scaffold.service.d.ts +0 -22
- package/types/src/version.d.ts +0 -6
- package/types/src/webapi/api-name.d.ts +0 -14
- package/types/src/webapi/auth0-commands.d.ts +0 -11
- package/types/src/webapi/authorize-scopes.d.ts +0 -7
- package/types/src/webapi/cli-options.d.ts +0 -6
- package/types/src/webapi/create-api.d.ts +0 -15
- package/types/src/webapi/devcontainer/codebase-rag-mcp.d.ts +0 -32
- package/types/src/webapi/devcontainer/codebase-rag-mcp.ts +0 -182
- package/types/src/webapi/devcontainer/host-scripts.d.ts +0 -7
- package/types/src/webapi/devcontainer/templates/codebase-rag.env.example +0 -3
- package/types/src/webapi/devcontainer/templates/initialize.sh +0 -169
- package/types/src/webapi/devcontainer/templates/post-create.sh +0 -35
- package/types/src/webapi/devcontainer/templates/post-start.sh +0 -65
- package/types/src/webapi/devcontainer/templates/tty-prompt.sh +0 -38
- package/types/src/webapi/features/auth.feature.d.ts +0 -10
- package/types/src/webapi/features/auth0-cli.d.ts +0 -84
- package/types/src/webapi/features/biome-format.d.ts +0 -5
- package/types/src/webapi/features/database.feature.d.ts +0 -7
- package/types/src/webapi/features/finalize.feature.d.ts +0 -2
- package/types/src/webapi/features/postman.feature.d.ts +0 -16
- package/types/src/webapi/models/api-package-json.model.d.ts +0 -18
- package/types/src/webapi/models/auth-config.model.d.ts +0 -26
- package/types/src/webapi/models/controller-source-file.model.d.ts +0 -46
- package/types/src/webapi/models/github-ci.model.d.ts +0 -9
- package/types/src/webapi/models/main-source-file.model.d.ts +0 -54
- package/types/src/webapi/models/production-dockerfile.model.d.ts +0 -18
- package/types/src/webapi/models/sample-files.model.d.ts +0 -7
- package/types/src/webapi/models/webapi-image.model.d.ts +0 -12
- package/types/src/webapi/postman/hello-openapi.d.ts +0 -19
- package/types/src/webapi/postman/local-view.d.ts +0 -17
- package/types/src/webapi/postman/openapi-to-postman.d.ts +0 -62
- package/types/src/webapi/workspace-readme.d.ts +0 -18
- package/types/src/webapi.action.d.ts +0 -14
- package/types/src/webapi.command.d.ts +0 -11
|
@@ -1,322 +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 POSTMAN_CALLBACKS () {
|
|
13
|
-
return POSTMAN_CALLBACKS;
|
|
14
|
-
},
|
|
15
|
-
get POSTMAN_SCOPES () {
|
|
16
|
-
return POSTMAN_SCOPES;
|
|
17
|
-
},
|
|
18
|
-
get PostmanFeature () {
|
|
19
|
-
return PostmanFeature;
|
|
20
|
-
},
|
|
21
|
-
get buildHelloOpenApiDocument () {
|
|
22
|
-
return _helloopenapi.buildHelloOpenApiDocument;
|
|
23
|
-
},
|
|
24
|
-
get buildHybridPostmanCollection () {
|
|
25
|
-
return buildHybridPostmanCollection;
|
|
26
|
-
}
|
|
27
|
-
});
|
|
28
|
-
const _nodecrypto = require("node:crypto");
|
|
29
|
-
const _nodefs = require("node:fs");
|
|
30
|
-
const _nodepath = require("node:path");
|
|
31
|
-
const _prompt = require("../../domain/prompt.js");
|
|
32
|
-
const _const = require("../../models/const.js");
|
|
33
|
-
const _helloopenapi = require("../postman/hello-openapi.js");
|
|
34
|
-
const _localview = require("../postman/local-view.js");
|
|
35
|
-
const _openapitopostman = require("../postman/openapi-to-postman.js");
|
|
36
|
-
const _auth0cli = /*#__PURE__*/ _interop_require_wildcard(require("./auth0-cli.js"));
|
|
37
|
-
function _getRequireWildcardCache(nodeInterop) {
|
|
38
|
-
if (typeof WeakMap !== "function") return null;
|
|
39
|
-
var cacheBabelInterop = new WeakMap();
|
|
40
|
-
var cacheNodeInterop = new WeakMap();
|
|
41
|
-
return (_getRequireWildcardCache = function(nodeInterop) {
|
|
42
|
-
return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
|
43
|
-
})(nodeInterop);
|
|
44
|
-
}
|
|
45
|
-
function _interop_require_wildcard(obj, nodeInterop) {
|
|
46
|
-
if (!nodeInterop && obj && obj.__esModule) {
|
|
47
|
-
return obj;
|
|
48
|
-
}
|
|
49
|
-
if (obj === null || typeof obj !== "object" && typeof obj !== "function") {
|
|
50
|
-
return {
|
|
51
|
-
default: obj
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
var cache = _getRequireWildcardCache(nodeInterop);
|
|
55
|
-
if (cache && cache.has(obj)) {
|
|
56
|
-
return cache.get(obj);
|
|
57
|
-
}
|
|
58
|
-
var newObj = {
|
|
59
|
-
__proto__: null
|
|
60
|
-
};
|
|
61
|
-
var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor;
|
|
62
|
-
for(var key in obj){
|
|
63
|
-
if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
64
|
-
var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null;
|
|
65
|
-
if (desc && (desc.get || desc.set)) {
|
|
66
|
-
Object.defineProperty(newObj, key, desc);
|
|
67
|
-
} else {
|
|
68
|
-
newObj[key] = obj[key];
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
newObj.default = obj;
|
|
73
|
-
if (cache) {
|
|
74
|
-
cache.set(obj, newObj);
|
|
75
|
-
}
|
|
76
|
-
return newObj;
|
|
77
|
-
}
|
|
78
|
-
/** Postman OAuth callback URLs (popup + "Authorize using browser"). */ const POSTMAN_CALLBACKS = [
|
|
79
|
-
"https://oauth.pstmn.io/v1/callback",
|
|
80
|
-
"https://oauth.pstmn.io/v1/browser-callback"
|
|
81
|
-
];
|
|
82
|
-
/** Scopes requested for Auth0 access tokens (OIDC + Hello CASL permissions). */ const POSTMAN_SCOPES = [
|
|
83
|
-
"openid",
|
|
84
|
-
"profile",
|
|
85
|
-
"email",
|
|
86
|
-
"read:Hello",
|
|
87
|
-
"create:Hello",
|
|
88
|
-
"update:Hello",
|
|
89
|
-
"delete:Hello"
|
|
90
|
-
].join(" ");
|
|
91
|
-
const COLLECTION_DESCRIPTION = "Open the project root in Postman Local View (not the postman/ folder). " + "Select the Local environment, then Authorization → Get New Access Token (PKCE). " + "Routes are generated from OpenAPI; Auth0 overlay is hand-authored.";
|
|
92
|
-
async function getOptions(_workspace, cliOptions) {
|
|
93
|
-
if (cliOptions.postmanProvider != null) return {};
|
|
94
|
-
const choices = [
|
|
95
|
-
{
|
|
96
|
-
label: "none",
|
|
97
|
-
description: "skip"
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
label: "local",
|
|
101
|
-
description: "Local View / Native Git files (no API key)"
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
label: "api",
|
|
105
|
-
description: "also upload to Postman.com (needs API key)"
|
|
106
|
-
}
|
|
107
|
-
];
|
|
108
|
-
const selected = await (0, _prompt.pick)("\n Postman: ", choices, (c)=>`${c.label} — ${c.description}`);
|
|
109
|
-
const provider = selected?.label ?? "none";
|
|
110
|
-
if (provider === "none") return {
|
|
111
|
-
postmanProvider: "none"
|
|
112
|
-
};
|
|
113
|
-
if (provider === "local") return {
|
|
114
|
-
postmanProvider: "local"
|
|
115
|
-
};
|
|
116
|
-
const apiKey = process.env.POSTMAN_API_KEY ?? await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../../domain/prompt.js"))).then((m)=>m.ask("\n Postman API key (app.getpostman.com/settings/me/api-keys): "));
|
|
117
|
-
const headers = {
|
|
118
|
-
"X-Api-Key": apiKey,
|
|
119
|
-
"Content-Type": "application/json"
|
|
120
|
-
};
|
|
121
|
-
const wsRes = await fetch("https://api.getpostman.com/workspaces", {
|
|
122
|
-
headers
|
|
123
|
-
});
|
|
124
|
-
if (!wsRes.ok) {
|
|
125
|
-
console.error("\n Postman API key invalid — falling back to local files.");
|
|
126
|
-
return {
|
|
127
|
-
postmanProvider: "local"
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
const { workspaces } = await wsRes.json();
|
|
131
|
-
const wsChoices = [
|
|
132
|
-
{
|
|
133
|
-
label: "<new workspace>",
|
|
134
|
-
id: null
|
|
135
|
-
},
|
|
136
|
-
...workspaces.map((w)=>({
|
|
137
|
-
label: `${w.name} (${w.type})`,
|
|
138
|
-
id: w.id
|
|
139
|
-
}))
|
|
140
|
-
];
|
|
141
|
-
const wsPicked = await (0, _prompt.pick)("\n Postman workspace: ", wsChoices, (c)=>c.label);
|
|
142
|
-
let workspaceId;
|
|
143
|
-
if (!wsPicked || wsPicked.id === null) {
|
|
144
|
-
const { ask } = await Promise.resolve().then(()=>/*#__PURE__*/ _interop_require_wildcard(require("../../domain/prompt.js")));
|
|
145
|
-
const wsName = await ask(` Workspace name: `);
|
|
146
|
-
const createRes = await fetch("https://api.getpostman.com/workspaces", {
|
|
147
|
-
method: "POST",
|
|
148
|
-
headers,
|
|
149
|
-
body: JSON.stringify({
|
|
150
|
-
workspace: {
|
|
151
|
-
name: wsName,
|
|
152
|
-
type: "personal"
|
|
153
|
-
}
|
|
154
|
-
})
|
|
155
|
-
});
|
|
156
|
-
const { workspace: ws } = await createRes.json();
|
|
157
|
-
workspaceId = ws.id;
|
|
158
|
-
console.log(` ✓ Workspace created: ${wsName}`);
|
|
159
|
-
} else {
|
|
160
|
-
workspaceId = wsPicked.id;
|
|
161
|
-
}
|
|
162
|
-
return {
|
|
163
|
-
postmanProvider: "api",
|
|
164
|
-
apiKey,
|
|
165
|
-
workspaceId
|
|
166
|
-
};
|
|
167
|
-
}
|
|
168
|
-
async function buildHybridPostmanCollection(projectName, auth) {
|
|
169
|
-
const openapi = (0, _helloopenapi.buildHelloOpenApiDocument)(projectName);
|
|
170
|
-
const base = await (0, _openapitopostman.openApiToPostmanCollection)(openapi);
|
|
171
|
-
if (base.info._postman_id == null) {
|
|
172
|
-
base.info._postman_id = (0, _nodecrypto.randomUUID)();
|
|
173
|
-
}
|
|
174
|
-
return (0, _openapitopostman.applyPostmanAuthOverlay)(base, projectName, auth, {
|
|
175
|
-
callbacks: POSTMAN_CALLBACKS,
|
|
176
|
-
scopes: POSTMAN_SCOPES,
|
|
177
|
-
description: COLLECTION_DESCRIPTION
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
/** Read a previously scaffolded auth0ClientId so re-scaffold never blanks it. */ function readExistingSpaClientId(targetDir, projectName) {
|
|
181
|
-
const envPath = (0, _nodepath.join)(targetDir, "postman/environments", `${projectName}-Local.environment.yaml`);
|
|
182
|
-
if (!(0, _nodefs.existsSync)(envPath)) return undefined;
|
|
183
|
-
const text = (0, _nodefs.readFileSync)(envPath, "utf8");
|
|
184
|
-
const match = text.match(/key:\s*auth0ClientId\s*\n\s*value:\s*["']?([^"'\n]+)/);
|
|
185
|
-
const value = match?.[1]?.trim();
|
|
186
|
-
return value && value.length > 0 ? value : undefined;
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Resolve the Postman SPA client id. Never returns empty — preserves an existing
|
|
190
|
-
* env value on Auth0 failure, otherwise throws so we do not write a blank env.
|
|
191
|
-
*/ function resolveSpaClientId(workspace) {
|
|
192
|
-
const appName = `${workspace.name} Postman`;
|
|
193
|
-
try {
|
|
194
|
-
const app = _auth0cli.ensureSpaApp(appName, [
|
|
195
|
-
...POSTMAN_CALLBACKS
|
|
196
|
-
]);
|
|
197
|
-
console.log(app.reused ? ` ✓ Reusing Auth0 SPA app for Postman PKCE (${app.client_id})` : ` ✓ Auth0 SPA app created for Postman PKCE (${app.client_id})`);
|
|
198
|
-
try {
|
|
199
|
-
_auth0cli.enableConnectionForClient(_auth0cli.DB_CONNECTION_NAME, app.client_id);
|
|
200
|
-
console.log(` ✓ Enabled ${_auth0cli.DB_CONNECTION_NAME} on Postman SPA`);
|
|
201
|
-
} catch (err) {
|
|
202
|
-
console.error(` ✗ Failed to enable DB connection on SPA: ${err instanceof Error ? err.message : err}`);
|
|
203
|
-
}
|
|
204
|
-
return app.client_id;
|
|
205
|
-
} catch (err) {
|
|
206
|
-
const existing = readExistingSpaClientId(workspace.targetDir, workspace.name);
|
|
207
|
-
if (existing) {
|
|
208
|
-
console.error(` ✗ Failed to ensure Auth0 SPA app (${err instanceof Error ? err.message : err})`);
|
|
209
|
-
console.log(` ✓ Keeping existing auth0ClientId from Postman environment (${existing})`);
|
|
210
|
-
return existing;
|
|
211
|
-
}
|
|
212
|
-
const detail = err instanceof Error ? err.message : String(err);
|
|
213
|
-
// Entity-limit messages are already actionable; avoid burying them.
|
|
214
|
-
if (_auth0cli.isAuth0EntityLimitError(err)) {
|
|
215
|
-
throw new Error(detail);
|
|
216
|
-
}
|
|
217
|
-
throw new Error(`Failed to ensure Auth0 SPA app for Postman — auth0ClientId would be empty: ${detail}`);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
async function scaffold(workspace, options) {
|
|
221
|
-
if (options.postmanProvider === "none" || options.postmanProvider == null) return;
|
|
222
|
-
const authConfigModel = workspace.getModel(_const.WELL_KNOWN_FILES.AUTH_CONFIG);
|
|
223
|
-
const authCfg = authConfigModel?.config;
|
|
224
|
-
const spaClientId = authCfg?.issuerBaseUrl ? resolveSpaClientId(workspace) : undefined;
|
|
225
|
-
const auth0Env = authCfg?.issuerBaseUrl ? {
|
|
226
|
-
auth0Domain: authCfg.issuerBaseUrl.replace(/^https?:\/\//, ""),
|
|
227
|
-
auth0Audience: authCfg.audience,
|
|
228
|
-
auth0ClientId: spaClientId
|
|
229
|
-
} : undefined;
|
|
230
|
-
const projectName = workspace.name;
|
|
231
|
-
const envName = `${projectName} Local`;
|
|
232
|
-
const collection = await buildHybridPostmanCollection(projectName, auth0Env);
|
|
233
|
-
const localFiles = (0, _localview.buildLocalViewFilesFromCollection)(collection, projectName, auth0Env, {
|
|
234
|
-
callbacks: POSTMAN_CALLBACKS,
|
|
235
|
-
scopes: POSTMAN_SCOPES
|
|
236
|
-
});
|
|
237
|
-
const envYaml = (0, _localview.buildEnvironmentYaml)(envName, auth0Env, {
|
|
238
|
-
callbacks: POSTMAN_CALLBACKS,
|
|
239
|
-
scopes: POSTMAN_SCOPES
|
|
240
|
-
});
|
|
241
|
-
workspace.addFile({
|
|
242
|
-
files: ()=>[
|
|
243
|
-
{
|
|
244
|
-
filePath: ".postman/resources.yaml",
|
|
245
|
-
content: [
|
|
246
|
-
"# Open this project root in Postman (Files → Open folder), not the postman/ subfolder.",
|
|
247
|
-
"# Postman fills workspace.id when you connect Local View.",
|
|
248
|
-
"#",
|
|
249
|
-
"# All resources under postman/ are registered automatically.",
|
|
250
|
-
"# Route requests are generated from OpenAPI; Auth0 overlay is hand-authored.",
|
|
251
|
-
""
|
|
252
|
-
].join("\n")
|
|
253
|
-
},
|
|
254
|
-
{
|
|
255
|
-
filePath: `postman/environments/${projectName}-Local.environment.yaml`,
|
|
256
|
-
content: envYaml
|
|
257
|
-
},
|
|
258
|
-
...localFiles
|
|
259
|
-
]
|
|
260
|
-
});
|
|
261
|
-
if (options.postmanProvider === "api" && options.apiKey) {
|
|
262
|
-
const headers = {
|
|
263
|
-
"X-Api-Key": options.apiKey,
|
|
264
|
-
"Content-Type": "application/json"
|
|
265
|
-
};
|
|
266
|
-
const colRes = await fetch(`https://api.getpostman.com/collections?workspace=${options.workspaceId}`, {
|
|
267
|
-
method: "POST",
|
|
268
|
-
headers,
|
|
269
|
-
body: JSON.stringify({
|
|
270
|
-
collection
|
|
271
|
-
})
|
|
272
|
-
});
|
|
273
|
-
if (colRes.ok) {
|
|
274
|
-
console.log(" ✓ Postman collection uploaded");
|
|
275
|
-
} else {
|
|
276
|
-
console.error(" ✗ Failed to upload Postman collection");
|
|
277
|
-
}
|
|
278
|
-
const envValues = [
|
|
279
|
-
{
|
|
280
|
-
key: "baseUrl",
|
|
281
|
-
value: "http://localhost:3000",
|
|
282
|
-
enabled: true
|
|
283
|
-
},
|
|
284
|
-
...auth0Env?.auth0Domain ? [
|
|
285
|
-
{
|
|
286
|
-
key: "auth0Domain",
|
|
287
|
-
value: auth0Env.auth0Domain,
|
|
288
|
-
enabled: true
|
|
289
|
-
},
|
|
290
|
-
{
|
|
291
|
-
key: "auth0Audience",
|
|
292
|
-
value: auth0Env.auth0Audience ?? "",
|
|
293
|
-
enabled: true
|
|
294
|
-
},
|
|
295
|
-
{
|
|
296
|
-
key: "auth0ClientId",
|
|
297
|
-
value: auth0Env.auth0ClientId ?? "",
|
|
298
|
-
enabled: true
|
|
299
|
-
}
|
|
300
|
-
] : []
|
|
301
|
-
];
|
|
302
|
-
const envRes = await fetch(`https://api.getpostman.com/environments?workspace=${options.workspaceId}`, {
|
|
303
|
-
method: "POST",
|
|
304
|
-
headers,
|
|
305
|
-
body: JSON.stringify({
|
|
306
|
-
environment: {
|
|
307
|
-
name: envName,
|
|
308
|
-
values: envValues
|
|
309
|
-
}
|
|
310
|
-
})
|
|
311
|
-
});
|
|
312
|
-
if (envRes.ok) {
|
|
313
|
-
console.log(" ✓ Postman environment uploaded");
|
|
314
|
-
} else {
|
|
315
|
-
console.error(" ✗ Failed to upload Postman environment");
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
const PostmanFeature = {
|
|
320
|
-
getOptions,
|
|
321
|
-
scaffold
|
|
322
|
-
};
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "ApiPackageJson", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return ApiPackageJson;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _const = require("../../models/const.js");
|
|
12
|
-
const _packagejsonmodel = require("../../models/package-json.model.js");
|
|
13
|
-
const _version = require("../../version.js");
|
|
14
|
-
class ApiPackageJson {
|
|
15
|
-
workspaceName;
|
|
16
|
-
apiName;
|
|
17
|
-
key = _const.WELL_KNOWN_FILES.API_PACKAGE_JSON;
|
|
18
|
-
_dependencies;
|
|
19
|
-
_devDependencies = {};
|
|
20
|
-
constructor(workspaceName, apiName = "api"){
|
|
21
|
-
this.workspaceName = workspaceName;
|
|
22
|
-
this.apiName = apiName;
|
|
23
|
-
this._dependencies = {
|
|
24
|
-
"@decohere/http": _version.CREATE_VERSION,
|
|
25
|
-
"@decohere/crucible": _version.CREATE_VERSION
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
addDependency(name, version) {
|
|
29
|
-
this._dependencies[name] = version;
|
|
30
|
-
return this;
|
|
31
|
-
}
|
|
32
|
-
addDevDependency(name, version) {
|
|
33
|
-
this._devDependencies[name] = version;
|
|
34
|
-
return this;
|
|
35
|
-
}
|
|
36
|
-
files() {
|
|
37
|
-
const pkg = {
|
|
38
|
-
name: `@${this.workspaceName}/${this.apiName}`,
|
|
39
|
-
version: "0.0.1",
|
|
40
|
-
private: true,
|
|
41
|
-
type: "module",
|
|
42
|
-
main: "src/main.ts",
|
|
43
|
-
scripts: {
|
|
44
|
-
lint: "biome check --write ."
|
|
45
|
-
},
|
|
46
|
-
dependencies: (0, _packagejsonmodel.sortStringMap)(this._dependencies)
|
|
47
|
-
};
|
|
48
|
-
if (Object.keys(this._devDependencies).length > 0) {
|
|
49
|
-
pkg.devDependencies = (0, _packagejsonmodel.sortStringMap)(this._devDependencies);
|
|
50
|
-
}
|
|
51
|
-
return [
|
|
52
|
-
{
|
|
53
|
-
filePath: "package.json",
|
|
54
|
-
content: JSON.stringify(pkg, null, 2)
|
|
55
|
-
}
|
|
56
|
-
];
|
|
57
|
-
}
|
|
58
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "AuthConfigModel", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return AuthConfigModel;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _const = require("../../models/const.js");
|
|
12
|
-
class AuthConfigModel {
|
|
13
|
-
config;
|
|
14
|
-
key = _const.WELL_KNOWN_FILES.AUTH_CONFIG;
|
|
15
|
-
constructor(config){
|
|
16
|
-
this.config = config;
|
|
17
|
-
}
|
|
18
|
-
setAdminUser(adminUser) {
|
|
19
|
-
this.config.adminUser = adminUser;
|
|
20
|
-
}
|
|
21
|
-
files() {
|
|
22
|
-
return [];
|
|
23
|
-
}
|
|
24
|
-
}
|
|
@@ -1,190 +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 ControllerMethod () {
|
|
13
|
-
return ControllerMethod;
|
|
14
|
-
},
|
|
15
|
-
get ControllerSourceFile () {
|
|
16
|
-
return ControllerSourceFile;
|
|
17
|
-
}
|
|
18
|
-
});
|
|
19
|
-
const _const = require("../../models/const.js");
|
|
20
|
-
/** Tracks named imports from a single module path, split into values and types. */ class ImportGroup {
|
|
21
|
-
_values = [];
|
|
22
|
-
_types = [];
|
|
23
|
-
add(name, typeOnly = false) {
|
|
24
|
-
if (typeOnly) {
|
|
25
|
-
// Value import already present — value wins, skip
|
|
26
|
-
if (!this._values.includes(name) && !this._types.includes(name)) {
|
|
27
|
-
this._types.push(name);
|
|
28
|
-
}
|
|
29
|
-
} else {
|
|
30
|
-
// Promote: remove from types if it was previously type-only
|
|
31
|
-
const typeIdx = this._types.indexOf(name);
|
|
32
|
-
if (typeIdx !== -1) this._types.splice(typeIdx, 1);
|
|
33
|
-
if (!this._values.includes(name)) this._values.push(name);
|
|
34
|
-
}
|
|
35
|
-
return this;
|
|
36
|
-
}
|
|
37
|
-
render(from) {
|
|
38
|
-
const values = [
|
|
39
|
-
...this._values
|
|
40
|
-
].sort();
|
|
41
|
-
const types = [
|
|
42
|
-
...this._types
|
|
43
|
-
].sort().map((n)=>`type ${n}`);
|
|
44
|
-
return `import { ${[
|
|
45
|
-
...values,
|
|
46
|
-
...types
|
|
47
|
-
].join(", ")} } from "${from}";`;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
class ControllerMethod {
|
|
51
|
-
name;
|
|
52
|
-
_httpDecorator;
|
|
53
|
-
_params;
|
|
54
|
-
_extraDecorators = [];
|
|
55
|
-
_body;
|
|
56
|
-
_isAsync;
|
|
57
|
-
constructor(name, _httpDecorator, _params, isAsync, body){
|
|
58
|
-
this.name = name;
|
|
59
|
-
this._httpDecorator = _httpDecorator;
|
|
60
|
-
this._params = _params;
|
|
61
|
-
this._isAsync = isAsync;
|
|
62
|
-
this._body = body;
|
|
63
|
-
}
|
|
64
|
-
addDecorator(decorator) {
|
|
65
|
-
this._extraDecorators.push(decorator);
|
|
66
|
-
return this;
|
|
67
|
-
}
|
|
68
|
-
/** Replace the method body lines (used by features that extend generated behaviour). */ setBody(body) {
|
|
69
|
-
this._body = body;
|
|
70
|
-
return this;
|
|
71
|
-
}
|
|
72
|
-
setAsync(isAsync = true) {
|
|
73
|
-
this._isAsync = isAsync;
|
|
74
|
-
return this;
|
|
75
|
-
}
|
|
76
|
-
render() {
|
|
77
|
-
const lines = [];
|
|
78
|
-
for (const d of this._extraDecorators){
|
|
79
|
-
lines.push(`\t${d}`);
|
|
80
|
-
}
|
|
81
|
-
lines.push(`\t${this._httpDecorator}`);
|
|
82
|
-
const asyncKw = this._isAsync ? "async " : "";
|
|
83
|
-
lines.push(`\tpublic ${asyncKw}${this.name}(${this._params}) {`);
|
|
84
|
-
for (const line of this._body){
|
|
85
|
-
lines.push(`\t\t${line}`);
|
|
86
|
-
}
|
|
87
|
-
lines.push("\t}");
|
|
88
|
-
return lines;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
class ControllerSourceFile {
|
|
92
|
-
key = _const.WELL_KNOWN_FILES.CONTROLLER_TS;
|
|
93
|
-
_cortexImports = new ImportGroup().add("Controller").add("Delete").add("Field").add("Get").add("Param").add("Post").add("Put").add("Query");
|
|
94
|
-
_extraImportGroups = new Map([
|
|
95
|
-
[
|
|
96
|
-
"@decohere/crucible/decorators",
|
|
97
|
-
new ImportGroup().add("inject")
|
|
98
|
-
]
|
|
99
|
-
]);
|
|
100
|
-
_classDecorators = [];
|
|
101
|
-
/** Explicit @inject(SampleService): using the class as a value keeps its import, which SWC can drop when the class is only a type (design:paramtypes then collapses to Object). */ _ctorParams = [
|
|
102
|
-
"@inject(SampleService) private readonly sample: SampleService"
|
|
103
|
-
];
|
|
104
|
-
_methods = new Map([
|
|
105
|
-
[
|
|
106
|
-
"getAll",
|
|
107
|
-
new ControllerMethod("getAll", "@Get()", '@Query("filter") filter: string', true, [
|
|
108
|
-
"const greeting = await this.sample.getGreeting();",
|
|
109
|
-
"return { filter, message: greeting };"
|
|
110
|
-
])
|
|
111
|
-
],
|
|
112
|
-
[
|
|
113
|
-
"getOne",
|
|
114
|
-
new ControllerMethod("getOne", '@Get("/:id")', '@Param("id") id: string', false, [
|
|
115
|
-
"return { id, message: `Hello ${id}!` };"
|
|
116
|
-
])
|
|
117
|
-
],
|
|
118
|
-
[
|
|
119
|
-
"create",
|
|
120
|
-
new ControllerMethod("create", "@Post()", "body: HelloBody", false, [
|
|
121
|
-
"return { message: body.message };"
|
|
122
|
-
])
|
|
123
|
-
],
|
|
124
|
-
[
|
|
125
|
-
"update",
|
|
126
|
-
new ControllerMethod("update", '@Put("/:id")', '@Param("id") id: string, body: HelloBody', false, [
|
|
127
|
-
"return { id, message: body.message };"
|
|
128
|
-
])
|
|
129
|
-
],
|
|
130
|
-
[
|
|
131
|
-
"remove",
|
|
132
|
-
new ControllerMethod("remove", '@Delete("/:id")', '@Param("id") id: string', false, [
|
|
133
|
-
"return { id, deleted: true };"
|
|
134
|
-
])
|
|
135
|
-
]
|
|
136
|
-
]);
|
|
137
|
-
/** Retrieve a method by name to mutate its decorators. */ getMethod(name) {
|
|
138
|
-
const m = this._methods.get(name);
|
|
139
|
-
if (m == null) throw new Error(`No method '${name}' on ControllerSourceFile`);
|
|
140
|
-
return m;
|
|
141
|
-
}
|
|
142
|
-
/** Add a class-level decorator (e.g. `@Authorize`). */ addDecorator(decorator) {
|
|
143
|
-
this._classDecorators.push(decorator);
|
|
144
|
-
return this;
|
|
145
|
-
}
|
|
146
|
-
/** Add a named import from `@decohere/http`. */ addCortexImport(name, typeOnly = false) {
|
|
147
|
-
this._cortexImports.add(name, typeOnly);
|
|
148
|
-
return this;
|
|
149
|
-
}
|
|
150
|
-
/** Add a named import from any other module. */ addImport(name, from, typeOnly = false) {
|
|
151
|
-
const group = this._extraImportGroups.get(from) ?? new ImportGroup();
|
|
152
|
-
group.add(name, typeOnly);
|
|
153
|
-
this._extraImportGroups.set(from, group);
|
|
154
|
-
return this;
|
|
155
|
-
}
|
|
156
|
-
/** Add a constructor parameter (e.g. an injected dependency). */ addConstructorParam(param) {
|
|
157
|
-
this._ctorParams.push(param);
|
|
158
|
-
return this;
|
|
159
|
-
}
|
|
160
|
-
files() {
|
|
161
|
-
const lines = [
|
|
162
|
-
this._cortexImports.render("@decohere/http"),
|
|
163
|
-
'import { SampleService } from "../services/sample.service.js";'
|
|
164
|
-
];
|
|
165
|
-
for (const [from, group] of this._extraImportGroups){
|
|
166
|
-
lines.push(group.render(from));
|
|
167
|
-
}
|
|
168
|
-
lines.push("", "class HelloBody {", '\t@Field("body", "message")', "\tpublic message!: string;", "}", "");
|
|
169
|
-
for (const d of this._classDecorators){
|
|
170
|
-
lines.push(d);
|
|
171
|
-
}
|
|
172
|
-
lines.push('@Controller({ prefix: "/hello" })', "export class HelloController {", "");
|
|
173
|
-
lines.push("\tpublic constructor(");
|
|
174
|
-
for (const p of this._ctorParams){
|
|
175
|
-
lines.push(`\t\t${p},`);
|
|
176
|
-
}
|
|
177
|
-
lines.push("\t) {}");
|
|
178
|
-
lines.push("");
|
|
179
|
-
for (const method of this._methods.values()){
|
|
180
|
-
lines.push(...method.render(), "");
|
|
181
|
-
}
|
|
182
|
-
lines.push("}", "");
|
|
183
|
-
return [
|
|
184
|
-
{
|
|
185
|
-
filePath: "src/controllers/hello.controller.ts",
|
|
186
|
-
content: lines.join("\n")
|
|
187
|
-
}
|
|
188
|
-
];
|
|
189
|
-
}
|
|
190
|
-
}
|