@advicenxt/sbp-server 0.1.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.
Files changed (63) hide show
  1. package/benchmarks/bench.ts +272 -0
  2. package/dist/auth.d.ts +20 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +69 -0
  5. package/dist/auth.js.map +1 -0
  6. package/dist/blackboard.d.ts +84 -0
  7. package/dist/blackboard.d.ts.map +1 -0
  8. package/dist/blackboard.js +502 -0
  9. package/dist/blackboard.js.map +1 -0
  10. package/dist/cli.d.ts +7 -0
  11. package/dist/cli.d.ts.map +1 -0
  12. package/dist/cli.js +102 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/conditions.d.ts +27 -0
  15. package/dist/conditions.d.ts.map +1 -0
  16. package/dist/conditions.js +240 -0
  17. package/dist/conditions.js.map +1 -0
  18. package/dist/decay.d.ts +21 -0
  19. package/dist/decay.d.ts.map +1 -0
  20. package/dist/decay.js +88 -0
  21. package/dist/decay.js.map +1 -0
  22. package/dist/index.d.ts +13 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +13 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/rate-limiter.d.ts +21 -0
  27. package/dist/rate-limiter.d.ts.map +1 -0
  28. package/dist/rate-limiter.js +75 -0
  29. package/dist/rate-limiter.js.map +1 -0
  30. package/dist/server.d.ts +63 -0
  31. package/dist/server.d.ts.map +1 -0
  32. package/dist/server.js +401 -0
  33. package/dist/server.js.map +1 -0
  34. package/dist/store.d.ts +54 -0
  35. package/dist/store.d.ts.map +1 -0
  36. package/dist/store.js +55 -0
  37. package/dist/store.js.map +1 -0
  38. package/dist/types.d.ts +247 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +26 -0
  41. package/dist/types.js.map +1 -0
  42. package/dist/validation.d.ts +296 -0
  43. package/dist/validation.d.ts.map +1 -0
  44. package/dist/validation.js +205 -0
  45. package/dist/validation.js.map +1 -0
  46. package/eslint.config.js +26 -0
  47. package/package.json +66 -0
  48. package/src/auth.ts +89 -0
  49. package/src/blackboard.test.ts +287 -0
  50. package/src/blackboard.ts +651 -0
  51. package/src/cli.ts +116 -0
  52. package/src/conditions.ts +305 -0
  53. package/src/conformance.test.ts +686 -0
  54. package/src/decay.ts +103 -0
  55. package/src/index.ts +24 -0
  56. package/src/rate-limiter.ts +104 -0
  57. package/src/server.integration.test.ts +436 -0
  58. package/src/server.ts +500 -0
  59. package/src/store.ts +108 -0
  60. package/src/types.ts +314 -0
  61. package/src/validation.ts +251 -0
  62. package/tsconfig.eslint.json +5 -0
  63. package/tsconfig.json +20 -0
@@ -0,0 +1,272 @@
1
+ /**
2
+ * SBP Benchmark Suite
3
+ *
4
+ * Measures performance characteristics of the core blackboard operations.
5
+ * Run: npx tsx benchmarks/bench.ts
6
+ */
7
+
8
+ import { Blackboard } from "../src/blackboard.js";
9
+ import { MemoryStore } from "../src/store.js";
10
+
11
+ // ============================================================================
12
+ // HELPERS
13
+ // ============================================================================
14
+
15
+ function formatOps(count: number, durationMs: number): string {
16
+ const opsPerSec = Math.round((count / durationMs) * 1000);
17
+ return `${opsPerSec.toLocaleString()} ops/sec`;
18
+ }
19
+
20
+ function formatDuration(ms: number): string {
21
+ if (ms < 1) return `${(ms * 1000).toFixed(0)}µs`;
22
+ if (ms < 1000) return `${ms.toFixed(2)}ms`;
23
+ return `${(ms / 1000).toFixed(2)}s`;
24
+ }
25
+
26
+ function runBench(name: string, fn: () => void, iterations: number = 100_000): void {
27
+ // Warmup
28
+ for (let i = 0; i < Math.min(1000, iterations / 10); i++) fn();
29
+
30
+ const start = performance.now();
31
+ for (let i = 0; i < iterations; i++) fn();
32
+ const elapsed = performance.now() - start;
33
+
34
+ const avg = elapsed / iterations;
35
+ console.log(
36
+ ` ${name.padEnd(45)} ${formatOps(iterations, elapsed).padStart(15)} (avg ${formatDuration(avg)})`
37
+ );
38
+ }
39
+
40
+ // ============================================================================
41
+ // BENCHMARKS
42
+ // ============================================================================
43
+
44
+ function benchEmitNew(): void {
45
+ const bb = new Blackboard();
46
+
47
+ console.log("\n📤 EMIT (new pheromone, no merge)");
48
+ let i = 0;
49
+ runBench("emit new pheromone", () => {
50
+ bb.emit({
51
+ trail: "bench.signals",
52
+ type: `type-${i++}`,
53
+ intensity: 0.8,
54
+ merge_strategy: "new",
55
+ });
56
+ }, 50_000);
57
+ }
58
+
59
+ function benchEmitReinforce(): void {
60
+ const bb = new Blackboard();
61
+ bb.emit({
62
+ trail: "bench.signals",
63
+ type: "target",
64
+ intensity: 0.5,
65
+ payload: { key: "value" },
66
+ });
67
+
68
+ console.log("\n🔁 EMIT (reinforce existing)");
69
+ runBench("emit reinforce", () => {
70
+ bb.emit({
71
+ trail: "bench.signals",
72
+ type: "target",
73
+ intensity: 0.7,
74
+ payload: { key: "value" },
75
+ merge_strategy: "reinforce",
76
+ });
77
+ }, 50_000);
78
+ }
79
+
80
+ function benchSniff(pheromoneCount: number): void {
81
+ const bb = new Blackboard();
82
+ for (let i = 0; i < pheromoneCount; i++) {
83
+ bb.emit({
84
+ trail: `bench.trail-${i % 10}`,
85
+ type: `type-${i}`,
86
+ intensity: Math.random(),
87
+ merge_strategy: "new",
88
+ });
89
+ }
90
+
91
+ console.log(`\n👃 SNIFF (${pheromoneCount.toLocaleString()} pheromones in store)`);
92
+
93
+ runBench("sniff all (no filters)", () => {
94
+ bb.sniff({ limit: 100 });
95
+ }, 10_000);
96
+
97
+ runBench("sniff single trail", () => {
98
+ bb.sniff({ trails: ["bench.trail-0"], limit: 100 });
99
+ }, 10_000);
100
+
101
+ runBench("sniff with min_intensity 0.5", () => {
102
+ bb.sniff({ min_intensity: 0.5, limit: 100 });
103
+ }, 10_000);
104
+
105
+ runBench("sniff with tags filter", () => {
106
+ bb.sniff({ tags: { any: ["tag-a"] }, limit: 100 });
107
+ }, 10_000);
108
+ }
109
+
110
+ function benchConditionEvaluation(): void {
111
+ const bb = new Blackboard();
112
+ for (let i = 0; i < 1000; i++) {
113
+ bb.emit({
114
+ trail: "bench.eval",
115
+ type: `signal-${i % 5}`,
116
+ intensity: 0.3 + Math.random() * 0.7,
117
+ merge_strategy: "new",
118
+ });
119
+ }
120
+
121
+ // Register a composite scent
122
+ bb.registerScent({
123
+ scent_id: "bench-scent",
124
+ agent_endpoint: "http://localhost:9999",
125
+ condition: {
126
+ type: "composite",
127
+ operator: "and",
128
+ conditions: [
129
+ {
130
+ type: "threshold",
131
+ trail: "bench.eval",
132
+ signal_type: "signal-0",
133
+ aggregation: "max",
134
+ operator: ">=",
135
+ value: 0.7,
136
+ },
137
+ {
138
+ type: "threshold",
139
+ trail: "bench.eval",
140
+ signal_type: "signal-1",
141
+ aggregation: "count",
142
+ operator: ">=",
143
+ value: 3,
144
+ },
145
+ ],
146
+ },
147
+ cooldown_ms: 0,
148
+ });
149
+
150
+ console.log("\n🧠 CONDITION EVALUATION (1,000 pheromones, composite AND)");
151
+ runBench("evaluateScents (sync part)", () => {
152
+ // Evaluate without triggering (cooldown = 0 means it fires and resets)
153
+ const pheromones = [...(bb as any).store.values()];
154
+ const { evaluateCondition } = require("../src/conditions.js");
155
+ evaluateCondition(
156
+ {
157
+ type: "composite",
158
+ operator: "and",
159
+ conditions: [
160
+ {
161
+ type: "threshold",
162
+ trail: "bench.eval",
163
+ signal_type: "signal-0",
164
+ aggregation: "max",
165
+ operator: ">=",
166
+ value: 0.7,
167
+ },
168
+ {
169
+ type: "threshold",
170
+ trail: "bench.eval",
171
+ signal_type: "signal-1",
172
+ aggregation: "count",
173
+ operator: ">=",
174
+ value: 3,
175
+ },
176
+ ],
177
+ },
178
+ { pheromones, now: Date.now(), emissionHistory: [] }
179
+ );
180
+ }, 10_000);
181
+ }
182
+
183
+ function benchGarbageCollection(): void {
184
+ const bb = new Blackboard({ defaultDecay: { type: "linear", rate_per_ms: 100 } });
185
+ for (let i = 0; i < 10_000; i++) {
186
+ bb.emit({
187
+ trail: "bench.gc",
188
+ type: `type-${i}`,
189
+ intensity: 0.5,
190
+ merge_strategy: "new",
191
+ });
192
+ }
193
+ // Let them evaporate
194
+ const delay = (ms: number) => {
195
+ const end = Date.now() + ms;
196
+ while (Date.now() < end) { }
197
+ };
198
+ delay(50);
199
+
200
+ console.log("\n🧹 GARBAGE COLLECTION (10,000 evaporated pheromones)");
201
+ const start = performance.now();
202
+ const removed = bb.gc();
203
+ const elapsed = performance.now() - start;
204
+ console.log(` gc() removed ${removed.toLocaleString()} pheromones in ${formatDuration(elapsed)}`);
205
+ }
206
+
207
+ function benchStoreOperations(): void {
208
+ console.log("\n💾 STORE OPERATIONS (MemoryStore)");
209
+ const store = new MemoryStore();
210
+
211
+ runBench("store.set + store.get", () => {
212
+ const p = {
213
+ id: "test-id",
214
+ trail: "bench",
215
+ type: "test",
216
+ emitted_at: Date.now(),
217
+ last_reinforced_at: Date.now(),
218
+ initial_intensity: 0.5,
219
+ decay_model: { type: "exponential" as const, half_life_ms: 300000 },
220
+ payload: {},
221
+ tags: [],
222
+ ttl_floor: 0.01,
223
+ };
224
+ store.set("test-id", p);
225
+ store.get("test-id");
226
+ }, 100_000);
227
+
228
+ // Fill store then iterate
229
+ for (let i = 0; i < 10_000; i++) {
230
+ store.set(`p-${i}`, {
231
+ id: `p-${i}`,
232
+ trail: "bench",
233
+ type: "test",
234
+ emitted_at: Date.now(),
235
+ last_reinforced_at: Date.now(),
236
+ initial_intensity: 0.5,
237
+ decay_model: { type: "exponential" as const, half_life_ms: 300000 },
238
+ payload: {},
239
+ tags: [],
240
+ ttl_floor: 0.01,
241
+ });
242
+ }
243
+
244
+ runBench("store.values() iteration (10k entries)", () => {
245
+ let count = 0;
246
+ for (const _p of store.values()) count++;
247
+ }, 1_000);
248
+ }
249
+
250
+ // ============================================================================
251
+ // MAIN
252
+ // ============================================================================
253
+
254
+ console.log("═══════════════════════════════════════════════════════════════");
255
+ console.log(" SBP Benchmark Suite");
256
+ console.log("═══════════════════════════════════════════════════════════════");
257
+ console.log(` Node ${process.version} | ${process.platform} ${process.arch}`);
258
+ console.log(` Date: ${new Date().toISOString()}`);
259
+ console.log("═══════════════════════════════════════════════════════════════");
260
+
261
+ benchEmitNew();
262
+ benchEmitReinforce();
263
+ benchSniff(100);
264
+ benchSniff(1_000);
265
+ benchSniff(10_000);
266
+ benchConditionEvaluation();
267
+ benchGarbageCollection();
268
+ benchStoreOperations();
269
+
270
+ console.log("\n═══════════════════════════════════════════════════════════════");
271
+ console.log(" Done.");
272
+ console.log("═══════════════════════════════════════════════════════════════\n");
package/dist/auth.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * SBP Authentication Middleware
3
+ * Basic API key authentication for the SBP server
4
+ */
5
+ import type { FastifyRequest, FastifyReply, HookHandlerDoneFunction } from "fastify";
6
+ export interface AuthOptions {
7
+ /** List of valid API keys */
8
+ apiKeys?: string[];
9
+ /** Whether authentication is required (default: false) */
10
+ requireAuth?: boolean;
11
+ }
12
+ /**
13
+ * Create a Fastify onRequest hook for API key authentication.
14
+ *
15
+ * Checks the `Authorization: Bearer <key>` header against the
16
+ * configured list of API keys. Returns 401 with SBP error code
17
+ * -32005 (UNAUTHORIZED) if the key is missing or invalid.
18
+ */
19
+ export declare function createAuthHook(options: AuthOptions): (request: FastifyRequest, reply: FastifyReply, done: HookHandlerDoneFunction) => void;
20
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAErF,MAAM,WAAW,WAAW;IACxB,6BAA6B;IAC7B,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,OAAO,CAAC;CACzB;AAKD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,WAAW,IAI3C,SAAS,cAAc,EACvB,OAAO,YAAY,EACnB,MAAM,uBAAuB,KAC9B,IAAI,CAyDV"}
package/dist/auth.js ADDED
@@ -0,0 +1,69 @@
1
+ /**
2
+ * SBP Authentication Middleware
3
+ * Basic API key authentication for the SBP server
4
+ */
5
+ /** Paths that skip authentication */
6
+ const PUBLIC_PATHS = ["/health"];
7
+ /**
8
+ * Create a Fastify onRequest hook for API key authentication.
9
+ *
10
+ * Checks the `Authorization: Bearer <key>` header against the
11
+ * configured list of API keys. Returns 401 with SBP error code
12
+ * -32005 (UNAUTHORIZED) if the key is missing or invalid.
13
+ */
14
+ export function createAuthHook(options) {
15
+ const { apiKeys = [], requireAuth = false } = options;
16
+ return function authHook(request, reply, done) {
17
+ // Skip auth if not required
18
+ if (!requireAuth || apiKeys.length === 0) {
19
+ done();
20
+ return;
21
+ }
22
+ // Skip auth for public paths and OPTIONS
23
+ const url = request.url.split("?")[0];
24
+ if (PUBLIC_PATHS.includes(url) || request.method === "OPTIONS") {
25
+ done();
26
+ return;
27
+ }
28
+ // Extract bearer token
29
+ const authHeader = request.headers.authorization;
30
+ if (!authHeader) {
31
+ reply.status(401).send({
32
+ jsonrpc: "2.0",
33
+ id: null,
34
+ error: {
35
+ code: -32005,
36
+ message: "Unauthorized: Missing Authorization header",
37
+ },
38
+ });
39
+ return;
40
+ }
41
+ const parts = authHeader.split(" ");
42
+ if (parts.length !== 2 || parts[0].toLowerCase() !== "bearer") {
43
+ reply.status(401).send({
44
+ jsonrpc: "2.0",
45
+ id: null,
46
+ error: {
47
+ code: -32005,
48
+ message: "Unauthorized: Invalid Authorization header format. Expected: Bearer <api-key>",
49
+ },
50
+ });
51
+ return;
52
+ }
53
+ const token = parts[1];
54
+ if (!apiKeys.includes(token)) {
55
+ reply.status(401).send({
56
+ jsonrpc: "2.0",
57
+ id: null,
58
+ error: {
59
+ code: -32005,
60
+ message: "Unauthorized: Invalid API key",
61
+ },
62
+ });
63
+ return;
64
+ }
65
+ // Valid key — attach agent info from token position (optional extension point)
66
+ done();
67
+ };
68
+ }
69
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAWH,qCAAqC;AACrC,MAAM,YAAY,GAAG,CAAC,SAAS,CAAC,CAAC;AAEjC;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAAC,OAAoB;IAC/C,MAAM,EAAE,OAAO,GAAG,EAAE,EAAE,WAAW,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IAEtD,OAAO,SAAS,QAAQ,CACpB,OAAuB,EACvB,KAAmB,EACnB,IAA6B;QAE7B,4BAA4B;QAC5B,IAAI,CAAC,WAAW,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvC,IAAI,EAAE,CAAC;YACP,OAAO;QACX,CAAC;QAED,yCAAyC;QACzC,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACtC,IAAI,YAAY,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7D,IAAI,EAAE,CAAC;YACP,OAAO;QACX,CAAC;QAED,uBAAuB;QACvB,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QACjD,IAAI,CAAC,UAAU,EAAE,CAAC;YACd,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,KAAK;gBACd,EAAE,EAAE,IAAI;gBACR,KAAK,EAAE;oBACH,IAAI,EAAE,CAAC,KAAK;oBACZ,OAAO,EAAE,4CAA4C;iBACxD;aACJ,CAAC,CAAC;YACH,OAAO;QACX,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,QAAQ,EAAE,CAAC;YAC5D,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,KAAK;gBACd,EAAE,EAAE,IAAI;gBACR,KAAK,EAAE;oBACH,IAAI,EAAE,CAAC,KAAK;oBACZ,OAAO,EAAE,+EAA+E;iBAC3F;aACJ,CAAC,CAAC;YACH,OAAO;QACX,CAAC;QAED,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACvB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC;gBACnB,OAAO,EAAE,KAAK;gBACd,EAAE,EAAE,IAAI;gBACR,KAAK,EAAE;oBACH,IAAI,EAAE,CAAC,KAAK;oBACZ,OAAO,EAAE,+BAA+B;iBAC3C;aACJ,CAAC,CAAC;YACH,OAAO;QACX,CAAC;QAED,+EAA+E;QAC/E,IAAI,EAAE,CAAC;IACX,CAAC,CAAC;AACN,CAAC"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * SBP Blackboard - Core State Management
3
+ */
4
+ import type { PheromoneStore } from "./store.js";
5
+ import type { DecayModel, EmitParams, EmitResult, SniffParams, SniffResult, RegisterScentParams, RegisterScentResult, DeregisterScentParams, DeregisterScentResult, EvaporateParams, EvaporateResult, InspectParams, InspectResult, TriggerPayload } from "./types.js";
6
+ export interface BlackboardOptions {
7
+ /** Interval for scent evaluation in ms (default: 100) */
8
+ evaluationInterval?: number;
9
+ /** Default decay model for new pheromones */
10
+ defaultDecay?: DecayModel;
11
+ /** Default TTL floor for evaporation */
12
+ defaultTtlFloor?: number;
13
+ /** Maximum pheromones before GC triggers */
14
+ maxPheromones?: number;
15
+ /** Enable emission history tracking for rate conditions */
16
+ trackEmissionHistory?: boolean;
17
+ /** How long to keep emission history (ms) */
18
+ emissionHistoryWindow?: number;
19
+ /** Pluggable pheromone storage backend (default: MemoryStore) */
20
+ store?: PheromoneStore;
21
+ }
22
+ export interface TriggerHandler {
23
+ (payload: TriggerPayload): Promise<void>;
24
+ }
25
+ export declare class Blackboard {
26
+ private store;
27
+ private scents;
28
+ private triggerHandlers;
29
+ private emissionHistory;
30
+ private evaluationTimer;
31
+ private startTime;
32
+ private options;
33
+ constructor(options?: BlackboardOptions);
34
+ emit(params: EmitParams): EmitResult;
35
+ sniff(params?: SniffParams): SniffResult;
36
+ registerScent(params: RegisterScentParams): RegisterScentResult;
37
+ deregisterScent(params: DeregisterScentParams): DeregisterScentResult;
38
+ evaporate(params?: EvaporateParams): EvaporateResult;
39
+ inspect(params?: InspectParams): InspectResult;
40
+ /**
41
+ * Register a local handler for a scent
42
+ */
43
+ onTrigger(scentId: string, handler: TriggerHandler): void;
44
+ /**
45
+ * Remove a local trigger handler
46
+ */
47
+ offTrigger(scentId: string): void;
48
+ /**
49
+ * Start the background scent evaluation loop
50
+ */
51
+ start(): void;
52
+ /**
53
+ * Stop the background evaluation loop
54
+ */
55
+ stop(): void;
56
+ /**
57
+ * Evaluate all scents and trigger as needed
58
+ */
59
+ evaluateScents(): Promise<void>;
60
+ /**
61
+ * Determine if a scent should trigger based on mode
62
+ */
63
+ private shouldTrigger;
64
+ /**
65
+ * Dispatch a trigger to the agent
66
+ */
67
+ private dispatchTrigger;
68
+ /**
69
+ * Garbage collect evaporated pheromones
70
+ */
71
+ gc(): number;
72
+ /**
73
+ * Get raw pheromone count (for testing)
74
+ */
75
+ get size(): number;
76
+ /**
77
+ * Get scent count
78
+ */
79
+ get scentCount(): number;
80
+ private hashPayload;
81
+ private matchTags;
82
+ private pruneEmissionHistory;
83
+ }
84
+ //# sourceMappingURL=blackboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blackboard.d.ts","sourceRoot":"","sources":["../src/blackboard.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,OAAO,KAAK,EAIV,UAAU,EACV,UAAU,EACV,UAAU,EACV,WAAW,EACX,WAAW,EAEX,mBAAmB,EACnB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,aAAa,EACb,aAAa,EACb,cAAc,EAEf,MAAM,YAAY,CAAC;AAKpB,MAAM,WAAW,iBAAiB;IAChC,yDAAyD;IACzD,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,6CAA6C;IAC7C,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B,wCAAwC;IACxC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,4CAA4C;IAC5C,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,2DAA2D;IAC3D,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,6CAA6C;IAC7C,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,iEAAiE;IACjE,KAAK,CAAC,EAAE,cAAc,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAiB;IAC9B,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,eAAe,CAAqC;IAC5D,OAAO,CAAC,eAAe,CAAiE;IACxF,OAAO,CAAC,eAAe,CAA+C;IACtE,OAAO,CAAC,SAAS,CAAc;IAE/B,OAAO,CAAC,OAAO,CAA6C;gBAEhD,OAAO,GAAE,iBAAsB;IAgB3C,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,UAAU;IAoHpC,KAAK,CAAC,MAAM,GAAE,WAAgB,GAAG,WAAW;IAwE5C,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,mBAAmB;IAoD/D,eAAe,CAAC,MAAM,EAAE,qBAAqB,GAAG,qBAAqB;IAgBrE,SAAS,CAAC,MAAM,GAAE,eAAoB,GAAG,eAAe;IAgCxD,OAAO,CAAC,MAAM,GAAE,aAAkB,GAAG,aAAa;IA0DlD;;OAEG;IACH,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,IAAI;IAIzD;;OAEG;IACH,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;IAQjC;;OAEG;IACH,KAAK,IAAI,IAAI;IAUb;;OAEG;IACH,IAAI,IAAI,IAAI;IAOZ;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IA0BrC;;OAEG;IACH,OAAO,CAAC,aAAa;IAgBrB;;OAEG;YACW,eAAe;IA6E7B;;OAEG;IACH,EAAE,IAAI,MAAM;IAiBZ;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,IAAI,UAAU,IAAI,MAAM,CAEvB;IAED,OAAO,CAAC,WAAW;IAKnB,OAAO,CAAC,SAAS;IAajB,OAAO,CAAC,oBAAoB;CAI7B"}