fg-devkit 1.7.1 → 1.7.3

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.md CHANGED
@@ -136,3 +136,27 @@ export default {
136
136
  ...basePrettierConfig,
137
137
  }
138
138
  ```
139
+ ## Install CLI in this repo
140
+
141
+ 1) Add this to your shell config (`~/.zshrc`) and update `FGD_DEVKIT_PATH`:
142
+
143
+ ```bash
144
+ cat <<'EOF' >> ~/.zshrc
145
+ # Smart fgd: use local project binary when available, otherwise fg-devkit script
146
+ unalias fgd 2>/dev/null
147
+ export FGD_DEVKIT_PATH="/Users/username/projects/fg-devkit"
148
+ fgd() {
149
+ if [ -x "./node_modules/.bin/fgd" ]; then
150
+ pnpm exec fgd "$@"
151
+ else
152
+ node "$FGD_DEVKIT_PATH/scripts/fg-devkit.mjs" "$@"
153
+ fi
154
+ }
155
+ EOF
156
+ ```
157
+
158
+ 2) Reload shell config:
159
+
160
+ ```bash
161
+ source ~/.zshrc
162
+ ```
@@ -6,10 +6,10 @@
6
6
  "@lexical/list": "0.30.0",
7
7
  "@lexical/rich-text": "0.30.0",
8
8
  "@piwikpro/vue-piwik-pro": "1.4.0",
9
- "@sentry/browser": "7.67.0",
10
- "@sentry/tracing": "7.37.2",
11
- "@sentry/vue": "7.37.2",
12
- "@sentry/wasm": "7.67.0",
9
+ "@sentry/browser": "10.50.0",
10
+ "@sentry/tracing": "7.120.4",
11
+ "@sentry/vue": "10.50.0",
12
+ "@sentry/wasm": "10.50.0",
13
13
  "@tailwindcss/postcss": "4.2.4",
14
14
  "@tanstack/vue-query": "5.99.2",
15
15
  "@types/leaflet": "1.5.7",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fg-devkit",
3
- "version": "1.7.1",
3
+ "version": "1.7.3",
4
4
  "description": "Shared configs, scripts, and DX tooling for Vue / TypeScript frontend repos",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -34,7 +34,7 @@
34
34
  "@tanstack/vue-query-devtools": "6.1.24",
35
35
  "@types/node": "22.16.5",
36
36
  "@vitejs/plugin-vue": "6.0.6",
37
- "fg-devkit": "1.7.1",
37
+ "fg-devkit": "1.7.3",
38
38
  "start-server-and-test": "2.1.3",
39
39
  "tailwindcss": "4.2.4",
40
40
  "tsx": "4.21.0",
@@ -440,7 +440,7 @@ Commands:
440
440
  fleet-vulns [projects-root] [--summary-only] [--delay-ms=<ms>]
441
441
  Run pnpm audit across Vue repos (full output + combined summary)
442
442
  fleet-bump-fg-devkit [projects-root] [--dry-run]
443
- Bump fg-devkit to latest across Vue repos, then run pnpm install
443
+ Bump fg-devkit, run sync-deps, then pnpm install across Vue repos
444
444
  policy-upgrade [policy-file] [ncu-args...]
445
445
  Upgrade policy packages via npm-check-updates (default interactive)
446
446
  sync-project-configs VS Code + tooling sync (overwrites config files)
@@ -576,7 +576,7 @@ const MAINTAINER_MENU = [
576
576
  },
577
577
  {
578
578
  key: 'm8',
579
- label: 'Maintainer: Bump fg-devkit in all Vue repos and run pnpm install',
579
+ label: 'Maintainer: Bump fg-devkit in all Vue repos, run sync-deps, and pnpm install',
580
580
  run: () => runNode('fleet-bump-fg-devkit.mjs'),
581
581
  },
582
582
  ]
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * Bumps fg-devkit to latest in Vue repositories and runs pnpm install.
3
+ * Bumps fg-devkit to latest in Vue repositories, syncs dependency policy, and runs pnpm install.
4
4
  *
5
5
  * Usage:
6
6
  * pnpm exec fg-devkit fleet-bump-fg-devkit [projects-root] [--dry-run]
@@ -82,10 +82,12 @@ let updated = 0
82
82
  for (const repoDir of uniqueRepos) {
83
83
  const repoName = path.basename(repoDir)
84
84
  const bumpArgs = ['up', 'fg-devkit@latest']
85
+ const syncDepsArgs = ['exec', 'fg-devkit', 'sync-deps']
85
86
  const installArgs = ['install']
86
87
 
87
88
  if (dryRun) {
88
89
  console.log(`[fleet-bump-fg-devkit] DRY RUN ${repoName}: pnpm ${bumpArgs.join(' ')}`)
90
+ console.log(`[fleet-bump-fg-devkit] DRY RUN ${repoName}: pnpm ${syncDepsArgs.join(' ')}`)
89
91
  console.log(`[fleet-bump-fg-devkit] DRY RUN ${repoName}: pnpm ${installArgs.join(' ')}`)
90
92
  continue
91
93
  }
@@ -114,6 +116,29 @@ for (const repoDir of uniqueRepos) {
114
116
  continue
115
117
  }
116
118
 
119
+ const syncDeps = spawnSync('pnpm', syncDepsArgs, {
120
+ cwd: repoDir,
121
+ stdio: 'inherit',
122
+ env: {
123
+ ...process.env,
124
+ npm_config_save_prefix: '',
125
+ npm_config_save_exact: 'true',
126
+ },
127
+ })
128
+
129
+ if (syncDeps.error) {
130
+ failures += 1
131
+ console.error(`[fleet-bump-fg-devkit] FAIL ${repoName}: ${syncDeps.error.message}`)
132
+ continue
133
+ }
134
+
135
+ const syncDepsStatus = syncDeps.status ?? 1
136
+ if (syncDepsStatus !== 0) {
137
+ failures += 1
138
+ console.error(`[fleet-bump-fg-devkit] FAIL ${repoName}: sync-deps exited with code ${syncDepsStatus}`)
139
+ continue
140
+ }
141
+
117
142
  const install = spawnSync('pnpm', installArgs, {
118
143
  cwd: repoDir,
119
144
  stdio: 'inherit',
@@ -12,6 +12,29 @@ import path from 'node:path'
12
12
 
13
13
  import { getFlagValue, getPoeditorToken, loadPoeditorConfig, parseFlags, resolvePathFromCwd } from './poeditor-common.mjs'
14
14
 
15
+ const MIN_UPLOAD_INTERVAL_MS = 20_000
16
+ const MAX_UPLOAD_RETRIES = 3
17
+ const RETRYABLE_UPLOAD_ERROR_CODES = new Set(['4048'])
18
+
19
+ function sleep(ms) {
20
+ return new Promise((resolve) => {
21
+ setTimeout(resolve, ms)
22
+ })
23
+ }
24
+
25
+ function parsePoeditorError(err) {
26
+ const message = err instanceof Error ? err.message : String(err)
27
+ const match = message.match(/Upload failed:\s*(\{.*\})$/)
28
+ if (!match) return null
29
+ try {
30
+ const payload = JSON.parse(match[1])
31
+ const code = payload?.response?.code
32
+ return typeof code === 'string' ? code : null
33
+ } catch {
34
+ return null
35
+ }
36
+ }
37
+
15
38
  function getUploadType(filePath) {
16
39
  const extension = path.extname(filePath).toLowerCase().replace('.', '')
17
40
  if (!extension) {
@@ -53,6 +76,30 @@ async function uploadFile({ token, projectId, language, filePath }) {
53
76
  )
54
77
  }
55
78
 
79
+ async function uploadFileWithRetry({ token, projectId, language, filePath }) {
80
+ let attempt = 0
81
+ while (true) {
82
+ attempt += 1
83
+ try {
84
+ await uploadFile({ token, projectId, language, filePath })
85
+ return
86
+ } catch (err) {
87
+ const errorCode = parsePoeditorError(err)
88
+ const isRetryable = errorCode && RETRYABLE_UPLOAD_ERROR_CODES.has(errorCode)
89
+ const retriesLeft = MAX_UPLOAD_RETRIES - attempt
90
+ if (!isRetryable || retriesLeft < 0) {
91
+ throw err
92
+ }
93
+
94
+ const waitMs = MIN_UPLOAD_INTERVAL_MS
95
+ console.warn(
96
+ `[poeditor-upload] Received retryable POEditor error code ${errorCode}. Retrying in ${Math.ceil(waitMs / 1000)}s (${retriesLeft} retries left).`,
97
+ )
98
+ await sleep(waitMs)
99
+ }
100
+ }
101
+ }
102
+
56
103
  async function main() {
57
104
  const { flags } = parseFlags(process.argv.slice(2))
58
105
  const cfg = loadPoeditorConfig(flags)
@@ -76,17 +123,26 @@ async function main() {
76
123
 
77
124
  const uploads = cfg.uploadFiles && typeof cfg.uploadFiles === 'object' ? Object.entries(cfg.uploadFiles) : []
78
125
  if (uploads.length > 0) {
126
+ let nextUploadAt = 0
79
127
  for (const [language, file] of uploads) {
128
+ const now = Date.now()
129
+ const waitBeforeUploadMs = Math.max(0, nextUploadAt - now)
130
+ if (waitBeforeUploadMs > 0) {
131
+ console.log(`[poeditor-upload] Waiting ${Math.ceil(waitBeforeUploadMs / 1000)}s before next upload...`)
132
+ await sleep(waitBeforeUploadMs)
133
+ }
134
+
80
135
  const filePath = resolvePathFromCwd(file)
81
- await uploadFile({ token, projectId, language, filePath })
136
+ await uploadFileWithRetry({ token, projectId, language, filePath })
82
137
  console.log(`[poeditor-upload] Upload completed for ${language}: ${filePath}`)
138
+ nextUploadAt = Date.now() + MIN_UPLOAD_INTERVAL_MS
83
139
  }
84
140
  return
85
141
  }
86
142
 
87
143
  const language = cfg.defaultLanguage
88
144
  const filePath = resolvePathFromCwd(cfg.uploadFile)
89
- await uploadFile({ token, projectId, language, filePath })
145
+ await uploadFileWithRetry({ token, projectId, language, filePath })
90
146
  console.log(`[poeditor-upload] Upload completed for ${language}: ${filePath}`)
91
147
  }
92
148
 
@@ -210,8 +210,6 @@ if (policyErrors.length > 0) {
210
210
  }
211
211
 
212
212
  fs.writeFileSync(policyPath, `${JSON.stringify(policy, null, 2)}\n`)
213
-
214
- const fgDevkitRoot = path.resolve(scriptDir, '..')
215
213
  syncScaffoldVueDepsFromPolicy({ fgDevkitRoot, policyPath })
216
214
 
217
215
  console.log(`[refresh-policy] projectsRoot=${projectsRoot}`)