@takuhon/cli 0.6.1 → 0.7.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/dist/index.js CHANGED
@@ -1,8 +1,79 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
+ import { readFileSync as readFileSync2 } from "fs";
5
+
6
+ // src/validate-command.ts
4
7
  import { readFileSync } from "fs";
5
- var pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
8
+ import { validate } from "@takuhon/core";
9
+ var DEFAULT_PATH = "takuhon.json";
10
+ var USAGE = `Usage: takuhon validate [path]
11
+
12
+ Validate a takuhon.json against the takuhon schema. With no path, validates
13
+ ./takuhon.json in the current working directory.
14
+
15
+ Exit codes: 0 = valid, 1 = invalid, 2 = file missing / unreadable / not JSON.
16
+ `;
17
+ function runValidate(args = []) {
18
+ if (args[0] === "--help" || args[0] === "-h") {
19
+ return { code: 0, stdout: USAGE, stderr: "" };
20
+ }
21
+ if (args.length > 1) {
22
+ return {
23
+ code: 2,
24
+ stdout: "",
25
+ stderr: "takuhon: `validate` takes at most one path argument.\nRun `takuhon validate --help` for usage.\n"
26
+ };
27
+ }
28
+ return validateFile(args[0]);
29
+ }
30
+ function validateFile(pathArg) {
31
+ const target = pathArg ?? DEFAULT_PATH;
32
+ let raw;
33
+ try {
34
+ raw = readFileSync(target, "utf8");
35
+ } catch {
36
+ return {
37
+ code: 2,
38
+ stdout: "",
39
+ stderr: `takuhon: cannot read '${target}'. Pass a path, or run from a directory containing a takuhon.json.
40
+ `
41
+ };
42
+ }
43
+ let data;
44
+ try {
45
+ data = JSON.parse(raw);
46
+ } catch (error) {
47
+ const detail = error instanceof Error ? error.message : String(error);
48
+ return {
49
+ code: 2,
50
+ stdout: "",
51
+ stderr: `takuhon: '${target}' is not valid JSON: ${detail}
52
+ `
53
+ };
54
+ }
55
+ const result = validate(data);
56
+ if (result.ok) {
57
+ return {
58
+ code: 0,
59
+ stdout: `${target}: valid (schemaVersion ${result.data.schemaVersion}).
60
+ `,
61
+ stderr: ""
62
+ };
63
+ }
64
+ const count = result.errors.length;
65
+ const lines = result.errors.map((error) => ` ${error.pointer || "/"}: ${error.message}`);
66
+ return {
67
+ code: 1,
68
+ stdout: "",
69
+ stderr: `${target}: invalid (${count} error${count === 1 ? "" : "s"}):
70
+ ${lines.join("\n")}
71
+ `
72
+ };
73
+ }
74
+
75
+ // src/index.ts
76
+ var pkg = JSON.parse(readFileSync2(new URL("../package.json", import.meta.url), "utf8"));
6
77
  var VERSION = pkg.version;
7
78
  var HELP = `takuhon ${VERSION}
8
79
 
@@ -12,11 +83,14 @@ Usage:
12
83
  takuhon --version Show the installed CLI version
13
84
  takuhon --help Show this help
14
85
 
86
+ Commands:
87
+ takuhon validate [path] Validate a takuhon.json (default: ./takuhon.json)
88
+
15
89
  Scaffolding a new profile project:
16
90
  npx create-takuhon my-profile
17
91
  npx create-takuhon my-profile --license CC-BY-4.0
18
92
 
19
- Subcommands (dev / validate / sync / export / migrate / restore) are planned
93
+ Subcommands (dev / sync / export / migrate / restore) are planned
20
94
  for a future release. Track progress at:
21
95
 
22
96
  https://github.com/takuhon-dev/takuhon
@@ -32,6 +106,12 @@ function main(argv) {
32
106
  process.stdout.write(HELP);
33
107
  return 0;
34
108
  }
109
+ if (first === "validate") {
110
+ const { code, stdout, stderr } = runValidate(argv.slice(1));
111
+ if (stdout) process.stdout.write(stdout);
112
+ if (stderr) process.stderr.write(stderr);
113
+ return code;
114
+ }
35
115
  process.stderr.write(
36
116
  `takuhon: unknown command '${first}'
37
117
  Run \`takuhon --help\` for usage. For scaffolding a new project, use \`create-takuhon\`.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * `@takuhon/cli` entry point — the `takuhon` command.\n *\n * At present this exposes only `--version` / `--help` plus a pointer to\n * `create-takuhon` for scaffolding. The dev / validate / sync / export /\n * migrate / restore subcommands land in subsequent releases. The bare-name\n * `takuhon` npm package (`packages/takuhon/`) redirects here via a 4-line\n * shim, so `npm i -g takuhon && takuhon --help` and `npm i -g @takuhon/cli\n * && takuhon --help` give the same output.\n */\n\nimport { readFileSync } from 'node:fs';\n\n// Source the reported version from package.json (read at runtime relative to\n// this module) so `takuhon --version` can never drift from the published\n// release — there is no hand-maintained version literal to fall out of sync.\nconst pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as {\n version: string;\n};\nconst VERSION = pkg.version;\n\nconst HELP = `takuhon ${VERSION}\n\nTakuhon — open-source portable profile API server.\n\nUsage:\n takuhon --version Show the installed CLI version\n takuhon --help Show this help\n\nScaffolding a new profile project:\n npx create-takuhon my-profile\n npx create-takuhon my-profile --license CC-BY-4.0\n\nSubcommands (dev / validate / sync / export / migrate / restore) are planned\nfor a future release. Track progress at:\n\n https://github.com/takuhon-dev/takuhon\n`;\n\nfunction main(argv: readonly string[]): number {\n const first = argv[0];\n\n if (first === '--version' || first === '-v') {\n process.stdout.write(`${VERSION}\\n`);\n return 0;\n }\n\n if (first === undefined || first === '--help' || first === '-h') {\n process.stdout.write(HELP);\n return 0;\n }\n\n process.stderr.write(\n `takuhon: unknown command '${first}'\\n` +\n `Run \\`takuhon --help\\` for usage. For scaffolding a new project, use \\`create-takuhon\\`.\\n`,\n );\n return 2;\n}\n\nprocess.exit(main(process.argv.slice(2)));\n"],"mappings":";;;AAaA,SAAS,oBAAoB;AAK7B,IAAM,MAAM,KAAK,MAAM,aAAa,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAAG,MAAM,CAAC;AAGxF,IAAM,UAAU,IAAI;AAEpB,IAAM,OAAO,WAAW,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkB/B,SAAS,KAAK,MAAiC;AAC7C,QAAM,QAAQ,KAAK,CAAC;AAEpB,MAAI,UAAU,eAAe,UAAU,MAAM;AAC3C,YAAQ,OAAO,MAAM,GAAG,OAAO;AAAA,CAAI;AACnC,WAAO;AAAA,EACT;AAEA,MAAI,UAAU,UAAa,UAAU,YAAY,UAAU,MAAM;AAC/D,YAAQ,OAAO,MAAM,IAAI;AACzB,WAAO;AAAA,EACT;AAEA,UAAQ,OAAO;AAAA,IACb,6BAA6B,KAAK;AAAA;AAAA;AAAA,EAEpC;AACA,SAAO;AACT;AAEA,QAAQ,KAAK,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC;","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/validate-command.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * `@takuhon/cli` entry point — the `takuhon` command.\n *\n * At present this exposes `--version` / `--help`, the `validate` command, and\n * a pointer to `create-takuhon` for scaffolding. The dev / sync / export /\n * migrate / restore subcommands land in subsequent releases. The bare-name\n * `takuhon` npm package (`packages/takuhon/`) redirects here via a 4-line\n * shim, so `npm i -g takuhon && takuhon --help` and `npm i -g @takuhon/cli\n * && takuhon --help` give the same output.\n */\n\nimport { readFileSync } from 'node:fs';\n\nimport { runValidate } from './validate-command.js';\n\n// Source the reported version from package.json (read at runtime relative to\n// this module) so `takuhon --version` can never drift from the published\n// release — there is no hand-maintained version literal to fall out of sync.\nconst pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8')) as {\n version: string;\n};\nconst VERSION = pkg.version;\n\nconst HELP = `takuhon ${VERSION}\n\nTakuhon — open-source portable profile API server.\n\nUsage:\n takuhon --version Show the installed CLI version\n takuhon --help Show this help\n\nCommands:\n takuhon validate [path] Validate a takuhon.json (default: ./takuhon.json)\n\nScaffolding a new profile project:\n npx create-takuhon my-profile\n npx create-takuhon my-profile --license CC-BY-4.0\n\nSubcommands (dev / sync / export / migrate / restore) are planned\nfor a future release. Track progress at:\n\n https://github.com/takuhon-dev/takuhon\n`;\n\nfunction main(argv: readonly string[]): number {\n const first = argv[0];\n\n if (first === '--version' || first === '-v') {\n process.stdout.write(`${VERSION}\\n`);\n return 0;\n }\n\n if (first === undefined || first === '--help' || first === '-h') {\n process.stdout.write(HELP);\n return 0;\n }\n\n if (first === 'validate') {\n const { code, stdout, stderr } = runValidate(argv.slice(1));\n if (stdout) process.stdout.write(stdout);\n if (stderr) process.stderr.write(stderr);\n return code;\n }\n\n process.stderr.write(\n `takuhon: unknown command '${first}'\\n` +\n `Run \\`takuhon --help\\` for usage. For scaffolding a new project, use \\`create-takuhon\\`.\\n`,\n );\n return 2;\n}\n\nprocess.exit(main(process.argv.slice(2)));\n","/**\n * `takuhon validate [path]` — validate a `takuhon.json` against `@takuhon/core`.\n *\n * All argument handling and the validation itself live here as pure,\n * side-effect-free functions so the whole command is unit-testable: `index.ts`\n * runs `process.exit(main(...))` at module top level and is therefore not\n * import-safe. `runValidate` reads the target file itself but returns its\n * output as strings plus an exit code, leaving the actual stdout/stderr writes\n * and `process.exit` to the caller.\n *\n * Exit codes:\n * 0 — the document is valid (or `--help` was requested)\n * 1 — the document was read and parsed but failed schema validation\n * 2 — the command could not run: bad arguments, a missing/unreadable file,\n * or a file that is not valid JSON (operational errors, distinct from an\n * invalid-but-readable document)\n */\n\nimport { readFileSync } from 'node:fs';\n\nimport { validate } from '@takuhon/core';\n\n/** Default profile filename, resolved relative to the current working directory. */\nconst DEFAULT_PATH = 'takuhon.json';\n\nconst USAGE = `Usage: takuhon validate [path]\n\nValidate a takuhon.json against the takuhon schema. With no path, validates\n./takuhon.json in the current working directory.\n\nExit codes: 0 = valid, 1 = invalid, 2 = file missing / unreadable / not JSON.\n`;\n\nexport interface ValidateOutcome {\n /** Process exit code (see module docstring). */\n readonly code: number;\n /** Text destined for stdout (empty when there is nothing to print). */\n readonly stdout: string;\n /** Text destined for stderr (empty when there is nothing to print). */\n readonly stderr: string;\n}\n\n/**\n * Run `takuhon validate` against the arguments that follow the subcommand\n * (i.e. `process.argv.slice(2)` minus the leading `\"validate\"`).\n *\n * Handles `--help` / `-h` and rejects extra positionals, then validates the\n * `takuhon.json` at the single optional path argument (default\n * `./takuhon.json`). Never throws and never writes to the process streams —\n * the caller renders the returned `stdout` / `stderr` and exits with `code`.\n */\nexport function runValidate(args: readonly string[] = []): ValidateOutcome {\n if (args[0] === '--help' || args[0] === '-h') {\n return { code: 0, stdout: USAGE, stderr: '' };\n }\n\n if (args.length > 1) {\n return {\n code: 2,\n stdout: '',\n stderr:\n 'takuhon: `validate` takes at most one path argument.\\n' +\n 'Run `takuhon validate --help` for usage.\\n',\n };\n }\n\n return validateFile(args[0]);\n}\n\n/** Read, parse, and schema-validate the profile at `pathArg` (default `./takuhon.json`). */\nfunction validateFile(pathArg?: string): ValidateOutcome {\n const target = pathArg ?? DEFAULT_PATH;\n\n let raw: string;\n try {\n raw = readFileSync(target, 'utf8');\n } catch {\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: cannot read '${target}'. Pass a path, or run from a directory containing a takuhon.json.\\n`,\n };\n }\n\n let data: unknown;\n try {\n data = JSON.parse(raw);\n } catch (error) {\n const detail = error instanceof Error ? error.message : String(error);\n return {\n code: 2,\n stdout: '',\n stderr: `takuhon: '${target}' is not valid JSON: ${detail}\\n`,\n };\n }\n\n const result = validate(data);\n if (result.ok) {\n return {\n code: 0,\n stdout: `${target}: valid (schemaVersion ${result.data.schemaVersion}).\\n`,\n stderr: '',\n };\n }\n\n const count = result.errors.length;\n const lines = result.errors.map((error) => ` ${error.pointer || '/'}: ${error.message}`);\n return {\n code: 1,\n stdout: '',\n stderr: `${target}: invalid (${count} error${count === 1 ? '' : 's'}):\\n${lines.join('\\n')}\\n`,\n };\n}\n"],"mappings":";;;AAaA,SAAS,gBAAAA,qBAAoB;;;ACK7B,SAAS,oBAAoB;AAE7B,SAAS,gBAAgB;AAGzB,IAAM,eAAe;AAErB,IAAM,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA0BP,SAAS,YAAY,OAA0B,CAAC,GAAoB;AACzE,MAAI,KAAK,CAAC,MAAM,YAAY,KAAK,CAAC,MAAM,MAAM;AAC5C,WAAO,EAAE,MAAM,GAAG,QAAQ,OAAO,QAAQ,GAAG;AAAA,EAC9C;AAEA,MAAI,KAAK,SAAS,GAAG;AACnB,WAAO;AAAA,MACL,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QACE;AAAA,IAEJ;AAAA,EACF;AAEA,SAAO,aAAa,KAAK,CAAC,CAAC;AAC7B;AAGA,SAAS,aAAa,SAAmC;AACvD,QAAM,SAAS,WAAW;AAE1B,MAAI;AACJ,MAAI;AACF,UAAM,aAAa,QAAQ,MAAM;AAAA,EACnC,QAAQ;AACN,WAAO;AAAA,MACL,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ,yBAAyB,MAAM;AAAA;AAAA,IACzC;AAAA,EACF;AAEA,MAAI;AACJ,MAAI;AACF,WAAO,KAAK,MAAM,GAAG;AAAA,EACvB,SAAS,OAAO;AACd,UAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACpE,WAAO;AAAA,MACL,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,QAAQ,aAAa,MAAM,wBAAwB,MAAM;AAAA;AAAA,IAC3D;AAAA,EACF;AAEA,QAAM,SAAS,SAAS,IAAI;AAC5B,MAAI,OAAO,IAAI;AACb,WAAO;AAAA,MACL,MAAM;AAAA,MACN,QAAQ,GAAG,MAAM,0BAA0B,OAAO,KAAK,aAAa;AAAA;AAAA,MACpE,QAAQ;AAAA,IACV;AAAA,EACF;AAEA,QAAM,QAAQ,OAAO,OAAO;AAC5B,QAAM,QAAQ,OAAO,OAAO,IAAI,CAAC,UAAU,KAAK,MAAM,WAAW,GAAG,KAAK,MAAM,OAAO,EAAE;AACxF,SAAO;AAAA,IACL,MAAM;AAAA,IACN,QAAQ;AAAA,IACR,QAAQ,GAAG,MAAM,cAAc,KAAK,SAAS,UAAU,IAAI,KAAK,GAAG;AAAA,EAAO,MAAM,KAAK,IAAI,CAAC;AAAA;AAAA,EAC5F;AACF;;;AD5FA,IAAM,MAAM,KAAK,MAAMC,cAAa,IAAI,IAAI,mBAAmB,YAAY,GAAG,GAAG,MAAM,CAAC;AAGxF,IAAM,UAAU,IAAI;AAEpB,IAAM,OAAO,WAAW,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAqB/B,SAAS,KAAK,MAAiC;AAC7C,QAAM,QAAQ,KAAK,CAAC;AAEpB,MAAI,UAAU,eAAe,UAAU,MAAM;AAC3C,YAAQ,OAAO,MAAM,GAAG,OAAO;AAAA,CAAI;AACnC,WAAO;AAAA,EACT;AAEA,MAAI,UAAU,UAAa,UAAU,YAAY,UAAU,MAAM;AAC/D,YAAQ,OAAO,MAAM,IAAI;AACzB,WAAO;AAAA,EACT;AAEA,MAAI,UAAU,YAAY;AACxB,UAAM,EAAE,MAAM,QAAQ,OAAO,IAAI,YAAY,KAAK,MAAM,CAAC,CAAC;AAC1D,QAAI,OAAQ,SAAQ,OAAO,MAAM,MAAM;AACvC,QAAI,OAAQ,SAAQ,OAAO,MAAM,MAAM;AACvC,WAAO;AAAA,EACT;AAEA,UAAQ,OAAO;AAAA,IACb,6BAA6B,KAAK;AAAA;AAAA;AAAA,EAEpC;AACA,SAAO;AACT;AAEA,QAAQ,KAAK,KAAK,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC;","names":["readFileSync","readFileSync"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takuhon/cli",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "create-takuhon scaffolding plus dev/validate/sync/export/migrate/restore commands",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Takuhon contributors",
@@ -48,7 +48,7 @@
48
48
  },
49
49
  "dependencies": {
50
50
  "@clack/prompts": "^0.11.0",
51
- "@takuhon/core": "0.6.1"
51
+ "@takuhon/core": "0.7.0"
52
52
  },
53
53
  "scripts": {
54
54
  "typecheck": "tsc",