@webpieces/core-util 0.4.828 → 0.4.830
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
CHANGED
|
@@ -60,13 +60,29 @@ export declare class GeneratedApiDocsLayout {
|
|
|
60
60
|
readonly targets: LayoutTargets;
|
|
61
61
|
/** The target the nx plugin infers on a project tagged `generate:openapi`. */
|
|
62
62
|
static readonly OPENAPI_TARGET = "openapi-generate";
|
|
63
|
+
/**
|
|
64
|
+
* The target the nx plugin infers on a DTO library tagged `generate:openapi-components`: it writes
|
|
65
|
+
* the library's components-only `components.openapi.json`, which contract documents `$ref` (#1058).
|
|
66
|
+
*/
|
|
67
|
+
static readonly COMPONENTS_TARGET = "openapi-components-generate";
|
|
63
68
|
/** The target the nx plugin infers on a project tagged `generate:docs-site`. */
|
|
64
69
|
static readonly DOCS_TARGET = "docs-generate";
|
|
70
|
+
/** The file a DTO library's components-only document is written to, beside its package.json. */
|
|
71
|
+
static readonly COMPONENTS_FILE = "components.openapi.json";
|
|
72
|
+
/** The tag that opts a project into `generateTarget` — what a cure tells the reader to add. */
|
|
73
|
+
static tagOf(generateTarget: string): string;
|
|
65
74
|
constructor(
|
|
66
75
|
/** Workspace-relative project root, e.g. `libraries/apis/internal/lang-apis`. */
|
|
67
76
|
projectRoot: string, projectName: string, targets: LayoutTargets);
|
|
68
|
-
/**
|
|
69
|
-
|
|
77
|
+
/**
|
|
78
|
+
* The target `generateTarget` dependsOn, and that target's outputPath.
|
|
79
|
+
*
|
|
80
|
+
* @param generateTarget the generating target whose output directory is asked for —
|
|
81
|
+
* {@link OPENAPI_TARGET} for a contract library's documents and catalogs, {@link COMPONENTS_TARGET}
|
|
82
|
+
* for a DTO library's components document. Both write into the outputPath of the ONE sibling
|
|
83
|
+
* target they dependsOn.
|
|
84
|
+
*/
|
|
85
|
+
outputTarget(generateTarget: string): GenerateOutputLookup;
|
|
70
86
|
/** nx's own tokens, so a path written the way nx accepts it resolves the way nx resolves it. */
|
|
71
87
|
interpolate(text: string): string;
|
|
72
88
|
private siblingName;
|
|
@@ -56,8 +56,22 @@ class GeneratedApiDocsLayout {
|
|
|
56
56
|
targets;
|
|
57
57
|
/** The target the nx plugin infers on a project tagged `generate:openapi`. */
|
|
58
58
|
static OPENAPI_TARGET = 'openapi-generate';
|
|
59
|
+
/**
|
|
60
|
+
* The target the nx plugin infers on a DTO library tagged `generate:openapi-components`: it writes
|
|
61
|
+
* the library's components-only `components.openapi.json`, which contract documents `$ref` (#1058).
|
|
62
|
+
*/
|
|
63
|
+
static COMPONENTS_TARGET = 'openapi-components-generate';
|
|
59
64
|
/** The target the nx plugin infers on a project tagged `generate:docs-site`. */
|
|
60
65
|
static DOCS_TARGET = 'docs-generate';
|
|
66
|
+
/** The file a DTO library's components-only document is written to, beside its package.json. */
|
|
67
|
+
static COMPONENTS_FILE = 'components.openapi.json';
|
|
68
|
+
/** The tag that opts a project into `generateTarget` — what a cure tells the reader to add. */
|
|
69
|
+
// webpieces-disable no-function-outside-class -- static lookup on the class that owns the target names
|
|
70
|
+
static tagOf(generateTarget) {
|
|
71
|
+
return generateTarget === GeneratedApiDocsLayout.COMPONENTS_TARGET
|
|
72
|
+
? 'generate:openapi-components'
|
|
73
|
+
: 'generate:openapi';
|
|
74
|
+
}
|
|
61
75
|
constructor(
|
|
62
76
|
/** Workspace-relative project root, e.g. `libraries/apis/internal/lang-apis`. */
|
|
63
77
|
projectRoot, projectName, targets) {
|
|
@@ -65,29 +79,36 @@ class GeneratedApiDocsLayout {
|
|
|
65
79
|
this.projectName = projectName;
|
|
66
80
|
this.targets = targets;
|
|
67
81
|
}
|
|
68
|
-
/**
|
|
69
|
-
|
|
82
|
+
/**
|
|
83
|
+
* The target `generateTarget` dependsOn, and that target's outputPath.
|
|
84
|
+
*
|
|
85
|
+
* @param generateTarget the generating target whose output directory is asked for —
|
|
86
|
+
* {@link OPENAPI_TARGET} for a contract library's documents and catalogs, {@link COMPONENTS_TARGET}
|
|
87
|
+
* for a DTO library's components document. Both write into the outputPath of the ONE sibling
|
|
88
|
+
* target they dependsOn.
|
|
89
|
+
*/
|
|
90
|
+
outputTarget(generateTarget) {
|
|
70
91
|
const where = `${this.projectRoot}/project.json`;
|
|
71
|
-
const generate = this.targets[
|
|
92
|
+
const generate = this.targets[generateTarget];
|
|
72
93
|
if (generate === undefined) {
|
|
73
|
-
return this.refuse(`${this.projectName} has no ${
|
|
74
|
-
`${
|
|
94
|
+
return this.refuse(`${this.projectName} has no ${generateTarget} target.`, `Tag the project "${GeneratedApiDocsLayout.tagOf(generateTarget)}" in ${where} and state targets.` +
|
|
95
|
+
`${generateTarget} (its dependsOn and options) there.`);
|
|
75
96
|
}
|
|
76
97
|
const siblings = (generate.dependsOn ?? [])
|
|
77
98
|
.map((entry) => this.siblingName(entry))
|
|
78
99
|
.filter((name) => name !== undefined);
|
|
79
100
|
if (siblings.length !== 1) {
|
|
80
|
-
return this.refuse(`${this.projectName}:${
|
|
101
|
+
return this.refuse(`${this.projectName}:${generateTarget} must dependsOn exactly ONE ` +
|
|
81
102
|
`target of its own project — the build step whose outputPath it writes into — and it ` +
|
|
82
|
-
`names ${siblings.length === 0 ? 'none' : siblings.join(', ')}.`, `Set "dependsOn": ["build"] on targets.${
|
|
103
|
+
`names ${siblings.length === 0 ? 'none' : siblings.join(', ')}.`, `Set "dependsOn": ["build"] on targets.${generateTarget} in ${where}, ` +
|
|
83
104
|
`where "build" is the @nx/js:tsc target (dependents pull generation in with ` +
|
|
84
|
-
`"^${
|
|
105
|
+
`"^${generateTarget}" in nx.json targetDefaults).`);
|
|
85
106
|
}
|
|
86
107
|
const targetName = siblings[0];
|
|
87
108
|
const declared = this.targets[targetName]?.options
|
|
88
109
|
?.outputPath;
|
|
89
110
|
if (typeof declared !== 'string' || declared.trim() === '') {
|
|
90
|
-
return this.refuse(`${this.projectName}:${targetName} — the target ${
|
|
111
|
+
return this.refuse(`${this.projectName}:${targetName} — the target ${generateTarget} ` +
|
|
91
112
|
`dependsOn — declares no options.outputPath, so there is no build output to write the ` +
|
|
92
113
|
`documents into.`, `Declare targets.${targetName}.options.outputPath in ${where}.`);
|
|
93
114
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GeneratedApiDocsLayout.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/GeneratedApiDocsLayout.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * WHERE an api library's generated documents and MCP tool catalogs live, read from the library's own\n * nx declarations — the ONE lookup both ends use:\n *\n * - the `openapi-generate` executor (`@webpieces/nx-webpieces-rules`) uses it to decide where to WRITE;\n * - `McpToolCatalog.fromPackages` (`@webpieces/mcp-server`) uses it to decide where to READ when the\n * package it resolved is a workspace SOURCE directory (a pnpm link onto `libraries/...`) rather than\n * a built one.\n *\n * The answer is the `outputPath` of the target `openapi-generate` dependsOn — the api library's\n * `build` (its @nx/js:tsc step), which writes the directory the package is packed from. It is never a hardcoded target\n * name and never an assumed `dist/`: webpieces-ts builds into a workspace-root `dist/apps/...` while\n * other repos build into a project-local `<project>/dist`, and either assumption is wrong in the other.\n *\n * Pure and browser-safe on purpose: it reads only the target map it is handed. The caller owns the\n * filesystem and its own error type, which is why a refusal is RETURNED rather than thrown.\n */\n\n/** nx's object spelling of a `dependsOn` entry. */\nexport type LayoutDependsOnObject = {\n readonly target?: string;\n readonly projects?: string | readonly string[];\n readonly dependencies?: boolean;\n};\n\n/** One `dependsOn` entry, in either of nx's two spellings. */\nexport type LayoutDependsOn = string | LayoutDependsOnObject;\n\n/** The one option this lookup reads off the target `openapi-generate` dependsOn. */\ntype OutputPathOptions = { readonly outputPath?: string };\n\n/** One target, as project.json or nx describes it — only the two fields this lookup reads. */\nexport type LayoutTarget = {\n readonly dependsOn?: readonly LayoutDependsOn[];\n readonly options?: object;\n};\n\n/** The project's targets by name, as project.json or nx describes them. */\nexport type LayoutTargets = { readonly [targetName: string]: LayoutTarget | undefined };\n\n/** The target `openapi-generate` writes into, and its outputPath. Data-only. */\nexport class GenerateOutputTarget {\n constructor(\n readonly targetName: string,\n /** Workspace-relative, with nx's `{workspaceRoot}` / `{projectRoot}` / `{projectName}` resolved. */\n readonly outputPath: string,\n ) {}\n}\n\n/** Why the lookup failed, and the one edit that fixes it. Data-only. */\nexport class GenerateLayoutProblem {\n constructor(\n readonly problem: string,\n readonly cure: string,\n ) {}\n}\n\n/** The outcome: exactly one of the two is set. Data-only. */\nexport class GenerateOutputLookup {\n constructor(\n readonly found: GenerateOutputTarget | undefined,\n readonly problem: GenerateLayoutProblem | undefined,\n ) {}\n}\n\nexport class GeneratedApiDocsLayout {\n /** The target the nx plugin infers on a project tagged `generate:openapi`. */\n static readonly OPENAPI_TARGET = 'openapi-generate';\n /** The target the nx plugin infers on a project tagged `generate:docs-site`. */\n static readonly DOCS_TARGET = 'docs-generate';\n\n constructor(\n /** Workspace-relative project root, e.g. `libraries/apis/internal/lang-apis`. */\n readonly projectRoot: string,\n readonly projectName: string,\n readonly targets: LayoutTargets,\n ) {}\n\n /** The target `openapi-generate` dependsOn, and that target's outputPath. */\n outputTarget(): GenerateOutputLookup {\n const where = `${this.projectRoot}/project.json`;\n const generate = this.targets[GeneratedApiDocsLayout.OPENAPI_TARGET];\n if (generate === undefined) {\n return this.refuse(\n `${this.projectName} has no ${GeneratedApiDocsLayout.OPENAPI_TARGET} target.`,\n `Tag the project \"generate:openapi\" in ${where} and state targets.` +\n `${GeneratedApiDocsLayout.OPENAPI_TARGET} (its dependsOn and options) there.`,\n );\n }\n const siblings = (generate.dependsOn ?? [])\n .map((entry: LayoutDependsOn) => this.siblingName(entry))\n .filter((name: string | undefined): name is string => name !== undefined);\n if (siblings.length !== 1) {\n return this.refuse(\n `${this.projectName}:${GeneratedApiDocsLayout.OPENAPI_TARGET} must dependsOn exactly ONE ` +\n `target of its own project — the build step whose outputPath it writes into — and it ` +\n `names ${siblings.length === 0 ? 'none' : siblings.join(', ')}.`,\n `Set \"dependsOn\": [\"build\"] on targets.${GeneratedApiDocsLayout.OPENAPI_TARGET} in ${where}, ` +\n `where \"build\" is the @nx/js:tsc target (dependents pull generation in with ` +\n `\"^${GeneratedApiDocsLayout.OPENAPI_TARGET}\" in nx.json targetDefaults).`,\n );\n }\n const targetName = siblings[0]!;\n const declared = (this.targets[targetName]?.options as OutputPathOptions | undefined)\n ?.outputPath;\n if (typeof declared !== 'string' || declared.trim() === '') {\n return this.refuse(\n `${this.projectName}:${targetName} — the target ${GeneratedApiDocsLayout.OPENAPI_TARGET} ` +\n `dependsOn — declares no options.outputPath, so there is no build output to write the ` +\n `documents into.`,\n `Declare targets.${targetName}.options.outputPath in ${where}.`,\n );\n }\n return new GenerateOutputLookup(new GenerateOutputTarget(targetName, this.interpolate(declared)), undefined);\n }\n\n /** nx's own tokens, so a path written the way nx accepts it resolves the way nx resolves it. */\n interpolate(text: string): string {\n return text\n .replace(/\\{workspaceRoot\\}\\/?/g, '')\n .replace(/\\{projectRoot\\}/g, this.projectRoot)\n .replace(/\\{projectName\\}/g, this.projectName);\n }\n\n private siblingName(entry: LayoutDependsOn): string | undefined {\n if (typeof entry === 'string') return entry.startsWith('^') ? undefined : entry;\n const projects = entry.projects;\n const self =\n projects === undefined ||\n projects === 'self' ||\n (Array.isArray(projects) && projects.length === 1 && projects[0] === this.projectName);\n return entry.target !== undefined && entry.dependencies !== true && self ? entry.target : undefined;\n }\n\n private refuse(problem: string, cure: string): GenerateOutputLookup {\n return new GenerateOutputLookup(undefined, new GenerateLayoutProblem(problem, cure));\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"GeneratedApiDocsLayout.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/mcp/GeneratedApiDocsLayout.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * WHERE an api library's generated documents and MCP tool catalogs live, read from the library's own\n * nx declarations — the ONE lookup both ends use:\n *\n * - the `openapi-generate` executor (`@webpieces/nx-webpieces-rules`) uses it to decide where to WRITE;\n * - `McpToolCatalog.fromPackages` (`@webpieces/mcp-server`) uses it to decide where to READ when the\n * package it resolved is a workspace SOURCE directory (a pnpm link onto `libraries/...`) rather than\n * a built one.\n *\n * The answer is the `outputPath` of the target `openapi-generate` dependsOn — the api library's\n * `build` (its @nx/js:tsc step), which writes the directory the package is packed from. It is never a hardcoded target\n * name and never an assumed `dist/`: webpieces-ts builds into a workspace-root `dist/apps/...` while\n * other repos build into a project-local `<project>/dist`, and either assumption is wrong in the other.\n *\n * Pure and browser-safe on purpose: it reads only the target map it is handed. The caller owns the\n * filesystem and its own error type, which is why a refusal is RETURNED rather than thrown.\n */\n\n/** nx's object spelling of a `dependsOn` entry. */\nexport type LayoutDependsOnObject = {\n readonly target?: string;\n readonly projects?: string | readonly string[];\n readonly dependencies?: boolean;\n};\n\n/** One `dependsOn` entry, in either of nx's two spellings. */\nexport type LayoutDependsOn = string | LayoutDependsOnObject;\n\n/** The one option this lookup reads off the target `openapi-generate` dependsOn. */\ntype OutputPathOptions = { readonly outputPath?: string };\n\n/** One target, as project.json or nx describes it — only the two fields this lookup reads. */\nexport type LayoutTarget = {\n readonly dependsOn?: readonly LayoutDependsOn[];\n readonly options?: object;\n};\n\n/** The project's targets by name, as project.json or nx describes them. */\nexport type LayoutTargets = { readonly [targetName: string]: LayoutTarget | undefined };\n\n/** The target `openapi-generate` writes into, and its outputPath. Data-only. */\nexport class GenerateOutputTarget {\n constructor(\n readonly targetName: string,\n /** Workspace-relative, with nx's `{workspaceRoot}` / `{projectRoot}` / `{projectName}` resolved. */\n readonly outputPath: string,\n ) {}\n}\n\n/** Why the lookup failed, and the one edit that fixes it. Data-only. */\nexport class GenerateLayoutProblem {\n constructor(\n readonly problem: string,\n readonly cure: string,\n ) {}\n}\n\n/** The outcome: exactly one of the two is set. Data-only. */\nexport class GenerateOutputLookup {\n constructor(\n readonly found: GenerateOutputTarget | undefined,\n readonly problem: GenerateLayoutProblem | undefined,\n ) {}\n}\n\nexport class GeneratedApiDocsLayout {\n /** The target the nx plugin infers on a project tagged `generate:openapi`. */\n static readonly OPENAPI_TARGET = 'openapi-generate';\n /**\n * The target the nx plugin infers on a DTO library tagged `generate:openapi-components`: it writes\n * the library's components-only `components.openapi.json`, which contract documents `$ref` (#1058).\n */\n static readonly COMPONENTS_TARGET = 'openapi-components-generate';\n /** The target the nx plugin infers on a project tagged `generate:docs-site`. */\n static readonly DOCS_TARGET = 'docs-generate';\n /** The file a DTO library's components-only document is written to, beside its package.json. */\n static readonly COMPONENTS_FILE = 'components.openapi.json';\n\n /** The tag that opts a project into `generateTarget` — what a cure tells the reader to add. */\n // webpieces-disable no-function-outside-class -- static lookup on the class that owns the target names\n static tagOf(generateTarget: string): string {\n return generateTarget === GeneratedApiDocsLayout.COMPONENTS_TARGET\n ? 'generate:openapi-components'\n : 'generate:openapi';\n }\n\n constructor(\n /** Workspace-relative project root, e.g. `libraries/apis/internal/lang-apis`. */\n readonly projectRoot: string,\n readonly projectName: string,\n readonly targets: LayoutTargets,\n ) {}\n\n /**\n * The target `generateTarget` dependsOn, and that target's outputPath.\n *\n * @param generateTarget the generating target whose output directory is asked for —\n * {@link OPENAPI_TARGET} for a contract library's documents and catalogs, {@link COMPONENTS_TARGET}\n * for a DTO library's components document. Both write into the outputPath of the ONE sibling\n * target they dependsOn.\n */\n outputTarget(generateTarget: string): GenerateOutputLookup {\n const where = `${this.projectRoot}/project.json`;\n const generate = this.targets[generateTarget];\n if (generate === undefined) {\n return this.refuse(\n `${this.projectName} has no ${generateTarget} target.`,\n `Tag the project \"${GeneratedApiDocsLayout.tagOf(generateTarget)}\" in ${where} and state targets.` +\n `${generateTarget} (its dependsOn and options) there.`,\n );\n }\n const siblings = (generate.dependsOn ?? [])\n .map((entry: LayoutDependsOn) => this.siblingName(entry))\n .filter((name: string | undefined): name is string => name !== undefined);\n if (siblings.length !== 1) {\n return this.refuse(\n `${this.projectName}:${generateTarget} must dependsOn exactly ONE ` +\n `target of its own project — the build step whose outputPath it writes into — and it ` +\n `names ${siblings.length === 0 ? 'none' : siblings.join(', ')}.`,\n `Set \"dependsOn\": [\"build\"] on targets.${generateTarget} in ${where}, ` +\n `where \"build\" is the @nx/js:tsc target (dependents pull generation in with ` +\n `\"^${generateTarget}\" in nx.json targetDefaults).`,\n );\n }\n const targetName = siblings[0]!;\n const declared = (this.targets[targetName]?.options as OutputPathOptions | undefined)\n ?.outputPath;\n if (typeof declared !== 'string' || declared.trim() === '') {\n return this.refuse(\n `${this.projectName}:${targetName} — the target ${generateTarget} ` +\n `dependsOn — declares no options.outputPath, so there is no build output to write the ` +\n `documents into.`,\n `Declare targets.${targetName}.options.outputPath in ${where}.`,\n );\n }\n return new GenerateOutputLookup(new GenerateOutputTarget(targetName, this.interpolate(declared)), undefined);\n }\n\n /** nx's own tokens, so a path written the way nx accepts it resolves the way nx resolves it. */\n interpolate(text: string): string {\n return text\n .replace(/\\{workspaceRoot\\}\\/?/g, '')\n .replace(/\\{projectRoot\\}/g, this.projectRoot)\n .replace(/\\{projectName\\}/g, this.projectName);\n }\n\n private siblingName(entry: LayoutDependsOn): string | undefined {\n if (typeof entry === 'string') return entry.startsWith('^') ? undefined : entry;\n const projects = entry.projects;\n const self =\n projects === undefined ||\n projects === 'self' ||\n (Array.isArray(projects) && projects.length === 1 && projects[0] === this.projectName);\n return entry.target !== undefined && entry.dependencies !== true && self ? entry.target : undefined;\n }\n\n private refuse(problem: string, cure: string): GenerateOutputLookup {\n return new GenerateOutputLookup(undefined, new GenerateLayoutProblem(problem, cure));\n }\n}\n"]}
|