@homebridge-plugins/homebridge-august 3.2.2 → 3.3.0-beta.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.
Files changed (46) hide show
  1. package/.github/copilot-instructions.md +200 -0
  2. package/.github/npm-version-script-esm.js +97 -0
  3. package/CHANGELOG.md +44 -9
  4. package/dist/Platform.Base.d.ts +288 -0
  5. package/dist/Platform.Base.d.ts.map +1 -0
  6. package/dist/Platform.Base.js +797 -0
  7. package/dist/Platform.Base.js.map +1 -0
  8. package/dist/Platform.HAP.d.ts +27 -0
  9. package/dist/Platform.HAP.d.ts.map +1 -0
  10. package/dist/Platform.HAP.js +68 -0
  11. package/dist/Platform.HAP.js.map +1 -0
  12. package/dist/connectivity-manager.d.ts +3 -3
  13. package/dist/connectivity-manager.d.ts.map +1 -1
  14. package/dist/connectivity-manager.js +57 -14
  15. package/dist/connectivity-manager.js.map +1 -1
  16. package/dist/devices/device.d.ts +5 -16
  17. package/dist/devices/device.d.ts.map +1 -1
  18. package/dist/devices/device.js +18 -75
  19. package/dist/devices/device.js.map +1 -1
  20. package/dist/devices/lock.d.ts +3 -3
  21. package/dist/devices/lock.d.ts.map +1 -1
  22. package/dist/devices/lock.js +41 -41
  23. package/dist/devices/lock.js.map +1 -1
  24. package/dist/index.js +4 -4
  25. package/dist/index.js.map +1 -1
  26. package/dist/package.json +10 -8
  27. package/dist/platform.matter.d.ts +18 -20
  28. package/dist/platform.matter.d.ts.map +1 -1
  29. package/dist/platform.matter.js +74 -80
  30. package/dist/platform.matter.js.map +1 -1
  31. package/docs/variables/default.html +1 -1
  32. package/package.json +10 -8
  33. package/test/device.test.ts +384 -0
  34. package/test/{platform.test.ts → platform.hap.test.ts} +23 -23
  35. package/test/platform.polling.test.ts +5 -5
  36. package/.copilot/instructions.md +0 -261
  37. package/.github/scripts/compute-version-override.cjs +0 -51
  38. package/.github/scripts/normalize-changelog-header.cjs +0 -86
  39. package/.github/scripts/npm-version-script-esm-auto.js +0 -201
  40. package/.github/scripts/resolve-version-bump-esm.js +0 -118
  41. package/dist/platform.d.ts +0 -146
  42. package/dist/platform.d.ts.map +0 -1
  43. package/dist/platform.js +0 -755
  44. package/dist/platform.js.map +0 -1
  45. package/test/connectivity-manager.test.ts +0 -590
  46. package/test/platform.matter.test.ts +0 -660
@@ -1,261 +0,0 @@
1
- # Copilot Instructions for Homebridge August Plugin
2
-
3
- ## Repository Overview
4
-
5
- This is a Homebridge plugin that provides HomeKit integration for August and Yale smart locks. The plugin is written in TypeScript with ES modules and follows Homebridge platform patterns.
6
-
7
- **Branch Strategy**: This repository uses a beta-first development workflow where all changes must target beta branches before being merged to main. Version bumps are determined by issue labels (patch/minor/major) that must be set before assigning work to Copilot.
8
-
9
- ## Architecture
10
-
11
- - **Plugin Type**: Homebridge dynamic platform plugin
12
- - **Language**: TypeScript with ES modules (ES2022)
13
- - **Runtime**: Node.js 20+ or 22+
14
- - **Build System**: TypeScript compiler (tsc) with bundler module resolution
15
- - **Testing**: Vitest with coverage support
16
- - **Linting**: ESLint with Antfu config and custom rules
17
- - **Documentation**: TypeDoc with modern theme
18
- - **External API**: august-yale package for August/Yale API communication
19
-
20
- ## Key Files and Directories
21
-
22
- - `src/` - TypeScript source code
23
- - `index.ts` - Plugin entry point that registers the platform
24
- - `platform.ts` - Main platform class (AugustPlatform)
25
- - `settings.ts` - Configuration interfaces and constants
26
- - `devices/` - Device-specific implementations
27
- - `lock.ts` - Lock mechanism implementation
28
- - `device.ts` - Base device class
29
- - `homebridge-ui/` - Custom UI for Homebridge Config UI X
30
- - `*.test.ts` - Test files (Vitest)
31
- - `dist/` - Compiled JavaScript output (generated, excluded from git)
32
- - `config.schema.json` - Homebridge configuration schema with custom UI
33
- - `package.json` - Dependencies and npm scripts
34
- - `tsconfig.json` - TypeScript configuration (ES2022, bundler resolution)
35
- - `eslint.config.js` - ESLint configuration with Antfu base and custom rules
36
-
37
- ## Development Workflow
38
-
39
- ### Branch Strategy and PR Workflow
40
-
41
- **IMPORTANT**: All pull requests must target a beta branch first, never directly to the main branch.
42
-
43
- #### Beta Branch Requirements
44
- - All PRs must be directed to a branch that starts with "beta-"
45
- - Beta branches should be named `beta-X.Y.Z` based on the expected version bump
46
- - If no appropriate beta branch exists, create one based on the next possible version:
47
- - **patch** releases (bug fixes): `beta-X.Y.Z+1` (e.g., current 3.0.2 → beta-3.0.3)
48
- - **minor** releases (new features): `beta-X.Y+1.0` (e.g., current 3.0.2 → beta-3.1.0)
49
- - **major** releases (breaking changes): `beta-X+1.0.0` (e.g., current 3.0.2 → beta-4.0.0)
50
-
51
- #### Required Labels
52
- Before assigning any issue to Copilot, the following labels **must** be set to determine the version bump:
53
- - `patch` - for bug fixes and minor improvements
54
- - `minor` - for new features and enhancements
55
- - `major` - for breaking changes
56
-
57
- #### Workflow Steps
58
- 1. Ensure proper label (patch/minor/major) is set on the issue
59
- 2. Identify or create appropriate beta branch based on the label
60
- 3. Target all development work and PRs to the beta branch
61
- 4. Once beta testing is complete, merge beta branch to main for release
62
-
63
- ### Building
64
- ```bash
65
- npm run build # Full build: clean + compile + copy UI files
66
- npm run clean # Remove dist directory
67
- npm run watch # Build + link + watch with nodemon
68
- ```
69
-
70
- ### Testing
71
- ```bash
72
- npm test # Run Vitest tests once
73
- npm run test:watch # Run tests in watch mode
74
- npm run test-coverage # Run tests with coverage report
75
- ```
76
-
77
- ### Linting
78
- ```bash
79
- npm run lint # Check for ESLint issues
80
- npm run lint:fix # Auto-fix ESLint issues
81
- ```
82
-
83
- ### Documentation
84
- ```bash
85
- npm run docs # Generate TypeDoc documentation
86
- npm run docs:lint # Validate documentation (treat warnings as errors)
87
- npm run docs:theme # Generate docs with default-modern theme
88
- ```
89
-
90
- ### Development Setup
91
- ```bash
92
- npm run watch # Start development with hot reload
93
- npm run plugin-ui # Copy UI files to dist
94
- ```
95
-
96
- ## Coding Standards
97
-
98
- 1. **TypeScript**:
99
- - ES2022 target with bundler module resolution
100
- - Strict mode enabled (except noImplicitAny: false)
101
- - Use ES modules with `.js` file extensions in imports
102
- - Generate declaration files and source maps
103
-
104
- 2. **ESLint**:
105
- - Antfu ESLint config as base
106
- - Custom rules for import ordering (perfectionist)
107
- - JSDoc alignment enforcement
108
- - Multi-line curly braces only
109
- - Consistent quote props
110
- - Test-specific rules (no-only-tests)
111
-
112
- 3. **Code Style**:
113
- - Use 1TBS brace style with single line allowed
114
- - Sort imports by type (builtin, external, internal, relative)
115
- - Sort named imports and exports
116
- - Use proper JSDoc formatting and alignment
117
-
118
- 4. **Homebridge Patterns**:
119
- - Implement DynamicPlatformPlugin interface
120
- - Use proper platform registration in index.ts
121
- - Follow accessory lifecycle management
122
- - Implement proper logging with Homebridge logger
123
- - Use PlatformAccessory for device management
124
-
125
- 5. **Error Handling**:
126
- - Proper try-catch blocks for async operations
127
- - Meaningful error messages with context
128
- - Graceful degradation for API failures
129
-
130
- ## Homebridge Specifics
131
-
132
- - **Platform Type**: Dynamic platform plugin (not accessory plugin)
133
- - **Platform Name**: "August" (defined in settings.ts)
134
- - **Plugin Name**: "homebridge-august" (must match package.json name)
135
- - **Configuration Schema**: Uses config.schema.json with custom UI
136
- - **Custom UI**: Located in src/homebridge-ui/ with custom configuration interface
137
- - **Platform Class**: AugustPlatform extends DynamicPlatformPlugin
138
- - **Device Management**: Uses PlatformAccessory for caching and state management
139
- - **HAP Services**: Implement LockMechanism service for smart locks
140
- - **Authentication**: Handle August API authentication with validation codes
141
- - **Rate Limiting**: Implement proper refresh/update/push rates for API calls
142
-
143
- ## August/Yale Integration
144
-
145
- - **API Package**: Uses `august-yale` npm package for API communication
146
- - **Authentication Flow**:
147
- 1. User provides augustId (email/phone) and password
148
- 2. System sends validation code to user
149
- 3. User enters validation code to complete authentication
150
- 4. API credentials are stored and used for subsequent requests
151
- - **Device Types**: Support multiple lock types (August Smart Lock, Yale Assure Lock variants)
152
- - **State Synchronization**: Keep HomeKit state in sync with August/Yale API
153
- - **Polling**: Implement configurable refresh rates for device state updates
154
- - **Configuration**: Use credentials interface for API authentication settings
155
-
156
- ## When Making Changes
157
-
158
- **CRITICAL**: Before starting any work, ensure the issue has the appropriate label (patch/minor/major) and target the correct beta branch.
159
-
160
- 0. **Branch Targeting**:
161
- - Never target PRs directly to main branch
162
- - Always target a beta branch (beta-X.Y.Z format)
163
- - Create beta branch if none exists for the expected version
164
- - Use issue labels to determine version bump:
165
- - `patch`: Bug fixes → beta-X.Y.Z+1
166
- - `minor`: New features → beta-X.Y+1.0
167
- - `major`: Breaking changes → beta-X+1.0.0
168
-
169
- 1. **Dependencies**:
170
- - Only add well-maintained dependencies
171
- - Keep runtime dependencies minimal
172
- - Update package.json and package-lock.json properly
173
- - Consider bundle size impact
174
-
175
- 2. **Breaking Changes**:
176
- - Follow semantic versioning
177
- - Update CHANGELOG.md appropriately
178
- - Consider migration paths for users
179
- - Test with multiple Homebridge versions
180
- - **Must** use `major` label and target appropriate beta-X+1.0.0 branch
181
-
182
- 3. **Configuration**:
183
- - Update config.schema.json for new options
184
- - Maintain backward compatibility
185
- - Add proper validation and defaults
186
- - Update TypeScript interfaces in settings.ts
187
-
188
- 4. **Documentation**:
189
- - Update README.md for user-facing changes
190
- - Add TSDoc comments for new APIs
191
- - Update TypeDoc comments for generated docs
192
- - Consider updating examples
193
-
194
- 5. **Testing**:
195
- - Add Vitest tests for new features
196
- - Mock external dependencies (August API)
197
- - Test error scenarios and edge cases
198
- - Maintain test coverage
199
-
200
- 6. **Compatibility**:
201
- - Support Node.js 20+ and 22+
202
- - Support Homebridge 1.9.0+ and 2.0.0+
203
- - Test with Config UI X integration
204
- - Verify ES module compatibility
205
-
206
- ## Common Patterns
207
-
208
- - **RxJS**: Use reactive programming patterns where appropriate
209
- - **Async/Await**: Prefer async/await over Promise chains
210
- - **ES Modules**: Use ES module syntax with `.js` extensions in imports
211
- - **Interface Design**: Define clear TypeScript interfaces in settings.ts
212
- - **Error Handling**: Implement comprehensive error handling with meaningful messages
213
- - **Logging**: Use this.log with appropriate log levels (info, warn, error, debug)
214
- - **Device Lifecycle**: Implement proper setup/cleanup in platform methods
215
- - **Configuration Validation**: Validate user configuration and provide helpful error messages
216
- - **API Rate Limiting**: Respect August API rate limits with configurable intervals
217
- - **State Management**: Maintain device state consistency between API and HomeKit
218
-
219
- ## Important Constants and Settings
220
-
221
- - **PLATFORM_NAME**: "August" (used in Homebridge registration)
222
- - **PLUGIN_NAME**: "homebridge-august" (must match package.json)
223
- - **Plugin Type**: "platform" (not accessory)
224
- - **Custom UI**: Enabled with path "./dist/homebridge-ui"
225
- - **Configuration**: Uses credentials and options interfaces
226
- - **Device Types**: Support for various August and Yale lock models
227
-
228
- ## Testing Guidelines
229
-
230
- - **Framework**: Use Vitest for testing with TypeScript support
231
- - **Test Files**: Name test files with `.test.ts` extension in src/
232
- - **Coverage**: Aim for good test coverage, use `npm run test-coverage`
233
- - **Mocking**: Mock external dependencies, especially:
234
- - August/Yale API calls
235
- - Homebridge API interactions
236
- - File system operations
237
- - **Test Categories**:
238
- - Unit tests for individual functions and classes
239
- - Integration tests for platform initialization
240
- - Configuration validation tests
241
- - Error handling tests
242
- - **Test Data**: Use realistic test data that matches August API responses
243
- - **Async Testing**: Properly test async operations and promises
244
- - **CI/CD**: Ensure tests run in continuous integration pipeline
245
-
246
- ## Files to Avoid Modifying
247
-
248
- - `dist/` directory (generated by TypeScript compilation)
249
- - `node_modules/` directory (managed by npm)
250
- - `docs/` directory (generated by TypeDoc)
251
- - `.git/` directory and git metadata
252
- - Files listed in `.gitignore`
253
- - `package-lock.json` (only modify through npm commands)
254
-
255
- ## Security Considerations
256
-
257
- - **Credentials**: Never log or expose user credentials
258
- - **API Keys**: Handle August API credentials securely
259
- - **Validation**: Validate all user inputs
260
- - **Error Messages**: Don't expose sensitive information in error messages
261
- - **Storage**: Store credentials securely using Homebridge storage mechanisms
@@ -1,51 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const child = require('node:child_process')
4
- const fs = require('node:fs')
5
-
6
- const bump = (process.argv[2] || 'patch').toLowerCase()
7
- const allowed = new Set(['major', 'minor', 'patch'])
8
-
9
- if (!allowed.has(bump)) {
10
- console.error(`Invalid bump level '${bump}'. Use major, minor, or patch.`)
11
- process.exit(1)
12
- }
13
-
14
- const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'))
15
-
16
- function inc(v, level) {
17
- const parts = v.split('-')[0].split('+')[0].split('.').map(Number)
18
- if (level === 'major') {
19
- parts[0] += 1
20
- parts[1] = 0
21
- parts[2] = 0
22
- } else if (level === 'minor') {
23
- parts[1] += 1
24
- parts[2] = 0
25
- } else {
26
- parts[2] += 1
27
- }
28
- return parts.join('.')
29
- }
30
-
31
- function latestStable(name) {
32
- try {
33
- const versions = JSON.parse(child.execSync(`npm info ${name} versions --json`).toString('utf8').trim())
34
- const stable = (Array.isArray(versions) ? versions : [])
35
- .filter(v => /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?(?:\+[0-9A-Za-z-.]+)?$/.test(v) && !v.includes('-'))
36
- stable.sort((a, b) => {
37
- const ap = a.split('.').map(Number)
38
- const bp = b.split('.').map(Number)
39
- for (let i = 0; i < 3; i++) {
40
- if (ap[i] !== bp[i]) return bp[i] - ap[i]
41
- }
42
- return 0
43
- })
44
- return stable[0] || ''
45
- } catch {
46
- return ''
47
- }
48
- }
49
-
50
- const base = latestStable(pkg.name) || pkg.version
51
- process.stdout.write(inc(base, bump))
@@ -1,86 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const child_process = require('node:child_process')
4
- const fs = require('node:fs')
5
-
6
- const changelogPath = 'CHANGELOG.md'
7
-
8
- function normalizeChangelogHeader(content) {
9
- const headerStart = content.indexOf('# Changelog\n')
10
- if (headerStart <= 0) {
11
- return content
12
- }
13
-
14
- const nextSectionStart = content.indexOf('\n## ', headerStart + 1)
15
- const headerBlock = (nextSectionStart === -1 ? content.slice(headerStart) : content.slice(headerStart, nextSectionStart)).trim()
16
- const prependedEntries = content.slice(0, headerStart).trim()
17
- const remainingEntries = (nextSectionStart === -1 ? '' : content.slice(nextSectionStart)).trim()
18
-
19
- return [headerBlock, prependedEntries, remainingEntries].filter(Boolean).join('\n\n') + '\n'
20
- }
21
-
22
- function getRepoUrl() {
23
- try {
24
- const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'))
25
- let url = (typeof pkg.repository === 'string' ? pkg.repository : pkg.repository?.url) || ''
26
- url = url.replace(/^git\+/, '').replace(/\.git$/, '')
27
- if (url.startsWith('https://')) return url
28
- } catch {}
29
- try {
30
- const remote = child_process.execSync('git remote get-url origin').toString().trim()
31
- return remote.replace(/^git\+/, '').replace(/\.git$/, '').replace(/^git@github\.com:/, 'https://github.com/')
32
- } catch {}
33
- return ''
34
- }
35
-
36
- function getPreviousTag() {
37
- try {
38
- const tags = child_process
39
- .execSync('git tag --sort=-version:refname')
40
- .toString()
41
- .trim()
42
- .split('\n')
43
- .filter(Boolean)
44
- return tags[0] || ''
45
- } catch {
46
- return ''
47
- }
48
- }
49
-
50
- function appendFullChangelogLink(content, currentTag, repoUrl) {
51
- const prefix = `**Full Changelog**: ${repoUrl}/compare/`
52
- // Find the bounds of the first release section (the newly generated one)
53
- const firstSectionStart = content.indexOf('\n## ')
54
- if (firstSectionStart === -1) return content
55
- const firstSectionEnd = content.indexOf('\n## ', firstSectionStart + 1)
56
- const firstSection = firstSectionEnd === -1 ? content.slice(firstSectionStart) : content.slice(firstSectionStart, firstSectionEnd)
57
-
58
- // Skip if a Full Changelog link is already present in that section
59
- if (firstSection.includes(prefix)) return content
60
-
61
- const prevTag = getPreviousTag()
62
- if (!prevTag) return content
63
-
64
- const link = `**Full Changelog**: ${repoUrl}/compare/${prevTag}...${currentTag}`
65
- if (firstSectionEnd === -1) {
66
- return content.trimEnd() + '\n\n' + link + '\n'
67
- }
68
- return content.slice(0, firstSectionEnd).trimEnd() + '\n\n' + link + '\n' + content.slice(firstSectionEnd)
69
- }
70
-
71
- exports.preCommit = (props) => {
72
- if (!fs.existsSync(changelogPath)) {
73
- return
74
- }
75
-
76
- let content = fs.readFileSync(changelogPath, 'utf8')
77
- content = normalizeChangelogHeader(content)
78
-
79
- const repoUrl = getRepoUrl()
80
- const currentTag = props?.tag || ''
81
- if (repoUrl && currentTag) {
82
- content = appendFullChangelogLink(content, currentTag, repoUrl)
83
- }
84
-
85
- fs.writeFileSync(changelogPath, content)
86
- }
@@ -1,201 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * Standalone ESM implementation — self-contained so this file can be copied
5
- * into other repos without pulling any other files.
6
- */
7
-
8
- import assert from 'node:assert'
9
- import child_process from 'node:child_process'
10
- import fs from 'node:fs'
11
- import process from 'node:process'
12
- // Minimal, in-file semver utilities used by this script.
13
- // This avoids an external dependency so the script can run in environments
14
- // where `semver` isn't installed.
15
- const semver = {
16
- // Very small validation: match major.minor.patch with optional prerelease/build
17
- valid(v) {
18
- return /^\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?(?:\+[0-9A-Za-z-.]+)?$/.test(v)
19
- },
20
- // Return prerelease identifiers array or null
21
- prerelease(v) {
22
- const m = v.match(/^\d+\.\d+\.\d+-(.+)$/)
23
- return m ? m[1].split('.') : null
24
- },
25
- // Compare two semver strings in reverse order (for rcompare use)
26
- rcompare(a, b) {
27
- // Use standard semver precedence rules for major.minor.patch; ignore prerelease complexity except that prerelease is lower than release
28
- const pa = a.split('-')
29
- const pb = b.split('-')
30
- const aCore = pa[0].split('.').map(Number)
31
- const bCore = pb[0].split('.').map(Number)
32
- for (let i = 0; i < 3; i++) {
33
- if (aCore[i] > bCore[i]) return -1
34
- if (aCore[i] < bCore[i]) return 1
35
- }
36
- // equal core; releases (no prerelease) are greater than prerelease
37
- if (pa.length === 1 && pb.length > 1) return -1
38
- if (pa.length > 1 && pb.length === 1) return 1
39
- // both same prerelease status: compare as strings
40
- if (a > b) return -1
41
- if (a < b) return 1
42
- return 0
43
- },
44
- // Increment version by 'major'|'minor'|'patch'
45
- inc(v, level) {
46
- if (!this.valid(v)) throw new Error(`Invalid semver passed to inc: ${v}`)
47
- // strip prerelease/build
48
- const core = v.split('-')[0].split('+')[0]
49
- const parts = core.split('.').map(Number)
50
- switch (level) {
51
- case 'major':
52
- parts[0] += 1
53
- parts[1] = 0
54
- parts[2] = 0
55
- break
56
- case 'minor':
57
- parts[1] += 1
58
- parts[2] = 0
59
- break
60
- case 'patch':
61
- default:
62
- parts[2] += 1
63
- break
64
- }
65
- return parts.join('.')
66
- }
67
- }
68
-
69
- const BRANCH_VERSION_PATTERN = /^([A-Z]+)-(\d+\.\d+\.\d+)$/i
70
-
71
- // Load package.json
72
- const packageJSON = JSON.parse(fs.readFileSync('package.json', 'utf8'))
73
-
74
- const refArgument = process.argv[2]
75
- const tagArgument = process.argv[3] || 'latest'
76
-
77
- if (!refArgument) {
78
- console.error('ref argument is missing')
79
- console.error('Usage: npm-version-script-esm.js <ref> [tag]')
80
- process.exit(1)
81
- }
82
-
83
- /**
84
- * Queries the NPM registry for the latest version for the provided base version and tag.
85
- * If the tag is latest, then the base version is returned if it exists. For other tags, the latest
86
- * version found for that base version and tag is returned.
87
- * @param baseVersion The base version to query for, e.g. 2.0.0
88
- * @param tag The tag to query for, e.g. beta or latest
89
- * @returns {string} Returns the version, or '' if not found
90
- */
91
- function getTagVersionFromNpm(baseVersion, tag) {
92
- try {
93
- return JSON.parse(child_process.execSync(`npm info ${packageJSON.name} versions --json`).toString('utf8').trim())
94
- .filter(v => (tag === 'latest' ? v === baseVersion : v.startsWith(`${baseVersion}-${tag}.`)))
95
- .reduce((_, current) => current, '')
96
- } catch (e) {
97
- console.error(`Failed to query the npm registry for the latest version for tag: ${tag}`, e)
98
- // throw e;
99
- return ''
100
- }
101
- }
102
-
103
- function getLatestStableVersionFromNpm() {
104
- try {
105
- const versions = JSON.parse(child_process.execSync(`npm info ${packageJSON.name} versions --json`).toString('utf8').trim())
106
- if (!Array.isArray(versions) || versions.length === 0) return ''
107
-
108
- // Only consider stable semver versions (no prerelease)
109
- const stable = versions.filter(v => semver.valid(v) && !semver.prerelease(v))
110
- if (stable.length === 0) return ''
111
-
112
- // Sort descending and return the highest
113
- const sorted = stable.sort(semver.rcompare)
114
- return sorted[0]
115
- } catch (e) {
116
- console.error('Failed to query the npm registry for published versions', e)
117
- return ''
118
- }
119
- }
120
-
121
- function desiredTargetVersion(ref) {
122
- // ref is a GitHub action ref string
123
- if (ref.startsWith('refs/pull/')) {
124
- throw new Error('The version script was executed inside a PR!')
125
- }
126
-
127
- assert(ref.startsWith('refs/heads/'))
128
- const branchName = ref.slice('refs/heads/'.length)
129
-
130
- const results = branchName.match(BRANCH_VERSION_PATTERN)
131
- if (results !== null) {
132
- if (results[1].toLowerCase() !== tagArgument.toLowerCase()) {
133
- console.warn(`The base branch name (${results[1]}) differs from the tag name ${tagArgument}`)
134
- }
135
-
136
- return results[2]
137
- }
138
-
139
- if (branchName === 'latest') {
140
- // For latest branch, start with package.json version as base
141
- return packageJSON.version
142
- }
143
-
144
- throw new Error(`Malformed branch name for ref: ${ref}. Must be beta-x.x.x, alpha-x.x.x, or 'latest'`)
145
- }
146
-
147
- function bumpVersion(baseVersion, level) {
148
- const normalizedLevel = (level || 'patch').toLowerCase()
149
- if (!semver.valid(baseVersion)) {
150
- throw new Error(`Invalid base version passed to bumpVersion: ${baseVersion}`)
151
- }
152
-
153
- switch (normalizedLevel) {
154
- case 'major':
155
- return semver.inc(baseVersion, 'major')
156
- case 'minor':
157
- return semver.inc(baseVersion, 'minor')
158
- case 'patch':
159
- default:
160
- return semver.inc(baseVersion, 'patch')
161
- }
162
- }
163
-
164
- const baseVersion = desiredTargetVersion(refArgument)
165
- let publishTag = baseVersion
166
-
167
- if (refArgument.includes('latest')) {
168
- // For latest branch, determine next version based on npm and bump patch.
169
- const latestPublishedStable = getLatestStableVersionFromNpm()
170
- if (latestPublishedStable) {
171
- console.warn(`Latest published stable version is ${latestPublishedStable}; bumping patch`)
172
- publishTag = bumpVersion(latestPublishedStable, 'patch')
173
- } else {
174
- console.warn('No published stable versions found; bumping package.json version patch')
175
- publishTag = bumpVersion(baseVersion, 'patch')
176
- }
177
- } else {
178
- // For alpha/beta, query npm for latest
179
- const latestReleasedVersion = getTagVersionFromNpm(baseVersion, tagArgument)
180
- if (latestReleasedVersion) {
181
- console.warn(`Latest published version for ${baseVersion} with tag ${tagArgument} is ${latestReleasedVersion}`)
182
- publishTag = latestReleasedVersion
183
- } else {
184
- console.warn(`No published versions for ${baseVersion} with tag ${tagArgument}`)
185
- }
186
- }
187
-
188
- if (packageJSON.version !== publishTag) {
189
- console.warn(`Updating version in package.json from ${packageJSON.version} to ${publishTag}`)
190
- packageJSON.version = publishTag
191
- fs.writeFileSync('package.json', JSON.stringify(packageJSON, null, 2))
192
-
193
- // perform the same change to the package-lock.json
194
- const packageLockJSON = JSON.parse(fs.readFileSync('package-lock.json', 'utf8'))
195
- packageLockJSON.version = publishTag
196
- fs.writeFileSync('package-lock.json', JSON.stringify(packageLockJSON, null, 2))
197
- } else {
198
- console.warn(`Version in package.json is already ${packageJSON.version}`)
199
- }
200
-
201
- console.log(publishTag)