@drawmotive/textgraph 0.1.0-alpha.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.
Files changed (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +92 -0
  3. package/docs/api-v1.md +87 -0
  4. package/docs/releases.md +100 -0
  5. package/examples/browser.html +5 -0
  6. package/examples/browser.js +4 -0
  7. package/examples/node.mjs +4 -0
  8. package/examples/render-png.mjs +11 -0
  9. package/examples/worker.js +6 -0
  10. package/generated/wasm/DrawMotive.TextGraph.Bridge.runtimeconfig.json +52 -0
  11. package/generated/wasm/DrawMotive.TextGraph.Bridge.wasm +0 -0
  12. package/generated/wasm/FuzzyBubbles-LICENSE.txt +93 -0
  13. package/generated/wasm/FuzzyBubbles-Regular.ttf +0 -0
  14. package/generated/wasm/Graphics.Core.wasm +0 -0
  15. package/generated/wasm/Grpc.Core.Api.wasm +0 -0
  16. package/generated/wasm/HarfBuzzSharp.wasm +0 -0
  17. package/generated/wasm/MagicOnion.Abstractions.wasm +0 -0
  18. package/generated/wasm/MagicOnion.Serialization.MessagePack.wasm +0 -0
  19. package/generated/wasm/MagicOnion.Shared.wasm +0 -0
  20. package/generated/wasm/Markdig.wasm +0 -0
  21. package/generated/wasm/Math.Core.wasm +0 -0
  22. package/generated/wasm/MemoryPack.Core.wasm +0 -0
  23. package/generated/wasm/MessagePack.Annotations.wasm +0 -0
  24. package/generated/wasm/MessagePack.wasm +0 -0
  25. package/generated/wasm/Microsoft.AspNetCore.Components.Web.wasm +0 -0
  26. package/generated/wasm/Microsoft.Extensions.DependencyInjection.Abstractions.wasm +0 -0
  27. package/generated/wasm/Microsoft.Extensions.DependencyInjection.wasm +0 -0
  28. package/generated/wasm/Microsoft.Extensions.Hosting.Abstractions.wasm +0 -0
  29. package/generated/wasm/Microsoft.Extensions.Logging.Abstractions.wasm +0 -0
  30. package/generated/wasm/Microsoft.Extensions.Logging.wasm +0 -0
  31. package/generated/wasm/Microsoft.Extensions.Options.wasm +0 -0
  32. package/generated/wasm/Microsoft.Extensions.Primitives.wasm +0 -0
  33. package/generated/wasm/Microsoft.NET.StringTools.wasm +0 -0
  34. package/generated/wasm/Nanoid.wasm +0 -0
  35. package/generated/wasm/NotoSans-LICENSE.txt +93 -0
  36. package/generated/wasm/NotoSans-Regular.ttf +0 -0
  37. package/generated/wasm/Polly.Core.wasm +0 -0
  38. package/generated/wasm/Polly.wasm +0 -0
  39. package/generated/wasm/R3.BlazorWebAssembly.wasm +0 -0
  40. package/generated/wasm/R3.wasm +0 -0
  41. package/generated/wasm/RBush.wasm +0 -0
  42. package/generated/wasm/SkiaSharp.HarfBuzz.wasm +0 -0
  43. package/generated/wasm/SkiaSharp.wasm +0 -0
  44. package/generated/wasm/Stateless.wasm +0 -0
  45. package/generated/wasm/System.Collections.Concurrent.wasm +0 -0
  46. package/generated/wasm/System.Collections.Immutable.wasm +0 -0
  47. package/generated/wasm/System.Collections.NonGeneric.wasm +0 -0
  48. package/generated/wasm/System.Collections.Specialized.wasm +0 -0
  49. package/generated/wasm/System.Collections.wasm +0 -0
  50. package/generated/wasm/System.ComponentModel.Annotations.wasm +0 -0
  51. package/generated/wasm/System.ComponentModel.Primitives.wasm +0 -0
  52. package/generated/wasm/System.ComponentModel.TypeConverter.wasm +0 -0
  53. package/generated/wasm/System.ComponentModel.wasm +0 -0
  54. package/generated/wasm/System.Console.wasm +0 -0
  55. package/generated/wasm/System.Diagnostics.StackTrace.wasm +0 -0
  56. package/generated/wasm/System.Diagnostics.Tracing.wasm +0 -0
  57. package/generated/wasm/System.IO.Compression.Brotli.wasm +0 -0
  58. package/generated/wasm/System.IO.Compression.wasm +0 -0
  59. package/generated/wasm/System.IO.Pipelines.wasm +0 -0
  60. package/generated/wasm/System.Linq.Expressions.wasm +0 -0
  61. package/generated/wasm/System.Linq.wasm +0 -0
  62. package/generated/wasm/System.Memory.wasm +0 -0
  63. package/generated/wasm/System.Net.Http.wasm +0 -0
  64. package/generated/wasm/System.Net.Primitives.wasm +0 -0
  65. package/generated/wasm/System.Numerics.Vectors.wasm +0 -0
  66. package/generated/wasm/System.ObjectModel.wasm +0 -0
  67. package/generated/wasm/System.Private.CoreLib.wasm +0 -0
  68. package/generated/wasm/System.Private.Uri.wasm +0 -0
  69. package/generated/wasm/System.Reflection.Emit.ILGeneration.wasm +0 -0
  70. package/generated/wasm/System.Reflection.Emit.wasm +0 -0
  71. package/generated/wasm/System.Reflection.Primitives.wasm +0 -0
  72. package/generated/wasm/System.Runtime.InteropServices.JavaScript.wasm +0 -0
  73. package/generated/wasm/System.Runtime.InteropServices.wasm +0 -0
  74. package/generated/wasm/System.Runtime.Intrinsics.wasm +0 -0
  75. package/generated/wasm/System.Runtime.Loader.wasm +0 -0
  76. package/generated/wasm/System.Runtime.Numerics.wasm +0 -0
  77. package/generated/wasm/System.Runtime.Serialization.Primitives.wasm +0 -0
  78. package/generated/wasm/System.Runtime.wasm +0 -0
  79. package/generated/wasm/System.Security.Cryptography.wasm +0 -0
  80. package/generated/wasm/System.Text.Encoding.Extensions.wasm +0 -0
  81. package/generated/wasm/System.Text.Encodings.Web.wasm +0 -0
  82. package/generated/wasm/System.Text.Json.wasm +0 -0
  83. package/generated/wasm/System.Text.RegularExpressions.wasm +0 -0
  84. package/generated/wasm/System.Threading.Channels.wasm +0 -0
  85. package/generated/wasm/System.Threading.Thread.wasm +0 -0
  86. package/generated/wasm/System.Threading.ThreadPool.wasm +0 -0
  87. package/generated/wasm/System.Threading.wasm +0 -0
  88. package/generated/wasm/System.wasm +0 -0
  89. package/generated/wasm/ZstdSharp.wasm +0 -0
  90. package/generated/wasm/dotnet.boot.js +443 -0
  91. package/generated/wasm/dotnet.js +4 -0
  92. package/generated/wasm/dotnet.native.js +5513 -0
  93. package/generated/wasm/dotnet.native.wasm +0 -0
  94. package/generated/wasm/dotnet.runtime.js +4 -0
  95. package/generated/wasm/main.mjs +5 -0
  96. package/generated/wasm/netstandard.wasm +0 -0
  97. package/generated/wasm/themes.css +85 -0
  98. package/generated/wasm-manifest.js +572 -0
  99. package/generated/wasm-manifest.json +572 -0
  100. package/package.json +66 -0
  101. package/schemas/render.schema.json +167 -0
  102. package/schemas/validation.schema.json +70 -0
  103. package/schemas/wasm-manifest.schema.json +208 -0
  104. package/src/adapters.js +27 -0
  105. package/src/index.d.ts +171 -0
  106. package/src/index.js +78 -0
  107. package/src/platform/browser.d.ts +13 -0
  108. package/src/platform/browser.js +13 -0
  109. package/src/platform/node.d.ts +3 -0
  110. package/src/platform/node.js +12 -0
  111. package/src/platform/worker.d.ts +3 -0
  112. package/src/platform/worker.js +4 -0
  113. package/src/runtime/assets.js +27 -0
  114. package/src/runtime/browser.js +41 -0
  115. package/src/runtime/dotnet.js +100 -0
  116. package/src/runtime/errors.js +14 -0
  117. package/src/runtime/integrity.js +12 -0
  118. package/src/runtime/language-packs.js +32 -0
  119. package/src/runtime/lifecycle.js +84 -0
  120. package/src/runtime/manifest.js +60 -0
  121. package/src/runtime/node.js +21 -0
  122. package/src/runtime/render-resources.js +51 -0
  123. package/src/runtime/rendering.js +66 -0
  124. package/src/runtime/shared.js +17 -0
  125. package/src/runtime/validation.js +38 -0
@@ -0,0 +1,66 @@
1
+ import { DrawMotiveError } from './errors.js';
2
+ import { decodeDiagnostics, validateSignal } from './validation.js';
3
+
4
+ const stages = ['parse', 'semantic', 'layout', 'render', 'font'];
5
+
6
+ /** Normalize export settings before queuing; native receives only the render contract. */
7
+ export function normalizeRenderOptions(source, options) {
8
+ const invalid = message => { throw new DrawMotiveError('INVALID_ARGUMENT', message); };
9
+ if (typeof source !== 'string' || !options || typeof options !== 'object' || Array.isArray(options)) invalid('Rendering requires a source string and options object');
10
+ const { encoding = 'bytes', scale = 2, padding = 10, maxWidth, signal } = options;
11
+ if (!['bytes', 'base64'].includes(encoding)) invalid('encoding must be bytes or base64');
12
+ // The native geometry contract uses float32; reject values lost at that boundary.
13
+ if (!Number.isFinite(scale) || !Number.isFinite(Math.fround(scale)) || Math.fround(scale) <= 0) invalid('scale must be positive and representable as a finite float32');
14
+ if (!Number.isFinite(padding) || !Number.isFinite(Math.fround(padding)) || padding < 0) invalid('padding must be non-negative and representable as a finite float32');
15
+ if (maxWidth !== undefined && (!Number.isSafeInteger(maxWidth) || maxWidth <= 0)) invalid('maxWidth must be a positive integer');
16
+ validateSignal(signal);
17
+ return { encoding, signal, request: { protocolVersion: 1, operation: 'render', source, export: { format: 'png', scale, padding, ...(maxWidth === undefined ? {} : { maxWidth }) } } };
18
+ }
19
+
20
+ /** Accept canonical base64 only; platform atob implementations otherwise tolerate corruption. */
21
+ function decodeBase64(value) {
22
+ if (typeof value !== 'string' || !value || value.length % 4 !== 0
23
+ || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) throw new Error('Invalid base64');
24
+ const binary = atob(value);
25
+ if (btoa(binary) !== value) throw new Error('Non-canonical base64');
26
+ return Uint8Array.from(binary, character => character.charCodeAt(0));
27
+ }
28
+
29
+ /** Validate the native image boundary before publishing caller-selected PNG data. */
30
+ export function decodeRender(json, encoding) {
31
+ try {
32
+ const value = JSON.parse(json);
33
+ if (value?.protocolVersion !== 1 || typeof value.success !== 'boolean') throw new Error('Invalid envelope');
34
+ const diagnostics = decodeDiagnostics(value.diagnostics, stages);
35
+ if (value.success === diagnostics.some(item => item.severity === 'error')) throw new Error('Inconsistent success');
36
+ if (!value.success) {
37
+ if (['png', 'width', 'height'].some(key => Object.hasOwn(value, key))) throw new Error('Failure contains image data');
38
+ return Object.freeze({ success: false, diagnostics });
39
+ }
40
+ if (![value.width, value.height].every(n => Number.isSafeInteger(n) && n > 0 && n <= 0x7fffffff)) throw new Error('Invalid dimensions');
41
+ const bytes = decodeBase64(value.png);
42
+ const signature = [137, 80, 78, 71, 13, 10, 26, 10];
43
+ if (bytes.length < 33 || !signature.every((byte, index) => bytes[index] === byte)) throw new Error('Invalid PNG signature');
44
+ const header = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
45
+ if (header.getUint32(8) !== 13 || header.getUint32(12) !== 0x49484452
46
+ || header.getUint32(16) !== value.width || header.getUint32(20) !== value.height) throw new Error('PNG dimensions disagree');
47
+ return Object.freeze({ success: true, png: encoding === 'base64' ? value.png : bytes, width: value.width, height: value.height, diagnostics });
48
+ } catch (cause) {
49
+ throw new DrawMotiveError('INVALID_RESPONSE', 'The rendering response violates protocol 1', { cause });
50
+ }
51
+ }
52
+
53
+ /** Resource setup and disposal failures are operational, separate from diagram diagnostics. */
54
+ export function decodeConfiguration(json, operation = 'configuration') {
55
+ let value;
56
+ let diagnostics;
57
+ try {
58
+ value = JSON.parse(json);
59
+ if (value?.protocolVersion !== 1 || typeof value.success !== 'boolean') throw new Error('Invalid envelope');
60
+ diagnostics = decodeDiagnostics(value.diagnostics, stages);
61
+ if (value.success === diagnostics.some(item => item.severity === 'error')) throw new Error('Inconsistent success');
62
+ } catch (cause) {
63
+ throw new DrawMotiveError('INVALID_RESPONSE', `The ${operation} response violates protocol 1`, { cause });
64
+ }
65
+ if (!value.success) throw new DrawMotiveError('RUNTIME_FAILED', `Rendering resource ${operation} failed`, { details: { diagnostics } });
66
+ }
@@ -0,0 +1,17 @@
1
+ import { resolveRuntimeAssets } from './assets.js';
2
+
3
+ /** Resolves every manifest asset while preserving the initial loader's baseUrl option. */
4
+ export function createRuntimeAssetPlan({ manifest, baseUrl, moduleUrl, resolveAsset }) {
5
+ if (baseUrl) {
6
+ const base = new URL(baseUrl);
7
+ return resolveRuntimeAssets({
8
+ manifest,
9
+ moduleUrl,
10
+ resolveAsset: (asset, _defaultUrl) => {
11
+ const defaultUrl = new URL(asset.path, base);
12
+ return resolveAsset ? resolveAsset(asset, defaultUrl) : defaultUrl;
13
+ },
14
+ });
15
+ }
16
+ return resolveRuntimeAssets({ manifest, moduleUrl, resolveAsset });
17
+ }
@@ -0,0 +1,38 @@
1
+ import { DrawMotiveError } from './errors.js';
2
+
3
+ /** Validates the wire boundary before publishing detached public diagnostic data. */
4
+ export function decodeValidation(json) {
5
+ try {
6
+ const value = JSON.parse(json);
7
+ if (value?.protocolVersion !== 1 || typeof value.valid !== 'boolean' || !Array.isArray(value.diagnostics)) throw new Error('Invalid envelope');
8
+ const diagnostics = decodeDiagnostics(value.diagnostics, ['parse', 'semantic']);
9
+ if (value.valid === diagnostics.some(item => item.severity === 'error')) throw new Error('Inconsistent validity');
10
+ return Object.freeze({ valid: value.valid, diagnostics });
11
+ } catch (cause) {
12
+ throw new DrawMotiveError('INVALID_RESPONSE', 'The validation response violates protocol 1', { cause });
13
+ }
14
+ }
15
+
16
+ /** Operations share diagnostic structure while retaining their own allowed stages. */
17
+ export function decodeDiagnostics(value, stages) {
18
+ if (!Array.isArray(value)) throw new Error('Invalid diagnostics');
19
+ return Object.freeze(value.map(item => {
20
+ if (!item || !stages.includes(item.stage) || !['error', 'warning'].includes(item.severity)
21
+ || typeof item.code !== 'string' || !item.code || typeof item.message !== 'string') throw new Error('Invalid diagnostic');
22
+ const diagnostic = { code: item.code, severity: item.severity, stage: item.stage, message: item.message };
23
+ if (item.location != null) {
24
+ const { line, column } = item.location;
25
+ if (![line, column].every(n => Number.isSafeInteger(n) && n >= 0)) throw new Error('Invalid location');
26
+ diagnostic.location = Object.freeze({ line, column });
27
+ }
28
+ return Object.freeze(diagnostic);
29
+ }));
30
+ }
31
+
32
+ /** Signals are host objects; validate their callable contract without realm-sensitive instanceof. */
33
+ export function validateSignal(signal) {
34
+ if (signal !== undefined && (!signal || typeof signal.aborted !== 'boolean'
35
+ || typeof signal.addEventListener !== 'function' || typeof signal.removeEventListener !== 'function')) {
36
+ throw new DrawMotiveError('INVALID_ARGUMENT', 'signal must be an AbortSignal');
37
+ }
38
+ }