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.
Files changed (65) hide show
  1. package/README.md +1 -1
  2. package/package.json +7 -7
  3. package/partials/agents.md +51 -0
  4. package/src/backend/client.js +173 -40
  5. package/src/backend/data-bundle.js +15 -3
  6. package/src/backend/foundation-bring-along.js +76 -21
  7. package/src/backend/payment-handoff.js +28 -9
  8. package/src/backend/site-media.js +147 -19
  9. package/src/backend/site-sync.js +362 -40
  10. package/src/commands/add.js +575 -273
  11. package/src/commands/build.js +160 -57
  12. package/src/commands/clone.js +79 -26
  13. package/src/commands/content.js +11 -7
  14. package/src/commands/deploy.js +80 -32
  15. package/src/commands/dev.js +39 -17
  16. package/src/commands/docs.js +44 -16
  17. package/src/commands/doctor.js +338 -82
  18. package/src/commands/export.js +15 -7
  19. package/src/commands/handoff.js +6 -2
  20. package/src/commands/i18n.js +248 -99
  21. package/src/commands/inspect.js +48 -19
  22. package/src/commands/invite.js +9 -3
  23. package/src/commands/org.js +19 -5
  24. package/src/commands/publish.js +229 -60
  25. package/src/commands/pull.js +157 -48
  26. package/src/commands/push.js +144 -42
  27. package/src/commands/refresh.js +37 -10
  28. package/src/commands/register.js +235 -64
  29. package/src/commands/rename.js +126 -46
  30. package/src/commands/runtime.js +74 -24
  31. package/src/commands/status.js +48 -15
  32. package/src/commands/sync.js +7 -2
  33. package/src/commands/template.js +12 -4
  34. package/src/commands/update.js +263 -87
  35. package/src/commands/validate.js +115 -32
  36. package/src/framework-index.json +16 -14
  37. package/src/index.js +298 -145
  38. package/src/templates/fetchers/github.js +7 -7
  39. package/src/templates/fetchers/npm.js +3 -3
  40. package/src/templates/fetchers/release.js +21 -18
  41. package/src/templates/index.js +34 -12
  42. package/src/templates/processor.js +44 -12
  43. package/src/templates/resolver.js +42 -15
  44. package/src/templates/validator.js +14 -7
  45. package/src/utils/asset-upload.js +90 -22
  46. package/src/utils/code-upload.js +62 -37
  47. package/src/utils/config.js +31 -10
  48. package/src/utils/dep-survey.js +33 -7
  49. package/src/utils/destination-prompt.js +27 -17
  50. package/src/utils/git.js +66 -22
  51. package/src/utils/host-prompt.js +4 -3
  52. package/src/utils/install-integrity.js +8 -4
  53. package/src/utils/interactive.js +3 -3
  54. package/src/utils/json-file.js +6 -2
  55. package/src/utils/names.js +15 -6
  56. package/src/utils/placement.js +16 -8
  57. package/src/utils/registry-auth.js +185 -57
  58. package/src/utils/registry-orgs.js +142 -53
  59. package/src/utils/runtime-upload.js +52 -14
  60. package/src/utils/scaffold.js +55 -14
  61. package/src/utils/site-content-refs.js +3 -1
  62. package/src/utils/update-check.js +43 -17
  63. package/src/utils/workspace.js +13 -7
  64. package/src/versions.js +18 -7
  65. package/templates/site/_gitignore +5 -0
@@ -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(`Run this command from your project root or a site/foundation directory.`)
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 { folderWillRename, newPath: oldPath, newDir: join(rootDir, oldPath) }
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(rootDir, oldPath, newPath) {
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 (Array.isArray(rootPkg.workspaces) && rootPkg.workspaces.includes(oldPath)) {
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(`Cannot rename: a package named ${colors.bright}${newName}${colors.reset} already exists in this workspace.`)
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(`No foundation named ${colors.bright}${oldName}${colors.reset} in this workspace.`)
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(`${colors.bright}${oldName}${colors.reset} is an extension, not a foundation.`)
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 { folderWillRename, newPath: newFoundationPath, newDir: newFoundationDir } =
225
- computeNewFolderPath(rootDir, oldFoundationPath, oldName, newName)
243
+ const {
244
+ folderWillRename,
245
+ newPath: newFoundationPath,
246
+ newDir: newFoundationDir
247
+ } = computeNewFolderPath(rootDir, oldFoundationPath, oldName, newName)
226
248
  if (folderWillRename && existsSync(newFoundationDir)) {
227
- error(`Cannot rename: target folder ${colors.bright}${newFoundationPath}/${colors.reset} already exists.`)
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(`${colors.bright}Rename foundation${colors.reset}: ${colors.yellow}${oldName}${colors.reset} → ${colors.green}${newName}${colors.reset}`)
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(` Folder: ${oldFoundationPath}/ (unchanged — leaf doesn't match package name)`)
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 // npm-pinned, leave it; rename-then-republish is out of scope.
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(s.siteYmlPath, yaml.dump(newYmlData, { flowLevel: -1, quotingType: "'" }))
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(rootDir, oldFoundationPath, newFoundationPath)
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(`Renamed foundation ${colors.bright}${oldName}${colors.reset} → ${colors.bright}${newName}${colors.reset}`)
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(`No site named ${colors.bright}${oldName}${colors.reset} in this workspace.`)
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 { folderWillRename, newPath: newSitePath, newDir: newSiteDir } =
347
- computeNewFolderPath(rootDir, oldSitePath, oldName, newName)
385
+ const {
386
+ folderWillRename,
387
+ newPath: newSitePath,
388
+ newDir: newSiteDir
389
+ } = computeNewFolderPath(rootDir, oldSitePath, oldName, newName)
348
390
  if (folderWillRename && existsSync(newSiteDir)) {
349
- error(`Cannot rename: target folder ${colors.bright}${newSitePath}/${colors.reset} already exists.`)
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(`${colors.bright}Rename site${colors.reset}: ${colors.yellow}${oldName}${colors.reset} → ${colors.green}${newName}${colors.reset}`)
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(` Folder: ${oldSitePath}/ (unchanged — leaf doesn't match package name)`)
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(rootDir, oldSitePath, newSitePath)
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(`Renamed site ${colors.bright}${oldName}${colors.reset} → ${colors.bright}${newName}${colors.reset}`)
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(`No package named ${colors.bright}${oldName}${colors.reset} in this workspace.`)
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(`${colors.bright}${oldName}${colors.reset} is a foundation, not an extension.`)
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 { folderWillRename, newPath: newExtPath, newDir: newExtDir } =
407
- computeNewFolderPath(rootDir, oldExtPath, oldName, newName)
464
+ const {
465
+ folderWillRename,
466
+ newPath: newExtPath,
467
+ newDir: newExtDir
468
+ } = computeNewFolderPath(rootDir, oldExtPath, oldName, newName)
408
469
  if (folderWillRename && existsSync(newExtDir)) {
409
- error(`Cannot rename: target folder ${colors.bright}${newExtPath}/${colors.reset} already exists.`)
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(e => typeof e === 'string' && e.startsWith(oldUrlPrefix))
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(`${colors.bright}Rename extension${colors.reset}: ${colors.yellow}${oldName}${colors.reset} → ${colors.green}${newName}${colors.reset}`)
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(` Folder: ${oldExtPath}/ (unchanged — leaf doesn't match package name)`)
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(` • ${site.name} at ${site.path}/ (${hits.length} entr${hits.length === 1 ? 'y' : 'ies'})`)
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(a.siteYmlPath, yaml.dump(newYmlData, { flowLevel: -1, quotingType: "'" }))
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(`Renamed extension ${colors.bright}${oldName}${colors.reset} → ${colors.bright}${newName}${colors.reset}`)
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(`Next: ${colors.cyan}${installCmd(pm)}${colors.reset} ${colors.dim}(refresh symlinks under the new name)${colors.reset}`)
491
- log(` ${colors.cyan}${prefix} doctor${colors.reset} ${colors.dim}(verify wiring)${colors.reset}`)
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) {
@@ -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 { collectRuntimeFiles, hasWorkerRuntime, hasShims } from '../utils/runtime-upload.js'
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', bold: '\x1b[1m', dim: '\x1b[2m',
30
- cyan: '\x1b[36m', green: '\x1b[32m', yellow: '\x1b[33m', red: '\x1b[31m',
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 (JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf8')).name === '@uniweb/runtime') {
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(sub ? `Unknown subcommand: runtime ${sub}` : 'Usage: uniweb runtime register')
58
- say.dim('uniweb runtime register — upload the built @uniweb/runtime to the backend (@std only).')
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(readFileSync(join(distDir, 'app', 'manifest.json'), 'utf8')).version
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('dist/app/manifest.json has no "version" field — rebuild the runtime.')
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("dist/worker-runtime.js is missing — the SSR isolate bundle won't be uploaded.")
97
- say.dim('Build it first: `pnpm build:worker` in @uniweb/runtime (after `pnpm build`).')
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('dist/worker-runtime.js is present but dist/shims/ is missing — the SSR isolate set is incomplete.')
100
- say.dim('The isolate resolves react/jsx-runtime/@uniweb/core through those shims; re-run `pnpm build:worker`.')
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(`Would register ${c.bold}@uniweb/runtime@${version}${c.reset} (${files.length} files):`)
105
- for (const f of files) say.dim(`${f.path} ${f.size} bytes ${f.content_type}`)
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: readFlagValue(rest, '--backend') || readFlagValue(rest, '--registry'),
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(`Registering ${c.bold}@uniweb/runtime@${version}${c.reset} → ${c.dim}${client.origin}${c.reset} (${files.length} files)…`)
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({ version, distDir, files, onProgress: (m) => say.dim(m) })
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('Not authorized — registering a runtime version requires @std membership.')
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('Set the origin with --backend <url>; auth with `uniweb login` or --token <bearer>.')
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) say.dim(`${f.path} — HTTP ${f.status} ${f.detail}`)
132
- say.dim('Re-run `uniweb runtime register` to resume completed files dedupe.')
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(`Registered ${c.bold}@uniweb/runtime@${version}${c.reset} (${result.uploaded.length} files, ${result.mode} mode)`)
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
  }