@mermanjs/node 0.8.0-alpha.5

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 (47) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/LICENSE-APACHE +201 -0
  3. package/LICENSE-MIT +21 -0
  4. package/README.md +11 -0
  5. package/THIRD_PARTY_LICENSES/beautiful-mermaid/LICENSE +21 -0
  6. package/THIRD_PARTY_LICENSES/cose-base-v1/LICENSE +21 -0
  7. package/THIRD_PARTY_LICENSES/cose-base-v2/LICENSE +21 -0
  8. package/THIRD_PARTY_LICENSES/cytoscape/LICENSE +19 -0
  9. package/THIRD_PARTY_LICENSES/cytoscape-cose-bilkent/LICENSE +21 -0
  10. package/THIRD_PARTY_LICENSES/cytoscape-fcose/LICENSE +19 -0
  11. package/THIRD_PARTY_LICENSES/d3-shape/LICENSE +13 -0
  12. package/THIRD_PARTY_LICENSES/dagre/LICENSE +19 -0
  13. package/THIRD_PARTY_LICENSES/dompurify/LICENSE +202 -0
  14. package/THIRD_PARTY_LICENSES/eclipse-elk/LICENSE.md +264 -0
  15. package/THIRD_PARTY_LICENSES/elkjs/LICENSE.md +264 -0
  16. package/THIRD_PARTY_LICENSES/fmin/LICENSE +27 -0
  17. package/THIRD_PARTY_LICENSES/graphlib/LICENSE +19 -0
  18. package/THIRD_PARTY_LICENSES/katex-fonts/FONT_NOTICE.txt +12 -0
  19. package/THIRD_PARTY_LICENSES/katex-fonts/OFL.txt +85 -0
  20. package/THIRD_PARTY_LICENSES/layout-base-v1/LICENSE +21 -0
  21. package/THIRD_PARTY_LICENSES/layout-base-v2/LICENSE +21 -0
  22. package/THIRD_PARTY_LICENSES/mermaid/LICENSE +21 -0
  23. package/THIRD_PARTY_LICENSES/mermaid-ascii/LICENSE +21 -0
  24. package/THIRD_PARTY_LICENSES/mermaid-rs-renderer/LICENSE +21 -0
  25. package/THIRD_PARTY_LICENSES/ratex/LICENSE +27 -0
  26. package/THIRD_PARTY_LICENSES/ratex/THIRD_PARTY_NOTICES.txt +19 -0
  27. package/THIRD_PARTY_LICENSES/rough-rs/LICENSE +21 -0
  28. package/THIRD_PARTY_LICENSES/roughjs/LICENSE +21 -0
  29. package/THIRD_PARTY_LICENSES/rust-cargo-dependencies.json +5562 -0
  30. package/THIRD_PARTY_LICENSES/sanitize-url/LICENSE +21 -0
  31. package/THIRD_PARTY_LICENSES/venn-js/LICENSE +22 -0
  32. package/THIRD_PARTY_LICENSES/wasm-minimal-protocol/LICENSE +24 -0
  33. package/THIRD_PARTY_LICENSES/zenuml-core/LICENSE +21 -0
  34. package/THIRD_PARTY_NOTICES.md +524 -0
  35. package/dist/bounded-executor.mjs +132 -0
  36. package/dist/candidates/native.mjs +51 -0
  37. package/dist/candidates/wrap-engine.mjs +28 -0
  38. package/dist/engine.mjs +1255 -0
  39. package/dist/errors.mjs +1199 -0
  40. package/dist/generated/binding-contract.mjs +873 -0
  41. package/dist/generated/capability-surface.mjs +66 -0
  42. package/dist/generated/node-wire-contract.json +122 -0
  43. package/dist/index.d.ts +239 -0
  44. package/dist/index.mjs +22 -0
  45. package/dist/native-loader.mjs +112 -0
  46. package/dist/transport-contract.mjs +87 -0
  47. package/package.json +50 -0
@@ -0,0 +1,132 @@
1
+ import {
2
+ MermanDisposedError,
3
+ MermanLifecycleError,
4
+ MermanQueueSaturatedError,
5
+ abortError,
6
+ } from "./errors.mjs";
7
+
8
+ export class BoundedExecutor {
9
+ #active = 0;
10
+ #concurrency;
11
+ #disposePromise = null;
12
+ #finishDispose = null;
13
+ #maxQueue;
14
+ #pending = [];
15
+ #state = "open";
16
+
17
+ constructor({ concurrency, maxQueue }) {
18
+ if (!Number.isSafeInteger(concurrency) || concurrency < 1) {
19
+ throw new RangeError("concurrency must be a positive integer.");
20
+ }
21
+ if (!Number.isSafeInteger(maxQueue) || maxQueue < 0) {
22
+ throw new RangeError("maxQueue must be a non-negative integer.");
23
+ }
24
+ this.#concurrency = concurrency;
25
+ this.#maxQueue = maxQueue;
26
+ }
27
+
28
+ get snapshot() {
29
+ return Object.freeze({
30
+ active: this.#active,
31
+ pending: this.#pending.length,
32
+ concurrency: this.#concurrency,
33
+ maxQueue: this.#maxQueue,
34
+ state: this.#state,
35
+ });
36
+ }
37
+
38
+ submit(run, { signal } = {}) {
39
+ if (this.#state !== "open") return Promise.reject(new MermanDisposedError());
40
+ if (signal?.aborted) return Promise.reject(abortError());
41
+
42
+ if (this.#active < this.#concurrency) {
43
+ return this.#start({ run, signal });
44
+ }
45
+ if (this.#pending.length >= this.#maxQueue) {
46
+ return Promise.reject(new MermanQueueSaturatedError(this.#maxQueue));
47
+ }
48
+
49
+ return new Promise((resolve, reject) => {
50
+ const job = { abortListener: null, reject, resolve, run, signal, started: false };
51
+ if (signal) {
52
+ job.abortListener = () => {
53
+ if (job.started) return;
54
+ const index = this.#pending.indexOf(job);
55
+ if (index === -1) return;
56
+ this.#pending.splice(index, 1);
57
+ reject(abortError());
58
+ };
59
+ signal.addEventListener("abort", job.abortListener, { once: true });
60
+ }
61
+ this.#pending.push(job);
62
+ });
63
+ }
64
+
65
+ assertSyncAvailable() {
66
+ this.assertOpen();
67
+ if (this.#active !== 0 || this.#pending.length !== 0) {
68
+ throw new MermanLifecycleError(
69
+ "renderSvgSync() cannot run while asynchronous operations are active or queued.",
70
+ );
71
+ }
72
+ }
73
+
74
+ assertOpen() {
75
+ if (this.#state !== "open") throw new MermanDisposedError();
76
+ }
77
+
78
+ dispose() {
79
+ if (this.#disposePromise) return this.#disposePromise;
80
+ this.#state = "disposing";
81
+ const disposed = new MermanDisposedError();
82
+ for (const job of this.#pending.splice(0)) {
83
+ this.#removeAbortListener(job);
84
+ job.reject(disposed);
85
+ }
86
+ this.#disposePromise = new Promise((resolve) => {
87
+ this.#finishDispose = resolve;
88
+ this.#completeDisposeIfIdle();
89
+ });
90
+ return this.#disposePromise;
91
+ }
92
+
93
+ #start(job) {
94
+ job.started = true;
95
+ this.#removeAbortListener(job);
96
+ this.#active += 1;
97
+ const operation = Promise.resolve().then(job.run);
98
+ const observed = job.resolve || job.reject
99
+ ? operation.then(job.resolve, job.reject)
100
+ : operation;
101
+ return observed.finally(() => {
102
+ this.#active -= 1;
103
+ this.#pump();
104
+ this.#completeDisposeIfIdle();
105
+ });
106
+ }
107
+
108
+ #pump() {
109
+ while (
110
+ this.#state === "open" &&
111
+ this.#active < this.#concurrency &&
112
+ this.#pending.length > 0
113
+ ) {
114
+ const job = this.#pending.shift();
115
+ void this.#start(job);
116
+ }
117
+ }
118
+
119
+ #completeDisposeIfIdle() {
120
+ if (this.#state !== "disposing" || this.#active !== 0) return;
121
+ this.#state = "disposed";
122
+ this.#finishDispose?.();
123
+ this.#finishDispose = null;
124
+ }
125
+
126
+ #removeAbortListener(job) {
127
+ if (job.signal && job.abortListener) {
128
+ job.signal.removeEventListener("abort", job.abortListener);
129
+ job.abortListener = null;
130
+ }
131
+ }
132
+ }
@@ -0,0 +1,51 @@
1
+ import {
2
+ MermanInvalidTransportError,
3
+ decodeWireCreationError,
4
+ validateTransportIdentityExport,
5
+ } from "../errors.mjs";
6
+ import {
7
+ assertRuntimePackageVersion,
8
+ loadNativeBinding,
9
+ nodeLoaderPackageVersion,
10
+ } from "../native-loader.mjs";
11
+ import { wrapCandidateEngine } from "./wrap-engine.mjs";
12
+
13
+ export async function loadNativeTransport(optionsJson, loaderOptions = {}) {
14
+ const binding = loadNativeBinding(loaderOptions);
15
+ const NativeEngine = binding?.NativeEngine ?? binding?.default?.NativeEngine;
16
+ if (typeof NativeEngine !== "function") {
17
+ throw new MermanInvalidTransportError(
18
+ "The target-specific Merman package does not export NativeEngine.",
19
+ );
20
+ }
21
+ validateTransportIdentityExport(binding?.transportIdentityJson, {
22
+ expectedPackageVersion: nodeLoaderPackageVersion(),
23
+ expectedTransport: "napi",
24
+ label: "The target-specific Merman package",
25
+ });
26
+ let transport;
27
+ try {
28
+ transport = wrapCandidateEngine(
29
+ new NativeEngine(optionsJson),
30
+ "The target-specific Merman addon",
31
+ );
32
+ } catch (cause) {
33
+ throw decodeWireCreationError(cause, "The target-specific Merman addon");
34
+ }
35
+ try {
36
+ const runtimeCatalogJson = assertRuntimePackageVersion(
37
+ transport.runtimeCatalogJson(),
38
+ );
39
+ return {
40
+ ...transport,
41
+ runtimeCatalogJson: () => runtimeCatalogJson,
42
+ };
43
+ } catch (cause) {
44
+ try {
45
+ await transport.dispose();
46
+ } catch {
47
+ // Preserve the package-version failure that made the transport unusable.
48
+ }
49
+ throw cause;
50
+ }
51
+ }
@@ -0,0 +1,28 @@
1
+ import { MermanDisposedError, MermanInvalidTransportError } from "../errors.mjs";
2
+
3
+ export function wrapCandidateEngine(engine, label) {
4
+ if (
5
+ typeof engine?.execute !== "function" ||
6
+ typeof engine?.executeSync !== "function" ||
7
+ typeof engine?.runtimeCatalogJson !== "function" ||
8
+ typeof engine?.metadataJson !== "function"
9
+ ) {
10
+ throw new MermanInvalidTransportError(`${label} does not implement the operation contract.`);
11
+ }
12
+ let ownedEngine = engine;
13
+ const currentEngine = () => {
14
+ if (!ownedEngine) throw new MermanDisposedError();
15
+ return ownedEngine;
16
+ };
17
+ return {
18
+ execute: (requestJson) => currentEngine().execute(requestJson),
19
+ executeSync: (requestJson) => currentEngine().executeSync(requestJson),
20
+ runtimeCatalogJson: () => currentEngine().runtimeCatalogJson(),
21
+ metadataJson: (id) => currentEngine().metadataJson(id),
22
+ dispose: () => {
23
+ const disposing = ownedEngine;
24
+ ownedEngine = null;
25
+ return disposing?.dispose?.();
26
+ },
27
+ };
28
+ }