everything-dev 1.17.0 → 1.20.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 (52) hide show
  1. package/dist/cli/init.cjs +165 -84
  2. package/dist/cli/init.cjs.map +1 -1
  3. package/dist/cli/init.d.cts +14 -2
  4. package/dist/cli/init.d.cts.map +1 -1
  5. package/dist/cli/init.d.mts +14 -2
  6. package/dist/cli/init.d.mts.map +1 -1
  7. package/dist/cli/init.mjs +164 -85
  8. package/dist/cli/init.mjs.map +1 -1
  9. package/dist/cli/prompts.cjs +12 -14
  10. package/dist/cli/prompts.cjs.map +1 -1
  11. package/dist/cli/prompts.mjs +12 -14
  12. package/dist/cli/prompts.mjs.map +1 -1
  13. package/dist/cli/timing.cjs +21 -1
  14. package/dist/cli/timing.cjs.map +1 -1
  15. package/dist/cli/timing.mjs +21 -1
  16. package/dist/cli/timing.mjs.map +1 -1
  17. package/dist/cli/upgrade.cjs +114 -49
  18. package/dist/cli/upgrade.cjs.map +1 -1
  19. package/dist/cli/upgrade.mjs +114 -49
  20. package/dist/cli/upgrade.mjs.map +1 -1
  21. package/dist/contract.cjs +1 -4
  22. package/dist/contract.cjs.map +1 -1
  23. package/dist/contract.d.cts +4 -10
  24. package/dist/contract.d.cts.map +1 -1
  25. package/dist/contract.d.mts +4 -10
  26. package/dist/contract.d.mts.map +1 -1
  27. package/dist/contract.meta.cjs +5 -5
  28. package/dist/contract.meta.cjs.map +1 -1
  29. package/dist/contract.meta.d.cts +9 -9
  30. package/dist/contract.meta.d.mts +9 -9
  31. package/dist/contract.meta.mjs +5 -5
  32. package/dist/contract.meta.mjs.map +1 -1
  33. package/dist/contract.mjs +1 -4
  34. package/dist/contract.mjs.map +1 -1
  35. package/dist/plugin.cjs +83 -119
  36. package/dist/plugin.cjs.map +1 -1
  37. package/dist/plugin.d.cts +3 -6
  38. package/dist/plugin.d.cts.map +1 -1
  39. package/dist/plugin.d.mts +3 -6
  40. package/dist/plugin.d.mts.map +1 -1
  41. package/dist/plugin.mjs +84 -120
  42. package/dist/plugin.mjs.map +1 -1
  43. package/dist/types.d.cts +2 -2
  44. package/dist/types.d.mts +2 -2
  45. package/package.json +5 -5
  46. package/src/cli/init.ts +224 -162
  47. package/src/cli/prompts.ts +17 -22
  48. package/src/cli/timing.ts +27 -0
  49. package/src/cli/upgrade.ts +173 -56
  50. package/src/contract.meta.ts +6 -8
  51. package/src/contract.ts +1 -4
  52. package/src/plugin.ts +189 -209
package/src/plugin.ts CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  runBunInstall,
16
16
  runDockerComposeUp,
17
17
  runTypesGen,
18
+ scaffoldMinimalProject,
18
19
  writeInitSnapshot,
19
20
  } from "./cli/init";
20
21
  import { promptInitOptions } from "./cli/prompts";
@@ -58,7 +59,7 @@ import {
58
59
  } from "./service-descriptor";
59
60
  import { syncAndGenerateSharedUi } from "./shared";
60
61
  import { writePluginSidebarGen } from "./sidebar";
61
- import type { BosConfig, BosPluginRef, RuntimeConfig, SourceMode } from "./types";
62
+ import type { BosConfig, BosConfigInput, BosPluginRef, RuntimeConfig, SourceMode } from "./types";
62
63
  import { run } from "./utils/run";
63
64
  import { saveBosConfig } from "./utils/save-config";
64
65
  import { colors } from "./utils/theme";
@@ -646,96 +647,32 @@ export default createPlugin({
646
647
  const version = manifest?.plugin.version ?? pkgJson.version;
647
648
 
648
649
  if (publishedUrl) {
649
- const pluginConfigPath = join(localPath, "bos.config.json");
650
- if (existsSync(pluginConfigPath)) {
651
- try {
652
- const pluginConfig = JSON.parse(readFileSync(pluginConfigPath, "utf-8")) as Record<
653
- string,
654
- unknown
655
- >;
656
- if (!pluginConfig.plugins || typeof pluginConfig.plugins !== "object") {
657
- pluginConfig.plugins = {};
658
- }
659
- const plugins = pluginConfig.plugins as Record<string, unknown>;
660
- if (!plugins[input.key] || typeof plugins[input.key] !== "object") {
661
- plugins[input.key] = {};
662
- }
663
- const entry = plugins[input.key] as Record<string, unknown>;
664
- entry.production = publishedUrl;
665
- if (integrity) {
666
- entry.integrity = integrity;
667
- } else {
668
- delete entry.integrity;
669
- }
670
- writeFileSync(pluginConfigPath, `${JSON.stringify(pluginConfig, null, 2)}\n`);
671
- console.log(` ✅ Updated ${pluginConfigPath}: plugins.${input.key}.production`);
672
- } catch (err) {
673
- console.error(
674
- ` ❌ Failed to update plugin bos.config.json:`,
675
- err instanceof Error ? err.message : err,
676
- );
677
- }
678
- }
679
-
680
- const account = deps.bosConfig.account;
681
- const network = getNetworkIdForAccount(account);
682
-
683
- let pluginDomain: string | undefined;
684
- if (existsSync(pluginConfigPath)) {
685
- try {
686
- const pluginConfig = JSON.parse(readFileSync(pluginConfigPath, "utf-8"));
687
- if (typeof pluginConfig.domain === "string") {
688
- pluginDomain = pluginConfig.domain;
689
- }
690
- } catch {}
691
- }
692
- if (!pluginDomain) {
693
- pluginDomain = `${input.key}.${deps.bosConfig.domain ?? "everything.dev"}`;
694
- }
695
-
650
+ const rootConfigPath = join(deps.configDir, "bos.config.json");
696
651
  try {
697
- const registryEntries: Record<string, string> = {};
698
-
699
- if (existsSync(pluginConfigPath)) {
700
- try {
701
- const publishedPluginConfig = JSON.parse(readFileSync(pluginConfigPath, "utf-8"));
702
- delete publishedPluginConfig.development;
703
- registryEntries[`apps/${account}/${pluginDomain}/bos.config.json`] =
704
- JSON.stringify(publishedPluginConfig);
705
- } catch {}
652
+ const rootConfig = JSON.parse(readFileSync(rootConfigPath, "utf-8")) as Record<
653
+ string,
654
+ unknown
655
+ >;
656
+ if (!rootConfig.plugins || typeof rootConfig.plugins !== "object") {
657
+ rootConfig.plugins = {};
706
658
  }
707
-
708
- if (Object.keys(registryEntries).length > 0) {
709
- const payload = JSON.stringify(registryEntries);
710
- const argsBase64 = Buffer.from(payload).toString("base64");
711
- const privateKey = process.env.NEAR_PRIVATE_KEY || process.env.BOS_NEAR_PRIVATE_KEY;
712
-
713
- await Effect.runPromise(ensureNearCli);
714
- try {
715
- await Effect.runPromise(
716
- executeTransaction({
717
- account,
718
- contract: getRegistryNamespaceForNetwork(network),
719
- method: "__fastdata_kv",
720
- argsBase64,
721
- network,
722
- privateKey,
723
- gas: "50Tgas",
724
- deposit: "0NEAR",
725
- }),
726
- );
727
- } catch (registryError) {
728
- const txHash = extractTransactionHash(registryError);
729
- if (!txHash) {
730
- console.warn(
731
- `[publish] Plugin registry write failed: ${registryError instanceof Error ? registryError.message : registryError}`,
732
- );
733
- }
734
- }
659
+ const plugins = rootConfig.plugins as Record<string, unknown>;
660
+ if (!plugins[input.key] || typeof plugins[input.key] !== "object") {
661
+ plugins[input.key] = {};
735
662
  }
736
- } catch (registryError) {
737
- console.warn(
738
- `[publish] Plugin registry write skipped: ${registryError instanceof Error ? registryError.message : registryError}`,
663
+ const entry = plugins[input.key] as Record<string, unknown>;
664
+ entry.production = publishedUrl;
665
+ if (integrity) {
666
+ entry.integrity = integrity;
667
+ } else {
668
+ delete entry.integrity;
669
+ }
670
+ writeFileSync(rootConfigPath, `${JSON.stringify(rootConfig, null, 2)}\n`);
671
+ console.log(` ✅ Updated bos.config.json: plugins.${input.key}.production`);
672
+ } catch (err) {
673
+ console.error(
674
+ ` ❌ Failed to update bos.config.json:`,
675
+ err instanceof Error ? err.message : err,
739
676
  );
740
677
  }
741
678
 
@@ -1143,29 +1080,6 @@ export default createPlugin({
1143
1080
  [`apps/${account}/${gateway}/bos.config.json`]: JSON.stringify(publishConfig),
1144
1081
  };
1145
1082
 
1146
- for (const [pluginKey, pluginEntry] of Object.entries(publishConfig.plugins ?? {})) {
1147
- const pluginRef = getPluginRef(pluginEntry);
1148
- if (!pluginRef?.development?.startsWith("local:")) continue;
1149
-
1150
- const localPath = join(deps.configDir, pluginRef.development.slice("local:".length));
1151
- const pluginConfigPath = join(localPath, "bos.config.json");
1152
- if (!existsSync(pluginConfigPath)) continue;
1153
-
1154
- try {
1155
- const pluginConfig = JSON.parse(readFileSync(pluginConfigPath, "utf-8")) as Record<
1156
- string,
1157
- unknown
1158
- >;
1159
- const pluginDomain =
1160
- typeof pluginConfig.domain === "string"
1161
- ? pluginConfig.domain
1162
- : `${pluginKey}.${gateway}`;
1163
- delete pluginConfig.development;
1164
- registryEntries[`apps/${account}/${pluginDomain}/bos.config.json`] =
1165
- JSON.stringify(pluginConfig);
1166
- } catch {}
1167
- }
1168
-
1169
1083
  const payload = JSON.stringify(registryEntries);
1170
1084
  const argsBase64 = Buffer.from(payload).toString("base64");
1171
1085
  const privateKey =
@@ -1277,8 +1191,8 @@ export default createPlugin({
1277
1191
  init: builder.init.handler(async ({ input }) => {
1278
1192
  try {
1279
1193
  const timings: PhaseTiming[] = [];
1280
- let extendsAccount = input.extendsAccount;
1281
- let extendsGateway = input.extendsGateway;
1194
+ let extendsAccount = "";
1195
+ let extendsGateway = "";
1282
1196
  let directory = input.directory;
1283
1197
  let account = input.account;
1284
1198
  let domain = input.domain;
@@ -1286,21 +1200,30 @@ export default createPlugin({
1286
1200
  let plugins = input.plugins;
1287
1201
 
1288
1202
  if (input.extends) {
1289
- const match = input.extends.match(/^(?:bos:\/\/)?([^/]+)\/(.+)$/);
1203
+ const normalized = input.extends.startsWith("bos://")
1204
+ ? input.extends
1205
+ : `bos://${input.extends}`;
1206
+ const match = normalized.match(/^bos:\/\/([^/]+)\/(.+)$/);
1290
1207
  if (match) {
1291
- if (!extendsAccount) extendsAccount = match[1];
1292
- if (!extendsGateway) extendsGateway = match[2];
1208
+ extendsAccount = match[1];
1209
+ extendsGateway = match[2];
1293
1210
  }
1294
1211
  }
1295
1212
 
1296
1213
  extendsAccount = extendsAccount || "dev.everything.near";
1297
1214
  extendsGateway = extendsGateway || "everything.dev";
1298
1215
 
1216
+ const s = p.spinner();
1217
+ s.start("Initializing project");
1218
+
1299
1219
  let parentPluginKeys: string[] = [];
1300
1220
  let parentConfig: BosConfig | null = null;
1301
1221
  try {
1302
- parentConfig = await timePhase(timings, "parent config", () =>
1303
- fetchParentConfig(extendsAccount!, extendsGateway!),
1222
+ parentConfig = await timePhase(
1223
+ timings,
1224
+ "parent config",
1225
+ () => fetchParentConfig(extendsAccount, extendsGateway),
1226
+ s,
1304
1227
  );
1305
1228
  if (parentConfig?.plugins && typeof parentConfig.plugins === "object") {
1306
1229
  parentPluginKeys = Object.keys(parentConfig.plugins);
@@ -1308,10 +1231,9 @@ export default createPlugin({
1308
1231
  } catch {}
1309
1232
 
1310
1233
  if (!input.noInteractive) {
1234
+ s.stop("Config fetched");
1311
1235
  const prompted = await promptInitOptions({
1312
- extendsAccount,
1313
- extendsGateway,
1314
- extends: input.extends,
1236
+ extends: `bos://${extendsAccount}/${extendsGateway}`,
1315
1237
  directory,
1316
1238
  account,
1317
1239
  domain,
@@ -1326,30 +1248,35 @@ export default createPlugin({
1326
1248
  domain = prompted.domain;
1327
1249
  withHost = prompted.withHost;
1328
1250
  plugins = prompted.plugins;
1251
+ s.start("Setting up project");
1329
1252
  }
1330
1253
 
1331
1254
  directory = directory || domain || extendsGateway;
1332
1255
  const targetDir = resolve(directory);
1333
1256
  plugins = plugins ?? [];
1257
+ const extendsRef = `bos://${extendsAccount}/${extendsGateway}`;
1334
1258
 
1335
1259
  if (!parentConfig) {
1336
1260
  try {
1337
- parentConfig = await timePhase(timings, "parent config", () =>
1338
- fetchParentConfig(extendsAccount!, extendsGateway!),
1261
+ parentConfig = await timePhase(
1262
+ timings,
1263
+ "parent config",
1264
+ () => fetchParentConfig(extendsAccount, extendsGateway),
1265
+ s,
1339
1266
  );
1340
1267
  } catch {
1268
+ s.stop("Failed");
1341
1269
  return {
1342
1270
  status: "error" as const,
1343
1271
  directory,
1344
- extendsAccount,
1345
- extendsGateway,
1272
+ extendsRef,
1346
1273
  account,
1347
1274
  domain,
1348
- extends: `bos://${extendsAccount}/${extendsGateway}`,
1275
+ extends: extendsRef,
1349
1276
  plugins: plugins ?? [],
1350
1277
  filesCopied: 0,
1351
1278
  timings,
1352
- error: `No config found at bos://${extendsAccount}/${extendsGateway} — are you sure this is the right parent?`,
1279
+ error: `No config found at ${extendsRef} — are you sure this is the right parent?`,
1353
1280
  };
1354
1281
  }
1355
1282
  }
@@ -1358,100 +1285,153 @@ export default createPlugin({
1358
1285
  sourceDir,
1359
1286
  parentConfig: resolvedParentConfig,
1360
1287
  cleanup,
1361
- } = await timePhase(timings, "template source", () =>
1362
- resolveSourceDir({
1363
- extendsAccount,
1364
- extendsGateway,
1365
- source: input.source,
1366
- }),
1288
+ } = await timePhase(
1289
+ timings,
1290
+ "template source",
1291
+ () =>
1292
+ resolveSourceDir({
1293
+ extendsAccount,
1294
+ extendsGateway,
1295
+ source: input.source,
1296
+ }),
1297
+ s,
1367
1298
  );
1368
1299
 
1369
1300
  parentConfig = resolvedParentConfig;
1370
1301
 
1302
+ const isMinimalScaffold = sourceDir === "";
1303
+
1371
1304
  try {
1372
- const patterns = await readTemplatekeep(sourceDir);
1373
- if (patterns.length === 0) {
1374
- return {
1375
- status: "error" as const,
1376
- directory,
1377
- extendsAccount,
1378
- extendsGateway,
1379
- account,
1380
- domain,
1381
- extends: `bos://${extendsAccount}/${extendsGateway}`,
1382
- plugins: plugins ?? [],
1383
- filesCopied: 0,
1384
- error: "No .templatekeep found in template source",
1385
- };
1386
- }
1305
+ let filesCopied: number;
1387
1306
 
1388
- const pluginRoutes: Record<string, string[]> = {};
1389
- const parentRuntimePlugins = await buildRuntimePluginsForConfig(
1390
- parentConfig as BosConfig,
1391
- sourceDir,
1392
- "production",
1393
- );
1394
- for (const [key, plugin] of Object.entries(parentRuntimePlugins ?? {})) {
1395
- if (plugin.routes && plugin.routes.length > 0) {
1396
- pluginRoutes[key] = plugin.routes;
1307
+ if (isMinimalScaffold) {
1308
+ filesCopied = await timePhase(
1309
+ timings,
1310
+ "scaffold project",
1311
+ () =>
1312
+ scaffoldMinimalProject(targetDir, parentConfig as unknown as BosConfigInput, {
1313
+ extendsAccount,
1314
+ extendsGateway,
1315
+ account: account || extendsAccount,
1316
+ domain,
1317
+ plugins,
1318
+ withHost,
1319
+ }),
1320
+ s,
1321
+ );
1322
+ } else {
1323
+ const patterns = await readTemplatekeep(sourceDir);
1324
+ if (patterns.length === 0) {
1325
+ s.stop("Failed");
1326
+ return {
1327
+ status: "error" as const,
1328
+ directory,
1329
+ extendsRef,
1330
+ account,
1331
+ domain,
1332
+ extends: extendsRef,
1333
+ plugins: plugins ?? [],
1334
+ filesCopied: 0,
1335
+ error: "No .templatekeep found in template source",
1336
+ };
1397
1337
  }
1398
- }
1399
1338
 
1400
- const s = p.spinner();
1401
- s.start("Setting up project");
1339
+ const pluginRoutes: Record<string, string[]> = {};
1340
+ const parentRuntimePlugins = await buildRuntimePluginsForConfig(
1341
+ parentConfig as BosConfig,
1342
+ sourceDir,
1343
+ "production",
1344
+ );
1345
+ for (const [key, plugin] of Object.entries(parentRuntimePlugins ?? {})) {
1346
+ if (plugin.routes && plugin.routes.length > 0) {
1347
+ pluginRoutes[key] = plugin.routes;
1348
+ }
1349
+ }
1402
1350
 
1403
- const filesCopied = await timePhase(timings, "copy files", () =>
1404
- copyFilteredFiles(sourceDir, targetDir, patterns, {
1405
- withHost,
1406
- plugins,
1407
- pluginRoutes,
1408
- }),
1409
- );
1351
+ filesCopied = await timePhase(
1352
+ timings,
1353
+ "copy files",
1354
+ () =>
1355
+ copyFilteredFiles(sourceDir, targetDir, patterns, {
1356
+ withHost,
1357
+ plugins,
1358
+ pluginRoutes,
1359
+ }),
1360
+ s,
1361
+ );
1410
1362
 
1411
- await timePhase(timings, "personalize config", () =>
1412
- personalizeConfig(targetDir, {
1413
- extendsAccount,
1414
- extendsGateway,
1415
- account: account || extendsAccount,
1416
- domain: domain || extendsGateway,
1417
- plugins,
1418
- pluginRoutes,
1419
- workspaceOpts: { sourceDir },
1420
- withHost,
1421
- }),
1422
- );
1363
+ await timePhase(
1364
+ timings,
1365
+ "personalize config",
1366
+ () =>
1367
+ personalizeConfig(targetDir, {
1368
+ extendsAccount,
1369
+ extendsGateway,
1370
+ account: account || extendsAccount,
1371
+ domain: domain || extendsGateway,
1372
+ plugins,
1373
+ pluginRoutes,
1374
+ workspaceOpts: { sourceDir },
1375
+ withHost,
1376
+ }),
1377
+ s,
1378
+ );
1423
1379
 
1424
- await timePhase(timings, "write snapshot", () =>
1425
- writeInitSnapshot(targetDir, extendsAccount, extendsGateway, sourceDir, patterns, {
1426
- withHost,
1427
- plugins,
1428
- pluginRoutes,
1429
- }),
1430
- );
1380
+ await timePhase(
1381
+ timings,
1382
+ "write snapshot",
1383
+ () =>
1384
+ writeInitSnapshot(targetDir, extendsAccount, extendsGateway, sourceDir, patterns, {
1385
+ withHost,
1386
+ plugins,
1387
+ pluginRoutes,
1388
+ }),
1389
+ s,
1390
+ );
1391
+ }
1431
1392
 
1432
- const initConfig = await timePhase(timings, "resolve config", () =>
1433
- loadConfig({ cwd: targetDir }),
1393
+ const initConfig = await timePhase(
1394
+ timings,
1395
+ "resolve config",
1396
+ () => loadConfig({ cwd: targetDir }),
1397
+ s,
1434
1398
  );
1435
1399
  if (initConfig?.runtime) {
1436
- await timePhase(timings, "generate env/docker", async () => {
1437
- writeGeneratedInfra(targetDir, initConfig.runtime);
1438
- });
1400
+ await timePhase(
1401
+ timings,
1402
+ "generate env/docker",
1403
+ async () => {
1404
+ writeGeneratedInfra(targetDir, initConfig.runtime);
1405
+ },
1406
+ s,
1407
+ );
1439
1408
  }
1440
- await timePhase(timings, "create env file", async () => {
1441
- ensureEnvFile(targetDir);
1442
- });
1409
+ await timePhase(
1410
+ timings,
1411
+ "create env file",
1412
+ async () => {
1413
+ ensureEnvFile(targetDir);
1414
+ },
1415
+ s,
1416
+ );
1443
1417
 
1444
1418
  if (!input.noInstall) {
1445
- await timePhase(timings, "install dependencies", () => runBunInstall(targetDir));
1446
- await timePhase(timings, "generate types", () => runTypesGen(targetDir));
1447
- await timePhase(timings, "generate migrations", () =>
1448
- generateDatabaseMigrations(targetDir),
1419
+ await timePhase(timings, "install dependencies", () => runBunInstall(targetDir), s);
1420
+ await timePhase(timings, "generate types", () => runTypesGen(targetDir), s);
1421
+ await timePhase(
1422
+ timings,
1423
+ "generate migrations",
1424
+ () => generateDatabaseMigrations(targetDir),
1425
+ s,
1449
1426
  );
1450
1427
  }
1451
1428
 
1452
1429
  if (input.noInstall && initConfig?.config) {
1453
- await timePhase(timings, "generate code artifacts", () =>
1454
- generateCodeArtifacts(targetDir, initConfig.config),
1430
+ await timePhase(
1431
+ timings,
1432
+ "generate code artifacts",
1433
+ () => generateCodeArtifacts(targetDir, initConfig.config),
1434
+ s,
1455
1435
  );
1456
1436
  }
1457
1437
 
@@ -1481,11 +1461,10 @@ export default createPlugin({
1481
1461
  return {
1482
1462
  status: "initialized" as const,
1483
1463
  directory,
1484
- extendsAccount,
1485
- extendsGateway,
1464
+ extendsRef,
1486
1465
  account,
1487
1466
  domain,
1488
- extends: `bos://${extendsAccount}/${extendsGateway}`,
1467
+ extends: extendsRef,
1489
1468
  plugins,
1490
1469
  filesCopied,
1491
1470
  timings,
@@ -1494,17 +1473,18 @@ export default createPlugin({
1494
1473
  await cleanup();
1495
1474
  }
1496
1475
  } catch (error) {
1476
+ const extendsRef = input.extends
1477
+ ? input.extends.startsWith("bos://")
1478
+ ? input.extends
1479
+ : `bos://${input.extends}`
1480
+ : "bos://dev.everything.near/everything.dev";
1497
1481
  return {
1498
1482
  status: "error" as const,
1499
1483
  directory: input.directory ?? "",
1500
- extendsAccount: input.extendsAccount ?? "",
1501
- extendsGateway: input.extendsGateway ?? "",
1484
+ extendsRef,
1502
1485
  account: input.account,
1503
1486
  domain: input.domain,
1504
- extends:
1505
- input.extendsAccount && input.extendsGateway
1506
- ? `bos://${input.extendsAccount}/${input.extendsGateway}`
1507
- : "",
1487
+ extends: extendsRef,
1508
1488
  plugins: input.plugins ?? [],
1509
1489
  filesCopied: 0,
1510
1490
  timings: [],