@asagiri-design/labels-config 0.2.2

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/docs/API.md ADDED
@@ -0,0 +1,309 @@
1
+ # API Reference
2
+
3
+ ## Types
4
+
5
+ ### `LabelConfig`
6
+
7
+ Main label configuration interface.
8
+
9
+ ```typescript
10
+ interface LabelConfig {
11
+ name: string // Label name (unique, 1-50 chars)
12
+ color: HexColor // Hex color code (6 digits)
13
+ description: string // Description (1-200 chars)
14
+ }
15
+ ```
16
+
17
+ ### `LabelRegistry`
18
+
19
+ Container for label collections with metadata.
20
+
21
+ ```typescript
22
+ interface LabelRegistry {
23
+ version: string
24
+ timestamp?: string
25
+ labels: LabelConfig[] | LabelCategory[]
26
+ metadata?: Record<string, unknown>
27
+ }
28
+ ```
29
+
30
+ ### `LabelCategory`
31
+
32
+ Grouping mechanism for related labels.
33
+
34
+ ```typescript
35
+ interface LabelCategory {
36
+ category: string
37
+ labels: LabelConfig[]
38
+ }
39
+ ```
40
+
41
+ ## Classes
42
+
43
+ ### `LabelManager`
44
+
45
+ Main class for managing label configurations in memory.
46
+
47
+ #### Constructor
48
+
49
+ ```typescript
50
+ new LabelManager(options?: LabelManagerOptions)
51
+ ```
52
+
53
+ Options:
54
+ - `labels?: LabelConfig[]` - Initial labels to load
55
+ - `strict?: boolean` - Strict validation mode (default: false)
56
+
57
+ #### Methods
58
+
59
+ ##### `loadLabels(labels: LabelConfig[]): void`
60
+
61
+ Load labels from array. Clears previous labels.
62
+
63
+ ##### `loadRegistry(registry: LabelRegistry): void`
64
+
65
+ Load labels from registry object. Handles both flat and categorized formats.
66
+
67
+ ##### `addLabel(label: LabelConfig): void`
68
+
69
+ Add a new label. Throws if name already exists.
70
+
71
+ ##### `updateLabel(name: string, updates: Partial<LabelConfig>): void`
72
+
73
+ Update an existing label. Throws if label not found.
74
+
75
+ ##### `removeLabel(name: string): void`
76
+
77
+ Remove a label by name.
78
+
79
+ ##### `getLabel(name: string): LabelConfig | undefined`
80
+
81
+ Get a label by name. Case-insensitive.
82
+
83
+ ##### `hasLabel(name: string): boolean`
84
+
85
+ Check if label exists. Case-insensitive.
86
+
87
+ ##### `getAllLabels(): LabelConfig[]`
88
+
89
+ Get all labels as array.
90
+
91
+ ##### `count(): number`
92
+
93
+ Get total number of labels.
94
+
95
+ ##### `export(): LabelConfig[]`
96
+
97
+ Export all labels as array.
98
+
99
+ ##### `exportRegistry(version?: string, metadata?: Record<string, unknown>): LabelRegistry`
100
+
101
+ Export as registry object with metadata.
102
+
103
+ ##### `search(query: string): LabelConfig[]`
104
+
105
+ Search labels by name or description. Case-insensitive.
106
+
107
+ ##### `findByColor(color: string): LabelConfig[]`
108
+
109
+ Find labels by color code. Case-insensitive.
110
+
111
+ ##### `clear(): void`
112
+
113
+ Clear all labels.
114
+
115
+ ##### `validate(): { valid: boolean; duplicates: string[] }`
116
+
117
+ Validate current state. Returns duplicate label names.
118
+
119
+ ### `GitHubLabelSync`
120
+
121
+ Synchronize labels with GitHub repositories.
122
+
123
+ #### Constructor
124
+
125
+ ```typescript
126
+ new GitHubLabelSync(options: GitHubSyncOptions)
127
+ ```
128
+
129
+ Options:
130
+ - `token: string` - GitHub personal access token (required)
131
+ - `owner: string` - Repository owner (required)
132
+ - `repo: string` - Repository name (required)
133
+ - `dryRun?: boolean` - Dry run mode (default: false)
134
+ - `deleteExtra?: boolean` - Delete extra labels (default: false)
135
+ - `verbose?: boolean` - Verbose logging (default: false)
136
+
137
+ #### Methods
138
+
139
+ ##### `async syncLabels(labels: LabelConfig[]): Promise<SyncResult>`
140
+
141
+ Sync local labels to GitHub repository.
142
+
143
+ Returns:
144
+ ```typescript
145
+ interface SyncResult {
146
+ created: LabelConfig[]
147
+ updated: LabelConfig[]
148
+ deleted: string[]
149
+ unchanged: LabelConfig[]
150
+ errors: Array<{ name: string; error: string }>
151
+ }
152
+ ```
153
+
154
+ ##### `async fetchLabels(): Promise<LabelConfig[]>`
155
+
156
+ Fetch all labels from GitHub repository.
157
+
158
+ ##### `async deleteLabel(name: string): Promise<void>`
159
+
160
+ Delete a single label from repository.
161
+
162
+ ##### `async updateLabel(name: string, updates: Partial<LabelConfig>): Promise<void>`
163
+
164
+ Update a single label in repository.
165
+
166
+ ### `ConfigLoader`
167
+
168
+ Load configurations from various sources.
169
+
170
+ #### Constructor
171
+
172
+ ```typescript
173
+ new ConfigLoader(options?: LoaderOptions)
174
+ ```
175
+
176
+ Options:
177
+ - `strict?: boolean` - Strict validation mode (default: true)
178
+
179
+ #### Methods
180
+
181
+ ##### `loadFromJSON(data: unknown): LabelConfig[]`
182
+
183
+ Load labels from JSON object or array.
184
+
185
+ ##### `loadFromString(jsonString: string): LabelConfig[]`
186
+
187
+ Load labels from JSON string.
188
+
189
+ ##### `loadRegistry(registry: LabelRegistry): LabelConfig[]`
190
+
191
+ Load labels from registry object.
192
+
193
+ ## Functions
194
+
195
+ ### Validation
196
+
197
+ #### `validateLabel(label: unknown): LabelConfig`
198
+
199
+ Validate and normalize a single label.
200
+
201
+ Throws: `ZodError` if validation fails.
202
+
203
+ #### `validateLabels(labels: unknown): LabelConfig[]`
204
+
205
+ Validate and normalize multiple labels.
206
+
207
+ Throws: `ZodError` if validation fails.
208
+
209
+ #### `validateRegistry(registry: unknown): LabelRegistry`
210
+
211
+ Validate complete registry object.
212
+
213
+ Throws: `ZodError` if validation fails.
214
+
215
+ #### `validateWithDetails(labels: unknown): ValidationResult`
216
+
217
+ Comprehensive validation with detailed error reporting.
218
+
219
+ ```typescript
220
+ interface ValidationResult {
221
+ valid: boolean
222
+ labels: LabelConfig[]
223
+ errors: {
224
+ duplicateNames?: string[]
225
+ duplicateColors?: string[]
226
+ validationErrors?: ZodError[]
227
+ }
228
+ }
229
+ ```
230
+
231
+ #### `checkDuplicateNames(labels: LabelConfig[]): string[]`
232
+
233
+ Find duplicate label names.
234
+
235
+ #### `checkDuplicateColors(labels: LabelConfig[]): string[]`
236
+
237
+ Find duplicate colors.
238
+
239
+ ### Type Guards
240
+
241
+ #### `isCategorized(labels: LabelConfig[] | LabelCategory[]): labels is LabelCategory[]`
242
+
243
+ Check if labels are categorized.
244
+
245
+ #### `flattenLabels(labels: LabelConfig[] | LabelCategory[]): LabelConfig[]`
246
+
247
+ Flatten categorized labels to flat array.
248
+
249
+ ### Templates
250
+
251
+ #### `getTemplate(name: TemplateName): LabelConfig[]`
252
+
253
+ Get predefined template by name.
254
+
255
+ Templates:
256
+ - `minimal` - Minimal set for getting started
257
+ - `github` - GitHub standard labels
258
+ - `prod` - Production project labels (Japanese)
259
+ - `prod-en` - Production project labels (English)
260
+ - `prod-ja` - Production project labels (Japanese, alias)
261
+ - `react` - React ecosystem labels
262
+ - `vue` - Vue.js ecosystem labels
263
+ - `frontend` - General frontend development labels
264
+ - `agile` - Agile/Scrum labels
265
+
266
+ #### `listTemplates(): TemplateName[]`
267
+
268
+ List all available template names.
269
+
270
+ ## Error Handling
271
+
272
+ All validation functions throw `ZodError` on invalid input:
273
+
274
+ ```typescript
275
+ import { validateLabel } from '@boxpistols/labels-config'
276
+
277
+ try {
278
+ validateLabel({ name: 'test', color: 'invalid' })
279
+ } catch (error) {
280
+ if (error instanceof ZodError) {
281
+ error.errors.forEach(err => {
282
+ console.log(err.path, err.message)
283
+ })
284
+ }
285
+ }
286
+ ```
287
+
288
+ ## Strict Mode
289
+
290
+ When `strict: true` is passed to managers/loaders:
291
+ - All errors throw immediately
292
+ - No partial results are returned
293
+ - Validation is mandatory
294
+
295
+ When `strict: false`:
296
+ - Some errors are warnings
297
+ - Partial results may be returned
298
+ - Validation is optional
299
+
300
+ ## Type Safety
301
+
302
+ All APIs are fully typed with TypeScript. No `any` types are used.
303
+
304
+ ```typescript
305
+ // Full type inference
306
+ const manager = new LabelManager({ labels: [] })
307
+ const label = manager.getLabel('test') // Type: LabelConfig | undefined
308
+ const all = manager.getAllLabels() // Type: LabelConfig[]
309
+ ```
@@ -0,0 +1,305 @@
1
+ # Getting Started with @boxpistols/labels-config
2
+
3
+ ## Installation
4
+
5
+ ### npm
6
+
7
+ ```bash
8
+ npm install @boxpistols/labels-config
9
+ ```
10
+
11
+ ### yarn
12
+
13
+ ```bash
14
+ yarn add @boxpistols/labels-config
15
+ ```
16
+
17
+ ### pnpm
18
+
19
+ ```bash
20
+ pnpm add @boxpistols/labels-config
21
+ ```
22
+
23
+ ### CDN
24
+
25
+ ```html
26
+ <script src="https://cdn.jsdelivr.net/npm/@boxpistols/labels-config/dist/index.umd.js"></script>
27
+ <script>
28
+ const { validateLabels } = window.LabelsConfig
29
+ </script>
30
+ ```
31
+
32
+ ## Basic Usage
33
+
34
+ ### Validate Labels
35
+
36
+ ```typescript
37
+ import { validateLabels } from '@boxpistols/labels-config'
38
+
39
+ const labels = [
40
+ {
41
+ name: 'bug',
42
+ color: 'ff0000',
43
+ description: 'Something is broken'
44
+ },
45
+ {
46
+ name: 'feature',
47
+ color: '00ff00',
48
+ description: 'New feature or request'
49
+ }
50
+ ]
51
+
52
+ // Validate labels
53
+ const validated = validateLabels(labels)
54
+ console.log(validated) // Returns validated and normalized labels
55
+ ```
56
+
57
+ ### Using Label Manager
58
+
59
+ ```typescript
60
+ import { LabelManager } from '@boxpistols/labels-config'
61
+
62
+ const manager = new LabelManager({
63
+ labels: [
64
+ { name: 'bug', color: 'ff0000', description: 'Bug' },
65
+ { name: 'feature', color: '00ff00', description: 'Feature' }
66
+ ]
67
+ })
68
+
69
+ // Add a label
70
+ manager.addLabel({ name: 'docs', color: '0000ff', description: 'Documentation' })
71
+
72
+ // Get all labels
73
+ const allLabels = manager.getAllLabels()
74
+
75
+ // Search labels
76
+ const results = manager.search('feature')
77
+
78
+ // Export
79
+ const exported = manager.export()
80
+ const registry = manager.exportRegistry('1.0.0')
81
+ ```
82
+
83
+ ### GitHub Synchronization
84
+
85
+ ```typescript
86
+ import { GitHubLabelSync } from '@boxpistols/labels-config/github'
87
+
88
+ const sync = new GitHubLabelSync({
89
+ token: process.env.GITHUB_TOKEN,
90
+ owner: 'your-org',
91
+ repo: 'your-repo',
92
+ verbose: true
93
+ })
94
+
95
+ // Sync local labels to GitHub
96
+ const result = await sync.syncLabels([
97
+ { name: 'bug', color: 'ff0000', description: 'Bug' },
98
+ { name: 'feature', color: '00ff00', description: 'Feature' }
99
+ ])
100
+
101
+ console.log(`Created: ${result.created.length}`)
102
+ console.log(`Updated: ${result.updated.length}`)
103
+ console.log(`Deleted: ${result.deleted.length}`)
104
+ ```
105
+
106
+ ### Load Configuration from File
107
+
108
+ ```typescript
109
+ import { ConfigLoader } from '@boxpistols/labels-config/config'
110
+ import { promises as fs } from 'fs'
111
+
112
+ const loader = new ConfigLoader()
113
+ const fileContent = await fs.readFile('labels.json', 'utf-8')
114
+ const labels = loader.loadFromString(fileContent)
115
+ ```
116
+
117
+ ### Using Templates
118
+
119
+ ```typescript
120
+ import { CONFIG_TEMPLATES, listTemplates } from '@boxpistols/labels-config/config'
121
+
122
+ // Get production template (Japanese)
123
+ const prodLabels = CONFIG_TEMPLATES.prod
124
+
125
+ // List all available templates
126
+ const templates = listTemplates() // ['minimal', 'github', 'prod', 'prod-en', 'prod-ja', 'react', 'vue', 'frontend', 'agile']
127
+
128
+ // Use GitHub standard template
129
+ const githubLabels = CONFIG_TEMPLATES.github
130
+ ```
131
+
132
+ ## CLI Usage
133
+
134
+ ### Initialize Configuration
135
+
136
+ ```bash
137
+ # Create from template
138
+ labels-config init prod-ja --file labels.json
139
+
140
+ # Available templates: minimal, github, prod, prod-en, prod-ja, react, vue, frontend, agile
141
+ labels-config init github --file labels.json
142
+ ```
143
+
144
+ ### Validate Configuration
145
+
146
+ ```bash
147
+ labels-config validate ./labels.json
148
+ ```
149
+
150
+ ### Sync to GitHub
151
+
152
+ ```bash
153
+ labels-config sync \
154
+ --token $GITHUB_TOKEN \
155
+ --owner your-org \
156
+ --repo your-repo \
157
+ --file labels.json \
158
+ --verbose
159
+ ```
160
+
161
+ ### Dry Run
162
+
163
+ ```bash
164
+ # See what would be changed without actually changing
165
+ labels-config sync \
166
+ --token $GITHUB_TOKEN \
167
+ --owner your-org \
168
+ --repo your-repo \
169
+ --file labels.json \
170
+ --dry-run \
171
+ --verbose
172
+ ```
173
+
174
+ ### Export from Repository
175
+
176
+ ```bash
177
+ # Export existing labels from GitHub
178
+ labels-config export \
179
+ --token $GITHUB_TOKEN \
180
+ --owner your-org \
181
+ --repo your-repo \
182
+ --file exported-labels.json
183
+ ```
184
+
185
+ ## Configuration File Format
186
+
187
+ ### Flat List
188
+
189
+ ```json
190
+ [
191
+ {
192
+ "name": "bug",
193
+ "color": "ff0000",
194
+ "description": "Something is broken"
195
+ },
196
+ {
197
+ "name": "feature",
198
+ "color": "00ff00",
199
+ "description": "New feature"
200
+ }
201
+ ]
202
+ ```
203
+
204
+ ### Registry Format
205
+
206
+ ```json
207
+ {
208
+ "version": "1.0.0",
209
+ "timestamp": "2024-11-12T00:00:00Z",
210
+ "labels": [
211
+ {
212
+ "name": "bug",
213
+ "color": "ff0000",
214
+ "description": "Something is broken"
215
+ }
216
+ ],
217
+ "metadata": {
218
+ "project": "my-project"
219
+ }
220
+ }
221
+ ```
222
+
223
+ ### Categorized Labels
224
+
225
+ ```json
226
+ {
227
+ "version": "1.0.0",
228
+ "labels": [
229
+ {
230
+ "category": "Type",
231
+ "labels": [
232
+ {
233
+ "name": "bug",
234
+ "color": "ff0000",
235
+ "description": "Bug fix"
236
+ },
237
+ {
238
+ "name": "feature",
239
+ "color": "00ff00",
240
+ "description": "New feature"
241
+ }
242
+ ]
243
+ },
244
+ {
245
+ "category": "Priority",
246
+ "labels": [
247
+ {
248
+ "name": "priority:high",
249
+ "color": "ff0000",
250
+ "description": "High priority"
251
+ }
252
+ ]
253
+ }
254
+ ]
255
+ }
256
+ ```
257
+
258
+ ## Advanced Usage
259
+
260
+ ### Error Handling
261
+
262
+ ```typescript
263
+ import { validateWithDetails } from '@boxpistols/labels-config'
264
+
265
+ const result = validateWithDetails(data)
266
+
267
+ if (!result.valid) {
268
+ console.log('Duplicate names:', result.errors.duplicateNames)
269
+ console.log('Duplicate colors:', result.errors.duplicateColors)
270
+ console.log('Validation errors:', result.errors.validationErrors)
271
+ }
272
+ ```
273
+
274
+ ### Custom Validation
275
+
276
+ ```typescript
277
+ import { checkDuplicateNames, checkDuplicateColors } from '@boxpistols/labels-config'
278
+
279
+ const duplicateNames = checkDuplicateNames(labels)
280
+ const duplicateColors = checkDuplicateColors(labels)
281
+
282
+ if (duplicateNames.length > 0) {
283
+ console.warn('Found duplicate label names:', duplicateNames)
284
+ }
285
+ ```
286
+
287
+ ### Search and Filter
288
+
289
+ ```typescript
290
+ // Search by name or description
291
+ const results = manager.search('bug')
292
+
293
+ // Find by color
294
+ const redLabels = manager.findByColor('ff0000')
295
+
296
+ // Case-insensitive retrieval
297
+ const label = manager.getLabel('BUG') // Returns label named 'bug'
298
+ ```
299
+
300
+ ## Next Steps
301
+
302
+ - Read the [API documentation](./API.md)
303
+ - Check [examples](../examples)
304
+ - View [CLI documentation](./CLI.md)
305
+ - See [templates documentation](./TEMPLATES.md)
package/package.json ADDED
@@ -0,0 +1,87 @@
1
+ {
2
+ "name": "@asagiri-design/labels-config",
3
+ "version": "0.2.2",
4
+ "description": "Terminal-first label management system for GitHub repositories using gh CLI - No token required",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.esm.js",
7
+ "types": "dist/index.d.ts",
8
+ "browser": "dist/index.umd.js",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.esm.js",
13
+ "require": "./dist/index.js"
14
+ },
15
+ "./config": {
16
+ "types": "./dist/config/index.d.ts",
17
+ "import": "./dist/config/index.esm.js",
18
+ "require": "./dist/config/index.js"
19
+ },
20
+ "./github": {
21
+ "types": "./dist/github/index.d.ts",
22
+ "import": "./dist/github/index.esm.js",
23
+ "require": "./dist/github/index.js"
24
+ }
25
+ },
26
+ "bin": {
27
+ "labels-config": "./dist/cli.js"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "templates",
32
+ "docs"
33
+ ],
34
+ "sideEffects": false,
35
+ "scripts": {
36
+ "build": "tsup src/index.ts src/config/index.ts src/github/index.ts src/cli.ts --format cjs,esm --dts --shims",
37
+ "build:umd": "tsup src/index.ts --format iife --dts --globalName LabelsConfig --outDir dist",
38
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
39
+ "type-check": "tsc --noEmit",
40
+ "test": "vitest",
41
+ "test:ui": "vitest --ui",
42
+ "test:coverage": "vitest --coverage",
43
+ "lint": "eslint src --fix",
44
+ "cli": "tsx src/cli.ts",
45
+ "prepublishOnly": "npm run build",
46
+ "version": "node -e \"const fs=require('fs');const v=require('./package.json').version;fs.writeFileSync('src/version.ts','export const version = \\''+v+'\\'\\n')\" && git add src/version.ts",
47
+ "release:patch": "npm version patch -m 'Release v%s [skip ci]' && npm publish --access public && git push --follow-tags",
48
+ "release:minor": "npm version minor -m 'Release v%s [skip ci]' && npm publish --access public && git push --follow-tags",
49
+ "release:major": "npm version major -m 'Release v%s [skip ci]' && npm publish --access public && git push --follow-tags",
50
+ "release:beta": "npm version prerelease --preid=beta -m 'Release v%s [skip ci]' && npm publish --access public --tag beta && git push --follow-tags"
51
+ },
52
+ "keywords": [
53
+ "labels",
54
+ "github",
55
+ "issue-tracking",
56
+ "config",
57
+ "management",
58
+ "schema",
59
+ "validation"
60
+ ],
61
+ "author": "BoxPistols",
62
+ "license": "MIT",
63
+ "repository": {
64
+ "type": "git",
65
+ "url": "https://github.com/BoxPistols/labels-config.git"
66
+ },
67
+ "bugs": {
68
+ "url": "https://github.com/BoxPistols/labels-config/issues"
69
+ },
70
+ "homepage": "https://github.com/BoxPistols/labels-config#readme",
71
+ "devDependencies": {
72
+ "@types/node": "^20.10.0",
73
+ "@typescript-eslint/eslint-plugin": "^6.13.0",
74
+ "@typescript-eslint/parser": "^6.13.0",
75
+ "eslint": "^8.54.0",
76
+ "tsup": "^8.0.2",
77
+ "tsx": "^4.7.0",
78
+ "typescript": "^5.3.3",
79
+ "vitest": "^1.0.4"
80
+ },
81
+ "dependencies": {
82
+ "zod": "^3.22.4"
83
+ },
84
+ "engines": {
85
+ "node": ">=18.0.0"
86
+ }
87
+ }