@allbluecn/kernel 0.2.1 → 0.3.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/bin.js +89 -14
- package/dist/index.d.ts +545 -8
- package/dist/index.js +2 -2
- package/dist/src-B6_ZEkl9.js +316 -0
- package/package.json +3 -3
- package/dist/src-Dvcggyr-.js +0 -122
package/dist/bin.js
CHANGED
|
@@ -1,41 +1,116 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { c as generateAssemblyCode, f as resolvePluginOrder, l as generateAssemblyFiles, s as dumpModules, u as parseProfileImports } from "./src-B6_ZEkl9.js";
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { dirname, relative, resolve } from "node:path";
|
|
6
6
|
//#region src/bin.ts
|
|
7
|
-
const { values } = parseArgs({
|
|
7
|
+
const { values, positionals } = parseArgs({
|
|
8
8
|
allowPositionals: true,
|
|
9
9
|
options: {
|
|
10
10
|
profile: {
|
|
11
11
|
type: "string",
|
|
12
12
|
short: "p"
|
|
13
13
|
},
|
|
14
|
-
"gen-out": { type: "string" }
|
|
14
|
+
"gen-out": { type: "string" },
|
|
15
|
+
"app-dir": { type: "string" },
|
|
16
|
+
"dry-run": {
|
|
17
|
+
type: "boolean",
|
|
18
|
+
default: false
|
|
19
|
+
},
|
|
20
|
+
assemble: {
|
|
21
|
+
type: "boolean",
|
|
22
|
+
default: false
|
|
23
|
+
},
|
|
24
|
+
modules: {
|
|
25
|
+
type: "boolean",
|
|
26
|
+
default: false
|
|
27
|
+
}
|
|
15
28
|
}
|
|
16
29
|
});
|
|
30
|
+
const isAssemble = values.assemble === true;
|
|
31
|
+
const isModules = values.modules === true || !isAssemble;
|
|
32
|
+
const appDir = typeof values["app-dir"] === "string" ? values["app-dir"] : "apps/web";
|
|
33
|
+
const dryRun = values["dry-run"] === true;
|
|
17
34
|
async function main() {
|
|
18
35
|
const profileArg = values.profile;
|
|
19
36
|
if (!profileArg) {
|
|
20
|
-
console.error("用法: allblue modules --profile <oss|saas|desktop> [--gen-out <file>]");
|
|
37
|
+
console.error("用法: allblue [--modules] --profile <oss|saas|desktop> [--gen-out <file>]");
|
|
38
|
+
console.error(" allblue --assemble --profile <oss|saas|desktop> [--app-dir <dir>] [--dry-run]");
|
|
21
39
|
process.exit(1);
|
|
22
40
|
}
|
|
23
41
|
const profilePath = resolve(profileArg.endsWith(".ts") ? profileArg : `profiles/${profileArg}.ts`);
|
|
24
42
|
const profile = (await import(pathToFileURL(profilePath).href)).default;
|
|
25
43
|
const resolved = resolvePluginOrder(profile.plugins);
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
44
|
+
if (isModules) {
|
|
45
|
+
if (values["gen-out"]) {
|
|
46
|
+
const outPath = resolve(values["gen-out"]);
|
|
47
|
+
const relProfile = relImportPath(outPath, profilePath);
|
|
48
|
+
const code = generateAssemblyCode({
|
|
49
|
+
profileId: profile.id,
|
|
50
|
+
profileModulePath: relProfile,
|
|
51
|
+
resolved
|
|
52
|
+
});
|
|
53
|
+
const { writeFile } = await import("node:fs/promises");
|
|
54
|
+
await writeFile(outPath, code + "\n", "utf8");
|
|
55
|
+
console.log(`已生成 ${outPath} (${resolved.order.length} plugins)`);
|
|
56
|
+
}
|
|
57
|
+
console.log(dumpModules(profile.id, resolved));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
if (isAssemble) {
|
|
61
|
+
const { readFileSync } = await import("node:fs");
|
|
62
|
+
const profileSource = readFileSync(profilePath, "utf8");
|
|
63
|
+
const imports = parseProfileImports(profileSource);
|
|
64
|
+
const pluginLocalNames = extractPluginLocalNames(profileSource);
|
|
65
|
+
const relProfilePath = relImportPath(resolve(appDir, "src/plugins/plugins.gen.ts"), profilePath);
|
|
66
|
+
const assembly = generateAssemblyFiles({
|
|
30
67
|
profileId: profile.id,
|
|
31
|
-
profileModulePath:
|
|
32
|
-
resolved
|
|
68
|
+
profileModulePath: relProfilePath,
|
|
69
|
+
resolved,
|
|
70
|
+
imports,
|
|
71
|
+
pluginLocalNames
|
|
33
72
|
});
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
73
|
+
if (dryRun) {
|
|
74
|
+
console.log("=== [dry-run] plugins.gen.ts ===");
|
|
75
|
+
console.log(assembly.pluginsGen);
|
|
76
|
+
console.log("=== [dry-run] modules.gen.ts ===");
|
|
77
|
+
console.log(assembly.modulesGen);
|
|
78
|
+
console.log("=== [dry-run] ui.gen.ts ===");
|
|
79
|
+
console.log(assembly.uiGen);
|
|
80
|
+
for (const [file, content] of assembly.routeFiles.entries()) {
|
|
81
|
+
console.log(`=== [dry-run] ${file} ===`);
|
|
82
|
+
console.log(content);
|
|
83
|
+
}
|
|
84
|
+
} else await writeAssemblyFiles(appDir, assembly);
|
|
85
|
+
console.log(dumpModules(profile.id, resolved));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function extractPluginLocalNames(source) {
|
|
90
|
+
const match = source.match(/plugins:\s*\[([^\]]*)\]/s);
|
|
91
|
+
if (!match) return [];
|
|
92
|
+
return match[1].split(",").map((s) => s.trim()).filter(Boolean);
|
|
93
|
+
}
|
|
94
|
+
async function writeAssemblyFiles(appDir, assembly) {
|
|
95
|
+
const { writeFile, readFile, unlink, mkdir } = await import("node:fs/promises");
|
|
96
|
+
const { join } = await import("node:path");
|
|
97
|
+
const pluginsDir = join(appDir, "src/plugins");
|
|
98
|
+
await mkdir(pluginsDir, { recursive: true });
|
|
99
|
+
const pluginsGenContent = assembly.pluginsGen;
|
|
100
|
+
await writeFile(join(pluginsDir, "plugins.gen.ts"), pluginsGenContent + "\n");
|
|
101
|
+
await writeFile(join(pluginsDir, "modules.gen.ts"), assembly.modulesGen + "\n");
|
|
102
|
+
await writeFile(join(pluginsDir, "ui.gen.ts"), assembly.uiGen + "\n");
|
|
103
|
+
const manifestPath = join(pluginsDir, ".gen-manifest.json");
|
|
104
|
+
const oldManifest = await readFile(manifestPath, "utf8").catch(() => null);
|
|
105
|
+
const oldFiles = oldManifest ? JSON.parse(oldManifest).routeFiles : [];
|
|
106
|
+
const newFiles = [...assembly.routeFiles.keys()];
|
|
107
|
+
for (const f of oldFiles) if (!newFiles.includes(f)) await unlink(join(appDir, f)).catch(() => {});
|
|
108
|
+
for (const [file, content] of assembly.routeFiles.entries()) {
|
|
109
|
+
const filePath = join(appDir, file);
|
|
110
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
111
|
+
await writeFile(filePath, content + "\n");
|
|
37
112
|
}
|
|
38
|
-
|
|
113
|
+
await writeFile(manifestPath, JSON.stringify({ routeFiles: newFiles }, null, 2));
|
|
39
114
|
}
|
|
40
115
|
function relImportPath(fromFile, toFile) {
|
|
41
116
|
let rel = relative(dirname(fromFile), toFile).replace(/\.ts$/, "");
|
package/dist/index.d.ts
CHANGED
|
@@ -1,28 +1,529 @@
|
|
|
1
1
|
import { ComponentType } from "react";
|
|
2
|
+
import { StreamChunk } from "@tanstack/ai";
|
|
3
|
+
//#region src/pro-contract/billing.d.ts
|
|
4
|
+
interface SubscriptionInfo {
|
|
5
|
+
planId: string;
|
|
6
|
+
planName: 'free' | 'pro' | 'enterprise' | 'open-source';
|
|
7
|
+
status: 'trialing' | 'active' | 'past_due' | 'canceled' | 'expired' | 'unpaid';
|
|
8
|
+
seats: number;
|
|
9
|
+
addonSeats: number;
|
|
10
|
+
currentPeriodStart: Date | null;
|
|
11
|
+
currentPeriodEnd: Date | null;
|
|
12
|
+
cancelAtPeriodEnd: boolean;
|
|
13
|
+
trialEndsAt: Date | null;
|
|
14
|
+
features: Record<string, boolean>;
|
|
15
|
+
currency: 'usd' | 'cny';
|
|
16
|
+
}
|
|
17
|
+
interface CheckoutParams {
|
|
18
|
+
userId: string;
|
|
19
|
+
planId: string;
|
|
20
|
+
successUrl: string;
|
|
21
|
+
cancelUrl: string;
|
|
22
|
+
trialDays?: number;
|
|
23
|
+
currency: 'usd' | 'cny';
|
|
24
|
+
}
|
|
25
|
+
interface PortalSessionParams {
|
|
26
|
+
userId: string;
|
|
27
|
+
returnUrl: string;
|
|
28
|
+
}
|
|
29
|
+
interface InvoiceInfo {
|
|
30
|
+
id: string;
|
|
31
|
+
providerInvoiceId: string;
|
|
32
|
+
amountDue: number;
|
|
33
|
+
amountPaid: number;
|
|
34
|
+
currency: string;
|
|
35
|
+
status: string;
|
|
36
|
+
pdfUrl: string | null;
|
|
37
|
+
hostedInvoiceUrl: string | null;
|
|
38
|
+
periodStart: Date;
|
|
39
|
+
periodEnd: Date;
|
|
40
|
+
paidAt: Date | null;
|
|
41
|
+
}
|
|
42
|
+
interface PlanInfo {
|
|
43
|
+
id: string;
|
|
44
|
+
name: string;
|
|
45
|
+
displayName: string;
|
|
46
|
+
description: string | null;
|
|
47
|
+
priceMonthly: number;
|
|
48
|
+
priceYearly: number | null;
|
|
49
|
+
currency: 'usd' | 'cny';
|
|
50
|
+
maxSeats: number | null;
|
|
51
|
+
aiQuotaMonthly: number | null;
|
|
52
|
+
features: Record<string, boolean>;
|
|
53
|
+
isPublic: boolean;
|
|
54
|
+
providerPriceId: string | null;
|
|
55
|
+
seatPriceMonthly: number | null;
|
|
56
|
+
}
|
|
57
|
+
interface SeatUpdateParams {
|
|
58
|
+
userId: string;
|
|
59
|
+
/** 目标加购席位总数(非增量),取值 [0, 100] */
|
|
60
|
+
addonSeats: number;
|
|
61
|
+
/** 首次加购跳转 Creem Checkout 付款成功后的回跳 URL(仅路径①首次加购使用) */
|
|
62
|
+
successUrl?: string;
|
|
63
|
+
}
|
|
64
|
+
interface SeatCheckResult {
|
|
65
|
+
available: boolean;
|
|
66
|
+
current: number;
|
|
67
|
+
max: number | null;
|
|
68
|
+
reason?: 'max_seats_reached' | 'unknown_plan';
|
|
69
|
+
}
|
|
70
|
+
declare class BillingUnavailableError extends Error {
|
|
71
|
+
constructor(message?: string);
|
|
72
|
+
}
|
|
73
|
+
declare class WebhookSignatureError extends Error {
|
|
74
|
+
constructor(message?: string);
|
|
75
|
+
}
|
|
76
|
+
declare class SeatsExceededError extends Error {
|
|
77
|
+
current: number;
|
|
78
|
+
max: number;
|
|
79
|
+
constructor(current: number, max: number);
|
|
80
|
+
}
|
|
81
|
+
interface BillingService {
|
|
82
|
+
getSubscription(userId: string): Promise<SubscriptionInfo | null>;
|
|
83
|
+
listPublicPlans(currency: 'usd' | 'cny'): Promise<PlanInfo[]>;
|
|
84
|
+
createCheckoutSession(params: CheckoutParams): Promise<{
|
|
85
|
+
url: string;
|
|
86
|
+
}>;
|
|
87
|
+
createCustomerPortalSession(params: PortalSessionParams): Promise<{
|
|
88
|
+
url: string;
|
|
89
|
+
}>;
|
|
90
|
+
listInvoices(userId: string, limit?: number): Promise<InvoiceInfo[]>;
|
|
91
|
+
checkSeatAvailability(userId: string): Promise<SeatCheckResult>;
|
|
92
|
+
handleWebhookEvent(rawBody: string, signature: string): Promise<{
|
|
93
|
+
received: boolean;
|
|
94
|
+
}>;
|
|
95
|
+
syncSubscription(userId: string): Promise<SubscriptionInfo>;
|
|
96
|
+
getEffectivePlan(userId: string): Promise<{
|
|
97
|
+
planName: string;
|
|
98
|
+
features: Record<string, boolean>;
|
|
99
|
+
}>;
|
|
100
|
+
updateSeats(params: SeatUpdateParams): Promise<SubscriptionInfo & {
|
|
101
|
+
checkoutUrl?: string;
|
|
102
|
+
}>;
|
|
103
|
+
}
|
|
104
|
+
//#endregion
|
|
105
|
+
//#region src/pro-contract/hosted-ai.d.ts
|
|
106
|
+
declare class HostedAiError extends Error {
|
|
107
|
+
readonly code: 'QUOTA_EXHAUSTED' | 'NO_SUBSCRIPTION' | 'NO_ACTIVE_WORKSPACE' | 'PROVIDER_MISCONFIGURED' | 'PROVIDER_ERROR';
|
|
108
|
+
readonly details?: {
|
|
109
|
+
used?: number;
|
|
110
|
+
limit?: number | null;
|
|
111
|
+
resetAt?: Date;
|
|
112
|
+
cause?: unknown;
|
|
113
|
+
} | undefined;
|
|
114
|
+
constructor(code: 'QUOTA_EXHAUSTED' | 'NO_SUBSCRIPTION' | 'NO_ACTIVE_WORKSPACE' | 'PROVIDER_MISCONFIGURED' | 'PROVIDER_ERROR', message: string, details?: {
|
|
115
|
+
used?: number;
|
|
116
|
+
limit?: number | null;
|
|
117
|
+
resetAt?: Date;
|
|
118
|
+
cause?: unknown;
|
|
119
|
+
} | undefined);
|
|
120
|
+
}
|
|
121
|
+
interface HostedAiChatParams {
|
|
122
|
+
workspaceId: string;
|
|
123
|
+
userId: string;
|
|
124
|
+
messages: Array<{
|
|
125
|
+
role: 'user' | 'assistant' | 'tool';
|
|
126
|
+
content: string;
|
|
127
|
+
}>;
|
|
128
|
+
systemPrompts?: string[];
|
|
129
|
+
model?: string;
|
|
130
|
+
threadId?: string;
|
|
131
|
+
runId?: string;
|
|
132
|
+
resume?: Array<unknown>;
|
|
133
|
+
webSearchEnabled?: boolean;
|
|
134
|
+
}
|
|
135
|
+
interface HostedAiChatResult {
|
|
136
|
+
content: string;
|
|
137
|
+
inputTokens: number;
|
|
138
|
+
outputTokens: number;
|
|
139
|
+
costUsd: number | null;
|
|
140
|
+
model: string;
|
|
141
|
+
}
|
|
142
|
+
interface AiQuota {
|
|
143
|
+
used: number;
|
|
144
|
+
limit: number | null;
|
|
145
|
+
resetAt: Date;
|
|
146
|
+
planName: string;
|
|
147
|
+
}
|
|
148
|
+
interface HostedAiModel {
|
|
149
|
+
id: string;
|
|
150
|
+
label: string;
|
|
151
|
+
provider: string;
|
|
152
|
+
streaming: boolean;
|
|
153
|
+
}
|
|
154
|
+
interface HostedAiService {
|
|
155
|
+
chatStream(params: HostedAiChatParams, signal?: AbortSignal): AsyncIterable<StreamChunk>;
|
|
156
|
+
chat(params: HostedAiChatParams, signal?: AbortSignal): Promise<HostedAiChatResult>;
|
|
157
|
+
getQuota(workspaceId: string): Promise<AiQuota>;
|
|
158
|
+
listAvailableModels(): Promise<HostedAiModel[]>;
|
|
159
|
+
}
|
|
160
|
+
//#endregion
|
|
161
|
+
//#region src/pro-contract/sso.d.ts
|
|
162
|
+
interface SsoProvider {
|
|
163
|
+
id: string;
|
|
164
|
+
type: 'saml' | 'oidc' | 'google' | 'okta' | 'azure';
|
|
165
|
+
name: string;
|
|
166
|
+
configured: boolean;
|
|
167
|
+
}
|
|
168
|
+
interface SsoService {
|
|
169
|
+
listProviders(workspaceId: string): Promise<SsoProvider[]>;
|
|
170
|
+
initiateLogin(providerId: string, returnUrl: string): Promise<{
|
|
171
|
+
url: string;
|
|
172
|
+
}>;
|
|
173
|
+
handleCallback(providerId: string, code: string, state: string): Promise<{
|
|
174
|
+
userId: string;
|
|
175
|
+
workspaceId: string;
|
|
176
|
+
}>;
|
|
177
|
+
}
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/pro-contract/audit.d.ts
|
|
180
|
+
interface AuditLogEntry {
|
|
181
|
+
id: string;
|
|
182
|
+
workspaceId: string;
|
|
183
|
+
userId: string;
|
|
184
|
+
action: string;
|
|
185
|
+
resourceType: string;
|
|
186
|
+
resourceId: string;
|
|
187
|
+
metadata: Record<string, unknown>;
|
|
188
|
+
ip: string | null;
|
|
189
|
+
userAgent: string | null;
|
|
190
|
+
createdAt: Date;
|
|
191
|
+
}
|
|
192
|
+
interface AuditQuery {
|
|
193
|
+
workspaceId: string;
|
|
194
|
+
userId?: string;
|
|
195
|
+
action?: string;
|
|
196
|
+
startDate?: Date;
|
|
197
|
+
endDate?: Date;
|
|
198
|
+
limit?: number;
|
|
199
|
+
offset?: number;
|
|
200
|
+
}
|
|
201
|
+
interface AuditService {
|
|
202
|
+
log(entry: Omit<AuditLogEntry, 'id' | 'createdAt'>): Promise<void>;
|
|
203
|
+
query(q: AuditQuery): Promise<{
|
|
204
|
+
entries: AuditLogEntry[];
|
|
205
|
+
total: number;
|
|
206
|
+
}>;
|
|
207
|
+
export(workspaceId: string, format: 'csv' | 'json'): Promise<{
|
|
208
|
+
url: string;
|
|
209
|
+
}>;
|
|
210
|
+
}
|
|
211
|
+
//#endregion
|
|
212
|
+
//#region src/pro-contract/permissions.d.ts
|
|
213
|
+
type PermissionLevel = 'none' | 'read' | 'write' | 'admin';
|
|
214
|
+
interface RoleDefinition {
|
|
215
|
+
id: string;
|
|
216
|
+
name: string;
|
|
217
|
+
workspaceId: string;
|
|
218
|
+
permissions: Record<string, PermissionLevel>;
|
|
219
|
+
isSystem: boolean;
|
|
220
|
+
}
|
|
221
|
+
interface PermissionsService {
|
|
222
|
+
listRoles(workspaceId: string): Promise<RoleDefinition[]>;
|
|
223
|
+
createRole(workspaceId: string, name: string, permissions: Record<string, PermissionLevel>): Promise<RoleDefinition>;
|
|
224
|
+
updateRole(roleId: string, updates: Partial<RoleDefinition>): Promise<void>;
|
|
225
|
+
deleteRole(roleId: string): Promise<void>;
|
|
226
|
+
checkPermission(userId: string, resource: string, action: string): Promise<PermissionLevel>;
|
|
227
|
+
}
|
|
228
|
+
//#endregion
|
|
229
|
+
//#region src/pro-contract/license.d.ts
|
|
230
|
+
interface LicenseInfo {
|
|
231
|
+
key: string;
|
|
232
|
+
customerName: string;
|
|
233
|
+
customerEmail: string;
|
|
234
|
+
plan: string;
|
|
235
|
+
seats: number;
|
|
236
|
+
features: Record<string, boolean>;
|
|
237
|
+
issuedAt: Date;
|
|
238
|
+
expiresAt: Date | null;
|
|
239
|
+
}
|
|
240
|
+
interface LicenseStatus {
|
|
241
|
+
valid: boolean;
|
|
242
|
+
reason: 'ok' | 'expired' | 'revoked' | 'invalid_signature' | 'not_activated' | 'fingerprint_mismatch';
|
|
243
|
+
license: LicenseInfo | null;
|
|
244
|
+
daysUntilExpiry: number | null;
|
|
245
|
+
inGracePeriod: boolean;
|
|
246
|
+
}
|
|
247
|
+
interface ActivationResult {
|
|
248
|
+
success: boolean;
|
|
249
|
+
message: string;
|
|
250
|
+
instanceId?: string;
|
|
251
|
+
}
|
|
252
|
+
interface LicenseIssueParams {
|
|
253
|
+
customerName: string;
|
|
254
|
+
customerEmail: string;
|
|
255
|
+
customerId: string;
|
|
256
|
+
plan: string;
|
|
257
|
+
seats: number;
|
|
258
|
+
features: Record<string, boolean>;
|
|
259
|
+
expiresAt: Date | null;
|
|
260
|
+
}
|
|
261
|
+
interface LicenseIssueResult {
|
|
262
|
+
/** 完整 license key(base64url(payload).base64url(signature),仅签发时返回一次) */
|
|
263
|
+
key: string;
|
|
264
|
+
signature: string;
|
|
265
|
+
payload: {
|
|
266
|
+
customerName: string;
|
|
267
|
+
customerEmail: string;
|
|
268
|
+
customerId: string;
|
|
269
|
+
plan: string;
|
|
270
|
+
seats: number;
|
|
271
|
+
features: Record<string, boolean>;
|
|
272
|
+
issuedAt: string;
|
|
273
|
+
expiresAt: string | null;
|
|
274
|
+
nonce: string;
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
interface LicenseService {
|
|
278
|
+
activate(key: string, instanceFingerprint: string): Promise<ActivationResult>;
|
|
279
|
+
verify(): Promise<LicenseStatus>;
|
|
280
|
+
revoke(instanceId: string): Promise<void>;
|
|
281
|
+
issue?(params: LicenseIssueParams): Promise<LicenseIssueResult>;
|
|
282
|
+
heartbeat?(instanceId: string): Promise<{
|
|
283
|
+
ok: boolean;
|
|
284
|
+
revoked: boolean;
|
|
285
|
+
}>;
|
|
286
|
+
}
|
|
287
|
+
//#endregion
|
|
288
|
+
//#region src/pro-contract/version-history.d.ts
|
|
289
|
+
interface DocumentVersion {
|
|
290
|
+
id: string;
|
|
291
|
+
documentId: string;
|
|
292
|
+
versionNumber: number;
|
|
293
|
+
content: string;
|
|
294
|
+
createdBy: string;
|
|
295
|
+
createdAt: Date;
|
|
296
|
+
changeSummary: string | null;
|
|
297
|
+
}
|
|
298
|
+
interface VersionHistoryService {
|
|
299
|
+
saveVersion(documentId: string, content: string, changeSummary?: string): Promise<DocumentVersion>;
|
|
300
|
+
listVersions(documentId: string, limit?: number): Promise<DocumentVersion[]>;
|
|
301
|
+
getVersion(versionId: string): Promise<DocumentVersion | null>;
|
|
302
|
+
restoreVersion(versionId: string): Promise<void>;
|
|
303
|
+
diffVersions(versionIdA: string, versionIdB: string): Promise<{
|
|
304
|
+
additions: number;
|
|
305
|
+
deletions: number;
|
|
306
|
+
content: string;
|
|
307
|
+
}>;
|
|
308
|
+
}
|
|
309
|
+
//#endregion
|
|
310
|
+
//#region src/pro-contract/automation.d.ts
|
|
311
|
+
interface AutomationRule {
|
|
312
|
+
id: string;
|
|
313
|
+
workspaceId: string;
|
|
314
|
+
name: string;
|
|
315
|
+
trigger: {
|
|
316
|
+
type: 'document_created' | 'status_changed' | 'schedule';
|
|
317
|
+
config: Record<string, unknown>;
|
|
318
|
+
};
|
|
319
|
+
action: {
|
|
320
|
+
type: 'ai_classify' | 'ai_summarize' | 'assign' | 'notify';
|
|
321
|
+
config: Record<string, unknown>;
|
|
322
|
+
};
|
|
323
|
+
enabled: boolean;
|
|
324
|
+
}
|
|
325
|
+
interface AutomationService {
|
|
326
|
+
listRules(workspaceId: string): Promise<AutomationRule[]>;
|
|
327
|
+
createRule(workspaceId: string, rule: Omit<AutomationRule, 'id' | 'workspaceId'>): Promise<AutomationRule>;
|
|
328
|
+
updateRule(ruleId: string, updates: Partial<AutomationRule>): Promise<void>;
|
|
329
|
+
deleteRule(ruleId: string): Promise<void>;
|
|
330
|
+
triggerRule(ruleId: string, context: Record<string, unknown>): Promise<{
|
|
331
|
+
success: boolean;
|
|
332
|
+
result: unknown;
|
|
333
|
+
}>;
|
|
334
|
+
}
|
|
335
|
+
//#endregion
|
|
336
|
+
//#region src/pro-contract/templates.d.ts
|
|
337
|
+
interface DocumentTemplate {
|
|
338
|
+
id: string;
|
|
339
|
+
workspaceId: string | null;
|
|
340
|
+
name: string;
|
|
341
|
+
description: string;
|
|
342
|
+
category: string;
|
|
343
|
+
content: string;
|
|
344
|
+
icon: string;
|
|
345
|
+
createdBy: string;
|
|
346
|
+
createdAt: Date;
|
|
347
|
+
}
|
|
348
|
+
interface TemplatesService {
|
|
349
|
+
listTemplates(workspaceId: string | null): Promise<DocumentTemplate[]>;
|
|
350
|
+
createTemplate(workspaceId: string | null, template: Omit<DocumentTemplate, 'id' | 'createdAt'>): Promise<DocumentTemplate>;
|
|
351
|
+
updateTemplate(templateId: string, updates: Partial<DocumentTemplate>): Promise<void>;
|
|
352
|
+
deleteTemplate(templateId: string): Promise<void>;
|
|
353
|
+
instantiate(templateId: string, name: string): Promise<{
|
|
354
|
+
documentId: string;
|
|
355
|
+
}>;
|
|
356
|
+
}
|
|
357
|
+
//#endregion
|
|
358
|
+
//#region src/pro-contract/analytics.d.ts
|
|
359
|
+
interface AnalyticsQuery {
|
|
360
|
+
workspaceId: string;
|
|
361
|
+
metric: string;
|
|
362
|
+
groupBy?: string;
|
|
363
|
+
startDate: Date;
|
|
364
|
+
endDate: Date;
|
|
365
|
+
}
|
|
366
|
+
interface AnalyticsResult {
|
|
367
|
+
metric: string;
|
|
368
|
+
dataPoints: Array<{
|
|
369
|
+
label: string;
|
|
370
|
+
value: number;
|
|
371
|
+
date: Date;
|
|
372
|
+
}>;
|
|
373
|
+
summary: {
|
|
374
|
+
total: number;
|
|
375
|
+
average: number;
|
|
376
|
+
trend: 'up' | 'down' | 'flat';
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
interface CustomDashboard {
|
|
380
|
+
id: string;
|
|
381
|
+
workspaceId: string;
|
|
382
|
+
name: string;
|
|
383
|
+
widgets: Array<{
|
|
384
|
+
id: string;
|
|
385
|
+
type: string;
|
|
386
|
+
query: AnalyticsQuery;
|
|
387
|
+
title: string;
|
|
388
|
+
}>;
|
|
389
|
+
}
|
|
390
|
+
interface AnalyticsService {
|
|
391
|
+
query(q: AnalyticsQuery): Promise<AnalyticsResult>;
|
|
392
|
+
listDashboards(workspaceId: string): Promise<CustomDashboard[]>;
|
|
393
|
+
createDashboard(workspaceId: string, dashboard: Omit<CustomDashboard, 'id'>): Promise<CustomDashboard>;
|
|
394
|
+
exportData(workspaceId: string, format: 'csv' | 'xlsx' | 'json'): Promise<{
|
|
395
|
+
url: string;
|
|
396
|
+
}>;
|
|
397
|
+
}
|
|
398
|
+
//#endregion
|
|
399
|
+
//#region src/pro-contract/global-search.d.ts
|
|
400
|
+
interface SearchResult {
|
|
401
|
+
id: string;
|
|
402
|
+
type: 'document' | 'project' | 'story' | 'comment' | 'user' | 'tag';
|
|
403
|
+
title: string;
|
|
404
|
+
snippet: string;
|
|
405
|
+
workspaceId: string;
|
|
406
|
+
url: string;
|
|
407
|
+
score: number;
|
|
408
|
+
highlightedAt: Date;
|
|
409
|
+
}
|
|
410
|
+
interface SearchParams {
|
|
411
|
+
query: string;
|
|
412
|
+
workspaceId?: string | null;
|
|
413
|
+
types?: Array<SearchResult['type']>;
|
|
414
|
+
limit?: number;
|
|
415
|
+
offset?: number;
|
|
416
|
+
}
|
|
417
|
+
interface GlobalSearchService {
|
|
418
|
+
search(params: SearchParams): Promise<{
|
|
419
|
+
results: SearchResult[];
|
|
420
|
+
total: number;
|
|
421
|
+
}>;
|
|
422
|
+
index(documentId: string, content: string, metadata: Record<string, unknown>): Promise<void>;
|
|
423
|
+
rebuildIndex(workspaceId?: string): Promise<{
|
|
424
|
+
indexed: number;
|
|
425
|
+
}>;
|
|
426
|
+
}
|
|
427
|
+
//#endregion
|
|
428
|
+
//#region src/pro-contract/compliance.d.ts
|
|
429
|
+
interface ComplianceJobSummary {
|
|
430
|
+
id: string;
|
|
431
|
+
type: 'export' | 'delete';
|
|
432
|
+
scope: 'user' | 'workspace';
|
|
433
|
+
targetId: string;
|
|
434
|
+
status: 'pending' | 'processing' | 'completed' | 'failed';
|
|
435
|
+
downloadUrl: string | null;
|
|
436
|
+
expiresAt: Date | null;
|
|
437
|
+
error: string | null;
|
|
438
|
+
requestedBy: string | null;
|
|
439
|
+
createdAt: Date;
|
|
440
|
+
completedAt: Date | null;
|
|
441
|
+
}
|
|
442
|
+
interface ComplianceService {
|
|
443
|
+
createExportJob(scope: 'user' | 'workspace', targetId: string, requestedBy?: string): Promise<{
|
|
444
|
+
id: string;
|
|
445
|
+
}>;
|
|
446
|
+
createDeleteJob(scope: 'user' | 'workspace', targetId: string, requestedBy?: string): Promise<{
|
|
447
|
+
id: string;
|
|
448
|
+
}>;
|
|
449
|
+
processJob(jobId: string): Promise<{
|
|
450
|
+
status: ComplianceJobSummary['status'];
|
|
451
|
+
error?: string;
|
|
452
|
+
}>;
|
|
453
|
+
listJobs(opts: {
|
|
454
|
+
status?: ComplianceJobSummary['status'];
|
|
455
|
+
page?: number;
|
|
456
|
+
pageSize?: number;
|
|
457
|
+
}): Promise<{
|
|
458
|
+
items: ComplianceJobSummary[];
|
|
459
|
+
total: number;
|
|
460
|
+
}>;
|
|
461
|
+
}
|
|
462
|
+
//#endregion
|
|
463
|
+
//#region src/pro-contract/module.d.ts
|
|
464
|
+
type ProEdition = 'oss' | 'pro';
|
|
465
|
+
type ProFeature = 'billing' | 'hostedAi' | 'sso' | 'audit' | 'permissions' | 'license' | 'versionHistory' | 'automation' | 'templates' | 'analytics' | 'globalSearch' | 'compliance';
|
|
466
|
+
interface ProModule {
|
|
467
|
+
readonly isAvailable: boolean;
|
|
468
|
+
readonly edition: ProEdition;
|
|
469
|
+
readonly version: string;
|
|
470
|
+
readonly billing: BillingService | null;
|
|
471
|
+
readonly hostedAi: HostedAiService | null;
|
|
472
|
+
readonly sso: SsoService | null;
|
|
473
|
+
readonly audit: AuditService | null;
|
|
474
|
+
readonly permissions: PermissionsService | null;
|
|
475
|
+
readonly license: LicenseService | null;
|
|
476
|
+
readonly versionHistory: VersionHistoryService | null;
|
|
477
|
+
readonly automation: AutomationService | null;
|
|
478
|
+
readonly templates: TemplatesService | null;
|
|
479
|
+
readonly analytics: AnalyticsService | null;
|
|
480
|
+
readonly globalSearch: GlobalSearchService | null;
|
|
481
|
+
readonly compliance: ComplianceService | null;
|
|
482
|
+
}
|
|
483
|
+
//#endregion
|
|
2
484
|
//#region src/contributions.d.ts
|
|
3
|
-
type
|
|
485
|
+
type Middleware = unknown;
|
|
486
|
+
type PluginRouteContext = {
|
|
487
|
+
params: Readonly<Record<string, string>>;
|
|
488
|
+
search?: unknown;
|
|
489
|
+
};
|
|
4
490
|
interface RouteContribution {
|
|
5
491
|
path: string;
|
|
6
|
-
|
|
492
|
+
beforeLoad?: (ctx: PluginRouteContext) => unknown;
|
|
493
|
+
loader?: (ctx: PluginRouteContext) => unknown;
|
|
494
|
+
component: ComponentType;
|
|
495
|
+
validateSearch?: (search: Record<string, unknown>) => Record<string, unknown>;
|
|
7
496
|
}
|
|
8
497
|
type QueryFactoryMap = Record<string, () => unknown>;
|
|
9
|
-
|
|
498
|
+
interface SettingsTabContribution {
|
|
499
|
+
labelKey: string;
|
|
500
|
+
path: string;
|
|
501
|
+
icon: ComponentType;
|
|
502
|
+
group?: string;
|
|
503
|
+
}
|
|
504
|
+
interface UiSlotContributions {
|
|
505
|
+
navItems?: Record<string, ComponentType>;
|
|
506
|
+
settingsTabs?: SettingsTabContribution[];
|
|
507
|
+
dashboardWidgets?: Record<string, ComponentType>;
|
|
508
|
+
}
|
|
10
509
|
interface FeatureContribution {
|
|
11
510
|
key: string;
|
|
12
|
-
tier:
|
|
13
|
-
module:
|
|
511
|
+
tier: 'oss' | 'paid' | 'enterprise';
|
|
512
|
+
module: ProFeature | null;
|
|
14
513
|
}
|
|
514
|
+
type ProServiceMap = Partial<Record<ProFeature, unknown>>;
|
|
15
515
|
interface PrismaContribution {
|
|
16
516
|
models: string[];
|
|
17
517
|
}
|
|
18
518
|
interface PluginContributions {
|
|
19
519
|
routes?: RouteContribution[];
|
|
20
|
-
serverMiddleware?:
|
|
520
|
+
serverMiddleware?: Middleware[];
|
|
21
521
|
queryOptions?: QueryFactoryMap;
|
|
22
|
-
uiSlots?:
|
|
522
|
+
uiSlots?: UiSlotContributions;
|
|
23
523
|
features?: FeatureContribution[];
|
|
24
524
|
aiTools?: unknown[];
|
|
25
525
|
prisma?: PrismaContribution;
|
|
526
|
+
services?: ProServiceMap;
|
|
26
527
|
}
|
|
27
528
|
//#endregion
|
|
28
529
|
//#region src/types.d.ts
|
|
@@ -50,6 +551,17 @@ interface ResolveResult {
|
|
|
50
551
|
}
|
|
51
552
|
declare function resolvePluginOrder(plugins: PluginDefinition[]): ResolveResult;
|
|
52
553
|
//#endregion
|
|
554
|
+
//#region src/assemble-input.d.ts
|
|
555
|
+
declare function parseProfileImports(source: string): Map<string, string>;
|
|
556
|
+
interface AssembleInput {
|
|
557
|
+
profileId: string;
|
|
558
|
+
profileModulePath: string;
|
|
559
|
+
resolved: ResolveResult;
|
|
560
|
+
imports: Map<string, string>;
|
|
561
|
+
pluginLocalNames: string[];
|
|
562
|
+
}
|
|
563
|
+
declare function resolveImportSpecifiers(input: AssembleInput): Map<string, string>;
|
|
564
|
+
//#endregion
|
|
53
565
|
//#region src/generate-assembly.d.ts
|
|
54
566
|
interface GenerateAssemblyInput {
|
|
55
567
|
profileId: string;
|
|
@@ -57,8 +569,33 @@ interface GenerateAssemblyInput {
|
|
|
57
569
|
resolved: ResolveResult;
|
|
58
570
|
}
|
|
59
571
|
declare function generateAssemblyCode(input: GenerateAssemblyInput): string;
|
|
572
|
+
interface AssemblyFiles {
|
|
573
|
+
pluginsGen: string;
|
|
574
|
+
modulesGen: string;
|
|
575
|
+
uiGen: string;
|
|
576
|
+
routeFiles: Map<string, string>;
|
|
577
|
+
}
|
|
578
|
+
declare function generateAssemblyFiles(input: AssembleInput): AssemblyFiles;
|
|
60
579
|
//#endregion
|
|
61
580
|
//#region src/dump-modules.d.ts
|
|
62
581
|
declare function dumpModules(profileId: string, resolved: ResolveResult): string;
|
|
63
582
|
//#endregion
|
|
64
|
-
|
|
583
|
+
//#region src/server-seam.d.ts
|
|
584
|
+
interface HostSession {
|
|
585
|
+
user?: {
|
|
586
|
+
id?: string;
|
|
587
|
+
role?: string | null;
|
|
588
|
+
[key: string]: unknown;
|
|
589
|
+
} | null;
|
|
590
|
+
[key: string]: unknown;
|
|
591
|
+
}
|
|
592
|
+
interface ServerSeam {
|
|
593
|
+
requireAuth: (request?: Request) => Promise<HostSession>;
|
|
594
|
+
requireUserInDb: (userId: string) => Promise<void>;
|
|
595
|
+
getCurrentSession: (request?: Request) => Promise<HostSession | null>;
|
|
596
|
+
isDesktop: () => boolean;
|
|
597
|
+
}
|
|
598
|
+
declare function registerServerSeam(impl: ServerSeam): void;
|
|
599
|
+
declare function getServerSeam(): ServerSeam;
|
|
600
|
+
//#endregion
|
|
601
|
+
export { type ActivationResult, type AiQuota, type AnalyticsQuery, type AnalyticsResult, type AnalyticsService, type AssembleInput, type AssemblyFiles, type AuditLogEntry, type AuditQuery, type AuditService, type AutomationRule, type AutomationService, type BillingService, BillingUnavailableError, type CheckoutParams, type ComplianceJobSummary, type ComplianceService, type CustomDashboard, type DocumentTemplate, type DocumentVersion, type FeatureContribution, type GlobalSearchService, type HostSession, type HostedAiChatParams, type HostedAiChatResult, HostedAiError, type HostedAiModel, type HostedAiService, type InvoiceInfo, type LicenseInfo, type LicenseIssueParams, type LicenseIssueResult, type LicenseService, type LicenseStatus, type Middleware, type PermissionLevel, type PermissionsService, type PlanInfo, type PluginContributions, type PluginDefinition, type PluginRouteContext, type PortalSessionParams, type PrismaContribution, type ProEdition, type ProFeature, type ProModule, type ProServiceMap, type ProfileDefinition, type QueryFactoryMap, type ResolveResult, type RoleDefinition, type RouteContribution, type SearchParams, type SearchResult, type SeatCheckResult, SeatsExceededError, type ServerSeam, type SettingsTabContribution, type SsoProvider, type SsoService, type SubscriptionInfo, type TemplatesService, type UiSlotContributions, type VersionHistoryService, WebhookSignatureError, definePlugin, defineProfile, dumpModules, generateAssemblyCode, generateAssemblyFiles, getServerSeam, parseProfileImports, registerServerSeam, resolveImportSpecifiers, resolvePluginOrder };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as
|
|
2
|
-
export { definePlugin, defineProfile, dumpModules, generateAssemblyCode, resolvePluginOrder };
|
|
1
|
+
import { a as SeatsExceededError, c as generateAssemblyCode, d as resolveImportSpecifiers, f as resolvePluginOrder, i as BillingUnavailableError, l as generateAssemblyFiles, m as definePlugin, n as registerServerSeam, o as WebhookSignatureError, p as defineProfile, r as HostedAiError, s as dumpModules, t as getServerSeam, u as parseProfileImports } from "./src-B6_ZEkl9.js";
|
|
2
|
+
export { BillingUnavailableError, HostedAiError, SeatsExceededError, WebhookSignatureError, definePlugin, defineProfile, dumpModules, generateAssemblyCode, generateAssemblyFiles, getServerSeam, parseProfileImports, registerServerSeam, resolveImportSpecifiers, resolvePluginOrder };
|
|
@@ -0,0 +1,316 @@
|
|
|
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/assemble-input.ts
|
|
87
|
+
function parseProfileImports(source) {
|
|
88
|
+
const map = /* @__PURE__ */ new Map();
|
|
89
|
+
const re = /import\s+([A-Za-z_$][\w$]*)\s+from\s+['"]([^'"]+)['"]/g;
|
|
90
|
+
let m;
|
|
91
|
+
while ((m = re.exec(source)) !== null) map.set(m[1], m[2]);
|
|
92
|
+
return map;
|
|
93
|
+
}
|
|
94
|
+
function resolveImportSpecifiers(input) {
|
|
95
|
+
const byId = /* @__PURE__ */ new Map();
|
|
96
|
+
input.resolved.order.forEach((plugin, index) => {
|
|
97
|
+
const localName = input.pluginLocalNames[index];
|
|
98
|
+
if (!localName) throw new Error(`插件 ${plugin.id} 在 profile 源码中找不到对应标识符`);
|
|
99
|
+
const specifier = input.imports.get(localName);
|
|
100
|
+
if (!specifier) throw new Error(`标识符 ${localName} 没有 default import(约定:profile 中插件必须以 default import 引入)`);
|
|
101
|
+
byId.set(plugin.id, specifier);
|
|
102
|
+
});
|
|
103
|
+
return byId;
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/generate-assembly.ts
|
|
107
|
+
function generateAssemblyCode(input) {
|
|
108
|
+
const { profileId, profileModulePath, resolved } = input;
|
|
109
|
+
const ids = resolved.ids.map((id) => `'${id}'`).join(", ");
|
|
110
|
+
return [
|
|
111
|
+
`// AUTO-GENERATED by @allbluecn/kernel — DO NOT EDIT`,
|
|
112
|
+
`// profile: ${profileId} (validated: ${resolved.order.length} plugins)`,
|
|
113
|
+
`// 拓扑序(被依赖者在前):${resolved.ids.join(" -> ")}`,
|
|
114
|
+
`import type { ProfileDefinition } from '@allbluecn/kernel'`,
|
|
115
|
+
``,
|
|
116
|
+
`export { default as profile } from '${profileModulePath}'`,
|
|
117
|
+
``,
|
|
118
|
+
`export const pluginIds: string[] = [${ids}]`,
|
|
119
|
+
``,
|
|
120
|
+
`declare const _profile: ProfileDefinition`
|
|
121
|
+
].join("\n");
|
|
122
|
+
}
|
|
123
|
+
const HEADER = "// AUTO-GENERATED by @allbluecn/kernel — DO NOT EDIT";
|
|
124
|
+
const ALL_FEATURES = [
|
|
125
|
+
"billing",
|
|
126
|
+
"hostedAi",
|
|
127
|
+
"sso",
|
|
128
|
+
"audit",
|
|
129
|
+
"permissions",
|
|
130
|
+
"license",
|
|
131
|
+
"versionHistory",
|
|
132
|
+
"automation",
|
|
133
|
+
"templates",
|
|
134
|
+
"analytics",
|
|
135
|
+
"globalSearch",
|
|
136
|
+
"compliance"
|
|
137
|
+
];
|
|
138
|
+
function generateAssemblyFiles(input) {
|
|
139
|
+
const specifiers = resolveImportSpecifiers(input);
|
|
140
|
+
const { resolved } = input;
|
|
141
|
+
const importLines = resolved.order.map((p, i) => {
|
|
142
|
+
return `import ${input.pluginLocalNames[i]} from '${specifiers.get(p.id)}'`;
|
|
143
|
+
});
|
|
144
|
+
const pluginList = input.pluginLocalNames.slice(0, resolved.order.length).join(", ");
|
|
145
|
+
const middlewareParts = resolved.order.map((p, i) => {
|
|
146
|
+
return ` ...${input.pluginLocalNames[i]}.contributes.serverMiddleware ?? [],`;
|
|
147
|
+
}).join("\n");
|
|
148
|
+
const pluginsGen = [
|
|
149
|
+
HEADER,
|
|
150
|
+
`// profile: ${input.profileId} (validated: ${resolved.order.length} plugins)`,
|
|
151
|
+
`// 拓扑序:${resolved.ids.join(" -> ")}`,
|
|
152
|
+
...importLines,
|
|
153
|
+
`import { defineProfile, type ProfileDefinition } from '@allbluecn/kernel'`,
|
|
154
|
+
``,
|
|
155
|
+
`export const profile: ProfileDefinition = defineProfile({`,
|
|
156
|
+
` id: '${input.profileId}',`,
|
|
157
|
+
` plugins: [${pluginList}],`,
|
|
158
|
+
`})`,
|
|
159
|
+
``,
|
|
160
|
+
`export const pluginIds: string[] = [${resolved.ids.map((i) => `'${i}'`).join(", ")}]`,
|
|
161
|
+
``,
|
|
162
|
+
`export const pluginMiddleware: unknown[] = [`,
|
|
163
|
+
middlewareParts,
|
|
164
|
+
`]`
|
|
165
|
+
].join("\n");
|
|
166
|
+
const implLines = ALL_FEATURES.map((f) => {
|
|
167
|
+
return ` ${f}: ${resolved.order.some((p) => p.contributes.services?.[f] !== void 0)},`;
|
|
168
|
+
});
|
|
169
|
+
const modulesGen = [
|
|
170
|
+
HEADER,
|
|
171
|
+
`import type { ProFeature } from '@allbluecn/kernel'`,
|
|
172
|
+
``,
|
|
173
|
+
`export const activePluginIds = [${resolved.ids.map((i) => `'${i}'`).join(", ")}] as const`,
|
|
174
|
+
`export type ActivePluginId = (typeof activePluginIds)[number]`,
|
|
175
|
+
``,
|
|
176
|
+
`export const implementedModules: Record<ProFeature, boolean> = {`,
|
|
177
|
+
...implLines,
|
|
178
|
+
`}`
|
|
179
|
+
].join("\n");
|
|
180
|
+
const uiImports = [];
|
|
181
|
+
const navItems = [];
|
|
182
|
+
const settingsTabs = [];
|
|
183
|
+
const widgets = [];
|
|
184
|
+
for (let i = 0; i < resolved.order.length; i++) {
|
|
185
|
+
const p = resolved.order[i];
|
|
186
|
+
const local = input.pluginLocalNames[i];
|
|
187
|
+
const spec = specifiers.get(p.id);
|
|
188
|
+
const slots = p.contributes.uiSlots;
|
|
189
|
+
if (!slots) continue;
|
|
190
|
+
for (const [key] of Object.entries(slots.navItems ?? {})) {
|
|
191
|
+
uiImports.push(`import { ${key} } from '${spec}/ui'`);
|
|
192
|
+
navItems.push(key);
|
|
193
|
+
}
|
|
194
|
+
if (slots.settingsTabs?.length) {
|
|
195
|
+
uiImports.push(`import { ${local}SettingsTabs } from '${spec}/ui'`);
|
|
196
|
+
settingsTabs.push(`...${local}SettingsTabs`);
|
|
197
|
+
}
|
|
198
|
+
for (const [key] of Object.entries(slots.dashboardWidgets ?? {})) {
|
|
199
|
+
uiImports.push(`import { ${key} } from '${spec}/ui'`);
|
|
200
|
+
widgets.push(key);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
const uiGen = [
|
|
204
|
+
HEADER,
|
|
205
|
+
...uiImports,
|
|
206
|
+
`import { type ComponentType } from 'react'`,
|
|
207
|
+
`import type { SettingsTabContribution } from '@allbluecn/kernel'`,
|
|
208
|
+
``,
|
|
209
|
+
`export const navItems: ComponentType[] = [${navItems.join(", ")}]`,
|
|
210
|
+
`export const settingsTabs: SettingsTabContribution[] = [${settingsTabs.join(", ")}]`,
|
|
211
|
+
`export const dashboardWidgets: ComponentType[] = [${widgets.join(", ")}]`
|
|
212
|
+
].join("\n");
|
|
213
|
+
const routeFiles = /* @__PURE__ */ new Map();
|
|
214
|
+
for (let i = 0; i < resolved.order.length; i++) {
|
|
215
|
+
const p = resolved.order[i];
|
|
216
|
+
const spec = specifiers.get(p.id);
|
|
217
|
+
p.contributes.routes?.forEach((r, j) => {
|
|
218
|
+
const file = routeContributionToFile(r.path);
|
|
219
|
+
const varName = `${input.pluginLocalNames[i]}_route_${j}`;
|
|
220
|
+
const routePath = r.path.endsWith("/") ? r.path.slice(0, -1) : r.path;
|
|
221
|
+
const lines = [
|
|
222
|
+
HEADER,
|
|
223
|
+
`import { createFileRoute } from '@tanstack/react-router'`,
|
|
224
|
+
`import type { PluginRouteContext } from '@allbluecn/kernel'`,
|
|
225
|
+
`import { ${varName} } from '${spec}/routes'`,
|
|
226
|
+
``,
|
|
227
|
+
`export const Route = createFileRoute('${routePath}')({`,
|
|
228
|
+
...r.beforeLoad ? [` beforeLoad: ((ctx: PluginRouteContext) => ${varName}.beforeLoad(ctx)) as any,`] : [],
|
|
229
|
+
...r.loader ? [` loader: ((ctx: PluginRouteContext) => ${varName}.loader(ctx)) as any,`] : [],
|
|
230
|
+
...r.validateSearch ? [` validateSearch: ${varName}.validateSearch as any,`] : [],
|
|
231
|
+
` component: ${varName}.Component as any,`,
|
|
232
|
+
`})`
|
|
233
|
+
];
|
|
234
|
+
routeFiles.set(file, lines.join("\n"));
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
pluginsGen,
|
|
239
|
+
modulesGen,
|
|
240
|
+
uiGen,
|
|
241
|
+
routeFiles
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function routeContributionToFile(path) {
|
|
245
|
+
if (path.endsWith("/")) return `src/routes${path.slice(0, -1) || ""}/index.tsx`;
|
|
246
|
+
return `src/routes${path}/route.tsx`;
|
|
247
|
+
}
|
|
248
|
+
//#endregion
|
|
249
|
+
//#region src/dump-modules.ts
|
|
250
|
+
function dumpModules(profileId, resolved) {
|
|
251
|
+
const lines = [`profile: ${profileId} (${resolved.order.length} plugins)`];
|
|
252
|
+
for (const plugin of resolved.order) {
|
|
253
|
+
const c = plugin.contributes;
|
|
254
|
+
const parts = [];
|
|
255
|
+
if (c.routes?.length) parts.push(`routes:${c.routes.length}`);
|
|
256
|
+
if (c.serverMiddleware?.length) parts.push(`middleware:${c.serverMiddleware.length}`);
|
|
257
|
+
if (c.queryOptions && Object.keys(c.queryOptions).length) parts.push(`queries:${Object.keys(c.queryOptions).length}`);
|
|
258
|
+
if (c.uiSlots && Object.keys(c.uiSlots).length) parts.push(`ui-slots:${Object.keys(c.uiSlots).length}`);
|
|
259
|
+
if (c.features?.length) parts.push(`features:${c.features.length}`);
|
|
260
|
+
if (c.aiTools?.length) parts.push(`ai-tools:${c.aiTools.length}`);
|
|
261
|
+
if (c.prisma?.models.length) parts.push(`prisma-models:${c.prisma.models.length}`);
|
|
262
|
+
lines.push(`${plugin.id}@${plugin.version} — ${parts.length ? parts.join(" ") : "no contributions"}`);
|
|
263
|
+
}
|
|
264
|
+
return lines.join("\n");
|
|
265
|
+
}
|
|
266
|
+
//#endregion
|
|
267
|
+
//#region src/pro-contract/billing.ts
|
|
268
|
+
var BillingUnavailableError = class BillingUnavailableError extends Error {
|
|
269
|
+
constructor(message = "计费模块不可用") {
|
|
270
|
+
super(message);
|
|
271
|
+
this.name = "BillingUnavailableError";
|
|
272
|
+
Object.setPrototypeOf(this, BillingUnavailableError.prototype);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
var WebhookSignatureError = class WebhookSignatureError extends Error {
|
|
276
|
+
constructor(message = "Webhook 签名验证失败") {
|
|
277
|
+
super(message);
|
|
278
|
+
this.name = "WebhookSignatureError";
|
|
279
|
+
Object.setPrototypeOf(this, WebhookSignatureError.prototype);
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
var SeatsExceededError = class SeatsExceededError extends Error {
|
|
283
|
+
current;
|
|
284
|
+
max;
|
|
285
|
+
constructor(current, max) {
|
|
286
|
+
super(`席位不足:当前 ${current}/${max}`);
|
|
287
|
+
this.name = "SeatsExceededError";
|
|
288
|
+
this.current = current;
|
|
289
|
+
this.max = max;
|
|
290
|
+
Object.setPrototypeOf(this, SeatsExceededError.prototype);
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
//#endregion
|
|
294
|
+
//#region src/pro-contract/hosted-ai.ts
|
|
295
|
+
var HostedAiError = class extends Error {
|
|
296
|
+
code;
|
|
297
|
+
details;
|
|
298
|
+
constructor(code, message, details) {
|
|
299
|
+
super(message);
|
|
300
|
+
this.code = code;
|
|
301
|
+
this.details = details;
|
|
302
|
+
this.name = "HostedAiError";
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
//#endregion
|
|
306
|
+
//#region src/server-seam.ts
|
|
307
|
+
let seam = null;
|
|
308
|
+
function registerServerSeam(impl) {
|
|
309
|
+
seam = impl;
|
|
310
|
+
}
|
|
311
|
+
function getServerSeam() {
|
|
312
|
+
if (!seam) throw new Error("ServerSeam 未注册:宿主需在 start.ts 中调用 registerServerSeam()");
|
|
313
|
+
return seam;
|
|
314
|
+
}
|
|
315
|
+
//#endregion
|
|
316
|
+
export { SeatsExceededError as a, generateAssemblyCode as c, resolveImportSpecifiers as d, resolvePluginOrder as f, BillingUnavailableError as i, generateAssemblyFiles as l, definePlugin as m, registerServerSeam as n, WebhookSignatureError as o, defineProfile as p, HostedAiError as r, dumpModules as s, getServerSeam as t, parseProfileImports as u };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allbluecn/kernel",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"license": "AGPL-3.0-or-later",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "AllBlue 插件协议:类型定义 + 构建期装配器(零运行时)",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"dist"
|
|
20
20
|
],
|
|
21
21
|
"peerDependencies": {
|
|
22
|
-
"@tanstack/react-start": "1.168.46",
|
|
23
|
-
"@tanstack/ai": "0.43.0",
|
|
22
|
+
"@tanstack/react-start": "^1.168.46",
|
|
23
|
+
"@tanstack/ai": "^0.43.0 || ^0.47.0",
|
|
24
24
|
"react": "^19"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
package/dist/src-Dvcggyr-.js
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
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 };
|