everything-dev 1.15.0 → 1.16.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 (82) hide show
  1. package/dist/cli/init.cjs +6 -18
  2. package/dist/cli/init.cjs.map +1 -1
  3. package/dist/cli/init.d.cts.map +1 -1
  4. package/dist/cli/init.d.mts.map +1 -1
  5. package/dist/cli/init.mjs +6 -18
  6. package/dist/cli/init.mjs.map +1 -1
  7. package/dist/cli/prompts.cjs +8 -8
  8. package/dist/cli/prompts.cjs.map +1 -1
  9. package/dist/cli/prompts.mjs +8 -8
  10. package/dist/cli/prompts.mjs.map +1 -1
  11. package/dist/cli/upgrade.cjs +75 -7
  12. package/dist/cli/upgrade.cjs.map +1 -1
  13. package/dist/cli/upgrade.mjs +74 -8
  14. package/dist/cli/upgrade.mjs.map +1 -1
  15. package/dist/cli.cjs +2 -0
  16. package/dist/cli.cjs.map +1 -1
  17. package/dist/cli.mjs +2 -0
  18. package/dist/cli.mjs.map +1 -1
  19. package/dist/config.cjs +2 -24
  20. package/dist/config.cjs.map +1 -1
  21. package/dist/config.d.cts.map +1 -1
  22. package/dist/config.d.mts.map +1 -1
  23. package/dist/config.mjs +3 -25
  24. package/dist/config.mjs.map +1 -1
  25. package/dist/contract.cjs +2 -0
  26. package/dist/contract.cjs.map +1 -1
  27. package/dist/contract.d.cts +10 -6
  28. package/dist/contract.d.cts.map +1 -1
  29. package/dist/contract.d.mts +10 -6
  30. package/dist/contract.d.mts.map +1 -1
  31. package/dist/contract.meta.cjs +2 -2
  32. package/dist/contract.meta.cjs.map +1 -1
  33. package/dist/contract.meta.d.cts +2 -2
  34. package/dist/contract.meta.d.mts +2 -2
  35. package/dist/contract.meta.mjs +2 -2
  36. package/dist/contract.meta.mjs.map +1 -1
  37. package/dist/contract.mjs +2 -0
  38. package/dist/contract.mjs.map +1 -1
  39. package/dist/fastkv.cjs +0 -45
  40. package/dist/fastkv.cjs.map +1 -1
  41. package/dist/fastkv.d.cts +1 -19
  42. package/dist/fastkv.d.cts.map +1 -1
  43. package/dist/fastkv.d.mts +1 -19
  44. package/dist/fastkv.d.mts.map +1 -1
  45. package/dist/fastkv.mjs +1 -44
  46. package/dist/fastkv.mjs.map +1 -1
  47. package/dist/index.cjs +0 -2
  48. package/dist/index.d.cts +2 -2
  49. package/dist/index.d.mts +2 -2
  50. package/dist/index.mjs +2 -2
  51. package/dist/merge.cjs +6 -11
  52. package/dist/merge.cjs.map +1 -1
  53. package/dist/merge.d.cts +1 -1
  54. package/dist/merge.d.cts.map +1 -1
  55. package/dist/merge.d.mts +1 -1
  56. package/dist/merge.d.mts.map +1 -1
  57. package/dist/merge.mjs +6 -11
  58. package/dist/merge.mjs.map +1 -1
  59. package/dist/near-cli.cjs +29 -52
  60. package/dist/near-cli.cjs.map +1 -1
  61. package/dist/near-cli.mjs +29 -52
  62. package/dist/near-cli.mjs.map +1 -1
  63. package/dist/plugin.cjs +36 -38
  64. package/dist/plugin.cjs.map +1 -1
  65. package/dist/plugin.d.cts +6 -4
  66. package/dist/plugin.d.mts +6 -4
  67. package/dist/plugin.mjs +36 -38
  68. package/dist/plugin.mjs.map +1 -1
  69. package/dist/types.d.cts +2 -2
  70. package/dist/types.d.mts +2 -2
  71. package/package.json +2 -1
  72. package/src/cli/init.ts +4 -24
  73. package/src/cli/prompts.ts +12 -8
  74. package/src/cli/upgrade.ts +117 -6
  75. package/src/cli.ts +6 -0
  76. package/src/config.ts +5 -34
  77. package/src/contract.meta.ts +2 -2
  78. package/src/contract.ts +2 -0
  79. package/src/fastkv.ts +0 -72
  80. package/src/merge.ts +8 -19
  81. package/src/near-cli.ts +42 -69
  82. package/src/plugin.ts +53 -47
@@ -1,9 +1,13 @@
1
1
  import { existsSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import process from "node:process";
4
+ import * as p from "@clack/prompts";
3
5
  import { glob } from "glob";
4
6
  import type { UpgradeOptions, UpgradeResult } from "../contract";
7
+ import { resolveExtendsRef } from "../merge";
8
+ import { saveBosConfig } from "../utils/save-config";
5
9
  import { readInstalledFrameworkVersion } from "./framework-version";
6
- import { runBunInstall, runTypesGen } from "./init";
10
+ import { fetchParentConfig, runBunInstall, runTypesGen } from "./init";
7
11
  import { syncTemplate } from "./sync";
8
12
 
9
13
  const FRAMEWORK_PACKAGES = ["everything-dev", "every-plugin"];
@@ -47,6 +51,105 @@ interface NpmPackageInfo {
47
51
  version: string;
48
52
  }
49
53
 
54
+ function getExtendsRef(config: Record<string, unknown>): string | undefined {
55
+ if (typeof config.extends === "string") {
56
+ return config.extends;
57
+ }
58
+
59
+ if (config.extends && typeof config.extends === "object") {
60
+ return resolveExtendsRef(config.extends as Record<string, string>, "production");
61
+ }
62
+
63
+ return undefined;
64
+ }
65
+
66
+ function parseBosRef(ref: string): { account: string; gateway: string } | null {
67
+ const match = ref.match(/^bos:\/\/([^/]+)\/(.+)$/);
68
+ if (!match?.[1] || !match[2]) return null;
69
+ return { account: match[1], gateway: match[2] };
70
+ }
71
+
72
+ async function loadParentPluginOptions(projectDir: string): Promise<{
73
+ localConfig: Record<string, unknown>;
74
+ parentPlugins: Record<string, unknown>;
75
+ newPluginKeys: string[];
76
+ } | null> {
77
+ const configPath = join(projectDir, "bos.config.json");
78
+ if (!existsSync(configPath)) {
79
+ return null;
80
+ }
81
+
82
+ const localConfig = JSON.parse(readFileSync(configPath, "utf-8")) as Record<string, unknown>;
83
+ const extendsRef = getExtendsRef(localConfig);
84
+ if (!extendsRef?.startsWith("bos://")) {
85
+ return null;
86
+ }
87
+
88
+ const parsed = parseBosRef(extendsRef);
89
+ if (!parsed) {
90
+ return null;
91
+ }
92
+
93
+ let parentConfig: Record<string, unknown>;
94
+ try {
95
+ parentConfig = await fetchParentConfig(parsed.account, parsed.gateway);
96
+ } catch {
97
+ return null;
98
+ }
99
+
100
+ const parentPlugins =
101
+ parentConfig.plugins && typeof parentConfig.plugins === "object"
102
+ ? (parentConfig.plugins as Record<string, unknown>)
103
+ : {};
104
+ const localPlugins =
105
+ localConfig.plugins && typeof localConfig.plugins === "object"
106
+ ? (localConfig.plugins as Record<string, unknown>)
107
+ : {};
108
+
109
+ const newPluginKeys = Object.keys(parentPlugins).filter((key) => !(key in localPlugins));
110
+ return { localConfig, parentPlugins, newPluginKeys };
111
+ }
112
+
113
+ async function addSelectedParentPlugins(projectDir: string): Promise<string[]> {
114
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
115
+ return [];
116
+ }
117
+
118
+ const pluginOptions = await loadParentPluginOptions(projectDir);
119
+ if (!pluginOptions || pluginOptions.newPluginKeys.length === 0) {
120
+ return [];
121
+ }
122
+
123
+ const selectedValue = await p.multiselect({
124
+ message: "Select new plugins from parent:",
125
+ options: pluginOptions.newPluginKeys.map((key) => ({ value: key, label: key })),
126
+ required: false,
127
+ });
128
+
129
+ if (p.isCancel(selectedValue)) {
130
+ process.exit(0);
131
+ }
132
+
133
+ const selected = selectedValue as string[];
134
+ if (selected.length === 0) {
135
+ return [];
136
+ }
137
+
138
+ const localPlugins =
139
+ pluginOptions.localConfig.plugins && typeof pluginOptions.localConfig.plugins === "object"
140
+ ? (pluginOptions.localConfig.plugins as Record<string, unknown>)
141
+ : {};
142
+ const nextPlugins = { ...localPlugins };
143
+ for (const key of selected) {
144
+ nextPlugins[key] = pluginOptions.parentPlugins[key];
145
+ }
146
+
147
+ pluginOptions.localConfig.plugins = nextPlugins;
148
+ await saveBosConfig(projectDir, pluginOptions.localConfig);
149
+
150
+ return selected;
151
+ }
152
+
50
153
  async function fetchLatestNpmVersion(packageName: string): Promise<string | null> {
51
154
  try {
52
155
  const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {
@@ -269,6 +372,7 @@ export async function upgradeTemplate(
269
372
 
270
373
  if (options.dryRun) {
271
374
  let changelogUrl: string | undefined;
375
+ const pluginOptions = options.noSync ? null : await loadParentPluginOptions(projectDir);
272
376
  if (hasUpdates) {
273
377
  const configPath = join(projectDir, "bos.config.json");
274
378
  let parentConfig: Record<string, unknown> | null = null;
@@ -289,6 +393,7 @@ export async function upgradeTemplate(
289
393
  ...packages,
290
394
  ...catalogVersionUpdates.map((u) => ({ name: u.name, from: u.from, to: u.to })),
291
395
  ],
396
+ availablePlugins: pluginOptions?.newPluginKeys,
292
397
  changelogUrl,
293
398
  };
294
399
  }
@@ -315,13 +420,13 @@ export async function upgradeTemplate(
315
420
  }
316
421
  }
317
422
 
318
- if (hasUpdates && !options.noInstall) {
319
- await runBunInstall(projectDir);
320
- await runTypesGen(projectDir);
321
- }
322
-
323
423
  let syncResult: UpgradeResult["sync"];
424
+ let addedPlugins: string[] = [];
324
425
  if (!options.noSync) {
426
+ if (!options.dryRun) {
427
+ addedPlugins = await addSelectedParentPlugins(projectDir);
428
+ }
429
+
325
430
  syncResult = await syncTemplate(projectDir, {
326
431
  dryRun: false,
327
432
  force: options.force,
@@ -329,6 +434,11 @@ export async function upgradeTemplate(
329
434
  });
330
435
  }
331
436
 
437
+ if ((hasUpdates || addedPlugins.length > 0) && !options.noInstall) {
438
+ await runBunInstall(projectDir);
439
+ await runTypesGen(projectDir);
440
+ }
441
+
332
442
  const migratedFiles = await rewriteLegacyUiImports(projectDir);
333
443
  for (const file of OBSOLETE_FILES) {
334
444
  const filePath = join(projectDir, file);
@@ -359,6 +469,7 @@ export async function upgradeTemplate(
359
469
  ],
360
470
  sync: syncResult,
361
471
  migrated: migratedFiles.length > 0 ? migratedFiles : undefined,
472
+ selectedPlugins: addedPlugins.length > 0 ? addedPlugins : undefined,
362
473
  changelogUrl,
363
474
  };
364
475
  }
package/src/cli.ts CHANGED
@@ -240,6 +240,12 @@ async function main() {
240
240
  if (result.changelogUrl) {
241
241
  console.log(` ${colors.dim("Changelog:")} ${result.changelogUrl}`);
242
242
  }
243
+ if (result.availablePlugins && result.availablePlugins.length > 0) {
244
+ console.log(` ${colors.dim("New parent plugins:")} ${result.availablePlugins.join(", ")}`);
245
+ }
246
+ if (result.selectedPlugins && result.selectedPlugins.length > 0) {
247
+ console.log(` ${colors.dim("Added plugins:")} ${result.selectedPlugins.join(", ")}`);
248
+ }
243
249
  if (result.sync) {
244
250
  const sync = result.sync;
245
251
  if (sync.updated.length > 0) {
package/src/config.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { dirname, isAbsolute, join, resolve } from "node:path";
3
- import { fetchBosConfigFromFastKv, fetchPluginFromRegistry, parsePluginBosUrl } from "./fastkv";
3
+ import { fetchBosConfigFromFastKv } from "./fastkv";
4
4
  import {
5
5
  type BosEnv,
6
6
  isPlainObject,
@@ -546,31 +546,6 @@ async function resolveRemotePluginRuntimeName(baseUrl: string, fallback: string)
546
546
  }
547
547
  }
548
548
 
549
- interface ResolvedBosPlugin {
550
- url: string;
551
- integrity?: string;
552
- }
553
-
554
- async function resolveBosPluginUrl(bosUrl: string): Promise<ResolvedBosPlugin | null> {
555
- const parsed = parsePluginBosUrl(bosUrl);
556
- if (!parsed) return null;
557
-
558
- try {
559
- const entry = await fetchPluginFromRegistry(parsed.accountId, parsed.pluginName);
560
- if (!entry) return null;
561
-
562
- const cdnUrl = entry.metadata.cdnUrl;
563
- if (!cdnUrl) return null;
564
-
565
- return {
566
- url: cdnUrl,
567
- integrity: entry.metadata.integrity ?? undefined,
568
- };
569
- } catch {
570
- return null;
571
- }
572
- }
573
-
574
549
  async function buildRuntimePluginConfig(
575
550
  pluginId: string,
576
551
  config: BosConfigInput,
@@ -586,16 +561,12 @@ async function buildRuntimePluginConfig(
586
561
  const sourceProduction = typeof source.production === "string" ? source.production : undefined;
587
562
  const proxy = typeof apiConfig.proxy === "string" ? apiConfig.proxy : undefined;
588
563
  const development = apiDevelopment ?? sourceDevelopment;
589
- let production = apiProduction ?? sourceProduction;
564
+ const production = apiProduction ?? sourceProduction;
590
565
 
591
566
  if (production?.startsWith("bos://")) {
592
- const resolved = await resolveBosPluginUrl(production);
593
- if (resolved) {
594
- production = resolved.url;
595
- if (resolved.integrity && env === "production") {
596
- source.integrity = resolved.integrity;
597
- }
598
- }
567
+ throw new Error(
568
+ `Plugin "${pluginId}" has unsupported production target "${production}". Use extends: "bos://account/domain" for plugin configs or a CDN URL for production.`,
569
+ );
599
570
  }
600
571
 
601
572
  const runtimeTarget =
@@ -44,7 +44,7 @@ export const cliCommandMeta = {
44
44
  fields: {
45
45
  source: {
46
46
  positional: true,
47
- description: "Plugin source (local:path, bos://account/plugins/name, or URL)",
47
+ description: "Plugin source (local:path, bos://account/domain, or URL)",
48
48
  },
49
49
  as: { description: "Plugin alias" },
50
50
  production: { description: "Production URL override" },
@@ -113,7 +113,7 @@ export const cliCommandMeta = {
113
113
  upgrade: {
114
114
  commandPath: ["upgrade"],
115
115
  summary: "Upgrade framework packages and sync template files",
116
- interactive: false,
116
+ interactive: true,
117
117
  fields: {
118
118
  dryRun: { description: "Preview changes without writing" },
119
119
  force: { description: "Overwrite user-modified files during sync" },
package/src/contract.ts CHANGED
@@ -201,6 +201,8 @@ export const UpgradeResultSchema = z.object({
201
201
  ),
202
202
  sync: SyncResultSchema.optional(),
203
203
  migrated: z.array(z.string()).optional(),
204
+ availablePlugins: z.array(z.string()).optional(),
205
+ selectedPlugins: z.array(z.string()).optional(),
204
206
  changelogUrl: z.string().optional(),
205
207
  error: z.string().optional(),
206
208
  });
package/src/fastkv.ts CHANGED
@@ -139,78 +139,6 @@ export interface PluginManifest {
139
139
  additionalExports?: Array<{ path: string; exports: string[]; sha256: string }>;
140
140
  }
141
141
 
142
- export interface PluginMetadata {
143
- title: string | null;
144
- description: string | null;
145
- repoUrl: string | null;
146
- version: string;
147
- publishedAt: string;
148
- cdnUrl: string;
149
- integrity: string | null;
150
- }
151
-
152
- export interface PluginRegistryEntry {
153
- manifest: PluginManifest;
154
- metadata: PluginMetadata;
155
- }
156
-
157
- export function parsePluginBosUrl(
158
- source: string,
159
- ): { accountId: string; pluginName: string } | null {
160
- if (!source.startsWith("bos://")) return null;
161
- const match = source.match(/^bos:\/\/([^/]+)\/plugins\/([^/]+)$/);
162
- if (!match?.[1] || !match[2]) return null;
163
- return { accountId: match[1], pluginName: match[2] };
164
- }
165
-
166
- async function fetchKvValue(accountId: string, key: string): Promise<unknown | null> {
167
- const payload = await fetchJson<FastKvListResponse>(
168
- `${getFastKvBaseUrlForAccount(accountId)}/v0/latest/${encodeURIComponent(getRegistryNamespaceForAccount(accountId))}/${encodeURIComponent(accountId)}`,
169
- {
170
- method: "POST",
171
- body: JSON.stringify({ key, limit: 1 }),
172
- },
173
- );
174
- const value = payload?.entries?.find(Boolean)?.value;
175
- if (value == null) return null;
176
- if (typeof value === "string") {
177
- try {
178
- return JSON.parse(value);
179
- } catch {
180
- return null;
181
- }
182
- }
183
- return value;
184
- }
185
-
186
- export async function fetchPluginFromRegistry(
187
- accountId: string,
188
- pluginName: string,
189
- ): Promise<PluginRegistryEntry | null> {
190
- const manifestKey = `plugins/${accountId}/${pluginName}/manifest.json`;
191
- const metadataKey = `plugins/${accountId}/${pluginName}/metadata`;
192
-
193
- const [rawManifest, rawMetadata] = await Promise.all([
194
- fetchKvValue(accountId, manifestKey),
195
- fetchKvValue(accountId, metadataKey),
196
- ]);
197
-
198
- if (!rawManifest || typeof rawManifest !== "object") return null;
199
-
200
- return {
201
- manifest: rawManifest as PluginManifest,
202
- metadata: (rawMetadata ?? {
203
- title: null,
204
- description: null,
205
- repoUrl: null,
206
- version: "",
207
- publishedAt: "",
208
- cdnUrl: "",
209
- integrity: null,
210
- }) as PluginMetadata,
211
- };
212
- }
213
-
214
142
  export async function fetchRemotePluginManifest(cdnUrl: string): Promise<PluginManifest | null> {
215
143
  try {
216
144
  const baseUrl = cdnUrl.replace(/\/$/, "");
package/src/merge.ts CHANGED
@@ -5,6 +5,8 @@ export const BOS_CONFIG_ORDER = [
5
5
  "extends",
6
6
  "account",
7
7
  "domain",
8
+ "title",
9
+ "description",
8
10
  "testnet",
9
11
  "staging",
10
12
  "repository",
@@ -93,28 +95,15 @@ export function mergeBosConfigWithExtends(
93
95
  parent: BosConfigInput,
94
96
  child: BosConfigInput,
95
97
  ): BosConfigInput {
96
- const merged = bosConfigMerger(child, parent) as BosConfigInput;
97
-
98
- if (isPlainObject(parent.plugins) && isPlainObject(child.plugins)) {
99
- const plugins: Record<string, unknown> = { ...parent.plugins };
100
- for (const [key, rawValue] of Object.entries(child.plugins)) {
101
- const value = rawValue as unknown;
102
- if (value === null || value === false) {
103
- delete plugins[key];
104
- } else if (isPlainObject(plugins[key]) && isPlainObject(value)) {
105
- plugins[key] = bosConfigMerger(
106
- value as Record<string, unknown>,
107
- plugins[key] as Record<string, unknown>,
108
- );
109
- } else {
110
- plugins[key] = value;
111
- }
112
- }
113
- (merged as Record<string, unknown>).plugins = plugins;
114
- } else if (child.plugins !== undefined) {
98
+ const { plugins: _ignoredParentPlugins, ...parentWithoutPlugins } = parent;
99
+ const merged = bosConfigMerger(child, parentWithoutPlugins) as BosConfigInput;
100
+
101
+ if (child.plugins !== undefined && isPlainObject(child.plugins)) {
115
102
  (merged as Record<string, unknown>).plugins = cleanNullSentinels(
116
103
  child.plugins as Record<string, unknown>,
117
104
  );
105
+ } else {
106
+ delete (merged as Record<string, unknown>).plugins;
118
107
  }
119
108
 
120
109
  const mergedRecord = merged as Record<string, unknown>;
package/src/near-cli.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { spawn } from "node:child_process";
2
1
  import { generateKeyPairSync } from "node:crypto";
3
2
  import { Effect } from "effect";
3
+ import { execa } from "execa";
4
4
 
5
5
  export interface NearTransactionConfig {
6
6
  account: string;
@@ -112,49 +112,34 @@ export function generateNearKeyPair(): NearKeyPair {
112
112
 
113
113
  const checkNearCliInstalled = Effect.tryPromise({
114
114
  try: async () => {
115
- return await new Promise<boolean>((resolve) => {
116
- const proc = spawn("near", ["--version"], { stdio: "pipe" });
117
- proc.on("close", (code) => resolve(code === 0));
118
- proc.on("error", () => resolve(false));
119
- });
115
+ try {
116
+ await execa("near", ["--version"], { stdio: "pipe" });
117
+ return true;
118
+ } catch {
119
+ return false;
120
+ }
120
121
  },
121
122
  catch: () => new Error("Failed to check NEAR CLI"),
122
123
  });
123
124
 
124
125
  const installNearCli = Effect.tryPromise({
125
126
  try: async () => {
126
- return await new Promise<void>((resolve, reject) => {
127
- const proc = spawn(
128
- "sh",
129
- ["-c", `curl --proto '=https' --tlsv1.2 -LsSf ${INSTALLER_URL} | sh`],
130
- {
131
- stdio: "inherit",
132
- },
133
- );
134
-
135
- proc.on("close", (code) => {
136
- if (code === 0) resolve();
137
- else reject(new NearCliInstallError(`Installer exited with code ${code}`));
138
- });
139
- proc.on("error", (err) => reject(new NearCliInstallError(err.message)));
127
+ await execa("sh", ["-c", `curl --proto '=https' --tlsv1.2 -LsSf ${INSTALLER_URL} | sh`], {
128
+ stdio: "inherit",
140
129
  });
141
130
  },
142
- catch: (error) => error as Error,
131
+ catch: (error) => {
132
+ if (error instanceof Error && "exitCode" in error) {
133
+ return new NearCliInstallError(
134
+ `Installer exited with code ${(error as { exitCode: number }).exitCode}`,
135
+ );
136
+ }
137
+ return new NearCliInstallError(error instanceof Error ? error.message : String(error));
138
+ },
143
139
  });
144
140
 
145
141
  async function runNearCommand(args: string[]): Promise<void> {
146
- await new Promise<void>((resolve, reject) => {
147
- const proc = spawn("near", args, {
148
- stdio: "inherit",
149
- });
150
-
151
- proc.on("close", (code) => {
152
- if (code === 0) resolve();
153
- else reject(new Error(`near ${args.join(" ")} failed with exit code ${code}`));
154
- });
155
-
156
- proc.on("error", (err) => reject(new Error(err.message)));
157
- });
142
+ await execa("near", args, { stdio: "inherit" });
158
143
  }
159
144
 
160
145
  export const ensureNearCli = Effect.gen(function* () {
@@ -209,43 +194,31 @@ export const executeTransaction = (
209
194
 
210
195
  const output = yield* Effect.tryPromise({
211
196
  try: async () => {
212
- return await new Promise<string>((resolve, reject) => {
213
- const proc = spawn("near", args, { stdio: ["inherit", "pipe", "pipe"] });
214
-
215
- let stdout = "";
216
- let stderr = "";
217
-
218
- proc.stdout?.on("data", (data) => {
219
- const text = data.toString();
220
- stdout += text;
221
- process.stdout.write(text);
222
- });
223
-
224
- proc.stderr?.on("data", (data) => {
225
- const text = data.toString();
226
- stderr += text;
227
- });
228
-
229
- proc.on("close", (code) => {
230
- const combined = `${stdout}\n${stderr}`;
231
- const txHashMatch = combined.match(/Transaction ID:\s*([A-Za-z0-9]+)/i);
232
- const hasCodeDoesNotExist = /CodeDoesNotExist/i.test(combined);
233
- const hasTransactionFailed = /Transaction failed/i.test(combined);
234
- const softSuccess =
235
- Boolean(txHashMatch?.[1]) && hasCodeDoesNotExist && hasTransactionFailed;
236
-
237
- if (code === 0 || softSuccess) {
238
- if (softSuccess) {
239
- console.log(` ${txHashMatch?.[1]} — FastDATA CodeDoesNotExist (expected)`);
240
- }
241
- resolve(combined);
242
- } else {
243
- reject(new NearTransactionError(stderr || `Transaction failed with code ${code}`));
244
- }
245
- });
246
-
247
- proc.on("error", (err) => reject(new NearTransactionError(err.message)));
197
+ const result = await execa("near", args, {
198
+ stdin: "inherit",
199
+ stdout: "pipe",
200
+ stderr: "pipe",
201
+ reject: false,
248
202
  });
203
+
204
+ process.stdout.write(result.stdout);
205
+ const combined = `${result.stdout}\n${result.stderr}`;
206
+ const txHashMatch = combined.match(/Transaction ID:\s*([A-Za-z0-9]+)/i);
207
+ const hasCodeDoesNotExist = /CodeDoesNotExist/i.test(combined);
208
+ const hasTransactionFailed = /Transaction failed/i.test(combined);
209
+ const softSuccess =
210
+ Boolean(txHashMatch?.[1]) && hasCodeDoesNotExist && hasTransactionFailed;
211
+
212
+ if (result.exitCode === 0 || softSuccess) {
213
+ if (softSuccess) {
214
+ console.log(` ${txHashMatch?.[1]} — FastDATA CodeDoesNotExist (expected)`);
215
+ }
216
+ return combined;
217
+ }
218
+
219
+ throw new NearTransactionError(
220
+ result.stderr || `Transaction failed with code ${result.exitCode}`,
221
+ );
249
222
  },
250
223
  catch: (error) => error as Error,
251
224
  });