@eudiplo/cli 7.0.1 → 7.1.0-main.56dc2f0
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/README.md +13 -35
- package/dist/config-validate/index.d.ts +4 -0
- package/dist/config-validate/index.d.ts.map +1 -0
- package/dist/config-validate/index.js +4 -0
- package/dist/config-validate/index.js.map +1 -0
- package/dist/config-validate/registry.d.ts +5 -0
- package/dist/config-validate/registry.d.ts.map +1 -0
- package/dist/config-validate/registry.js +8 -0
- package/dist/config-validate/registry.js.map +1 -0
- package/dist/config-validate/registry.json +138 -0
- package/dist/config-validate/report.d.ts +21 -0
- package/dist/config-validate/report.d.ts.map +1 -0
- package/dist/config-validate/report.js +78 -0
- package/dist/config-validate/report.js.map +1 -0
- package/dist/config-validate/schemas.d.ts +2 -0
- package/dist/config-validate/schemas.d.ts.map +1 -0
- package/dist/config-validate/schemas.js +24 -0
- package/dist/config-validate/schemas.js.map +1 -0
- package/dist/config-validate/types.d.ts +33 -0
- package/dist/config-validate/types.d.ts.map +1 -0
- package/dist/config-validate/types.js +2 -0
- package/dist/config-validate/types.js.map +1 -0
- package/dist/config-validate/validator.d.ts +4 -0
- package/dist/config-validate/validator.d.ts.map +1 -0
- package/dist/config-validate/validator.js +187 -0
- package/dist/config-validate/validator.js.map +1 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +45 -4
- package/dist/runtime.js.map +1 -1
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +6 -2
- package/templates/demo-config/issuance/status-lists/pid.json +1 -1
- package/templates/schemas/CreateAttributeProviderDto.schema.json +99 -0
- package/templates/schemas/CreateClientDto.schema.json +76 -0
- package/templates/schemas/CreateRegistrarConfigDto.schema.json +61 -0
- package/templates/schemas/CreateWebhookEndpointDto.schema.json +99 -0
- package/templates/schemas/CredentialConfigCreate.schema.json +673 -0
- package/templates/schemas/ImportTenantDto.schema.json +24 -0
- package/templates/schemas/IssuanceConfig.schema.json +497 -0
- package/templates/schemas/KeyChainImportDto.schema.json +111 -0
- package/templates/schemas/KmsConfigDto.schema.json +990 -0
- package/templates/schemas/PresentationConfigCreateDto.schema.json +627 -0
- package/templates/schemas/StatusListImportDto.schema.json +61 -0
- package/templates/schemas/TrustListCreateDto.schema.json +191 -0
- package/templates/schemas.manifest.json +14 -0
package/README.md
CHANGED
|
@@ -3,11 +3,16 @@
|
|
|
3
3
|
The EUDIPLO CLI provides deployment-neutral commands for inspecting an EUDIPLO
|
|
4
4
|
instance and deployment-driver commands for local Docker Compose demos.
|
|
5
5
|
|
|
6
|
+
For installation, command reference, configuration validation, standalone
|
|
7
|
+
releases, and local development, see the [CLI documentation](../../docs/getting-started/cli.md).
|
|
8
|
+
|
|
6
9
|
## Quick Start
|
|
7
10
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
+
This package is the npm distribution of the **EUDIPLO CLI** and requires
|
|
12
|
+
Node.js 22+.
|
|
13
|
+
|
|
14
|
+
For users who do not want a local Node.js dependency, use the standalone CLI
|
|
15
|
+
installer instead: `curl -fsSL https://eudiplo.dev/install.sh | bash`
|
|
11
16
|
|
|
12
17
|
```bash
|
|
13
18
|
npx @eudiplo/cli demo
|
|
@@ -29,6 +34,10 @@ Tag selection defaults to the CLI version. For prerelease versions containing
|
|
|
29
34
|
The generated demo config remains editable after creation. Existing files are
|
|
30
35
|
preserved unless you pass `--force`.
|
|
31
36
|
|
|
37
|
+
The standalone CLI and this npm package are two distributions of the same
|
|
38
|
+
`eudiplo` command. The standalone CLI removes the Node.js requirement, but
|
|
39
|
+
Docker and Docker Compose are still required for the demo deployment.
|
|
40
|
+
|
|
32
41
|
## Package and Binary Names
|
|
33
42
|
|
|
34
43
|
The npm package is scoped as `@eudiplo/cli`, while the installed binary is
|
|
@@ -43,23 +52,7 @@ pnpm add -D @eudiplo/cli
|
|
|
43
52
|
pnpm eudiplo status
|
|
44
53
|
```
|
|
45
54
|
|
|
46
|
-
## Commands
|
|
47
|
-
|
|
48
|
-
Deployment-neutral commands work with any registered EUDIPLO instance and do not
|
|
49
|
-
require Docker:
|
|
50
|
-
|
|
51
|
-
```bash
|
|
52
|
-
eudiplo instance add production --url https://eudiplo.example.com
|
|
53
|
-
eudiplo doctor --instance production
|
|
54
|
-
eudiplo status --instance production
|
|
55
|
-
eudiplo config validate
|
|
56
|
-
eudiplo --version
|
|
57
|
-
eudiplo version
|
|
58
|
-
```
|
|
59
|
-
|
|
60
|
-
`--version` and `-v` print the installed CLI version without network access.
|
|
61
|
-
`version` also checks the npm registry and reports whether an update is
|
|
62
|
-
available.
|
|
55
|
+
## Compose Driver Commands
|
|
63
56
|
|
|
64
57
|
Compose driver commands require a `compose` instance:
|
|
65
58
|
|
|
@@ -103,21 +96,6 @@ Do not store secrets in the CLI config. Commands that need authenticated API
|
|
|
103
96
|
access read credentials from environment variables such as `EUDIPLO_CLIENT_ID`
|
|
104
97
|
and `EUDIPLO_CLIENT_SECRET`.
|
|
105
98
|
|
|
106
|
-
## Local Development
|
|
107
|
-
|
|
108
|
-
From the repository root:
|
|
109
|
-
|
|
110
|
-
```bash
|
|
111
|
-
pnpm --filter @eudiplo/cli test
|
|
112
|
-
pnpm --filter @eudiplo/cli build
|
|
113
|
-
pnpm --filter @eudiplo/cli lint
|
|
114
|
-
```
|
|
115
|
-
|
|
116
|
-
The build synchronizes and validates bundled CLI assets from canonical sources:
|
|
117
|
-
|
|
118
|
-
- `deployment/docker-compose/docker-compose.yml`
|
|
119
|
-
- `assets/config/demo/**`
|
|
120
|
-
|
|
121
99
|
## Single Executable Application
|
|
122
100
|
|
|
123
101
|
To produce a standalone CLI binary with Node.js SEA support, run:
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config-validate/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config-validate/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAC;AACvD,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { TenantResourceDefinition } from "./types.js";
|
|
2
|
+
/** Entries actually scanned by the CLI validator (excludes editor-authoring-only schemas). */
|
|
3
|
+
export declare const CLI_VALIDATED_REGISTRY: readonly TenantResourceDefinition[];
|
|
4
|
+
export declare function requiredSchemaFiles(): string[];
|
|
5
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/config-validate/registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAK3D,8FAA8F;AAC9F,eAAO,MAAM,sBAAsB,EAAE,SAAS,wBAAwB,EACM,CAAC;AAE7E,wBAAgB,mBAAmB,IAAI,MAAM,EAAE,CAE9C"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import registryData from "./registry.json" with { type: "json" };
|
|
2
|
+
const TENANT_RESOURCE_REGISTRY = registryData;
|
|
3
|
+
/** Entries actually scanned by the CLI validator (excludes editor-authoring-only schemas). */
|
|
4
|
+
export const CLI_VALIDATED_REGISTRY = TENANT_RESOURCE_REGISTRY.filter((entry) => entry.cliValidated !== false);
|
|
5
|
+
export function requiredSchemaFiles() {
|
|
6
|
+
return Array.from(new Set(CLI_VALIDATED_REGISTRY.map((entry) => entry.schemaFile)));
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/config-validate/registry.ts"],"names":[],"mappings":"AAAA,OAAO,YAAY,MAAM,iBAAiB,CAAC,OAAO,IAAI,EAAE,MAAM,EAAE,CAAC;AAGjE,MAAM,wBAAwB,GAC1B,YAA0C,CAAC;AAE/C,8FAA8F;AAC9F,MAAM,CAAC,MAAM,sBAAsB,GAC/B,wBAAwB,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,KAAK,KAAK,CAAC,CAAC;AAE7E,MAAM,UAAU,mBAAmB;IAC/B,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,sBAAsB,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;AACxF,CAAC"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"kind": "file",
|
|
4
|
+
"file": "info.json",
|
|
5
|
+
"resourceType": "tenant metadata",
|
|
6
|
+
"schemaFile": "ImportTenantDto.schema.json",
|
|
7
|
+
"required": true,
|
|
8
|
+
"fileMatch": [
|
|
9
|
+
"/assets/config/*/info.json",
|
|
10
|
+
"/apps/backend/test/fixtures/*/info.json"
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"kind": "file",
|
|
15
|
+
"file": "registrar.json",
|
|
16
|
+
"resourceType": "registrar config",
|
|
17
|
+
"schemaFile": "CreateRegistrarConfigDto.schema.json",
|
|
18
|
+
"required": false,
|
|
19
|
+
"fileMatch": [
|
|
20
|
+
"/assets/config/*/registrar*.json",
|
|
21
|
+
"/apps/backend/test/fixtures/*/registrar*.json"
|
|
22
|
+
]
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"kind": "file",
|
|
26
|
+
"file": "kms.json",
|
|
27
|
+
"resourceType": "tenant KMS config",
|
|
28
|
+
"schemaFile": "KmsConfigDto.schema.json",
|
|
29
|
+
"required": false,
|
|
30
|
+
"fileMatch": [
|
|
31
|
+
"/assets/config/kms.json",
|
|
32
|
+
"/assets/config/*/kms.json",
|
|
33
|
+
"/apps/backend/test/fixtures/kms.json",
|
|
34
|
+
"/apps/backend/test/fixtures/*/kms.json"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"kind": "directory",
|
|
39
|
+
"subfolder": "clients",
|
|
40
|
+
"resourceType": "client config",
|
|
41
|
+
"schemaFile": "CreateClientDto.schema.json",
|
|
42
|
+
"fileMatch": [
|
|
43
|
+
"/assets/config/*/clients/*.json",
|
|
44
|
+
"/apps/backend/test/fixtures/*/clients/*.json"
|
|
45
|
+
]
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"kind": "directory",
|
|
49
|
+
"subfolder": "key-chains",
|
|
50
|
+
"resourceType": "key-chain",
|
|
51
|
+
"schemaFile": "KeyChainImportDto.schema.json",
|
|
52
|
+
"fileMatch": [
|
|
53
|
+
"/assets/config/*/key-chains/*.json",
|
|
54
|
+
"/apps/backend/test/fixtures/*/key-chains/*.json"
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
"kind": "directory",
|
|
59
|
+
"subfolder": "certs",
|
|
60
|
+
"resourceType": "certificate",
|
|
61
|
+
"schemaFile": "CertImportDto.schema.json",
|
|
62
|
+
"cliValidated": false,
|
|
63
|
+
"fileMatch": [
|
|
64
|
+
"/assets/config/*/certs/*.json",
|
|
65
|
+
"/apps/backend/test/fixtures/*/certs/*.json"
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"kind": "directory",
|
|
70
|
+
"subfolder": "attribute-providers",
|
|
71
|
+
"resourceType": "attribute provider",
|
|
72
|
+
"schemaFile": "CreateAttributeProviderDto.schema.json",
|
|
73
|
+
"fileMatch": [
|
|
74
|
+
"/assets/config/*/attribute-providers/*.json",
|
|
75
|
+
"/apps/backend/test/fixtures/*/attribute-providers/*.json"
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
"kind": "directory",
|
|
80
|
+
"subfolder": "webhook-endpoints",
|
|
81
|
+
"resourceType": "webhook endpoint",
|
|
82
|
+
"schemaFile": "CreateWebhookEndpointDto.schema.json",
|
|
83
|
+
"fileMatch": [
|
|
84
|
+
"/assets/config/*/webhook-endpoints/*.json",
|
|
85
|
+
"/apps/backend/test/fixtures/*/webhook-endpoints/*.json"
|
|
86
|
+
]
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
"kind": "directory",
|
|
90
|
+
"subfolder": "issuance",
|
|
91
|
+
"resourceType": "issuance config",
|
|
92
|
+
"schemaFile": "IssuanceConfig.schema.json",
|
|
93
|
+
"fileMatch": [
|
|
94
|
+
"/assets/config/*/issuance/issuance.json",
|
|
95
|
+
"/apps/backend/test/fixtures/*/issuance/issuance.json"
|
|
96
|
+
]
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"kind": "directory",
|
|
100
|
+
"subfolder": "issuance/credentials",
|
|
101
|
+
"resourceType": "credential config",
|
|
102
|
+
"schemaFile": "CredentialConfigCreate.schema.json",
|
|
103
|
+
"fileMatch": [
|
|
104
|
+
"/assets/config/*/issuance/credentials/*.json",
|
|
105
|
+
"/apps/backend/test/fixtures/*/issuance/credentials/*.json"
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"kind": "directory",
|
|
110
|
+
"subfolder": "issuance/status-lists",
|
|
111
|
+
"resourceType": "status list",
|
|
112
|
+
"schemaFile": "StatusListImportDto.schema.json",
|
|
113
|
+
"fileMatch": [
|
|
114
|
+
"/assets/config/*/issuance/status-lists/*.json",
|
|
115
|
+
"/apps/backend/test/fixtures/*/issuance/status-lists/*.json"
|
|
116
|
+
]
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"kind": "directory",
|
|
120
|
+
"subfolder": "trust-lists",
|
|
121
|
+
"resourceType": "trustlist",
|
|
122
|
+
"schemaFile": "TrustListCreateDto.schema.json",
|
|
123
|
+
"fileMatch": [
|
|
124
|
+
"/assets/config/*/trust-lists/*.json",
|
|
125
|
+
"/apps/backend/test/fixtures/*/trust-lists/*.json"
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"kind": "directory",
|
|
130
|
+
"subfolder": "presentation",
|
|
131
|
+
"resourceType": "presentation config",
|
|
132
|
+
"schemaFile": "PresentationConfigCreateDto.schema.json",
|
|
133
|
+
"fileMatch": [
|
|
134
|
+
"/assets/config/*/presentation/*.json",
|
|
135
|
+
"/apps/backend/test/fixtures/*/presentation/*.json"
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
]
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { TenantValidationResult } from "./types.js";
|
|
2
|
+
export declare function buildJsonReport(results: TenantValidationResult[]): {
|
|
3
|
+
valid: boolean;
|
|
4
|
+
tenants: {
|
|
5
|
+
id: string;
|
|
6
|
+
valid: boolean;
|
|
7
|
+
files: number;
|
|
8
|
+
errors: {
|
|
9
|
+
file: string;
|
|
10
|
+
path: string | undefined;
|
|
11
|
+
message: string;
|
|
12
|
+
}[];
|
|
13
|
+
}[];
|
|
14
|
+
summary: {
|
|
15
|
+
tenants: number;
|
|
16
|
+
files: number;
|
|
17
|
+
errors: number;
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
export declare function formatTextReport(scope: "tenant" | "tenants", rootPath: string, results: TenantValidationResult[]): string;
|
|
21
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../../src/config-validate/report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAmB,MAAM,YAAY,CAAC;AAE1E,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,EAAE;IAEzD,KAAK;IACL,OAAO;;;;;;;;;;IAUP,OAAO;QACH,OAAO;QACP,KAAK;QACL,MAAM;;EAGjB;AAED,wBAAgB,gBAAgB,CAC5B,KAAK,EAAE,QAAQ,GAAG,SAAS,EAC3B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,sBAAsB,EAAE,GAClC,MAAM,CASR"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export function buildJsonReport(results) {
|
|
2
|
+
return {
|
|
3
|
+
valid: results.every((result) => result.valid),
|
|
4
|
+
tenants: results.map((result) => ({
|
|
5
|
+
id: result.id,
|
|
6
|
+
valid: result.valid,
|
|
7
|
+
files: result.files,
|
|
8
|
+
errors: result.errors.map((error) => ({
|
|
9
|
+
file: error.file,
|
|
10
|
+
path: error.path,
|
|
11
|
+
message: error.message,
|
|
12
|
+
})),
|
|
13
|
+
})),
|
|
14
|
+
summary: {
|
|
15
|
+
tenants: results.length,
|
|
16
|
+
files: results.reduce((sum, result) => sum + result.files, 0),
|
|
17
|
+
errors: results.reduce((sum, result) => sum + result.errors.length, 0),
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export function formatTextReport(scope, rootPath, results) {
|
|
22
|
+
const header = scope === "tenants"
|
|
23
|
+
? `Validating tenant configuration root: ${rootPath}`
|
|
24
|
+
: `Validating tenant configuration: ${rootPath}`;
|
|
25
|
+
const lines = [header, "", ...results.flatMap(formatTenantBlock), formatSummaryLine(results)];
|
|
26
|
+
return `${lines.join("\n")}\n`;
|
|
27
|
+
}
|
|
28
|
+
function formatTenantBlock(result) {
|
|
29
|
+
const lines = [`${result.valid ? "PASS" : "FAIL"} ${result.id}`];
|
|
30
|
+
if (result.valid) {
|
|
31
|
+
for (const [resourceType, count] of Object.entries(result.resourceCounts)) {
|
|
32
|
+
lines.push(` ${count} ${pluralize(resourceType, count)}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
lines.push(...formatTenantErrors(result.errors));
|
|
37
|
+
}
|
|
38
|
+
lines.push("");
|
|
39
|
+
return lines;
|
|
40
|
+
}
|
|
41
|
+
function formatTenantErrors(errors) {
|
|
42
|
+
const lines = [];
|
|
43
|
+
for (const [file, issues] of groupErrorsByFile(errors)) {
|
|
44
|
+
lines.push(` ${file}`);
|
|
45
|
+
for (const issue of issues) {
|
|
46
|
+
lines.push(` ${formatIssueMessage(issue)}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return lines;
|
|
50
|
+
}
|
|
51
|
+
function formatIssueMessage(issue) {
|
|
52
|
+
return issue.path ? `${issue.path}: ${issue.message}` : issue.message;
|
|
53
|
+
}
|
|
54
|
+
function formatSummaryLine(results) {
|
|
55
|
+
const totalFiles = results.reduce((sum, result) => sum + result.files, 0);
|
|
56
|
+
const totalErrors = results.reduce((sum, result) => sum + result.errors.length, 0);
|
|
57
|
+
if (totalErrors === 0) {
|
|
58
|
+
return `Validated ${results.length} tenant(s) and ${totalFiles} configuration file(s).\nNo errors found.`;
|
|
59
|
+
}
|
|
60
|
+
const failedTenants = results.filter((result) => !result.valid).length;
|
|
61
|
+
return `Validation failed: ${totalErrors} error(s) in ${failedTenants} tenant(s).`;
|
|
62
|
+
}
|
|
63
|
+
function groupErrorsByFile(errors) {
|
|
64
|
+
const grouped = new Map();
|
|
65
|
+
for (const error of errors) {
|
|
66
|
+
const existing = grouped.get(error.file);
|
|
67
|
+
if (existing) {
|
|
68
|
+
existing.push(error);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
grouped.set(error.file, [error]);
|
|
72
|
+
}
|
|
73
|
+
return Array.from(grouped.entries());
|
|
74
|
+
}
|
|
75
|
+
function pluralize(label, count) {
|
|
76
|
+
return count === 1 ? label : `${label}s`;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../../src/config-validate/report.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/config-validate/schemas.ts"],"names":[],"mappings":"AAKA,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAmB7F"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { readCliTextAsset } from "../sea-assets.js";
|
|
2
|
+
import { requiredSchemaFiles } from "./registry.js";
|
|
3
|
+
const manifestAssetKey = "templates/schemas.manifest.json";
|
|
4
|
+
export async function loadTenantConfigSchemas() {
|
|
5
|
+
const manifest = await readSchemaManifest();
|
|
6
|
+
const schemas = new Map();
|
|
7
|
+
for (const schemaFile of requiredSchemaFiles()) {
|
|
8
|
+
if (!manifest.includes(schemaFile)) {
|
|
9
|
+
throw new Error(`Bundled schema manifest is missing "${schemaFile}". Run "pnpm --filter @eudiplo/cli assets:sync".`);
|
|
10
|
+
}
|
|
11
|
+
const text = await readCliTextAsset(`templates/schemas/${schemaFile}`, new URL(`../../templates/schemas/${schemaFile}`, import.meta.url));
|
|
12
|
+
schemas.set(schemaFile, JSON.parse(text));
|
|
13
|
+
}
|
|
14
|
+
return schemas;
|
|
15
|
+
}
|
|
16
|
+
async function readSchemaManifest() {
|
|
17
|
+
const text = await readCliTextAsset(manifestAssetKey, new URL("../../templates/schemas.manifest.json", import.meta.url));
|
|
18
|
+
const parsed = JSON.parse(text);
|
|
19
|
+
if (!Array.isArray(parsed) || !parsed.every((item) => typeof item === "string")) {
|
|
20
|
+
throw new Error("Bundled schema manifest is malformed.");
|
|
21
|
+
}
|
|
22
|
+
return parsed;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../../src/config-validate/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpD,MAAM,gBAAgB,GAAG,iCAAiC,CAAC;AAE3D,MAAM,CAAC,KAAK,UAAU,uBAAuB;IACzC,MAAM,QAAQ,GAAG,MAAM,kBAAkB,EAAE,CAAC;IAC5C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAmC,CAAC;IAE3D,KAAK,MAAM,UAAU,IAAI,mBAAmB,EAAE,EAAE,CAAC;QAC7C,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CACX,uCAAuC,UAAU,kDAAkD,CACtG,CAAC;QACN,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAC/B,qBAAqB,UAAU,EAAE,EACjC,IAAI,GAAG,CAAC,2BAA2B,UAAU,EAAE,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CACpE,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,KAAK,UAAU,kBAAkB;IAC7B,MAAM,IAAI,GAAG,MAAM,gBAAgB,CAC/B,gBAAgB,EAChB,IAAI,GAAG,CAAC,uCAAuC,EAAE,OAAO,IAAI,CAAC,GAAG,CAAC,CACpE,CAAC;IACF,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,EAAE,CAAC;QAC9E,MAAM,IAAI,KAAK,CAAC,uCAAuC,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,MAAM,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export type FixedFileResourceDefinition = {
|
|
2
|
+
kind: "file";
|
|
3
|
+
file: string;
|
|
4
|
+
resourceType: string;
|
|
5
|
+
schemaFile: string;
|
|
6
|
+
required: boolean;
|
|
7
|
+
/** Editor (`.vscode/settings.json`) `json.schemas` fileMatch globs generated for this entry. */
|
|
8
|
+
fileMatch: string[];
|
|
9
|
+
/** Set to false for schemas that aren't scanned by the CLI validator (editor authoring only). */
|
|
10
|
+
cliValidated?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type DirectoryResourceDefinition = {
|
|
13
|
+
kind: "directory";
|
|
14
|
+
subfolder: string;
|
|
15
|
+
resourceType: string;
|
|
16
|
+
schemaFile: string;
|
|
17
|
+
fileMatch: string[];
|
|
18
|
+
cliValidated?: boolean;
|
|
19
|
+
};
|
|
20
|
+
export type TenantResourceDefinition = FixedFileResourceDefinition | DirectoryResourceDefinition;
|
|
21
|
+
export interface ValidationIssue {
|
|
22
|
+
file: string;
|
|
23
|
+
path?: string;
|
|
24
|
+
message: string;
|
|
25
|
+
}
|
|
26
|
+
export interface TenantValidationResult {
|
|
27
|
+
id: string;
|
|
28
|
+
valid: boolean;
|
|
29
|
+
files: number;
|
|
30
|
+
errors: ValidationIssue[];
|
|
31
|
+
resourceCounts: Record<string, number>;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/config-validate/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,2BAA2B,GAAG;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;IAClB,gGAAgG;IAChG,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,iGAAiG;IACjG,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACtC,IAAI,EAAE,WAAW,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,2BAA2B,GAAG,2BAA2B,CAAC;AAEjG,MAAM,WAAW,eAAe;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,sBAAsB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC1C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/config-validate/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { TenantValidationResult } from "./types.js";
|
|
2
|
+
export declare function validateTenantsRoot(rootPath: string, schemas: Map<string, Record<string, unknown>>, env: NodeJS.ProcessEnv): Promise<TenantValidationResult[]>;
|
|
3
|
+
export declare function validateTenantDirectory(tenantPath: string, tenantId: string, schemas: Map<string, Record<string, unknown>>, env: NodeJS.ProcessEnv): Promise<TenantValidationResult>;
|
|
4
|
+
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/config-validate/validator.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAGR,sBAAsB,EAEzB,MAAM,YAAY,CAAC;AAYpB,wBAAsB,mBAAmB,CACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC7C,GAAG,EAAE,MAAM,CAAC,UAAU,GACvB,OAAO,CAAC,sBAAsB,EAAE,CAAC,CAanC;AAED,wBAAsB,uBAAuB,CACzC,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EAC7C,GAAG,EAAE,MAAM,CAAC,UAAU,GACvB,OAAO,CAAC,sBAAsB,CAAC,CA4BjC"}
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import Ajv2020 from "ajv/dist/2020";
|
|
5
|
+
import addFormatsModule from "ajv-formats";
|
|
6
|
+
import { CLI_VALIDATED_REGISTRY } from "./registry.js";
|
|
7
|
+
const PLACEHOLDER_PATTERN = /\$\{([A-Z0-9_]+)(?::([^}]*))?\}/g;
|
|
8
|
+
async function discoverTenantDirectories(rootPath) {
|
|
9
|
+
const entries = await readdir(rootPath, { withFileTypes: true });
|
|
10
|
+
return entries
|
|
11
|
+
.filter((entry) => entry.isDirectory())
|
|
12
|
+
.map((entry) => entry.name)
|
|
13
|
+
.sort((left, right) => left.localeCompare(right));
|
|
14
|
+
}
|
|
15
|
+
export async function validateTenantsRoot(rootPath, schemas, env) {
|
|
16
|
+
if (!existsSync(rootPath)) {
|
|
17
|
+
throw new Error(`Configuration root not found: ${rootPath}`);
|
|
18
|
+
}
|
|
19
|
+
const tenantIds = await discoverTenantDirectories(rootPath);
|
|
20
|
+
const results = [];
|
|
21
|
+
for (const tenantId of tenantIds) {
|
|
22
|
+
results.push(await validateTenantDirectory(join(rootPath, tenantId), tenantId, schemas, env));
|
|
23
|
+
}
|
|
24
|
+
return results;
|
|
25
|
+
}
|
|
26
|
+
export async function validateTenantDirectory(tenantPath, tenantId, schemas, env) {
|
|
27
|
+
if (!existsSync(tenantPath)) {
|
|
28
|
+
throw new Error(`Tenant directory not found: ${tenantPath}`);
|
|
29
|
+
}
|
|
30
|
+
const state = {
|
|
31
|
+
getValidator: createValidatorFactory(schemas),
|
|
32
|
+
env,
|
|
33
|
+
errors: [],
|
|
34
|
+
resourceCounts: {},
|
|
35
|
+
files: 0,
|
|
36
|
+
};
|
|
37
|
+
for (const entry of CLI_VALIDATED_REGISTRY) {
|
|
38
|
+
if (entry.kind === "file") {
|
|
39
|
+
await validateFixedFileEntry(tenantPath, entry, state);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
await validateDirectoryEntry(tenantPath, entry, state);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
id: tenantId,
|
|
47
|
+
valid: state.errors.length === 0,
|
|
48
|
+
files: state.files,
|
|
49
|
+
errors: state.errors,
|
|
50
|
+
resourceCounts: state.resourceCounts,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function createValidatorFactory(schemas) {
|
|
54
|
+
const ajv = new Ajv2020({ allErrors: true, strict: false });
|
|
55
|
+
addFormatsModule.default(ajv);
|
|
56
|
+
const validators = new Map();
|
|
57
|
+
return (schemaFile) => {
|
|
58
|
+
const cached = validators.get(schemaFile);
|
|
59
|
+
if (cached) {
|
|
60
|
+
return cached;
|
|
61
|
+
}
|
|
62
|
+
const schema = schemas.get(schemaFile);
|
|
63
|
+
if (!schema) {
|
|
64
|
+
throw new Error(`No bundled schema found for "${schemaFile}".`);
|
|
65
|
+
}
|
|
66
|
+
const validator = ajv.compile(schema);
|
|
67
|
+
validators.set(schemaFile, validator);
|
|
68
|
+
return validator;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
async function validateFixedFileEntry(tenantPath, entry, state) {
|
|
72
|
+
const filePath = join(tenantPath, entry.file);
|
|
73
|
+
if (!existsSync(filePath)) {
|
|
74
|
+
if (entry.required) {
|
|
75
|
+
state.errors.push({
|
|
76
|
+
file: entry.file,
|
|
77
|
+
message: `Missing required ${entry.resourceType} file`,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
state.files += 1;
|
|
83
|
+
await recordValidationOutcome(filePath, entry.file, entry.resourceType, entry.schemaFile, state);
|
|
84
|
+
}
|
|
85
|
+
async function validateDirectoryEntry(tenantPath, entry, state) {
|
|
86
|
+
const directoryPath = join(tenantPath, entry.subfolder);
|
|
87
|
+
if (!existsSync(directoryPath)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const dirEntries = (await readdir(directoryPath, { withFileTypes: true }))
|
|
91
|
+
.filter((dirEntry) => dirEntry.isFile() && dirEntry.name.endsWith(".json"))
|
|
92
|
+
.sort((left, right) => left.name.localeCompare(right.name));
|
|
93
|
+
for (const dirEntry of dirEntries) {
|
|
94
|
+
const relativeFile = `${entry.subfolder}/${dirEntry.name}`;
|
|
95
|
+
const filePath = join(directoryPath, dirEntry.name);
|
|
96
|
+
state.files += 1;
|
|
97
|
+
await recordValidationOutcome(filePath, relativeFile, entry.resourceType, entry.schemaFile, state);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
async function recordValidationOutcome(filePath, relativeFile, resourceType, schemaFile, state) {
|
|
101
|
+
const isValid = await validateResourceFile(filePath, relativeFile, schemaFile, state.getValidator, state.env, state.errors);
|
|
102
|
+
if (isValid) {
|
|
103
|
+
state.resourceCounts[resourceType] = (state.resourceCounts[resourceType] ?? 0) + 1;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async function validateResourceFile(filePath, relativeFile, schemaFile, getValidator, env, errors) {
|
|
107
|
+
let raw;
|
|
108
|
+
try {
|
|
109
|
+
raw = await readFile(filePath, "utf8");
|
|
110
|
+
}
|
|
111
|
+
catch (error) {
|
|
112
|
+
errors.push({
|
|
113
|
+
file: relativeFile,
|
|
114
|
+
message: `Failed to read file: ${error.message}`,
|
|
115
|
+
});
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
let payload;
|
|
119
|
+
try {
|
|
120
|
+
payload = JSON.parse(raw);
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
errors.push({ file: relativeFile, message: `Invalid JSON: ${error.message}` });
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
const errorsBefore = errors.length;
|
|
127
|
+
const resolved = resolvePlaceholders(payload, env, relativeFile, errors);
|
|
128
|
+
if (errors.length > errorsBefore) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
const validate = getValidator(schemaFile);
|
|
132
|
+
if (!validate(resolved)) {
|
|
133
|
+
for (const issue of validate.errors ?? []) {
|
|
134
|
+
errors.push({
|
|
135
|
+
file: relativeFile,
|
|
136
|
+
path: formatErrorPath(issue),
|
|
137
|
+
message: issue.message ?? "Invalid value",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
function formatErrorPath(issue) {
|
|
145
|
+
const path = issue.instancePath.replace(/^\//, "").replaceAll("/", ".");
|
|
146
|
+
if (path) {
|
|
147
|
+
return path;
|
|
148
|
+
}
|
|
149
|
+
const missingProperty = issue.params
|
|
150
|
+
?.missingProperty;
|
|
151
|
+
return missingProperty;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Resolves ${VAR} / ${VAR:default} placeholders for schema validation without ever
|
|
155
|
+
* emitting resolved secret values in error output; unresolved required placeholders
|
|
156
|
+
* are reported by reference to their variable name only.
|
|
157
|
+
*/
|
|
158
|
+
function resolvePlaceholders(value, env, file, errors) {
|
|
159
|
+
if (typeof value === "string") {
|
|
160
|
+
return value.replace(PLACEHOLDER_PATTERN, (match, varName, defaultValue) => {
|
|
161
|
+
const envValue = env[varName];
|
|
162
|
+
if (envValue !== undefined && envValue !== "") {
|
|
163
|
+
return envValue;
|
|
164
|
+
}
|
|
165
|
+
if (defaultValue !== undefined) {
|
|
166
|
+
return defaultValue;
|
|
167
|
+
}
|
|
168
|
+
errors.push({
|
|
169
|
+
file,
|
|
170
|
+
message: `Unresolved placeholder \${${varName}}: no environment value or default is available`,
|
|
171
|
+
});
|
|
172
|
+
return match;
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
if (Array.isArray(value)) {
|
|
176
|
+
return value.map((item) => resolvePlaceholders(item, env, file, errors));
|
|
177
|
+
}
|
|
178
|
+
if (value && typeof value === "object") {
|
|
179
|
+
const result = {};
|
|
180
|
+
for (const [key, val] of Object.entries(value)) {
|
|
181
|
+
result[key] = resolvePlaceholders(val, env, file, errors);
|
|
182
|
+
}
|
|
183
|
+
return result;
|
|
184
|
+
}
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
//# sourceMappingURL=validator.js.map
|