everything-dev 1.15.0 → 1.16.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/dist/cli/init.cjs +6 -18
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts.map +1 -1
- package/dist/cli/init.d.mts.map +1 -1
- package/dist/cli/init.mjs +6 -18
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli/prompts.cjs +8 -8
- package/dist/cli/prompts.cjs.map +1 -1
- package/dist/cli/prompts.mjs +8 -8
- package/dist/cli/prompts.mjs.map +1 -1
- package/dist/contract.d.cts +6 -6
- package/dist/contract.d.mts +6 -6
- package/dist/merge.cjs +2 -0
- package/dist/merge.cjs.map +1 -1
- package/dist/merge.d.cts +1 -1
- package/dist/merge.d.cts.map +1 -1
- package/dist/merge.d.mts +1 -1
- package/dist/merge.d.mts.map +1 -1
- package/dist/merge.mjs +2 -0
- package/dist/merge.mjs.map +1 -1
- package/dist/near-cli.cjs +29 -52
- package/dist/near-cli.cjs.map +1 -1
- package/dist/near-cli.mjs +29 -52
- package/dist/near-cli.mjs.map +1 -1
- package/dist/plugin.cjs +15 -7
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +4 -4
- package/dist/plugin.d.mts +4 -4
- package/dist/plugin.mjs +15 -7
- package/dist/plugin.mjs.map +1 -1
- package/dist/types.d.cts +2 -2
- package/dist/types.d.mts +2 -2
- package/package.json +2 -1
- package/src/cli/init.ts +4 -24
- package/src/cli/prompts.ts +12 -8
- package/src/merge.ts +2 -0
- package/src/near-cli.ts +42 -69
- package/src/plugin.ts +38 -19
package/dist/cli/prompts.mjs
CHANGED
|
@@ -15,10 +15,6 @@ function deriveAccountFromDomain(domain, extendsAccount) {
|
|
|
15
15
|
if (!firstSegment) return "";
|
|
16
16
|
return `${firstSegment}.${extendsAccount.includes(".") ? extendsAccount.substring(extendsAccount.indexOf(".") + 1) : extendsAccount}`;
|
|
17
17
|
}
|
|
18
|
-
const AVAILABLE_PLUGINS = [{
|
|
19
|
-
value: "settings",
|
|
20
|
-
label: "settings"
|
|
21
|
-
}];
|
|
22
18
|
async function promptInitOptions(input) {
|
|
23
19
|
p.intro("Let's build an app...");
|
|
24
20
|
const domain = input.domain ?? await p.text({
|
|
@@ -50,12 +46,16 @@ async function promptInitOptions(input) {
|
|
|
50
46
|
});
|
|
51
47
|
if (p.isCancel(account)) process.exit(0);
|
|
52
48
|
const directory = input.directory || domain || extendsGateway;
|
|
53
|
-
const
|
|
49
|
+
const parentPlugins = input.parentPluginKeys ?? [];
|
|
50
|
+
const pluginOptions = parentPlugins.length > 0 ? parentPlugins.map((key) => ({
|
|
51
|
+
value: key,
|
|
52
|
+
label: key
|
|
53
|
+
})) : [];
|
|
54
|
+
const plugins = input.plugins ?? (pluginOptions.length > 0 ? await p.multiselect({
|
|
54
55
|
message: "Select plugins:",
|
|
55
|
-
options:
|
|
56
|
-
initialValues: ["settings"],
|
|
56
|
+
options: pluginOptions,
|
|
57
57
|
required: false
|
|
58
|
-
});
|
|
58
|
+
}) : []);
|
|
59
59
|
if (p.isCancel(plugins)) process.exit(0);
|
|
60
60
|
const go = input.withHost !== void 0 ? true : await p.confirm({
|
|
61
61
|
message: "GO!",
|
package/dist/cli/prompts.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.mjs","names":[],"sources":["../../src/cli/prompts.ts"],"sourcesContent":["import process from \"node:process\";\nimport * as p from \"@clack/prompts\";\n\nfunction parseExtendsRef(ref: string): { account: string; gateway: string } | null {\n const match = ref.match(/^(?:bos:\\/\\/)?([^/]+)\\/(.+)$/);\n if (!match) return null;\n return { account: match[1], gateway: match[2] };\n}\n\nfunction deriveAccountFromDomain(domain: string, extendsAccount: string): string {\n const firstSegment = domain.split(\".\")[0];\n if (!firstSegment) return \"\";\n const suffix = extendsAccount.includes(\".\")\n ? extendsAccount.substring(extendsAccount.indexOf(\".\") + 1)\n : extendsAccount;\n return `${firstSegment}.${suffix}`;\n}\n\
|
|
1
|
+
{"version":3,"file":"prompts.mjs","names":[],"sources":["../../src/cli/prompts.ts"],"sourcesContent":["import process from \"node:process\";\nimport * as p from \"@clack/prompts\";\n\nfunction parseExtendsRef(ref: string): { account: string; gateway: string } | null {\n const match = ref.match(/^(?:bos:\\/\\/)?([^/]+)\\/(.+)$/);\n if (!match) return null;\n return { account: match[1], gateway: match[2] };\n}\n\nfunction deriveAccountFromDomain(domain: string, extendsAccount: string): string {\n const firstSegment = domain.split(\".\")[0];\n if (!firstSegment) return \"\";\n const suffix = extendsAccount.includes(\".\")\n ? extendsAccount.substring(extendsAccount.indexOf(\".\") + 1)\n : extendsAccount;\n return `${firstSegment}.${suffix}`;\n}\n\nexport async function promptInitOptions(input: {\n extendsAccount?: string;\n extendsGateway?: string;\n extends?: string;\n directory?: string;\n account?: string;\n domain?: string;\n plugins?: string[];\n withHost?: boolean;\n parentPluginKeys?: string[];\n}): Promise<{\n extendsAccount: string;\n extendsGateway: string;\n directory: string;\n account?: string;\n domain?: string;\n plugins: string[];\n withHost: boolean;\n}> {\n p.intro(\"Let's build an app...\");\n\n const domain =\n input.domain ??\n ((await p.text({\n message: \"Starting with a domain?\",\n placeholder: \"no\",\n })) as string);\n\n if (p.isCancel(domain)) process.exit(0);\n\n const extendsPlaceholder = \"bos://dev.everything.near/everything.dev\";\n const extendsInput =\n input.extends ??\n ((await p.text({\n message: \"Extending an existing app?\",\n placeholder: extendsPlaceholder,\n })) as string);\n\n if (p.isCancel(extendsInput)) process.exit(0);\n\n let extendsAccount = input.extendsAccount || \"\";\n let extendsGateway = input.extendsGateway || \"\";\n\n if (extendsInput) {\n const parsed = parseExtendsRef(extendsInput);\n if (parsed) {\n extendsAccount = extendsAccount || parsed.account;\n extendsGateway = extendsGateway || parsed.gateway;\n }\n }\n\n extendsAccount = extendsAccount || \"dev.everything.near\";\n extendsGateway = extendsGateway || \"everything.dev\";\n\n const accountDefault = domain ? deriveAccountFromDomain(domain, extendsAccount) : \"\";\n const account =\n input.account ??\n ((await p.text({\n message: \"What NEAR account will you publish from?\",\n placeholder: accountDefault || \"skip\",\n defaultValue: accountDefault,\n })) as string);\n\n if (p.isCancel(account)) process.exit(0);\n\n const directory = input.directory || domain || extendsGateway;\n\n const parentPlugins = input.parentPluginKeys ?? [];\n const pluginOptions =\n parentPlugins.length > 0 ? parentPlugins.map((key) => ({ value: key, label: key })) : [];\n\n const plugins =\n input.plugins ??\n (pluginOptions.length > 0\n ? ((await p.multiselect({\n message: \"Select plugins:\",\n options: pluginOptions,\n required: false,\n })) as string[])\n : []);\n\n if (p.isCancel(plugins)) process.exit(0);\n\n const go =\n input.withHost !== undefined\n ? true\n : await p.confirm({\n message: \"GO!\",\n initialValue: true,\n });\n\n if (p.isCancel(go) || !go) process.exit(0);\n\n const withHost = input.withHost ?? false;\n\n return {\n extendsAccount,\n extendsGateway,\n directory,\n account: account || undefined,\n domain: domain || undefined,\n plugins,\n withHost,\n };\n}\n"],"mappings":";;;;AAGA,SAAS,gBAAgB,KAA0D;CACjF,MAAM,QAAQ,IAAI,MAAM,+BAA+B;AACvD,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO;EAAE,SAAS,MAAM;EAAI,SAAS,MAAM;EAAI;;AAGjD,SAAS,wBAAwB,QAAgB,gBAAgC;CAC/E,MAAM,eAAe,OAAO,MAAM,IAAI,CAAC;AACvC,KAAI,CAAC,aAAc,QAAO;AAI1B,QAAO,GAAG,aAAa,GAHR,eAAe,SAAS,IAAI,GACvC,eAAe,UAAU,eAAe,QAAQ,IAAI,GAAG,EAAE,GACzD;;AAIN,eAAsB,kBAAkB,OAkBrC;AACD,GAAE,MAAM,wBAAwB;CAEhC,MAAM,SACJ,MAAM,UACJ,MAAM,EAAE,KAAK;EACb,SAAS;EACT,aAAa;EACd,CAAC;AAEJ,KAAI,EAAE,SAAS,OAAO,CAAE,SAAQ,KAAK,EAAE;CAGvC,MAAM,eACJ,MAAM,WACJ,MAAM,EAAE,KAAK;EACb,SAAS;EACT,aALuB;EAMxB,CAAC;AAEJ,KAAI,EAAE,SAAS,aAAa,CAAE,SAAQ,KAAK,EAAE;CAE7C,IAAI,iBAAiB,MAAM,kBAAkB;CAC7C,IAAI,iBAAiB,MAAM,kBAAkB;AAE7C,KAAI,cAAc;EAChB,MAAM,SAAS,gBAAgB,aAAa;AAC5C,MAAI,QAAQ;AACV,oBAAiB,kBAAkB,OAAO;AAC1C,oBAAiB,kBAAkB,OAAO;;;AAI9C,kBAAiB,kBAAkB;AACnC,kBAAiB,kBAAkB;CAEnC,MAAM,iBAAiB,SAAS,wBAAwB,QAAQ,eAAe,GAAG;CAClF,MAAM,UACJ,MAAM,WACJ,MAAM,EAAE,KAAK;EACb,SAAS;EACT,aAAa,kBAAkB;EAC/B,cAAc;EACf,CAAC;AAEJ,KAAI,EAAE,SAAS,QAAQ,CAAE,SAAQ,KAAK,EAAE;CAExC,MAAM,YAAY,MAAM,aAAa,UAAU;CAE/C,MAAM,gBAAgB,MAAM,oBAAoB,EAAE;CAClD,MAAM,gBACJ,cAAc,SAAS,IAAI,cAAc,KAAK,SAAS;EAAE,OAAO;EAAK,OAAO;EAAK,EAAE,GAAG,EAAE;CAE1F,MAAM,UACJ,MAAM,YACL,cAAc,SAAS,IAClB,MAAM,EAAE,YAAY;EACpB,SAAS;EACT,SAAS;EACT,UAAU;EACX,CAAC,GACF,EAAE;AAER,KAAI,EAAE,SAAS,QAAQ,CAAE,SAAQ,KAAK,EAAE;CAExC,MAAM,KACJ,MAAM,aAAa,SACf,OACA,MAAM,EAAE,QAAQ;EACd,SAAS;EACT,cAAc;EACf,CAAC;AAER,KAAI,EAAE,SAAS,GAAG,IAAI,CAAC,GAAI,SAAQ,KAAK,EAAE;CAE1C,MAAM,WAAW,MAAM,YAAY;AAEnC,QAAO;EACL;EACA;EACA;EACA,SAAS,WAAW;EACpB,QAAQ,UAAU;EAClB;EACA;EACD"}
|
package/dist/contract.d.cts
CHANGED
|
@@ -57,8 +57,8 @@ declare const BuildOptionsSchema: z.ZodObject<{
|
|
|
57
57
|
}, z.core.$strip>;
|
|
58
58
|
declare const BuildResultSchema: z.ZodObject<{
|
|
59
59
|
status: z.ZodEnum<{
|
|
60
|
-
error: "error";
|
|
61
60
|
success: "success";
|
|
61
|
+
error: "error";
|
|
62
62
|
}>;
|
|
63
63
|
built: z.ZodArray<z.ZodString>;
|
|
64
64
|
skipped: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -388,15 +388,15 @@ declare const StatusResultSchema: z.ZodObject<{
|
|
|
388
388
|
}, z.core.$strip>;
|
|
389
389
|
declare const TypesGenOptionsSchema: z.ZodObject<{
|
|
390
390
|
env: z.ZodOptional<z.ZodEnum<{
|
|
391
|
-
production: "production";
|
|
392
391
|
development: "development";
|
|
392
|
+
production: "production";
|
|
393
393
|
}>>;
|
|
394
394
|
dryRun: z.ZodDefault<z.ZodBoolean>;
|
|
395
395
|
}, z.core.$strip>;
|
|
396
396
|
declare const TypesGenResultSchema: z.ZodObject<{
|
|
397
397
|
status: z.ZodEnum<{
|
|
398
|
-
error: "error";
|
|
399
398
|
success: "success";
|
|
399
|
+
error: "error";
|
|
400
400
|
}>;
|
|
401
401
|
generated: z.ZodArray<z.ZodString>;
|
|
402
402
|
fetched: z.ZodArray<z.ZodString>;
|
|
@@ -461,8 +461,8 @@ declare const bosContract: {
|
|
|
461
461
|
deploy: z.ZodDefault<z.ZodBoolean>;
|
|
462
462
|
}, z.core.$strip>, z.ZodObject<{
|
|
463
463
|
status: z.ZodEnum<{
|
|
464
|
-
error: "error";
|
|
465
464
|
success: "success";
|
|
465
|
+
error: "error";
|
|
466
466
|
}>;
|
|
467
467
|
built: z.ZodArray<z.ZodString>;
|
|
468
468
|
skipped: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -784,14 +784,14 @@ declare const bosContract: {
|
|
|
784
784
|
}, z.core.$strip>, MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
|
|
785
785
|
typesGen: ContractProcedure<z.ZodObject<{
|
|
786
786
|
env: z.ZodOptional<z.ZodEnum<{
|
|
787
|
-
production: "production";
|
|
788
787
|
development: "development";
|
|
788
|
+
production: "production";
|
|
789
789
|
}>>;
|
|
790
790
|
dryRun: z.ZodDefault<z.ZodBoolean>;
|
|
791
791
|
}, z.core.$strip>, z.ZodObject<{
|
|
792
792
|
status: z.ZodEnum<{
|
|
793
|
-
error: "error";
|
|
794
793
|
success: "success";
|
|
794
|
+
error: "error";
|
|
795
795
|
}>;
|
|
796
796
|
generated: z.ZodArray<z.ZodString>;
|
|
797
797
|
fetched: z.ZodArray<z.ZodString>;
|
package/dist/contract.d.mts
CHANGED
|
@@ -57,8 +57,8 @@ declare const BuildOptionsSchema: z.ZodObject<{
|
|
|
57
57
|
}, z.core.$strip>;
|
|
58
58
|
declare const BuildResultSchema: z.ZodObject<{
|
|
59
59
|
status: z.ZodEnum<{
|
|
60
|
-
error: "error";
|
|
61
60
|
success: "success";
|
|
61
|
+
error: "error";
|
|
62
62
|
}>;
|
|
63
63
|
built: z.ZodArray<z.ZodString>;
|
|
64
64
|
skipped: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -388,15 +388,15 @@ declare const StatusResultSchema: z.ZodObject<{
|
|
|
388
388
|
}, z.core.$strip>;
|
|
389
389
|
declare const TypesGenOptionsSchema: z.ZodObject<{
|
|
390
390
|
env: z.ZodOptional<z.ZodEnum<{
|
|
391
|
-
production: "production";
|
|
392
391
|
development: "development";
|
|
392
|
+
production: "production";
|
|
393
393
|
}>>;
|
|
394
394
|
dryRun: z.ZodDefault<z.ZodBoolean>;
|
|
395
395
|
}, z.core.$strip>;
|
|
396
396
|
declare const TypesGenResultSchema: z.ZodObject<{
|
|
397
397
|
status: z.ZodEnum<{
|
|
398
|
-
error: "error";
|
|
399
398
|
success: "success";
|
|
399
|
+
error: "error";
|
|
400
400
|
}>;
|
|
401
401
|
generated: z.ZodArray<z.ZodString>;
|
|
402
402
|
fetched: z.ZodArray<z.ZodString>;
|
|
@@ -461,8 +461,8 @@ declare const bosContract: {
|
|
|
461
461
|
deploy: z.ZodDefault<z.ZodBoolean>;
|
|
462
462
|
}, z.core.$strip>, z.ZodObject<{
|
|
463
463
|
status: z.ZodEnum<{
|
|
464
|
-
error: "error";
|
|
465
464
|
success: "success";
|
|
465
|
+
error: "error";
|
|
466
466
|
}>;
|
|
467
467
|
built: z.ZodArray<z.ZodString>;
|
|
468
468
|
skipped: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
@@ -784,14 +784,14 @@ declare const bosContract: {
|
|
|
784
784
|
}, z.core.$strip>, MergedErrorMap<Record<never, never>, Record<never, never>>, Record<never, never>>;
|
|
785
785
|
typesGen: ContractProcedure<z.ZodObject<{
|
|
786
786
|
env: z.ZodOptional<z.ZodEnum<{
|
|
787
|
-
production: "production";
|
|
788
787
|
development: "development";
|
|
788
|
+
production: "production";
|
|
789
789
|
}>>;
|
|
790
790
|
dryRun: z.ZodDefault<z.ZodBoolean>;
|
|
791
791
|
}, z.core.$strip>, z.ZodObject<{
|
|
792
792
|
status: z.ZodEnum<{
|
|
793
|
-
error: "error";
|
|
794
793
|
success: "success";
|
|
794
|
+
error: "error";
|
|
795
795
|
}>;
|
|
796
796
|
generated: z.ZodArray<z.ZodString>;
|
|
797
797
|
fetched: z.ZodArray<z.ZodString>;
|
package/dist/merge.cjs
CHANGED
package/dist/merge.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.cjs","names":[],"sources":["../src/merge.ts"],"sourcesContent":["import { createDefu } from \"defu\";\nimport type { BosConfigInput, ExtendsConfig } from \"./types\";\n\nexport const BOS_CONFIG_ORDER = [\n \"extends\",\n \"account\",\n \"domain\",\n \"testnet\",\n \"staging\",\n \"repository\",\n \"app\",\n \"plugins\",\n \"shared\",\n] as const;\n\nexport type BosConfigFieldName = (typeof BOS_CONFIG_ORDER)[number];\n\nexport type BosEnv = \"development\" | \"production\" | \"staging\";\n\nexport interface ResolvedConfigMeta {\n env: BosEnv;\n resolvedAt: string;\n extendsChain: string[];\n source?: string;\n}\n\nconst ARRAY_UNION_KEYS = new Set([\"secrets\"]);\n\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction unionArrays(a: unknown, b: unknown): unknown[] | undefined {\n const aArr = Array.isArray(a) ? a : [];\n const bArr = Array.isArray(b) ? b : [];\n if (aArr.length === 0 && bArr.length === 0) return undefined;\n const seen = new Set<string>();\n const result: unknown[] = [];\n for (const item of [...aArr, ...bArr]) {\n if (typeof item === \"string\") {\n if (seen.has(item)) continue;\n seen.add(item);\n }\n result.push(item);\n }\n return result;\n}\n\nfunction cleanNullSentinels(obj: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (value === null || value === undefined) continue;\n if (isPlainObject(value)) {\n const cleaned = cleanNullSentinels(value);\n if (Object.keys(cleaned).length > 0) {\n out[key] = cleaned;\n }\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst bosConfigMerger = createDefu((obj: any, key: any, value: any): boolean | undefined => {\n if (obj[key] === null) return true;\n if (value === null) {\n obj[key] = null;\n return true;\n }\n if (Array.isArray(obj[key]) && Array.isArray(value)) {\n if (ARRAY_UNION_KEYS.has(key)) {\n obj[key] = unionArrays(obj[key], value);\n } else {\n obj[key] = value;\n }\n return true;\n }\n return false;\n});\n\nexport function resolveExtendsRef(\n extendsField: string | ExtendsConfig | undefined,\n env: BosEnv,\n): string | undefined {\n if (!extendsField) return undefined;\n if (typeof extendsField === \"string\") return extendsField;\n return extendsField[env] ?? extendsField.production ?? Object.values(extendsField).find(Boolean);\n}\n\nexport function mergeBosConfigWithExtends(\n parent: BosConfigInput,\n child: BosConfigInput,\n): BosConfigInput {\n const merged = bosConfigMerger(child, parent) as BosConfigInput;\n\n if (isPlainObject(parent.plugins) && isPlainObject(child.plugins)) {\n const plugins: Record<string, unknown> = { ...parent.plugins };\n for (const [key, rawValue] of Object.entries(child.plugins)) {\n const value = rawValue as unknown;\n if (value === null || value === false) {\n delete plugins[key];\n } else if (isPlainObject(plugins[key]) && isPlainObject(value)) {\n plugins[key] = bosConfigMerger(\n value as Record<string, unknown>,\n plugins[key] as Record<string, unknown>,\n );\n } else {\n plugins[key] = value;\n }\n }\n (merged as Record<string, unknown>).plugins = plugins;\n } else if (child.plugins !== undefined) {\n (merged as Record<string, unknown>).plugins = cleanNullSentinels(\n child.plugins as Record<string, unknown>,\n );\n }\n\n const mergedRecord = merged as Record<string, unknown>;\n\n if (isPlainObject(mergedRecord.app)) {\n for (const entryVal of Object.values(mergedRecord.app as Record<string, unknown>)) {\n if (!isPlainObject(entryVal)) continue;\n for (const secretKey of ARRAY_UNION_KEYS) {\n if (Array.isArray(entryVal[secretKey])) {\n entryVal[secretKey] =\n (unionArrays(entryVal[secretKey] as unknown[], []) as string[] | undefined)?.filter(\n Boolean,\n ) ?? entryVal[secretKey];\n }\n }\n }\n }\n\n if (isPlainObject(mergedRecord.plugins)) {\n for (const pluginVal of Object.values(mergedRecord.plugins as Record<string, unknown>)) {\n if (!isPlainObject(pluginVal)) continue;\n for (const secretKey of ARRAY_UNION_KEYS) {\n if (Array.isArray(pluginVal[secretKey])) {\n pluginVal[secretKey] =\n (unionArrays(pluginVal[secretKey] as unknown[], []) as string[] | undefined)?.filter(\n Boolean,\n ) ?? pluginVal[secretKey];\n }\n }\n }\n }\n\n return rebuildOrderedConfig(mergedRecord) as BosConfigInput;\n}\n\nexport function mergeBosConfigWithTemplate(\n local: BosConfigInput,\n template: BosConfigInput,\n): BosConfigInput {\n const merged = mergeJsonValuesPreservingLocalOrder(local, template) as BosConfigInput;\n return rebuildOrderedConfig(merged as Record<string, unknown>) as BosConfigInput;\n}\n\nfunction mergeJsonValuesPreservingLocalOrder(local: unknown, template: unknown): unknown {\n if (isPlainObject(local) && isPlainObject(template)) {\n const merged: Record<string, unknown> = {};\n for (const key of Object.keys(local)) {\n merged[key] = mergeJsonValuesPreservingLocalOrder(\n local[key],\n (template as Record<string, unknown>)[key],\n );\n }\n for (const key of Object.keys(template as Record<string, unknown>)) {\n if (!(key in merged)) {\n merged[key] = (template as Record<string, unknown>)[key];\n }\n }\n return merged;\n }\n return local ?? template;\n}\n\nexport function rebuildOrderedConfig<T extends Record<string, unknown>>(config: T): T {\n const ordered: Record<string, unknown> = {};\n\n for (const key of BOS_CONFIG_ORDER) {\n if (key in config) {\n ordered[key] = config[key];\n }\n }\n\n for (const key of Object.keys(config)) {\n if (!BOS_CONFIG_ORDER.includes(key as BosConfigFieldName)) {\n ordered[key] = config[key];\n }\n }\n\n return ordered as T;\n}\n\nexport { bosConfigMerger };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"merge.cjs","names":[],"sources":["../src/merge.ts"],"sourcesContent":["import { createDefu } from \"defu\";\nimport type { BosConfigInput, ExtendsConfig } from \"./types\";\n\nexport const BOS_CONFIG_ORDER = [\n \"extends\",\n \"account\",\n \"domain\",\n \"title\",\n \"description\",\n \"testnet\",\n \"staging\",\n \"repository\",\n \"app\",\n \"plugins\",\n \"shared\",\n] as const;\n\nexport type BosConfigFieldName = (typeof BOS_CONFIG_ORDER)[number];\n\nexport type BosEnv = \"development\" | \"production\" | \"staging\";\n\nexport interface ResolvedConfigMeta {\n env: BosEnv;\n resolvedAt: string;\n extendsChain: string[];\n source?: string;\n}\n\nconst ARRAY_UNION_KEYS = new Set([\"secrets\"]);\n\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction unionArrays(a: unknown, b: unknown): unknown[] | undefined {\n const aArr = Array.isArray(a) ? a : [];\n const bArr = Array.isArray(b) ? b : [];\n if (aArr.length === 0 && bArr.length === 0) return undefined;\n const seen = new Set<string>();\n const result: unknown[] = [];\n for (const item of [...aArr, ...bArr]) {\n if (typeof item === \"string\") {\n if (seen.has(item)) continue;\n seen.add(item);\n }\n result.push(item);\n }\n return result;\n}\n\nfunction cleanNullSentinels(obj: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (value === null || value === undefined) continue;\n if (isPlainObject(value)) {\n const cleaned = cleanNullSentinels(value);\n if (Object.keys(cleaned).length > 0) {\n out[key] = cleaned;\n }\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst bosConfigMerger = createDefu((obj: any, key: any, value: any): boolean | undefined => {\n if (obj[key] === null) return true;\n if (value === null) {\n obj[key] = null;\n return true;\n }\n if (Array.isArray(obj[key]) && Array.isArray(value)) {\n if (ARRAY_UNION_KEYS.has(key)) {\n obj[key] = unionArrays(obj[key], value);\n } else {\n obj[key] = value;\n }\n return true;\n }\n return false;\n});\n\nexport function resolveExtendsRef(\n extendsField: string | ExtendsConfig | undefined,\n env: BosEnv,\n): string | undefined {\n if (!extendsField) return undefined;\n if (typeof extendsField === \"string\") return extendsField;\n return extendsField[env] ?? extendsField.production ?? Object.values(extendsField).find(Boolean);\n}\n\nexport function mergeBosConfigWithExtends(\n parent: BosConfigInput,\n child: BosConfigInput,\n): BosConfigInput {\n const merged = bosConfigMerger(child, parent) as BosConfigInput;\n\n if (isPlainObject(parent.plugins) && isPlainObject(child.plugins)) {\n const plugins: Record<string, unknown> = { ...parent.plugins };\n for (const [key, rawValue] of Object.entries(child.plugins)) {\n const value = rawValue as unknown;\n if (value === null || value === false) {\n delete plugins[key];\n } else if (isPlainObject(plugins[key]) && isPlainObject(value)) {\n plugins[key] = bosConfigMerger(\n value as Record<string, unknown>,\n plugins[key] as Record<string, unknown>,\n );\n } else {\n plugins[key] = value;\n }\n }\n (merged as Record<string, unknown>).plugins = plugins;\n } else if (child.plugins !== undefined) {\n (merged as Record<string, unknown>).plugins = cleanNullSentinels(\n child.plugins as Record<string, unknown>,\n );\n }\n\n const mergedRecord = merged as Record<string, unknown>;\n\n if (isPlainObject(mergedRecord.app)) {\n for (const entryVal of Object.values(mergedRecord.app as Record<string, unknown>)) {\n if (!isPlainObject(entryVal)) continue;\n for (const secretKey of ARRAY_UNION_KEYS) {\n if (Array.isArray(entryVal[secretKey])) {\n entryVal[secretKey] =\n (unionArrays(entryVal[secretKey] as unknown[], []) as string[] | undefined)?.filter(\n Boolean,\n ) ?? entryVal[secretKey];\n }\n }\n }\n }\n\n if (isPlainObject(mergedRecord.plugins)) {\n for (const pluginVal of Object.values(mergedRecord.plugins as Record<string, unknown>)) {\n if (!isPlainObject(pluginVal)) continue;\n for (const secretKey of ARRAY_UNION_KEYS) {\n if (Array.isArray(pluginVal[secretKey])) {\n pluginVal[secretKey] =\n (unionArrays(pluginVal[secretKey] as unknown[], []) as string[] | undefined)?.filter(\n Boolean,\n ) ?? pluginVal[secretKey];\n }\n }\n }\n }\n\n return rebuildOrderedConfig(mergedRecord) as BosConfigInput;\n}\n\nexport function mergeBosConfigWithTemplate(\n local: BosConfigInput,\n template: BosConfigInput,\n): BosConfigInput {\n const merged = mergeJsonValuesPreservingLocalOrder(local, template) as BosConfigInput;\n return rebuildOrderedConfig(merged as Record<string, unknown>) as BosConfigInput;\n}\n\nfunction mergeJsonValuesPreservingLocalOrder(local: unknown, template: unknown): unknown {\n if (isPlainObject(local) && isPlainObject(template)) {\n const merged: Record<string, unknown> = {};\n for (const key of Object.keys(local)) {\n merged[key] = mergeJsonValuesPreservingLocalOrder(\n local[key],\n (template as Record<string, unknown>)[key],\n );\n }\n for (const key of Object.keys(template as Record<string, unknown>)) {\n if (!(key in merged)) {\n merged[key] = (template as Record<string, unknown>)[key];\n }\n }\n return merged;\n }\n return local ?? template;\n}\n\nexport function rebuildOrderedConfig<T extends Record<string, unknown>>(config: T): T {\n const ordered: Record<string, unknown> = {};\n\n for (const key of BOS_CONFIG_ORDER) {\n if (key in config) {\n ordered[key] = config[key];\n }\n }\n\n for (const key of Object.keys(config)) {\n if (!BOS_CONFIG_ORDER.includes(key as BosConfigFieldName)) {\n ordered[key] = config[key];\n }\n }\n\n return ordered as T;\n}\n\nexport { bosConfigMerger };\n"],"mappings":"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"}
|
package/dist/merge.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/merge.d.ts
|
|
2
|
-
declare const BOS_CONFIG_ORDER: readonly ["extends", "account", "domain", "testnet", "staging", "repository", "app", "plugins", "shared"];
|
|
2
|
+
declare const BOS_CONFIG_ORDER: readonly ["extends", "account", "domain", "title", "description", "testnet", "staging", "repository", "app", "plugins", "shared"];
|
|
3
3
|
type BosEnv = "development" | "production" | "staging";
|
|
4
4
|
declare function rebuildOrderedConfig<T extends Record<string, unknown>>(config: T): T;
|
|
5
5
|
//#endregion
|
package/dist/merge.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.d.cts","names":[],"sources":["../src/merge.ts"],"mappings":";cAGa,gBAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"merge.d.cts","names":[],"sources":["../src/merge.ts"],"mappings":";cAGa,gBAAA;AAAA,KAgBD,MAAA;AAAA,iBAkKI,oBAAA,WAA+B,MAAA,kBAAA,CAAyB,MAAA,EAAQ,CAAA,GAAI,CAAA"}
|
package/dist/merge.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/merge.d.ts
|
|
2
|
-
declare const BOS_CONFIG_ORDER: readonly ["extends", "account", "domain", "testnet", "staging", "repository", "app", "plugins", "shared"];
|
|
2
|
+
declare const BOS_CONFIG_ORDER: readonly ["extends", "account", "domain", "title", "description", "testnet", "staging", "repository", "app", "plugins", "shared"];
|
|
3
3
|
type BosEnv = "development" | "production" | "staging";
|
|
4
4
|
declare function rebuildOrderedConfig<T extends Record<string, unknown>>(config: T): T;
|
|
5
5
|
//#endregion
|
package/dist/merge.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.d.mts","names":[],"sources":["../src/merge.ts"],"mappings":";cAGa,gBAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"merge.d.mts","names":[],"sources":["../src/merge.ts"],"mappings":";cAGa,gBAAA;AAAA,KAgBD,MAAA;AAAA,iBAkKI,oBAAA,WAA+B,MAAA,kBAAA,CAAyB,MAAA,EAAQ,CAAA,GAAI,CAAA"}
|
package/dist/merge.mjs
CHANGED
package/dist/merge.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merge.mjs","names":[],"sources":["../src/merge.ts"],"sourcesContent":["import { createDefu } from \"defu\";\nimport type { BosConfigInput, ExtendsConfig } from \"./types\";\n\nexport const BOS_CONFIG_ORDER = [\n \"extends\",\n \"account\",\n \"domain\",\n \"testnet\",\n \"staging\",\n \"repository\",\n \"app\",\n \"plugins\",\n \"shared\",\n] as const;\n\nexport type BosConfigFieldName = (typeof BOS_CONFIG_ORDER)[number];\n\nexport type BosEnv = \"development\" | \"production\" | \"staging\";\n\nexport interface ResolvedConfigMeta {\n env: BosEnv;\n resolvedAt: string;\n extendsChain: string[];\n source?: string;\n}\n\nconst ARRAY_UNION_KEYS = new Set([\"secrets\"]);\n\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction unionArrays(a: unknown, b: unknown): unknown[] | undefined {\n const aArr = Array.isArray(a) ? a : [];\n const bArr = Array.isArray(b) ? b : [];\n if (aArr.length === 0 && bArr.length === 0) return undefined;\n const seen = new Set<string>();\n const result: unknown[] = [];\n for (const item of [...aArr, ...bArr]) {\n if (typeof item === \"string\") {\n if (seen.has(item)) continue;\n seen.add(item);\n }\n result.push(item);\n }\n return result;\n}\n\nfunction cleanNullSentinels(obj: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (value === null || value === undefined) continue;\n if (isPlainObject(value)) {\n const cleaned = cleanNullSentinels(value);\n if (Object.keys(cleaned).length > 0) {\n out[key] = cleaned;\n }\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst bosConfigMerger = createDefu((obj: any, key: any, value: any): boolean | undefined => {\n if (obj[key] === null) return true;\n if (value === null) {\n obj[key] = null;\n return true;\n }\n if (Array.isArray(obj[key]) && Array.isArray(value)) {\n if (ARRAY_UNION_KEYS.has(key)) {\n obj[key] = unionArrays(obj[key], value);\n } else {\n obj[key] = value;\n }\n return true;\n }\n return false;\n});\n\nexport function resolveExtendsRef(\n extendsField: string | ExtendsConfig | undefined,\n env: BosEnv,\n): string | undefined {\n if (!extendsField) return undefined;\n if (typeof extendsField === \"string\") return extendsField;\n return extendsField[env] ?? extendsField.production ?? Object.values(extendsField).find(Boolean);\n}\n\nexport function mergeBosConfigWithExtends(\n parent: BosConfigInput,\n child: BosConfigInput,\n): BosConfigInput {\n const merged = bosConfigMerger(child, parent) as BosConfigInput;\n\n if (isPlainObject(parent.plugins) && isPlainObject(child.plugins)) {\n const plugins: Record<string, unknown> = { ...parent.plugins };\n for (const [key, rawValue] of Object.entries(child.plugins)) {\n const value = rawValue as unknown;\n if (value === null || value === false) {\n delete plugins[key];\n } else if (isPlainObject(plugins[key]) && isPlainObject(value)) {\n plugins[key] = bosConfigMerger(\n value as Record<string, unknown>,\n plugins[key] as Record<string, unknown>,\n );\n } else {\n plugins[key] = value;\n }\n }\n (merged as Record<string, unknown>).plugins = plugins;\n } else if (child.plugins !== undefined) {\n (merged as Record<string, unknown>).plugins = cleanNullSentinels(\n child.plugins as Record<string, unknown>,\n );\n }\n\n const mergedRecord = merged as Record<string, unknown>;\n\n if (isPlainObject(mergedRecord.app)) {\n for (const entryVal of Object.values(mergedRecord.app as Record<string, unknown>)) {\n if (!isPlainObject(entryVal)) continue;\n for (const secretKey of ARRAY_UNION_KEYS) {\n if (Array.isArray(entryVal[secretKey])) {\n entryVal[secretKey] =\n (unionArrays(entryVal[secretKey] as unknown[], []) as string[] | undefined)?.filter(\n Boolean,\n ) ?? entryVal[secretKey];\n }\n }\n }\n }\n\n if (isPlainObject(mergedRecord.plugins)) {\n for (const pluginVal of Object.values(mergedRecord.plugins as Record<string, unknown>)) {\n if (!isPlainObject(pluginVal)) continue;\n for (const secretKey of ARRAY_UNION_KEYS) {\n if (Array.isArray(pluginVal[secretKey])) {\n pluginVal[secretKey] =\n (unionArrays(pluginVal[secretKey] as unknown[], []) as string[] | undefined)?.filter(\n Boolean,\n ) ?? pluginVal[secretKey];\n }\n }\n }\n }\n\n return rebuildOrderedConfig(mergedRecord) as BosConfigInput;\n}\n\nexport function mergeBosConfigWithTemplate(\n local: BosConfigInput,\n template: BosConfigInput,\n): BosConfigInput {\n const merged = mergeJsonValuesPreservingLocalOrder(local, template) as BosConfigInput;\n return rebuildOrderedConfig(merged as Record<string, unknown>) as BosConfigInput;\n}\n\nfunction mergeJsonValuesPreservingLocalOrder(local: unknown, template: unknown): unknown {\n if (isPlainObject(local) && isPlainObject(template)) {\n const merged: Record<string, unknown> = {};\n for (const key of Object.keys(local)) {\n merged[key] = mergeJsonValuesPreservingLocalOrder(\n local[key],\n (template as Record<string, unknown>)[key],\n );\n }\n for (const key of Object.keys(template as Record<string, unknown>)) {\n if (!(key in merged)) {\n merged[key] = (template as Record<string, unknown>)[key];\n }\n }\n return merged;\n }\n return local ?? template;\n}\n\nexport function rebuildOrderedConfig<T extends Record<string, unknown>>(config: T): T {\n const ordered: Record<string, unknown> = {};\n\n for (const key of BOS_CONFIG_ORDER) {\n if (key in config) {\n ordered[key] = config[key];\n }\n }\n\n for (const key of Object.keys(config)) {\n if (!BOS_CONFIG_ORDER.includes(key as BosConfigFieldName)) {\n ordered[key] = config[key];\n }\n }\n\n return ordered as T;\n}\n\nexport { bosConfigMerger };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"merge.mjs","names":[],"sources":["../src/merge.ts"],"sourcesContent":["import { createDefu } from \"defu\";\nimport type { BosConfigInput, ExtendsConfig } from \"./types\";\n\nexport const BOS_CONFIG_ORDER = [\n \"extends\",\n \"account\",\n \"domain\",\n \"title\",\n \"description\",\n \"testnet\",\n \"staging\",\n \"repository\",\n \"app\",\n \"plugins\",\n \"shared\",\n] as const;\n\nexport type BosConfigFieldName = (typeof BOS_CONFIG_ORDER)[number];\n\nexport type BosEnv = \"development\" | \"production\" | \"staging\";\n\nexport interface ResolvedConfigMeta {\n env: BosEnv;\n resolvedAt: string;\n extendsChain: string[];\n source?: string;\n}\n\nconst ARRAY_UNION_KEYS = new Set([\"secrets\"]);\n\nexport function isPlainObject(value: unknown): value is Record<string, unknown> {\n return Boolean(value) && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction unionArrays(a: unknown, b: unknown): unknown[] | undefined {\n const aArr = Array.isArray(a) ? a : [];\n const bArr = Array.isArray(b) ? b : [];\n if (aArr.length === 0 && bArr.length === 0) return undefined;\n const seen = new Set<string>();\n const result: unknown[] = [];\n for (const item of [...aArr, ...bArr]) {\n if (typeof item === \"string\") {\n if (seen.has(item)) continue;\n seen.add(item);\n }\n result.push(item);\n }\n return result;\n}\n\nfunction cleanNullSentinels(obj: Record<string, unknown>): Record<string, unknown> {\n const out: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(obj)) {\n if (value === null || value === undefined) continue;\n if (isPlainObject(value)) {\n const cleaned = cleanNullSentinels(value);\n if (Object.keys(cleaned).length > 0) {\n out[key] = cleaned;\n }\n } else {\n out[key] = value;\n }\n }\n return out;\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nconst bosConfigMerger = createDefu((obj: any, key: any, value: any): boolean | undefined => {\n if (obj[key] === null) return true;\n if (value === null) {\n obj[key] = null;\n return true;\n }\n if (Array.isArray(obj[key]) && Array.isArray(value)) {\n if (ARRAY_UNION_KEYS.has(key)) {\n obj[key] = unionArrays(obj[key], value);\n } else {\n obj[key] = value;\n }\n return true;\n }\n return false;\n});\n\nexport function resolveExtendsRef(\n extendsField: string | ExtendsConfig | undefined,\n env: BosEnv,\n): string | undefined {\n if (!extendsField) return undefined;\n if (typeof extendsField === \"string\") return extendsField;\n return extendsField[env] ?? extendsField.production ?? Object.values(extendsField).find(Boolean);\n}\n\nexport function mergeBosConfigWithExtends(\n parent: BosConfigInput,\n child: BosConfigInput,\n): BosConfigInput {\n const merged = bosConfigMerger(child, parent) as BosConfigInput;\n\n if (isPlainObject(parent.plugins) && isPlainObject(child.plugins)) {\n const plugins: Record<string, unknown> = { ...parent.plugins };\n for (const [key, rawValue] of Object.entries(child.plugins)) {\n const value = rawValue as unknown;\n if (value === null || value === false) {\n delete plugins[key];\n } else if (isPlainObject(plugins[key]) && isPlainObject(value)) {\n plugins[key] = bosConfigMerger(\n value as Record<string, unknown>,\n plugins[key] as Record<string, unknown>,\n );\n } else {\n plugins[key] = value;\n }\n }\n (merged as Record<string, unknown>).plugins = plugins;\n } else if (child.plugins !== undefined) {\n (merged as Record<string, unknown>).plugins = cleanNullSentinels(\n child.plugins as Record<string, unknown>,\n );\n }\n\n const mergedRecord = merged as Record<string, unknown>;\n\n if (isPlainObject(mergedRecord.app)) {\n for (const entryVal of Object.values(mergedRecord.app as Record<string, unknown>)) {\n if (!isPlainObject(entryVal)) continue;\n for (const secretKey of ARRAY_UNION_KEYS) {\n if (Array.isArray(entryVal[secretKey])) {\n entryVal[secretKey] =\n (unionArrays(entryVal[secretKey] as unknown[], []) as string[] | undefined)?.filter(\n Boolean,\n ) ?? entryVal[secretKey];\n }\n }\n }\n }\n\n if (isPlainObject(mergedRecord.plugins)) {\n for (const pluginVal of Object.values(mergedRecord.plugins as Record<string, unknown>)) {\n if (!isPlainObject(pluginVal)) continue;\n for (const secretKey of ARRAY_UNION_KEYS) {\n if (Array.isArray(pluginVal[secretKey])) {\n pluginVal[secretKey] =\n (unionArrays(pluginVal[secretKey] as unknown[], []) as string[] | undefined)?.filter(\n Boolean,\n ) ?? pluginVal[secretKey];\n }\n }\n }\n }\n\n return rebuildOrderedConfig(mergedRecord) as BosConfigInput;\n}\n\nexport function mergeBosConfigWithTemplate(\n local: BosConfigInput,\n template: BosConfigInput,\n): BosConfigInput {\n const merged = mergeJsonValuesPreservingLocalOrder(local, template) as BosConfigInput;\n return rebuildOrderedConfig(merged as Record<string, unknown>) as BosConfigInput;\n}\n\nfunction mergeJsonValuesPreservingLocalOrder(local: unknown, template: unknown): unknown {\n if (isPlainObject(local) && isPlainObject(template)) {\n const merged: Record<string, unknown> = {};\n for (const key of Object.keys(local)) {\n merged[key] = mergeJsonValuesPreservingLocalOrder(\n local[key],\n (template as Record<string, unknown>)[key],\n );\n }\n for (const key of Object.keys(template as Record<string, unknown>)) {\n if (!(key in merged)) {\n merged[key] = (template as Record<string, unknown>)[key];\n }\n }\n return merged;\n }\n return local ?? template;\n}\n\nexport function rebuildOrderedConfig<T extends Record<string, unknown>>(config: T): T {\n const ordered: Record<string, unknown> = {};\n\n for (const key of BOS_CONFIG_ORDER) {\n if (key in config) {\n ordered[key] = config[key];\n }\n }\n\n for (const key of Object.keys(config)) {\n if (!BOS_CONFIG_ORDER.includes(key as BosConfigFieldName)) {\n ordered[key] = config[key];\n }\n }\n\n return ordered as T;\n}\n\nexport { bosConfigMerger };\n"],"mappings":"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"}
|
package/dist/near-cli.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
2
2
|
let node_crypto = require("node:crypto");
|
|
3
3
|
let effect = require("effect");
|
|
4
|
-
let
|
|
4
|
+
let execa = require("execa");
|
|
5
5
|
|
|
6
6
|
//#region src/near-cli.ts
|
|
7
7
|
const INSTALLER_URL = `https://github.com/near/near-cli-rs/releases/download/v0.23.5/near-cli-rs-installer.sh`;
|
|
@@ -63,36 +63,26 @@ function generateNearKeyPair() {
|
|
|
63
63
|
}
|
|
64
64
|
const checkNearCliInstalled = effect.Effect.tryPromise({
|
|
65
65
|
try: async () => {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
66
|
+
try {
|
|
67
|
+
await (0, execa.execa)("near", ["--version"], { stdio: "pipe" });
|
|
68
|
+
return true;
|
|
69
|
+
} catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
71
72
|
},
|
|
72
73
|
catch: () => /* @__PURE__ */ new Error("Failed to check NEAR CLI")
|
|
73
74
|
});
|
|
74
75
|
const installNearCli = effect.Effect.tryPromise({
|
|
75
76
|
try: async () => {
|
|
76
|
-
|
|
77
|
-
const proc = (0, node_child_process.spawn)("sh", ["-c", `curl --proto '=https' --tlsv1.2 -LsSf ${INSTALLER_URL} | sh`], { stdio: "inherit" });
|
|
78
|
-
proc.on("close", (code) => {
|
|
79
|
-
if (code === 0) resolve();
|
|
80
|
-
else reject(new NearCliInstallError(`Installer exited with code ${code}`));
|
|
81
|
-
});
|
|
82
|
-
proc.on("error", (err) => reject(new NearCliInstallError(err.message)));
|
|
83
|
-
});
|
|
77
|
+
await (0, execa.execa)("sh", ["-c", `curl --proto '=https' --tlsv1.2 -LsSf ${INSTALLER_URL} | sh`], { stdio: "inherit" });
|
|
84
78
|
},
|
|
85
|
-
catch: (error) =>
|
|
79
|
+
catch: (error) => {
|
|
80
|
+
if (error instanceof Error && "exitCode" in error) return new NearCliInstallError(`Installer exited with code ${error.exitCode}`);
|
|
81
|
+
return new NearCliInstallError(error instanceof Error ? error.message : String(error));
|
|
82
|
+
}
|
|
86
83
|
});
|
|
87
84
|
async function runNearCommand(args) {
|
|
88
|
-
await
|
|
89
|
-
const proc = (0, node_child_process.spawn)("near", args, { stdio: "inherit" });
|
|
90
|
-
proc.on("close", (code) => {
|
|
91
|
-
if (code === 0) resolve();
|
|
92
|
-
else reject(/* @__PURE__ */ new Error(`near ${args.join(" ")} failed with exit code ${code}`));
|
|
93
|
-
});
|
|
94
|
-
proc.on("error", (err) => reject(new Error(err.message)));
|
|
95
|
-
});
|
|
85
|
+
await (0, execa.execa)("near", args, { stdio: "inherit" });
|
|
96
86
|
}
|
|
97
87
|
const ensureNearCli = effect.Effect.gen(function* () {
|
|
98
88
|
if (yield* checkNearCliInstalled) return;
|
|
@@ -134,36 +124,23 @@ const executeTransaction = (config) => effect.Effect.gen(function* () {
|
|
|
134
124
|
success: true,
|
|
135
125
|
txHash: (yield* effect.Effect.tryPromise({
|
|
136
126
|
try: async () => {
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
] });
|
|
143
|
-
let stdout = "";
|
|
144
|
-
let stderr = "";
|
|
145
|
-
proc.stdout?.on("data", (data) => {
|
|
146
|
-
const text = data.toString();
|
|
147
|
-
stdout += text;
|
|
148
|
-
process.stdout.write(text);
|
|
149
|
-
});
|
|
150
|
-
proc.stderr?.on("data", (data) => {
|
|
151
|
-
const text = data.toString();
|
|
152
|
-
stderr += text;
|
|
153
|
-
});
|
|
154
|
-
proc.on("close", (code) => {
|
|
155
|
-
const combined = `${stdout}\n${stderr}`;
|
|
156
|
-
const txHashMatch = combined.match(/Transaction ID:\s*([A-Za-z0-9]+)/i);
|
|
157
|
-
const hasCodeDoesNotExist = /CodeDoesNotExist/i.test(combined);
|
|
158
|
-
const hasTransactionFailed = /Transaction failed/i.test(combined);
|
|
159
|
-
const softSuccess = Boolean(txHashMatch?.[1]) && hasCodeDoesNotExist && hasTransactionFailed;
|
|
160
|
-
if (code === 0 || softSuccess) {
|
|
161
|
-
if (softSuccess) console.log(` ${txHashMatch?.[1]} — FastDATA CodeDoesNotExist (expected)`);
|
|
162
|
-
resolve(combined);
|
|
163
|
-
} else reject(new NearTransactionError(stderr || `Transaction failed with code ${code}`));
|
|
164
|
-
});
|
|
165
|
-
proc.on("error", (err) => reject(new NearTransactionError(err.message)));
|
|
127
|
+
const result = await (0, execa.execa)("near", args, {
|
|
128
|
+
stdin: "inherit",
|
|
129
|
+
stdout: "pipe",
|
|
130
|
+
stderr: "pipe",
|
|
131
|
+
reject: false
|
|
166
132
|
});
|
|
133
|
+
process.stdout.write(result.stdout);
|
|
134
|
+
const combined = `${result.stdout}\n${result.stderr}`;
|
|
135
|
+
const txHashMatch = combined.match(/Transaction ID:\s*([A-Za-z0-9]+)/i);
|
|
136
|
+
const hasCodeDoesNotExist = /CodeDoesNotExist/i.test(combined);
|
|
137
|
+
const hasTransactionFailed = /Transaction failed/i.test(combined);
|
|
138
|
+
const softSuccess = Boolean(txHashMatch?.[1]) && hasCodeDoesNotExist && hasTransactionFailed;
|
|
139
|
+
if (result.exitCode === 0 || softSuccess) {
|
|
140
|
+
if (softSuccess) console.log(` ${txHashMatch?.[1]} — FastDATA CodeDoesNotExist (expected)`);
|
|
141
|
+
return combined;
|
|
142
|
+
}
|
|
143
|
+
throw new NearTransactionError(result.stderr || `Transaction failed with code ${result.exitCode}`);
|
|
167
144
|
},
|
|
168
145
|
catch: (error) => error
|
|
169
146
|
})).match(/Transaction ID:\s*([A-Za-z0-9]+)/i)?.[1]
|
package/dist/near-cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"near-cli.cjs","names":["Effect"],"sources":["../src/near-cli.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport { generateKeyPairSync } from \"node:crypto\";\nimport { Effect } from \"effect\";\n\nexport interface NearTransactionConfig {\n account: string;\n contract: string;\n method: string;\n argsBase64: string;\n network?: \"mainnet\" | \"testnet\";\n privateKey?: string;\n gas?: string;\n deposit?: string;\n}\n\nexport interface NearTransactionResult {\n success: boolean;\n txHash?: string;\n error?: string;\n}\n\nexport interface NearKeyPair {\n publicKey: string;\n privateKey: string;\n}\n\nexport interface FunctionCallAccessKeyConfig {\n account: string;\n contract: string;\n allowance: string;\n functionNames: string[];\n network?: \"mainnet\" | \"testnet\";\n}\n\nconst NEAR_CLI_VERSION = \"0.23.5\";\nconst INSTALLER_URL = `https://github.com/near/near-cli-rs/releases/download/v${NEAR_CLI_VERSION}/near-cli-rs-installer.sh`;\nconst BASE58_ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n\nexport class NearCliNotFoundError extends Error {\n readonly _tag = \"NearCliNotFoundError\";\n constructor() {\n super(\"NEAR CLI not found\");\n }\n}\n\nexport class NearCliInstallError extends Error {\n readonly _tag = \"NearCliInstallError\";\n constructor(message: string) {\n super(`Failed to install NEAR CLI: ${message}`);\n }\n}\n\nexport class NearTransactionError extends Error {\n readonly _tag = \"NearTransactionError\";\n}\n\nfunction base64UrlToBytes(input: string): Uint8Array {\n const normalized = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n return new Uint8Array(Buffer.from(normalized, \"base64\"));\n}\n\nfunction base58Encode(input: Uint8Array): string {\n if (input.length === 0) return \"\";\n\n const digits: number[] = [0];\n for (const byte of input) {\n let carry = byte;\n for (let i = 0; i < digits.length; i++) {\n carry += digits[i]! << 8;\n digits[i] = carry % 58;\n carry = Math.floor(carry / 58);\n }\n while (carry > 0) {\n digits.push(carry % 58);\n carry = Math.floor(carry / 58);\n }\n }\n\n let output = \"\";\n for (const byte of input) {\n if (byte === 0) output += BASE58_ALPHABET[0];\n else break;\n }\n\n for (let i = digits.length - 1; i >= 0; i--) {\n output += BASE58_ALPHABET[digits[i]!]!;\n }\n\n return output;\n}\n\nexport function generateNearKeyPair(): NearKeyPair {\n const { publicKey, privateKey } = generateKeyPairSync(\"ed25519\");\n const publicJwk = publicKey.export({ format: \"jwk\" }) as JsonWebKey;\n const privateJwk = privateKey.export({ format: \"jwk\" }) as JsonWebKey;\n\n if (!publicJwk.x || !privateJwk.d) {\n throw new Error(\"Failed to generate NEAR keypair\");\n }\n\n const publicBytes = base64UrlToBytes(publicJwk.x);\n const privateSeed = base64UrlToBytes(privateJwk.d);\n const secretBytes = new Uint8Array(privateSeed.length + publicBytes.length);\n secretBytes.set(privateSeed, 0);\n secretBytes.set(publicBytes, privateSeed.length);\n\n return {\n publicKey: `ed25519:${base58Encode(publicBytes)}`,\n privateKey: `ed25519:${base58Encode(secretBytes)}`,\n };\n}\n\nconst checkNearCliInstalled = Effect.tryPromise({\n try: async () => {\n return await new Promise<boolean>((resolve) => {\n const proc = spawn(\"near\", [\"--version\"], { stdio: \"pipe\" });\n proc.on(\"close\", (code) => resolve(code === 0));\n proc.on(\"error\", () => resolve(false));\n });\n },\n catch: () => new Error(\"Failed to check NEAR CLI\"),\n});\n\nconst installNearCli = Effect.tryPromise({\n try: async () => {\n return await new Promise<void>((resolve, reject) => {\n const proc = spawn(\n \"sh\",\n [\"-c\", `curl --proto '=https' --tlsv1.2 -LsSf ${INSTALLER_URL} | sh`],\n {\n stdio: \"inherit\",\n },\n );\n\n proc.on(\"close\", (code) => {\n if (code === 0) resolve();\n else reject(new NearCliInstallError(`Installer exited with code ${code}`));\n });\n proc.on(\"error\", (err) => reject(new NearCliInstallError(err.message)));\n });\n },\n catch: (error) => error as Error,\n});\n\nasync function runNearCommand(args: string[]): Promise<void> {\n await new Promise<void>((resolve, reject) => {\n const proc = spawn(\"near\", args, {\n stdio: \"inherit\",\n });\n\n proc.on(\"close\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`near ${args.join(\" \")} failed with exit code ${code}`));\n });\n\n proc.on(\"error\", (err) => reject(new Error(err.message)));\n });\n}\n\nexport const ensureNearCli = Effect.gen(function* () {\n const isInstalled = yield* checkNearCliInstalled;\n if (isInstalled) return;\n\n if (process.env.BOS_INSTALL_NEAR_CLI === \"true\") {\n yield* installNearCli;\n return;\n }\n\n console.log();\n console.log(\" NEAR CLI not found\");\n\n console.log();\n console.log(` To install manually: curl --proto '=https' --tlsv1.2 -LsSf ${INSTALLER_URL} | sh`);\n console.log();\n yield* Effect.fail(new NearCliNotFoundError());\n});\n\nexport const executeTransaction = (\n config: NearTransactionConfig,\n): Effect.Effect<NearTransactionResult, Error> =>\n Effect.gen(function* () {\n const gas = (config.gas || \"300Tgas\").replace(/\\s+/g, \"\");\n const deposit = (config.deposit || \"0NEAR\").replace(/\\s+/g, \"\");\n const network = config.network || (config.account.endsWith(\".testnet\") ? \"testnet\" : \"mainnet\");\n\n const args = [\n \"contract\",\n \"call-function\",\n \"as-transaction\",\n config.contract,\n config.method,\n \"base64-args\",\n config.argsBase64,\n \"prepaid-gas\",\n gas,\n \"attached-deposit\",\n deposit,\n \"sign-as\",\n config.account,\n \"network-config\",\n network,\n ];\n\n if (config.privateKey) {\n args.push(\"sign-with-plaintext-private-key\", config.privateKey, \"send\");\n } else {\n args.push(\"sign-with-keychain\", \"send\");\n }\n\n const output = yield* Effect.tryPromise({\n try: async () => {\n return await new Promise<string>((resolve, reject) => {\n const proc = spawn(\"near\", args, { stdio: [\"inherit\", \"pipe\", \"pipe\"] });\n\n let stdout = \"\";\n let stderr = \"\";\n\n proc.stdout?.on(\"data\", (data) => {\n const text = data.toString();\n stdout += text;\n process.stdout.write(text);\n });\n\n proc.stderr?.on(\"data\", (data) => {\n const text = data.toString();\n stderr += text;\n });\n\n proc.on(\"close\", (code) => {\n const combined = `${stdout}\\n${stderr}`;\n const txHashMatch = combined.match(/Transaction ID:\\s*([A-Za-z0-9]+)/i);\n const hasCodeDoesNotExist = /CodeDoesNotExist/i.test(combined);\n const hasTransactionFailed = /Transaction failed/i.test(combined);\n const softSuccess =\n Boolean(txHashMatch?.[1]) && hasCodeDoesNotExist && hasTransactionFailed;\n\n if (code === 0 || softSuccess) {\n if (softSuccess) {\n console.log(` ${txHashMatch?.[1]} — FastDATA CodeDoesNotExist (expected)`);\n }\n resolve(combined);\n } else {\n reject(new NearTransactionError(stderr || `Transaction failed with code ${code}`));\n }\n });\n\n proc.on(\"error\", (err) => reject(new NearTransactionError(err.message)));\n });\n },\n catch: (error) => error as Error,\n });\n\n const txHashMatch = output.match(/Transaction ID:\\s*([A-Za-z0-9]+)/i);\n\n return {\n success: true,\n txHash: txHashMatch?.[1],\n };\n });\n\nexport async function addFunctionCallAccessKey(\n config: FunctionCallAccessKeyConfig,\n): Promise<NearKeyPair> {\n const keyPair = generateNearKeyPair();\n const args = [\n \"account\",\n \"add-key\",\n config.account,\n \"grant-function-call-access\",\n \"--allowance\",\n config.allowance,\n \"--contract-account-id\",\n config.contract,\n \"--function-names\",\n config.functionNames.join(\", \"),\n \"use-manually-provided-public-key\",\n keyPair.publicKey,\n \"network-config\",\n config.network || (config.account.endsWith(\".testnet\") ? \"testnet\" : \"mainnet\"),\n \"sign-with-keychain\",\n \"send\",\n ];\n\n await runNearCommand(args);\n return keyPair;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"near-cli.cjs","names":["Effect"],"sources":["../src/near-cli.ts"],"sourcesContent":["import { generateKeyPairSync } from \"node:crypto\";\nimport { Effect } from \"effect\";\nimport { execa } from \"execa\";\n\nexport interface NearTransactionConfig {\n account: string;\n contract: string;\n method: string;\n argsBase64: string;\n network?: \"mainnet\" | \"testnet\";\n privateKey?: string;\n gas?: string;\n deposit?: string;\n}\n\nexport interface NearTransactionResult {\n success: boolean;\n txHash?: string;\n error?: string;\n}\n\nexport interface NearKeyPair {\n publicKey: string;\n privateKey: string;\n}\n\nexport interface FunctionCallAccessKeyConfig {\n account: string;\n contract: string;\n allowance: string;\n functionNames: string[];\n network?: \"mainnet\" | \"testnet\";\n}\n\nconst NEAR_CLI_VERSION = \"0.23.5\";\nconst INSTALLER_URL = `https://github.com/near/near-cli-rs/releases/download/v${NEAR_CLI_VERSION}/near-cli-rs-installer.sh`;\nconst BASE58_ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n\nexport class NearCliNotFoundError extends Error {\n readonly _tag = \"NearCliNotFoundError\";\n constructor() {\n super(\"NEAR CLI not found\");\n }\n}\n\nexport class NearCliInstallError extends Error {\n readonly _tag = \"NearCliInstallError\";\n constructor(message: string) {\n super(`Failed to install NEAR CLI: ${message}`);\n }\n}\n\nexport class NearTransactionError extends Error {\n readonly _tag = \"NearTransactionError\";\n}\n\nfunction base64UrlToBytes(input: string): Uint8Array {\n const normalized = input.replace(/-/g, \"+\").replace(/_/g, \"/\");\n return new Uint8Array(Buffer.from(normalized, \"base64\"));\n}\n\nfunction base58Encode(input: Uint8Array): string {\n if (input.length === 0) return \"\";\n\n const digits: number[] = [0];\n for (const byte of input) {\n let carry = byte;\n for (let i = 0; i < digits.length; i++) {\n carry += digits[i]! << 8;\n digits[i] = carry % 58;\n carry = Math.floor(carry / 58);\n }\n while (carry > 0) {\n digits.push(carry % 58);\n carry = Math.floor(carry / 58);\n }\n }\n\n let output = \"\";\n for (const byte of input) {\n if (byte === 0) output += BASE58_ALPHABET[0];\n else break;\n }\n\n for (let i = digits.length - 1; i >= 0; i--) {\n output += BASE58_ALPHABET[digits[i]!]!;\n }\n\n return output;\n}\n\nexport function generateNearKeyPair(): NearKeyPair {\n const { publicKey, privateKey } = generateKeyPairSync(\"ed25519\");\n const publicJwk = publicKey.export({ format: \"jwk\" }) as JsonWebKey;\n const privateJwk = privateKey.export({ format: \"jwk\" }) as JsonWebKey;\n\n if (!publicJwk.x || !privateJwk.d) {\n throw new Error(\"Failed to generate NEAR keypair\");\n }\n\n const publicBytes = base64UrlToBytes(publicJwk.x);\n const privateSeed = base64UrlToBytes(privateJwk.d);\n const secretBytes = new Uint8Array(privateSeed.length + publicBytes.length);\n secretBytes.set(privateSeed, 0);\n secretBytes.set(publicBytes, privateSeed.length);\n\n return {\n publicKey: `ed25519:${base58Encode(publicBytes)}`,\n privateKey: `ed25519:${base58Encode(secretBytes)}`,\n };\n}\n\nconst checkNearCliInstalled = Effect.tryPromise({\n try: async () => {\n try {\n await execa(\"near\", [\"--version\"], { stdio: \"pipe\" });\n return true;\n } catch {\n return false;\n }\n },\n catch: () => new Error(\"Failed to check NEAR CLI\"),\n});\n\nconst installNearCli = Effect.tryPromise({\n try: async () => {\n await execa(\"sh\", [\"-c\", `curl --proto '=https' --tlsv1.2 -LsSf ${INSTALLER_URL} | sh`], {\n stdio: \"inherit\",\n });\n },\n catch: (error) => {\n if (error instanceof Error && \"exitCode\" in error) {\n return new NearCliInstallError(\n `Installer exited with code ${(error as { exitCode: number }).exitCode}`,\n );\n }\n return new NearCliInstallError(error instanceof Error ? error.message : String(error));\n },\n});\n\nasync function runNearCommand(args: string[]): Promise<void> {\n await execa(\"near\", args, { stdio: \"inherit\" });\n}\n\nexport const ensureNearCli = Effect.gen(function* () {\n const isInstalled = yield* checkNearCliInstalled;\n if (isInstalled) return;\n\n if (process.env.BOS_INSTALL_NEAR_CLI === \"true\") {\n yield* installNearCli;\n return;\n }\n\n console.log();\n console.log(\" NEAR CLI not found\");\n\n console.log();\n console.log(` To install manually: curl --proto '=https' --tlsv1.2 -LsSf ${INSTALLER_URL} | sh`);\n console.log();\n yield* Effect.fail(new NearCliNotFoundError());\n});\n\nexport const executeTransaction = (\n config: NearTransactionConfig,\n): Effect.Effect<NearTransactionResult, Error> =>\n Effect.gen(function* () {\n const gas = (config.gas || \"300Tgas\").replace(/\\s+/g, \"\");\n const deposit = (config.deposit || \"0NEAR\").replace(/\\s+/g, \"\");\n const network = config.network || (config.account.endsWith(\".testnet\") ? \"testnet\" : \"mainnet\");\n\n const args = [\n \"contract\",\n \"call-function\",\n \"as-transaction\",\n config.contract,\n config.method,\n \"base64-args\",\n config.argsBase64,\n \"prepaid-gas\",\n gas,\n \"attached-deposit\",\n deposit,\n \"sign-as\",\n config.account,\n \"network-config\",\n network,\n ];\n\n if (config.privateKey) {\n args.push(\"sign-with-plaintext-private-key\", config.privateKey, \"send\");\n } else {\n args.push(\"sign-with-keychain\", \"send\");\n }\n\n const output = yield* Effect.tryPromise({\n try: async () => {\n const result = await execa(\"near\", args, {\n stdin: \"inherit\",\n stdout: \"pipe\",\n stderr: \"pipe\",\n reject: false,\n });\n\n process.stdout.write(result.stdout);\n const combined = `${result.stdout}\\n${result.stderr}`;\n const txHashMatch = combined.match(/Transaction ID:\\s*([A-Za-z0-9]+)/i);\n const hasCodeDoesNotExist = /CodeDoesNotExist/i.test(combined);\n const hasTransactionFailed = /Transaction failed/i.test(combined);\n const softSuccess =\n Boolean(txHashMatch?.[1]) && hasCodeDoesNotExist && hasTransactionFailed;\n\n if (result.exitCode === 0 || softSuccess) {\n if (softSuccess) {\n console.log(` ${txHashMatch?.[1]} — FastDATA CodeDoesNotExist (expected)`);\n }\n return combined;\n }\n\n throw new NearTransactionError(\n result.stderr || `Transaction failed with code ${result.exitCode}`,\n );\n },\n catch: (error) => error as Error,\n });\n\n const txHashMatch = output.match(/Transaction ID:\\s*([A-Za-z0-9]+)/i);\n\n return {\n success: true,\n txHash: txHashMatch?.[1],\n };\n });\n\nexport async function addFunctionCallAccessKey(\n config: FunctionCallAccessKeyConfig,\n): Promise<NearKeyPair> {\n const keyPair = generateNearKeyPair();\n const args = [\n \"account\",\n \"add-key\",\n config.account,\n \"grant-function-call-access\",\n \"--allowance\",\n config.allowance,\n \"--contract-account-id\",\n config.contract,\n \"--function-names\",\n config.functionNames.join(\", \"),\n \"use-manually-provided-public-key\",\n keyPair.publicKey,\n \"network-config\",\n config.network || (config.account.endsWith(\".testnet\") ? \"testnet\" : \"mainnet\"),\n \"sign-with-keychain\",\n \"send\",\n ];\n\n await runNearCommand(args);\n return keyPair;\n}\n"],"mappings":"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"}
|
package/dist/near-cli.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { generateKeyPairSync } from "node:crypto";
|
|
2
2
|
import { Effect } from "effect";
|
|
3
|
-
import {
|
|
3
|
+
import { execa } from "execa";
|
|
4
4
|
|
|
5
5
|
//#region src/near-cli.ts
|
|
6
6
|
const INSTALLER_URL = `https://github.com/near/near-cli-rs/releases/download/v0.23.5/near-cli-rs-installer.sh`;
|
|
@@ -62,36 +62,26 @@ function generateNearKeyPair() {
|
|
|
62
62
|
}
|
|
63
63
|
const checkNearCliInstalled = Effect.tryPromise({
|
|
64
64
|
try: async () => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
try {
|
|
66
|
+
await execa("near", ["--version"], { stdio: "pipe" });
|
|
67
|
+
return true;
|
|
68
|
+
} catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
70
71
|
},
|
|
71
72
|
catch: () => /* @__PURE__ */ new Error("Failed to check NEAR CLI")
|
|
72
73
|
});
|
|
73
74
|
const installNearCli = Effect.tryPromise({
|
|
74
75
|
try: async () => {
|
|
75
|
-
|
|
76
|
-
const proc = spawn("sh", ["-c", `curl --proto '=https' --tlsv1.2 -LsSf ${INSTALLER_URL} | sh`], { stdio: "inherit" });
|
|
77
|
-
proc.on("close", (code) => {
|
|
78
|
-
if (code === 0) resolve();
|
|
79
|
-
else reject(new NearCliInstallError(`Installer exited with code ${code}`));
|
|
80
|
-
});
|
|
81
|
-
proc.on("error", (err) => reject(new NearCliInstallError(err.message)));
|
|
82
|
-
});
|
|
76
|
+
await execa("sh", ["-c", `curl --proto '=https' --tlsv1.2 -LsSf ${INSTALLER_URL} | sh`], { stdio: "inherit" });
|
|
83
77
|
},
|
|
84
|
-
catch: (error) =>
|
|
78
|
+
catch: (error) => {
|
|
79
|
+
if (error instanceof Error && "exitCode" in error) return new NearCliInstallError(`Installer exited with code ${error.exitCode}`);
|
|
80
|
+
return new NearCliInstallError(error instanceof Error ? error.message : String(error));
|
|
81
|
+
}
|
|
85
82
|
});
|
|
86
83
|
async function runNearCommand(args) {
|
|
87
|
-
await
|
|
88
|
-
const proc = spawn("near", args, { stdio: "inherit" });
|
|
89
|
-
proc.on("close", (code) => {
|
|
90
|
-
if (code === 0) resolve();
|
|
91
|
-
else reject(/* @__PURE__ */ new Error(`near ${args.join(" ")} failed with exit code ${code}`));
|
|
92
|
-
});
|
|
93
|
-
proc.on("error", (err) => reject(new Error(err.message)));
|
|
94
|
-
});
|
|
84
|
+
await execa("near", args, { stdio: "inherit" });
|
|
95
85
|
}
|
|
96
86
|
const ensureNearCli = Effect.gen(function* () {
|
|
97
87
|
if (yield* checkNearCliInstalled) return;
|
|
@@ -133,36 +123,23 @@ const executeTransaction = (config) => Effect.gen(function* () {
|
|
|
133
123
|
success: true,
|
|
134
124
|
txHash: (yield* Effect.tryPromise({
|
|
135
125
|
try: async () => {
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
] });
|
|
142
|
-
let stdout = "";
|
|
143
|
-
let stderr = "";
|
|
144
|
-
proc.stdout?.on("data", (data) => {
|
|
145
|
-
const text = data.toString();
|
|
146
|
-
stdout += text;
|
|
147
|
-
process.stdout.write(text);
|
|
148
|
-
});
|
|
149
|
-
proc.stderr?.on("data", (data) => {
|
|
150
|
-
const text = data.toString();
|
|
151
|
-
stderr += text;
|
|
152
|
-
});
|
|
153
|
-
proc.on("close", (code) => {
|
|
154
|
-
const combined = `${stdout}\n${stderr}`;
|
|
155
|
-
const txHashMatch = combined.match(/Transaction ID:\s*([A-Za-z0-9]+)/i);
|
|
156
|
-
const hasCodeDoesNotExist = /CodeDoesNotExist/i.test(combined);
|
|
157
|
-
const hasTransactionFailed = /Transaction failed/i.test(combined);
|
|
158
|
-
const softSuccess = Boolean(txHashMatch?.[1]) && hasCodeDoesNotExist && hasTransactionFailed;
|
|
159
|
-
if (code === 0 || softSuccess) {
|
|
160
|
-
if (softSuccess) console.log(` ${txHashMatch?.[1]} — FastDATA CodeDoesNotExist (expected)`);
|
|
161
|
-
resolve(combined);
|
|
162
|
-
} else reject(new NearTransactionError(stderr || `Transaction failed with code ${code}`));
|
|
163
|
-
});
|
|
164
|
-
proc.on("error", (err) => reject(new NearTransactionError(err.message)));
|
|
126
|
+
const result = await execa("near", args, {
|
|
127
|
+
stdin: "inherit",
|
|
128
|
+
stdout: "pipe",
|
|
129
|
+
stderr: "pipe",
|
|
130
|
+
reject: false
|
|
165
131
|
});
|
|
132
|
+
process.stdout.write(result.stdout);
|
|
133
|
+
const combined = `${result.stdout}\n${result.stderr}`;
|
|
134
|
+
const txHashMatch = combined.match(/Transaction ID:\s*([A-Za-z0-9]+)/i);
|
|
135
|
+
const hasCodeDoesNotExist = /CodeDoesNotExist/i.test(combined);
|
|
136
|
+
const hasTransactionFailed = /Transaction failed/i.test(combined);
|
|
137
|
+
const softSuccess = Boolean(txHashMatch?.[1]) && hasCodeDoesNotExist && hasTransactionFailed;
|
|
138
|
+
if (result.exitCode === 0 || softSuccess) {
|
|
139
|
+
if (softSuccess) console.log(` ${txHashMatch?.[1]} — FastDATA CodeDoesNotExist (expected)`);
|
|
140
|
+
return combined;
|
|
141
|
+
}
|
|
142
|
+
throw new NearTransactionError(result.stderr || `Transaction failed with code ${result.exitCode}`);
|
|
166
143
|
},
|
|
167
144
|
catch: (error) => error
|
|
168
145
|
})).match(/Transaction ID:\s*([A-Za-z0-9]+)/i)?.[1]
|