@dsz-examaware/plugin-demo 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,27 @@
1
+ # ExamAware Demo Plugin
2
+
3
+ A ready-to-run ExamAware plugin showcasing both main and renderer entries, a hosted service, and a settings page.
4
+
5
+ ## Quickstart
6
+
7
+ ```bash
8
+ pnpm install
9
+ pnpm dev # build main + renderer in watch mode
10
+ # or
11
+ pnpm build
12
+ pnpm pack # builds then packs into .ea2x
13
+ ```
14
+
15
+ In ExamAware desktop, choose "解压缩插件" and select this folder (the one containing `package.json`).
16
+
17
+ ## What it demonstrates
18
+
19
+ - Main process: exposes `hello.message` service and logs a heartbeat.
20
+ - Renderer: registers a settings page with a counter that persists via plugin settings API.
21
+ - Settings schema: `schema.json` declares the `demo.clicks` and `demo.message` fields.
22
+
23
+ ## Customize
24
+
25
+ - Update `package.json` `name`, `examaware.displayName`, and `examaware.settings.namespace` to your plugin's ID.
26
+ - Tweak `src/main/index.ts` heartbeat message or wire new services.
27
+ - Adjust the settings UI in `src/renderer/components/PluginSettingsPage.vue`.
@@ -0,0 +1,538 @@
1
+ "use strict";
2
+ const PluginContextToken = /* @__PURE__ */ Symbol.for("examaware.hosting.ctx");
3
+ const PluginLoggerToken = /* @__PURE__ */ Symbol.for("examaware.hosting.logger");
4
+ const PluginSettingsToken = /* @__PURE__ */ Symbol.for("examaware.hosting.settings");
5
+ const DesktopApiToken = /* @__PURE__ */ Symbol.for("examaware.hosting.desktopApi");
6
+ const HostApplicationLifetimeToken = /* @__PURE__ */ Symbol.for("examaware.hosting.lifetime");
7
+ class DefaultHostApplicationLifetime {
8
+ constructor(logger = console) {
9
+ this.logger = logger;
10
+ this.started = /* @__PURE__ */ new Set();
11
+ this.stopping = /* @__PURE__ */ new Set();
12
+ this.stopped = /* @__PURE__ */ new Set();
13
+ }
14
+ // 已停止事件处理器
15
+ // 注册启动事件处理器
16
+ onStarted(handler) {
17
+ this.started.add(handler);
18
+ return () => {
19
+ this.started.delete(handler);
20
+ };
21
+ }
22
+ // 注册停止事件处理器
23
+ onStopping(handler) {
24
+ this.stopping.add(handler);
25
+ return () => {
26
+ this.stopping.delete(handler);
27
+ };
28
+ }
29
+ // 注册已停止事件处理器
30
+ onStopped(handler) {
31
+ this.stopped.add(handler);
32
+ return () => {
33
+ this.stopped.delete(handler);
34
+ };
35
+ }
36
+ // 通知所有启动事件处理器
37
+ async notifyStarted() {
38
+ await this.dispatch(this.started);
39
+ }
40
+ // 通知所有停止事件处理器
41
+ async notifyStopping() {
42
+ await this.dispatch(this.stopping);
43
+ }
44
+ // 通知所有已停止事件处理器
45
+ async notifyStopped() {
46
+ await this.dispatch(this.stopped);
47
+ }
48
+ // 执行事件处理器列表
49
+ async dispatch(targets) {
50
+ for (const handler of Array.from(targets)) {
51
+ try {
52
+ await handler();
53
+ } catch (error) {
54
+ this.logger.error("[PluginHostLifetime] handler failed", error);
55
+ }
56
+ }
57
+ }
58
+ }
59
+ function isConstructor(value) {
60
+ return typeof value === "function" && !!value.prototype && value.prototype.constructor === value;
61
+ }
62
+ class ServiceCollection {
63
+ // 应用生命周期管理器
64
+ constructor(ctx) {
65
+ this.ctx = ctx;
66
+ this.descriptors = /* @__PURE__ */ new Map();
67
+ this.hostLifetime = new DefaultHostApplicationLifetime(ctx.logger ?? console);
68
+ this.addSingleton(PluginContextToken, () => ctx);
69
+ this.addSingleton(PluginLoggerToken, () => ctx.logger);
70
+ this.addSingleton(PluginSettingsToken, () => ctx.settings);
71
+ this.addSingleton(DesktopApiToken, () => ctx.desktopApi);
72
+ this.addSingleton(HostApplicationLifetimeToken, () => this.hostLifetime);
73
+ }
74
+ // 获取生命周期管理器
75
+ getLifetime() {
76
+ return this.hostLifetime;
77
+ }
78
+ // 添加单例服务
79
+ addSingleton(token, impl) {
80
+ return this.register(token, "singleton", impl);
81
+ }
82
+ // 添加作用域服务
83
+ addScoped(token, impl) {
84
+ return this.register(token, "scoped", impl);
85
+ }
86
+ // 添加瞬时服务
87
+ addTransient(token, impl) {
88
+ return this.register(token, "transient", impl);
89
+ }
90
+ // 尝试添加单例服务(如果不存在)
91
+ tryAddSingleton(token, impl) {
92
+ if (!this.descriptors.has(token)) {
93
+ this.addSingleton(token, impl);
94
+ }
95
+ return this;
96
+ }
97
+ // 检查服务是否已注册
98
+ has(token) {
99
+ return this.descriptors.has(token);
100
+ }
101
+ // 清空所有服务
102
+ clear() {
103
+ this.descriptors.clear();
104
+ }
105
+ // 构建服务提供者
106
+ buildServiceProvider() {
107
+ return new ServiceProvider(this.ctx, new Map(this.descriptors));
108
+ }
109
+ // 注册服务
110
+ register(token, lifetime, impl) {
111
+ const descriptor = {
112
+ token,
113
+ lifetime,
114
+ factory: this.normalizeFactory(impl)
115
+ // 标准化工厂函数
116
+ };
117
+ this.descriptors.set(token, descriptor);
118
+ return this;
119
+ }
120
+ // 标准化工厂函数
121
+ normalizeFactory(impl) {
122
+ if (isConstructor(impl)) {
123
+ return (provider) => this.instantiateClass(impl, provider);
124
+ }
125
+ if (typeof impl === "function") {
126
+ return impl;
127
+ }
128
+ return () => impl;
129
+ }
130
+ // 实例化类,注入依赖
131
+ instantiateClass(Ctor, provider) {
132
+ const deps = [...Ctor.inject ?? []].map((token) => provider.get(token));
133
+ return new Ctor(...deps);
134
+ }
135
+ }
136
+ class ServiceProvider {
137
+ // 根提供者
138
+ constructor(ctx, descriptors, root = null, isScope = false) {
139
+ this.ctx = ctx;
140
+ this.descriptors = descriptors;
141
+ this.isScope = isScope;
142
+ this.scopedCache = /* @__PURE__ */ new Map();
143
+ this.scopedCleanup = [];
144
+ this.root = root;
145
+ this.singletonCache = root ? root.singletonCache : /* @__PURE__ */ new Map();
146
+ this.singletonCleanup = root ? root.singletonCleanup : [];
147
+ }
148
+ // 获取服务实例
149
+ get(token) {
150
+ const descriptor = this.descriptors.get(token);
151
+ if (!descriptor) {
152
+ const hostValue = this.resolveFromHost(token);
153
+ if (hostValue !== void 0) return hostValue;
154
+ throw new Error(`Service not registered for token: ${token.toString?.() ?? String(token)}`);
155
+ }
156
+ switch (descriptor.lifetime) {
157
+ case "singleton":
158
+ return this.resolveSingleton(descriptor);
159
+ case "scoped":
160
+ return this.resolveScoped(descriptor);
161
+ case "transient":
162
+ default:
163
+ return this.instantiate(descriptor);
164
+ }
165
+ }
166
+ // 创建作用域提供者
167
+ createScope() {
168
+ return new ServiceProvider(this.ctx, this.descriptors, this.root ?? this, true);
169
+ }
170
+ // 释放资源
171
+ async dispose() {
172
+ await this.flushCleanup(this.scopedCleanup);
173
+ if (!this.isScope) {
174
+ await this.flushCleanup(this.singletonCleanup);
175
+ this.singletonCache.clear();
176
+ }
177
+ this.scopedCache.clear();
178
+ }
179
+ // 解析单例服务
180
+ resolveSingleton(descriptor) {
181
+ const cacheOwner = this.root ?? this;
182
+ if (cacheOwner.singletonCache.has(descriptor.token)) {
183
+ return cacheOwner.singletonCache.get(descriptor.token);
184
+ }
185
+ const instance = this.instantiate(descriptor);
186
+ cacheOwner.singletonCache.set(descriptor.token, instance);
187
+ const disposer = this.extractDisposer(instance);
188
+ if (disposer) cacheOwner.singletonCleanup.push(disposer);
189
+ return instance;
190
+ }
191
+ // 解析作用域服务
192
+ resolveScoped(descriptor) {
193
+ if (this.scopedCache.has(descriptor.token)) {
194
+ return this.scopedCache.get(descriptor.token);
195
+ }
196
+ const instance = this.instantiate(descriptor);
197
+ this.scopedCache.set(descriptor.token, instance);
198
+ const disposer = this.extractDisposer(instance);
199
+ if (disposer) this.scopedCleanup.push(disposer);
200
+ return instance;
201
+ }
202
+ // 实例化服务
203
+ instantiate(descriptor) {
204
+ return descriptor.factory(this);
205
+ }
206
+ // 从宿主服务API解析
207
+ resolveFromHost(token) {
208
+ if (typeof token === "string" && this.ctx.services?.has?.(token)) {
209
+ return this.ctx.services.inject(token);
210
+ }
211
+ return void 0;
212
+ }
213
+ // 提取实例的清理函数
214
+ extractDisposer(instance) {
215
+ if (!instance) return null;
216
+ if (typeof instance.dispose === "function") {
217
+ return () => instance.dispose();
218
+ }
219
+ if (typeof instance.destroy === "function") {
220
+ return () => instance.destroy();
221
+ }
222
+ if (typeof instance.stop === "function") {
223
+ return () => instance.stop();
224
+ }
225
+ return null;
226
+ }
227
+ // 执行清理函数列表
228
+ async flushCleanup(cleanup) {
229
+ while (cleanup.length) {
230
+ const disposer = cleanup.pop();
231
+ if (!disposer) continue;
232
+ await disposer();
233
+ }
234
+ }
235
+ }
236
+ class PluginHostApplication {
237
+ // 是否已启动
238
+ constructor(context, provider, configureDelegates, middleware, hostedTokens) {
239
+ this.context = context;
240
+ this.provider = provider;
241
+ this.configureDelegates = configureDelegates;
242
+ this.middleware = middleware;
243
+ this.hostedTokens = hostedTokens;
244
+ this.hostedInstances = [];
245
+ this.hostDisposers = [];
246
+ this.started = false;
247
+ }
248
+ // 包装应用,添加服务暴露功能
249
+ withExposures(exposures) {
250
+ return new PluginHostApplicationWithExposures(this, exposures);
251
+ }
252
+ // 启动应用
253
+ async start() {
254
+ if (this.started) return;
255
+ const appCtx = this.createApplicationContext();
256
+ for (const configure of this.configureDelegates) {
257
+ await configure(this.context, appCtx);
258
+ }
259
+ await this.dispatch(0, appCtx, async () => {
260
+ await this.bootstrapHostedServices();
261
+ });
262
+ this.started = true;
263
+ await this.context.lifetime.notifyStarted();
264
+ }
265
+ // 停止应用
266
+ async stop() {
267
+ if (!this.started) return;
268
+ await this.context.lifetime.notifyStopping();
269
+ await this.disposeHostedServices();
270
+ await this.context.lifetime.notifyStopped();
271
+ this.started = false;
272
+ }
273
+ // 释放资源
274
+ async dispose() {
275
+ await this.stop();
276
+ await this.provider.dispose();
277
+ }
278
+ // 获取服务提供者
279
+ get services() {
280
+ return this.provider;
281
+ }
282
+ // 获取Host上下文
283
+ get hostContext() {
284
+ return this.context;
285
+ }
286
+ // 创建应用上下文
287
+ createApplicationContext() {
288
+ return {
289
+ ctx: this.context.ctx,
290
+ services: this.provider,
291
+ host: this.context
292
+ };
293
+ }
294
+ // 启动所有托管服务
295
+ async bootstrapHostedServices() {
296
+ for (const token of this.hostedTokens) {
297
+ const service = this.provider.get(token);
298
+ this.hostedInstances.push(service);
299
+ if (typeof service.start === "function") {
300
+ await service.start();
301
+ }
302
+ }
303
+ }
304
+ // 停止所有托管服务
305
+ async disposeHostedServices() {
306
+ while (this.hostedInstances.length) {
307
+ const service = this.hostedInstances.pop();
308
+ if (!service) continue;
309
+ if (typeof service.stop === "function") {
310
+ await service.stop();
311
+ }
312
+ }
313
+ }
314
+ // 执行中间件管道
315
+ async dispatch(index2, appCtx, terminal) {
316
+ const middleware = this.middleware[index2];
317
+ if (!middleware) {
318
+ await terminal();
319
+ return;
320
+ }
321
+ await middleware(appCtx, () => this.dispatch(index2 + 1, appCtx, terminal));
322
+ }
323
+ }
324
+ class PluginHostApplicationWithExposures {
325
+ // 暴露服务的清理函数
326
+ constructor(app, exposures) {
327
+ this.app = app;
328
+ this.exposures = exposures;
329
+ this.exposureDisposers = [];
330
+ }
331
+ // 启动应用,包括注册暴露服务
332
+ async start() {
333
+ try {
334
+ await this.registerExposures();
335
+ await this.app.start();
336
+ } catch (error) {
337
+ await this.disposeExposures();
338
+ throw error;
339
+ }
340
+ }
341
+ // 停止应用,包括清理暴露服务
342
+ async stop() {
343
+ await this.app.stop();
344
+ await this.disposeExposures();
345
+ }
346
+ // 释放资源
347
+ async dispose() {
348
+ await this.app.dispose();
349
+ await this.disposeExposures();
350
+ }
351
+ // 获取服务提供者
352
+ get services() {
353
+ return this.app.services;
354
+ }
355
+ // 获取Host上下文
356
+ get hostContext() {
357
+ return this.app.hostContext;
358
+ }
359
+ // 注册暴露的服务到插件上下文
360
+ async registerExposures() {
361
+ const ctx = this.app.hostContext.ctx;
362
+ if (!ctx.services) return;
363
+ for (const exposure of this.exposures) {
364
+ const value = exposure.resolver(this.app.services);
365
+ const disposer = ctx.services.provide(exposure.name, value);
366
+ this.exposureDisposers.push(disposer);
367
+ }
368
+ }
369
+ // 清理暴露的服务
370
+ async disposeExposures() {
371
+ while (this.exposureDisposers.length) {
372
+ const dispose = this.exposureDisposers.pop();
373
+ if (!dispose) continue;
374
+ await dispose();
375
+ }
376
+ }
377
+ }
378
+ class ExamAwareHostBuilder {
379
+ // 构建器上下文
380
+ constructor(runtimeCtx, settings = {}) {
381
+ this.runtimeCtx = runtimeCtx;
382
+ this.configureServicesDelegates = [];
383
+ this.configureDelegates = [];
384
+ this.middleware = [];
385
+ this.hostedServices = [];
386
+ this.exposures = [];
387
+ this.serviceCollection = new ServiceCollection(runtimeCtx);
388
+ this.builderContext = {
389
+ ctx: runtimeCtx,
390
+ environmentName: settings.environment ?? process.env.EXAMAWARE_ENV ?? "Production",
391
+ properties: new Map(Object.entries(settings.properties ?? {})),
392
+ lifetime: this.serviceCollection.getLifetime()
393
+ // 获取应用生命周期管理器
394
+ };
395
+ }
396
+ // 获取构建器上下文
397
+ get context() {
398
+ return this.builderContext;
399
+ }
400
+ // 添加服务配置委托
401
+ configureServices(callback) {
402
+ this.configureServicesDelegates.push(callback);
403
+ return this;
404
+ }
405
+ // 添加应用配置委托
406
+ configure(callback) {
407
+ this.configureDelegates.push(callback);
408
+ return this;
409
+ }
410
+ // 添加中间件
411
+ use(middleware) {
412
+ this.middleware.push(middleware);
413
+ return this;
414
+ }
415
+ // 添加托管服务
416
+ addHostedService(token) {
417
+ this.hostedServices.push(token);
418
+ return this;
419
+ }
420
+ // 暴露服务到插件上下文
421
+ exposeHostService(name, resolver) {
422
+ const normalized = this.normalizeResolver(resolver);
423
+ if (normalized) {
424
+ this.exposures.push({ name, resolver: normalized });
425
+ }
426
+ return this;
427
+ }
428
+ // 构建Host应用
429
+ async build() {
430
+ for (const configure of this.configureServicesDelegates) {
431
+ await configure(this.builderContext, this.serviceCollection);
432
+ }
433
+ const provider = this.serviceCollection.buildServiceProvider();
434
+ const application = new PluginHostApplication(
435
+ this.builderContext,
436
+ provider,
437
+ this.configureDelegates,
438
+ this.middleware,
439
+ this.hostedServices
440
+ );
441
+ return new PluginHost(application.withExposures(this.exposures));
442
+ }
443
+ // 标准化暴露解析器
444
+ normalizeResolver(resolver) {
445
+ if (resolver.token) {
446
+ return (provider) => provider.get(resolver.token);
447
+ }
448
+ if (resolver.factory) {
449
+ return resolver.factory;
450
+ }
451
+ return null;
452
+ }
453
+ }
454
+ class PluginHost {
455
+ constructor(app) {
456
+ this.app = app;
457
+ }
458
+ // 获取服务提供者
459
+ get services() {
460
+ return this.app.services;
461
+ }
462
+ // 获取Host上下文
463
+ get hostContext() {
464
+ return this.app.hostContext;
465
+ }
466
+ // 启动应用
467
+ async start() {
468
+ await this.app.start();
469
+ }
470
+ // 停止应用
471
+ async stop() {
472
+ await this.app.stop();
473
+ }
474
+ // 释放资源
475
+ async dispose() {
476
+ await this.app.dispose();
477
+ }
478
+ // 运行应用,返回停止函数
479
+ async run() {
480
+ await this.start();
481
+ return async () => {
482
+ await this.dispose();
483
+ };
484
+ }
485
+ }
486
+ function createPluginHostBuilder(ctx, settings) {
487
+ return new ExamAwareHostBuilder(ctx, settings);
488
+ }
489
+ const Host = {
490
+ createApplicationBuilder: createPluginHostBuilder
491
+ };
492
+ function defineExamAwarePlugin(setup) {
493
+ return async function examAwarePlugin(ctx) {
494
+ const builder = Host.createApplicationBuilder(ctx);
495
+ await setup(builder);
496
+ const host = await builder.build();
497
+ return host.run();
498
+ };
499
+ }
500
+ class HeartbeatService {
501
+ constructor(message) {
502
+ this.message = message;
503
+ }
504
+ async start() {
505
+ this.interval = setInterval(() => {
506
+ console.info("[examaware-plugin-template]", this.message.text, (/* @__PURE__ */ new Date()).toISOString());
507
+ }, 1e4);
508
+ }
509
+ async stop() {
510
+ if (this.interval) clearInterval(this.interval);
511
+ }
512
+ }
513
+ const index = defineExamAwarePlugin((builder) => {
514
+ builder.configureServices((context, services) => {
515
+ services.addSingleton("hello.message", () => ({
516
+ text: context.ctx.config?.message ?? "Hello from ExamAware Demo Plugin1",
517
+ timestamp: Date.now()
518
+ }));
519
+ services.addSingleton(HeartbeatService, (sp) => new HeartbeatService(sp.get("hello.message")));
520
+ services.tryAddSingleton("heartbeat.service", (sp) => sp.get(HeartbeatService));
521
+ context.ctx.logger.info(
522
+ "[examaware-plugin-template] registered HeartbeatService/heartbeat.service"
523
+ );
524
+ });
525
+ builder.exposeHostService("hello.message", { token: "hello.message" });
526
+ builder.addHostedService("heartbeat.service");
527
+ builder.use(async ({ ctx }, next) => {
528
+ ctx.logger.info("Plugin boot sequence started");
529
+ await next();
530
+ ctx.logger.info("Plugin boot sequence completed");
531
+ });
532
+ builder.configure((host, app) => {
533
+ host.lifetime.onStarted(() => {
534
+ app.ctx.logger.info("Host lifetime onStarted hook invoked");
535
+ });
536
+ });
537
+ });
538
+ module.exports = index;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","sources":["../../../../packages/plugin-sdk/dist/index.mjs","../../src/main/index.ts"],"sourcesContent":["const PluginContextToken = Symbol.for(\"examaware.hosting.ctx\");\nconst PluginLoggerToken = Symbol.for(\"examaware.hosting.logger\");\nconst PluginSettingsToken = Symbol.for(\"examaware.hosting.settings\");\nconst DesktopApiToken = Symbol.for(\"examaware.hosting.desktopApi\");\nconst HostApplicationLifetimeToken = Symbol.for(\"examaware.hosting.lifetime\");\nclass DefaultHostApplicationLifetime {\n constructor() {\n this.started = /* @__PURE__ */ new Set();\n this.stopping = /* @__PURE__ */ new Set();\n this.stopped = /* @__PURE__ */ new Set();\n }\n // 已停止事件处理器\n // 注册启动事件处理器\n onStarted(handler) {\n this.started.add(handler);\n return () => {\n this.started.delete(handler);\n };\n }\n // 注册停止事件处理器\n onStopping(handler) {\n this.stopping.add(handler);\n return () => {\n this.stopping.delete(handler);\n };\n }\n // 注册已停止事件处理器\n onStopped(handler) {\n this.stopped.add(handler);\n return () => {\n this.stopped.delete(handler);\n };\n }\n // 通知所有启动事件处理器\n async notifyStarted() {\n await this.dispatch(this.started);\n }\n // 通知所有停止事件处理器\n async notifyStopping() {\n await this.dispatch(this.stopping);\n }\n // 通知所有已停止事件处理器\n async notifyStopped() {\n await this.dispatch(this.stopped);\n }\n // 执行事件处理器列表\n async dispatch(targets) {\n for (const handler of Array.from(targets)) {\n try {\n await handler();\n } catch (error) {\n console.error(\"[PluginHostLifetime] handler failed\", error);\n }\n }\n }\n}\nfunction isConstructor(value) {\n return typeof value === \"function\" && !!value.prototype && value.prototype.constructor === value;\n}\nclass ServiceCollection {\n // 应用生命周期管理器\n constructor(ctx) {\n this.ctx = ctx;\n this.descriptors = /* @__PURE__ */ new Map();\n this.hostLifetime = new DefaultHostApplicationLifetime();\n this.addSingleton(PluginContextToken, () => ctx);\n this.addSingleton(PluginLoggerToken, () => ctx.logger);\n this.addSingleton(PluginSettingsToken, () => ctx.settings);\n this.addSingleton(DesktopApiToken, () => ctx.desktopApi);\n this.addSingleton(HostApplicationLifetimeToken, () => this.hostLifetime);\n }\n // 获取生命周期管理器\n getLifetime() {\n return this.hostLifetime;\n }\n // 添加单例服务\n addSingleton(token, impl) {\n return this.register(token, \"singleton\", impl);\n }\n // 添加作用域服务\n addScoped(token, impl) {\n return this.register(token, \"scoped\", impl);\n }\n // 添加瞬时服务\n addTransient(token, impl) {\n return this.register(token, \"transient\", impl);\n }\n // 尝试添加单例服务(如果不存在)\n tryAddSingleton(token, impl) {\n if (!this.descriptors.has(token)) {\n this.addSingleton(token, impl);\n }\n return this;\n }\n // 检查服务是否已注册\n has(token) {\n return this.descriptors.has(token);\n }\n // 清空所有服务\n clear() {\n this.descriptors.clear();\n }\n // 构建服务提供者\n buildServiceProvider() {\n return new ServiceProvider(this.ctx, new Map(this.descriptors));\n }\n // 注册服务\n register(token, lifetime, impl) {\n const descriptor = {\n token,\n lifetime,\n factory: this.normalizeFactory(impl)\n // 标准化工厂函数\n };\n this.descriptors.set(token, descriptor);\n return this;\n }\n // 标准化工厂函数\n normalizeFactory(impl) {\n if (isConstructor(impl)) {\n return (provider) => this.instantiateClass(impl, provider);\n }\n if (typeof impl === \"function\") {\n return impl;\n }\n return () => impl;\n }\n // 实例化类,注入依赖\n instantiateClass(Ctor, provider) {\n const deps = [...Ctor.inject ?? []].map((token) => provider.get(token));\n return new Ctor(...deps);\n }\n}\nclass ServiceProvider {\n // 根提供者\n constructor(ctx, descriptors, root = null, isScope = false) {\n this.ctx = ctx;\n this.descriptors = descriptors;\n this.isScope = isScope;\n this.scopedCache = /* @__PURE__ */ new Map();\n this.scopedCleanup = [];\n this.root = root;\n this.singletonCache = root ? root.singletonCache : /* @__PURE__ */ new Map();\n this.singletonCleanup = root ? root.singletonCleanup : [];\n }\n // 获取服务实例\n get(token) {\n const descriptor = this.descriptors.get(token);\n if (!descriptor) {\n const hostValue = this.resolveFromHost(token);\n if (hostValue !== void 0) return hostValue;\n throw new Error(`Service not registered for token: ${token.toString?.() ?? String(token)}`);\n }\n switch (descriptor.lifetime) {\n case \"singleton\":\n return this.resolveSingleton(descriptor);\n case \"scoped\":\n return this.resolveScoped(descriptor);\n case \"transient\":\n default:\n return this.instantiate(descriptor);\n }\n }\n // 创建作用域提供者\n createScope() {\n return new ServiceProvider(this.ctx, this.descriptors, this.root ?? this, true);\n }\n // 释放资源\n async dispose() {\n await this.flushCleanup(this.scopedCleanup);\n if (!this.isScope) {\n await this.flushCleanup(this.singletonCleanup);\n this.singletonCache.clear();\n }\n this.scopedCache.clear();\n }\n // 解析单例服务\n resolveSingleton(descriptor) {\n const cacheOwner = this.root ?? this;\n if (cacheOwner.singletonCache.has(descriptor.token)) {\n return cacheOwner.singletonCache.get(descriptor.token);\n }\n const instance = this.instantiate(descriptor);\n cacheOwner.singletonCache.set(descriptor.token, instance);\n const disposer = this.extractDisposer(instance);\n if (disposer) cacheOwner.singletonCleanup.push(disposer);\n return instance;\n }\n // 解析作用域服务\n resolveScoped(descriptor) {\n if (this.scopedCache.has(descriptor.token)) {\n return this.scopedCache.get(descriptor.token);\n }\n const instance = this.instantiate(descriptor);\n this.scopedCache.set(descriptor.token, instance);\n const disposer = this.extractDisposer(instance);\n if (disposer) this.scopedCleanup.push(disposer);\n return instance;\n }\n // 实例化服务\n instantiate(descriptor) {\n return descriptor.factory(this);\n }\n // 从宿主服务API解析\n resolveFromHost(token) {\n if (typeof token === \"string\" && this.ctx.services?.has?.(token)) {\n return this.ctx.services.inject(token);\n }\n return void 0;\n }\n // 提取实例的清理函数\n extractDisposer(instance) {\n if (!instance) return null;\n if (typeof instance.dispose === \"function\") {\n return () => instance.dispose();\n }\n if (typeof instance.destroy === \"function\") {\n return () => instance.destroy();\n }\n if (typeof instance.stop === \"function\") {\n return () => instance.stop();\n }\n return null;\n }\n // 执行清理函数列表\n async flushCleanup(cleanup) {\n while (cleanup.length) {\n const disposer = cleanup.pop();\n if (!disposer) continue;\n await disposer();\n }\n }\n}\nclass PluginHostApplication {\n // 是否已启动\n constructor(context, provider, configureDelegates, middleware, hostedTokens) {\n this.context = context;\n this.provider = provider;\n this.configureDelegates = configureDelegates;\n this.middleware = middleware;\n this.hostedTokens = hostedTokens;\n this.hostedInstances = [];\n this.hostDisposers = [];\n this.started = false;\n }\n // 包装应用,添加服务暴露功能\n withExposures(exposures) {\n return new PluginHostApplicationWithExposures(this, exposures);\n }\n // 启动应用\n async start() {\n if (this.started) return;\n const appCtx = this.createApplicationContext();\n for (const configure of this.configureDelegates) {\n await configure(this.context, appCtx);\n }\n await this.dispatch(0, appCtx, async () => {\n await this.bootstrapHostedServices();\n });\n this.started = true;\n await this.context.lifetime.notifyStarted();\n }\n // 停止应用\n async stop() {\n if (!this.started) return;\n await this.context.lifetime.notifyStopping();\n await this.disposeHostedServices();\n await this.context.lifetime.notifyStopped();\n this.started = false;\n }\n // 释放资源\n async dispose() {\n await this.stop();\n await this.provider.dispose();\n }\n // 获取服务提供者\n get services() {\n return this.provider;\n }\n // 获取Host上下文\n get hostContext() {\n return this.context;\n }\n // 创建应用上下文\n createApplicationContext() {\n return {\n ctx: this.context.ctx,\n services: this.provider,\n host: this.context\n };\n }\n // 启动所有托管服务\n async bootstrapHostedServices() {\n for (const token of this.hostedTokens) {\n const service = this.provider.get(token);\n this.hostedInstances.push(service);\n if (typeof service.start === \"function\") {\n await service.start();\n }\n }\n }\n // 停止所有托管服务\n async disposeHostedServices() {\n while (this.hostedInstances.length) {\n const service = this.hostedInstances.pop();\n if (!service) continue;\n if (typeof service.stop === \"function\") {\n await service.stop();\n }\n }\n }\n // 执行中间件管道\n async dispatch(index, appCtx, terminal) {\n const middleware = this.middleware[index];\n if (!middleware) {\n await terminal();\n return;\n }\n await middleware(appCtx, () => this.dispatch(index + 1, appCtx, terminal));\n }\n}\nclass PluginHostApplicationWithExposures {\n // 暴露服务的清理函数\n constructor(app, exposures) {\n this.app = app;\n this.exposures = exposures;\n this.exposureDisposers = [];\n }\n // 启动应用,包括注册暴露服务\n async start() {\n try {\n await this.registerExposures();\n await this.app.start();\n } catch (error) {\n await this.disposeExposures();\n throw error;\n }\n }\n // 停止应用,包括清理暴露服务\n async stop() {\n await this.app.stop();\n await this.disposeExposures();\n }\n // 释放资源\n async dispose() {\n await this.app.dispose();\n await this.disposeExposures();\n }\n // 获取服务提供者\n get services() {\n return this.app.services;\n }\n // 获取Host上下文\n get hostContext() {\n return this.app.hostContext;\n }\n // 注册暴露的服务到插件上下文\n async registerExposures() {\n const ctx = this.app.hostContext.ctx;\n if (!ctx.services) return;\n for (const exposure of this.exposures) {\n const value = exposure.resolver(this.app.services);\n const disposer = ctx.services.provide(exposure.name, value);\n this.exposureDisposers.push(disposer);\n }\n }\n // 清理暴露的服务\n async disposeExposures() {\n while (this.exposureDisposers.length) {\n const dispose = this.exposureDisposers.pop();\n if (!dispose) continue;\n await dispose();\n }\n }\n}\nclass ExamAwareHostBuilder {\n // 构建器上下文\n constructor(runtimeCtx, settings = {}) {\n this.runtimeCtx = runtimeCtx;\n this.configureServicesDelegates = [];\n this.configureDelegates = [];\n this.middleware = [];\n this.hostedServices = [];\n this.exposures = [];\n this.serviceCollection = new ServiceCollection(runtimeCtx);\n this.builderContext = {\n ctx: runtimeCtx,\n environmentName: settings.environment ?? process.env.EXAMAWARE_ENV ?? \"Production\",\n properties: new Map(Object.entries(settings.properties ?? {})),\n lifetime: this.serviceCollection.getLifetime()\n // 获取应用生命周期管理器\n };\n }\n // 获取构建器上下文\n get context() {\n return this.builderContext;\n }\n // 添加服务配置委托\n configureServices(callback) {\n this.configureServicesDelegates.push(callback);\n return this;\n }\n // 添加应用配置委托\n configure(callback) {\n this.configureDelegates.push(callback);\n return this;\n }\n // 添加中间件\n use(middleware) {\n this.middleware.push(middleware);\n return this;\n }\n // 添加托管服务\n addHostedService(token) {\n this.hostedServices.push(token);\n return this;\n }\n // 暴露服务到插件上下文\n exposeHostService(name, resolver) {\n const normalized = this.normalizeResolver(resolver);\n if (normalized) {\n this.exposures.push({ name, resolver: normalized });\n }\n return this;\n }\n // 构建Host应用\n async build() {\n for (const configure of this.configureServicesDelegates) {\n await configure(this.builderContext, this.serviceCollection);\n }\n const provider = this.serviceCollection.buildServiceProvider();\n const application = new PluginHostApplication(\n this.builderContext,\n provider,\n this.configureDelegates,\n this.middleware,\n this.hostedServices\n );\n return new PluginHost(application.withExposures(this.exposures));\n }\n // 标准化暴露解析器\n normalizeResolver(resolver) {\n if (resolver.token) {\n return (provider) => provider.get(resolver.token);\n }\n if (resolver.factory) {\n return resolver.factory;\n }\n return null;\n }\n}\nclass PluginHost {\n constructor(app) {\n this.app = app;\n }\n // 获取服务提供者\n get services() {\n return this.app.services;\n }\n // 获取Host上下文\n get hostContext() {\n return this.app.hostContext;\n }\n // 启动应用\n async start() {\n await this.app.start();\n }\n // 停止应用\n async stop() {\n await this.app.stop();\n }\n // 释放资源\n async dispose() {\n await this.app.dispose();\n }\n // 运行应用,返回停止函数\n async run() {\n await this.start();\n return async () => {\n await this.dispose();\n };\n }\n}\nfunction createPluginHostBuilder(ctx, settings) {\n return new ExamAwareHostBuilder(ctx, settings);\n}\nconst Host = {\n createApplicationBuilder: createPluginHostBuilder\n};\nfunction defineExamAwarePlugin(setup) {\n return async function examAwarePlugin(ctx) {\n const builder = Host.createApplicationBuilder(ctx);\n await setup(builder);\n const host = await builder.build();\n return host.run();\n };\n}\nexport {\n DesktopApiToken,\n ExamAwareHostBuilder,\n Host,\n HostApplicationLifetimeToken,\n PluginContextToken,\n PluginHost,\n PluginLoggerToken,\n PluginSettingsToken,\n ServiceCollection,\n ServiceProvider,\n createPluginHostBuilder,\n defineExamAwarePlugin\n};\n//# sourceMappingURL=index.mjs.map\n","import { defineExamAwarePlugin } from '@dsz-examaware/plugin-sdk';\nimport type { HostedService } from '@dsz-examaware/plugin-sdk';\n\ninterface HelloMessage {\n text: string;\n timestamp: number;\n}\n\nclass HeartbeatService implements HostedService {\n constructor(private readonly message: HelloMessage) {}\n\n private interval?: ReturnType<typeof setInterval>;\n\n async start() {\n this.interval = setInterval(() => {\n console.info('[examaware-plugin-template]', this.message.text, new Date().toISOString());\n }, 10_000);\n }\n\n async stop() {\n if (this.interval) clearInterval(this.interval);\n }\n}\n\nexport default defineExamAwarePlugin((builder) => {\n builder.configureServices((context, services) => {\n services.addSingleton('hello.message', () => ({\n text: context.ctx.config?.message ?? 'Hello from ExamAware Demo Plugin1',\n timestamp: Date.now()\n }));\n\n // Register both class and string tokens to avoid identity mismatches\n services.addSingleton(HeartbeatService, (sp) => new HeartbeatService(sp.get('hello.message')));\n services.tryAddSingleton('heartbeat.service', (sp) => sp.get(HeartbeatService));\n context.ctx.logger.info(\n '[examaware-plugin-template] registered HeartbeatService/heartbeat.service'\n );\n });\n\n builder.exposeHostService('hello.message', { token: 'hello.message' });\n builder.addHostedService('heartbeat.service');\n\n builder.use(async ({ ctx }, next) => {\n ctx.logger.info('Plugin boot sequence started');\n await next();\n ctx.logger.info('Plugin boot sequence completed');\n });\n\n builder.configure((host, app) => {\n host.lifetime.onStarted(() => {\n app.ctx.logger.info('Host lifetime onStarted hook invoked');\n });\n });\n});\n"],"names":["index"],"mappings":";AAAA,MAAM,qBAAqB,OAAO,IAAI,uBAAuB;AAC7D,MAAM,oBAAoB,OAAO,IAAI,0BAA0B;AAC/D,MAAM,sBAAsB,OAAO,IAAI,4BAA4B;AACnE,MAAM,kBAAkB,OAAO,IAAI,8BAA8B;AACjE,MAAM,+BAA+B,OAAO,IAAI,4BAA4B;AAC5E,MAAM,+BAA+B;AAAA,EACnC,cAAc;AACZ,SAAK,UAA0B,oBAAI,IAAG;AACtC,SAAK,WAA2B,oBAAI,IAAG;AACvC,SAAK,UAA0B,oBAAI,IAAG;AAAA,EACxC;AAAA;AAAA;AAAA,EAGA,UAAU,SAAS;AACjB,SAAK,QAAQ,IAAI,OAAO;AACxB,WAAO,MAAM;AACX,WAAK,QAAQ,OAAO,OAAO;AAAA,IAC7B;AAAA,EACF;AAAA;AAAA,EAEA,WAAW,SAAS;AAClB,SAAK,SAAS,IAAI,OAAO;AACzB,WAAO,MAAM;AACX,WAAK,SAAS,OAAO,OAAO;AAAA,IAC9B;AAAA,EACF;AAAA;AAAA,EAEA,UAAU,SAAS;AACjB,SAAK,QAAQ,IAAI,OAAO;AACxB,WAAO,MAAM;AACX,WAAK,QAAQ,OAAO,OAAO;AAAA,IAC7B;AAAA,EACF;AAAA;AAAA,EAEA,MAAM,gBAAgB;AACpB,UAAM,KAAK,SAAS,KAAK,OAAO;AAAA,EAClC;AAAA;AAAA,EAEA,MAAM,iBAAiB;AACrB,UAAM,KAAK,SAAS,KAAK,QAAQ;AAAA,EACnC;AAAA;AAAA,EAEA,MAAM,gBAAgB;AACpB,UAAM,KAAK,SAAS,KAAK,OAAO;AAAA,EAClC;AAAA;AAAA,EAEA,MAAM,SAAS,SAAS;AACtB,eAAW,WAAW,MAAM,KAAK,OAAO,GAAG;AACzC,UAAI;AACF,cAAM,QAAO;AAAA,MACf,SAAS,OAAO;AACd,gBAAQ,MAAM,uCAAuC,KAAK;AAAA,MAC5D;AAAA,IACF;AAAA,EACF;AACF;AACA,SAAS,cAAc,OAAO;AAC5B,SAAO,OAAO,UAAU,cAAc,CAAC,CAAC,MAAM,aAAa,MAAM,UAAU,gBAAgB;AAC7F;AACA,MAAM,kBAAkB;AAAA;AAAA,EAEtB,YAAY,KAAK;AACf,SAAK,MAAM;AACX,SAAK,cAA8B,oBAAI,IAAG;AAC1C,SAAK,eAAe,IAAI,+BAA8B;AACtD,SAAK,aAAa,oBAAoB,MAAM,GAAG;AAC/C,SAAK,aAAa,mBAAmB,MAAM,IAAI,MAAM;AACrD,SAAK,aAAa,qBAAqB,MAAM,IAAI,QAAQ;AACzD,SAAK,aAAa,iBAAiB,MAAM,IAAI,UAAU;AACvD,SAAK,aAAa,8BAA8B,MAAM,KAAK,YAAY;AAAA,EACzE;AAAA;AAAA,EAEA,cAAc;AACZ,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAEA,aAAa,OAAO,MAAM;AACxB,WAAO,KAAK,SAAS,OAAO,aAAa,IAAI;AAAA,EAC/C;AAAA;AAAA,EAEA,UAAU,OAAO,MAAM;AACrB,WAAO,KAAK,SAAS,OAAO,UAAU,IAAI;AAAA,EAC5C;AAAA;AAAA,EAEA,aAAa,OAAO,MAAM;AACxB,WAAO,KAAK,SAAS,OAAO,aAAa,IAAI;AAAA,EAC/C;AAAA;AAAA,EAEA,gBAAgB,OAAO,MAAM;AAC3B,QAAI,CAAC,KAAK,YAAY,IAAI,KAAK,GAAG;AAChC,WAAK,aAAa,OAAO,IAAI;AAAA,IAC/B;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,IAAI,OAAO;AACT,WAAO,KAAK,YAAY,IAAI,KAAK;AAAA,EACnC;AAAA;AAAA,EAEA,QAAQ;AACN,SAAK,YAAY,MAAK;AAAA,EACxB;AAAA;AAAA,EAEA,uBAAuB;AACrB,WAAO,IAAI,gBAAgB,KAAK,KAAK,IAAI,IAAI,KAAK,WAAW,CAAC;AAAA,EAChE;AAAA;AAAA,EAEA,SAAS,OAAO,UAAU,MAAM;AAC9B,UAAM,aAAa;AAAA,MACjB;AAAA,MACA;AAAA,MACA,SAAS,KAAK,iBAAiB,IAAI;AAAA;AAAA,IAEzC;AACI,SAAK,YAAY,IAAI,OAAO,UAAU;AACtC,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,iBAAiB,MAAM;AACrB,QAAI,cAAc,IAAI,GAAG;AACvB,aAAO,CAAC,aAAa,KAAK,iBAAiB,MAAM,QAAQ;AAAA,IAC3D;AACA,QAAI,OAAO,SAAS,YAAY;AAC9B,aAAO;AAAA,IACT;AACA,WAAO,MAAM;AAAA,EACf;AAAA;AAAA,EAEA,iBAAiB,MAAM,UAAU;AAC/B,UAAM,OAAO,CAAC,GAAG,KAAK,UAAU,CAAA,CAAE,EAAE,IAAI,CAAC,UAAU,SAAS,IAAI,KAAK,CAAC;AACtE,WAAO,IAAI,KAAK,GAAG,IAAI;AAAA,EACzB;AACF;AACA,MAAM,gBAAgB;AAAA;AAAA,EAEpB,YAAY,KAAK,aAAa,OAAO,MAAM,UAAU,OAAO;AAC1D,SAAK,MAAM;AACX,SAAK,cAAc;AACnB,SAAK,UAAU;AACf,SAAK,cAA8B,oBAAI,IAAG;AAC1C,SAAK,gBAAgB,CAAA;AACrB,SAAK,OAAO;AACZ,SAAK,iBAAiB,OAAO,KAAK,iBAAiC,oBAAI,IAAG;AAC1E,SAAK,mBAAmB,OAAO,KAAK,mBAAmB,CAAA;AAAA,EACzD;AAAA;AAAA,EAEA,IAAI,OAAO;AACT,UAAM,aAAa,KAAK,YAAY,IAAI,KAAK;AAC7C,QAAI,CAAC,YAAY;AACf,YAAM,YAAY,KAAK,gBAAgB,KAAK;AAC5C,UAAI,cAAc,OAAQ,QAAO;AACjC,YAAM,IAAI,MAAM,qCAAqC,MAAM,WAAQ,KAAQ,OAAO,KAAK,CAAC,EAAE;AAAA,IAC5F;AACA,YAAQ,WAAW,UAAQ;AAAA,MACzB,KAAK;AACH,eAAO,KAAK,iBAAiB,UAAU;AAAA,MACzC,KAAK;AACH,eAAO,KAAK,cAAc,UAAU;AAAA,MACtC,KAAK;AAAA,MACL;AACE,eAAO,KAAK,YAAY,UAAU;AAAA,IAC1C;AAAA,EACE;AAAA;AAAA,EAEA,cAAc;AACZ,WAAO,IAAI,gBAAgB,KAAK,KAAK,KAAK,aAAa,KAAK,QAAQ,MAAM,IAAI;AAAA,EAChF;AAAA;AAAA,EAEA,MAAM,UAAU;AACd,UAAM,KAAK,aAAa,KAAK,aAAa;AAC1C,QAAI,CAAC,KAAK,SAAS;AACjB,YAAM,KAAK,aAAa,KAAK,gBAAgB;AAC7C,WAAK,eAAe,MAAK;AAAA,IAC3B;AACA,SAAK,YAAY,MAAK;AAAA,EACxB;AAAA;AAAA,EAEA,iBAAiB,YAAY;AAC3B,UAAM,aAAa,KAAK,QAAQ;AAChC,QAAI,WAAW,eAAe,IAAI,WAAW,KAAK,GAAG;AACnD,aAAO,WAAW,eAAe,IAAI,WAAW,KAAK;AAAA,IACvD;AACA,UAAM,WAAW,KAAK,YAAY,UAAU;AAC5C,eAAW,eAAe,IAAI,WAAW,OAAO,QAAQ;AACxD,UAAM,WAAW,KAAK,gBAAgB,QAAQ;AAC9C,QAAI,SAAU,YAAW,iBAAiB,KAAK,QAAQ;AACvD,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,cAAc,YAAY;AACxB,QAAI,KAAK,YAAY,IAAI,WAAW,KAAK,GAAG;AAC1C,aAAO,KAAK,YAAY,IAAI,WAAW,KAAK;AAAA,IAC9C;AACA,UAAM,WAAW,KAAK,YAAY,UAAU;AAC5C,SAAK,YAAY,IAAI,WAAW,OAAO,QAAQ;AAC/C,UAAM,WAAW,KAAK,gBAAgB,QAAQ;AAC9C,QAAI,SAAU,MAAK,cAAc,KAAK,QAAQ;AAC9C,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,YAAY,YAAY;AACtB,WAAO,WAAW,QAAQ,IAAI;AAAA,EAChC;AAAA;AAAA,EAEA,gBAAgB,OAAO;AACrB,QAAI,OAAO,UAAU,YAAY,KAAK,IAAI,UAAU,MAAM,KAAK,GAAG;AAChE,aAAO,KAAK,IAAI,SAAS,OAAO,KAAK;AAAA,IACvC;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,gBAAgB,UAAU;AACxB,QAAI,CAAC,SAAU,QAAO;AACtB,QAAI,OAAO,SAAS,YAAY,YAAY;AAC1C,aAAO,MAAM,SAAS,QAAO;AAAA,IAC/B;AACA,QAAI,OAAO,SAAS,YAAY,YAAY;AAC1C,aAAO,MAAM,SAAS,QAAO;AAAA,IAC/B;AACA,QAAI,OAAO,SAAS,SAAS,YAAY;AACvC,aAAO,MAAM,SAAS,KAAI;AAAA,IAC5B;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,MAAM,aAAa,SAAS;AAC1B,WAAO,QAAQ,QAAQ;AACrB,YAAM,WAAW,QAAQ,IAAG;AAC5B,UAAI,CAAC,SAAU;AACf,YAAM,SAAQ;AAAA,IAChB;AAAA,EACF;AACF;AACA,MAAM,sBAAsB;AAAA;AAAA,EAE1B,YAAY,SAAS,UAAU,oBAAoB,YAAY,cAAc;AAC3E,SAAK,UAAU;AACf,SAAK,WAAW;AAChB,SAAK,qBAAqB;AAC1B,SAAK,aAAa;AAClB,SAAK,eAAe;AACpB,SAAK,kBAAkB,CAAA;AACvB,SAAK,gBAAgB,CAAA;AACrB,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAEA,cAAc,WAAW;AACvB,WAAO,IAAI,mCAAmC,MAAM,SAAS;AAAA,EAC/D;AAAA;AAAA,EAEA,MAAM,QAAQ;AACZ,QAAI,KAAK,QAAS;AAClB,UAAM,SAAS,KAAK,yBAAwB;AAC5C,eAAW,aAAa,KAAK,oBAAoB;AAC/C,YAAM,UAAU,KAAK,SAAS,MAAM;AAAA,IACtC;AACA,UAAM,KAAK,SAAS,GAAG,QAAQ,YAAY;AACzC,YAAM,KAAK,wBAAuB;AAAA,IACpC,CAAC;AACD,SAAK,UAAU;AACf,UAAM,KAAK,QAAQ,SAAS,cAAa;AAAA,EAC3C;AAAA;AAAA,EAEA,MAAM,OAAO;AACX,QAAI,CAAC,KAAK,QAAS;AACnB,UAAM,KAAK,QAAQ,SAAS,eAAc;AAC1C,UAAM,KAAK,sBAAqB;AAChC,UAAM,KAAK,QAAQ,SAAS,cAAa;AACzC,SAAK,UAAU;AAAA,EACjB;AAAA;AAAA,EAEA,MAAM,UAAU;AACd,UAAM,KAAK,KAAI;AACf,UAAM,KAAK,SAAS,QAAO;AAAA,EAC7B;AAAA;AAAA,EAEA,IAAI,WAAW;AACb,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAEA,IAAI,cAAc;AAChB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAEA,2BAA2B;AACzB,WAAO;AAAA,MACL,KAAK,KAAK,QAAQ;AAAA,MAClB,UAAU,KAAK;AAAA,MACf,MAAM,KAAK;AAAA,IACjB;AAAA,EACE;AAAA;AAAA,EAEA,MAAM,0BAA0B;AAC9B,eAAW,SAAS,KAAK,cAAc;AACrC,YAAM,UAAU,KAAK,SAAS,IAAI,KAAK;AACvC,WAAK,gBAAgB,KAAK,OAAO;AACjC,UAAI,OAAO,QAAQ,UAAU,YAAY;AACvC,cAAM,QAAQ,MAAK;AAAA,MACrB;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAEA,MAAM,wBAAwB;AAC5B,WAAO,KAAK,gBAAgB,QAAQ;AAClC,YAAM,UAAU,KAAK,gBAAgB,IAAG;AACxC,UAAI,CAAC,QAAS;AACd,UAAI,OAAO,QAAQ,SAAS,YAAY;AACtC,cAAM,QAAQ,KAAI;AAAA,MACpB;AAAA,IACF;AAAA,EACF;AAAA;AAAA,EAEA,MAAM,SAASA,QAAO,QAAQ,UAAU;AACtC,UAAM,aAAa,KAAK,WAAWA,MAAK;AACxC,QAAI,CAAC,YAAY;AACf,YAAM,SAAQ;AACd;AAAA,IACF;AACA,UAAM,WAAW,QAAQ,MAAM,KAAK,SAASA,SAAQ,GAAG,QAAQ,QAAQ,CAAC;AAAA,EAC3E;AACF;AACA,MAAM,mCAAmC;AAAA;AAAA,EAEvC,YAAY,KAAK,WAAW;AAC1B,SAAK,MAAM;AACX,SAAK,YAAY;AACjB,SAAK,oBAAoB,CAAA;AAAA,EAC3B;AAAA;AAAA,EAEA,MAAM,QAAQ;AACZ,QAAI;AACF,YAAM,KAAK,kBAAiB;AAC5B,YAAM,KAAK,IAAI,MAAK;AAAA,IACtB,SAAS,OAAO;AACd,YAAM,KAAK,iBAAgB;AAC3B,YAAM;AAAA,IACR;AAAA,EACF;AAAA;AAAA,EAEA,MAAM,OAAO;AACX,UAAM,KAAK,IAAI,KAAI;AACnB,UAAM,KAAK,iBAAgB;AAAA,EAC7B;AAAA;AAAA,EAEA,MAAM,UAAU;AACd,UAAM,KAAK,IAAI,QAAO;AACtB,UAAM,KAAK,iBAAgB;AAAA,EAC7B;AAAA;AAAA,EAEA,IAAI,WAAW;AACb,WAAO,KAAK,IAAI;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI,cAAc;AAChB,WAAO,KAAK,IAAI;AAAA,EAClB;AAAA;AAAA,EAEA,MAAM,oBAAoB;AACxB,UAAM,MAAM,KAAK,IAAI,YAAY;AACjC,QAAI,CAAC,IAAI,SAAU;AACnB,eAAW,YAAY,KAAK,WAAW;AACrC,YAAM,QAAQ,SAAS,SAAS,KAAK,IAAI,QAAQ;AACjD,YAAM,WAAW,IAAI,SAAS,QAAQ,SAAS,MAAM,KAAK;AAC1D,WAAK,kBAAkB,KAAK,QAAQ;AAAA,IACtC;AAAA,EACF;AAAA;AAAA,EAEA,MAAM,mBAAmB;AACvB,WAAO,KAAK,kBAAkB,QAAQ;AACpC,YAAM,UAAU,KAAK,kBAAkB,IAAG;AAC1C,UAAI,CAAC,QAAS;AACd,YAAM,QAAO;AAAA,IACf;AAAA,EACF;AACF;AACA,MAAM,qBAAqB;AAAA;AAAA,EAEzB,YAAY,YAAY,WAAW,IAAI;AACrC,SAAK,aAAa;AAClB,SAAK,6BAA6B,CAAA;AAClC,SAAK,qBAAqB,CAAA;AAC1B,SAAK,aAAa,CAAA;AAClB,SAAK,iBAAiB,CAAA;AACtB,SAAK,YAAY,CAAA;AACjB,SAAK,oBAAoB,IAAI,kBAAkB,UAAU;AACzD,SAAK,iBAAiB;AAAA,MACpB,KAAK;AAAA,MACL,iBAAiB,SAAS,eAAe,QAAQ,IAAI,iBAAiB;AAAA,MACtE,YAAY,IAAI,IAAI,OAAO,QAAQ,SAAS,cAAc,CAAA,CAAE,CAAC;AAAA,MAC7D,UAAU,KAAK,kBAAkB,YAAW;AAAA;AAAA,IAElD;AAAA,EACE;AAAA;AAAA,EAEA,IAAI,UAAU;AACZ,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAEA,kBAAkB,UAAU;AAC1B,SAAK,2BAA2B,KAAK,QAAQ;AAC7C,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,UAAU,UAAU;AAClB,SAAK,mBAAmB,KAAK,QAAQ;AACrC,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,IAAI,YAAY;AACd,SAAK,WAAW,KAAK,UAAU;AAC/B,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,iBAAiB,OAAO;AACtB,SAAK,eAAe,KAAK,KAAK;AAC9B,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,kBAAkB,MAAM,UAAU;AAChC,UAAM,aAAa,KAAK,kBAAkB,QAAQ;AAClD,QAAI,YAAY;AACd,WAAK,UAAU,KAAK,EAAE,MAAM,UAAU,YAAY;AAAA,IACpD;AACA,WAAO;AAAA,EACT;AAAA;AAAA,EAEA,MAAM,QAAQ;AACZ,eAAW,aAAa,KAAK,4BAA4B;AACvD,YAAM,UAAU,KAAK,gBAAgB,KAAK,iBAAiB;AAAA,IAC7D;AACA,UAAM,WAAW,KAAK,kBAAkB,qBAAoB;AAC5D,UAAM,cAAc,IAAI;AAAA,MACtB,KAAK;AAAA,MACL;AAAA,MACA,KAAK;AAAA,MACL,KAAK;AAAA,MACL,KAAK;AAAA,IACX;AACI,WAAO,IAAI,WAAW,YAAY,cAAc,KAAK,SAAS,CAAC;AAAA,EACjE;AAAA;AAAA,EAEA,kBAAkB,UAAU;AAC1B,QAAI,SAAS,OAAO;AAClB,aAAO,CAAC,aAAa,SAAS,IAAI,SAAS,KAAK;AAAA,IAClD;AACA,QAAI,SAAS,SAAS;AACpB,aAAO,SAAS;AAAA,IAClB;AACA,WAAO;AAAA,EACT;AACF;AACA,MAAM,WAAW;AAAA,EACf,YAAY,KAAK;AACf,SAAK,MAAM;AAAA,EACb;AAAA;AAAA,EAEA,IAAI,WAAW;AACb,WAAO,KAAK,IAAI;AAAA,EAClB;AAAA;AAAA,EAEA,IAAI,cAAc;AAChB,WAAO,KAAK,IAAI;AAAA,EAClB;AAAA;AAAA,EAEA,MAAM,QAAQ;AACZ,UAAM,KAAK,IAAI,MAAK;AAAA,EACtB;AAAA;AAAA,EAEA,MAAM,OAAO;AACX,UAAM,KAAK,IAAI,KAAI;AAAA,EACrB;AAAA;AAAA,EAEA,MAAM,UAAU;AACd,UAAM,KAAK,IAAI,QAAO;AAAA,EACxB;AAAA;AAAA,EAEA,MAAM,MAAM;AACV,UAAM,KAAK,MAAK;AAChB,WAAO,YAAY;AACjB,YAAM,KAAK,QAAO;AAAA,IACpB;AAAA,EACF;AACF;AACA,SAAS,wBAAwB,KAAK,UAAU;AAC9C,SAAO,IAAI,qBAAqB,KAAK,QAAQ;AAC/C;AACA,MAAM,OAAO;AAAA,EACX,0BAA0B;AAC5B;AACA,SAAS,sBAAsB,OAAO;AACpC,SAAO,eAAe,gBAAgB,KAAK;AACzC,UAAM,UAAU,KAAK,yBAAyB,GAAG;AACjD,UAAM,MAAM,OAAO;AACnB,UAAM,OAAO,MAAM,QAAQ,MAAK;AAChC,WAAO,KAAK,IAAG;AAAA,EACjB;AACF;ACxeA,MAAM,iBAA0C;AAAA,EAC9C,YAA6B,SAAuB;AAAvB,SAAA,UAAA;AAAA,EAAwB;AAAA,EAIrD,MAAM,QAAQ;AACZ,SAAK,WAAW,YAAY,MAAM;AAChC,cAAQ,KAAK,+BAA+B,KAAK,QAAQ,OAAM,oBAAI,QAAO,aAAa;AAAA,IACzF,GAAG,GAAM;AAAA,EACX;AAAA,EAEA,MAAM,OAAO;AACX,QAAI,KAAK,SAAU,eAAc,KAAK,QAAQ;AAAA,EAChD;AACF;AAEA,MAAA,QAAe,sBAAsB,CAAC,YAAY;AAChD,UAAQ,kBAAkB,CAAC,SAAS,aAAa;AAC/C,aAAS,aAAa,iBAAiB,OAAO;AAAA,MAC5C,MAAM,QAAQ,IAAI,QAAQ,WAAW;AAAA,MACrC,WAAW,KAAK,IAAA;AAAA,IAAI,EACpB;AAGF,aAAS,aAAa,kBAAkB,CAAC,OAAO,IAAI,iBAAiB,GAAG,IAAI,eAAe,CAAC,CAAC;AAC7F,aAAS,gBAAgB,qBAAqB,CAAC,OAAO,GAAG,IAAI,gBAAgB,CAAC;AAC9E,YAAQ,IAAI,OAAO;AAAA,MACjB;AAAA,IAAA;AAAA,EAEJ,CAAC;AAED,UAAQ,kBAAkB,iBAAiB,EAAE,OAAO,iBAAiB;AACrE,UAAQ,iBAAiB,mBAAmB;AAE5C,UAAQ,IAAI,OAAO,EAAE,IAAA,GAAO,SAAS;AACnC,QAAI,OAAO,KAAK,8BAA8B;AAC9C,UAAM,KAAA;AACN,QAAI,OAAO,KAAK,gCAAgC;AAAA,EAClD,CAAC;AAED,UAAQ,UAAU,CAAC,MAAM,QAAQ;AAC/B,SAAK,SAAS,UAAU,MAAM;AAC5B,UAAI,IAAI,OAAO,KAAK,sCAAsC;AAAA,IAC5D,CAAC;AAAA,EACH,CAAC;AACH,CAAC;;"}
@@ -0,0 +1,2 @@
1
+ declare const _default: (ctx: import("@dsz-examaware/plugin-sdk").PluginRuntimeContext) => Promise<() => Promise<void>>;
2
+ export default _default;
@@ -0,0 +1,43 @@
1
+
2
+ .plugin-card[data-v-bdfa1e77] {
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: 12px;
6
+ padding: 20px;
7
+ border-radius: 12px;
8
+ background: #fff;
9
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.08);
10
+ }
11
+ .plugin-desc[data-v-bdfa1e77] {
12
+ font-size: 14px;
13
+ color: #5f6c7b;
14
+ }
15
+ .plugin-log[data-v-bdfa1e77] {
16
+ font-size: 12px;
17
+ color: #9aa5b5;
18
+ }
19
+ .plugin-actions[data-v-bdfa1e77] {
20
+ display: flex;
21
+ gap: 10px;
22
+ }
23
+ button[data-v-bdfa1e77] {
24
+ border: none;
25
+ border-radius: 8px;
26
+ padding: 8px 16px;
27
+ font-size: 14px;
28
+ cursor: pointer;
29
+ transition: all 0.2s ease;
30
+ }
31
+ button[data-v-bdfa1e77]:disabled {
32
+ opacity: 0.6;
33
+ cursor: not-allowed;
34
+ }
35
+ button.primary[data-v-bdfa1e77] {
36
+ background: #0052d9;
37
+ color: #fff;
38
+ }
39
+ button.ghost[data-v-bdfa1e77] {
40
+ background: transparent;
41
+ color: #0052d9;
42
+ border: 1px solid #0052d9;
43
+ }
@@ -0,0 +1,7 @@
1
+ import type { PluginRuntimeContext } from '@dsz-examaware/plugin-sdk';
2
+ type __VLS_Props = {
3
+ ctx: PluginRuntimeContext;
4
+ };
5
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
6
+ declare const _default: typeof __VLS_export;
7
+ export default _default;