uniweb 0.13.16 → 0.14.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.md +1 -1
- package/package.json +7 -7
- package/partials/agents.md +51 -0
- package/src/backend/client.js +173 -40
- package/src/backend/data-bundle.js +15 -3
- package/src/backend/foundation-bring-along.js +76 -21
- package/src/backend/payment-handoff.js +28 -9
- package/src/backend/site-media.js +147 -19
- package/src/backend/site-sync.js +362 -40
- package/src/commands/add.js +575 -273
- package/src/commands/build.js +160 -57
- package/src/commands/clone.js +79 -26
- package/src/commands/content.js +11 -7
- package/src/commands/deploy.js +80 -32
- package/src/commands/dev.js +39 -17
- package/src/commands/docs.js +44 -16
- package/src/commands/doctor.js +338 -82
- package/src/commands/export.js +15 -7
- package/src/commands/handoff.js +6 -2
- package/src/commands/i18n.js +248 -99
- package/src/commands/inspect.js +48 -19
- package/src/commands/invite.js +9 -3
- package/src/commands/org.js +19 -5
- package/src/commands/publish.js +229 -60
- package/src/commands/pull.js +157 -48
- package/src/commands/push.js +144 -42
- package/src/commands/refresh.js +37 -10
- package/src/commands/register.js +235 -64
- package/src/commands/rename.js +126 -46
- package/src/commands/runtime.js +74 -24
- package/src/commands/status.js +48 -15
- package/src/commands/sync.js +7 -2
- package/src/commands/template.js +12 -4
- package/src/commands/update.js +263 -87
- package/src/commands/validate.js +115 -32
- package/src/framework-index.json +16 -14
- package/src/index.js +298 -145
- package/src/templates/fetchers/github.js +7 -7
- package/src/templates/fetchers/npm.js +3 -3
- package/src/templates/fetchers/release.js +21 -18
- package/src/templates/index.js +34 -12
- package/src/templates/processor.js +44 -12
- package/src/templates/resolver.js +42 -15
- package/src/templates/validator.js +14 -7
- package/src/utils/asset-upload.js +90 -22
- package/src/utils/code-upload.js +62 -37
- package/src/utils/config.js +31 -10
- package/src/utils/dep-survey.js +33 -7
- package/src/utils/destination-prompt.js +27 -17
- package/src/utils/git.js +66 -22
- package/src/utils/host-prompt.js +4 -3
- package/src/utils/install-integrity.js +8 -4
- package/src/utils/interactive.js +3 -3
- package/src/utils/json-file.js +6 -2
- package/src/utils/names.js +15 -6
- package/src/utils/placement.js +16 -8
- package/src/utils/registry-auth.js +185 -57
- package/src/utils/registry-orgs.js +142 -53
- package/src/utils/runtime-upload.js +52 -14
- package/src/utils/scaffold.js +55 -14
- package/src/utils/site-content-refs.js +3 -1
- package/src/utils/update-check.js +43 -17
- package/src/utils/workspace.js +13 -7
- package/src/versions.js +18 -7
- package/templates/site/_gitignore +5 -0
package/src/commands/rename.js
CHANGED
|
@@ -53,7 +53,7 @@ import {
|
|
|
53
53
|
writeWorkspaceConfig,
|
|
54
54
|
readRootPackageJson,
|
|
55
55
|
writeRootPackageJson,
|
|
56
|
-
updateRootScripts
|
|
56
|
+
updateRootScripts
|
|
57
57
|
} from '../utils/config.js'
|
|
58
58
|
import { discoverFoundations, discoverSites } from '../utils/discover.js'
|
|
59
59
|
import { writeJsonPreservingStyleAsync } from '../utils/json-file.js'
|
|
@@ -68,7 +68,7 @@ const colors = {
|
|
|
68
68
|
cyan: '\x1b[36m',
|
|
69
69
|
green: '\x1b[32m',
|
|
70
70
|
yellow: '\x1b[33m',
|
|
71
|
-
red: '\x1b[31m'
|
|
71
|
+
red: '\x1b[31m'
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
const success = (msg) => console.log(`${colors.green}✓${colors.reset} ${msg}`)
|
|
@@ -107,7 +107,9 @@ export async function rename(args = []) {
|
|
|
107
107
|
const rootDir = findWorkspaceRoot()
|
|
108
108
|
if (!rootDir) {
|
|
109
109
|
error('Not in a Uniweb workspace.')
|
|
110
|
-
log(
|
|
110
|
+
log(
|
|
111
|
+
`Run this command from your project root or a site/foundation directory.`
|
|
112
|
+
)
|
|
111
113
|
process.exit(1)
|
|
112
114
|
}
|
|
113
115
|
|
|
@@ -133,7 +135,11 @@ function computeNewFolderPath(rootDir, oldPath, oldName, newName) {
|
|
|
133
135
|
const leaf = basename(oldPath)
|
|
134
136
|
const folderWillRename = leaf === oldName
|
|
135
137
|
if (!folderWillRename) {
|
|
136
|
-
return {
|
|
138
|
+
return {
|
|
139
|
+
folderWillRename,
|
|
140
|
+
newPath: oldPath,
|
|
141
|
+
newDir: join(rootDir, oldPath)
|
|
142
|
+
}
|
|
137
143
|
}
|
|
138
144
|
const parent = dirname(oldPath)
|
|
139
145
|
const newPath = parent === '.' ? newName : join(parent, newName)
|
|
@@ -146,7 +152,11 @@ function computeNewFolderPath(rootDir, oldPath, oldName, newName) {
|
|
|
146
152
|
* (the multi-PM compatibility invariant). Wildcard entries
|
|
147
153
|
* (`extensions/*`) are left alone — only specific paths get rewritten.
|
|
148
154
|
*/
|
|
149
|
-
async function updateWorkspaceManifestsForFolderMove(
|
|
155
|
+
async function updateWorkspaceManifestsForFolderMove(
|
|
156
|
+
rootDir,
|
|
157
|
+
oldPath,
|
|
158
|
+
newPath
|
|
159
|
+
) {
|
|
150
160
|
const wsConfig = await readWorkspaceConfig(rootDir)
|
|
151
161
|
const rootPkg = await readRootPackageJson(rootDir)
|
|
152
162
|
if (wsConfig.packages.includes(oldPath)) {
|
|
@@ -154,7 +164,10 @@ async function updateWorkspaceManifestsForFolderMove(rootDir, oldPath, newPath)
|
|
|
154
164
|
wsConfig.packages[idx] = newPath
|
|
155
165
|
await writeWorkspaceConfig(rootDir, wsConfig)
|
|
156
166
|
}
|
|
157
|
-
if (
|
|
167
|
+
if (
|
|
168
|
+
Array.isArray(rootPkg.workspaces) &&
|
|
169
|
+
rootPkg.workspaces.includes(oldPath)
|
|
170
|
+
) {
|
|
158
171
|
const idx = rootPkg.workspaces.indexOf(oldPath)
|
|
159
172
|
rootPkg.workspaces[idx] = newPath
|
|
160
173
|
await writeRootPackageJson(rootDir, rootPkg)
|
|
@@ -178,7 +191,9 @@ async function validateRenameName(rootDir, newName) {
|
|
|
178
191
|
}
|
|
179
192
|
const existingNames = await getExistingPackageNames(rootDir)
|
|
180
193
|
if (existingNames.has(newName)) {
|
|
181
|
-
error(
|
|
194
|
+
error(
|
|
195
|
+
`Cannot rename: a package named ${colors.bright}${newName}${colors.reset} already exists in this workspace.`
|
|
196
|
+
)
|
|
182
197
|
process.exit(1)
|
|
183
198
|
}
|
|
184
199
|
}
|
|
@@ -199,11 +214,13 @@ async function renameFoundation(rootDir, oldName, newName, prefix) {
|
|
|
199
214
|
await validateRenameName(rootDir, newName)
|
|
200
215
|
|
|
201
216
|
const foundations = await discoverFoundations(rootDir)
|
|
202
|
-
const target = foundations.find(f => f.name === oldName)
|
|
217
|
+
const target = foundations.find((f) => f.name === oldName)
|
|
203
218
|
if (!target) {
|
|
204
|
-
error(
|
|
219
|
+
error(
|
|
220
|
+
`No foundation named ${colors.bright}${oldName}${colors.reset} in this workspace.`
|
|
221
|
+
)
|
|
205
222
|
if (foundations.length > 0) {
|
|
206
|
-
log(`Available: ${foundations.map(f => f.name).join(', ')}`)
|
|
223
|
+
log(`Available: ${foundations.map((f) => f.name).join(', ')}`)
|
|
207
224
|
}
|
|
208
225
|
process.exit(1)
|
|
209
226
|
}
|
|
@@ -214,17 +231,24 @@ async function renameFoundation(rootDir, oldName, newName, prefix) {
|
|
|
214
231
|
// sites' deps + site.yml::foundation; extension rename touches
|
|
215
232
|
// site.yml::extensions URLs).
|
|
216
233
|
if (isExtensionPackage(join(rootDir, target.path))) {
|
|
217
|
-
error(
|
|
234
|
+
error(
|
|
235
|
+
`${colors.bright}${oldName}${colors.reset} is an extension, not a foundation.`
|
|
236
|
+
)
|
|
218
237
|
log(`Use \`${prefix} rename extension ${oldName} ${newName}\` instead.`)
|
|
219
238
|
process.exit(1)
|
|
220
239
|
}
|
|
221
240
|
|
|
222
241
|
const oldFoundationPath = target.path
|
|
223
242
|
const oldFoundationDir = join(rootDir, oldFoundationPath)
|
|
224
|
-
const {
|
|
225
|
-
|
|
243
|
+
const {
|
|
244
|
+
folderWillRename,
|
|
245
|
+
newPath: newFoundationPath,
|
|
246
|
+
newDir: newFoundationDir
|
|
247
|
+
} = computeNewFolderPath(rootDir, oldFoundationPath, oldName, newName)
|
|
226
248
|
if (folderWillRename && existsSync(newFoundationDir)) {
|
|
227
|
-
error(
|
|
249
|
+
error(
|
|
250
|
+
`Cannot rename: target folder ${colors.bright}${newFoundationPath}/${colors.reset} already exists.`
|
|
251
|
+
)
|
|
228
252
|
process.exit(1)
|
|
229
253
|
}
|
|
230
254
|
|
|
@@ -263,7 +287,7 @@ async function renameFoundation(rootDir, oldName, newName, prefix) {
|
|
|
263
287
|
siteYmlPath,
|
|
264
288
|
ymlData,
|
|
265
289
|
hasDep,
|
|
266
|
-
ymlMatches
|
|
290
|
+
ymlMatches
|
|
267
291
|
})
|
|
268
292
|
}
|
|
269
293
|
}
|
|
@@ -271,12 +295,16 @@ async function renameFoundation(rootDir, oldName, newName, prefix) {
|
|
|
271
295
|
// ─── Print plan, then execute ────────────────────────────────
|
|
272
296
|
|
|
273
297
|
log('')
|
|
274
|
-
log(
|
|
298
|
+
log(
|
|
299
|
+
`${colors.bright}Rename foundation${colors.reset}: ${colors.yellow}${oldName}${colors.reset} → ${colors.green}${newName}${colors.reset}`
|
|
300
|
+
)
|
|
275
301
|
log('')
|
|
276
302
|
if (folderWillRename) {
|
|
277
303
|
info(` Folder: ${oldFoundationPath}/ → ${newFoundationPath}/`)
|
|
278
304
|
} else {
|
|
279
|
-
info(
|
|
305
|
+
info(
|
|
306
|
+
` Folder: ${oldFoundationPath}/ (unchanged — leaf doesn't match package name)`
|
|
307
|
+
)
|
|
280
308
|
}
|
|
281
309
|
info(` package.json::name: "${oldName}" → "${newName}"`)
|
|
282
310
|
if (affectedSites.length === 0) {
|
|
@@ -301,17 +329,24 @@ async function renameFoundation(rootDir, oldName, newName, prefix) {
|
|
|
301
329
|
delete s.pkg.dependencies[oldName]
|
|
302
330
|
s.pkg.dependencies[newName] = oldValue.startsWith('file:')
|
|
303
331
|
? `file:${newRel}`
|
|
304
|
-
: oldValue
|
|
332
|
+
: oldValue // npm-pinned, leave it; rename-then-republish is out of scope.
|
|
305
333
|
await writeJsonPreservingStyleAsync(s.sitePkgPath, s.pkg, s.pkgSrc)
|
|
306
334
|
}
|
|
307
335
|
if (s.ymlMatches) {
|
|
308
336
|
const newYmlData = { ...s.ymlData, foundation: newName }
|
|
309
|
-
await writeFile(
|
|
337
|
+
await writeFile(
|
|
338
|
+
s.siteYmlPath,
|
|
339
|
+
yaml.dump(newYmlData, { flowLevel: -1, quotingType: "'" })
|
|
340
|
+
)
|
|
310
341
|
}
|
|
311
342
|
}
|
|
312
343
|
|
|
313
344
|
if (folderWillRename) {
|
|
314
|
-
await updateWorkspaceManifestsForFolderMove(
|
|
345
|
+
await updateWorkspaceManifestsForFolderMove(
|
|
346
|
+
rootDir,
|
|
347
|
+
oldFoundationPath,
|
|
348
|
+
newFoundationPath
|
|
349
|
+
)
|
|
315
350
|
}
|
|
316
351
|
|
|
317
352
|
// Root scripts can reference the foundation by name (e.g. `pnpm --filter
|
|
@@ -322,7 +357,9 @@ async function renameFoundation(rootDir, oldName, newName, prefix) {
|
|
|
322
357
|
await updateRootScripts(rootDir, freshSites, pm)
|
|
323
358
|
|
|
324
359
|
log('')
|
|
325
|
-
success(
|
|
360
|
+
success(
|
|
361
|
+
`Renamed foundation ${colors.bright}${oldName}${colors.reset} → ${colors.bright}${newName}${colors.reset}`
|
|
362
|
+
)
|
|
326
363
|
printNextSteps(prefix, pm)
|
|
327
364
|
}
|
|
328
365
|
|
|
@@ -332,31 +369,42 @@ async function renameSite(rootDir, oldName, newName, prefix) {
|
|
|
332
369
|
await validateRenameName(rootDir, newName)
|
|
333
370
|
|
|
334
371
|
const sites = await discoverSites(rootDir)
|
|
335
|
-
const target = sites.find(s => s.name === oldName)
|
|
372
|
+
const target = sites.find((s) => s.name === oldName)
|
|
336
373
|
if (!target) {
|
|
337
|
-
error(
|
|
374
|
+
error(
|
|
375
|
+
`No site named ${colors.bright}${oldName}${colors.reset} in this workspace.`
|
|
376
|
+
)
|
|
338
377
|
if (sites.length > 0) {
|
|
339
|
-
log(`Available: ${sites.map(s => s.name).join(', ')}`)
|
|
378
|
+
log(`Available: ${sites.map((s) => s.name).join(', ')}`)
|
|
340
379
|
}
|
|
341
380
|
process.exit(1)
|
|
342
381
|
}
|
|
343
382
|
|
|
344
383
|
const oldSitePath = target.path
|
|
345
384
|
const oldSiteDir = join(rootDir, oldSitePath)
|
|
346
|
-
const {
|
|
347
|
-
|
|
385
|
+
const {
|
|
386
|
+
folderWillRename,
|
|
387
|
+
newPath: newSitePath,
|
|
388
|
+
newDir: newSiteDir
|
|
389
|
+
} = computeNewFolderPath(rootDir, oldSitePath, oldName, newName)
|
|
348
390
|
if (folderWillRename && existsSync(newSiteDir)) {
|
|
349
|
-
error(
|
|
391
|
+
error(
|
|
392
|
+
`Cannot rename: target folder ${colors.bright}${newSitePath}/${colors.reset} already exists.`
|
|
393
|
+
)
|
|
350
394
|
process.exit(1)
|
|
351
395
|
}
|
|
352
396
|
|
|
353
397
|
log('')
|
|
354
|
-
log(
|
|
398
|
+
log(
|
|
399
|
+
`${colors.bright}Rename site${colors.reset}: ${colors.yellow}${oldName}${colors.reset} → ${colors.green}${newName}${colors.reset}`
|
|
400
|
+
)
|
|
355
401
|
log('')
|
|
356
402
|
if (folderWillRename) {
|
|
357
403
|
info(` Folder: ${oldSitePath}/ → ${newSitePath}/`)
|
|
358
404
|
} else {
|
|
359
|
-
info(
|
|
405
|
+
info(
|
|
406
|
+
` Folder: ${oldSitePath}/ (unchanged — leaf doesn't match package name)`
|
|
407
|
+
)
|
|
360
408
|
}
|
|
361
409
|
info(` package.json::name: "${oldName}" → "${newName}"`)
|
|
362
410
|
log('')
|
|
@@ -366,7 +414,11 @@ async function renameSite(rootDir, oldName, newName, prefix) {
|
|
|
366
414
|
}
|
|
367
415
|
await rewritePackageJsonName(join(newSiteDir, 'package.json'), newName)
|
|
368
416
|
if (folderWillRename) {
|
|
369
|
-
await updateWorkspaceManifestsForFolderMove(
|
|
417
|
+
await updateWorkspaceManifestsForFolderMove(
|
|
418
|
+
rootDir,
|
|
419
|
+
oldSitePath,
|
|
420
|
+
newSitePath
|
|
421
|
+
)
|
|
370
422
|
}
|
|
371
423
|
|
|
372
424
|
// Root scripts include `pnpm --filter <site-name>` style invocations
|
|
@@ -377,7 +429,9 @@ async function renameSite(rootDir, oldName, newName, prefix) {
|
|
|
377
429
|
await updateRootScripts(rootDir, freshSites, pm)
|
|
378
430
|
|
|
379
431
|
log('')
|
|
380
|
-
success(
|
|
432
|
+
success(
|
|
433
|
+
`Renamed site ${colors.bright}${oldName}${colors.reset} → ${colors.bright}${newName}${colors.reset}`
|
|
434
|
+
)
|
|
381
435
|
printNextSteps(prefix, pm)
|
|
382
436
|
}
|
|
383
437
|
|
|
@@ -390,23 +444,32 @@ async function renameExtension(rootDir, oldName, newName, prefix) {
|
|
|
390
444
|
// by `extension: true` declaration). Find via discoverFoundations,
|
|
391
445
|
// then verify the extension marker.
|
|
392
446
|
const foundations = await discoverFoundations(rootDir)
|
|
393
|
-
const target = foundations.find(f => f.name === oldName)
|
|
447
|
+
const target = foundations.find((f) => f.name === oldName)
|
|
394
448
|
if (!target) {
|
|
395
|
-
error(
|
|
449
|
+
error(
|
|
450
|
+
`No package named ${colors.bright}${oldName}${colors.reset} in this workspace.`
|
|
451
|
+
)
|
|
396
452
|
process.exit(1)
|
|
397
453
|
}
|
|
398
454
|
if (!isExtensionPackage(join(rootDir, target.path))) {
|
|
399
|
-
error(
|
|
455
|
+
error(
|
|
456
|
+
`${colors.bright}${oldName}${colors.reset} is a foundation, not an extension.`
|
|
457
|
+
)
|
|
400
458
|
log(`Use \`${prefix} rename foundation ${oldName} ${newName}\` instead.`)
|
|
401
459
|
process.exit(1)
|
|
402
460
|
}
|
|
403
461
|
|
|
404
462
|
const oldExtPath = target.path
|
|
405
463
|
const oldExtDir = join(rootDir, oldExtPath)
|
|
406
|
-
const {
|
|
407
|
-
|
|
464
|
+
const {
|
|
465
|
+
folderWillRename,
|
|
466
|
+
newPath: newExtPath,
|
|
467
|
+
newDir: newExtDir
|
|
468
|
+
} = computeNewFolderPath(rootDir, oldExtPath, oldName, newName)
|
|
408
469
|
if (folderWillRename && existsSync(newExtDir)) {
|
|
409
|
-
error(
|
|
470
|
+
error(
|
|
471
|
+
`Cannot rename: target folder ${colors.bright}${newExtPath}/${colors.reset} already exists.`
|
|
472
|
+
)
|
|
410
473
|
process.exit(1)
|
|
411
474
|
}
|
|
412
475
|
|
|
@@ -429,19 +492,25 @@ async function renameExtension(rootDir, oldName, newName, prefix) {
|
|
|
429
492
|
continue
|
|
430
493
|
}
|
|
431
494
|
const exts = Array.isArray(ymlData.extensions) ? ymlData.extensions : []
|
|
432
|
-
const hits = exts.filter(
|
|
495
|
+
const hits = exts.filter(
|
|
496
|
+
(e) => typeof e === 'string' && e.startsWith(oldUrlPrefix)
|
|
497
|
+
)
|
|
433
498
|
if (hits.length > 0) {
|
|
434
499
|
affectedSites.push({ site, ymlData, siteYmlPath, hits })
|
|
435
500
|
}
|
|
436
501
|
}
|
|
437
502
|
|
|
438
503
|
log('')
|
|
439
|
-
log(
|
|
504
|
+
log(
|
|
505
|
+
`${colors.bright}Rename extension${colors.reset}: ${colors.yellow}${oldName}${colors.reset} → ${colors.green}${newName}${colors.reset}`
|
|
506
|
+
)
|
|
440
507
|
log('')
|
|
441
508
|
if (folderWillRename) {
|
|
442
509
|
info(` Folder: ${oldExtPath}/ → ${newExtPath}/`)
|
|
443
510
|
} else {
|
|
444
|
-
info(
|
|
511
|
+
info(
|
|
512
|
+
` Folder: ${oldExtPath}/ (unchanged — leaf doesn't match package name)`
|
|
513
|
+
)
|
|
445
514
|
}
|
|
446
515
|
info(` package.json::name: "${oldName}" → "${newName}"`)
|
|
447
516
|
if (affectedSites.length === 0) {
|
|
@@ -449,7 +518,9 @@ async function renameExtension(rootDir, oldName, newName, prefix) {
|
|
|
449
518
|
} else {
|
|
450
519
|
info(` Sites referencing this extension:`)
|
|
451
520
|
for (const { site, hits } of affectedSites) {
|
|
452
|
-
info(
|
|
521
|
+
info(
|
|
522
|
+
` • ${site.name} at ${site.path}/ (${hits.length} entr${hits.length === 1 ? 'y' : 'ies'})`
|
|
523
|
+
)
|
|
453
524
|
}
|
|
454
525
|
}
|
|
455
526
|
log('')
|
|
@@ -460,13 +531,16 @@ async function renameExtension(rootDir, oldName, newName, prefix) {
|
|
|
460
531
|
await rewritePackageJsonName(join(newExtDir, 'package.json'), newName)
|
|
461
532
|
|
|
462
533
|
for (const a of affectedSites) {
|
|
463
|
-
const newExts = (a.ymlData.extensions || []).map(e =>
|
|
534
|
+
const newExts = (a.ymlData.extensions || []).map((e) =>
|
|
464
535
|
typeof e === 'string' && e.startsWith(oldUrlPrefix)
|
|
465
536
|
? newUrlPrefix + e.slice(oldUrlPrefix.length)
|
|
466
537
|
: e
|
|
467
538
|
)
|
|
468
539
|
const newYmlData = { ...a.ymlData, extensions: newExts }
|
|
469
|
-
await writeFile(
|
|
540
|
+
await writeFile(
|
|
541
|
+
a.siteYmlPath,
|
|
542
|
+
yaml.dump(newYmlData, { flowLevel: -1, quotingType: "'" })
|
|
543
|
+
)
|
|
470
544
|
}
|
|
471
545
|
|
|
472
546
|
if (folderWillRename) {
|
|
@@ -479,7 +553,9 @@ async function renameExtension(rootDir, oldName, newName, prefix) {
|
|
|
479
553
|
|
|
480
554
|
const pm = detectPackageManager()
|
|
481
555
|
log('')
|
|
482
|
-
success(
|
|
556
|
+
success(
|
|
557
|
+
`Renamed extension ${colors.bright}${oldName}${colors.reset} → ${colors.bright}${newName}${colors.reset}`
|
|
558
|
+
)
|
|
483
559
|
printNextSteps(prefix, pm)
|
|
484
560
|
}
|
|
485
561
|
|
|
@@ -487,8 +563,12 @@ async function renameExtension(rootDir, oldName, newName, prefix) {
|
|
|
487
563
|
|
|
488
564
|
function printNextSteps(prefix, pm) {
|
|
489
565
|
log('')
|
|
490
|
-
log(
|
|
491
|
-
|
|
566
|
+
log(
|
|
567
|
+
`Next: ${colors.cyan}${installCmd(pm)}${colors.reset} ${colors.dim}(refresh symlinks under the new name)${colors.reset}`
|
|
568
|
+
)
|
|
569
|
+
log(
|
|
570
|
+
` ${colors.cyan}${prefix} doctor${colors.reset} ${colors.dim}(verify wiring)${colors.reset}`
|
|
571
|
+
)
|
|
492
572
|
}
|
|
493
573
|
|
|
494
574
|
function showHelp(prefix) {
|
package/src/commands/runtime.js
CHANGED
|
@@ -22,19 +22,28 @@ import { readFileSync } from 'node:fs'
|
|
|
22
22
|
import { resolve, join } from 'node:path'
|
|
23
23
|
|
|
24
24
|
import { BackendClient } from '../backend/client.js'
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
collectRuntimeFiles,
|
|
27
|
+
hasWorkerRuntime,
|
|
28
|
+
hasShims
|
|
29
|
+
} from '../utils/runtime-upload.js'
|
|
26
30
|
import { readFlagValue } from '../utils/args.js'
|
|
27
31
|
|
|
28
32
|
const c = {
|
|
29
|
-
reset: '\x1b[0m',
|
|
30
|
-
|
|
33
|
+
reset: '\x1b[0m',
|
|
34
|
+
bold: '\x1b[1m',
|
|
35
|
+
dim: '\x1b[2m',
|
|
36
|
+
cyan: '\x1b[36m',
|
|
37
|
+
green: '\x1b[32m',
|
|
38
|
+
yellow: '\x1b[33m',
|
|
39
|
+
red: '\x1b[31m'
|
|
31
40
|
}
|
|
32
41
|
const say = {
|
|
33
42
|
ok: (m) => console.log(`${c.green}✓${c.reset} ${m}`),
|
|
34
43
|
info: (m) => console.log(`${c.cyan}→${c.reset} ${m}`),
|
|
35
44
|
warn: (m) => console.log(`${c.yellow}⚠${c.reset} ${m}`),
|
|
36
45
|
err: (m) => console.error(`${c.red}✗${c.reset} ${m}`),
|
|
37
|
-
dim: (m) => console.log(` ${c.dim}${m}${c.reset}`)
|
|
46
|
+
dim: (m) => console.log(` ${c.dim}${m}${c.reset}`)
|
|
38
47
|
}
|
|
39
48
|
|
|
40
49
|
// The runtime package dir: --path, else the cwd when it IS @uniweb/runtime.
|
|
@@ -42,7 +51,10 @@ function resolveRuntimeDir(args) {
|
|
|
42
51
|
const pathFlag = readFlagValue(args, '--path')
|
|
43
52
|
if (pathFlag) return resolve(pathFlag)
|
|
44
53
|
try {
|
|
45
|
-
if (
|
|
54
|
+
if (
|
|
55
|
+
JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf8'))
|
|
56
|
+
.name === '@uniweb/runtime'
|
|
57
|
+
) {
|
|
46
58
|
return process.cwd()
|
|
47
59
|
}
|
|
48
60
|
} catch {
|
|
@@ -54,8 +66,14 @@ function resolveRuntimeDir(args) {
|
|
|
54
66
|
export async function runtime(args = []) {
|
|
55
67
|
const sub = args[0]
|
|
56
68
|
if (sub !== 'register') {
|
|
57
|
-
say.err(
|
|
58
|
-
|
|
69
|
+
say.err(
|
|
70
|
+
sub
|
|
71
|
+
? `Unknown subcommand: runtime ${sub}`
|
|
72
|
+
: 'Usage: uniweb runtime register'
|
|
73
|
+
)
|
|
74
|
+
say.dim(
|
|
75
|
+
'uniweb runtime register — upload the built @uniweb/runtime to the backend (@std only).'
|
|
76
|
+
)
|
|
59
77
|
return { exitCode: sub ? 1 : 0 }
|
|
60
78
|
}
|
|
61
79
|
const rest = args.slice(1)
|
|
@@ -80,60 +98,92 @@ export async function runtime(args = []) {
|
|
|
80
98
|
let version = readFlagValue(rest, '--version')
|
|
81
99
|
if (!version) {
|
|
82
100
|
try {
|
|
83
|
-
version = JSON.parse(
|
|
101
|
+
version = JSON.parse(
|
|
102
|
+
readFileSync(join(distDir, 'app', 'manifest.json'), 'utf8')
|
|
103
|
+
).version
|
|
84
104
|
} catch (err) {
|
|
85
105
|
say.err(`Could not read dist/app/manifest.json: ${err.message}`)
|
|
86
106
|
return { exitCode: 2 }
|
|
87
107
|
}
|
|
88
108
|
if (!version) {
|
|
89
|
-
say.err(
|
|
109
|
+
say.err(
|
|
110
|
+
'dist/app/manifest.json has no "version" field — rebuild the runtime.'
|
|
111
|
+
)
|
|
90
112
|
return { exitCode: 2 }
|
|
91
113
|
}
|
|
92
114
|
}
|
|
93
115
|
// The ssr-edge artifact is a SET: worker-runtime.js + its shims/*.js. Warn when
|
|
94
116
|
// the set is absent or incomplete (a worker without shims can't resolve react).
|
|
95
117
|
if (!hasWorkerRuntime(files)) {
|
|
96
|
-
say.warn(
|
|
97
|
-
|
|
118
|
+
say.warn(
|
|
119
|
+
"dist/worker-runtime.js is missing — the SSR isolate bundle won't be uploaded."
|
|
120
|
+
)
|
|
121
|
+
say.dim(
|
|
122
|
+
'Build it first: `pnpm build:worker` in @uniweb/runtime (after `pnpm build`).'
|
|
123
|
+
)
|
|
98
124
|
} else if (!hasShims(files)) {
|
|
99
|
-
say.warn(
|
|
100
|
-
|
|
125
|
+
say.warn(
|
|
126
|
+
'dist/worker-runtime.js is present but dist/shims/ is missing — the SSR isolate set is incomplete.'
|
|
127
|
+
)
|
|
128
|
+
say.dim(
|
|
129
|
+
'The isolate resolves react/jsx-runtime/@uniweb/core through those shims; re-run `pnpm build:worker`.'
|
|
130
|
+
)
|
|
101
131
|
}
|
|
102
132
|
|
|
103
133
|
if (dryRun) {
|
|
104
|
-
say.info(
|
|
105
|
-
|
|
134
|
+
say.info(
|
|
135
|
+
`Would register ${c.bold}@uniweb/runtime@${version}${c.reset} (${files.length} files):`
|
|
136
|
+
)
|
|
137
|
+
for (const f of files)
|
|
138
|
+
say.dim(`${f.path} ${f.size} bytes ${f.content_type}`)
|
|
106
139
|
return { exitCode: 0 }
|
|
107
140
|
}
|
|
108
141
|
|
|
109
142
|
const client = new BackendClient({
|
|
110
|
-
originFlag:
|
|
143
|
+
originFlag:
|
|
144
|
+
readFlagValue(rest, '--backend') || readFlagValue(rest, '--registry'),
|
|
111
145
|
token: readFlagValue(rest, '--token') || undefined,
|
|
112
146
|
args: rest,
|
|
113
|
-
command: 'Registering the runtime'
|
|
147
|
+
command: 'Registering the runtime'
|
|
114
148
|
})
|
|
115
149
|
|
|
116
|
-
say.info(
|
|
150
|
+
say.info(
|
|
151
|
+
`Registering ${c.bold}@uniweb/runtime@${version}${c.reset} → ${c.dim}${client.origin}${c.reset} (${files.length} files)…`
|
|
152
|
+
)
|
|
117
153
|
let result
|
|
118
154
|
try {
|
|
119
|
-
result = await client.uploadRuntime({
|
|
155
|
+
result = await client.uploadRuntime({
|
|
156
|
+
version,
|
|
157
|
+
distDir,
|
|
158
|
+
files,
|
|
159
|
+
onProgress: (m) => say.dim(m)
|
|
160
|
+
})
|
|
120
161
|
} catch (err) {
|
|
121
162
|
if (err.status === 403) {
|
|
122
|
-
say.err(
|
|
163
|
+
say.err(
|
|
164
|
+
'Not authorized — registering a runtime version requires @std membership.'
|
|
165
|
+
)
|
|
123
166
|
return { exitCode: 1 }
|
|
124
167
|
}
|
|
125
168
|
say.err(`Runtime registration failed: ${err.message}`)
|
|
126
|
-
say.dim(
|
|
169
|
+
say.dim(
|
|
170
|
+
'Set the origin with --backend <url>; auth with `uniweb login` or --token <bearer>.'
|
|
171
|
+
)
|
|
127
172
|
return { exitCode: 1 }
|
|
128
173
|
}
|
|
129
174
|
if (result.failed.length) {
|
|
130
175
|
say.err(`${result.failed.length} file(s) failed to upload:`)
|
|
131
|
-
for (const f of result.failed)
|
|
132
|
-
|
|
176
|
+
for (const f of result.failed)
|
|
177
|
+
say.dim(`${f.path} — HTTP ${f.status} ${f.detail}`)
|
|
178
|
+
say.dim(
|
|
179
|
+
'Re-run `uniweb runtime register` to resume — completed files dedupe.'
|
|
180
|
+
)
|
|
133
181
|
return { exitCode: 1 }
|
|
134
182
|
}
|
|
135
183
|
console.log('')
|
|
136
|
-
say.ok(
|
|
184
|
+
say.ok(
|
|
185
|
+
`Registered ${c.bold}@uniweb/runtime@${version}${c.reset} (${result.uploaded.length} files, ${result.mode} mode)`
|
|
186
|
+
)
|
|
137
187
|
if (result.serveBase) say.dim(`served at ${result.serveBase}`)
|
|
138
188
|
return { exitCode: 0 }
|
|
139
189
|
}
|