@amritk/mjst 0.12.2 → 0.13.0
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 +28 -1
- package/dist/cli.js +289 -362
- package/dist/combine-files.js +24 -35
- package/dist/compile-api/collect-route-contracts.d.ts +9 -0
- package/dist/compile-api/collect-route-contracts.js +15 -0
- package/dist/compile-api/default-routes-import.d.ts +9 -0
- package/dist/compile-api/default-routes-import.js +8 -0
- package/dist/compile-api/help-text.d.ts +6 -0
- package/dist/compile-api/help-text.js +27 -0
- package/dist/compile-api/load-routes-module.d.ts +8 -0
- package/dist/compile-api/load-routes-module.js +13 -0
- package/dist/compile-api/parse-compile-api-args.d.ts +25 -0
- package/dist/compile-api/parse-compile-api-args.js +73 -0
- package/dist/compile-api/read-compile-options.d.ts +9 -0
- package/dist/compile-api/read-compile-options.js +24 -0
- package/dist/compile-api/run.d.ts +9 -0
- package/dist/compile-api/run.js +75 -0
- package/dist/detect-helpers-mode.js +28 -41
- package/dist/emit-examples.js +18 -28
- package/dist/has-external-refs.js +16 -21
- package/dist/help-text.d.ts +1 -1
- package/dist/help-text.js +8 -7
- package/dist/lint/resolver.js +79 -112
- package/dist/lint/ruleset-loader.js +30 -28
- package/dist/lint/run.js +153 -194
- package/dist/load-config.js +44 -45
- package/dist/load-schema-module.js +28 -43
- package/dist/load-schema.js +31 -47
- package/dist/parse-cli-args.js +190 -229
- package/dist/read-version.js +8 -12
- package/dist/resolve-import-ext.js +4 -9
- package/package.json +12 -11
package/dist/load-config.js
CHANGED
|
@@ -1,46 +1,45 @@
|
|
|
1
|
-
import { readFile } from
|
|
2
|
-
import { resolve } from
|
|
3
|
-
const SOURCE_FORMATS = [
|
|
4
|
-
const isSourceFormat = (value) => typeof value ===
|
|
5
|
-
const isHelpersMode = (value) => value ===
|
|
6
|
-
const isImportExt = (value) => value ===
|
|
7
|
-
const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry ===
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
};
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
const SOURCE_FORMATS = ["json", "typebox", "zod", "valibot", "effect"];
|
|
4
|
+
const isSourceFormat = (value) => typeof value === "string" && SOURCE_FORMATS.includes(value);
|
|
5
|
+
const isHelpersMode = (value) => value === "package" || value === "embedded";
|
|
6
|
+
const isImportExt = (value) => value === "js" || value === "ts";
|
|
7
|
+
const isStringArray = (value) => Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
8
|
+
const loadConfig = async (configPath) => {
|
|
9
|
+
const absolutePath = resolve(configPath);
|
|
10
|
+
const raw = await readFile(absolutePath, "utf-8");
|
|
11
|
+
const parsed = JSON.parse(raw);
|
|
12
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
13
|
+
throw new Error(`Config file must be a JSON object: ${configPath}`);
|
|
14
|
+
}
|
|
15
|
+
const obj = parsed;
|
|
16
|
+
return {
|
|
17
|
+
...typeof obj["schema"] === "string" && { schema: obj["schema"] },
|
|
18
|
+
...typeof obj["schemaDir"] === "string" && { schemaDir: obj["schemaDir"] },
|
|
19
|
+
...typeof obj["outDir"] === "string" && { outDir: obj["outDir"] },
|
|
20
|
+
...typeof obj["outFile"] === "string" && { outFile: obj["outFile"] },
|
|
21
|
+
...isSourceFormat(obj["input"]) && { input: obj["input"] },
|
|
22
|
+
...typeof obj["export"] === "string" && { export: obj["export"] },
|
|
23
|
+
...typeof obj["validators"] === "boolean" && { validators: obj["validators"] },
|
|
24
|
+
...typeof obj["typesOnly"] === "boolean" && { typesOnly: obj["typesOnly"] },
|
|
25
|
+
...typeof obj["examples"] === "boolean" && { examples: obj["examples"] },
|
|
26
|
+
...typeof obj["build"] === "boolean" && { build: obj["build"] },
|
|
27
|
+
...typeof obj["logWarnings"] === "boolean" && { logWarnings: obj["logWarnings"] },
|
|
28
|
+
...typeof obj["strict"] === "boolean" && { strict: obj["strict"] },
|
|
29
|
+
...typeof obj["stripUnknown"] === "boolean" && { stripUnknown: obj["stripUnknown"] },
|
|
30
|
+
...typeof obj["caseInsensitive"] === "boolean" && { caseInsensitive: obj["caseInsensitive"] },
|
|
31
|
+
...typeof obj["readonly"] === "boolean" && { readonly: obj["readonly"] },
|
|
32
|
+
...isHelpersMode(obj["helpers"]) && { helpers: obj["helpers"] },
|
|
33
|
+
...isImportExt(obj["importExt"]) && { importExt: obj["importExt"] },
|
|
34
|
+
...typeof obj["rootType"] === "string" && { rootType: obj["rootType"] },
|
|
35
|
+
...typeof obj["typeSuffix"] === "string" && { typeSuffix: obj["typeSuffix"] },
|
|
36
|
+
// `banner` is a boolean toggle or a custom header string, mirroring the CLI flag.
|
|
37
|
+
...(typeof obj["banner"] === "boolean" || typeof obj["banner"] === "string") && { banner: obj["banner"] },
|
|
38
|
+
...typeof obj["resolveRemote"] === "boolean" && { resolveRemote: obj["resolveRemote"] },
|
|
39
|
+
...isStringArray(obj["allowedHosts"]) && { allowedHosts: obj["allowedHosts"] },
|
|
40
|
+
...typeof obj["allowPrivateHosts"] === "boolean" && { allowPrivateHosts: obj["allowPrivateHosts"] }
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export {
|
|
44
|
+
loadConfig
|
|
46
45
|
};
|
|
@@ -1,45 +1,30 @@
|
|
|
1
|
-
import { pathToFileURL } from
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* with a loader such as `tsx`). We surface a friendly hint when the import fails
|
|
15
|
-
* for a `.ts` path so the cause is not a cryptic module-resolution error.
|
|
16
|
-
*/
|
|
17
|
-
export const loadSchemaModule = async (modulePath, exportName) => {
|
|
18
|
-
let mod;
|
|
19
|
-
try {
|
|
20
|
-
mod = (await import(pathToFileURL(modulePath).href));
|
|
1
|
+
import { pathToFileURL } from "node:url";
|
|
2
|
+
const loadSchemaModule = async (modulePath, exportName) => {
|
|
3
|
+
let mod;
|
|
4
|
+
try {
|
|
5
|
+
mod = await import(pathToFileURL(modulePath).href);
|
|
6
|
+
} catch (error) {
|
|
7
|
+
const hint = modulePath.endsWith(".ts") ? " Importing a .ts schema requires a TypeScript-capable runtime \u2014 run mjst via `bunx`, or install `tsx` and run Node with `--import tsx`." : "";
|
|
8
|
+
throw new Error(`Failed to load schema module at ${modulePath}.${hint}
|
|
9
|
+
${String(error)}`);
|
|
10
|
+
}
|
|
11
|
+
if (exportName) {
|
|
12
|
+
if (!(exportName in mod)) {
|
|
13
|
+
throw new Error(`Schema module ${modulePath} has no export named '${exportName}'.`);
|
|
21
14
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const namedKeys = Object.keys(mod).filter((key) => key !== 'default');
|
|
38
|
-
if (namedKeys.length === 1) {
|
|
39
|
-
const key = namedKeys[0];
|
|
40
|
-
if (key)
|
|
41
|
-
return mod[key];
|
|
42
|
-
}
|
|
43
|
-
throw new Error(`Schema module ${modulePath} exports ${namedKeys.length} values (${namedKeys.join(', ') || 'none'}). ` +
|
|
44
|
-
`Specify which one to use with --export <name>.`);
|
|
15
|
+
return mod[exportName];
|
|
16
|
+
}
|
|
17
|
+
if ("default" in mod && mod["default"] !== void 0) {
|
|
18
|
+
return mod["default"];
|
|
19
|
+
}
|
|
20
|
+
const namedKeys = Object.keys(mod).filter((key) => key !== "default");
|
|
21
|
+
if (namedKeys.length === 1) {
|
|
22
|
+
const key = namedKeys[0];
|
|
23
|
+
if (key)
|
|
24
|
+
return mod[key];
|
|
25
|
+
}
|
|
26
|
+
throw new Error(`Schema module ${modulePath} exports ${namedKeys.length} values (${namedKeys.join(", ") || "none"}). Specify which one to use with --export <name>.`);
|
|
27
|
+
};
|
|
28
|
+
export {
|
|
29
|
+
loadSchemaModule
|
|
45
30
|
};
|
package/dist/load-schema.js
CHANGED
|
@@ -1,52 +1,36 @@
|
|
|
1
|
-
import { readFile } from
|
|
2
|
-
import { getAdapter } from
|
|
3
|
-
import { resolveRefsFromFile } from
|
|
4
|
-
import { hasExternalRefs } from
|
|
5
|
-
import { loadSchemaModule } from
|
|
6
|
-
/**
|
|
7
|
-
* Builds the `@amritk/resolve-refs` options from the CLI config, mirroring the
|
|
8
|
-
* safety posture of `mjst lint`: remote (`http(s)`) `$ref`s are refused unless
|
|
9
|
-
* opted into, a non-empty `allowedHosts` implies remote fetching, and private/
|
|
10
|
-
* loopback hosts stay blocked as an SSRF guard unless explicitly permitted.
|
|
11
|
-
*/
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { getAdapter } from "@amritk/adapters/get-adapter";
|
|
3
|
+
import { resolveRefsFromFile } from "@amritk/resolve-refs";
|
|
4
|
+
import { hasExternalRefs } from "./has-external-refs.js";
|
|
5
|
+
import { loadSchemaModule } from "./load-schema-module.js";
|
|
12
6
|
const buildResolveOptions = (config) => {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
};
|
|
7
|
+
const remote = (config.resolveRemote ?? false) || (config.allowedHosts?.length ?? 0) > 0;
|
|
8
|
+
return {
|
|
9
|
+
remote,
|
|
10
|
+
...config.allowedHosts ? { allowedHosts: [...config.allowedHosts] } : {},
|
|
11
|
+
...config.allowPrivateHosts ? { allowPrivateHosts: config.allowPrivateHosts } : {}
|
|
12
|
+
};
|
|
20
13
|
};
|
|
21
|
-
/** Renders resolver failures into a single CLI error message. */
|
|
22
14
|
const formatResolveErrors = (schemaPath, errors) => {
|
|
23
|
-
|
|
24
|
-
|
|
15
|
+
const details = errors.map((error) => ` - ${error.message}`).join("\n");
|
|
16
|
+
return `Failed to resolve $refs in ${schemaPath}:
|
|
17
|
+
${details}`;
|
|
25
18
|
};
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return data;
|
|
44
|
-
const { resolved, errors } = await resolveRefsFromFile(schemaPath, buildResolveOptions(config));
|
|
45
|
-
if (errors.length > 0)
|
|
46
|
-
throw new Error(formatResolveErrors(schemaPath, errors));
|
|
47
|
-
return resolved;
|
|
48
|
-
}
|
|
49
|
-
console.log(`Input format: ${inputFormat}`);
|
|
50
|
-
const source = await loadSchemaModule(schemaPath, config.export);
|
|
51
|
-
return getAdapter(inputFormat).toJSONSchema(source);
|
|
19
|
+
const loadSchema = async (config, schemaPath) => {
|
|
20
|
+
const inputFormat = config.input ?? "json";
|
|
21
|
+
if (inputFormat === "json") {
|
|
22
|
+
const data = JSON.parse(await readFile(schemaPath, "utf-8"));
|
|
23
|
+
if (!hasExternalRefs(data))
|
|
24
|
+
return data;
|
|
25
|
+
const { resolved, errors } = await resolveRefsFromFile(schemaPath, buildResolveOptions(config));
|
|
26
|
+
if (errors.length > 0)
|
|
27
|
+
throw new Error(formatResolveErrors(schemaPath, errors));
|
|
28
|
+
return resolved;
|
|
29
|
+
}
|
|
30
|
+
console.log(`Input format: ${inputFormat}`);
|
|
31
|
+
const source = await loadSchemaModule(schemaPath, config.export);
|
|
32
|
+
return getAdapter(inputFormat).toJSONSchema(source);
|
|
33
|
+
};
|
|
34
|
+
export {
|
|
35
|
+
loadSchema
|
|
52
36
|
};
|