@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 CHANGED
@@ -1,41 +1,116 @@
1
1
  #!/usr/bin/env node
2
- import { n as generateAssemblyCode, r as resolvePluginOrder, t as dumpModules } from "./src-Dvcggyr-.js";
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 (values["gen-out"]) {
27
- const outPath = resolve(values["gen-out"]);
28
- const relProfile = relImportPath(outPath, profilePath);
29
- const code = generateAssemblyCode({
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: relProfile,
32
- resolved
68
+ profileModulePath: relProfilePath,
69
+ resolved,
70
+ imports,
71
+ pluginLocalNames
33
72
  });
34
- const { writeFile } = await import("node:fs/promises");
35
- await writeFile(outPath, code + "\n", "utf8");
36
- console.log(`已生成 ${outPath} (${resolved.order.length} plugins)`);
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
- console.log(dumpModules(profile.id, resolved));
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 ServerMiddleware = unknown;
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
- route: unknown;
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
- type SlotComponentMap = Record<string, ComponentType>;
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: string;
13
- module: string;
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?: ServerMiddleware[];
520
+ serverMiddleware?: Middleware[];
21
521
  queryOptions?: QueryFactoryMap;
22
- uiSlots?: SlotComponentMap;
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
- export { type FeatureContribution, type PluginContributions, type PluginDefinition, type PrismaContribution, type ProfileDefinition, type QueryFactoryMap, type ResolveResult, type RouteContribution, type SlotComponentMap, definePlugin, defineProfile, dumpModules, generateAssemblyCode, resolvePluginOrder };
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 definePlugin, i as defineProfile, n as generateAssemblyCode, r as resolvePluginOrder, t as dumpModules } from "./src-Dvcggyr-.js";
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.2.1",
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": {
@@ -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 };