everything-dev 1.12.3 → 1.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.js +1 -1
- package/dist/app.cjs +24 -101
- package/dist/app.cjs.map +1 -1
- package/dist/app.mjs +25 -102
- package/dist/app.mjs.map +1 -1
- package/dist/cli/init.cjs +143 -66
- package/dist/cli/init.cjs.map +1 -1
- package/dist/cli/init.d.cts +1 -1
- package/dist/cli/init.d.cts.map +1 -1
- package/dist/cli/init.d.mts +1 -1
- package/dist/cli/init.d.mts.map +1 -1
- package/dist/cli/init.mjs +144 -67
- package/dist/cli/init.mjs.map +1 -1
- package/dist/cli/prompts.cjs +3 -3
- package/dist/cli/prompts.cjs.map +1 -1
- package/dist/cli/prompts.mjs +3 -3
- package/dist/cli/prompts.mjs.map +1 -1
- package/dist/cli/sync.cjs +15 -56
- package/dist/cli/sync.cjs.map +1 -1
- package/dist/cli/sync.mjs +15 -56
- package/dist/cli/sync.mjs.map +1 -1
- package/dist/cli/upgrade.cjs +3 -1
- package/dist/cli/upgrade.cjs.map +1 -1
- package/dist/cli/upgrade.mjs +3 -1
- package/dist/cli/upgrade.mjs.map +1 -1
- package/dist/config.cjs +223 -81
- package/dist/config.cjs.map +1 -1
- package/dist/config.d.cts +21 -5
- package/dist/config.d.cts.map +1 -1
- package/dist/config.d.mts +21 -5
- package/dist/config.d.mts.map +1 -1
- package/dist/config.mjs +217 -83
- package/dist/config.mjs.map +1 -1
- package/dist/contract.d.cts +104 -8
- package/dist/contract.d.cts.map +1 -1
- package/dist/contract.d.mts +104 -8
- package/dist/contract.d.mts.map +1 -1
- package/dist/host.cjs +34 -1
- package/dist/host.cjs.map +1 -1
- package/dist/host.d.cts.map +1 -1
- package/dist/host.d.mts.map +1 -1
- package/dist/host.mjs +34 -1
- package/dist/host.mjs.map +1 -1
- package/dist/index.cjs +17 -0
- package/dist/index.d.cts +5 -3
- package/dist/index.d.mts +5 -3
- package/dist/index.mjs +5 -3
- package/dist/merge.cjs +113 -0
- package/dist/merge.cjs.map +1 -0
- package/dist/merge.d.cts +7 -0
- package/dist/merge.d.cts.map +1 -0
- package/dist/merge.d.mts +7 -0
- package/dist/merge.d.mts.map +1 -0
- package/dist/merge.mjs +107 -0
- package/dist/merge.mjs.map +1 -0
- package/dist/plugin.cjs +117 -105
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +114 -8
- package/dist/plugin.d.cts.map +1 -1
- package/dist/plugin.d.mts +114 -8
- package/dist/plugin.d.mts.map +1 -1
- package/dist/plugin.mjs +117 -105
- package/dist/plugin.mjs.map +1 -1
- package/dist/service-descriptor.cjs +21 -0
- package/dist/service-descriptor.cjs.map +1 -1
- package/dist/service-descriptor.d.cts +23 -1
- package/dist/service-descriptor.d.cts.map +1 -1
- package/dist/service-descriptor.d.mts +23 -1
- package/dist/service-descriptor.d.mts.map +1 -1
- package/dist/service-descriptor.mjs +21 -0
- package/dist/service-descriptor.mjs.map +1 -1
- package/dist/shared.cjs +24 -2
- package/dist/shared.cjs.map +1 -1
- package/dist/shared.d.cts +3 -0
- package/dist/shared.d.cts.map +1 -1
- package/dist/shared.d.mts +3 -0
- package/dist/shared.d.mts.map +1 -1
- package/dist/shared.mjs +25 -3
- package/dist/shared.mjs.map +1 -1
- package/dist/sidebar.cjs +124 -0
- package/dist/sidebar.cjs.map +1 -0
- package/dist/sidebar.d.cts +8 -0
- package/dist/sidebar.d.cts.map +1 -0
- package/dist/sidebar.d.mts +8 -0
- package/dist/sidebar.d.mts.map +1 -0
- package/dist/sidebar.mjs +122 -0
- package/dist/sidebar.mjs.map +1 -0
- package/dist/types.cjs +104 -10
- package/dist/types.cjs.map +1 -1
- package/dist/types.d.cts +256 -29
- package/dist/types.d.cts.map +1 -1
- package/dist/types.d.mts +256 -29
- package/dist/types.d.mts.map +1 -1
- package/dist/types.mjs +100 -11
- package/dist/types.mjs.map +1 -1
- package/dist/utils/path-match.cjs +18 -0
- package/dist/utils/path-match.cjs.map +1 -0
- package/dist/utils/path-match.mjs +17 -0
- package/dist/utils/path-match.mjs.map +1 -0
- package/dist/utils/save-config.cjs +19 -0
- package/dist/utils/save-config.cjs.map +1 -0
- package/dist/utils/save-config.mjs +18 -0
- package/dist/utils/save-config.mjs.map +1 -0
- package/package.json +3 -2
- package/skills/dev-workflow/SKILL.md +8 -0
- package/skills/extends-config/SKILL.md +132 -0
- package/skills/init-upgrade/SKILL.md +128 -0
- package/skills/publish-sync/SKILL.md +30 -0
- package/src/app.ts +23 -118
- package/src/cli/init.ts +199 -100
- package/src/cli/prompts.ts +2 -2
- package/src/cli/sync.ts +27 -96
- package/src/cli/upgrade.ts +2 -0
- package/src/config.ts +356 -132
- package/src/host.ts +45 -0
- package/src/index.ts +1 -0
- package/src/merge.ts +198 -0
- package/src/plugin.ts +340 -318
- package/src/service-descriptor.ts +23 -0
- package/src/shared.ts +48 -5
- package/src/sidebar.ts +162 -0
- package/src/types.ts +134 -28
- package/src/utils/path-match.ts +16 -0
- package/src/utils/save-config.ts +20 -0
package/src/cli/init.ts
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
existsSync,
|
|
6
6
|
lstatSync,
|
|
7
7
|
mkdirSync,
|
|
8
|
+
mkdtempSync,
|
|
8
9
|
readFileSync,
|
|
9
10
|
rmSync,
|
|
10
11
|
writeFileSync,
|
|
@@ -20,40 +21,12 @@ import {
|
|
|
20
21
|
normalizePackageManifestsInTree,
|
|
21
22
|
} from "../internal/manifest-normalizer";
|
|
22
23
|
import type { BosConfig } from "../types";
|
|
24
|
+
import { isPathExcluded } from "../utils/path-match";
|
|
25
|
+
import { saveBosConfig } from "../utils/save-config";
|
|
23
26
|
import { writeSnapshot } from "./snapshot";
|
|
24
27
|
|
|
25
28
|
const require = createRequire(import.meta.url);
|
|
26
29
|
|
|
27
|
-
const BOS_CONFIG_ORDER = [
|
|
28
|
-
"extends",
|
|
29
|
-
"account",
|
|
30
|
-
"domain",
|
|
31
|
-
"testnet",
|
|
32
|
-
"staging",
|
|
33
|
-
"repository",
|
|
34
|
-
"app",
|
|
35
|
-
"plugins",
|
|
36
|
-
"shared",
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
function rebuildOrderedConfig(config: Record<string, unknown>): Record<string, unknown> {
|
|
40
|
-
const ordered: Record<string, unknown> = {};
|
|
41
|
-
|
|
42
|
-
for (const key of BOS_CONFIG_ORDER) {
|
|
43
|
-
if (key in config) {
|
|
44
|
-
ordered[key] = config[key];
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
for (const key of Object.keys(config)) {
|
|
49
|
-
if (!BOS_CONFIG_ORDER.includes(key)) {
|
|
50
|
-
ordered[key] = config[key];
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return ordered;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
30
|
interface SourceResult {
|
|
58
31
|
sourceDir: string;
|
|
59
32
|
parentConfig: BosConfig;
|
|
@@ -86,6 +59,19 @@ export async function resolveSourceDir(opts: {
|
|
|
86
59
|
return { sourceDir, parentConfig, cleanup };
|
|
87
60
|
}
|
|
88
61
|
|
|
62
|
+
export async function readTemplatekeep(sourceDir: string): Promise<string[]> {
|
|
63
|
+
const keepFile = join(sourceDir, ".templatekeep");
|
|
64
|
+
if (!existsSync(keepFile)) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const content = readFileSync(keepFile, "utf-8");
|
|
69
|
+
return content
|
|
70
|
+
.split("\n")
|
|
71
|
+
.map((line) => line.trim())
|
|
72
|
+
.filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
73
|
+
}
|
|
74
|
+
|
|
89
75
|
export async function fetchParentConfig(
|
|
90
76
|
extendsAccount: string,
|
|
91
77
|
extendsGateway: string,
|
|
@@ -161,19 +147,6 @@ function parseGitHubUrl(url: string): { owner: string; repo: string; branch: str
|
|
|
161
147
|
return null;
|
|
162
148
|
}
|
|
163
149
|
|
|
164
|
-
export async function readTemplatekeep(sourceDir: string): Promise<string[]> {
|
|
165
|
-
const keepFile = join(sourceDir, ".templatekeep");
|
|
166
|
-
if (!existsSync(keepFile)) {
|
|
167
|
-
return [];
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const content = readFileSync(keepFile, "utf-8");
|
|
171
|
-
return content
|
|
172
|
-
.split("\n")
|
|
173
|
-
.map((line) => line.trim())
|
|
174
|
-
.filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
175
|
-
}
|
|
176
|
-
|
|
177
150
|
export async function copyFilteredFiles(
|
|
178
151
|
sourceDir: string,
|
|
179
152
|
destination: string,
|
|
@@ -218,7 +191,7 @@ export async function copyFilteredFiles(
|
|
|
218
191
|
const pluginName = pluginMatch[1];
|
|
219
192
|
if (!(options.plugins?.includes(pluginName) ?? true)) continue;
|
|
220
193
|
}
|
|
221
|
-
if (
|
|
194
|
+
if (isPathExcluded(match, excludedRoutePatterns)) continue;
|
|
222
195
|
allFiles.add(match);
|
|
223
196
|
}
|
|
224
197
|
}
|
|
@@ -235,7 +208,7 @@ export async function copyFilteredFiles(
|
|
|
235
208
|
absolute: false,
|
|
236
209
|
});
|
|
237
210
|
for (const match of matches) {
|
|
238
|
-
if (!
|
|
211
|
+
if (!isPathExcluded(match, excludedRoutePatterns)) {
|
|
239
212
|
routeFiles.add(match);
|
|
240
213
|
}
|
|
241
214
|
}
|
|
@@ -266,19 +239,6 @@ export async function copyFilteredFiles(
|
|
|
266
239
|
return count;
|
|
267
240
|
}
|
|
268
241
|
|
|
269
|
-
function isRouteExcluded(filePath: string, excludedPatterns: string[]): boolean {
|
|
270
|
-
if (excludedPatterns.length === 0) return false;
|
|
271
|
-
for (const pattern of excludedPatterns) {
|
|
272
|
-
if (pattern.endsWith("/**")) {
|
|
273
|
-
const prefix = pattern.slice(0, -3);
|
|
274
|
-
if (filePath.startsWith(`${prefix}/`) || filePath === prefix) return true;
|
|
275
|
-
} else if (filePath === pattern || filePath.startsWith(`${pattern}/`)) {
|
|
276
|
-
return true;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
242
|
export async function personalizeConfig(
|
|
283
243
|
destination: string,
|
|
284
244
|
opts: {
|
|
@@ -334,13 +294,124 @@ export async function personalizeConfig(
|
|
|
334
294
|
}
|
|
335
295
|
|
|
336
296
|
if (isInit) {
|
|
297
|
+
const parentDomain = opts.extendsGateway;
|
|
298
|
+
|
|
337
299
|
for (const pluginKey of Object.keys(plugins)) {
|
|
338
300
|
const plugin = plugins[pluginKey];
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
301
|
+
let pluginObj: Record<string, unknown>;
|
|
302
|
+
|
|
303
|
+
if (typeof plugin === "string") {
|
|
304
|
+
pluginObj = { extends: plugin };
|
|
305
|
+
plugins[pluginKey] = pluginObj;
|
|
306
|
+
} else if (plugin && typeof plugin === "object") {
|
|
307
|
+
pluginObj = { ...(plugin as Record<string, unknown>) };
|
|
308
|
+
} else {
|
|
309
|
+
continue;
|
|
343
310
|
}
|
|
311
|
+
|
|
312
|
+
if (
|
|
313
|
+
pluginObj.development &&
|
|
314
|
+
typeof pluginObj.development === "string" &&
|
|
315
|
+
pluginObj.development.startsWith("local:")
|
|
316
|
+
) {
|
|
317
|
+
const pluginDir = join(destination, pluginObj.development.slice("local:".length));
|
|
318
|
+
const pluginConfigPath = join(pluginDir, "bos.config.json");
|
|
319
|
+
|
|
320
|
+
if (existsSync(pluginConfigPath)) {
|
|
321
|
+
try {
|
|
322
|
+
const pluginConfig = JSON.parse(readFileSync(pluginConfigPath, "utf-8")) as Record<
|
|
323
|
+
string,
|
|
324
|
+
unknown
|
|
325
|
+
>;
|
|
326
|
+
delete pluginConfig.extends;
|
|
327
|
+
if (pluginConfig.app && typeof pluginConfig.app === "object") {
|
|
328
|
+
const app = pluginConfig.app as Record<string, unknown>;
|
|
329
|
+
for (const entryKey of Object.keys(app)) {
|
|
330
|
+
const entry = app[entryKey];
|
|
331
|
+
if (entry && typeof entry === "object") {
|
|
332
|
+
const e = entry as Record<string, unknown>;
|
|
333
|
+
delete e.production;
|
|
334
|
+
delete e.integrity;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
writeFileSync(pluginConfigPath, `${JSON.stringify(pluginConfig, null, 2)}\n`);
|
|
339
|
+
} catch {}
|
|
340
|
+
} else if (existsSync(pluginDir)) {
|
|
341
|
+
const pluginConfig: Record<string, unknown> = {};
|
|
342
|
+
pluginConfig.domain = `${pluginKey}.${opts.domain ?? parentDomain}`;
|
|
343
|
+
pluginConfig.app = { api: { development: "local:." } };
|
|
344
|
+
|
|
345
|
+
if (opts.pluginRoutes?.[pluginKey]) {
|
|
346
|
+
pluginConfig.routes = opts.pluginRoutes[pluginKey];
|
|
347
|
+
}
|
|
348
|
+
if (pluginObj.sidebar) {
|
|
349
|
+
pluginConfig.sidebar = pluginObj.sidebar;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
mkdirSync(pluginDir, { recursive: true });
|
|
353
|
+
writeFileSync(pluginConfigPath, `${JSON.stringify(pluginConfig, null, 2)}\n`);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const cleanEntry: Record<string, unknown> = { development: pluginObj.development };
|
|
357
|
+
if (pluginObj.extends) {
|
|
358
|
+
cleanEntry.extends = pluginObj.extends;
|
|
359
|
+
}
|
|
360
|
+
if (pluginObj.secrets) {
|
|
361
|
+
cleanEntry.secrets = pluginObj.secrets;
|
|
362
|
+
}
|
|
363
|
+
if (pluginObj.variables) {
|
|
364
|
+
cleanEntry.variables = pluginObj.variables;
|
|
365
|
+
}
|
|
366
|
+
plugins[pluginKey] = cleanEntry;
|
|
367
|
+
} else {
|
|
368
|
+
delete pluginObj.production;
|
|
369
|
+
delete pluginObj.integrity;
|
|
370
|
+
delete pluginObj.sidebar;
|
|
371
|
+
delete pluginObj.routes;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
} else {
|
|
375
|
+
for (const pluginKey of Object.keys(plugins)) {
|
|
376
|
+
const pluginDir = resolve(
|
|
377
|
+
destination,
|
|
378
|
+
(plugins[pluginKey] as Record<string, unknown>)?.development
|
|
379
|
+
?.toString()
|
|
380
|
+
?.slice("local:".length) ?? "",
|
|
381
|
+
);
|
|
382
|
+
const pluginConfigPath = join(pluginDir, "bos.config.json");
|
|
383
|
+
if (!existsSync(pluginConfigPath)) continue;
|
|
384
|
+
|
|
385
|
+
try {
|
|
386
|
+
const pluginConfig = JSON.parse(readFileSync(pluginConfigPath, "utf-8")) as Record<
|
|
387
|
+
string,
|
|
388
|
+
unknown
|
|
389
|
+
>;
|
|
390
|
+
let changed = false;
|
|
391
|
+
|
|
392
|
+
if ("extends" in pluginConfig) {
|
|
393
|
+
delete pluginConfig.extends;
|
|
394
|
+
changed = true;
|
|
395
|
+
}
|
|
396
|
+
if (pluginConfig.app && typeof pluginConfig.app === "object") {
|
|
397
|
+
const app = pluginConfig.app as Record<string, unknown>;
|
|
398
|
+
for (const entryKey of Object.keys(app)) {
|
|
399
|
+
const entry = app[entryKey];
|
|
400
|
+
if (entry && typeof entry === "object") {
|
|
401
|
+
const e = entry as Record<string, unknown>;
|
|
402
|
+
if ("production" in e || "integrity" in e) {
|
|
403
|
+
delete e.production;
|
|
404
|
+
delete e.integrity;
|
|
405
|
+
changed = true;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
if (changed) {
|
|
412
|
+
writeFileSync(pluginConfigPath, `${JSON.stringify(pluginConfig, null, 2)}\n`);
|
|
413
|
+
}
|
|
414
|
+
} catch {}
|
|
344
415
|
}
|
|
345
416
|
}
|
|
346
417
|
|
|
@@ -349,7 +420,7 @@ export async function personalizeConfig(
|
|
|
349
420
|
}
|
|
350
421
|
}
|
|
351
422
|
|
|
352
|
-
|
|
423
|
+
await saveBosConfig(destination, config);
|
|
353
424
|
}
|
|
354
425
|
|
|
355
426
|
const pkgPath = join(destination, "package.json");
|
|
@@ -463,32 +534,71 @@ export async function personalizeConfig(
|
|
|
463
534
|
);
|
|
464
535
|
}
|
|
465
536
|
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
);
|
|
537
|
+
const authTypesContent = generateAuthTypesTemplate();
|
|
538
|
+
const authTypesPaths = [
|
|
539
|
+
join(destination, "ui", "src", "lib", "auth-types.gen.ts"),
|
|
540
|
+
join(destination, "api", "src", "lib", "auth-types.gen.ts"),
|
|
541
|
+
];
|
|
542
|
+
if (existsSync(join(destination, "host", "src"))) {
|
|
543
|
+
authTypesPaths.push(join(destination, "host", "src", "lib", "auth-types.gen.ts"));
|
|
473
544
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
apiAuthTypesGenPath,
|
|
480
|
-
`import type { Auth } from "better-auth";\nexport type { Auth } from "better-auth";\nexport type AuthSessionUser = NonNullable<Auth["$Infer"]["Session"]["user"]> & {\n role?: string | null;\n isAnonymous?: boolean | null;\n walletAddress?: string | null;\n banned?: boolean | null;\n};\nexport type AuthSessionData = NonNullable<Auth["$Infer"]["Session"]["session"]> & {\n activeOrganizationId?: string | null;\n};\nexport type AuthSession = {\n user: AuthSessionUser | null;\n session: AuthSessionData | null;\n};\nexport interface AuthOrganizationContext {\n activeOrganizationId: string | null;\n organization: { id: string; name: string; slug: string; logo?: string | null; metadata?: Record<string, unknown> } | null;\n member: { id: string; role: string } | null;\n isPersonal: boolean;\n hasOrganization: boolean;\n}\nexport interface AuthRequestContext {\n user: AuthSessionUser | null;\n userId: string | null;\n isAuthenticated: boolean;\n authMethod: "session" | "apiKey" | "anonymous" | "none";\n near: {\n primaryAccountId: string | null;\n linkedAccounts: Array<{ accountId: string; network: string; publicKey: string; isPrimary: boolean }>;\n hasNearAccount: boolean;\n };\n organization: AuthOrganizationContext;\n organizations?: Array<{ id: string; role: string; name?: string; slug?: string }>;\n}\nexport type AuthActiveMember = { id: string | null; role: string | null; organizationId: string | null };\nexport type AuthOrganization = NonNullable<AuthOrganizationContext["organization"]>;\nexport type AuthOrganizationMember = NonNullable<AuthOrganizationContext["member"]>;\nexport type AuthOrganizationSummary = NonNullable<AuthRequestContext["organizations"]>[number];\nexport type AuthBaseSession = Auth["$Infer"]["Session"];\nexport type createAuthInstance = never;\nexport interface AuthServices {\n auth: Auth;\n db: unknown;\n driver: { close(): Promise<void> };\n handler: (req: Request) => Promise<Response>;\n}\n`,
|
|
481
|
-
);
|
|
545
|
+
for (const authTypesGenPath of authTypesPaths) {
|
|
546
|
+
if (!existsSync(authTypesGenPath)) {
|
|
547
|
+
mkdirSync(dirname(authTypesGenPath), { recursive: true });
|
|
548
|
+
writeFileSync(authTypesGenPath, authTypesContent);
|
|
549
|
+
}
|
|
482
550
|
}
|
|
551
|
+
}
|
|
483
552
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
553
|
+
function generateAuthTypesTemplate(): string {
|
|
554
|
+
return `import type { Auth } from "better-auth";
|
|
555
|
+
export type { Auth } from "better-auth";
|
|
556
|
+
export type AuthSessionUser = NonNullable<Auth["$Infer"]["Session"]["user"]> & {
|
|
557
|
+
role?: string | null;
|
|
558
|
+
isAnonymous?: boolean | null;
|
|
559
|
+
walletAddress?: string | null;
|
|
560
|
+
banned?: boolean | null;
|
|
561
|
+
};
|
|
562
|
+
export type AuthSessionData = NonNullable<Auth["$Infer"]["Session"]["session"]> & {
|
|
563
|
+
activeOrganizationId?: string | null;
|
|
564
|
+
};
|
|
565
|
+
export type AuthSession = {
|
|
566
|
+
user: AuthSessionUser | null;
|
|
567
|
+
session: AuthSessionData | null;
|
|
568
|
+
};
|
|
569
|
+
export interface AuthOrganizationContext {
|
|
570
|
+
activeOrganizationId: string | null;
|
|
571
|
+
organization: { id: string; name: string; slug: string; logo?: string | null; metadata?: Record<string, unknown> } | null;
|
|
572
|
+
member: { id: string; role: string } | null;
|
|
573
|
+
isPersonal: boolean;
|
|
574
|
+
hasOrganization: boolean;
|
|
575
|
+
}
|
|
576
|
+
export interface AuthRequestContext {
|
|
577
|
+
user: AuthSessionUser | null;
|
|
578
|
+
userId: string | null;
|
|
579
|
+
isAuthenticated: boolean;
|
|
580
|
+
authMethod: "session" | "apiKey" | "anonymous" | "none";
|
|
581
|
+
near: {
|
|
582
|
+
primaryAccountId: string | null;
|
|
583
|
+
linkedAccounts: Array<{ accountId: string; network: string; publicKey: string; isPrimary: boolean }>;
|
|
584
|
+
hasNearAccount: boolean;
|
|
585
|
+
};
|
|
586
|
+
organization: AuthOrganizationContext;
|
|
587
|
+
organizations?: Array<{ id: string; role: string; name?: string; slug?: string }>;
|
|
588
|
+
}
|
|
589
|
+
export type AuthActiveMember = { id: string | null; role: string | null; organizationId: string | null };
|
|
590
|
+
export type AuthOrganization = NonNullable<AuthOrganizationContext["organization"]>;
|
|
591
|
+
export type AuthOrganizationMember = NonNullable<AuthOrganizationContext["member"]>;
|
|
592
|
+
export type AuthOrganizationSummary = NonNullable<AuthRequestContext["organizations"]>[number];
|
|
593
|
+
export type AuthBaseSession = Auth["$Infer"]["Session"];
|
|
594
|
+
export type createAuthInstance = never;
|
|
595
|
+
export interface AuthServices {
|
|
596
|
+
auth: Auth;
|
|
597
|
+
db: unknown;
|
|
598
|
+
driver: { close(): Promise<void> };
|
|
599
|
+
handler: (req: Request) => Promise<Response>;
|
|
600
|
+
}
|
|
601
|
+
`;
|
|
492
602
|
}
|
|
493
603
|
|
|
494
604
|
export async function runBunInstall(destination: string): Promise<void> {
|
|
@@ -579,7 +689,7 @@ export async function writeInitSnapshot(
|
|
|
579
689
|
for (const match of matches) {
|
|
580
690
|
const pluginMatch = match.match(/^plugins\/([^/]+)/);
|
|
581
691
|
if (pluginMatch && !(options.plugins?.includes(pluginMatch[1]) ?? true)) continue;
|
|
582
|
-
if (
|
|
692
|
+
if (isPathExcluded(match, excludedRoutePatterns)) continue;
|
|
583
693
|
allFiles.add(match);
|
|
584
694
|
}
|
|
585
695
|
}
|
|
@@ -595,7 +705,7 @@ export async function writeInitSnapshot(
|
|
|
595
705
|
absolute: false,
|
|
596
706
|
});
|
|
597
707
|
for (const match of matches) {
|
|
598
|
-
if (!
|
|
708
|
+
if (!isPathExcluded(match, excludedRoutePatterns)) {
|
|
599
709
|
allFiles.add(match);
|
|
600
710
|
}
|
|
601
711
|
}
|
|
@@ -626,18 +736,7 @@ function computeHash(data: Uint8Array): string {
|
|
|
626
736
|
}
|
|
627
737
|
|
|
628
738
|
function mkTmpDir(prefix: string): string {
|
|
629
|
-
|
|
630
|
-
let attempt = 0;
|
|
631
|
-
while (true) {
|
|
632
|
-
const dir = `${base}-${Date.now()}-${attempt}`;
|
|
633
|
-
try {
|
|
634
|
-
mkdirSync(dir, { recursive: true });
|
|
635
|
-
return dir;
|
|
636
|
-
} catch {
|
|
637
|
-
attempt++;
|
|
638
|
-
if (attempt > 10) throw new Error("Failed to create temp directory");
|
|
639
|
-
}
|
|
640
|
-
}
|
|
739
|
+
return mkdtempSync(join(tmpdir(), `${prefix}-`));
|
|
641
740
|
}
|
|
642
741
|
|
|
643
742
|
export async function generateDatabaseMigrations(destination: string): Promise<void> {
|
package/src/cli/prompts.ts
CHANGED
|
@@ -16,7 +16,7 @@ function deriveAccountFromDomain(domain: string, extendsAccount: string): string
|
|
|
16
16
|
return `${firstSegment}.${suffix}`;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
|
-
const AVAILABLE_PLUGINS = [{ value: "
|
|
19
|
+
const AVAILABLE_PLUGINS = [{ value: "settings", label: "settings" }];
|
|
20
20
|
|
|
21
21
|
export async function promptInitOptions(input: {
|
|
22
22
|
extendsAccount?: string;
|
|
@@ -89,7 +89,7 @@ export async function promptInitOptions(input: {
|
|
|
89
89
|
((await p.multiselect({
|
|
90
90
|
message: "Select plugins:",
|
|
91
91
|
options: AVAILABLE_PLUGINS,
|
|
92
|
-
initialValues: ["
|
|
92
|
+
initialValues: ["settings"],
|
|
93
93
|
required: false,
|
|
94
94
|
})) as string[]);
|
|
95
95
|
|
package/src/cli/sync.ts
CHANGED
|
@@ -10,6 +10,13 @@ import {
|
|
|
10
10
|
import { dirname, join } from "node:path";
|
|
11
11
|
import { glob } from "glob";
|
|
12
12
|
import type { SyncOptions, SyncResult } from "../contract";
|
|
13
|
+
import {
|
|
14
|
+
isPlainObject as isPlainObjectFromMerge,
|
|
15
|
+
mergeBosConfigWithTemplate,
|
|
16
|
+
resolveExtendsRef,
|
|
17
|
+
} from "../merge";
|
|
18
|
+
import type { BosPluginRef } from "../types";
|
|
19
|
+
import { isPathExcluded } from "../utils/path-match";
|
|
13
20
|
import {
|
|
14
21
|
personalizeConfig,
|
|
15
22
|
readTemplatekeep,
|
|
@@ -24,6 +31,8 @@ const FRAMEWORK_OWNED_SYNC_FILES = new Set([
|
|
|
24
31
|
"biome.json",
|
|
25
32
|
"bos.config.json",
|
|
26
33
|
"package.json",
|
|
34
|
+
".github/renovate.json",
|
|
35
|
+
".github/workflows/ci.yml",
|
|
27
36
|
".github/workflows/release-sync.yml",
|
|
28
37
|
"ui/package.json",
|
|
29
38
|
"ui/postcss.config.mjs",
|
|
@@ -67,22 +76,6 @@ export function readLocalSyncExcludes(projectDir: string): string[] {
|
|
|
67
76
|
return readExcludeFile(join(projectDir, ".bos", "sync-local-exclude"));
|
|
68
77
|
}
|
|
69
78
|
|
|
70
|
-
function isExcluded(filePath: string, excludePatterns: string[]): boolean {
|
|
71
|
-
for (const pattern of excludePatterns) {
|
|
72
|
-
if (pattern.endsWith("/**")) {
|
|
73
|
-
const prefix = pattern.slice(0, -3);
|
|
74
|
-
if (filePath.startsWith(`${prefix}/`) || filePath === prefix) return true;
|
|
75
|
-
} else if (pattern.endsWith("/*")) {
|
|
76
|
-
const prefix = pattern.slice(0, -2);
|
|
77
|
-
const slashIdx = filePath.indexOf("/", prefix.length + 1);
|
|
78
|
-
if (filePath.startsWith(`${prefix}/`) && slashIdx === -1) return true;
|
|
79
|
-
} else if (filePath === pattern || filePath.startsWith(`${pattern}/`)) {
|
|
80
|
-
return true;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
79
|
function computeLocalHash(projectDir: string, filePath: string): string | null {
|
|
87
80
|
const fullPath = join(projectDir, filePath);
|
|
88
81
|
if (!existsSync(fullPath)) return null;
|
|
@@ -111,78 +104,6 @@ function backupFiles(projectDir: string, filePaths: string[]): string | null {
|
|
|
111
104
|
return backupDir;
|
|
112
105
|
}
|
|
113
106
|
|
|
114
|
-
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
115
|
-
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function mergeJsonValues(local: unknown, template: unknown): unknown {
|
|
119
|
-
if (isPlainObject(local) && isPlainObject(template)) {
|
|
120
|
-
const merged: Record<string, unknown> = {};
|
|
121
|
-
// Preserve local key order first
|
|
122
|
-
for (const key of Object.keys(local)) {
|
|
123
|
-
merged[key] = mergeJsonValues(local[key], template[key]);
|
|
124
|
-
}
|
|
125
|
-
// Append any new template keys at the end
|
|
126
|
-
for (const key of Object.keys(template)) {
|
|
127
|
-
if (!(key in merged)) {
|
|
128
|
-
merged[key] = template[key];
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
return merged;
|
|
132
|
-
}
|
|
133
|
-
// For arrays and primitives, local always wins
|
|
134
|
-
return local ?? template;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function mergeBosConfig(
|
|
138
|
-
local: Record<string, unknown>,
|
|
139
|
-
template: Record<string, unknown>,
|
|
140
|
-
): Record<string, unknown> {
|
|
141
|
-
const merged: Record<string, unknown> = {};
|
|
142
|
-
|
|
143
|
-
// 1. extends always first
|
|
144
|
-
if (local.extends !== undefined) merged.extends = local.extends;
|
|
145
|
-
else if (template.extends !== undefined) merged.extends = template.extends;
|
|
146
|
-
|
|
147
|
-
// 2. Fixed trailing group: app, plugins, shared
|
|
148
|
-
const TRAIL_GROUP = ["app", "plugins", "shared"];
|
|
149
|
-
|
|
150
|
-
const localKeys = Object.keys(local).filter((k) => k !== "extends");
|
|
151
|
-
const templateKeys = Object.keys(template).filter(
|
|
152
|
-
(k) => k !== "extends" && !localKeys.includes(k),
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
// Find the first trailing-group key present locally ("app" comes first in the group)
|
|
156
|
-
const firstTrailIndex = localKeys.findIndex((k) => TRAIL_GROUP.includes(k));
|
|
157
|
-
|
|
158
|
-
const orderedKeys: string[] = [];
|
|
159
|
-
|
|
160
|
-
if (firstTrailIndex >= 0) {
|
|
161
|
-
// Keys before the trail group stay in local order
|
|
162
|
-
orderedKeys.push(...localKeys.slice(0, firstTrailIndex));
|
|
163
|
-
// New template keys inserted right before the trail group
|
|
164
|
-
orderedKeys.push(...templateKeys);
|
|
165
|
-
// Trail group keys (app, plugins, shared) in canonical order, preserving local if present
|
|
166
|
-
for (const trailKey of TRAIL_GROUP) {
|
|
167
|
-
if (localKeys.includes(trailKey)) orderedKeys.push(trailKey);
|
|
168
|
-
}
|
|
169
|
-
} else {
|
|
170
|
-
// No trail group found locally — keep local order then append new keys and trail group
|
|
171
|
-
orderedKeys.push(...localKeys);
|
|
172
|
-
orderedKeys.push(...templateKeys);
|
|
173
|
-
for (const trailKey of TRAIL_GROUP) {
|
|
174
|
-
if (templateKeys.includes(trailKey)) orderedKeys.push(trailKey);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// 3. Merge values for each key
|
|
179
|
-
for (const key of orderedKeys) {
|
|
180
|
-
merged[key] = mergeJsonValues(local[key], template[key]);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return merged;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
107
|
function mergeStringMaps(
|
|
187
108
|
local: Record<string, string> | undefined,
|
|
188
109
|
template: Record<string, string> | undefined,
|
|
@@ -328,7 +249,7 @@ function writeSyncedFile(sourceDir: string, projectDir: string, filePath: string
|
|
|
328
249
|
if (localContent) {
|
|
329
250
|
const local = JSON.parse(localContent) as Record<string, unknown>;
|
|
330
251
|
const template = JSON.parse(templateContent) as Record<string, unknown>;
|
|
331
|
-
const merged =
|
|
252
|
+
const merged = mergeBosConfigWithTemplate(local, template);
|
|
332
253
|
writeFileSync(dest, `${JSON.stringify(merged, null, 2)}\n`);
|
|
333
254
|
return;
|
|
334
255
|
}
|
|
@@ -351,11 +272,20 @@ function writeSyncedFile(sourceDir: string, projectDir: string, filePath: string
|
|
|
351
272
|
}
|
|
352
273
|
|
|
353
274
|
export async function syncTemplate(projectDir: string, options: SyncOptions): Promise<SyncResult> {
|
|
275
|
+
// Sync reads the raw bos.config.json (not the resolved config) because it needs
|
|
276
|
+
// the user's explicit local settings: their extends ref, selected plugins, etc.
|
|
277
|
+
// The resolved config is the merged result and would include inherited parent
|
|
278
|
+
// values that the user didn't explicitly choose, which would break sync filtering.
|
|
354
279
|
const localConfig = JSON.parse(
|
|
355
280
|
readFileSync(join(projectDir, "bos.config.json"), "utf-8"),
|
|
356
281
|
) as Record<string, unknown>;
|
|
357
282
|
|
|
358
|
-
|
|
283
|
+
let extendsRef: string | undefined;
|
|
284
|
+
if (typeof localConfig.extends === "string") {
|
|
285
|
+
extendsRef = localConfig.extends;
|
|
286
|
+
} else if (isPlainObjectFromMerge(localConfig.extends)) {
|
|
287
|
+
extendsRef = resolveExtendsRef(localConfig.extends as Record<string, string>, "production");
|
|
288
|
+
}
|
|
359
289
|
if (!extendsRef?.startsWith("bos://")) {
|
|
360
290
|
return {
|
|
361
291
|
status: "error",
|
|
@@ -421,8 +351,9 @@ export async function syncTemplate(projectDir: string, options: SyncOptions): Pr
|
|
|
421
351
|
|
|
422
352
|
const pluginRoutes: Record<string, string[]> = {};
|
|
423
353
|
if (parentConfig.plugins) {
|
|
424
|
-
for (const [key,
|
|
425
|
-
|
|
354
|
+
for (const [key, entry] of Object.entries(parentConfig.plugins)) {
|
|
355
|
+
const ref: BosPluginRef | null = entry && typeof entry !== "string" ? entry : null;
|
|
356
|
+
if (ref?.routes && ref.routes.length > 0) {
|
|
426
357
|
pluginRoutes[key] = ref.routes;
|
|
427
358
|
}
|
|
428
359
|
}
|
|
@@ -439,7 +370,7 @@ export async function syncTemplate(projectDir: string, options: SyncOptions): Pr
|
|
|
439
370
|
for (const filePath of allTemplateFiles) {
|
|
440
371
|
const pluginMatch = filePath.match(/^plugins\/([^/]+)/);
|
|
441
372
|
if (pluginMatch && !childPlugins.includes(pluginMatch[1])) continue;
|
|
442
|
-
if (
|
|
373
|
+
if (isPathExcluded(filePath, excludedRoutePatterns)) continue;
|
|
443
374
|
filteredFiles.add(filePath);
|
|
444
375
|
}
|
|
445
376
|
|
|
@@ -453,7 +384,7 @@ export async function syncTemplate(projectDir: string, options: SyncOptions): Pr
|
|
|
453
384
|
absolute: false,
|
|
454
385
|
});
|
|
455
386
|
for (const match of matches) {
|
|
456
|
-
if (!
|
|
387
|
+
if (!isPathExcluded(match, excludedRoutePatterns)) {
|
|
457
388
|
filteredFiles.add(match);
|
|
458
389
|
}
|
|
459
390
|
}
|
|
@@ -469,7 +400,7 @@ export async function syncTemplate(projectDir: string, options: SyncOptions): Pr
|
|
|
469
400
|
for (const filePath of filteredFiles) {
|
|
470
401
|
const destPath = toDestPath(filePath);
|
|
471
402
|
const frameworkOwned = isFrameworkOwnedSyncFile(destPath);
|
|
472
|
-
if (
|
|
403
|
+
if (isPathExcluded(destPath, excludePatterns) && !frameworkOwned) continue;
|
|
473
404
|
|
|
474
405
|
const localHash = computeLocalHash(projectDir, destPath);
|
|
475
406
|
const sourceContent = readFileSync(join(sourceDir, filePath));
|
|
@@ -515,7 +446,7 @@ export async function syncTemplate(projectDir: string, options: SyncOptions): Pr
|
|
|
515
446
|
}
|
|
516
447
|
|
|
517
448
|
const filesToWrite = [...updated, ...added].filter(
|
|
518
|
-
(f) => isFrameworkOwnedSyncFile(f) || !
|
|
449
|
+
(f) => isFrameworkOwnedSyncFile(f) || !isPathExcluded(f, excludePatterns),
|
|
519
450
|
);
|
|
520
451
|
|
|
521
452
|
const destToSource = new Map<string, string>();
|
package/src/cli/upgrade.ts
CHANGED