everything-dev 1.8.2 → 1.8.3
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/_virtual/_rolldown/runtime.mjs +7 -0
- package/dist/api-contract.cjs +5 -5
- package/dist/api-contract.cjs.map +1 -1
- package/dist/api-contract.mjs +5 -5
- package/dist/api-contract.mjs.map +1 -1
- package/dist/cli/prompts.cjs +38 -63
- package/dist/cli/prompts.cjs.map +1 -1
- package/dist/cli/prompts.mjs +36 -63
- package/dist/cli/prompts.mjs.map +1 -1
- package/dist/plugin.cjs +38 -14
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +8 -0
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +8 -0
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +38 -14
- package/dist/plugin.mjs.map +1 -1
- package/dist/service-descriptor.d.cts +8 -0
- package/dist/service-descriptor.d.cts.map +1 -1
- package/dist/service-descriptor.d.mts +8 -0
- package/dist/service-descriptor.d.mts.map +1 -1
- package/dist/types.cjs +4 -1
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +8 -0
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +8 -0
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +4 -1
- package/dist/types.mjs.map +1 -1
- package/package.json +3 -2
- package/src/api-contract.ts +10 -12
- package/src/cli/prompts.ts +54 -88
- package/src/plugin.ts +68 -18
- package/src/types.ts +1 -0
package/src/api-contract.ts
CHANGED
|
@@ -145,12 +145,9 @@ async function resolveContractSource(opts: {
|
|
|
145
145
|
generatedSubdir: string;
|
|
146
146
|
localSourceFactory?: (configDir: string) => ContractSource;
|
|
147
147
|
}): Promise<ContractSource> {
|
|
148
|
-
if (
|
|
149
|
-
opts.key === "api" &&
|
|
150
|
-
(!opts.source || !("localPath" in opts.source) || opts.source.localPath)
|
|
151
|
-
) {
|
|
148
|
+
if (opts.key === "api") {
|
|
152
149
|
const localPath = opts.source && "localPath" in opts.source ? opts.source.localPath : undefined;
|
|
153
|
-
if (localPath) {
|
|
150
|
+
if (localPath != null && localPath !== "") {
|
|
154
151
|
return {
|
|
155
152
|
key: opts.key,
|
|
156
153
|
importName: "BaseApiContract",
|
|
@@ -163,13 +160,9 @@ async function resolveContractSource(opts: {
|
|
|
163
160
|
}
|
|
164
161
|
}
|
|
165
162
|
|
|
166
|
-
if (
|
|
167
|
-
opts.key === "auth" &&
|
|
168
|
-
opts.localSourceFactory &&
|
|
169
|
-
(!opts.source || !("localPath" in opts.source) || opts.source.localPath)
|
|
170
|
-
) {
|
|
163
|
+
if (opts.key === "auth" && opts.localSourceFactory) {
|
|
171
164
|
const localPath = opts.source && "localPath" in opts.source ? opts.source.localPath : undefined;
|
|
172
|
-
if (localPath) {
|
|
165
|
+
if (localPath != null && localPath !== "") {
|
|
173
166
|
return {
|
|
174
167
|
key: opts.key,
|
|
175
168
|
importName: "authContract",
|
|
@@ -182,7 +175,12 @@ async function resolveContractSource(opts: {
|
|
|
182
175
|
}
|
|
183
176
|
}
|
|
184
177
|
|
|
185
|
-
if (
|
|
178
|
+
if (
|
|
179
|
+
opts.source &&
|
|
180
|
+
"localPath" in opts.source &&
|
|
181
|
+
opts.source.localPath != null &&
|
|
182
|
+
opts.source.localPath !== ""
|
|
183
|
+
) {
|
|
186
184
|
return {
|
|
187
185
|
key: opts.key,
|
|
188
186
|
importName: `${sanitizeIdentifier(opts.key)}Contract`,
|
package/src/cli/prompts.ts
CHANGED
|
@@ -1,29 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
export async function prompt(question: string, defaultValue?: string): Promise<string> {
|
|
4
|
-
const rl = createInterface({
|
|
5
|
-
input: process.stdin,
|
|
6
|
-
output: process.stdout,
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
const suffix = defaultValue ? ` [${defaultValue}]` : "";
|
|
10
|
-
const fullQuestion = `${question}${suffix}: `;
|
|
11
|
-
|
|
12
|
-
return new Promise<string>((resolve) => {
|
|
13
|
-
rl.question(fullQuestion, (answer) => {
|
|
14
|
-
rl.close();
|
|
15
|
-
const trimmed = answer.trim();
|
|
16
|
-
resolve(trimmed || defaultValue || "");
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export async function promptYesNo(question: string, defaultVal = false): Promise<boolean> {
|
|
22
|
-
const hint = defaultVal ? "Y/n" : "y/N";
|
|
23
|
-
const answer = await prompt(`${question} (${hint})`);
|
|
24
|
-
if (!answer) return defaultVal;
|
|
25
|
-
return answer.toLowerCase() === "y" || answer.toLowerCase() === "yes";
|
|
26
|
-
}
|
|
1
|
+
import process from "node:process";
|
|
2
|
+
import * as p from "@clack/prompts";
|
|
27
3
|
|
|
28
4
|
function parseExtendsRef(ref: string): { account: string; gateway: string } | null {
|
|
29
5
|
const match = ref.match(/^(?:bos:\/\/)?([^/]+)\/(.+)$/);
|
|
@@ -41,64 +17,10 @@ function deriveAccountFromDomain(domain: string, extendsAccount: string): string
|
|
|
41
17
|
}
|
|
42
18
|
|
|
43
19
|
const AVAILABLE_PLUGINS = [
|
|
44
|
-
{
|
|
45
|
-
|
|
46
|
-
label: "template",
|
|
47
|
-
description: "Plugin scaffold and boilerplate",
|
|
48
|
-
default: true,
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
key: "registry",
|
|
52
|
-
label: "registry",
|
|
53
|
-
description: "FastKV app discovery and metadata",
|
|
54
|
-
default: false,
|
|
55
|
-
},
|
|
20
|
+
{ value: "_template", label: "template" },
|
|
21
|
+
{ value: "registry", label: "registry" },
|
|
56
22
|
];
|
|
57
23
|
|
|
58
|
-
async function promptPluginSelect(): Promise<string[]> {
|
|
59
|
-
const selected = new Set<string>(AVAILABLE_PLUGINS.filter((p) => p.default).map((p) => p.key));
|
|
60
|
-
|
|
61
|
-
console.log();
|
|
62
|
-
console.log(" Select plugins (enter number to toggle, enter to confirm):");
|
|
63
|
-
for (let i = 0; i < AVAILABLE_PLUGINS.length; i++) {
|
|
64
|
-
const p = AVAILABLE_PLUGINS[i];
|
|
65
|
-
const marker = selected.has(p.key) ? "●" : "○";
|
|
66
|
-
console.log(` ${marker} ${i + 1}. ${p.label} — ${p.description}`);
|
|
67
|
-
}
|
|
68
|
-
console.log();
|
|
69
|
-
|
|
70
|
-
while (true) {
|
|
71
|
-
const answer = await prompt(
|
|
72
|
-
" Plugins",
|
|
73
|
-
selected.size > 0 ? Array.from(selected).join(",") : "",
|
|
74
|
-
);
|
|
75
|
-
if (!answer) break;
|
|
76
|
-
|
|
77
|
-
const num = Number.parseInt(answer, 10);
|
|
78
|
-
if (num >= 1 && num <= AVAILABLE_PLUGINS.length) {
|
|
79
|
-
const plugin = AVAILABLE_PLUGINS[num - 1];
|
|
80
|
-
if (selected.has(plugin.key)) {
|
|
81
|
-
selected.delete(plugin.key);
|
|
82
|
-
} else {
|
|
83
|
-
selected.add(plugin.key);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
console.log(" Current selection:");
|
|
87
|
-
for (let i = 0; i < AVAILABLE_PLUGINS.length; i++) {
|
|
88
|
-
const p = AVAILABLE_PLUGINS[i];
|
|
89
|
-
const marker = selected.has(p.key) ? "●" : "○";
|
|
90
|
-
console.log(` ${marker} ${i + 1}. ${p.label}`);
|
|
91
|
-
}
|
|
92
|
-
console.log();
|
|
93
|
-
continue;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
break;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return Array.from(selected);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
24
|
export async function promptInitOptions(input: {
|
|
103
25
|
extendsAccount?: string;
|
|
104
26
|
extendsGateway?: string;
|
|
@@ -117,9 +39,27 @@ export async function promptInitOptions(input: {
|
|
|
117
39
|
plugins: string[];
|
|
118
40
|
withHost: boolean;
|
|
119
41
|
}> {
|
|
120
|
-
|
|
42
|
+
p.intro("Let's build an app...");
|
|
43
|
+
|
|
44
|
+
const domain =
|
|
45
|
+
input.domain ??
|
|
46
|
+
((await p.text({
|
|
47
|
+
message: "Starting with a domain?",
|
|
48
|
+
placeholder: "no",
|
|
49
|
+
})) as string);
|
|
50
|
+
|
|
51
|
+
if (p.isCancel(domain)) process.exit(0);
|
|
52
|
+
|
|
53
|
+
const extendsPlaceholder = "bos://dev.everything.near/everything.dev";
|
|
54
|
+
const extendsInput =
|
|
55
|
+
input.extends ??
|
|
56
|
+
((await p.text({
|
|
57
|
+
message: "Extending an existing app?",
|
|
58
|
+
placeholder: extendsPlaceholder,
|
|
59
|
+
})) as string);
|
|
60
|
+
|
|
61
|
+
if (p.isCancel(extendsInput)) process.exit(0);
|
|
121
62
|
|
|
122
|
-
const extendsInput = input.extends || (await prompt("Extend from", ""));
|
|
123
63
|
let extendsAccount = input.extendsAccount || "";
|
|
124
64
|
let extendsGateway = input.extendsGateway || "";
|
|
125
65
|
|
|
@@ -135,14 +75,40 @@ export async function promptInitOptions(input: {
|
|
|
135
75
|
extendsGateway = extendsGateway || "everything.dev";
|
|
136
76
|
|
|
137
77
|
const accountDefault = domain ? deriveAccountFromDomain(domain, extendsAccount) : "";
|
|
138
|
-
const account =
|
|
78
|
+
const account =
|
|
79
|
+
input.account ??
|
|
80
|
+
((await p.text({
|
|
81
|
+
message: "What NEAR account will you publish from?",
|
|
82
|
+
placeholder: accountDefault || "skip",
|
|
83
|
+
defaultValue: accountDefault,
|
|
84
|
+
})) as string);
|
|
85
|
+
|
|
86
|
+
if (p.isCancel(account)) process.exit(0);
|
|
139
87
|
|
|
140
88
|
const directory = input.directory || domain || extendsGateway;
|
|
141
89
|
|
|
142
|
-
const plugins =
|
|
90
|
+
const plugins =
|
|
91
|
+
input.plugins ??
|
|
92
|
+
((await p.multiselect({
|
|
93
|
+
message: "Select plugins:",
|
|
94
|
+
options: AVAILABLE_PLUGINS,
|
|
95
|
+
initialValues: ["_template"],
|
|
96
|
+
required: false,
|
|
97
|
+
})) as string[]);
|
|
98
|
+
|
|
99
|
+
if (p.isCancel(plugins)) process.exit(0);
|
|
100
|
+
|
|
101
|
+
const go =
|
|
102
|
+
input.withHost !== undefined
|
|
103
|
+
? true
|
|
104
|
+
: await p.confirm({
|
|
105
|
+
message: "GO!",
|
|
106
|
+
initialValue: true,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (p.isCancel(go) || !go) process.exit(0);
|
|
143
110
|
|
|
144
|
-
const withHost =
|
|
145
|
-
input.withHost !== undefined ? input.withHost : await promptYesNo("Include host?", false);
|
|
111
|
+
const withHost = input.withHost ?? false;
|
|
146
112
|
|
|
147
113
|
return {
|
|
148
114
|
extendsAccount,
|
package/src/plugin.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { basename, dirname, join, resolve } from "node:path";
|
|
4
|
+
import * as p from "@clack/prompts";
|
|
4
5
|
import { Effect } from "effect";
|
|
5
6
|
import { syncApiContractBridge } from "./api-contract";
|
|
6
7
|
import { buildRuntimeConfig, detectLocalPackages, prepareDevelopmentRuntimeConfig } from "./app";
|
|
@@ -921,6 +922,66 @@ export default createPlugin({
|
|
|
921
922
|
plugins: runtimePlugins,
|
|
922
923
|
});
|
|
923
924
|
|
|
925
|
+
// ── Production Readiness Validation ──
|
|
926
|
+
const productionEnv: Record<string, string> = {};
|
|
927
|
+
|
|
928
|
+
// Default CORS_ORIGIN to the configured domain if not set
|
|
929
|
+
if (!process.env.CORS_ORIGIN && config.domain) {
|
|
930
|
+
const defaultOrigin = `https://${config.domain}`;
|
|
931
|
+
productionEnv.CORS_ORIGIN = defaultOrigin;
|
|
932
|
+
console.log(`[Start] CORS_ORIGIN not set, defaulting to ${defaultOrigin}`);
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
// Validate shared.plugins packages are resolvable
|
|
936
|
+
const missingSharedDeps: string[] = [];
|
|
937
|
+
if (config.shared?.plugins) {
|
|
938
|
+
for (const depName of Object.keys(config.shared.plugins)) {
|
|
939
|
+
try {
|
|
940
|
+
require.resolve(depName);
|
|
941
|
+
} catch {
|
|
942
|
+
missingSharedDeps.push(depName);
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
if (missingSharedDeps.length > 0) {
|
|
947
|
+
console.error(
|
|
948
|
+
`[Start] Missing ${missingSharedDeps.length} shared plugin dependency(s): ${missingSharedDeps.join(", ")}`,
|
|
949
|
+
);
|
|
950
|
+
console.error(`[Start] Ensure these packages are installed in node_modules.`);
|
|
951
|
+
}
|
|
952
|
+
|
|
953
|
+
// Validate required secrets
|
|
954
|
+
const requiredSecrets = new Set<string>();
|
|
955
|
+
const missingSecrets: string[] = [];
|
|
956
|
+
|
|
957
|
+
if (runtimeConfig.auth?.secrets) {
|
|
958
|
+
for (const s of runtimeConfig.auth.secrets) requiredSecrets.add(s);
|
|
959
|
+
}
|
|
960
|
+
if (runtimeConfig.api?.secrets) {
|
|
961
|
+
for (const s of runtimeConfig.api.secrets) requiredSecrets.add(s);
|
|
962
|
+
}
|
|
963
|
+
for (const plugin of Object.values(runtimeConfig.plugins ?? {})) {
|
|
964
|
+
if (plugin.secrets) {
|
|
965
|
+
for (const s of plugin.secrets) requiredSecrets.add(s);
|
|
966
|
+
}
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
for (const secret of requiredSecrets) {
|
|
970
|
+
const value = process.env[secret];
|
|
971
|
+
if (!value || value.length === 0) {
|
|
972
|
+
missingSecrets.push(secret);
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
if (missingSecrets.length > 0) {
|
|
977
|
+
console.warn(
|
|
978
|
+
`[Start] Missing ${missingSecrets.length} required secret(s): ${missingSecrets.join(", ")}`,
|
|
979
|
+
);
|
|
980
|
+
console.warn(
|
|
981
|
+
`[Start] Auth endpoints and database connections may fail without these secrets.`,
|
|
982
|
+
);
|
|
983
|
+
}
|
|
984
|
+
|
|
924
985
|
const services = buildServiceDescriptorMap(runtimeConfig);
|
|
925
986
|
|
|
926
987
|
await syncApiContractBridge({
|
|
@@ -937,6 +998,7 @@ export default createPlugin({
|
|
|
937
998
|
packages: ["host"],
|
|
938
999
|
env: {
|
|
939
1000
|
NODE_ENV: "production",
|
|
1001
|
+
...productionEnv,
|
|
940
1002
|
...stagingEnvVars,
|
|
941
1003
|
},
|
|
942
1004
|
description: `${isStaging ? "Staging" : "Production"} Mode (${config.account})`,
|
|
@@ -1193,7 +1255,7 @@ export default createPlugin({
|
|
|
1193
1255
|
}
|
|
1194
1256
|
}
|
|
1195
1257
|
|
|
1196
|
-
if (!input.noInteractive
|
|
1258
|
+
if (!input.noInteractive) {
|
|
1197
1259
|
const prompted = await promptInitOptions({
|
|
1198
1260
|
extendsAccount,
|
|
1199
1261
|
extendsGateway,
|
|
@@ -1213,23 +1275,6 @@ export default createPlugin({
|
|
|
1213
1275
|
plugins = prompted.plugins;
|
|
1214
1276
|
}
|
|
1215
1277
|
|
|
1216
|
-
if (!domain) {
|
|
1217
|
-
return {
|
|
1218
|
-
status: "error" as const,
|
|
1219
|
-
directory: "",
|
|
1220
|
-
extendsAccount: extendsAccount ?? "",
|
|
1221
|
-
extendsGateway: extendsGateway ?? "",
|
|
1222
|
-
account: input.account,
|
|
1223
|
-
domain: input.domain,
|
|
1224
|
-
extends:
|
|
1225
|
-
extendsAccount && extendsGateway ? `bos://${extendsAccount}/${extendsGateway}` : "",
|
|
1226
|
-
plugins: plugins ?? [],
|
|
1227
|
-
filesCopied: 0,
|
|
1228
|
-
error:
|
|
1229
|
-
"domain is required (use --no-interactive to skip prompts and provide it as a flag)",
|
|
1230
|
-
};
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
1278
|
extendsAccount = extendsAccount || "dev.everything.near";
|
|
1234
1279
|
extendsGateway = extendsGateway || "everything.dev";
|
|
1235
1280
|
directory = directory || domain || extendsGateway;
|
|
@@ -1284,6 +1329,9 @@ export default createPlugin({
|
|
|
1284
1329
|
}
|
|
1285
1330
|
}
|
|
1286
1331
|
|
|
1332
|
+
const s = p.spinner();
|
|
1333
|
+
s.start("Setting up project");
|
|
1334
|
+
|
|
1287
1335
|
const filesCopied = await copyFilteredFiles(sourceDir, directory, patterns, {
|
|
1288
1336
|
withHost,
|
|
1289
1337
|
plugins,
|
|
@@ -1312,6 +1360,8 @@ export default createPlugin({
|
|
|
1312
1360
|
|
|
1313
1361
|
ensureEnvFile(directory);
|
|
1314
1362
|
|
|
1363
|
+
s.stop("Project initialized");
|
|
1364
|
+
|
|
1315
1365
|
return {
|
|
1316
1366
|
status: "initialized" as const,
|
|
1317
1367
|
directory,
|
package/src/types.ts
CHANGED
|
@@ -153,6 +153,7 @@ export const RuntimeConfigSchema = z.object({
|
|
|
153
153
|
shared: z
|
|
154
154
|
.object({
|
|
155
155
|
ui: z.record(z.string(), SharedConfigSchema).optional(),
|
|
156
|
+
plugins: z.record(z.string(), SharedConfigSchema).optional(),
|
|
156
157
|
})
|
|
157
158
|
.optional(),
|
|
158
159
|
ui: FederationEntrySchema.extend({
|