@homebridge-plugins/homebridge-august 3.2.3 → 3.3.0-beta.1

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 (52) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/CLAUDE.md +48 -0
  3. package/README.md +39 -27
  4. package/config.schema.json +47 -3
  5. package/dist/Platform.HAP.d.ts.map +1 -1
  6. package/dist/Platform.HAP.js +1 -2
  7. package/dist/Platform.HAP.js.map +1 -1
  8. package/dist/connectivity-manager.d.ts.map +1 -1
  9. package/dist/connectivity-manager.js.map +1 -1
  10. package/dist/platform.matter.d.ts +6 -0
  11. package/dist/platform.matter.d.ts.map +1 -1
  12. package/dist/platform.matter.js +15 -2
  13. package/dist/platform.matter.js.map +1 -1
  14. package/dist/settings.d.ts +1 -0
  15. package/dist/settings.d.ts.map +1 -1
  16. package/package.json +37 -31
  17. package/test/connectivity-manager.test.ts +25 -23
  18. package/.github/FUNDING.yml +0 -4
  19. package/.github/ISSUE_TEMPLATE/bug-report.yml +0 -97
  20. package/.github/ISSUE_TEMPLATE/config.yml +0 -8
  21. package/.github/ISSUE_TEMPLATE/feature-request.yml +0 -38
  22. package/.github/ISSUE_TEMPLATE/support-request.yml +0 -85
  23. package/.github/ISSUE_TEMPLATE.md +0 -48
  24. package/.github/PULL_REQUEST_TEMPLATE/pull_request.md +0 -27
  25. package/.github/copilot-instructions.md +0 -200
  26. package/.github/dependabot.yml +0 -17
  27. package/.github/labeler.yml +0 -38
  28. package/.github/scripts/compute-version-override.cjs +0 -51
  29. package/.github/scripts/normalize-changelog-header.cjs +0 -86
  30. package/.github/scripts/npm-version-script-esm-auto.js +0 -201
  31. package/.github/scripts/resolve-version-bump-esm.js +0 -118
  32. package/.github/workflows/release.yml +0 -169
  33. package/.github/workflows/stale.yml +0 -12
  34. package/SECURITY.md +0 -19
  35. package/branding/Homebridge_x_August.svg +0 -32
  36. package/branding/icon.png +0 -0
  37. package/dist/package.json +0 -84
  38. package/docs/.nojekyll +0 -1
  39. package/docs/assets/hierarchy.js +0 -1
  40. package/docs/assets/highlight.css +0 -22
  41. package/docs/assets/icons.js +0 -18
  42. package/docs/assets/icons.svg +0 -1
  43. package/docs/assets/main.js +0 -60
  44. package/docs/assets/navigation.js +0 -1
  45. package/docs/assets/search.js +0 -1
  46. package/docs/assets/style.css +0 -1633
  47. package/docs/hierarchy.html +0 -1
  48. package/docs/index.html +0 -44
  49. package/docs/modules.html +0 -1
  50. package/docs/variables/default.html +0 -1
  51. package/eslint.config.js +0 -47
  52. package/vitest.config.ts +0 -8
@@ -1,200 +0,0 @@
1
- # Copilot Instructions for Homebridge August Plugin
2
-
3
- ## Repository Overview
4
-
5
- This repository is a Homebridge dynamic platform plugin for August and Yale locks.
6
-
7
- - Runtime uses TypeScript + ES modules.
8
- - The plugin supports a dual runtime path:
9
- - HAP path via AugustPlatform.
10
- - Matter path via AugustMatterPlatform when Homebridge Matter is available and enabled.
11
-
12
- ## Current Architecture
13
-
14
- - Plugin type: dynamic platform plugin.
15
- - Entry point: src/index.ts.
16
- - HAP platform class: src/Platform.HAP.ts (AugustPlatform).
17
- - Matter platform class: src/Platform.Matter.ts (AugustMatterPlatform).
18
- - Shared device logic: src/devices/device.ts and src/devices/lock.ts.
19
- - Connectivity state machine: src/connectivity-manager.ts.
20
- - Config/types/constants: src/settings.ts.
21
- - UI backend/static assets: src/homebridge-ui/.
22
-
23
- The entry point in src/index.ts registers a proxy constructor that selects Matter or HAP at runtime based on:
24
- - api.isMatterAvailable()
25
- - api.isMatterEnabled()
26
- - config.options.disableMatter
27
-
28
- ## Toolchain and Runtime
29
-
30
- Use the values from package.json and tsconfig.json as source of truth.
31
-
32
- - Node support: ^22 || ^24
33
- - Homebridge support: ^2.0.0
34
- - TypeScript target: ES2022
35
- - Module: ES2022
36
- - Module resolution: bundler
37
- - Strict mode: enabled (noImplicitAny is false)
38
-
39
- ## Commands
40
-
41
- ### Build and Packaging
42
-
43
- ```bash
44
- npm run clean
45
- npm run build
46
- npm run prepublishOnly
47
- ```
48
-
49
- ### Lint
50
-
51
- ```bash
52
- npm run lint
53
- npm run lint:fix
54
- ```
55
-
56
- ### Test
57
-
58
- ```bash
59
- npm test
60
- npm run test:watch
61
- npm run test-coverage
62
- ```
63
-
64
- ### Docs
65
-
66
- ```bash
67
- npm run docs
68
- npm run docs:lint
69
- ```
70
-
71
- ### Development
72
-
73
- ```bash
74
- npm run watch
75
- npm run plugin-ui
76
- ```
77
-
78
- ## Branch and Release Workflow
79
-
80
- This repo follows a beta-first workflow.
81
-
82
- - Do not target main directly.
83
- - Target a beta branch named beta-X.Y.Z.
84
- - Use issue labels to determine bump type:
85
- - patch
86
- - minor
87
- - major
88
-
89
- If no matching beta branch exists, create one from the next expected version.
90
-
91
- ## Coding Expectations
92
-
93
- 1. Keep imports as ES module paths ending in .js.
94
- 2. Preserve Homebridge plugin constants:
95
- - PLATFORM_NAME = August
96
- - PLUGIN_NAME = homebridge-august
97
- 3. Maintain backward compatibility unless issue scope is explicitly breaking.
98
- 4. Keep runtime and schema aligned:
99
- - Update src/settings.ts and config.schema.json together for option changes.
100
- 5. Add or update Vitest coverage for behavior changes.
101
- 6. Never commit generated output from dist/.
102
-
103
- ## Homebridge and August/Yale Patterns
104
-
105
- - Use Homebridge DynamicPlatformPlugin patterns for lifecycle methods.
106
- - Use PlatformAccessory caching and proper unregister/update behavior.
107
- - Handle August auth and refresh flows defensively.
108
- - Respect polling and push/update intervals from options.
109
- - Prefer robust error classification and graceful retries over broad catch-and-ignore.
110
-
111
- ## Matter and HomeKit Integration
112
-
113
- ### Matter Implementation
114
-
115
- The plugin uses a dual-mode runtime that selects HAP or Matter at startup.
116
-
117
- - HAP mode (HomeKit Accessory Protocol): implemented by AugustPlatform in src/Platform.HAP.ts.
118
- - Matter mode: implemented by AugustMatterPlatform in src/Platform.Matter.ts.
119
-
120
- In src/index.ts, the platform proxy chooses AugustMatterPlatform only when Matter is available and enabled, and the user has not disabled Matter via config.options.disableMatter. Otherwise it falls back to AugustPlatform.
121
-
122
- ### Matter Device Type Mapping
123
-
124
- Matter registrations use api.matter.deviceTypes.* and api.matter.types.* from Homebridge's Matter API.
125
-
126
- | August Device | HAP Service | Matter DeviceType | Matter Clusters |
127
- |---|---|---|---|
128
- | August/Yale lock | LockMechanism (plus optional ContactSensor in HAP mode) | DoorLock | doorLock |
129
-
130
- Notes:
131
- - AugustMatterPlatform currently maps lock accessories to Matter DoorLock.
132
- - Lock state updates are written through the doorLock cluster (for example lockState) and mirrored from August API polling/push updates.
133
-
134
- ### Authoritative Matter References
135
-
136
- 1. https://matter-js.github.io/docs/index.html
137
- 2. https://github.com/homebridge-plugins/homebridge-matter
138
-
139
- For cluster, attribute, deviceType, and conversion details, use the homebridge-matter wiki:
140
- - Introduction: https://github.com/homebridge-plugins/homebridge-matter/wiki/Introduction
141
- - Core Concepts: https://github.com/homebridge-plugins/homebridge-matter/wiki/Core-Concepts
142
- - Getting Started: https://github.com/homebridge-plugins/homebridge-matter/wiki/Getting-Started
143
- - State Management: https://github.com/homebridge-plugins/homebridge-matter/wiki/State-Management
144
- - Monitoring External Changes: https://github.com/homebridge-plugins/homebridge-matter/wiki/Monitoring-External-Changes
145
- - Best Practices: https://github.com/homebridge-plugins/homebridge-matter/wiki/Best-Practices
146
- - Advanced Patterns: https://github.com/homebridge-plugins/homebridge-matter/wiki/Advanced-Patterns
147
- - API Reference: https://github.com/homebridge-plugins/homebridge-matter/wiki/API-Reference
148
- - Matter Types: https://github.com/homebridge-plugins/homebridge-matter/wiki/Matter-Types
149
- - Value Conversions: https://github.com/homebridge-plugins/homebridge-matter/wiki/Value-Conversions
150
-
151
- Device reference pages:
152
- - Sensors (Section 7): https://github.com/homebridge-plugins/homebridge-matter/wiki/Section-7-Sensors
153
- - Closure (Section 8): https://github.com/homebridge-plugins/homebridge-matter/wiki/Section-8-Closure
154
-
155
- ## Testing Guidelines
156
-
157
- - Test files live under test/ with .test.ts suffix.
158
- - Mock external dependencies:
159
- - august-yale
160
- - file system
161
- - Homebridge API surfaces
162
- - Cover:
163
- - startup and discovery
164
- - polling loops
165
- - push/pull state sync
166
- - Matter fallback behavior
167
- - error paths and retries
168
-
169
- ## Files to Avoid Editing Directly
170
-
171
- - dist/
172
- - node_modules/
173
- - docs/ generated output unless intentionally regenerating docs
174
-
175
- ## Changelog Requirements
176
-
177
- When adding a release entry to CHANGELOG.md:
178
-
179
- 1. Header format:
180
-
181
- ```md
182
- ## [X.Y.Z](https://github.com/homebridge-plugins/homebridge-august/releases/tag/vX.Y.Z) (YYYY-MM-DD)
183
- ```
184
-
185
- 2. Add sections as needed (for example Bug Fixes, Features, Documentation).
186
-
187
- 3. Include a trailing full changelog comparison line:
188
-
189
- ```md
190
- **Full Changelog**: https://github.com/homebridge-plugins/homebridge-august/compare/vX.Y.(Z-1)...vX.Y.Z
191
- ```
192
-
193
- 4. Ensure commit links point to this repository:
194
- - https://github.com/homebridge-plugins/homebridge-august/commit/<sha>
195
-
196
- ## Security and Privacy
197
-
198
- - Never log secrets (passwords, tokens, validation codes).
199
- - Avoid exposing sensitive values in thrown errors.
200
- - Validate user-supplied config inputs before use.
@@ -1,17 +0,0 @@
1
- # To get started with Dependabot version updates, you'll need to specify which
2
- # package ecosystems to update and where the package manifests are located.
3
- # Please see the documentation for all configuration options:
4
- # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
5
-
6
- version: 2
7
- updates:
8
- - package-ecosystem: 'npm' # See documentation for possible values
9
- directory: '/' # Location of package manifests
10
- target-branch: 'beta-*.*.*'
11
- schedule:
12
- interval: 'daily'
13
- - package-ecosystem: 'github-actions' # See documentation for possible values
14
- directory: '/' # Location of package manifests
15
- target-branch: 'beta-*.*.*'
16
- schedule:
17
- interval: 'daily'
@@ -1,38 +0,0 @@
1
- # Add 'branding' label to any changes within 'docs' folder or any subfolders
2
- branding:
3
- - changed-files:
4
- - any-glob-to-any-file: branding/**
5
-
6
- # Add 'docs' label to any change to .md files within the entire repository
7
- docs:
8
- - changed-files:
9
- - any-glob-to-any-file: '**/*.md'
10
-
11
- # Add 'enhancement' label to any change to src files within the source dir EXCEPT for the docs sub-folder
12
- enhancement:
13
- - changed-files:
14
- - any-glob-to-any-file: 'src/**/*'
15
- - any-glob-to-any-file: 'config.schema.json'
16
-
17
- # Add 'dependencies' label to any change to src files within the source dir EXCEPT for the docs sub-folder
18
- dependencies:
19
- - changed-files:
20
- - any-glob-to-any-file: 'package.json'
21
- - any-glob-to-any-file: 'package-lock.json'
22
-
23
- # Add 'beta' label to any PR that is opened against the `beta` branch
24
- beta:
25
- - base-branch: 'beta*'
26
-
27
- # Add 'alpha' label to any PR that is opened against the `alpha` branch
28
- alpha:
29
- - base-branch: 'alpha*'
30
-
31
- # Add 'latest' label to any PR that is opened against the `latest` branch
32
- latest:
33
- - base-branch: 'latest'
34
-
35
- # Add 'workflow' to any changes within 'workflow' folder or any subfolders
36
- workflow:
37
- - changed-files:
38
- - any-glob-to-any-file: .github/**
@@ -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)