@polderlabs/bizar 4.8.0 → 5.0.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 (129) hide show
  1. package/bizar-dash/dist/assets/icons-CFqu2M-c.js +656 -0
  2. package/bizar-dash/dist/assets/icons-CFqu2M-c.js.map +1 -0
  3. package/bizar-dash/dist/assets/index-DmpSFPJY.js +9 -0
  4. package/bizar-dash/dist/assets/index-DmpSFPJY.js.map +1 -0
  5. package/bizar-dash/dist/assets/main-Dl8yY5_H.js +16 -0
  6. package/bizar-dash/dist/assets/main-Dl8yY5_H.js.map +1 -0
  7. package/bizar-dash/dist/assets/{main-DX_Jh8Wc.css → main-ZAfGKENE.css} +1 -1
  8. package/bizar-dash/dist/assets/markdown-DIquRulQ.js +29 -0
  9. package/bizar-dash/dist/assets/markdown-DIquRulQ.js.map +1 -0
  10. package/bizar-dash/dist/assets/mobile-C2gysFOZ.js +2 -0
  11. package/bizar-dash/dist/assets/mobile-C2gysFOZ.js.map +1 -0
  12. package/bizar-dash/dist/assets/mobile-DHXXbn1A.js +1 -0
  13. package/bizar-dash/dist/assets/{mobile-Chvf9u_B.js.map → mobile-DHXXbn1A.js.map} +1 -1
  14. package/bizar-dash/dist/assets/react-vendor-DZRUXSPQ.js +40 -0
  15. package/bizar-dash/dist/assets/react-vendor-DZRUXSPQ.js.map +1 -0
  16. package/bizar-dash/dist/index.html +6 -3
  17. package/bizar-dash/dist/mobile.html +5 -2
  18. package/bizar-dash/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  19. package/bizar-dash/skills/eval/SKILL.md +237 -0
  20. package/bizar-dash/src/server/api.mjs +28 -0
  21. package/bizar-dash/src/server/auth.mjs +155 -1
  22. package/bizar-dash/src/server/eval-store.mjs +226 -0
  23. package/bizar-dash/src/server/eval.mjs +347 -0
  24. package/bizar-dash/src/server/memory-lightrag.mjs +109 -0
  25. package/bizar-dash/src/server/memory-store.mjs +121 -0
  26. package/bizar-dash/src/server/ocr.mjs +55 -0
  27. package/bizar-dash/src/server/otel.mjs +133 -0
  28. package/bizar-dash/src/server/plugins/registry.mjs +363 -0
  29. package/bizar-dash/src/server/plugins/sandbox.mjs +655 -0
  30. package/bizar-dash/src/server/plugins/store.mjs +659 -0
  31. package/bizar-dash/src/server/routes/chat.mjs +246 -170
  32. package/bizar-dash/src/server/routes/clipboard.mjs +173 -0
  33. package/bizar-dash/src/server/routes/eval.mjs +147 -0
  34. package/bizar-dash/src/server/routes/memory.mjs +46 -0
  35. package/bizar-dash/src/server/routes/ocr.mjs +182 -0
  36. package/bizar-dash/src/server/routes/opencode-sessions.mjs +82 -48
  37. package/bizar-dash/src/server/routes/plugins.mjs +220 -0
  38. package/bizar-dash/src/server/routes/users.mjs +84 -0
  39. package/bizar-dash/src/server/routes/voice.mjs +131 -0
  40. package/bizar-dash/src/server/routes/workspaces.mjs +204 -0
  41. package/bizar-dash/src/server/server.mjs +40 -0
  42. package/bizar-dash/src/server/voice-store.mjs +202 -0
  43. package/bizar-dash/src/server/voice-transcribe.mjs +72 -0
  44. package/bizar-dash/src/server/workspaces.mjs +626 -0
  45. package/bizar-dash/src/web/components/InviteDialog.tsx +205 -0
  46. package/bizar-dash/src/web/components/ScreenshotCapture.tsx +138 -0
  47. package/bizar-dash/src/web/components/ScreenshotOCR.tsx +42 -0
  48. package/bizar-dash/src/web/components/SettingsSearch.tsx +204 -89
  49. package/bizar-dash/src/web/components/VoiceNotesPanel.tsx +182 -0
  50. package/bizar-dash/src/web/components/VoiceRecorder.tsx +104 -0
  51. package/bizar-dash/src/web/components/WorkspaceSelector.tsx +158 -0
  52. package/bizar-dash/src/web/lib/search.ts +115 -0
  53. package/bizar-dash/src/web/mobile/views/MobileSettings.tsx +10 -35
  54. package/bizar-dash/src/web/mobile/views/QrCodePanel.tsx +69 -0
  55. package/bizar-dash/src/web/styles/memory.css +166 -1
  56. package/bizar-dash/src/web/styles/settings.css +80 -0
  57. package/bizar-dash/src/web/views/Memory.tsx +22 -2
  58. package/bizar-dash/src/web/views/Settings.tsx +99 -0
  59. package/bizar-dash/src/web/views/Workspace.tsx +294 -0
  60. package/bizar-dash/src/web/views/memory/FromScreenshotPanel.tsx +23 -0
  61. package/bizar-dash/src/web/views/memory/MemoryGraphLegend.tsx +29 -0
  62. package/bizar-dash/src/web/views/memory/MemoryGraphPanel.tsx +192 -0
  63. package/bizar-dash/src/web/views/memory/MemoryGraphView.tsx +336 -0
  64. package/bizar-dash/src/web/views/memory/VaultFromClipboardPanel.tsx +101 -0
  65. package/bizar-dash/src/web/views/settings/WorkspacesSection.tsx +189 -0
  66. package/bizar-dash/tests/bundle-analysis.test.mjs +73 -0
  67. package/bizar-dash/tests/clipboard.test.mjs +147 -0
  68. package/bizar-dash/tests/components/screenshot-ocr.test.tsx +75 -0
  69. package/bizar-dash/tests/components/settings-search.test.tsx +180 -0
  70. package/bizar-dash/tests/components/workspace-selector.test.tsx +73 -0
  71. package/bizar-dash/tests/deploy-templates.test.mjs +100 -0
  72. package/bizar-dash/tests/docker-build.test.mjs +96 -0
  73. package/bizar-dash/tests/eval/fixtures.test.mjs +141 -0
  74. package/bizar-dash/tests/eval/report.test.mjs +284 -0
  75. package/bizar-dash/tests/eval/runner.test.mjs +471 -0
  76. package/bizar-dash/tests/lib/search-fuzzy.test.ts +149 -0
  77. package/bizar-dash/tests/memory-graph-view.test.tsx +69 -0
  78. package/bizar-dash/tests/memory-graph.test.mjs +95 -0
  79. package/bizar-dash/tests/ocr.test.mjs +87 -0
  80. package/bizar-dash/tests/otel.test.mjs +188 -0
  81. package/bizar-dash/tests/plugins-registry.test.mjs +387 -0
  82. package/bizar-dash/tests/plugins-sandbox.test.mjs +374 -0
  83. package/bizar-dash/tests/plugins-store.test.mjs +455 -0
  84. package/bizar-dash/tests/users.test.mjs +108 -0
  85. package/bizar-dash/tests/voice-recorder.test.tsx +95 -0
  86. package/bizar-dash/tests/voice-store.test.mjs +148 -0
  87. package/bizar-dash/tests/voice-transcribe.test.mjs +87 -0
  88. package/bizar-dash/tests/workspaces.test.mjs +527 -0
  89. package/cli/bin.mjs +72 -2
  90. package/cli/commands/clip.mjs +146 -0
  91. package/cli/commands/dash.mjs +6 -0
  92. package/cli/commands/deploy/cloudflare.mjs +250 -0
  93. package/cli/commands/deploy/docker.mjs +221 -0
  94. package/cli/commands/deploy/fly.mjs +161 -0
  95. package/cli/commands/deploy/vercel.mjs +225 -0
  96. package/cli/commands/deploy.mjs +240 -0
  97. package/cli/commands/eval.mjs +378 -0
  98. package/cli/commands/marketplace.mjs +64 -0
  99. package/cli/commands/ocr.mjs +165 -0
  100. package/cli/commands/plugin.mjs +358 -0
  101. package/cli/commands/voice.mjs +211 -0
  102. package/cli/commands/workspace.mjs +247 -0
  103. package/package.json +12 -2
  104. package/templates/deploy/cloudflare/README.md +32 -0
  105. package/templates/deploy/cloudflare/functions-index.template.js +15 -0
  106. package/templates/deploy/cloudflare/wrangler.toml.template +9 -0
  107. package/templates/deploy/docker/.env.template +16 -0
  108. package/templates/deploy/docker/README.md +58 -0
  109. package/templates/deploy/docker/docker-compose.template.yml +23 -0
  110. package/templates/deploy/fly/README.md +35 -0
  111. package/templates/deploy/fly/fly.toml.template +28 -0
  112. package/templates/deploy/vercel/README.md +29 -0
  113. package/templates/deploy/vercel/api-index.template.js +18 -0
  114. package/templates/deploy/vercel/vercel.json.template +16 -0
  115. package/templates/eval-fixtures/README.md +58 -0
  116. package/templates/eval-fixtures/code-search-basic.json +28 -0
  117. package/templates/eval-fixtures/latency-bounds.json +16 -0
  118. package/templates/eval-fixtures/regression-suite.json +79 -0
  119. package/templates/eval-fixtures/response-format.json +30 -0
  120. package/templates/eval-fixtures/tool-call-correctness.json +24 -0
  121. package/templates/plugin-template/README.md +121 -0
  122. package/templates/plugin-template/index.js +66 -0
  123. package/templates/plugin-template/plugin.json +42 -0
  124. package/templates/plugin-template/tests/plugin.test.js +83 -0
  125. package/bizar-dash/dist/assets/main-DHZmbnxQ.js +0 -361
  126. package/bizar-dash/dist/assets/main-DHZmbnxQ.js.map +0 -1
  127. package/bizar-dash/dist/assets/mobile-BK8-ythT.js +0 -351
  128. package/bizar-dash/dist/assets/mobile-BK8-ythT.js.map +0 -1
  129. package/bizar-dash/dist/assets/mobile-Chvf9u_B.js +0 -1
@@ -0,0 +1,95 @@
1
+ /**
2
+ * tests/memory-graph.test.mjs
3
+ *
4
+ * Tests for getObsidianLinkGraph in memory-store.mjs
5
+ * and getLightRAGGraph in memory-lightrag.mjs.
6
+ */
7
+
8
+ import { describe, it, beforeEach, afterEach } from 'node:test';
9
+ import assert from 'node:assert';
10
+ import { tmpdir } from 'node:os';
11
+ import { join } from 'node:path';
12
+ import { mkdirSync, rmSync, writeFileSync } from 'node:fs';
13
+
14
+ const TEST_STORE = await import('../src/server/memory-store.mjs').then((m) => m);
15
+
16
+ describe('getObsidianLinkGraph', () => {
17
+ let projectRoot;
18
+
19
+ beforeEach(() => {
20
+ projectRoot = join(tmpdir(), `bizar-graph-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
21
+ mkdirSync(projectRoot, { recursive: true });
22
+ mkdirSync(join(projectRoot, '.bizar'), { recursive: true });
23
+ });
24
+
25
+ afterEach(() => {
26
+ try { rmSync(projectRoot, { recursive: true, force: true }); } catch { /* ignore */ }
27
+ });
28
+
29
+ function writeNote(relPath, body) {
30
+ const full = join(projectRoot, '.obsidian', relPath);
31
+ mkdirSync(join(projectRoot, '.obsidian', relPath.split('/').slice(0, -1).join('/') || '.'), { recursive: true });
32
+ const title = relPath.split('/').pop()?.replace(/\.md$/i, '') || relPath;
33
+ writeFileSync(full, `---\ntitle: ${title}\n---\n\n${body}`, 'utf8');
34
+ }
35
+
36
+ it('extracts wikilinks and builds nodes + edges', () => {
37
+ writeNote('notes/alpha.md', 'This links to [[Beta Note]] and [[Gamma]].');
38
+ writeNote('notes/beta.md', 'Back to [[Alpha]].');
39
+ writeNote('notes/gamma.md', 'No links here.');
40
+
41
+ const { nodes, edges } = TEST_STORE.getObsidianLinkGraph({ projectRoot, limit: 200 });
42
+
43
+ // Should have 3 nodes.
44
+ assert.ok(nodes.length >= 3, `Expected >= 3 nodes, got ${nodes.length}`);
45
+
46
+ // Each node should have required fields.
47
+ for (const n of nodes) {
48
+ assert.ok('id' in n, 'node must have id');
49
+ assert.ok('label' in n, 'node must have label');
50
+ assert.ok('type' in n, 'node must have type');
51
+ assert.ok('size' in n, 'node must have size');
52
+ assert.ok('group' in n, 'node must have group');
53
+ }
54
+
55
+ // Should have edges.
56
+ assert.ok(edges.length > 0, 'Expected at least 1 edge');
57
+ for (const e of edges) {
58
+ assert.ok('source' in e, 'edge must have source');
59
+ assert.ok('target' in e, 'edge must have target');
60
+ assert.ok('type' in e, 'edge must have type');
61
+ assert.ok('weight' in e, 'edge must have weight');
62
+ }
63
+ });
64
+
65
+ it('handles empty vault gracefully', () => {
66
+ const { nodes, edges } = TEST_STORE.getObsidianLinkGraph({ projectRoot, limit: 200 });
67
+ assert.ok(Array.isArray(nodes), 'nodes must be an array');
68
+ assert.ok(Array.isArray(edges), 'edges must be an array');
69
+ });
70
+
71
+ it('resolves wikilink targets by basename', () => {
72
+ writeNote('foo.md', 'See [[Bar]] for details.');
73
+ writeNote('bar.md', 'Related to [[Foo]].');
74
+ const { edges } = TEST_STORE.getObsidianLinkGraph({ projectRoot, limit: 200 });
75
+ // Should have resolved [[Bar]] -> bar.md and [[Foo]] -> foo.md.
76
+ assert.ok(edges.length >= 1, 'Expected at least 1 edge from wikilink resolution');
77
+ });
78
+
79
+ it('respects the limit parameter', () => {
80
+ for (let i = 0; i < 10; i++) {
81
+ writeNote(`note${i}.md`, i === 0 ? 'Links to [[Note1]].' : '');
82
+ }
83
+ const { nodes, edges } = TEST_STORE.getObsidianLinkGraph({ projectRoot, limit: 3 });
84
+ assert.ok(nodes.length <= 3, `Expected <= 3 nodes, got ${nodes.length}`);
85
+ assert.ok(edges.length <= 9, `Expected <= 9 edges, got ${edges.length}`);
86
+ });
87
+
88
+ it('node label uses title frontmatter when available', () => {
89
+ writeNote('mydoc.md', 'Content.');
90
+ const { nodes } = TEST_STORE.getObsidianLinkGraph({ projectRoot, limit: 200 });
91
+ const mydoc = nodes.find((n) => n.id === 'mydoc.md');
92
+ assert.ok(mydoc, 'mydoc.md should be a node');
93
+ assert.strictEqual(mydoc.label, 'mydoc');
94
+ });
95
+ });
@@ -0,0 +1,87 @@
1
+ /**
2
+ * tests/ocr.test.mjs
3
+ *
4
+ * v5.0.0 — Tests for the OCR helper module and routes.
5
+ *
6
+ * The extractText tests require tesseract.js to be installed.
7
+ */
8
+
9
+ import { describe, it, beforeEach, afterEach, before } from 'node:test';
10
+ import assert from 'node:assert/strict';
11
+ import { tmpdir } from 'node:os';
12
+ import { join } from 'node:path';
13
+ import { mkdirSync, rmSync } from 'node:fs';
14
+
15
+ // ── Test OCR helper ────────────────────────────────────────────────────────────
16
+
17
+ describe('ocr.extractText', () => {
18
+ let ocrModule;
19
+
20
+ before(async () => {
21
+ try {
22
+ ocrModule = await import('../src/server/ocr.mjs');
23
+ } catch {
24
+ // tesseract.js or its deps not available
25
+ }
26
+ });
27
+
28
+ it('extractText returns a string for a valid PNG buffer', { skip: !ocrModule }, async () => {
29
+ const pngBuffer = Buffer.from(
30
+ 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
31
+ 'base64',
32
+ );
33
+ const text = await ocrModule.extractText(pngBuffer);
34
+ assert.strictEqual(typeof text, 'string');
35
+ });
36
+
37
+ it('checkOcrHealth returns ok or error message', { skip: !ocrModule }, async () => {
38
+ const health = await ocrModule.checkOcrHealth();
39
+ assert.ok(typeof health.ok === 'boolean');
40
+ assert.ok(typeof health.message === 'string');
41
+ });
42
+ });
43
+
44
+ // ── Test OCR routes ────────────────────────────────────────────────────────────
45
+
46
+ describe('ocr routes', () => {
47
+ let projectRoot;
48
+ let server;
49
+ let baseUrl;
50
+ let ocrRouterModule;
51
+
52
+ before(async () => {
53
+ ocrRouterModule = await import('../src/server/routes/ocr.mjs');
54
+ });
55
+
56
+ beforeEach(async () => {
57
+ projectRoot = join(tmpdir(), `bizar-ocr-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
58
+ mkdirSync(projectRoot, { recursive: true });
59
+ mkdirSync(join(projectRoot, '.bizar'), { recursive: true });
60
+
61
+ const express = (await import('express')).default;
62
+ const router = await ocrRouterModule.createOcrRouter({ projectRoot });
63
+ const app = express();
64
+ app.use(express.json({ limit: '25mb' }));
65
+ app.use('/api', router);
66
+
67
+ await new Promise((resolve) => {
68
+ server = app.listen(0, () => {
69
+ const addr = server.address();
70
+ baseUrl = `http://127.0.0.1:${addr.port}`;
71
+ resolve();
72
+ });
73
+ });
74
+ });
75
+
76
+ afterEach(() => {
77
+ try { server?.close?.(); } catch { /* ignore */ }
78
+ try { rmSync(projectRoot, { recursive: true, force: true }); } catch { /* ignore */ }
79
+ });
80
+
81
+ it('GET /api/ocr/list returns entries array', async () => {
82
+ const r = await fetch(`${baseUrl}/api/ocr/list`);
83
+ assert.strictEqual(r.status, 200);
84
+ const data = await r.json();
85
+ assert.ok(Array.isArray(data.entries));
86
+ });
87
+ });
@@ -0,0 +1,188 @@
1
+ /**
2
+ * tests/otel.test.mjs — v4.9.0
3
+ *
4
+ * Tests for src/server/otel.mjs. The OpenTelemetry SDK and helpers
5
+ * must:
6
+ * - export a `tracer` that has `startActiveSpan` (works even before
7
+ * `initOtel()` is called, because the no-op global tracer covers
8
+ * the uninitialised case)
9
+ * - return an SDK instance from `initOtel()` and only initialise
10
+ * once across repeated calls
11
+ * - shut down cleanly via `shutdownOtel()`, even when the OTLP
12
+ * endpoint is unreachable
13
+ * - tolerate a malformed endpoint / thrown init error without
14
+ * crashing the importing module
15
+ *
16
+ * Each test loads the module via a fresh URL to defeat Node's ESM
17
+ * module cache; `otel.mjs` uses module-scoped state (the `sdk` and
18
+ * `shuttingDown` lets) and we want each test to see its own clean
19
+ * registry the same way `metrics.test.mjs` does.
20
+ */
21
+
22
+ import test from 'node:test';
23
+ import assert from 'node:assert/strict';
24
+
25
+ const OTEL_URL = '../src/server/otel.mjs';
26
+
27
+ async function loadOtel() {
28
+ const url = `${OTEL_URL}?v=${Date.now()}-${Math.random()}`;
29
+ return import(url);
30
+ }
31
+
32
+ test('tracer is exported and has startActiveSpan', async () => {
33
+ const otel = await loadOtel();
34
+ try {
35
+ assert.equal(typeof otel.tracer, 'object');
36
+ assert.equal(typeof otel.tracer.startActiveSpan, 'function');
37
+ } finally {
38
+ await otel.shutdownOtel();
39
+ }
40
+ });
41
+
42
+ test('initOtel() returns an SDK instance and is idempotent', async () => {
43
+ const otel = await loadOtel();
44
+ try {
45
+ // Before init: isOtelEnabled() is false (fresh module load).
46
+ assert.equal(otel.isOtelEnabled(), false);
47
+ const sdk1 = otel.initOtel({
48
+ // Use an endpoint that won't connect; the test only cares
49
+ // that the SDK object is returned, not that spans export.
50
+ endpoint: 'http://127.0.0.1:1/v1/traces',
51
+ });
52
+ assert.ok(sdk1, 'initOtel should return a non-null SDK instance');
53
+ assert.equal(otel.isOtelEnabled(), true);
54
+
55
+ const sdk2 = otel.initOtel({ endpoint: 'http://127.0.0.1:1/v1/traces' });
56
+ assert.equal(
57
+ sdk1,
58
+ sdk2,
59
+ 'a second initOtel() call must return the cached SDK instance',
60
+ );
61
+ } finally {
62
+ await otel.shutdownOtel();
63
+ }
64
+ });
65
+
66
+ test('shutdownOtel() is callable and resets the SDK cache', async () => {
67
+ const otel = await loadOtel();
68
+ try {
69
+ otel.initOtel({ endpoint: 'http://127.0.0.1:1/v1/traces' });
70
+ assert.equal(otel.isOtelEnabled(), true);
71
+ await otel.shutdownOtel();
72
+ assert.equal(otel.isOtelEnabled(), false);
73
+
74
+ // Calling shutdownOtel again must be a no-op (does not throw).
75
+ await otel.shutdownOtel();
76
+ assert.equal(otel.isOtelEnabled(), false);
77
+ } finally {
78
+ await otel.shutdownOtel();
79
+ }
80
+ });
81
+
82
+ test('graceful degradation when OTLP endpoint is unreachable', async () => {
83
+ // Port 1 on loopback never accepts connections — the exporter will
84
+ // log batch-send errors internally but must not crash the module.
85
+ const otel = await loadOtel();
86
+ try {
87
+ const sdk = otel.initOtel({
88
+ endpoint: 'http://127.0.0.1:1/v1/traces',
89
+ });
90
+ assert.ok(sdk, 'initOtel must succeed even with an unreachable endpoint');
91
+ assert.equal(otel.isOtelEnabled(), true);
92
+
93
+ // Spans created on the live tracer must complete without errors.
94
+ await new Promise((resolve, reject) => {
95
+ otel.tracer.startActiveSpan('test.unreachable_endpoint', async (span) => {
96
+ try {
97
+ span.setAttribute('test.attr', 1);
98
+ span.setStatus({ code: 1 /* OK */ });
99
+ await Promise.resolve();
100
+ resolve();
101
+ } catch (e) {
102
+ reject(e);
103
+ } finally {
104
+ span.end();
105
+ }
106
+ });
107
+ });
108
+
109
+ await otel.shutdownOtel();
110
+ } finally {
111
+ await otel.shutdownOtel();
112
+ }
113
+ });
114
+
115
+ test('spans can be created and ended without errors (no SDK initialised)', async () => {
116
+ // No initOtel call — exercises the no-op tracer path. Route
117
+ // handlers rely on this when OTEL is disabled (the default).
118
+ const otel = await loadOtel();
119
+ assert.equal(otel.isOtelEnabled(), false);
120
+
121
+ const result = await new Promise((resolve, reject) => {
122
+ otel.tracer.startActiveSpan('test.noop', (span) => {
123
+ try {
124
+ // Sanity-check that the no-op span still answers every
125
+ // method without throwing — that's the contract route
126
+ // handlers rely on.
127
+ span.setAttribute('a', 1);
128
+ span.setAttribute('b', 'hello');
129
+ span.addEvent('event-1');
130
+ span.setStatus({ code: 1 });
131
+ resolve('ok');
132
+ } catch (err) {
133
+ reject(err);
134
+ } finally {
135
+ span.end();
136
+ }
137
+ });
138
+ });
139
+ assert.equal(result, 'ok');
140
+
141
+ await otel.shutdownOtel();
142
+ });
143
+
144
+ test('tracer.startActiveSpan callback can be async (returns a Promise)', async () => {
145
+ // The OpenTelemetry spec lets the callback return a Promise;
146
+ // the tracer awaits it. Make sure the no-op implementation
147
+ // (used when the SDK is disabled) honors this.
148
+ const otel = await loadOtel();
149
+ try {
150
+ let observed = null;
151
+ await otel.tracer.startActiveSpan('test.async', async (span) => {
152
+ try {
153
+ observed = await Promise.resolve('inside-span');
154
+ } finally {
155
+ span.end();
156
+ }
157
+ });
158
+ assert.equal(observed, 'inside-span');
159
+ } finally {
160
+ await otel.shutdownOtel();
161
+ }
162
+ });
163
+
164
+ test('initOtel accepts custom serviceName override', async () => {
165
+ const otel = await loadOtel();
166
+ try {
167
+ const sdk = otel.initOtel({
168
+ serviceName: 'bizar-dash-test',
169
+ endpoint: 'http://127.0.0.1:1/v1/traces',
170
+ });
171
+ assert.ok(sdk, 'initOtel with a custom serviceName should succeed');
172
+ await otel.shutdownOtel();
173
+ } finally {
174
+ await otel.shutdownOtel();
175
+ }
176
+ });
177
+
178
+ test('module does not crash when imported (no init, no shutdown)', async () => {
179
+ // Defensive sanity check — every other test file in this repo
180
+ // transitively imports dozens of modules. If a syntax/import
181
+ // error sneaks into otel.mjs, this test catches it before the
182
+ // larger suite blows up somewhere unrelated.
183
+ const otel = await loadOtel();
184
+ assert.equal(typeof otel.initOtel, 'function');
185
+ assert.equal(typeof otel.shutdownOtel, 'function');
186
+ assert.equal(typeof otel.tracer, 'object');
187
+ await otel.shutdownOtel();
188
+ });