@decohere/scaffold 0.0.1-dev.126 → 0.0.1-dev.80
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/commonjs/src/add-api.action.js +3 -1
- package/commonjs/src/add-api.command.js +3 -1
- package/commonjs/src/add.command.js +3 -1
- package/commonjs/src/add.module.js +3 -1
- package/commonjs/src/cli.js +3 -2
- package/commonjs/src/create.command.js +3 -1
- package/commonjs/src/create.module.js +3 -1
- package/commonjs/src/domain/prompt.js +2 -0
- package/commonjs/src/index.js +2 -12
- package/commonjs/src/models/compose.model.js +3 -111
- package/commonjs/src/models/config-yaml.model.js +2 -0
- package/commonjs/src/models/const.js +2 -0
- package/commonjs/src/models/dev-container.model.js +10 -66
- package/commonjs/src/models/docker-image.model.js +6 -24
- package/commonjs/src/models/env-file.model.js +2 -0
- package/commonjs/src/models/gitignore.model.js +2 -7
- package/commonjs/src/models/launch-json.model.js +4 -0
- package/commonjs/src/models/package-json.model.js +7 -14
- package/commonjs/src/models/project.model.js +2 -0
- package/commonjs/src/models/source-file.js +2 -0
- package/commonjs/src/models/tsconfig.model.js +2 -0
- package/commonjs/src/models/vitest-config.model.js +2 -0
- package/commonjs/src/models/vscode-settings.model.js +2 -0
- package/commonjs/src/program.js +6 -13
- package/commonjs/src/resolve-target-dir.js +2 -0
- package/commonjs/src/services/scaffold.service.js +2 -0
- package/commonjs/src/version.js +2 -0
- package/commonjs/src/webapi/api-name.js +2 -0
- package/commonjs/src/webapi/cli-options.js +2 -0
- package/commonjs/src/webapi/const.js +3 -7
- package/commonjs/src/webapi/create-api.js +18 -46
- package/commonjs/src/webapi/devcontainer/codebase-rag-mcp.js +2 -0
- package/commonjs/src/webapi/devcontainer/host-scripts.js +2 -0
- package/commonjs/src/webapi/features/auth.feature.js +44 -11
- package/commonjs/src/webapi/features/auth0-cli.js +3 -40
- package/commonjs/src/webapi/features/biome-format.js +2 -0
- package/commonjs/src/webapi/features/database.feature.js +10 -10
- package/commonjs/src/webapi/features/finalize.feature.js +4 -2
- package/commonjs/src/webapi/features/postman.feature.js +2 -0
- package/commonjs/src/webapi/models/api-package-json.model.js +3 -1
- package/commonjs/src/webapi/models/auth-config.model.js +2 -0
- package/commonjs/src/webapi/models/controller-source-file.model.js +8 -5
- package/commonjs/src/webapi/models/github-ci.model.js +6 -6
- package/commonjs/src/webapi/models/main-source-file.model.js +5 -3
- package/commonjs/src/webapi/models/production-dockerfile.model.js +109 -27
- package/commonjs/src/webapi/models/sample-files.model.js +4 -2
- package/commonjs/src/webapi/models/webapi-image.model.js +2 -0
- package/commonjs/src/webapi/postman/hello-openapi.js +2 -0
- package/commonjs/src/webapi/postman/local-view.js +2 -0
- package/commonjs/src/webapi/postman/openapi-to-postman.js +8 -44
- package/commonjs/src/webapi/workspace-readme.js +5 -4
- package/commonjs/src/webapi.action.js +3 -1
- package/commonjs/src/webapi.command.js +4 -2
- package/es6/src/add-api.action.js +3 -1
- package/es6/src/add-api.command.js +3 -1
- package/es6/src/add.command.js +3 -1
- package/es6/src/add.module.js +3 -1
- package/es6/src/cli.js +3 -2
- package/es6/src/create.command.js +3 -1
- package/es6/src/create.module.js +3 -1
- package/es6/src/domain/prompt.js +2 -0
- package/es6/src/index.js +2 -3
- package/es6/src/models/compose.model.js +3 -113
- package/es6/src/models/config-yaml.model.js +2 -0
- package/es6/src/models/const.js +2 -0
- package/es6/src/models/dev-container.model.js +10 -66
- package/es6/src/models/docker-image.model.js +6 -24
- package/es6/src/models/env-file.model.js +2 -0
- package/es6/src/models/gitignore.model.js +2 -7
- package/es6/src/models/launch-json.model.js +4 -0
- package/es6/src/models/package-json.model.js +7 -11
- package/es6/src/models/project.model.js +2 -0
- package/es6/src/models/source-file.js +2 -0
- package/es6/src/models/tsconfig.model.js +2 -0
- package/es6/src/models/vitest-config.model.js +2 -0
- package/es6/src/models/vscode-settings.model.js +2 -0
- package/es6/src/program.js +7 -18
- package/es6/src/resolve-target-dir.js +2 -0
- package/es6/src/services/scaffold.service.js +2 -0
- package/es6/src/version.js +2 -0
- package/es6/src/webapi/api-name.js +2 -0
- package/es6/src/webapi/cli-options.js +2 -0
- package/es6/src/webapi/const.js +3 -4
- package/es6/src/webapi/create-api.js +20 -48
- package/es6/src/webapi/devcontainer/codebase-rag-mcp.js +2 -0
- package/es6/src/webapi/devcontainer/host-scripts.js +2 -0
- package/es6/src/webapi/features/auth.feature.js +44 -11
- package/es6/src/webapi/features/auth0-cli.js +4 -35
- package/es6/src/webapi/features/biome-format.js +2 -0
- package/es6/src/webapi/features/database.feature.js +10 -10
- package/es6/src/webapi/features/finalize.feature.js +4 -2
- package/es6/src/webapi/features/postman.feature.js +2 -0
- package/es6/src/webapi/models/api-package-json.model.js +4 -2
- package/es6/src/webapi/models/auth-config.model.js +2 -0
- package/es6/src/webapi/models/controller-source-file.model.js +8 -5
- package/es6/src/webapi/models/github-ci.model.js +6 -6
- package/es6/src/webapi/models/main-source-file.model.js +5 -3
- package/es6/src/webapi/models/production-dockerfile.model.js +117 -35
- package/es6/src/webapi/models/sample-files.model.js +4 -2
- package/es6/src/webapi/models/webapi-image.model.js +2 -0
- package/es6/src/webapi/postman/hello-openapi.js +2 -0
- package/es6/src/webapi/postman/local-view.js +2 -0
- package/es6/src/webapi/postman/openapi-to-postman.js +4 -4
- package/es6/src/webapi/workspace-readme.js +5 -4
- package/es6/src/webapi.action.js +3 -1
- package/es6/src/webapi.command.js +4 -2
- package/package.json +4 -14
- package/types/src/add-api.action.d.ts +1 -1
- package/types/src/add-api.command.d.ts +1 -1
- package/types/src/add.command.d.ts +2 -2
- package/types/src/add.module.d.ts +1 -1
- package/types/src/create.command.d.ts +2 -2
- package/types/src/create.module.d.ts +1 -1
- package/types/src/index.d.ts +0 -4
- package/types/src/models/compose.model.d.ts +0 -9
- package/types/src/models/dev-container.model.d.ts +1 -13
- package/types/src/models/docker-image.model.d.ts +0 -6
- package/types/src/models/gitignore.model.d.ts +0 -2
- package/types/src/models/package-json.model.d.ts +0 -1
- package/types/src/program.d.ts +1 -6
- package/types/src/webapi/const.d.ts +0 -2
- package/types/src/webapi/create-api.d.ts +1 -1
- package/types/src/webapi/features/auth0-cli.d.ts +0 -4
- package/types/src/webapi/models/api-package-json.model.d.ts +1 -1
- package/types/src/webapi/models/controller-source-file.model.d.ts +2 -2
- package/types/src/webapi/models/main-source-file.model.d.ts +1 -1
- package/types/src/webapi/models/production-dockerfile.model.d.ts +14 -9
- package/types/src/webapi.action.d.ts +1 -1
- package/types/src/webapi.command.d.ts +1 -1
- package/LICENSE +0 -15
- 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/webapi/auth0-commands.js +0 -250
- package/commonjs/src/webapi/authorize-scopes.js +0 -79
- 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/webapi/auth0-commands.js +0 -185
- package/es6/src/webapi/authorize-scopes.js +0 -55
- 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/webapi/auth0-commands.d.ts +0 -11
- package/types/src/webapi/authorize-scopes.d.ts +0 -7
|
@@ -1,79 +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 OIDC_SCOPES () {
|
|
13
|
-
return OIDC_SCOPES;
|
|
14
|
-
},
|
|
15
|
-
get discoverAuthorizeScopes () {
|
|
16
|
-
return discoverAuthorizeScopes;
|
|
17
|
-
},
|
|
18
|
-
get mergeApiScopes () {
|
|
19
|
-
return mergeApiScopes;
|
|
20
|
-
},
|
|
21
|
-
get postmanRequestedScopes () {
|
|
22
|
-
return postmanRequestedScopes;
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
const _nodefs = require("node:fs");
|
|
26
|
-
const _nodepath = require("node:path");
|
|
27
|
-
const _apiname = require("./api-name.js");
|
|
28
|
-
const OIDC_SCOPES = [
|
|
29
|
-
"openid",
|
|
30
|
-
"profile",
|
|
31
|
-
"email"
|
|
32
|
-
];
|
|
33
|
-
const AUTHORIZE_RE = /@Authorize\(\s*(["'])([^"']+)\1(?:\s*,\s*(["'])([^"']+)\3)?\s*\)/g;
|
|
34
|
-
function isAppSource(fileName) {
|
|
35
|
-
return fileName.endsWith(".ts") && !fileName.endsWith(".spec.ts") && !fileName.endsWith(".test.ts") && !fileName.endsWith(".d.ts");
|
|
36
|
-
}
|
|
37
|
-
function discoverAuthorizeScopes(rootDir) {
|
|
38
|
-
const scopes = new Set();
|
|
39
|
-
for (const apiName of (0, _apiname.listExistingApiNames)(rootDir)){
|
|
40
|
-
collectFromDir((0, _nodepath.join)(rootDir, "projects", apiName, "src"), scopes);
|
|
41
|
-
}
|
|
42
|
-
return [
|
|
43
|
-
...scopes
|
|
44
|
-
].sort();
|
|
45
|
-
}
|
|
46
|
-
function collectFromDir(dir, scopes) {
|
|
47
|
-
if (!(0, _nodefs.existsSync)(dir)) return;
|
|
48
|
-
for (const entry of (0, _nodefs.readdirSync)(dir, {
|
|
49
|
-
withFileTypes: true
|
|
50
|
-
})){
|
|
51
|
-
const path = (0, _nodepath.join)(dir, entry.name);
|
|
52
|
-
if (entry.isDirectory()) {
|
|
53
|
-
collectFromDir(path, scopes);
|
|
54
|
-
continue;
|
|
55
|
-
}
|
|
56
|
-
if (!entry.isFile() || !isAppSource(entry.name)) continue;
|
|
57
|
-
const text = (0, _nodefs.readFileSync)(path, "utf8");
|
|
58
|
-
for (const match of text.matchAll(AUTHORIZE_RE)){
|
|
59
|
-
const action = match[2];
|
|
60
|
-
if (action == null || action === "") continue;
|
|
61
|
-
const subject = match[4] ?? "all";
|
|
62
|
-
scopes.add(`${action}:${subject}`);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
function mergeApiScopes(discovered, existing) {
|
|
67
|
-
return [
|
|
68
|
-
...new Set([
|
|
69
|
-
...existing,
|
|
70
|
-
...discovered
|
|
71
|
-
])
|
|
72
|
-
].sort();
|
|
73
|
-
}
|
|
74
|
-
function postmanRequestedScopes(apiScopes) {
|
|
75
|
-
return [
|
|
76
|
-
...OIDC_SCOPES,
|
|
77
|
-
...apiScopes
|
|
78
|
-
].join(" ");
|
|
79
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
-
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
-
}
|
|
7
|
-
import { injectable } from "@decohere/crucible/decorators";
|
|
8
|
-
import { ActionResponse } from "@decohere/devkit/cli";
|
|
9
|
-
import { runAuth0Configure } from "./webapi/auth0-commands.js";
|
|
10
|
-
export class Auth0ConfigureAction {
|
|
11
|
-
async run(options) {
|
|
12
|
-
runAuth0Configure(typeof options.d === "string" ? options.d : ".");
|
|
13
|
-
return ActionResponse.Success("configure");
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
Auth0ConfigureAction = _ts_decorate([
|
|
17
|
-
injectable()
|
|
18
|
-
], Auth0ConfigureAction);
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { DecohereCommand } from "@decohere/devkit/cli";
|
|
2
|
-
import { Auth0ConfigureAction } from "./auth0-configure.action.js";
|
|
3
|
-
export class Auth0ConfigureCommand extends DecohereCommand {
|
|
4
|
-
get actions() {
|
|
5
|
-
return [
|
|
6
|
-
Auth0ConfigureAction
|
|
7
|
-
];
|
|
8
|
-
}
|
|
9
|
-
constructor(name){
|
|
10
|
-
super(name);
|
|
11
|
-
this.description("Update Auth0 API permissions and Postman SPA from @Authorize in this workspace");
|
|
12
|
-
this.option("-d <directory>", "Workspace directory (default: current directory)", ".");
|
|
13
|
-
}
|
|
14
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
2
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
-
else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
-
}
|
|
7
|
-
import { injectable } from "@decohere/crucible/decorators";
|
|
8
|
-
import { ActionResponse } from "@decohere/devkit/cli";
|
|
9
|
-
import { runAuth0Login } from "./webapi/auth0-commands.js";
|
|
10
|
-
export class Auth0LoginAction {
|
|
11
|
-
async run() {
|
|
12
|
-
runAuth0Login();
|
|
13
|
-
return ActionResponse.Success("login");
|
|
14
|
-
}
|
|
15
|
-
}
|
|
16
|
-
Auth0LoginAction = _ts_decorate([
|
|
17
|
-
injectable()
|
|
18
|
-
], Auth0LoginAction);
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { DecohereCommand } from "@decohere/devkit/cli";
|
|
2
|
-
import { Auth0LoginAction } from "./auth0-login.action.js";
|
|
3
|
-
export class Auth0LoginCommand extends DecohereCommand {
|
|
4
|
-
get actions() {
|
|
5
|
-
return [
|
|
6
|
-
Auth0LoginAction
|
|
7
|
-
];
|
|
8
|
-
}
|
|
9
|
-
constructor(name){
|
|
10
|
-
super(name);
|
|
11
|
-
this.description("Authenticate the Auth0 CLI (device URL; tokens persist on the host)");
|
|
12
|
-
}
|
|
13
|
-
}
|
package/es6/src/auth0.command.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { DecohereCommand } from "@decohere/devkit/cli";
|
|
2
|
-
export class Auth0Command extends DecohereCommand {
|
|
3
|
-
constructor(commands, handler){
|
|
4
|
-
super(Auth0Command);
|
|
5
|
-
this.description("Log in to Auth0 and update a scaffolded client");
|
|
6
|
-
this.exitOverride();
|
|
7
|
-
this.registerSubcommand(commands, handler);
|
|
8
|
-
}
|
|
9
|
-
run(_actions, _options) {
|
|
10
|
-
return this.help();
|
|
11
|
-
}
|
|
12
|
-
}
|
package/es6/src/auth0.module.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { ModuleManifest } from "@decohere/devkit/cli";
|
|
2
|
-
import { Auth0Command } from "./auth0.command.js";
|
|
3
|
-
import { Auth0ConfigureAction } from "./auth0-configure.action.js";
|
|
4
|
-
import { Auth0ConfigureCommand } from "./auth0-configure.command.js";
|
|
5
|
-
import { Auth0LoginAction } from "./auth0-login.action.js";
|
|
6
|
-
import { Auth0LoginCommand } from "./auth0-login.command.js";
|
|
7
|
-
export class Auth0Module extends ModuleManifest {
|
|
8
|
-
id = "auth0";
|
|
9
|
-
command = Auth0Command;
|
|
10
|
-
commands = {
|
|
11
|
-
login: Auth0LoginCommand,
|
|
12
|
-
configure: Auth0ConfigureCommand
|
|
13
|
-
};
|
|
14
|
-
actions = {
|
|
15
|
-
login: Auth0LoginAction,
|
|
16
|
-
configure: Auth0ConfigureAction
|
|
17
|
-
};
|
|
18
|
-
}
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { basename, join, resolve } from "node:path";
|
|
3
|
-
import { CreateCliError, isDecohereWorkspace } from "./api-name.js";
|
|
4
|
-
import { discoverAuthorizeScopes, mergeApiScopes, postmanRequestedScopes } from "./authorize-scopes.js";
|
|
5
|
-
import * as auth0Cli from "./features/auth0-cli.js";
|
|
6
|
-
/** Same Postman OAuth callbacks as `PostmanFeature`. */ const POSTMAN_CALLBACKS = [
|
|
7
|
-
"https://oauth.pstmn.io/v1/callback",
|
|
8
|
-
"https://oauth.pstmn.io/v1/browser-callback"
|
|
9
|
-
];
|
|
10
|
-
const SPA_SUFFIX = " Postman";
|
|
11
|
-
export const AUTH0_CLI_INSTALL_MESSAGE = "The Auth0 CLI is required.\n" + " It is bundled in the Decohere create image (`npx @decohere/create auth0 login`).\n" + " Host install: brew install auth0/auth0-cli/auth0\n" + " Or visit: https://github.com/auth0/auth0-cli";
|
|
12
|
-
function fail(message) {
|
|
13
|
-
throw new CreateCliError(message);
|
|
14
|
-
}
|
|
15
|
-
/** Throw if the `auth0` binary is missing. */ export function requireInstalled() {
|
|
16
|
-
if (auth0Cli.isInstalled()) return;
|
|
17
|
-
fail(AUTH0_CLI_INSTALL_MESSAGE);
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Log in when there is no tenant session or the Management API token is expired.
|
|
21
|
-
* Returns the active tenant.
|
|
22
|
-
*/ export function ensureLoggedIn() {
|
|
23
|
-
requireInstalled();
|
|
24
|
-
process.stdout.write("\n Checking Auth0 credentials...");
|
|
25
|
-
let tenants = auth0Cli.listTenants();
|
|
26
|
-
if (tenants.length === 0) {
|
|
27
|
-
process.stdout.write(" not logged in.\n");
|
|
28
|
-
console.log(" Logging in to Auth0 (open the URL in your browser)...");
|
|
29
|
-
if (!auth0Cli.login()) fail("Auth0 login failed.");
|
|
30
|
-
tenants = auth0Cli.listTenants();
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
auth0Cli.listApis();
|
|
34
|
-
process.stdout.write(" ok.\n");
|
|
35
|
-
} catch (err) {
|
|
36
|
-
if (err instanceof auth0Cli.Auth0AuthError) {
|
|
37
|
-
process.stdout.write(" token expired.\n");
|
|
38
|
-
console.log(" Re-authenticating (open the URL in your browser)...");
|
|
39
|
-
if (!auth0Cli.login()) fail("Auth0 login failed.");
|
|
40
|
-
tenants = auth0Cli.listTenants();
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
const active = tenants.find((t)=>t.active) ?? tenants[0];
|
|
44
|
-
if (active == null) fail("No Auth0 tenant after login.");
|
|
45
|
-
return active;
|
|
46
|
-
}
|
|
47
|
-
export function runAuth0Login() {
|
|
48
|
-
requireInstalled();
|
|
49
|
-
console.log(" Logging in to Auth0 (open the URL in your browser)...");
|
|
50
|
-
if (!auth0Cli.login()) fail("Auth0 login failed.");
|
|
51
|
-
const tenants = auth0Cli.listTenants();
|
|
52
|
-
const active = tenants.find((t)=>t.active) ?? tenants[0];
|
|
53
|
-
if (active == null) fail("No Auth0 tenant after login.");
|
|
54
|
-
console.log(`\n ✓ Auth0 tenant: https://${active.name}`);
|
|
55
|
-
console.log(" Tokens are stored in ~/.config/auth0 (mounted into the create image).");
|
|
56
|
-
console.log(" Next: npx @decohere/create auth0 configure");
|
|
57
|
-
}
|
|
58
|
-
export function runAuth0Configure(directory = ".") {
|
|
59
|
-
const targetDir = resolve(directory);
|
|
60
|
-
if (!isDecohereWorkspace(targetDir)) {
|
|
61
|
-
throw new CreateCliError(`Not a Decohere workspace: ${targetDir}. Scaffold one first with \`npx @decohere/create webapi\`.`);
|
|
62
|
-
}
|
|
63
|
-
const active = ensureLoggedIn();
|
|
64
|
-
const issuerBaseUrl = `https://${active.name}`;
|
|
65
|
-
const projectName = process.env.DECOHERE_PROJECT_NAME?.trim() || basename(targetDir);
|
|
66
|
-
const envPath = join(targetDir, ".env");
|
|
67
|
-
const existing = readEnvFile(envPath);
|
|
68
|
-
const existingAudience = existing.AUTH0_AUDIENCE?.trim();
|
|
69
|
-
const discovered = discoverAuthorizeScopes(targetDir);
|
|
70
|
-
let existingScopes = [];
|
|
71
|
-
if (existingAudience) {
|
|
72
|
-
try {
|
|
73
|
-
existingScopes = auth0Cli.listApiScopes(existingAudience);
|
|
74
|
-
} catch (err) {
|
|
75
|
-
if (err instanceof auth0Cli.Auth0AuthError) throw err;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
const apiScopes = resolveApiScopes(discovered, existingScopes);
|
|
79
|
-
console.log(discovered.length > 0 ? ` ✓ Scanned @Authorize — ${discovered.length} scope(s) from controllers` : " ✓ No @Authorize in projects/*/src — keeping existing / Hello scopes");
|
|
80
|
-
let audience = existingAudience;
|
|
81
|
-
if (audience) {
|
|
82
|
-
try {
|
|
83
|
-
auth0Cli.updateApiScopes(audience, apiScopes);
|
|
84
|
-
console.log(` ✓ API scopes ensured (${apiScopes.join(", ")})`);
|
|
85
|
-
} catch (err) {
|
|
86
|
-
console.error(` ✗ Failed to update API scopes: ${err instanceof Error ? err.message : err}`);
|
|
87
|
-
}
|
|
88
|
-
} else {
|
|
89
|
-
const identifier = `https://api.${projectName}`;
|
|
90
|
-
console.log(`\n Creating Auth0 API ${identifier}...`);
|
|
91
|
-
audience = auth0Cli.createApi(`${projectName} API`, identifier, apiScopes).identifier;
|
|
92
|
-
console.log(` ✓ API created: ${audience}`);
|
|
93
|
-
}
|
|
94
|
-
const appName = `${projectName}${SPA_SUFFIX}`;
|
|
95
|
-
const app = auth0Cli.ensureSpaApp(appName, [
|
|
96
|
-
...POSTMAN_CALLBACKS
|
|
97
|
-
]);
|
|
98
|
-
console.log(app.reused ? ` ✓ Reusing Auth0 SPA app (${app.client_id})` : ` ✓ Auth0 SPA app created (${app.client_id})`);
|
|
99
|
-
try {
|
|
100
|
-
auth0Cli.enableConnectionForClient(auth0Cli.DB_CONNECTION_NAME, app.client_id);
|
|
101
|
-
console.log(` ✓ Enabled ${auth0Cli.DB_CONNECTION_NAME} on SPA`);
|
|
102
|
-
} catch (err) {
|
|
103
|
-
console.error(` ✗ Failed to enable DB connection on SPA: ${err instanceof Error ? err.message : err}`);
|
|
104
|
-
}
|
|
105
|
-
upsertEnv(envPath, {
|
|
106
|
-
AUTH0_ISSUER_BASE_URL: issuerBaseUrl,
|
|
107
|
-
AUTH0_AUDIENCE: audience
|
|
108
|
-
});
|
|
109
|
-
console.log(` ✓ Wrote AUTH0_ISSUER_BASE_URL and AUTH0_AUDIENCE to .env`);
|
|
110
|
-
const adminSub = existing.AUTH0_ADMIN_USER_SUB?.trim();
|
|
111
|
-
if (adminSub) {
|
|
112
|
-
try {
|
|
113
|
-
auth0Cli.assignUserPermissions(adminSub, audience, apiScopes);
|
|
114
|
-
console.log(` ✓ Assigned ${apiScopes.length} API permission(s) to admin user`);
|
|
115
|
-
} catch (err) {
|
|
116
|
-
console.error(` ✗ Failed to assign admin permissions: ${err instanceof Error ? err.message : err}`);
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
const domain = issuerBaseUrl.replace(/^https?:\/\//, "");
|
|
120
|
-
patchPostmanAuth(targetDir, projectName, {
|
|
121
|
-
domain,
|
|
122
|
-
audience,
|
|
123
|
-
clientId: app.client_id,
|
|
124
|
-
scopes: postmanRequestedScopes(apiScopes)
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
function readEnvFile(path) {
|
|
128
|
-
if (!existsSync(path)) return {};
|
|
129
|
-
const out = {};
|
|
130
|
-
for (const line of readFileSync(path, "utf8").split("\n")){
|
|
131
|
-
const trimmed = line.trim();
|
|
132
|
-
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
133
|
-
const eq = line.indexOf("=");
|
|
134
|
-
if (eq <= 0) continue;
|
|
135
|
-
out[line.slice(0, eq).trim()] = line.slice(eq + 1);
|
|
136
|
-
}
|
|
137
|
-
return out;
|
|
138
|
-
}
|
|
139
|
-
function resolveApiScopes(discovered, existing) {
|
|
140
|
-
const merged = mergeApiScopes(discovered, existing);
|
|
141
|
-
return merged.length > 0 ? merged : [
|
|
142
|
-
...auth0Cli.HELLO_API_SCOPES
|
|
143
|
-
].sort();
|
|
144
|
-
}
|
|
145
|
-
function upsertEnv(path, entries) {
|
|
146
|
-
let text = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
147
|
-
for (const [key, value] of Object.entries(entries)){
|
|
148
|
-
const re = new RegExp(`^${key}=.*$`, "m");
|
|
149
|
-
if (re.test(text)) {
|
|
150
|
-
text = text.replace(re, `${key}=${value}`);
|
|
151
|
-
} else {
|
|
152
|
-
text = `${text}${text === "" || text.endsWith("\n") ? "" : "\n"}${key}=${value}\n`;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
if (text !== "" && !text.endsWith("\n")) text = `${text}\n`;
|
|
156
|
-
writeFileSync(path, text);
|
|
157
|
-
}
|
|
158
|
-
function patchPostmanAuth(targetDir, projectName, auth) {
|
|
159
|
-
const envYaml = join(targetDir, "postman/environments", `${projectName}-Local.environment.yaml`);
|
|
160
|
-
if (existsSync(envYaml)) {
|
|
161
|
-
let yaml = readFileSync(envYaml, "utf8");
|
|
162
|
-
yaml = replaceYamlKeyedValue(yaml, "auth0Domain", auth.domain);
|
|
163
|
-
yaml = replaceYamlKeyedValue(yaml, "auth0Audience", auth.audience);
|
|
164
|
-
yaml = replaceYamlKeyedValue(yaml, "auth0ClientId", auth.clientId);
|
|
165
|
-
yaml = replaceYamlKeyedValue(yaml, "authUrl", `https://${auth.domain}/authorize?audience=${auth.audience}`);
|
|
166
|
-
yaml = replaceYamlKeyedValue(yaml, "accessTokenUrl", `https://${auth.domain}/oauth/token`);
|
|
167
|
-
yaml = replaceYamlKeyedValue(yaml, "scope", auth.scopes);
|
|
168
|
-
writeFileSync(envYaml, yaml);
|
|
169
|
-
console.log(` ✓ Updated Postman environment auth0ClientId (${auth.clientId})`);
|
|
170
|
-
console.log(` ✓ Updated Postman token scopes`);
|
|
171
|
-
}
|
|
172
|
-
const collectionDef = join(targetDir, "postman/collections", `${projectName} WebAPI`, ".resources/definition.yaml");
|
|
173
|
-
if (existsSync(collectionDef)) {
|
|
174
|
-
let yaml = readFileSync(collectionDef, "utf8");
|
|
175
|
-
yaml = yaml.replace(/^(\s*clientId:\s*).+$/m, `$1${JSON.stringify(auth.clientId)}`);
|
|
176
|
-
yaml = yaml.replace(/^(\s*scope:\s*).+$/m, `$1${JSON.stringify(auth.scopes)}`);
|
|
177
|
-
writeFileSync(collectionDef, yaml);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
/** Replace `key: <name>` / `value: …` pairs in Postman Local View environment YAML. */ function replaceYamlKeyedValue(yaml, key, value) {
|
|
181
|
-
const quoted = JSON.stringify(value);
|
|
182
|
-
const re = new RegExp(`(key:\\s*${key}\\s*\\n\\s*value:\\s*)[^\\n]+`, "m");
|
|
183
|
-
if (!re.test(yaml)) return yaml;
|
|
184
|
-
return yaml.replace(re, `$1${quoted}`);
|
|
185
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
|
-
import { listExistingApiNames } from "./api-name.js";
|
|
4
|
-
/** OIDC scopes requested on the Postman SPA token (not Auth0 API permissions). */ export const OIDC_SCOPES = [
|
|
5
|
-
"openid",
|
|
6
|
-
"profile",
|
|
7
|
-
"email"
|
|
8
|
-
];
|
|
9
|
-
const AUTHORIZE_RE = /@Authorize\(\s*(["'])([^"']+)\1(?:\s*,\s*(["'])([^"']+)\3)?\s*\)/g;
|
|
10
|
-
function isAppSource(fileName) {
|
|
11
|
-
return fileName.endsWith(".ts") && !fileName.endsWith(".spec.ts") && !fileName.endsWith(".test.ts") && !fileName.endsWith(".d.ts");
|
|
12
|
-
}
|
|
13
|
-
/** Collect action:Subject scopes from Authorize decorators in each API src tree. */ export function discoverAuthorizeScopes(rootDir) {
|
|
14
|
-
const scopes = new Set();
|
|
15
|
-
for (const apiName of listExistingApiNames(rootDir)){
|
|
16
|
-
collectFromDir(join(rootDir, "projects", apiName, "src"), scopes);
|
|
17
|
-
}
|
|
18
|
-
return [
|
|
19
|
-
...scopes
|
|
20
|
-
].sort();
|
|
21
|
-
}
|
|
22
|
-
function collectFromDir(dir, scopes) {
|
|
23
|
-
if (!existsSync(dir)) return;
|
|
24
|
-
for (const entry of readdirSync(dir, {
|
|
25
|
-
withFileTypes: true
|
|
26
|
-
})){
|
|
27
|
-
const path = join(dir, entry.name);
|
|
28
|
-
if (entry.isDirectory()) {
|
|
29
|
-
collectFromDir(path, scopes);
|
|
30
|
-
continue;
|
|
31
|
-
}
|
|
32
|
-
if (!entry.isFile() || !isAppSource(entry.name)) continue;
|
|
33
|
-
const text = readFileSync(path, "utf8");
|
|
34
|
-
for (const match of text.matchAll(AUTHORIZE_RE)){
|
|
35
|
-
const action = match[2];
|
|
36
|
-
if (action == null || action === "") continue;
|
|
37
|
-
const subject = match[4] ?? "all";
|
|
38
|
-
scopes.add(`${action}:${subject}`);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
/** Additive union so Auth0 CLI scope replace does not drop permissions already on the API. */ export function mergeApiScopes(discovered, existing) {
|
|
43
|
-
return [
|
|
44
|
-
...new Set([
|
|
45
|
-
...existing,
|
|
46
|
-
...discovered
|
|
47
|
-
])
|
|
48
|
-
].sort();
|
|
49
|
-
}
|
|
50
|
-
export function postmanRequestedScopes(apiScopes) {
|
|
51
|
-
return [
|
|
52
|
-
...OIDC_SCOPES,
|
|
53
|
-
...apiScopes
|
|
54
|
-
].join(" ");
|
|
55
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { type Action, ActionResponse } from "@decohere/devkit/cli";
|
|
2
|
-
export type Auth0ConfigureOptions = {
|
|
3
|
-
d?: string;
|
|
4
|
-
};
|
|
5
|
-
export declare class Auth0ConfigureAction implements Action<Auth0ConfigureOptions> {
|
|
6
|
-
run(options: Auth0ConfigureOptions): Promise<ActionResponse>;
|
|
7
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { DecohereCommand } from "@decohere/devkit/cli";
|
|
2
|
-
import { Auth0ConfigureAction } from "./auth0-configure.action.js";
|
|
3
|
-
export declare class Auth0ConfigureCommand extends DecohereCommand {
|
|
4
|
-
get actions(): (typeof Auth0ConfigureAction)[];
|
|
5
|
-
constructor(name: string);
|
|
6
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { Actions } from "@decohere/devkit/cli";
|
|
2
|
-
import { type CommandHandler, type CommandResponse, type CommandType, DecohereCommand } from "@decohere/devkit/cli";
|
|
3
|
-
export declare class Auth0Command extends DecohereCommand {
|
|
4
|
-
constructor(commands: Record<string, CommandType>, handler: CommandHandler);
|
|
5
|
-
run(_actions: Actions, _options: never): Promise<CommandResponse>;
|
|
6
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { ModuleManifest } from "@decohere/devkit/cli";
|
|
2
|
-
import { Auth0Command } from "./auth0.command.js";
|
|
3
|
-
import { Auth0ConfigureAction } from "./auth0-configure.action.js";
|
|
4
|
-
import { Auth0ConfigureCommand } from "./auth0-configure.command.js";
|
|
5
|
-
import { Auth0LoginAction } from "./auth0-login.action.js";
|
|
6
|
-
import { Auth0LoginCommand } from "./auth0-login.command.js";
|
|
7
|
-
export declare class Auth0Module extends ModuleManifest {
|
|
8
|
-
readonly id = "auth0";
|
|
9
|
-
readonly command: typeof Auth0Command;
|
|
10
|
-
readonly commands: {
|
|
11
|
-
login: typeof Auth0LoginCommand;
|
|
12
|
-
configure: typeof Auth0ConfigureCommand;
|
|
13
|
-
};
|
|
14
|
-
readonly actions: {
|
|
15
|
-
login: typeof Auth0LoginAction;
|
|
16
|
-
configure: typeof Auth0ConfigureAction;
|
|
17
|
-
};
|
|
18
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import * as auth0Cli from "./features/auth0-cli.js";
|
|
2
|
-
export declare const AUTH0_CLI_INSTALL_MESSAGE: string;
|
|
3
|
-
/** Throw if the `auth0` binary is missing. */
|
|
4
|
-
export declare function requireInstalled(): void;
|
|
5
|
-
/**
|
|
6
|
-
* Log in when there is no tenant session or the Management API token is expired.
|
|
7
|
-
* Returns the active tenant.
|
|
8
|
-
*/
|
|
9
|
-
export declare function ensureLoggedIn(): auth0Cli.Auth0Tenant;
|
|
10
|
-
export declare function runAuth0Login(): void;
|
|
11
|
-
export declare function runAuth0Configure(directory?: string): void;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/** OIDC scopes requested on the Postman SPA token (not Auth0 API permissions). */
|
|
2
|
-
export declare const OIDC_SCOPES: readonly ["openid", "profile", "email"];
|
|
3
|
-
/** Collect action:Subject scopes from Authorize decorators in each API src tree. */
|
|
4
|
-
export declare function discoverAuthorizeScopes(rootDir: string): string[];
|
|
5
|
-
/** Additive union so Auth0 CLI scope replace does not drop permissions already on the API. */
|
|
6
|
-
export declare function mergeApiScopes(discovered: readonly string[], existing: readonly string[]): string[];
|
|
7
|
-
export declare function postmanRequestedScopes(apiScopes: readonly string[]): string;
|