everything-dev 1.4.1 → 1.6.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 +78 -8
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +6 -0
- package/dist/cli/init.d.cts.map +1 -1
- package/dist/cli/init.d.mts +6 -0
- package/dist/cli/init.d.mts.map +1 -1
- package/dist/cli/init.mjs +78 -8
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli/prompts.cjs +64 -6
- package/dist/cli/prompts.cjs.map +1 -1
- package/dist/cli/prompts.mjs +64 -6
- package/dist/cli/prompts.mjs.map +1 -1
- package/dist/cli/sync.cjs +85 -18
- package/dist/cli/sync.cjs.map +1 -1
- package/dist/cli/sync.mjs +85 -18
- package/dist/cli/sync.mjs.map +1 -1
- package/dist/cli.cjs +41 -4
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.mjs +41 -4
- package/dist/cli.mjs.map +1 -1
- package/dist/contract.cjs +3 -0
- package/dist/contract.cjs.map +1 -1
- package/dist/contract.d.cts +14 -6
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +14 -6
- package/dist/contract.d.mts.map +1 -1
- package/dist/contract.mjs +3 -0
- package/dist/contract.mjs.map +1 -1
- package/dist/plugin.cjs +48 -17
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +9 -4
- package/dist/plugin.d.mts +9 -4
- package/dist/plugin.mjs +48 -17
- package/dist/plugin.mjs.map +1 -1
- package/dist/types.cjs +2 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +4 -2
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +4 -2
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +2 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +2 -2
- package/src/cli/init.ts +122 -7
- package/src/cli/prompts.ts +84 -10
- package/src/cli/sync.ts +142 -17
- package/src/cli.ts +55 -4
- package/src/contract.ts +3 -0
- package/src/plugin.ts +51 -18
- package/src/types.ts +1 -0
package/dist/cli/prompts.cjs
CHANGED
|
@@ -20,22 +20,79 @@ async function promptYesNo(question, defaultVal = false) {
|
|
|
20
20
|
if (!answer) return defaultVal;
|
|
21
21
|
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
22
22
|
}
|
|
23
|
-
function
|
|
24
|
-
|
|
23
|
+
function parseExtendsRef(ref) {
|
|
24
|
+
const match = ref.match(/^(?:bos:\/\/)?([^/]+)\/(.+)$/);
|
|
25
|
+
if (!match) return null;
|
|
26
|
+
return {
|
|
27
|
+
account: match[1],
|
|
28
|
+
gateway: match[2]
|
|
29
|
+
};
|
|
25
30
|
}
|
|
26
31
|
function deriveAccountFromDomain(domain, extendsAccount) {
|
|
27
32
|
const firstSegment = domain.split(".")[0];
|
|
28
33
|
if (!firstSegment) return "";
|
|
29
34
|
return `${firstSegment}.${extendsAccount.includes(".") ? extendsAccount.substring(extendsAccount.indexOf(".") + 1) : extendsAccount}`;
|
|
30
35
|
}
|
|
36
|
+
const AVAILABLE_PLUGINS = [{
|
|
37
|
+
key: "_template",
|
|
38
|
+
label: "template",
|
|
39
|
+
description: "Plugin scaffold and boilerplate",
|
|
40
|
+
default: true
|
|
41
|
+
}, {
|
|
42
|
+
key: "registry",
|
|
43
|
+
label: "registry",
|
|
44
|
+
description: "FastKV app discovery and metadata",
|
|
45
|
+
default: false
|
|
46
|
+
}];
|
|
47
|
+
async function promptPluginSelect() {
|
|
48
|
+
const selected = new Set(AVAILABLE_PLUGINS.filter((p) => p.default).map((p) => p.key));
|
|
49
|
+
console.log();
|
|
50
|
+
console.log(" Select plugins (enter number to toggle, enter to confirm):");
|
|
51
|
+
for (let i = 0; i < AVAILABLE_PLUGINS.length; i++) {
|
|
52
|
+
const p = AVAILABLE_PLUGINS[i];
|
|
53
|
+
const marker = selected.has(p.key) ? "●" : "○";
|
|
54
|
+
console.log(` ${marker} ${i + 1}. ${p.label} — ${p.description}`);
|
|
55
|
+
}
|
|
56
|
+
console.log();
|
|
57
|
+
while (true) {
|
|
58
|
+
const answer = await prompt(" Plugins", selected.size > 0 ? Array.from(selected).join(",") : "");
|
|
59
|
+
if (!answer) break;
|
|
60
|
+
const num = Number.parseInt(answer, 10);
|
|
61
|
+
if (num >= 1 && num <= AVAILABLE_PLUGINS.length) {
|
|
62
|
+
const plugin = AVAILABLE_PLUGINS[num - 1];
|
|
63
|
+
if (selected.has(plugin.key)) selected.delete(plugin.key);
|
|
64
|
+
else selected.add(plugin.key);
|
|
65
|
+
console.log(" Current selection:");
|
|
66
|
+
for (let i = 0; i < AVAILABLE_PLUGINS.length; i++) {
|
|
67
|
+
const p = AVAILABLE_PLUGINS[i];
|
|
68
|
+
const marker = selected.has(p.key) ? "●" : "○";
|
|
69
|
+
console.log(` ${marker} ${i + 1}. ${p.label}`);
|
|
70
|
+
}
|
|
71
|
+
console.log();
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
return Array.from(selected);
|
|
77
|
+
}
|
|
31
78
|
async function promptInitOptions(input) {
|
|
32
|
-
const extendsAccount = input.extendsAccount || await prompt("Extends account", "dev.everything.near");
|
|
33
|
-
const extendsGateway = input.extendsGateway || await prompt("Extends gateway", "everything.dev");
|
|
34
79
|
const domain = input.domain || await prompt("Project domain");
|
|
80
|
+
const extendsInput = input.extends || await prompt("Extend from", "");
|
|
81
|
+
let extendsAccount = input.extendsAccount || "";
|
|
82
|
+
let extendsGateway = input.extendsGateway || "";
|
|
83
|
+
if (extendsInput) {
|
|
84
|
+
const parsed = parseExtendsRef(extendsInput);
|
|
85
|
+
if (parsed) {
|
|
86
|
+
extendsAccount = extendsAccount || parsed.account;
|
|
87
|
+
extendsGateway = extendsGateway || parsed.gateway;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
extendsAccount = extendsAccount || "dev.everything.near";
|
|
91
|
+
extendsGateway = extendsGateway || "everything.dev";
|
|
35
92
|
const accountDefault = domain ? deriveAccountFromDomain(domain, extendsAccount) : "";
|
|
36
93
|
const account = input.account || await prompt("Project NEAR account", accountDefault);
|
|
37
|
-
const
|
|
38
|
-
const
|
|
94
|
+
const directory = input.directory || domain || extendsGateway;
|
|
95
|
+
const plugins = input.plugins || await promptPluginSelect();
|
|
39
96
|
const withHost = input.withHost !== void 0 ? input.withHost : await promptYesNo("Include host?", false);
|
|
40
97
|
return {
|
|
41
98
|
extendsAccount,
|
|
@@ -43,6 +100,7 @@ async function promptInitOptions(input) {
|
|
|
43
100
|
directory,
|
|
44
101
|
account: account || void 0,
|
|
45
102
|
domain: domain || void 0,
|
|
103
|
+
plugins,
|
|
46
104
|
withHost
|
|
47
105
|
};
|
|
48
106
|
}
|
package/dist/cli/prompts.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.cjs","names":[],"sources":["../../src/cli/prompts.ts"],"sourcesContent":["import { createInterface } from \"node:readline\";\n\nexport async function prompt(question: string, defaultValue?: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const suffix = defaultValue ? ` [${defaultValue}]` : \"\";\n const fullQuestion = `${question}${suffix}: `;\n\n return new Promise<string>((resolve) => {\n rl.question(fullQuestion, (answer) => {\n rl.close();\n const trimmed = answer.trim();\n resolve(trimmed || defaultValue || \"\");\n });\n });\n}\n\nexport async function promptYesNo(question: string, defaultVal = false): Promise<boolean> {\n const hint = defaultVal ? \"Y/n\" : \"y/N\";\n const answer = await prompt(`${question} (${hint})`);\n if (!answer) return defaultVal;\n return answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\";\n}\n\nfunction
|
|
1
|
+
{"version":3,"file":"prompts.cjs","names":[],"sources":["../../src/cli/prompts.ts"],"sourcesContent":["import { createInterface } from \"node:readline\";\n\nexport async function prompt(question: string, defaultValue?: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const suffix = defaultValue ? ` [${defaultValue}]` : \"\";\n const fullQuestion = `${question}${suffix}: `;\n\n return new Promise<string>((resolve) => {\n rl.question(fullQuestion, (answer) => {\n rl.close();\n const trimmed = answer.trim();\n resolve(trimmed || defaultValue || \"\");\n });\n });\n}\n\nexport async function promptYesNo(question: string, defaultVal = false): Promise<boolean> {\n const hint = defaultVal ? \"Y/n\" : \"y/N\";\n const answer = await prompt(`${question} (${hint})`);\n if (!answer) return defaultVal;\n return answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\";\n}\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\nconst AVAILABLE_PLUGINS = [\n {\n key: \"_template\",\n label: \"template\",\n description: \"Plugin scaffold and boilerplate\",\n default: true,\n },\n {\n key: \"registry\",\n label: \"registry\",\n description: \"FastKV app discovery and metadata\",\n default: false,\n },\n];\n\nasync function promptPluginSelect(): Promise<string[]> {\n const selected = new Set<string>(AVAILABLE_PLUGINS.filter((p) => p.default).map((p) => p.key));\n\n console.log();\n console.log(\" Select plugins (enter number to toggle, enter to confirm):\");\n for (let i = 0; i < AVAILABLE_PLUGINS.length; i++) {\n const p = AVAILABLE_PLUGINS[i];\n const marker = selected.has(p.key) ? \"●\" : \"○\";\n console.log(` ${marker} ${i + 1}. ${p.label} — ${p.description}`);\n }\n console.log();\n\n while (true) {\n const answer = await prompt(\n \" Plugins\",\n selected.size > 0 ? Array.from(selected).join(\",\") : \"\",\n );\n if (!answer) break;\n\n const num = Number.parseInt(answer, 10);\n if (num >= 1 && num <= AVAILABLE_PLUGINS.length) {\n const plugin = AVAILABLE_PLUGINS[num - 1];\n if (selected.has(plugin.key)) {\n selected.delete(plugin.key);\n } else {\n selected.add(plugin.key);\n }\n\n console.log(\" Current selection:\");\n for (let i = 0; i < AVAILABLE_PLUGINS.length; i++) {\n const p = AVAILABLE_PLUGINS[i];\n const marker = selected.has(p.key) ? \"●\" : \"○\";\n console.log(` ${marker} ${i + 1}. ${p.label}`);\n }\n console.log();\n continue;\n }\n\n break;\n }\n\n return Array.from(selected);\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}): Promise<{\n extendsAccount: string;\n extendsGateway: string;\n directory: string;\n account?: string;\n domain?: string;\n plugins: string[];\n withHost: boolean;\n}> {\n const domain = input.domain || (await prompt(\"Project domain\"));\n\n const extendsInput = input.extends || (await prompt(\"Extend from\", \"\"));\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 = input.account || (await prompt(\"Project NEAR account\", accountDefault));\n\n const directory = input.directory || domain || extendsGateway;\n\n const plugins = input.plugins || (await promptPluginSelect());\n\n const withHost =\n input.withHost !== undefined ? input.withHost : await promptYesNo(\"Include host?\", 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":"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"}
|
package/dist/cli/prompts.mjs
CHANGED
|
@@ -19,22 +19,79 @@ async function promptYesNo(question, defaultVal = false) {
|
|
|
19
19
|
if (!answer) return defaultVal;
|
|
20
20
|
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
21
21
|
}
|
|
22
|
-
function
|
|
23
|
-
|
|
22
|
+
function parseExtendsRef(ref) {
|
|
23
|
+
const match = ref.match(/^(?:bos:\/\/)?([^/]+)\/(.+)$/);
|
|
24
|
+
if (!match) return null;
|
|
25
|
+
return {
|
|
26
|
+
account: match[1],
|
|
27
|
+
gateway: match[2]
|
|
28
|
+
};
|
|
24
29
|
}
|
|
25
30
|
function deriveAccountFromDomain(domain, extendsAccount) {
|
|
26
31
|
const firstSegment = domain.split(".")[0];
|
|
27
32
|
if (!firstSegment) return "";
|
|
28
33
|
return `${firstSegment}.${extendsAccount.includes(".") ? extendsAccount.substring(extendsAccount.indexOf(".") + 1) : extendsAccount}`;
|
|
29
34
|
}
|
|
35
|
+
const AVAILABLE_PLUGINS = [{
|
|
36
|
+
key: "_template",
|
|
37
|
+
label: "template",
|
|
38
|
+
description: "Plugin scaffold and boilerplate",
|
|
39
|
+
default: true
|
|
40
|
+
}, {
|
|
41
|
+
key: "registry",
|
|
42
|
+
label: "registry",
|
|
43
|
+
description: "FastKV app discovery and metadata",
|
|
44
|
+
default: false
|
|
45
|
+
}];
|
|
46
|
+
async function promptPluginSelect() {
|
|
47
|
+
const selected = new Set(AVAILABLE_PLUGINS.filter((p) => p.default).map((p) => p.key));
|
|
48
|
+
console.log();
|
|
49
|
+
console.log(" Select plugins (enter number to toggle, enter to confirm):");
|
|
50
|
+
for (let i = 0; i < AVAILABLE_PLUGINS.length; i++) {
|
|
51
|
+
const p = AVAILABLE_PLUGINS[i];
|
|
52
|
+
const marker = selected.has(p.key) ? "●" : "○";
|
|
53
|
+
console.log(` ${marker} ${i + 1}. ${p.label} — ${p.description}`);
|
|
54
|
+
}
|
|
55
|
+
console.log();
|
|
56
|
+
while (true) {
|
|
57
|
+
const answer = await prompt(" Plugins", selected.size > 0 ? Array.from(selected).join(",") : "");
|
|
58
|
+
if (!answer) break;
|
|
59
|
+
const num = Number.parseInt(answer, 10);
|
|
60
|
+
if (num >= 1 && num <= AVAILABLE_PLUGINS.length) {
|
|
61
|
+
const plugin = AVAILABLE_PLUGINS[num - 1];
|
|
62
|
+
if (selected.has(plugin.key)) selected.delete(plugin.key);
|
|
63
|
+
else selected.add(plugin.key);
|
|
64
|
+
console.log(" Current selection:");
|
|
65
|
+
for (let i = 0; i < AVAILABLE_PLUGINS.length; i++) {
|
|
66
|
+
const p = AVAILABLE_PLUGINS[i];
|
|
67
|
+
const marker = selected.has(p.key) ? "●" : "○";
|
|
68
|
+
console.log(` ${marker} ${i + 1}. ${p.label}`);
|
|
69
|
+
}
|
|
70
|
+
console.log();
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
return Array.from(selected);
|
|
76
|
+
}
|
|
30
77
|
async function promptInitOptions(input) {
|
|
31
|
-
const extendsAccount = input.extendsAccount || await prompt("Extends account", "dev.everything.near");
|
|
32
|
-
const extendsGateway = input.extendsGateway || await prompt("Extends gateway", "everything.dev");
|
|
33
78
|
const domain = input.domain || await prompt("Project domain");
|
|
79
|
+
const extendsInput = input.extends || await prompt("Extend from", "");
|
|
80
|
+
let extendsAccount = input.extendsAccount || "";
|
|
81
|
+
let extendsGateway = input.extendsGateway || "";
|
|
82
|
+
if (extendsInput) {
|
|
83
|
+
const parsed = parseExtendsRef(extendsInput);
|
|
84
|
+
if (parsed) {
|
|
85
|
+
extendsAccount = extendsAccount || parsed.account;
|
|
86
|
+
extendsGateway = extendsGateway || parsed.gateway;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
extendsAccount = extendsAccount || "dev.everything.near";
|
|
90
|
+
extendsGateway = extendsGateway || "everything.dev";
|
|
34
91
|
const accountDefault = domain ? deriveAccountFromDomain(domain, extendsAccount) : "";
|
|
35
92
|
const account = input.account || await prompt("Project NEAR account", accountDefault);
|
|
36
|
-
const
|
|
37
|
-
const
|
|
93
|
+
const directory = input.directory || domain || extendsGateway;
|
|
94
|
+
const plugins = input.plugins || await promptPluginSelect();
|
|
38
95
|
const withHost = input.withHost !== void 0 ? input.withHost : await promptYesNo("Include host?", false);
|
|
39
96
|
return {
|
|
40
97
|
extendsAccount,
|
|
@@ -42,6 +99,7 @@ async function promptInitOptions(input) {
|
|
|
42
99
|
directory,
|
|
43
100
|
account: account || void 0,
|
|
44
101
|
domain: domain || void 0,
|
|
102
|
+
plugins,
|
|
45
103
|
withHost
|
|
46
104
|
};
|
|
47
105
|
}
|
package/dist/cli/prompts.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.mjs","names":[],"sources":["../../src/cli/prompts.ts"],"sourcesContent":["import { createInterface } from \"node:readline\";\n\nexport async function prompt(question: string, defaultValue?: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const suffix = defaultValue ? ` [${defaultValue}]` : \"\";\n const fullQuestion = `${question}${suffix}: `;\n\n return new Promise<string>((resolve) => {\n rl.question(fullQuestion, (answer) => {\n rl.close();\n const trimmed = answer.trim();\n resolve(trimmed || defaultValue || \"\");\n });\n });\n}\n\nexport async function promptYesNo(question: string, defaultVal = false): Promise<boolean> {\n const hint = defaultVal ? \"Y/n\" : \"y/N\";\n const answer = await prompt(`${question} (${hint})`);\n if (!answer) return defaultVal;\n return answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\";\n}\n\nfunction
|
|
1
|
+
{"version":3,"file":"prompts.mjs","names":[],"sources":["../../src/cli/prompts.ts"],"sourcesContent":["import { createInterface } from \"node:readline\";\n\nexport async function prompt(question: string, defaultValue?: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n const suffix = defaultValue ? ` [${defaultValue}]` : \"\";\n const fullQuestion = `${question}${suffix}: `;\n\n return new Promise<string>((resolve) => {\n rl.question(fullQuestion, (answer) => {\n rl.close();\n const trimmed = answer.trim();\n resolve(trimmed || defaultValue || \"\");\n });\n });\n}\n\nexport async function promptYesNo(question: string, defaultVal = false): Promise<boolean> {\n const hint = defaultVal ? \"Y/n\" : \"y/N\";\n const answer = await prompt(`${question} (${hint})`);\n if (!answer) return defaultVal;\n return answer.toLowerCase() === \"y\" || answer.toLowerCase() === \"yes\";\n}\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\nconst AVAILABLE_PLUGINS = [\n {\n key: \"_template\",\n label: \"template\",\n description: \"Plugin scaffold and boilerplate\",\n default: true,\n },\n {\n key: \"registry\",\n label: \"registry\",\n description: \"FastKV app discovery and metadata\",\n default: false,\n },\n];\n\nasync function promptPluginSelect(): Promise<string[]> {\n const selected = new Set<string>(AVAILABLE_PLUGINS.filter((p) => p.default).map((p) => p.key));\n\n console.log();\n console.log(\" Select plugins (enter number to toggle, enter to confirm):\");\n for (let i = 0; i < AVAILABLE_PLUGINS.length; i++) {\n const p = AVAILABLE_PLUGINS[i];\n const marker = selected.has(p.key) ? \"●\" : \"○\";\n console.log(` ${marker} ${i + 1}. ${p.label} — ${p.description}`);\n }\n console.log();\n\n while (true) {\n const answer = await prompt(\n \" Plugins\",\n selected.size > 0 ? Array.from(selected).join(\",\") : \"\",\n );\n if (!answer) break;\n\n const num = Number.parseInt(answer, 10);\n if (num >= 1 && num <= AVAILABLE_PLUGINS.length) {\n const plugin = AVAILABLE_PLUGINS[num - 1];\n if (selected.has(plugin.key)) {\n selected.delete(plugin.key);\n } else {\n selected.add(plugin.key);\n }\n\n console.log(\" Current selection:\");\n for (let i = 0; i < AVAILABLE_PLUGINS.length; i++) {\n const p = AVAILABLE_PLUGINS[i];\n const marker = selected.has(p.key) ? \"●\" : \"○\";\n console.log(` ${marker} ${i + 1}. ${p.label}`);\n }\n console.log();\n continue;\n }\n\n break;\n }\n\n return Array.from(selected);\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}): Promise<{\n extendsAccount: string;\n extendsGateway: string;\n directory: string;\n account?: string;\n domain?: string;\n plugins: string[];\n withHost: boolean;\n}> {\n const domain = input.domain || (await prompt(\"Project domain\"));\n\n const extendsInput = input.extends || (await prompt(\"Extend from\", \"\"));\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 = input.account || (await prompt(\"Project NEAR account\", accountDefault));\n\n const directory = input.directory || domain || extendsGateway;\n\n const plugins = input.plugins || (await promptPluginSelect());\n\n const withHost =\n input.withHost !== undefined ? input.withHost : await promptYesNo(\"Include host?\", 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":"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"}
|
package/dist/cli/sync.cjs
CHANGED
|
@@ -50,6 +50,47 @@ function backupFiles(projectDir, filePaths) {
|
|
|
50
50
|
}
|
|
51
51
|
return backupDir;
|
|
52
52
|
}
|
|
53
|
+
function mergePackageJson(local, template) {
|
|
54
|
+
const merged = { ...template };
|
|
55
|
+
for (const depField of [
|
|
56
|
+
"dependencies",
|
|
57
|
+
"devDependencies",
|
|
58
|
+
"peerDependencies",
|
|
59
|
+
"overrides"
|
|
60
|
+
]) {
|
|
61
|
+
const localDeps = local[depField];
|
|
62
|
+
const templateDeps = template[depField];
|
|
63
|
+
if (!localDeps && !templateDeps) continue;
|
|
64
|
+
const mergedDeps = { ...templateDeps ?? {} };
|
|
65
|
+
if (localDeps) {
|
|
66
|
+
for (const [name, version] of Object.entries(localDeps)) if (!(name in mergedDeps)) mergedDeps[name] = version;
|
|
67
|
+
}
|
|
68
|
+
if (Object.keys(mergedDeps).length > 0) merged[depField] = mergedDeps;
|
|
69
|
+
}
|
|
70
|
+
if (local.scripts && typeof local.scripts === "object") merged.scripts = {
|
|
71
|
+
...template.scripts ?? {},
|
|
72
|
+
...local.scripts
|
|
73
|
+
};
|
|
74
|
+
return merged;
|
|
75
|
+
}
|
|
76
|
+
function toDestPath(filePath) {
|
|
77
|
+
return filePath.startsWith(".templates/") ? filePath.slice(11) : filePath;
|
|
78
|
+
}
|
|
79
|
+
function writeSyncedFile(sourceDir, projectDir, filePath) {
|
|
80
|
+
const src = (0, node_path.join)(sourceDir, filePath);
|
|
81
|
+
const dest = (0, node_path.join)(projectDir, filePath.startsWith(".templates/") ? filePath.slice(11) : filePath);
|
|
82
|
+
(0, node_fs.mkdirSync)((0, node_path.dirname)(dest), { recursive: true });
|
|
83
|
+
if (filePath.endsWith("package.json")) {
|
|
84
|
+
const localContent = (0, node_fs.existsSync)(dest) ? (0, node_fs.readFileSync)(dest, "utf-8") : null;
|
|
85
|
+
const templateContent = (0, node_fs.readFileSync)(src, "utf-8");
|
|
86
|
+
if (localContent) {
|
|
87
|
+
const merged = mergePackageJson(JSON.parse(localContent), JSON.parse(templateContent));
|
|
88
|
+
(0, node_fs.writeFileSync)(dest, `${JSON.stringify(merged, null, 2)}\n`);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
(0, node_fs.writeFileSync)(dest, (0, node_fs.readFileSync)(src));
|
|
93
|
+
}
|
|
53
94
|
async function syncTemplate(projectDir, options) {
|
|
54
95
|
const localConfig = JSON.parse((0, node_fs.readFileSync)((0, node_path.join)(projectDir, "bos.config.json"), "utf-8"));
|
|
55
96
|
const extendsRef = localConfig.extends;
|
|
@@ -70,7 +111,7 @@ async function syncTemplate(projectDir, options) {
|
|
|
70
111
|
};
|
|
71
112
|
const extendsAccount = extendsMatch[1];
|
|
72
113
|
const extendsGateway = extendsMatch[2];
|
|
73
|
-
const { sourceDir, cleanup } = await require_cli_init.resolveSourceDir({
|
|
114
|
+
const { sourceDir, parentConfig, cleanup } = await require_cli_init.resolveSourceDir({
|
|
74
115
|
extendsAccount,
|
|
75
116
|
extendsGateway
|
|
76
117
|
});
|
|
@@ -96,28 +137,55 @@ async function syncTemplate(projectDir, options) {
|
|
|
96
137
|
});
|
|
97
138
|
for (const match of matches) allTemplateFiles.add(match);
|
|
98
139
|
}
|
|
140
|
+
const childPlugins = localConfig.plugins && typeof localConfig.plugins === "object" ? Object.keys(localConfig.plugins) : [];
|
|
141
|
+
const pluginRoutes = {};
|
|
142
|
+
if (parentConfig.plugins) {
|
|
143
|
+
for (const [key, ref] of Object.entries(parentConfig.plugins)) if (ref.routes && ref.routes.length > 0) pluginRoutes[key] = ref.routes;
|
|
144
|
+
}
|
|
145
|
+
const excludedRoutePatterns = [];
|
|
146
|
+
for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) if (!childPlugins.includes(pluginKey)) excludedRoutePatterns.push(...routePatterns);
|
|
147
|
+
const filteredFiles = /* @__PURE__ */ new Set();
|
|
148
|
+
for (const filePath of allTemplateFiles) {
|
|
149
|
+
const pluginMatch = filePath.match(/^plugins\/([^/]+)/);
|
|
150
|
+
if (pluginMatch && !childPlugins.includes(pluginMatch[1])) continue;
|
|
151
|
+
if (isExcluded(filePath, excludedRoutePatterns)) continue;
|
|
152
|
+
filteredFiles.add(filePath);
|
|
153
|
+
}
|
|
154
|
+
for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {
|
|
155
|
+
if (!childPlugins.includes(pluginKey)) continue;
|
|
156
|
+
for (const rp of routePatterns) {
|
|
157
|
+
const matches = await (0, glob.glob)(rp, {
|
|
158
|
+
cwd: sourceDir,
|
|
159
|
+
nodir: true,
|
|
160
|
+
dot: true,
|
|
161
|
+
absolute: false
|
|
162
|
+
});
|
|
163
|
+
for (const match of matches) if (!isExcluded(match, excludedRoutePatterns)) filteredFiles.add(match);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
99
166
|
const snapshot = await require_snapshot.readSnapshot(projectDir);
|
|
100
167
|
const updated = [];
|
|
101
168
|
const skipped = [];
|
|
102
169
|
const added = [];
|
|
103
|
-
for (const filePath of
|
|
104
|
-
|
|
105
|
-
|
|
170
|
+
for (const filePath of filteredFiles) {
|
|
171
|
+
const destPath = toDestPath(filePath);
|
|
172
|
+
if (isExcluded(destPath, excludePatterns)) continue;
|
|
173
|
+
const localHash = computeLocalHash(projectDir, destPath);
|
|
106
174
|
const sourceContent = (0, node_fs.readFileSync)((0, node_path.join)(sourceDir, filePath));
|
|
107
175
|
const sourceHash = (0, node_crypto.createHash)("sha256").update(sourceContent).digest("hex").substring(0, 16);
|
|
108
176
|
if (localHash === null) {
|
|
109
|
-
added.push(
|
|
177
|
+
added.push(destPath);
|
|
110
178
|
continue;
|
|
111
179
|
}
|
|
112
180
|
if (localHash === sourceHash) continue;
|
|
113
|
-
const snapshotHash = snapshot?.files[
|
|
181
|
+
const snapshotHash = snapshot?.files[destPath];
|
|
114
182
|
if (snapshotHash === void 0) {
|
|
115
|
-
updated.push(
|
|
183
|
+
updated.push(destPath);
|
|
116
184
|
continue;
|
|
117
185
|
}
|
|
118
|
-
if (localHash === snapshotHash) updated.push(
|
|
119
|
-
else if (options.force) updated.push(
|
|
120
|
-
else skipped.push(
|
|
186
|
+
if (localHash === snapshotHash) updated.push(destPath);
|
|
187
|
+
else if (options.force) updated.push(destPath);
|
|
188
|
+
else skipped.push(destPath);
|
|
121
189
|
}
|
|
122
190
|
if (options.dryRun) return {
|
|
123
191
|
status: "dry-run",
|
|
@@ -126,21 +194,18 @@ async function syncTemplate(projectDir, options) {
|
|
|
126
194
|
added
|
|
127
195
|
};
|
|
128
196
|
const filesToWrite = [...updated, ...added].filter((f) => !isExcluded(f, excludePatterns));
|
|
197
|
+
const destToSource = /* @__PURE__ */ new Map();
|
|
198
|
+
for (const filePath of filteredFiles) destToSource.set(toDestPath(filePath), filePath);
|
|
129
199
|
if (filesToWrite.length > 0) {
|
|
130
200
|
backupFiles(projectDir, filesToWrite);
|
|
131
|
-
for (const
|
|
132
|
-
const src = (0, node_path.join)(sourceDir, filePath);
|
|
133
|
-
const dest = (0, node_path.join)(projectDir, filePath);
|
|
134
|
-
(0, node_fs.mkdirSync)((0, node_path.dirname)(dest), { recursive: true });
|
|
135
|
-
(0, node_fs.writeFileSync)(dest, (0, node_fs.readFileSync)(src));
|
|
136
|
-
}
|
|
201
|
+
for (const destPath of filesToWrite) writeSyncedFile(sourceDir, projectDir, destToSource.get(destPath) ?? destPath);
|
|
137
202
|
}
|
|
138
203
|
const newSnapshotFiles = {};
|
|
139
|
-
for (const filePath of
|
|
204
|
+
for (const filePath of filteredFiles) {
|
|
140
205
|
const src = (0, node_path.join)(sourceDir, filePath);
|
|
141
206
|
if (!(0, node_fs.lstatSync)(src).isFile()) continue;
|
|
142
207
|
const content = (0, node_fs.readFileSync)(src);
|
|
143
|
-
newSnapshotFiles[filePath] = (0, node_crypto.createHash)("sha256").update(content).digest("hex").substring(0, 16);
|
|
208
|
+
newSnapshotFiles[toDestPath(filePath)] = (0, node_crypto.createHash)("sha256").update(content).digest("hex").substring(0, 16);
|
|
144
209
|
}
|
|
145
210
|
await require_snapshot.writeSnapshot(projectDir, {
|
|
146
211
|
parentRef: `bos://${extendsAccount}/${extendsGateway}`,
|
|
@@ -151,6 +216,8 @@ async function syncTemplate(projectDir, options) {
|
|
|
151
216
|
extendsGateway,
|
|
152
217
|
account: localConfig.account || extendsAccount,
|
|
153
218
|
domain: localConfig.domain || extendsGateway,
|
|
219
|
+
plugins: childPlugins,
|
|
220
|
+
pluginRoutes,
|
|
154
221
|
workspaceOpts: { sourceDir }
|
|
155
222
|
});
|
|
156
223
|
if (!options.noInstall) await require_cli_init.runBunInstall(projectDir);
|
package/dist/cli/sync.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.cjs","names":["resolveSourceDir","readTemplatekeep","readSnapshot","writeSnapshot","personalizeConfig","runBunInstall"],"sources":["../../src/cli/sync.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n copyFileSync,\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { glob } from \"glob\";\nimport type { SyncOptions, SyncResult } from \"../contract\";\nimport { personalizeConfig, readTemplatekeep, resolveSourceDir, runBunInstall } from \"./init\";\nimport { readSnapshot, writeSnapshot } from \"./snapshot\";\n\nfunction readExcludeFile(filePath: string): string[] {\n if (!existsSync(filePath)) return [];\n const content = readFileSync(filePath, \"utf-8\");\n return content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"));\n}\n\nexport async function readTemplatesyncExclude(sourceDir: string): Promise<string[]> {\n return readExcludeFile(join(sourceDir, \".templatesync-exclude\"));\n}\n\nexport function readLocalSyncExcludes(projectDir: string): string[] {\n return readExcludeFile(join(projectDir, \".bos\", \"sync-local-exclude\"));\n}\n\nfunction isExcluded(filePath: string, excludePatterns: string[]): boolean {\n for (const pattern of excludePatterns) {\n if (pattern.endsWith(\"/**\")) {\n const prefix = pattern.slice(0, -3);\n if (filePath.startsWith(`${prefix}/`) || filePath === prefix) return true;\n } else if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -2);\n const slashIdx = filePath.indexOf(\"/\", prefix.length + 1);\n if (filePath.startsWith(`${prefix}/`) && slashIdx === -1) return true;\n } else if (filePath === pattern || filePath.startsWith(`${pattern}/`)) {\n return true;\n }\n }\n return false;\n}\n\nfunction computeLocalHash(projectDir: string, filePath: string): string | null {\n const fullPath = join(projectDir, filePath);\n if (!existsSync(fullPath)) return null;\n try {\n const content = readFileSync(fullPath);\n return createHash(\"sha256\").update(content).digest(\"hex\").substring(0, 16);\n } catch {\n return null;\n }\n}\n\nfunction backupFiles(projectDir: string, filePaths: string[]): string | null {\n const filesToBackup = filePaths.filter((f) => existsSync(join(projectDir, f)));\n if (filesToBackup.length === 0) return null;\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backupDir = join(projectDir, \".bos\", \"sync-backup\", timestamp);\n\n for (const filePath of filesToBackup) {\n const src = join(projectDir, filePath);\n const dest = join(backupDir, filePath);\n mkdirSync(dirname(dest), { recursive: true });\n copyFileSync(src, dest);\n }\n\n return backupDir;\n}\n\nexport async function syncTemplate(projectDir: string, options: SyncOptions): Promise<SyncResult> {\n const localConfig = JSON.parse(\n readFileSync(join(projectDir, \"bos.config.json\"), \"utf-8\"),\n ) as Record<string, unknown>;\n\n const extendsRef = localConfig.extends as string | undefined;\n if (!extendsRef?.startsWith(\"bos://\")) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No extends field found in bos.config.json — cannot determine parent\",\n };\n }\n\n const extendsMatch = extendsRef.match(/^bos:\\/\\/([^/]+)\\/(.+)$/);\n if (!extendsMatch) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: `Invalid extends reference: ${extendsRef}`,\n };\n }\n\n const extendsAccount = extendsMatch[1];\n const extendsGateway = extendsMatch[2];\n\n const { sourceDir, cleanup } = await resolveSourceDir({ extendsAccount, extendsGateway });\n\n try {\n const patterns = await readTemplatekeep(sourceDir);\n if (patterns.length === 0) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No .templatekeep found in template source\",\n };\n }\n\n const parentExcludes = await readTemplatesyncExclude(sourceDir);\n const localExcludes = readLocalSyncExcludes(projectDir);\n const excludePatterns = [...parentExcludes, ...localExcludes];\n\n const allTemplateFiles = new Set<string>();\n for (const pattern of patterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n allTemplateFiles.add(match);\n }\n }\n\n const snapshot = await readSnapshot(projectDir);\n\n const updated: string[] = [];\n const skipped: string[] = [];\n const added: string[] = [];\n\n for (const filePath of allTemplateFiles) {\n if (isExcluded(filePath, excludePatterns)) continue;\n\n const localHash = computeLocalHash(projectDir, filePath);\n const sourceContent = readFileSync(join(sourceDir, filePath));\n const sourceHash = createHash(\"sha256\").update(sourceContent).digest(\"hex\").substring(0, 16);\n\n if (localHash === null) {\n added.push(filePath);\n continue;\n }\n\n if (localHash === sourceHash) continue;\n\n const snapshotHash = snapshot?.files[filePath];\n\n if (snapshotHash === undefined) {\n updated.push(filePath);\n continue;\n }\n\n if (localHash === snapshotHash) {\n updated.push(filePath);\n } else {\n if (options.force) {\n updated.push(filePath);\n } else {\n skipped.push(filePath);\n }\n }\n }\n\n if (options.dryRun) {\n return {\n status: \"dry-run\",\n updated,\n skipped,\n added,\n };\n }\n\n const filesToWrite = [...updated, ...added].filter((f) => !isExcluded(f, excludePatterns));\n\n if (filesToWrite.length > 0) {\n backupFiles(projectDir, filesToWrite);\n\n for (const filePath of filesToWrite) {\n const src = join(sourceDir, filePath);\n const dest = join(projectDir, filePath);\n mkdirSync(dirname(dest), { recursive: true });\n writeFileSync(dest, readFileSync(src));\n }\n }\n\n const newSnapshotFiles: Record<string, string> = {};\n for (const filePath of allTemplateFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n const content = readFileSync(src);\n newSnapshotFiles[filePath] = createHash(\"sha256\")\n .update(content)\n .digest(\"hex\")\n .substring(0, 16);\n }\n\n await writeSnapshot(projectDir, {\n parentRef: `bos://${extendsAccount}/${extendsGateway}`,\n files: newSnapshotFiles,\n });\n\n const account = (localConfig.account as string) || extendsAccount;\n const domain = (localConfig.domain as string) || extendsGateway;\n\n await personalizeConfig(projectDir, {\n extendsAccount,\n extendsGateway,\n account,\n domain,\n workspaceOpts: { sourceDir },\n });\n\n if (!options.noInstall) {\n await runBunInstall(projectDir);\n }\n\n return {\n status: \"synced\",\n updated,\n skipped,\n added,\n };\n } finally {\n await cleanup();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sync.cjs","names":["resolveSourceDir","readTemplatekeep","readSnapshot","writeSnapshot","personalizeConfig","runBunInstall"],"sources":["../../src/cli/sync.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport {\n copyFileSync,\n existsSync,\n lstatSync,\n mkdirSync,\n readFileSync,\n writeFileSync,\n} from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { glob } from \"glob\";\nimport type { SyncOptions, SyncResult } from \"../contract\";\nimport { personalizeConfig, readTemplatekeep, resolveSourceDir, runBunInstall } from \"./init\";\nimport { readSnapshot, writeSnapshot } from \"./snapshot\";\n\nfunction readExcludeFile(filePath: string): string[] {\n if (!existsSync(filePath)) return [];\n const content = readFileSync(filePath, \"utf-8\");\n return content\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0 && !line.startsWith(\"#\"));\n}\n\nexport async function readTemplatesyncExclude(sourceDir: string): Promise<string[]> {\n return readExcludeFile(join(sourceDir, \".templatesync-exclude\"));\n}\n\nexport function readLocalSyncExcludes(projectDir: string): string[] {\n return readExcludeFile(join(projectDir, \".bos\", \"sync-local-exclude\"));\n}\n\nfunction isExcluded(filePath: string, excludePatterns: string[]): boolean {\n for (const pattern of excludePatterns) {\n if (pattern.endsWith(\"/**\")) {\n const prefix = pattern.slice(0, -3);\n if (filePath.startsWith(`${prefix}/`) || filePath === prefix) return true;\n } else if (pattern.endsWith(\"/*\")) {\n const prefix = pattern.slice(0, -2);\n const slashIdx = filePath.indexOf(\"/\", prefix.length + 1);\n if (filePath.startsWith(`${prefix}/`) && slashIdx === -1) return true;\n } else if (filePath === pattern || filePath.startsWith(`${pattern}/`)) {\n return true;\n }\n }\n return false;\n}\n\nfunction computeLocalHash(projectDir: string, filePath: string): string | null {\n const fullPath = join(projectDir, filePath);\n if (!existsSync(fullPath)) return null;\n try {\n const content = readFileSync(fullPath);\n return createHash(\"sha256\").update(content).digest(\"hex\").substring(0, 16);\n } catch {\n return null;\n }\n}\n\nfunction backupFiles(projectDir: string, filePaths: string[]): string | null {\n const filesToBackup = filePaths.filter((f) => existsSync(join(projectDir, f)));\n if (filesToBackup.length === 0) return null;\n\n const timestamp = new Date().toISOString().replace(/[:.]/g, \"-\");\n const backupDir = join(projectDir, \".bos\", \"sync-backup\", timestamp);\n\n for (const filePath of filesToBackup) {\n const src = join(projectDir, filePath);\n const dest = join(backupDir, filePath);\n mkdirSync(dirname(dest), { recursive: true });\n copyFileSync(src, dest);\n }\n\n return backupDir;\n}\n\nfunction mergePackageJson(\n local: Record<string, unknown>,\n template: Record<string, unknown>,\n): Record<string, unknown> {\n const merged = { ...template };\n\n for (const depField of [\n \"dependencies\",\n \"devDependencies\",\n \"peerDependencies\",\n \"overrides\",\n ] as const) {\n const localDeps = local[depField] as Record<string, string> | undefined;\n const templateDeps = template[depField] as Record<string, string> | undefined;\n\n if (!localDeps && !templateDeps) continue;\n\n const mergedDeps: Record<string, string> = { ...(templateDeps ?? {}) };\n\n if (localDeps) {\n for (const [name, version] of Object.entries(localDeps)) {\n if (!(name in mergedDeps)) {\n mergedDeps[name] = version;\n }\n }\n }\n\n if (Object.keys(mergedDeps).length > 0) {\n merged[depField] = mergedDeps;\n }\n }\n\n if (local.scripts && typeof local.scripts === \"object\") {\n merged.scripts = {\n ...((template.scripts as Record<string, string>) ?? {}),\n ...(local.scripts as Record<string, string>),\n };\n }\n\n return merged;\n}\n\nfunction toDestPath(filePath: string): string {\n return filePath.startsWith(\".templates/\") ? filePath.slice(\".templates/\".length) : filePath;\n}\n\nfunction writeSyncedFile(sourceDir: string, projectDir: string, filePath: string): void {\n const src = join(sourceDir, filePath);\n const destPath = filePath.startsWith(\".templates/\")\n ? filePath.slice(\".templates/\".length)\n : filePath;\n const dest = join(projectDir, destPath);\n mkdirSync(dirname(dest), { recursive: true });\n\n if (filePath.endsWith(\"package.json\")) {\n const localContent = existsSync(dest) ? readFileSync(dest, \"utf-8\") : null;\n const templateContent = readFileSync(src, \"utf-8\");\n\n if (localContent) {\n const local = JSON.parse(localContent) as Record<string, unknown>;\n const template = JSON.parse(templateContent) as Record<string, unknown>;\n const merged = mergePackageJson(local, template);\n writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\\n`);\n return;\n }\n }\n\n writeFileSync(dest, readFileSync(src));\n}\n\nexport async function syncTemplate(projectDir: string, options: SyncOptions): Promise<SyncResult> {\n const localConfig = JSON.parse(\n readFileSync(join(projectDir, \"bos.config.json\"), \"utf-8\"),\n ) as Record<string, unknown>;\n\n const extendsRef = localConfig.extends as string | undefined;\n if (!extendsRef?.startsWith(\"bos://\")) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No extends field found in bos.config.json — cannot determine parent\",\n };\n }\n\n const extendsMatch = extendsRef.match(/^bos:\\/\\/([^/]+)\\/(.+)$/);\n if (!extendsMatch) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: `Invalid extends reference: ${extendsRef}`,\n };\n }\n\n const extendsAccount = extendsMatch[1];\n const extendsGateway = extendsMatch[2];\n\n const { sourceDir, parentConfig, cleanup } = await resolveSourceDir({\n extendsAccount,\n extendsGateway,\n });\n\n try {\n const patterns = await readTemplatekeep(sourceDir);\n if (patterns.length === 0) {\n return {\n status: \"error\",\n updated: [],\n skipped: [],\n added: [],\n error: \"No .templatekeep found in template source\",\n };\n }\n\n const parentExcludes = await readTemplatesyncExclude(sourceDir);\n const localExcludes = readLocalSyncExcludes(projectDir);\n const excludePatterns = [...parentExcludes, ...localExcludes];\n\n const allTemplateFiles = new Set<string>();\n for (const pattern of patterns) {\n const matches = await glob(pattern, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n allTemplateFiles.add(match);\n }\n }\n\n const childPlugins =\n localConfig.plugins && typeof localConfig.plugins === \"object\"\n ? Object.keys(localConfig.plugins as Record<string, unknown>)\n : [];\n\n const pluginRoutes: Record<string, string[]> = {};\n if (parentConfig.plugins) {\n for (const [key, ref] of Object.entries(parentConfig.plugins)) {\n if (ref.routes && ref.routes.length > 0) {\n pluginRoutes[key] = ref.routes;\n }\n }\n }\n\n const excludedRoutePatterns: string[] = [];\n for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {\n if (!childPlugins.includes(pluginKey)) {\n excludedRoutePatterns.push(...routePatterns);\n }\n }\n\n const filteredFiles = new Set<string>();\n for (const filePath of allTemplateFiles) {\n const pluginMatch = filePath.match(/^plugins\\/([^/]+)/);\n if (pluginMatch && !childPlugins.includes(pluginMatch[1])) continue;\n if (isExcluded(filePath, excludedRoutePatterns)) continue;\n filteredFiles.add(filePath);\n }\n\n for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {\n if (!childPlugins.includes(pluginKey)) continue;\n for (const rp of routePatterns) {\n const matches = await glob(rp, {\n cwd: sourceDir,\n nodir: true,\n dot: true,\n absolute: false,\n });\n for (const match of matches) {\n if (!isExcluded(match, excludedRoutePatterns)) {\n filteredFiles.add(match);\n }\n }\n }\n }\n\n const snapshot = await readSnapshot(projectDir);\n\n const updated: string[] = [];\n const skipped: string[] = [];\n const added: string[] = [];\n\n for (const filePath of filteredFiles) {\n const destPath = toDestPath(filePath);\n if (isExcluded(destPath, excludePatterns)) continue;\n\n const localHash = computeLocalHash(projectDir, destPath);\n const sourceContent = readFileSync(join(sourceDir, filePath));\n const sourceHash = createHash(\"sha256\").update(sourceContent).digest(\"hex\").substring(0, 16);\n\n if (localHash === null) {\n added.push(destPath);\n continue;\n }\n\n if (localHash === sourceHash) continue;\n\n const snapshotHash = snapshot?.files[destPath];\n\n if (snapshotHash === undefined) {\n updated.push(destPath);\n continue;\n }\n\n if (localHash === snapshotHash) {\n updated.push(destPath);\n } else {\n if (options.force) {\n updated.push(destPath);\n } else {\n skipped.push(destPath);\n }\n }\n }\n\n if (options.dryRun) {\n return {\n status: \"dry-run\",\n updated,\n skipped,\n added,\n };\n }\n\n const filesToWrite = [...updated, ...added].filter((f) => !isExcluded(f, excludePatterns));\n\n const destToSource = new Map<string, string>();\n for (const filePath of filteredFiles) {\n destToSource.set(toDestPath(filePath), filePath);\n }\n\n if (filesToWrite.length > 0) {\n backupFiles(projectDir, filesToWrite);\n\n for (const destPath of filesToWrite) {\n const sourcePath = destToSource.get(destPath) ?? destPath;\n writeSyncedFile(sourceDir, projectDir, sourcePath);\n }\n }\n\n const newSnapshotFiles: Record<string, string> = {};\n for (const filePath of filteredFiles) {\n const src = join(sourceDir, filePath);\n const stat = lstatSync(src);\n if (!stat.isFile()) continue;\n const content = readFileSync(src);\n newSnapshotFiles[toDestPath(filePath)] = createHash(\"sha256\")\n .update(content)\n .digest(\"hex\")\n .substring(0, 16);\n }\n\n await writeSnapshot(projectDir, {\n parentRef: `bos://${extendsAccount}/${extendsGateway}`,\n files: newSnapshotFiles,\n });\n\n const account = (localConfig.account as string) || extendsAccount;\n const domain = (localConfig.domain as string) || extendsGateway;\n\n await personalizeConfig(projectDir, {\n extendsAccount,\n extendsGateway,\n account,\n domain,\n plugins: childPlugins,\n pluginRoutes,\n workspaceOpts: { sourceDir },\n });\n\n if (!options.noInstall) {\n await runBunInstall(projectDir);\n }\n\n return {\n status: \"synced\",\n updated,\n skipped,\n added,\n };\n } finally {\n await cleanup();\n }\n}\n"],"mappings":"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"}
|
package/dist/cli/sync.mjs
CHANGED
|
@@ -49,6 +49,47 @@ function backupFiles(projectDir, filePaths) {
|
|
|
49
49
|
}
|
|
50
50
|
return backupDir;
|
|
51
51
|
}
|
|
52
|
+
function mergePackageJson(local, template) {
|
|
53
|
+
const merged = { ...template };
|
|
54
|
+
for (const depField of [
|
|
55
|
+
"dependencies",
|
|
56
|
+
"devDependencies",
|
|
57
|
+
"peerDependencies",
|
|
58
|
+
"overrides"
|
|
59
|
+
]) {
|
|
60
|
+
const localDeps = local[depField];
|
|
61
|
+
const templateDeps = template[depField];
|
|
62
|
+
if (!localDeps && !templateDeps) continue;
|
|
63
|
+
const mergedDeps = { ...templateDeps ?? {} };
|
|
64
|
+
if (localDeps) {
|
|
65
|
+
for (const [name, version] of Object.entries(localDeps)) if (!(name in mergedDeps)) mergedDeps[name] = version;
|
|
66
|
+
}
|
|
67
|
+
if (Object.keys(mergedDeps).length > 0) merged[depField] = mergedDeps;
|
|
68
|
+
}
|
|
69
|
+
if (local.scripts && typeof local.scripts === "object") merged.scripts = {
|
|
70
|
+
...template.scripts ?? {},
|
|
71
|
+
...local.scripts
|
|
72
|
+
};
|
|
73
|
+
return merged;
|
|
74
|
+
}
|
|
75
|
+
function toDestPath(filePath) {
|
|
76
|
+
return filePath.startsWith(".templates/") ? filePath.slice(11) : filePath;
|
|
77
|
+
}
|
|
78
|
+
function writeSyncedFile(sourceDir, projectDir, filePath) {
|
|
79
|
+
const src = join(sourceDir, filePath);
|
|
80
|
+
const dest = join(projectDir, filePath.startsWith(".templates/") ? filePath.slice(11) : filePath);
|
|
81
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
82
|
+
if (filePath.endsWith("package.json")) {
|
|
83
|
+
const localContent = existsSync(dest) ? readFileSync(dest, "utf-8") : null;
|
|
84
|
+
const templateContent = readFileSync(src, "utf-8");
|
|
85
|
+
if (localContent) {
|
|
86
|
+
const merged = mergePackageJson(JSON.parse(localContent), JSON.parse(templateContent));
|
|
87
|
+
writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\n`);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
writeFileSync(dest, readFileSync(src));
|
|
92
|
+
}
|
|
52
93
|
async function syncTemplate(projectDir, options) {
|
|
53
94
|
const localConfig = JSON.parse(readFileSync(join(projectDir, "bos.config.json"), "utf-8"));
|
|
54
95
|
const extendsRef = localConfig.extends;
|
|
@@ -69,7 +110,7 @@ async function syncTemplate(projectDir, options) {
|
|
|
69
110
|
};
|
|
70
111
|
const extendsAccount = extendsMatch[1];
|
|
71
112
|
const extendsGateway = extendsMatch[2];
|
|
72
|
-
const { sourceDir, cleanup } = await resolveSourceDir({
|
|
113
|
+
const { sourceDir, parentConfig, cleanup } = await resolveSourceDir({
|
|
73
114
|
extendsAccount,
|
|
74
115
|
extendsGateway
|
|
75
116
|
});
|
|
@@ -95,28 +136,55 @@ async function syncTemplate(projectDir, options) {
|
|
|
95
136
|
});
|
|
96
137
|
for (const match of matches) allTemplateFiles.add(match);
|
|
97
138
|
}
|
|
139
|
+
const childPlugins = localConfig.plugins && typeof localConfig.plugins === "object" ? Object.keys(localConfig.plugins) : [];
|
|
140
|
+
const pluginRoutes = {};
|
|
141
|
+
if (parentConfig.plugins) {
|
|
142
|
+
for (const [key, ref] of Object.entries(parentConfig.plugins)) if (ref.routes && ref.routes.length > 0) pluginRoutes[key] = ref.routes;
|
|
143
|
+
}
|
|
144
|
+
const excludedRoutePatterns = [];
|
|
145
|
+
for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) if (!childPlugins.includes(pluginKey)) excludedRoutePatterns.push(...routePatterns);
|
|
146
|
+
const filteredFiles = /* @__PURE__ */ new Set();
|
|
147
|
+
for (const filePath of allTemplateFiles) {
|
|
148
|
+
const pluginMatch = filePath.match(/^plugins\/([^/]+)/);
|
|
149
|
+
if (pluginMatch && !childPlugins.includes(pluginMatch[1])) continue;
|
|
150
|
+
if (isExcluded(filePath, excludedRoutePatterns)) continue;
|
|
151
|
+
filteredFiles.add(filePath);
|
|
152
|
+
}
|
|
153
|
+
for (const [pluginKey, routePatterns] of Object.entries(pluginRoutes)) {
|
|
154
|
+
if (!childPlugins.includes(pluginKey)) continue;
|
|
155
|
+
for (const rp of routePatterns) {
|
|
156
|
+
const matches = await glob(rp, {
|
|
157
|
+
cwd: sourceDir,
|
|
158
|
+
nodir: true,
|
|
159
|
+
dot: true,
|
|
160
|
+
absolute: false
|
|
161
|
+
});
|
|
162
|
+
for (const match of matches) if (!isExcluded(match, excludedRoutePatterns)) filteredFiles.add(match);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
98
165
|
const snapshot = await readSnapshot(projectDir);
|
|
99
166
|
const updated = [];
|
|
100
167
|
const skipped = [];
|
|
101
168
|
const added = [];
|
|
102
|
-
for (const filePath of
|
|
103
|
-
|
|
104
|
-
|
|
169
|
+
for (const filePath of filteredFiles) {
|
|
170
|
+
const destPath = toDestPath(filePath);
|
|
171
|
+
if (isExcluded(destPath, excludePatterns)) continue;
|
|
172
|
+
const localHash = computeLocalHash(projectDir, destPath);
|
|
105
173
|
const sourceContent = readFileSync(join(sourceDir, filePath));
|
|
106
174
|
const sourceHash = createHash("sha256").update(sourceContent).digest("hex").substring(0, 16);
|
|
107
175
|
if (localHash === null) {
|
|
108
|
-
added.push(
|
|
176
|
+
added.push(destPath);
|
|
109
177
|
continue;
|
|
110
178
|
}
|
|
111
179
|
if (localHash === sourceHash) continue;
|
|
112
|
-
const snapshotHash = snapshot?.files[
|
|
180
|
+
const snapshotHash = snapshot?.files[destPath];
|
|
113
181
|
if (snapshotHash === void 0) {
|
|
114
|
-
updated.push(
|
|
182
|
+
updated.push(destPath);
|
|
115
183
|
continue;
|
|
116
184
|
}
|
|
117
|
-
if (localHash === snapshotHash) updated.push(
|
|
118
|
-
else if (options.force) updated.push(
|
|
119
|
-
else skipped.push(
|
|
185
|
+
if (localHash === snapshotHash) updated.push(destPath);
|
|
186
|
+
else if (options.force) updated.push(destPath);
|
|
187
|
+
else skipped.push(destPath);
|
|
120
188
|
}
|
|
121
189
|
if (options.dryRun) return {
|
|
122
190
|
status: "dry-run",
|
|
@@ -125,21 +193,18 @@ async function syncTemplate(projectDir, options) {
|
|
|
125
193
|
added
|
|
126
194
|
};
|
|
127
195
|
const filesToWrite = [...updated, ...added].filter((f) => !isExcluded(f, excludePatterns));
|
|
196
|
+
const destToSource = /* @__PURE__ */ new Map();
|
|
197
|
+
for (const filePath of filteredFiles) destToSource.set(toDestPath(filePath), filePath);
|
|
128
198
|
if (filesToWrite.length > 0) {
|
|
129
199
|
backupFiles(projectDir, filesToWrite);
|
|
130
|
-
for (const
|
|
131
|
-
const src = join(sourceDir, filePath);
|
|
132
|
-
const dest = join(projectDir, filePath);
|
|
133
|
-
mkdirSync(dirname(dest), { recursive: true });
|
|
134
|
-
writeFileSync(dest, readFileSync(src));
|
|
135
|
-
}
|
|
200
|
+
for (const destPath of filesToWrite) writeSyncedFile(sourceDir, projectDir, destToSource.get(destPath) ?? destPath);
|
|
136
201
|
}
|
|
137
202
|
const newSnapshotFiles = {};
|
|
138
|
-
for (const filePath of
|
|
203
|
+
for (const filePath of filteredFiles) {
|
|
139
204
|
const src = join(sourceDir, filePath);
|
|
140
205
|
if (!lstatSync(src).isFile()) continue;
|
|
141
206
|
const content = readFileSync(src);
|
|
142
|
-
newSnapshotFiles[filePath] = createHash("sha256").update(content).digest("hex").substring(0, 16);
|
|
207
|
+
newSnapshotFiles[toDestPath(filePath)] = createHash("sha256").update(content).digest("hex").substring(0, 16);
|
|
143
208
|
}
|
|
144
209
|
await writeSnapshot(projectDir, {
|
|
145
210
|
parentRef: `bos://${extendsAccount}/${extendsGateway}`,
|
|
@@ -150,6 +215,8 @@ async function syncTemplate(projectDir, options) {
|
|
|
150
215
|
extendsGateway,
|
|
151
216
|
account: localConfig.account || extendsAccount,
|
|
152
217
|
domain: localConfig.domain || extendsGateway,
|
|
218
|
+
plugins: childPlugins,
|
|
219
|
+
pluginRoutes,
|
|
153
220
|
workspaceOpts: { sourceDir }
|
|
154
221
|
});
|
|
155
222
|
if (!options.noInstall) await runBunInstall(projectDir);
|