@allbluecn/kernel 0.2.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/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # @allbluecn/kernel
2
+
3
+ AllBlue 插件协议:类型定义 + 构建期装配器(零运行时)。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ pnpm add @allbluecn/kernel
9
+ ```
10
+
11
+ ## 用法
12
+
13
+ ```ts
14
+ import { definePlugin, defineProfile, resolvePluginOrder } from '@allbluecn/kernel'
15
+
16
+ const auth = definePlugin({
17
+ id: '@allblue/auth',
18
+ version: '0.1.0',
19
+ contributes: {
20
+ features: [{ key: 'sso', tier: 'pro', module: 'auth' }],
21
+ },
22
+ })
23
+
24
+ const profile = defineProfile({
25
+ id: 'oss',
26
+ plugins: [auth],
27
+ })
28
+
29
+ const resolved = resolvePluginOrder(profile.plugins)
30
+ ```
31
+
32
+ ### CLI
33
+
34
+ ```bash
35
+ pnpm allblue modules --profile oss
36
+ ```
package/dist/bin.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {}
package/dist/bin.js ADDED
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+ import { n as generateAssemblyCode, r as resolvePluginOrder, t as dumpModules } from "./src-Dvcggyr-.js";
3
+ import { parseArgs } from "node:util";
4
+ import { pathToFileURL } from "node:url";
5
+ import { dirname, relative, resolve } from "node:path";
6
+ //#region src/bin.ts
7
+ const { values } = parseArgs({
8
+ allowPositionals: true,
9
+ options: {
10
+ profile: {
11
+ type: "string",
12
+ short: "p"
13
+ },
14
+ "gen-out": { type: "string" }
15
+ }
16
+ });
17
+ async function main() {
18
+ const profileArg = values.profile;
19
+ if (!profileArg) {
20
+ console.error("用法: allblue modules --profile <oss|saas|desktop> [--gen-out <file>]");
21
+ process.exit(1);
22
+ }
23
+ const profilePath = resolve(profileArg.endsWith(".ts") ? profileArg : `profiles/${profileArg}.ts`);
24
+ const profile = (await import(pathToFileURL(profilePath).href)).default;
25
+ const resolved = resolvePluginOrder(profile.plugins);
26
+ if (values["gen-out"]) {
27
+ const outPath = resolve(values["gen-out"]);
28
+ const relProfile = relImportPath(outPath, profilePath);
29
+ const code = generateAssemblyCode({
30
+ profileId: profile.id,
31
+ profileModulePath: relProfile,
32
+ resolved
33
+ });
34
+ const { writeFile } = await import("node:fs/promises");
35
+ await writeFile(outPath, code + "\n", "utf8");
36
+ console.log(`已生成 ${outPath} (${resolved.order.length} plugins)`);
37
+ }
38
+ console.log(dumpModules(profile.id, resolved));
39
+ }
40
+ function relImportPath(fromFile, toFile) {
41
+ let rel = relative(dirname(fromFile), toFile).replace(/\.ts$/, "");
42
+ if (!rel.startsWith(".")) rel = `./${rel}`;
43
+ return rel;
44
+ }
45
+ main().catch((error) => {
46
+ console.error(error instanceof Error ? error.message : error);
47
+ process.exit(1);
48
+ });
49
+ //#endregion
50
+ export {};
@@ -0,0 +1,64 @@
1
+ import { ComponentType } from "react";
2
+ //#region src/contributions.d.ts
3
+ type ServerMiddleware = unknown;
4
+ interface RouteContribution {
5
+ path: string;
6
+ route: unknown;
7
+ }
8
+ type QueryFactoryMap = Record<string, () => unknown>;
9
+ type SlotComponentMap = Record<string, ComponentType>;
10
+ interface FeatureContribution {
11
+ key: string;
12
+ tier: string;
13
+ module: string;
14
+ }
15
+ interface PrismaContribution {
16
+ models: string[];
17
+ }
18
+ interface PluginContributions {
19
+ routes?: RouteContribution[];
20
+ serverMiddleware?: ServerMiddleware[];
21
+ queryOptions?: QueryFactoryMap;
22
+ uiSlots?: SlotComponentMap;
23
+ features?: FeatureContribution[];
24
+ aiTools?: unknown[];
25
+ prisma?: PrismaContribution;
26
+ }
27
+ //#endregion
28
+ //#region src/types.d.ts
29
+ interface PluginDefinition {
30
+ id: string;
31
+ version: string;
32
+ dependencies?: string[];
33
+ contributes: PluginContributions;
34
+ }
35
+ //#endregion
36
+ //#region src/define-profile.d.ts
37
+ interface ProfileDefinition {
38
+ id: string;
39
+ plugins: PluginDefinition[];
40
+ }
41
+ declare function defineProfile(profile: ProfileDefinition): ProfileDefinition;
42
+ //#endregion
43
+ //#region src/define-plugin.d.ts
44
+ declare function definePlugin(def: PluginDefinition): PluginDefinition;
45
+ //#endregion
46
+ //#region src/resolve-order.d.ts
47
+ interface ResolveResult {
48
+ order: PluginDefinition[];
49
+ ids: string[];
50
+ }
51
+ declare function resolvePluginOrder(plugins: PluginDefinition[]): ResolveResult;
52
+ //#endregion
53
+ //#region src/generate-assembly.d.ts
54
+ interface GenerateAssemblyInput {
55
+ profileId: string;
56
+ profileModulePath: string;
57
+ resolved: ResolveResult;
58
+ }
59
+ declare function generateAssemblyCode(input: GenerateAssemblyInput): string;
60
+ //#endregion
61
+ //#region src/dump-modules.d.ts
62
+ declare function dumpModules(profileId: string, resolved: ResolveResult): string;
63
+ //#endregion
64
+ export { type FeatureContribution, type PluginContributions, type PluginDefinition, type PrismaContribution, type ProfileDefinition, type QueryFactoryMap, type ResolveResult, type RouteContribution, type SlotComponentMap, definePlugin, defineProfile, dumpModules, generateAssemblyCode, resolvePluginOrder };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import { a as definePlugin, i as defineProfile, n as generateAssemblyCode, r as resolvePluginOrder, t as dumpModules } from "./src-Dvcggyr-.js";
2
+ export { definePlugin, defineProfile, dumpModules, generateAssemblyCode, resolvePluginOrder };
@@ -0,0 +1,122 @@
1
+ //#region src/define-plugin.ts
2
+ const PLUGIN_ID_RE = /^@[a-z0-9-]+\/[a-z0-9-]+$/;
3
+ function definePlugin(def) {
4
+ if (!PLUGIN_ID_RE.test(def.id)) throw new Error(`插件 id 必须形如 '@scope/name'(小写),收到: ${def.id}`);
5
+ if (!/^\d+\.\d+\.\d+/.test(def.version)) throw new Error(`插件 version 必须是语义化版本,收到: ${def.version}`);
6
+ return deepFreeze(def);
7
+ }
8
+ function deepFreeze(value) {
9
+ if (value && typeof value === "object" && !Object.isFrozen(value)) {
10
+ Object.freeze(value);
11
+ for (const key of Object.keys(value)) deepFreeze(value[key]);
12
+ }
13
+ return value;
14
+ }
15
+ //#endregion
16
+ //#region src/define-profile.ts
17
+ const PROFILE_ID_RE = /^[a-z0-9][a-z0-9-]*$/;
18
+ function defineProfile(profile) {
19
+ if (!PROFILE_ID_RE.test(profile.id)) throw new Error(`profile id 只允许小写字母/数字/连字符,收到: ${profile.id}`);
20
+ const seen = /* @__PURE__ */ new Set();
21
+ for (const plugin of profile.plugins) {
22
+ if (seen.has(plugin.id)) throw new Error(`profile 中存在重复插件 id: ${plugin.id}`);
23
+ seen.add(plugin.id);
24
+ }
25
+ return Object.freeze({
26
+ ...profile,
27
+ plugins: Object.freeze([...profile.plugins])
28
+ });
29
+ }
30
+ //#endregion
31
+ //#region src/resolve-order.ts
32
+ function resolvePluginOrder(plugins) {
33
+ const byId = /* @__PURE__ */ new Map();
34
+ for (const plugin of plugins) {
35
+ if (byId.has(plugin.id)) throw new Error(`重复插件 id: ${plugin.id}`);
36
+ byId.set(plugin.id, plugin);
37
+ }
38
+ for (const plugin of plugins) for (const dep of plugin.dependencies ?? []) if (!byId.has(dep)) throw new Error(`插件 ${plugin.id} 依赖的 ${dep} 未在 profile 中声明`);
39
+ const declarationIndex = new Map(plugins.map((p, i) => [p.id, i]));
40
+ const inDegree = /* @__PURE__ */ new Map();
41
+ const dependents = /* @__PURE__ */ new Map();
42
+ for (const plugin of plugins) {
43
+ inDegree.set(plugin.id, plugin.dependencies?.length ?? 0);
44
+ for (const dep of plugin.dependencies ?? []) dependents.set(dep, [...dependents.get(dep) ?? [], plugin.id]);
45
+ }
46
+ const ready = plugins.filter((p) => (inDegree.get(p.id) ?? 0) === 0).sort((a, b) => declarationIndex.get(a.id) - declarationIndex.get(b.id)).map((p) => p.id);
47
+ const orderIds = [];
48
+ while (ready.length > 0) {
49
+ const id = ready.shift();
50
+ orderIds.push(id);
51
+ for (const dependent of dependents.get(id) ?? []) {
52
+ const next = (inDegree.get(dependent) ?? 0) - 1;
53
+ inDegree.set(dependent, next);
54
+ if (next === 0) ready.push(dependent);
55
+ }
56
+ }
57
+ if (orderIds.length !== plugins.length) {
58
+ const cyclePath = findCyclePath(plugins.filter((p) => !orderIds.includes(p.id)).map((p) => p.id), byId);
59
+ throw new Error(`检测到依赖环: ${cyclePath.join(" -> ")}`);
60
+ }
61
+ return {
62
+ order: orderIds.map((id) => byId.get(id)),
63
+ ids: orderIds
64
+ };
65
+ }
66
+ function findCyclePath(cyclicIds, byId) {
67
+ const start = cyclicIds[0];
68
+ const path = [start];
69
+ const visited = /* @__PURE__ */ new Set([start]);
70
+ let current = start;
71
+ while (true) {
72
+ const deps = byId.get(current)?.dependencies ?? [];
73
+ const next = deps.find((d) => cyclicIds.includes(d)) ?? deps[0];
74
+ if (!next) break;
75
+ if (next === start || visited.has(next)) {
76
+ path.push(next);
77
+ return path;
78
+ }
79
+ visited.add(next);
80
+ path.push(next);
81
+ current = next;
82
+ }
83
+ return path;
84
+ }
85
+ //#endregion
86
+ //#region src/generate-assembly.ts
87
+ function generateAssemblyCode(input) {
88
+ const { profileId, profileModulePath, resolved } = input;
89
+ const ids = resolved.ids.map((id) => `'${id}'`).join(", ");
90
+ return [
91
+ `// AUTO-GENERATED by @allbluecn/kernel — DO NOT EDIT`,
92
+ `// profile: ${profileId} (validated: ${resolved.order.length} plugins)`,
93
+ `// 拓扑序(被依赖者在前):${resolved.ids.join(" -> ")}`,
94
+ `import type { ProfileDefinition } from '@allbluecn/kernel'`,
95
+ ``,
96
+ `export { default as profile } from '${profileModulePath}'`,
97
+ ``,
98
+ `export const pluginIds: string[] = [${ids}]`,
99
+ ``,
100
+ `declare const _profile: ProfileDefinition`
101
+ ].join("\n");
102
+ }
103
+ //#endregion
104
+ //#region src/dump-modules.ts
105
+ function dumpModules(profileId, resolved) {
106
+ const lines = [`profile: ${profileId} (${resolved.order.length} plugins)`];
107
+ for (const plugin of resolved.order) {
108
+ const c = plugin.contributes;
109
+ const parts = [];
110
+ if (c.routes?.length) parts.push(`routes:${c.routes.length}`);
111
+ if (c.serverMiddleware?.length) parts.push(`middleware:${c.serverMiddleware.length}`);
112
+ if (c.queryOptions && Object.keys(c.queryOptions).length) parts.push(`queries:${Object.keys(c.queryOptions).length}`);
113
+ if (c.uiSlots && Object.keys(c.uiSlots).length) parts.push(`ui-slots:${Object.keys(c.uiSlots).length}`);
114
+ if (c.features?.length) parts.push(`features:${c.features.length}`);
115
+ if (c.aiTools?.length) parts.push(`ai-tools:${c.aiTools.length}`);
116
+ if (c.prisma?.models.length) parts.push(`prisma-models:${c.prisma.models.length}`);
117
+ lines.push(`${plugin.id}@${plugin.version} — ${parts.length ? parts.join(" ") : "no contributions"}`);
118
+ }
119
+ return lines.join("\n");
120
+ }
121
+ //#endregion
122
+ export { definePlugin as a, defineProfile as i, generateAssemblyCode as n, resolvePluginOrder as r, dumpModules as t };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@allbluecn/kernel",
3
+ "version": "0.2.0",
4
+ "license": "AGPL-3.0-or-later",
5
+ "type": "module",
6
+ "description": "AllBlue 插件协议:类型定义 + 构建期装配器(零运行时)",
7
+ "main": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "bin": {
16
+ "allblue": "./dist/bin.js"
17
+ },
18
+ "files": ["dist"],
19
+ "scripts": {
20
+ "build": "tsdown",
21
+ "lint": "tsc --noEmit",
22
+ "test": "vitest",
23
+ "test:run": "vitest run",
24
+ "typecheck": "tsc --noEmit"
25
+ },
26
+ "peerDependencies": {
27
+ "@tanstack/react-start": "1.168.46",
28
+ "@tanstack/ai": "0.43.0",
29
+ "react": "^19"
30
+ },
31
+ "devDependencies": {
32
+ "@tanstack/react-start": "1.168.46",
33
+ "@tanstack/ai": "0.43.0",
34
+ "@types/node": "^24.13.3",
35
+ "@types/react": "^19",
36
+ "react": "^19",
37
+ "tsdown": "^0.22.0",
38
+ "typescript": "^7",
39
+ "vitest": "4.1.5"
40
+ }
41
+ }