@goqoo/trunks 0.2.0 → 0.3.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.ja.md ADDED
@@ -0,0 +1,214 @@
1
+ # trunks
2
+
3
+ [![Node.js](https://img.shields.io/badge/Node.js-20+-339933?style=flat&logo=node.js)](https://nodejs.org/)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ [English](/README.md) | 日本語
7
+
8
+ [@kintone/dts-gen](https://github.com/kintone/js-sdk/tree/main/packages/dts-gen) のラッパー CLI ツール。設定ファイル1つで複数の kintone アプリの TypeScript 型定義を一括生成します。
9
+
10
+ ## 特徴
11
+
12
+ - 1コマンドで複数アプリの型定義を生成
13
+ - TypeScript 設定ファイルで型安全な設定(`trunks.config.ts`)
14
+ - 複数の認証方式に対応(パスワード、API トークン、OAuth)
15
+ - Prettier による自動フォーマット(オプション)
16
+ - プレビュー環境・ゲストスペースに対応
17
+
18
+ ## クイックスタート
19
+
20
+ 1. プロジェクトルートに設定ファイル `trunks.config.ts` を作成:
21
+
22
+ ```typescript
23
+ import { defineConfig } from '@goqoo/trunks';
24
+
25
+ export default defineConfig({
26
+ host: 'your-subdomain.cybozu.com',
27
+ apps: {
28
+ customer: 123,
29
+ order: 456,
30
+ product: 789,
31
+ },
32
+ auth: { type: 'oauth' },
33
+ });
34
+ ```
35
+
36
+ 2. コマンドを実行:
37
+
38
+ グローバルインストールあり:
39
+
40
+ ```bash
41
+ npm install -g @goqoo/trunks
42
+ trunks
43
+ ```
44
+
45
+ グローバルインストールなし:
46
+
47
+ ```bash
48
+ npx @goqoo/trunks
49
+ ```
50
+
51
+ 3. `dts/` ディレクトリに型定義ファイルが生成されます:
52
+ - `dts/customer-fields.d.ts`
53
+ - `dts/order-fields.d.ts`
54
+ - `dts/product-fields.d.ts`
55
+
56
+ ## 設定
57
+
58
+ ### 基本設定
59
+
60
+ ```typescript
61
+ import { defineConfig } from '@goqoo/trunks';
62
+
63
+ export default defineConfig({
64
+ // 必須
65
+ host: 'your-subdomain.cybozu.com',
66
+ apps: {
67
+ customer: 123, // { アプリ名: アプリID }
68
+ order: 456,
69
+ },
70
+ auth: { type: 'oauth' },
71
+
72
+ // オプション
73
+ outDir: 'dts', // 出力ディレクトリ(デフォルト: "dts")
74
+ preview: false, // プレビュー環境を使用(デフォルト: false)
75
+ guestSpaceId: 5, // ゲストスペース ID(該当する場合)
76
+ namespace: 'kintone.types', // TypeScript の namespace(デフォルト: "kintone.types")
77
+ format: true, // Prettier でフォーマット(デフォルト: false)
78
+ });
79
+ ```
80
+
81
+ ### 認証方式
82
+
83
+ 環境変数はプロジェクトルートの `.env` ファイルに設定できます:
84
+
85
+ ```bash
86
+ # .env
87
+ KINTONE_USERNAME=your-username
88
+ KINTONE_PASSWORD=your-password
89
+ KINTONE_API_TOKEN=your-api-token
90
+ ```
91
+
92
+ > **注意**: 設定ファイルに認証情報を直書きする場合は、設定ファイルを `.gitignore` に追加して、バージョン管理に機密情報がコミットされないようにしてください。環境変数(`.env` ファイル)または標準入力の使用を推奨します。
93
+
94
+ #### パスワード
95
+
96
+ 認証情報は設定ファイル、環境変数 `KINTONE_USERNAME` と `KINTONE_PASSWORD`、または標準入力から取得します(この優先順位)。
97
+
98
+ ```typescript
99
+ auth: { type: 'password' },
100
+ // または直書き(非推奨)
101
+ auth: { type: 'password', username: 'user', password: 'pass' },
102
+ ```
103
+
104
+ #### API トークン
105
+
106
+ トークンは設定ファイル、環境変数 `KINTONE_API_TOKEN`、または標準入力から取得します(この優先順位)。
107
+
108
+ ```typescript
109
+ auth: { type: 'api-token' },
110
+ // または直書き(非推奨)
111
+ auth: { type: 'api-token', token: 'your-token' },
112
+ ```
113
+
114
+ 複数アプリの場合は `.env` でカンマ区切りで指定:
115
+
116
+ ```bash
117
+ KINTONE_API_TOKEN=token1,token2,token3
118
+ ```
119
+
120
+ #### OAuth
121
+
122
+ [Gyuma](https://github.com/nicecai/gyuma) を使用した OAuth 認証。ブラウザが開いて認証を行います。
123
+
124
+ ```typescript
125
+ auth: {
126
+ type: 'oauth',
127
+ scope: 'k:app_settings:read', // オプション: カスタムスコープ
128
+ },
129
+ ```
130
+
131
+ ### その他のオプション
132
+
133
+ #### Basic 認証
134
+
135
+ Basic 認証が必要な環境の場合:
136
+
137
+ ```typescript
138
+ basicAuth: {
139
+ username: 'basic-user',
140
+ password: 'basic-password',
141
+ },
142
+ ```
143
+
144
+ #### プロキシ
145
+
146
+ ```typescript
147
+ proxy: {
148
+ host: 'proxy.example.com',
149
+ port: 8080,
150
+ },
151
+ ```
152
+
153
+ #### クライアント証明書(OAuth 用)
154
+
155
+ ```typescript
156
+ pfx: {
157
+ filepath: '/path/to/cert.pfx',
158
+ password: 'certificate-password',
159
+ },
160
+ ```
161
+
162
+ ## CLI オプション
163
+
164
+ ```bash
165
+ trunks [options]
166
+
167
+ Options:
168
+ -c, --config <path> 設定ファイルのパス(デフォルト: 自動検出)
169
+ -h, --help ヘルプを表示
170
+ -V, --version バージョンを表示
171
+ ```
172
+
173
+ ## 生成される出力
174
+
175
+ アプリ名 `customer`(ID: 123)の場合、以下のファイルが生成されます:
176
+
177
+ ```typescript
178
+ // dts/customer-fields.d.ts
179
+ declare namespace kintone.types {
180
+ interface CustomerFields {
181
+ companyName: kintone.fieldTypes.SingleLineText;
182
+ email: kintone.fieldTypes.Link;
183
+ // ...
184
+ }
185
+ interface SavedCustomerFields extends CustomerFields {
186
+ $id: kintone.fieldTypes.Id;
187
+ $revision: kintone.fieldTypes.Revision;
188
+ // ...
189
+ }
190
+ }
191
+ ```
192
+
193
+ ## 開発
194
+
195
+ ```bash
196
+ # ビルド
197
+ yarn build
198
+
199
+ # テスト
200
+ yarn test
201
+
202
+ # ウォッチモード
203
+ yarn dev
204
+ ```
205
+
206
+ ## 関連プロジェクト
207
+
208
+ - [@kintone/dts-gen](https://github.com/kintone/js-sdk/tree/main/packages/dts-gen) - 型定義生成の本体
209
+ - [Gyuma](https://github.com/nicecai/gyuma) - kintone の OAuth 認証
210
+ - [gotenks](https://github.com/goqoo-on-kintone/gotenks) - kintone TypeScript 型を Go 型に変換
211
+
212
+ ## ライセンス
213
+
214
+ MIT License - 詳細は [LICENSE](LICENSE) を参照してください。
package/README.md ADDED
@@ -0,0 +1,214 @@
1
+ # trunks
2
+
3
+ [![Node.js](https://img.shields.io/badge/Node.js-20+-339933?style=flat&logo=node.js)](https://nodejs.org/)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ English | [日本語](/README.ja.md)
7
+
8
+ A CLI wrapper for [@kintone/dts-gen](https://github.com/kintone/js-sdk/tree/main/packages/dts-gen) that generates TypeScript type definitions for multiple Kintone apps with a single configuration file.
9
+
10
+ ## Features
11
+
12
+ - Generate type definitions for multiple apps in one command
13
+ - TypeScript configuration file with type safety (`trunks.config.ts`)
14
+ - Multiple authentication methods (Password, API Token, OAuth)
15
+ - Optional Prettier formatting for generated files
16
+ - Support for preview environments and guest spaces
17
+
18
+ ## Quick Start
19
+
20
+ 1. Create a configuration file `trunks.config.ts` in your project root:
21
+
22
+ ```typescript
23
+ import { defineConfig } from '@goqoo/trunks';
24
+
25
+ export default defineConfig({
26
+ host: 'your-subdomain.cybozu.com',
27
+ apps: {
28
+ customer: 123,
29
+ order: 456,
30
+ product: 789,
31
+ },
32
+ auth: { type: 'oauth' },
33
+ });
34
+ ```
35
+
36
+ 2. Run the command:
37
+
38
+ With global installation:
39
+
40
+ ```bash
41
+ npm install -g @goqoo/trunks
42
+ trunks
43
+ ```
44
+
45
+ Without global installation:
46
+
47
+ ```bash
48
+ npx @goqoo/trunks
49
+ ```
50
+
51
+ 3. Type definition files will be generated in the `dts/` directory:
52
+ - `dts/customer-fields.d.ts`
53
+ - `dts/order-fields.d.ts`
54
+ - `dts/product-fields.d.ts`
55
+
56
+ ## Configuration
57
+
58
+ ### Basic Configuration
59
+
60
+ ```typescript
61
+ import { defineConfig } from '@goqoo/trunks';
62
+
63
+ export default defineConfig({
64
+ // Required
65
+ host: 'your-subdomain.cybozu.com',
66
+ apps: {
67
+ customer: 123, // { appName: appId }
68
+ order: 456,
69
+ },
70
+ auth: { type: 'oauth' },
71
+
72
+ // Optional
73
+ outDir: 'dts', // Output directory (default: "dts")
74
+ preview: false, // Use preview environment (default: false)
75
+ guestSpaceId: 5, // Guest space ID (if applicable)
76
+ namespace: 'kintone.types', // TypeScript namespace (default: "kintone.types")
77
+ format: true, // Format with Prettier (default: false)
78
+ });
79
+ ```
80
+
81
+ ### Authentication Methods
82
+
83
+ Environment variables can be set in a `.env` file in your project root:
84
+
85
+ ```bash
86
+ # .env
87
+ KINTONE_USERNAME=your-username
88
+ KINTONE_PASSWORD=your-password
89
+ KINTONE_API_TOKEN=your-api-token
90
+ ```
91
+
92
+ > **Warning**: If you write credentials directly in the config file, make sure to add the config file to `.gitignore` to avoid committing sensitive information to version control. Using environment variables (`.env` file) or stdin prompts is recommended.
93
+
94
+ #### Password
95
+
96
+ Credentials are read from the config file, environment variables `KINTONE_USERNAME` and `KINTONE_PASSWORD`, or prompted via stdin (in that order of priority).
97
+
98
+ ```typescript
99
+ auth: { type: 'password' },
100
+ // Or with direct credentials (not recommended)
101
+ auth: { type: 'password', username: 'user', password: 'pass' },
102
+ ```
103
+
104
+ #### API Token
105
+
106
+ Token is read from the config file, environment variable `KINTONE_API_TOKEN`, or prompted via stdin (in that order of priority).
107
+
108
+ ```typescript
109
+ auth: { type: 'api-token' },
110
+ // Or with direct token (not recommended)
111
+ auth: { type: 'api-token', token: 'your-token' },
112
+ ```
113
+
114
+ For multiple apps, use comma-separated tokens in `.env`:
115
+
116
+ ```bash
117
+ KINTONE_API_TOKEN=token1,token2,token3
118
+ ```
119
+
120
+ #### OAuth
121
+
122
+ Uses [Gyuma](https://github.com/nicecai/gyuma) for OAuth authentication. A browser window will open for authentication.
123
+
124
+ ```typescript
125
+ auth: {
126
+ type: 'oauth',
127
+ scope: 'k:app_settings:read', // Optional: custom scope
128
+ },
129
+ ```
130
+
131
+ ### Additional Options
132
+
133
+ #### Basic Authentication
134
+
135
+ For environments that require basic authentication:
136
+
137
+ ```typescript
138
+ basicAuth: {
139
+ username: 'basic-user',
140
+ password: 'basic-password',
141
+ },
142
+ ```
143
+
144
+ #### Proxy
145
+
146
+ ```typescript
147
+ proxy: {
148
+ host: 'proxy.example.com',
149
+ port: 8080,
150
+ },
151
+ ```
152
+
153
+ #### Client Certificate (for OAuth)
154
+
155
+ ```typescript
156
+ pfx: {
157
+ filepath: '/path/to/cert.pfx',
158
+ password: 'certificate-password',
159
+ },
160
+ ```
161
+
162
+ ## CLI Options
163
+
164
+ ```bash
165
+ trunks [options]
166
+
167
+ Options:
168
+ -c, --config <path> Path to config file (default: auto-detect)
169
+ -h, --help Display help
170
+ -V, --version Display version
171
+ ```
172
+
173
+ ## Generated Output
174
+
175
+ For an app named `customer` (ID: 123), the following file is generated:
176
+
177
+ ```typescript
178
+ // dts/customer-fields.d.ts
179
+ declare namespace kintone.types {
180
+ interface CustomerFields {
181
+ companyName: kintone.fieldTypes.SingleLineText;
182
+ email: kintone.fieldTypes.Link;
183
+ // ...
184
+ }
185
+ interface SavedCustomerFields extends CustomerFields {
186
+ $id: kintone.fieldTypes.Id;
187
+ $revision: kintone.fieldTypes.Revision;
188
+ // ...
189
+ }
190
+ }
191
+ ```
192
+
193
+ ## Development
194
+
195
+ ```bash
196
+ # Build
197
+ yarn build
198
+
199
+ # Test
200
+ yarn test
201
+
202
+ # Watch mode
203
+ yarn dev
204
+ ```
205
+
206
+ ## Related Projects
207
+
208
+ - [@kintone/dts-gen](https://github.com/kintone/js-sdk/tree/main/packages/dts-gen) - The underlying type definition generator
209
+ - [Gyuma](https://github.com/nicecai/gyuma) - OAuth authentication for Kintone
210
+ - [gotenks](https://github.com/goqoo-on-kintone/gotenks) - Convert kintone TypeScript types to Go types
211
+
212
+ ## License
213
+
214
+ MIT License - see [LICENSE](LICENSE) for details.
package/dist/cli.js CHANGED
@@ -1,13 +1,20 @@
1
1
  #!/usr/bin/env node
2
+ import { createRequire } from 'module';
3
+ import { config as loadEnv } from 'dotenv';
2
4
  import { Command } from 'commander';
3
5
  import chalk from 'chalk';
4
6
  import { loadConfig } from './config.js';
5
7
  import { generate } from './generate.js';
8
+ // package.jsonからバージョンを取得
9
+ const require = createRequire(import.meta.url);
10
+ const { version } = require('../package.json');
11
+ // .envファイルがあれば環境変数として読み込む
12
+ loadEnv({ quiet: true });
6
13
  const program = new Command();
7
14
  program
8
15
  .name('trunks')
9
16
  .description('Generate TypeScript type definitions for multiple Kintone apps')
10
- .version('0.1.0');
17
+ .version(version);
11
18
  program
12
19
  .command('generate', { isDefault: true })
13
20
  .description('Generate type definitions for all configured apps')
package/dist/generate.js CHANGED
@@ -90,9 +90,10 @@ async function buildAuthArgs(config) {
90
90
  const args = {};
91
91
  switch (config.auth.type) {
92
92
  case 'password': {
93
- let username = process.env.KINTONE_USERNAME;
94
- let password = process.env.KINTONE_PASSWORD;
95
- // 環境変数が未設定の場合は標準入力で取得
93
+ // 設定ファイル 環境変数 → 標準入力の優先順位
94
+ let username = config.auth.username ?? process.env.KINTONE_USERNAME;
95
+ let password = config.auth.password ?? process.env.KINTONE_PASSWORD;
96
+ // 未設定の場合は標準入力で取得
96
97
  if (!username) {
97
98
  username = await prompt('Kintone Username: ');
98
99
  }
@@ -116,9 +117,18 @@ async function buildAuthArgs(config) {
116
117
  args['oauth-token'] = oauthToken;
117
118
  break;
118
119
  }
119
- case 'api-token':
120
- args['api-token'] = config.auth.token;
120
+ case 'api-token': {
121
+ // 設定ファイル → 環境変数 → 標準入力の優先順位
122
+ let token = config.auth.token ?? process.env.KINTONE_API_TOKEN;
123
+ if (!token) {
124
+ token = await promptPassword('Kintone API Token: ');
125
+ }
126
+ if (!token) {
127
+ throw new Error('API token is required for api-token auth');
128
+ }
129
+ args['api-token'] = token;
121
130
  break;
131
+ }
122
132
  }
123
133
  // Basic認証
124
134
  if (config.basicAuth) {
package/dist/types.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export type PasswordAuth = {
2
2
  type: 'password';
3
+ username?: string;
4
+ password?: string;
3
5
  };
4
6
  export type OAuthAuth = {
5
7
  type: 'oauth';
@@ -7,7 +9,7 @@ export type OAuthAuth = {
7
9
  };
8
10
  export type ApiTokenAuth = {
9
11
  type: 'api-token';
10
- token: string;
12
+ token?: string;
11
13
  };
12
14
  export type Auth = PasswordAuth | OAuthAuth | ApiTokenAuth;
13
15
  export type ProxyConfig = {
package/package.json CHANGED
@@ -1,9 +1,20 @@
1
1
  {
2
2
  "name": "@goqoo/trunks",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "A CLI wrapper for @kintone/dts-gen that generates type definitions for multiple Kintone apps",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "package.json"
17
+ ],
7
18
  "bin": {
8
19
  "trunks": "dist/cli.js"
9
20
  },
@@ -37,6 +48,7 @@
37
48
  "chalk": "^5.4.0",
38
49
  "change-case": "^5.4.0",
39
50
  "commander": "^13.0.0",
51
+ "dotenv": "^17.3.1",
40
52
  "gyuma": "^0.6.1",
41
53
  "jiti": "^2.4.0"
42
54
  }
@@ -1,7 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(yarn test)"
5
- ]
6
- }
7
- }
package/.prettierignore DELETED
@@ -1 +0,0 @@
1
- *.md
package/.prettierrc.cjs DELETED
@@ -1,6 +0,0 @@
1
- module.exports = {
2
- semi: true,
3
- singleQuote: false,
4
- printWidth: 120,
5
- quoteProps: "consistent",
6
- };
package/CLAUDE.md DELETED
@@ -1,71 +0,0 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- ## プロジェクト概要
6
-
7
- @goqoo/trunks - `@kintone/dts-gen`のラッパーCLIツール。
8
-
9
- ### 背景・目的
10
-
11
- `@kintone/dts-gen`は優れた型定義ファイルを生成するが、CLIとしての使い勝手に課題がある(特に複数アプリの一括生成ができない)。このツールは設定ファイルベースで複数アプリの型定義を一括生成する。
12
-
13
- ### 参考実装
14
-
15
- [goqoo/src/generator/dts/index.ts](https://github.com/goqoo-on-kintone/goqoo/blob/main/src/generator/dts/index.ts) の機能を単独抽出したもの。
16
-
17
- goqooでの処理フロー:
18
- 1. 設定ファイルから対象環境を特定
19
- 2. OAuth or ユーザー名/パスワード認証を設定
20
- 3. `appId`オブジェクトをループして各アプリに対し`npx kintone-dts-gen`を実行
21
- 4. 出力ファイル名はkebab-case、型名はPascalCaseで自動生成
22
-
23
- ### 設定ファイル構造
24
-
25
- ```typescript
26
- type Config = {
27
- host: string // Kintone環境のホスト(例: "example.cybozu.com")
28
- apps: Record<string, number> // { appName: appId }
29
- auth:
30
- | { type: 'password' } // 環境変数 KINTONE_USERNAME, KINTONE_PASSWORD
31
- | { type: 'oauth'; scope?: string } // OAuth認証
32
- | { type: 'api-token'; token: string } // APIトークン
33
- proxy?: { host: string; port: number } // プロキシ設定(任意)
34
- basicAuth?: { username: string; password: string } // Basic認証(任意)
35
- }
36
- ```
37
-
38
- ## 開発コマンド
39
-
40
- ```bash
41
- yarn install # パッケージインストール
42
- yarn build # TypeScriptビルド
43
- yarn dev # ウォッチモードでビルド
44
- yarn test # テスト実行(Jest + ESM)
45
- yarn test:watch # ウォッチモードでテスト
46
- yarn prettier --write . # コードフォーマット
47
- ```
48
-
49
- ## ソース構造
50
-
51
- ```
52
- src/
53
- ├── cli.ts # CLIエントリーポイント(Commander.js)
54
- ├── config.ts # 設定ファイル読み込み(jiti使用)
55
- ├── generate.ts # dts-gen実行処理
56
- ├── index.ts # エクスポート
57
- └── types.ts # 型定義・defineConfig
58
-
59
- test/
60
- ├── config.test.ts # configのテスト
61
- ├── generate.test.ts # generateのテスト
62
- └── types.test.ts # typesのテスト
63
- ```
64
-
65
- ## 技術スタック
66
-
67
- - **ランタイム:** Node.js >= 20
68
- - **パッケージマネージャー:** Yarn
69
- - **主要依存:** @kintone/dts-gen, Commander.js, jiti, change-case, chalk
70
- - **テスト:** Jest + ts-jest(ESMモード)
71
- - **フォーマッタ:** Prettier(シングルクォート、120文字/行)
@@ -1,25 +0,0 @@
1
- declare namespace kintone.types {
2
- interface ActivityFields {
3
- 案件No: kintone.fieldTypes.Number;
4
- 顧客No: kintone.fieldTypes.Number;
5
- タイトル: kintone.fieldTypes.SingleLineText;
6
- 案件名: kintone.fieldTypes.SingleLineText;
7
- 対応日付: kintone.fieldTypes.Date;
8
- 対応種別: kintone.fieldTypes.DropDown;
9
- 内容: kintone.fieldTypes.MultiLineText;
10
- 会社名: kintone.fieldTypes.SingleLineText;
11
-
12
- 対応者: kintone.fieldTypes.UserSelect;
13
- 所属組織: kintone.fieldTypes.OrganizationSelect;
14
- 添付ファイル: kintone.fieldTypes.File;
15
- }
16
- interface SavedActivityFields extends ActivityFields {
17
- $id: kintone.fieldTypes.Id;
18
- $revision: kintone.fieldTypes.Revision;
19
- 更新者: kintone.fieldTypes.Modifier;
20
- 作成者: kintone.fieldTypes.Creator;
21
- レコード番号: kintone.fieldTypes.RecordNumber;
22
- 更新日時: kintone.fieldTypes.UpdatedTime;
23
- 作成日時: kintone.fieldTypes.CreatedTime;
24
- }
25
- }
@@ -1,28 +0,0 @@
1
- declare namespace kintone.types {
2
- interface CustomerFields {
3
- 顧客情報メモ欄: kintone.fieldTypes.MultiLineText;
4
- 文字列__1行_: kintone.fieldTypes.SingleLineText;
5
- Webサイト: kintone.fieldTypes.Link;
6
- 建物名: kintone.fieldTypes.SingleLineText;
7
- 顧客ランク: kintone.fieldTypes.RadioButton;
8
- 支払日: kintone.fieldTypes.DropDown;
9
- 住所: kintone.fieldTypes.SingleLineText;
10
- 電話番号: kintone.fieldTypes.Link;
11
- 会社名: kintone.fieldTypes.SingleLineText;
12
- 郵便番号: kintone.fieldTypes.SingleLineText;
13
- 業種: kintone.fieldTypes.DropDown;
14
- 文字列__1行__0: kintone.fieldTypes.SingleLineText;
15
- 都道府県: kintone.fieldTypes.DropDown;
16
- 締め日: kintone.fieldTypes.DropDown;
17
- FAX: kintone.fieldTypes.Link;
18
- }
19
- interface SavedCustomerFields extends CustomerFields {
20
- $id: kintone.fieldTypes.Id;
21
- $revision: kintone.fieldTypes.Revision;
22
- 更新者: kintone.fieldTypes.Modifier;
23
- 作成者: kintone.fieldTypes.Creator;
24
- 顧客No: kintone.fieldTypes.RecordNumber;
25
- 更新日時: kintone.fieldTypes.UpdatedTime;
26
- 作成日時: kintone.fieldTypes.CreatedTime;
27
- }
28
- }
package/jest.config.js DELETED
@@ -1,19 +0,0 @@
1
- /** @type {import('ts-jest').JestConfigWithTsJest} */
2
- export default {
3
- preset: 'ts-jest/presets/default-esm',
4
- testEnvironment: 'node',
5
- extensionsToTreatAsEsm: ['.ts'],
6
- moduleNameMapper: {
7
- '^(\\.{1,2}/.*)\\.js$': '$1',
8
- },
9
- transform: {
10
- '^.+\\.ts$': [
11
- 'ts-jest',
12
- {
13
- useESM: true,
14
- },
15
- ],
16
- },
17
- testMatch: ['<rootDir>/test/**/*.test.ts'],
18
- collectCoverageFrom: ['src/**/*.ts', '!src/cli.ts'],
19
- };
package/src/cli.ts DELETED
@@ -1,28 +0,0 @@
1
- #!/usr/bin/env node
2
- import { Command } from 'commander';
3
- import chalk from 'chalk';
4
- import { loadConfig } from './config.js';
5
- import { generate } from './generate.js';
6
-
7
- const program = new Command();
8
-
9
- program
10
- .name('trunks')
11
- .description('Generate TypeScript type definitions for multiple Kintone apps')
12
- .version('0.1.0');
13
-
14
- program
15
- .command('generate', { isDefault: true })
16
- .description('Generate type definitions for all configured apps')
17
- .option('-c, --config <path>', 'Path to config file')
18
- .action(async (options) => {
19
- try {
20
- const config = await loadConfig(options.config ? undefined : process.cwd());
21
- await generate(config);
22
- } catch (error) {
23
- console.error(chalk.red('Error:'), error instanceof Error ? error.message : error);
24
- process.exit(1);
25
- }
26
- });
27
-
28
- program.parse();
package/src/config.ts DELETED
@@ -1,23 +0,0 @@
1
- import { existsSync } from 'fs';
2
- import { resolve } from 'path';
3
- import { createJiti } from 'jiti';
4
- import type { Config } from './types.js';
5
-
6
- const CONFIG_FILES = ['trunks.config.ts', 'trunks.config.js', 'trunks.config.mjs'];
7
-
8
- // 設定ファイルを検索して読み込む
9
- export async function loadConfig(cwd: string = process.cwd()): Promise<Config> {
10
- const jiti = createJiti(cwd, { interopDefault: true });
11
-
12
- for (const filename of CONFIG_FILES) {
13
- const configPath = resolve(cwd, filename);
14
- if (existsSync(configPath)) {
15
- const config = await jiti.import(configPath);
16
- return config as Config;
17
- }
18
- }
19
-
20
- throw new Error(
21
- `Config file not found. Create one of: ${CONFIG_FILES.join(', ')}`
22
- );
23
- }
package/src/generate.ts DELETED
@@ -1,273 +0,0 @@
1
- import { spawn, spawnSync } from 'child_process';
2
- import { mkdirSync } from 'fs';
3
- import * as readline from 'readline';
4
- import chalk from 'chalk';
5
- import { kebabCase, pascalCase } from 'change-case';
6
- import type { AgentOptions, Config } from './types.js';
7
- import { getOauthToken } from './oauth.js';
8
-
9
- // npx prettierが実行可能かチェック
10
- function isPrettierAvailable(): boolean {
11
- const result = spawnSync('npx', ['prettier', '--version'], {
12
- stdio: 'pipe',
13
- encoding: 'utf-8',
14
- });
15
- return result.status === 0;
16
- }
17
-
18
- // Prettierでファイルをフォーマット
19
- function formatWithPrettier(filePath: string): Promise<boolean> {
20
- return new Promise((resolve) => {
21
- const proc = spawn('npx', ['prettier', '--write', filePath], {
22
- cwd: process.cwd(),
23
- stdio: 'pipe',
24
- });
25
-
26
- proc.on('close', (code) => {
27
- resolve(code === 0);
28
- });
29
-
30
- proc.on('error', () => {
31
- resolve(false);
32
- });
33
- });
34
- }
35
-
36
- type DtsGenArgs = Record<string, string | undefined>;
37
-
38
- // 標準入力からテキストを取得
39
- function prompt(question: string): Promise<string> {
40
- const rl = readline.createInterface({
41
- input: process.stdin,
42
- output: process.stdout,
43
- });
44
-
45
- return new Promise((resolve) => {
46
- rl.question(question, (answer) => {
47
- rl.close();
48
- resolve(answer);
49
- });
50
- });
51
- }
52
-
53
- // 標準入力からパスワードを取得(入力を隠す)
54
- function promptPassword(question: string): Promise<string> {
55
- return new Promise((resolve) => {
56
- process.stdout.write(question);
57
-
58
- const stdin = process.stdin;
59
- if (!stdin.isTTY) {
60
- // TTYでない場合は通常の入力
61
- const rl = readline.createInterface({ input: stdin, output: process.stdout });
62
- rl.question('', (answer) => {
63
- rl.close();
64
- resolve(answer);
65
- });
66
- return;
67
- }
68
-
69
- // TTYの場合はraw modeで入力を隠す
70
- stdin.setRawMode(true);
71
- stdin.resume();
72
- stdin.setEncoding('utf8');
73
-
74
- let password = '';
75
- const onData = (char: string) => {
76
- if (char === '\n' || char === '\r' || char === '\u0004') {
77
- // Enter or Ctrl+D
78
- stdin.setRawMode(false);
79
- stdin.removeListener('data', onData);
80
- stdin.pause();
81
- process.stdout.write('\n');
82
- resolve(password);
83
- } else if (char === '\u0003') {
84
- // Ctrl+C
85
- stdin.setRawMode(false);
86
- process.stdout.write('\n');
87
- process.exit(1);
88
- } else if (char === '\u007F' || char === '\b') {
89
- // Backspace
90
- password = password.slice(0, -1);
91
- } else {
92
- password += char;
93
- }
94
- };
95
-
96
- stdin.on('data', onData);
97
- });
98
- }
99
-
100
- // 認証引数を構築
101
- async function buildAuthArgs(config: Config): Promise<DtsGenArgs> {
102
- const args: DtsGenArgs = {};
103
-
104
- switch (config.auth.type) {
105
- case 'password': {
106
- let username = process.env.KINTONE_USERNAME;
107
- let password = process.env.KINTONE_PASSWORD;
108
-
109
- // 環境変数が未設定の場合は標準入力で取得
110
- if (!username) {
111
- username = await prompt('Kintone Username: ');
112
- }
113
- if (!password) {
114
- password = await promptPassword('Kintone Password: ');
115
- }
116
-
117
- if (!username || !password) {
118
- throw new Error('Username and password are required for password auth');
119
- }
120
-
121
- args['username'] = username;
122
- args['password'] = password;
123
- break;
124
- }
125
- case 'oauth': {
126
- // Gyumaを使ってOAuthトークンを取得
127
- const agentOptions: AgentOptions = {
128
- proxy: config.proxy ? `http://${config.proxy.host}:${config.proxy.port}` : undefined,
129
- pfx: config.pfx,
130
- };
131
- const oauthToken = await getOauthToken(config.host, config.auth.scope, agentOptions);
132
- args['oauth-token'] = oauthToken;
133
- break;
134
- }
135
- case 'api-token':
136
- args['api-token'] = config.auth.token;
137
- break;
138
- }
139
-
140
- // Basic認証
141
- if (config.basicAuth) {
142
- args['basic-auth-username'] = config.basicAuth.username;
143
- args['basic-auth-password'] = config.basicAuth.password;
144
- }
145
-
146
- // プロキシ
147
- if (config.proxy) {
148
- args['proxy'] = `http://${config.proxy.host}:${config.proxy.port}`;
149
- }
150
-
151
- return args;
152
- }
153
-
154
- // kintoneのエラーレスポンスを抽出
155
- function extractKintoneError(output: string): { code: string; id: string; message: string } | null {
156
- // 形式: code: 'GAIA_AP15' または "code": "GAIA_AP15"
157
- const codeMatch = output.match(/code:\s*'([^']+)'/) ?? output.match(/"code":\s*"([^"]+)"/);
158
- const idMatch = output.match(/id:\s*'([^']+)'/) ?? output.match(/"id":\s*"([^"]+)"/);
159
- const messageMatch = output.match(/message:\s*'([^']+)'/) ?? output.match(/"message":\s*"([^"]+)"/);
160
-
161
- const code = codeMatch?.[1];
162
- const id = idMatch?.[1];
163
- const message = messageMatch?.[1];
164
-
165
- if (code && message) {
166
- return { code, id: id ?? '', message };
167
- }
168
- return null;
169
- }
170
-
171
- // 単一アプリの型定義を生成
172
- function generateForApp(
173
- appName: string,
174
- appId: number,
175
- config: Config,
176
- authArgs: DtsGenArgs,
177
- outDir: string
178
- ): Promise<{ success: boolean; output: string }> {
179
- return new Promise((resolve) => {
180
- const outputPath = `${outDir}/${kebabCase(appName)}-fields.d.ts`;
181
- const args: DtsGenArgs = {
182
- 'base-url': `https://${config.host}`,
183
- ...authArgs,
184
- 'type-name': `${pascalCase(appName)}Fields`,
185
- 'app-id': String(appId),
186
- 'output': outputPath,
187
- 'guest-space-id': config.guestSpaceId !== undefined ? String(config.guestSpaceId) : undefined,
188
- 'namespace': config.namespace,
189
- };
190
-
191
- // undefined値をフィルタリングして引数配列を作成
192
- const cliArgs = Object.entries(args)
193
- .filter(([, value]) => value !== undefined)
194
- .map(([key, value]) => `--${key}=${value}`);
195
-
196
- // プレビュー環境の場合は--previewフラグを追加
197
- if (config.preview) {
198
- cliArgs.push('--preview');
199
- }
200
-
201
- const proc = spawn('npx', ['kintone-dts-gen', ...cliArgs], {
202
- cwd: process.cwd(),
203
- stdio: ['inherit', 'pipe', 'pipe'],
204
- });
205
-
206
- let stdout = '';
207
- let stderr = '';
208
- proc.stdout?.on('data', (data) => {
209
- stdout += data.toString();
210
- });
211
- proc.stderr?.on('data', (data) => {
212
- stderr += data.toString();
213
- });
214
-
215
- proc.on('close', (code) => {
216
- if (code !== 0) {
217
- // stdoutとstderr両方からエラー情報を探す
218
- const output = stdout + stderr;
219
- const kintoneError = extractKintoneError(output);
220
- if (kintoneError) {
221
- console.error(chalk.red(`Error [${appName}]:`), kintoneError.message);
222
- console.error(chalk.gray(` code: ${kintoneError.code}, id: ${kintoneError.id}`));
223
- } else {
224
- console.error(chalk.red(`Error [${appName}]:`), `kintone-dts-gen exited with code ${code}`);
225
- }
226
- resolve({ success: false, output: outputPath });
227
- } else {
228
- console.info(`${chalk.cyan('info')} ${chalk.magenta('Created')} ${chalk.green(outputPath)}`);
229
- resolve({ success: true, output: outputPath });
230
- }
231
- });
232
-
233
- proc.on('error', (err) => {
234
- console.error(chalk.red(`Error [${appName}]:`), err.message);
235
- resolve({ success: false, output: outputPath });
236
- });
237
- });
238
- }
239
-
240
- // 全アプリの型定義を生成
241
- export async function generate(config: Config): Promise<void> {
242
- const outDir = config.outDir ?? 'dts';
243
- mkdirSync(outDir, { recursive: true });
244
-
245
- const authArgs = await buildAuthArgs(config);
246
- const apps = Object.entries(config.apps);
247
-
248
- // format: trueの場合のみPrettierを使用
249
- const usePrettier = config.format === true && isPrettierAvailable();
250
-
251
- console.info(chalk.cyan(`Generating type definitions for ${apps.length} app(s)...`));
252
-
253
- // 順次実行(並列だとコンソール出力が混在する)
254
- const results: { success: boolean; output: string }[] = [];
255
- for (const [appName, appId] of apps) {
256
- const result = await generateForApp(appName, appId, config, authArgs, outDir);
257
- results.push(result);
258
-
259
- // 成功したファイルをPrettierでフォーマット
260
- if (result.success && usePrettier) {
261
- await formatWithPrettier(result.output);
262
- }
263
- }
264
-
265
- const successCount = results.filter((r) => r.success).length;
266
- const failCount = results.length - successCount;
267
-
268
- if (failCount > 0) {
269
- console.info(chalk.yellow(`\nCompleted with ${failCount} error(s). (${successCount}/${results.length} succeeded)`));
270
- } else {
271
- console.info(chalk.green('Done!'));
272
- }
273
- }
package/src/index.ts DELETED
@@ -1,14 +0,0 @@
1
- export { defineConfig } from './types.js';
2
- export type {
3
- Config,
4
- Auth,
5
- PasswordAuth,
6
- OAuthAuth,
7
- ApiTokenAuth,
8
- ProxyConfig,
9
- BasicAuthConfig,
10
- PfxConfig,
11
- AgentOptions,
12
- } from './types.js';
13
- export { loadConfig } from './config.js';
14
- export { generate } from './generate.js';
package/src/oauth.ts DELETED
@@ -1,14 +0,0 @@
1
- import { gyuma } from 'gyuma';
2
- import type { AgentOptions } from './types.js';
3
-
4
- // dts-genはフィールド情報を読み取るので k:app_settings:read が必要
5
- const DEFAULT_SCOPE = 'k:app_settings:read';
6
-
7
- export const getOauthToken = async (
8
- domain: string,
9
- scope: string | undefined,
10
- agentOptions: AgentOptions
11
- ): Promise<string> => {
12
- const token = await gyuma({ domain, scope: scope ?? DEFAULT_SCOPE, ...agentOptions }, true);
13
- return token;
14
- };
package/src/types.ts DELETED
@@ -1,65 +0,0 @@
1
- // 認証設定
2
- export type PasswordAuth = {
3
- type: 'password';
4
- // 環境変数 KINTONE_USERNAME, KINTONE_PASSWORD から取得
5
- };
6
-
7
- export type OAuthAuth = {
8
- type: 'oauth';
9
- scope?: string;
10
- };
11
-
12
- export type ApiTokenAuth = {
13
- type: 'api-token';
14
- token: string;
15
- };
16
-
17
- export type Auth = PasswordAuth | OAuthAuth | ApiTokenAuth;
18
-
19
- // プロキシ設定
20
- export type ProxyConfig = {
21
- host: string;
22
- port: number;
23
- };
24
-
25
- // Basic認証設定
26
- export type BasicAuthConfig = {
27
- username: string;
28
- password: string;
29
- };
30
-
31
- // クライアント証明書設定(PFX/PKCS#12形式)
32
- export type PfxConfig = {
33
- filepath: string;
34
- password: string;
35
- };
36
-
37
- // Gyuma用のエージェントオプション
38
- export type AgentOptions = {
39
- proxy?: string;
40
- pfx?: PfxConfig;
41
- };
42
-
43
- // メイン設定
44
- export type Config = {
45
- host: string; // Kintone環境のホスト(例: "example.cybozu.com")
46
- apps: Record<string, number>; // { appName: appId }
47
- auth: Auth;
48
- proxy?: ProxyConfig;
49
- basicAuth?: BasicAuthConfig;
50
- // クライアント証明書(OAuth使用時にGyumaへ渡す)
51
- pfx?: PfxConfig;
52
- // 出力ディレクトリ(デフォルト: "dts")
53
- outDir?: string;
54
- // プレビュー環境を参照する場合はtrue(デフォルト: false)
55
- preview?: boolean;
56
- // ゲストスペースID(ゲストスペース内のアプリの場合に指定)
57
- guestSpaceId?: number;
58
- // 生成する型のnamespace(デフォルト: "kintone.types")
59
- namespace?: string;
60
- // 生成後にPrettierでフォーマットするか(デフォルト: false)
61
- format?: boolean;
62
- };
63
-
64
- // 設定ファイルの型(defineConfigのため)
65
- export const defineConfig = (config: Config): Config => config;
@@ -1,15 +0,0 @@
1
- import { describe, it, expect } from '@jest/globals';
2
- import { resolve } from 'path';
3
- import { existsSync } from 'fs';
4
- import { loadConfig } from '../src/config';
5
-
6
- describe('loadConfig', () => {
7
- it('設定ファイルが見つからない場合はエラーをスローする', async () => {
8
- // 存在しないディレクトリを指定
9
- await expect(loadConfig('/non-existent-dir-12345')).rejects.toThrow('Config file not found');
10
- });
11
-
12
- it('エラーメッセージに設定ファイル名の候補を含む', async () => {
13
- await expect(loadConfig('/non-existent-dir-12345')).rejects.toThrow('trunks.config.ts');
14
- });
15
- });
@@ -1,32 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
2
- import { generate } from '../src/generate';
3
- import type { Config } from '../src/types';
4
-
5
- describe('generate', () => {
6
- const originalEnv = process.env;
7
-
8
- beforeEach(() => {
9
- process.env = { ...originalEnv };
10
- });
11
-
12
- afterEach(() => {
13
- process.env = originalEnv;
14
- });
15
-
16
- // NOTE: パスワード認証で環境変数が未設定の場合は標準入力を求めるため、
17
- // 自動テストでは検証が困難。手動テストで確認する。
18
-
19
- // NOTE: OAuth認証はGyumaがブラウザを開いて認証フローを実行するため、
20
- // 自動テストでは検証が困難。手動テストで確認する。
21
-
22
- it('appsが空の場合でもエラーにならない', async () => {
23
- const config: Config = {
24
- host: 'example.cybozu.com',
25
- apps: {},
26
- auth: { type: 'api-token', token: 'test' },
27
- };
28
-
29
- // appsが空なのでspawnは呼ばれず、正常終了するはず
30
- await expect(generate(config)).resolves.toBeUndefined();
31
- });
32
- });
@@ -1,116 +0,0 @@
1
- import { describe, it, expect } from '@jest/globals';
2
- import { defineConfig } from '../src/types';
3
- import type { Config } from '../src/types';
4
-
5
- describe('defineConfig', () => {
6
- it('パスワード認証の設定を返す', () => {
7
- const config = defineConfig({
8
- host: 'example.cybozu.com',
9
- apps: { customer: 1, order: 2 },
10
- auth: { type: 'password' },
11
- });
12
-
13
- expect(config.host).toBe('example.cybozu.com');
14
- expect(config.apps).toEqual({ customer: 1, order: 2 });
15
- expect(config.auth.type).toBe('password');
16
- });
17
-
18
- it('APIトークン認証の設定を返す', () => {
19
- const config = defineConfig({
20
- host: 'example.cybozu.com',
21
- apps: { customer: 1 },
22
- auth: { type: 'api-token', token: 'test-token' },
23
- });
24
-
25
- expect(config.auth.type).toBe('api-token');
26
- if (config.auth.type === 'api-token') {
27
- expect(config.auth.token).toBe('test-token');
28
- }
29
- });
30
-
31
- it('OAuth認証の設定を返す', () => {
32
- const config = defineConfig({
33
- host: 'example.cybozu.com',
34
- apps: { customer: 1 },
35
- auth: { type: 'oauth', scope: 'k:app_record:read' },
36
- });
37
-
38
- expect(config.auth.type).toBe('oauth');
39
- if (config.auth.type === 'oauth') {
40
- expect(config.auth.scope).toBe('k:app_record:read');
41
- }
42
- });
43
-
44
- it('オプション設定を含む設定を返す', () => {
45
- const config = defineConfig({
46
- host: 'example.cybozu.com',
47
- apps: { customer: 1 },
48
- auth: { type: 'password' },
49
- proxy: { host: 'proxy.example.com', port: 8080 },
50
- basicAuth: { username: 'user', password: 'pass' },
51
- outDir: 'types',
52
- });
53
-
54
- expect(config.proxy).toEqual({ host: 'proxy.example.com', port: 8080 });
55
- expect(config.basicAuth).toEqual({ username: 'user', password: 'pass' });
56
- expect(config.outDir).toBe('types');
57
- });
58
-
59
- it('プレビュー環境の設定を返す', () => {
60
- const config = defineConfig({
61
- host: 'example.cybozu.com',
62
- apps: { customer: 1 },
63
- auth: { type: 'api-token', token: 'test-token' },
64
- preview: true,
65
- });
66
-
67
- expect(config.preview).toBe(true);
68
- });
69
-
70
- it('ゲストスペースの設定を返す', () => {
71
- const config = defineConfig({
72
- host: 'example.cybozu.com',
73
- apps: { customer: 1 },
74
- auth: { type: 'api-token', token: 'test-token' },
75
- guestSpaceId: 5,
76
- });
77
-
78
- expect(config.guestSpaceId).toBe(5);
79
- });
80
-
81
- it('namespaceの設定を返す', () => {
82
- const config = defineConfig({
83
- host: 'example.cybozu.com',
84
- apps: { customer: 1 },
85
- auth: { type: 'api-token', token: 'test-token' },
86
- namespace: 'myapp.types',
87
- });
88
-
89
- expect(config.namespace).toBe('myapp.types');
90
- });
91
-
92
- it('型チェックが正しく機能する', () => {
93
- // 型レベルでのテスト(コンパイルが通ればOK)
94
- const passwordConfig: Config = {
95
- host: 'example.cybozu.com',
96
- apps: { app1: 1 },
97
- auth: { type: 'password' },
98
- };
99
-
100
- const apiTokenConfig: Config = {
101
- host: 'example.cybozu.com',
102
- apps: { app1: 1 },
103
- auth: { type: 'api-token', token: 'xxx' },
104
- };
105
-
106
- const oauthConfig: Config = {
107
- host: 'example.cybozu.com',
108
- apps: { app1: 1 },
109
- auth: { type: 'oauth' },
110
- };
111
-
112
- expect(passwordConfig).toBeDefined();
113
- expect(apiTokenConfig).toBeDefined();
114
- expect(oauthConfig).toBeDefined();
115
- });
116
- });
package/trunks.config.ts DELETED
@@ -1,19 +0,0 @@
1
- import { defineConfig } from './src/types'
2
-
3
- export default defineConfig({
4
- host: 'the-red.cybozu.com',
5
- format: true,
6
- preview: false,
7
- apps: {
8
- activity: 265,
9
- customer: 266,
10
- // project: 267,
11
- },
12
- // 以下のいずれかの認証方式を選択
13
- auth: { type: 'oauth' },
14
- // または
15
- // auth: {
16
- // type: 'api-token',
17
- // token: '6mrxShdoQdFdN943q9Wg50nr7LxdwMsy1TTFkttF,K6t3rpw8PGoKQm7UPtiG0lyZawUyK0SPyjy4x7wJ',
18
- // }, // 環境変数 KINTONE_USERNAME, KINTONE_PASSWORD を使用
19
- })
package/tsconfig.json DELETED
@@ -1,16 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "NodeNext",
5
- "moduleResolution": "NodeNext",
6
- "outDir": "dist",
7
- "rootDir": "src",
8
- "strict": true,
9
- "isolatedModules": true,
10
- "esModuleInterop": true,
11
- "skipLibCheck": true,
12
- "declaration": true
13
- },
14
- "include": ["src/**/*"],
15
- "exclude": ["node_modules", "dist"]
16
- }