aub-workspace 0.3.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 (152) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +23 -0
  3. package/bin/aub-workspace.mjs +246 -0
  4. package/package.json +32 -0
  5. package/vendor/aub/apps/editor/dist/assets/_commonjs-dynamic-modules-TDtrdbi3.js +1 -0
  6. package/vendor/aub/apps/editor/dist/assets/angular-importer.lib-dB_jK4mR.js +32 -0
  7. package/vendor/aub/apps/editor/dist/assets/canvas-tools-CuYC7cA2.js +364 -0
  8. package/vendor/aub/apps/editor/dist/assets/design-bridge.lib-DJvaK6AX.js +1 -0
  9. package/vendor/aub/apps/editor/dist/assets/export-agent-prompt.lib-BsP0KNqo.js +2 -0
  10. package/vendor/aub/apps/editor/dist/assets/export-md.lib-DdmdeWgO.js +3 -0
  11. package/vendor/aub/apps/editor/dist/assets/handoff-package.lib-DDYpcEma.js +20 -0
  12. package/vendor/aub/apps/editor/dist/assets/implementation-report.lib-CmsSB_8s.js +1 -0
  13. package/vendor/aub/apps/editor/dist/assets/index-BCH-ek3h.js +2 -0
  14. package/vendor/aub/apps/editor/dist/assets/index-lAnc928Q.css +1 -0
  15. package/vendor/aub/apps/editor/dist/assets/index-vt1nM1M4.js +507 -0
  16. package/vendor/aub/apps/editor/dist/assets/jszip.min-CRfXyL92.js +12 -0
  17. package/vendor/aub/apps/editor/dist/assets/react-vendor-ByX9Pqse.js +40 -0
  18. package/vendor/aub/apps/editor/dist/brand/android-chrome-192x192.png +0 -0
  19. package/vendor/aub/apps/editor/dist/brand/android-chrome-512x512.png +0 -0
  20. package/vendor/aub/apps/editor/dist/brand/app-icon-1024.png +0 -0
  21. package/vendor/aub/apps/editor/dist/brand/app-icon-192.png +0 -0
  22. package/vendor/aub/apps/editor/dist/brand/app-icon-512.png +0 -0
  23. package/vendor/aub/apps/editor/dist/brand/apple-touch-icon.png +0 -0
  24. package/vendor/aub/apps/editor/dist/brand/aub-logo-mark.svg +28 -0
  25. package/vendor/aub/apps/editor/dist/brand/favicon-16x16.png +0 -0
  26. package/vendor/aub/apps/editor/dist/brand/favicon-32x32.png +0 -0
  27. package/vendor/aub/apps/editor/dist/brand/favicon-48x48.png +0 -0
  28. package/vendor/aub/apps/editor/dist/brand/favicon.ico +0 -0
  29. package/vendor/aub/apps/editor/dist/brand/favicon.svg +9 -0
  30. package/vendor/aub/apps/editor/dist/brand/maskable-icon-512.png +0 -0
  31. package/vendor/aub/apps/editor/dist/brand/mstile-150x150.png +0 -0
  32. package/vendor/aub/apps/editor/dist/brand/safari-pinned-tab.svg +8 -0
  33. package/vendor/aub/apps/editor/dist/browserconfig.xml +9 -0
  34. package/vendor/aub/apps/editor/dist/index.html +22 -0
  35. package/vendor/aub/apps/editor/dist/manifest.webmanifest +28 -0
  36. package/vendor/aub/apps/editor/dist/template-previews/admin-table.png +0 -0
  37. package/vendor/aub/apps/editor/dist/template-previews/booking.png +0 -0
  38. package/vendor/aub/apps/editor/dist/template-previews/calendar.png +0 -0
  39. package/vendor/aub/apps/editor/dist/template-previews/catalog.png +0 -0
  40. package/vendor/aub/apps/editor/dist/template-previews/chat.png +0 -0
  41. package/vendor/aub/apps/editor/dist/template-previews/checkout.png +0 -0
  42. package/vendor/aub/apps/editor/dist/template-previews/crm.png +0 -0
  43. package/vendor/aub/apps/editor/dist/template-previews/dashboard.png +0 -0
  44. package/vendor/aub/apps/editor/dist/template-previews/feed.png +0 -0
  45. package/vendor/aub/apps/editor/dist/template-previews/files.png +0 -0
  46. package/vendor/aub/apps/editor/dist/template-previews/kanban.png +0 -0
  47. package/vendor/aub/apps/editor/dist/template-previews/landing.png +0 -0
  48. package/vendor/aub/apps/editor/dist/template-previews/mail.png +0 -0
  49. package/vendor/aub/apps/editor/dist/template-previews/onboarding.png +0 -0
  50. package/vendor/aub/apps/editor/dist/template-previews/pricing.png +0 -0
  51. package/vendor/aub/apps/editor/dist/template-previews/product-detail.png +0 -0
  52. package/vendor/aub/apps/editor/dist/template-previews/settings.png +0 -0
  53. package/vendor/aub/apps/editor/dist/template-previews/wiki.png +0 -0
  54. package/vendor/aub/apps/mcp-server/dist/aub.js +15 -0
  55. package/vendor/aub/apps/mcp-server/dist/context.js +1 -0
  56. package/vendor/aub/apps/mcp-server/dist/http.js +123 -0
  57. package/vendor/aub/apps/mcp-server/dist/index.js +23 -0
  58. package/vendor/aub/apps/mcp-server/dist/repo.js +17 -0
  59. package/vendor/aub/apps/mcp-server/dist/schema.js +42 -0
  60. package/vendor/aub/apps/mcp-server/dist/server.js +80 -0
  61. package/vendor/aub/apps/mcp-server/dist/tools/approve-component-candidate.js +27 -0
  62. package/vendor/aub/apps/mcp-server/dist/tools/diff-blueprints.js +27 -0
  63. package/vendor/aub/apps/mcp-server/dist/tools/export-handoff.js +87 -0
  64. package/vendor/aub/apps/mcp-server/dist/tools/export-prompt.js +35 -0
  65. package/vendor/aub/apps/mcp-server/dist/tools/export-template-authoring-prompt.js +13 -0
  66. package/vendor/aub/apps/mcp-server/dist/tools/generate-template-from-source.js +25 -0
  67. package/vendor/aub/apps/mcp-server/dist/tools/get-aub-session.js +13 -0
  68. package/vendor/aub/apps/mcp-server/dist/tools/get-blueprint.js +28 -0
  69. package/vendor/aub/apps/mcp-server/dist/tools/get-project.js +45 -0
  70. package/vendor/aub/apps/mcp-server/dist/tools/get-workspace-status.js +10 -0
  71. package/vendor/aub/apps/mcp-server/dist/tools/import-design-bridge.js +62 -0
  72. package/vendor/aub/apps/mcp-server/dist/tools/list-blueprints.js +11 -0
  73. package/vendor/aub/apps/mcp-server/dist/tools/list-projects.js +11 -0
  74. package/vendor/aub/apps/mcp-server/dist/tools/lock-blueprint.js +33 -0
  75. package/vendor/aub/apps/mcp-server/dist/tools/migrate-blueprint.js +38 -0
  76. package/vendor/aub/apps/mcp-server/dist/tools/resolve-component.js +51 -0
  77. package/vendor/aub/apps/mcp-server/dist/tools/scaffold-blueprint.js +53 -0
  78. package/vendor/aub/apps/mcp-server/dist/tools/scan-project-ui.js +18 -0
  79. package/vendor/aub/apps/mcp-server/dist/tools/submit-report.js +48 -0
  80. package/vendor/aub/apps/mcp-server/dist/tools/update-aub-session.js +14 -0
  81. package/vendor/aub/apps/mcp-server/dist/tools/validate-blueprint.js +67 -0
  82. package/vendor/aub/apps/mcp-server/dist/tools/validate-project.js +74 -0
  83. package/vendor/aub/apps/mcp-server/dist/tools/write-blueprint.js +72 -0
  84. package/vendor/aub/apps/mcp-server/dist/workspace.js +138 -0
  85. package/vendor/aub/docs/agent-handoff.md +85 -0
  86. package/vendor/aub/docs/agent-handoff.zh-Hant.md +85 -0
  87. package/vendor/aub/docs/template-authoring-agent.md +86 -0
  88. package/vendor/aub/schema/aub-ci.schema.json +34 -0
  89. package/vendor/aub/schema/aub.registry.schema.json +118 -0
  90. package/vendor/aub/schema/design-bridge.schema.json +44 -0
  91. package/vendor/aub/schema/implementation-report.schema.json +93 -0
  92. package/vendor/aub/schema/project-types.ts +72 -0
  93. package/vendor/aub/schema/registry/components.json +118 -0
  94. package/vendor/aub/schema/types.js +13 -0
  95. package/vendor/aub/schema/types.ts +348 -0
  96. package/vendor/aub/schema/ui-blueprint-lock.schema.json +61 -0
  97. package/vendor/aub/schema/ui-blueprint.schema.json +1339 -0
  98. package/vendor/aub/schema/ui-project.schema.json +139 -0
  99. package/vendor/aub/scripts/agent-implementation-benchmark.lib.mjs +125 -0
  100. package/vendor/aub/scripts/angular-importer.lib.mjs +982 -0
  101. package/vendor/aub/scripts/check-editor-bundle-budget.mjs +36 -0
  102. package/vendor/aub/scripts/ci-verify.lib.mjs +256 -0
  103. package/vendor/aub/scripts/ci-verify.mjs +45 -0
  104. package/vendor/aub/scripts/create-authoring-kit.mjs +84 -0
  105. package/vendor/aub/scripts/create-implementation-report.mjs +24 -0
  106. package/vendor/aub/scripts/design-bridge.lib.d.mts +32 -0
  107. package/vendor/aub/scripts/design-bridge.lib.mjs +69 -0
  108. package/vendor/aub/scripts/diff-blueprint.lib.d.mts +18 -0
  109. package/vendor/aub/scripts/diff-blueprint.lib.mjs +148 -0
  110. package/vendor/aub/scripts/diff-blueprint.mjs +25 -0
  111. package/vendor/aub/scripts/export-agent-prompt.lib.d.mts +10 -0
  112. package/vendor/aub/scripts/export-agent-prompt.lib.mjs +160 -0
  113. package/vendor/aub/scripts/export-agent-prompt.mjs +79 -0
  114. package/vendor/aub/scripts/export-md.lib.d.mts +3 -0
  115. package/vendor/aub/scripts/export-md.lib.mjs +302 -0
  116. package/vendor/aub/scripts/export-md.mjs +43 -0
  117. package/vendor/aub/scripts/generate-registry-artifacts.lib.mjs +118 -0
  118. package/vendor/aub/scripts/generate-registry-artifacts.mjs +65 -0
  119. package/vendor/aub/scripts/generate-site-locales.mjs +545 -0
  120. package/vendor/aub/scripts/handoff-package.lib.d.mts +20 -0
  121. package/vendor/aub/scripts/handoff-package.lib.mjs +111 -0
  122. package/vendor/aub/scripts/implementation-report.lib.d.mts +21 -0
  123. package/vendor/aub/scripts/implementation-report.lib.mjs +97 -0
  124. package/vendor/aub/scripts/import-angular-component.mjs +72 -0
  125. package/vendor/aub/scripts/import-design-bridge.mjs +59 -0
  126. package/vendor/aub/scripts/lock-blueprint.lib.d.mts +23 -0
  127. package/vendor/aub/scripts/lock-blueprint.lib.mjs +58 -0
  128. package/vendor/aub/scripts/lock-blueprint.mjs +36 -0
  129. package/vendor/aub/scripts/migrate-blueprint-cli.mjs +28 -0
  130. package/vendor/aub/scripts/migrate-blueprint.d.mts +5 -0
  131. package/vendor/aub/scripts/migrate-blueprint.mjs +95 -0
  132. package/vendor/aub/scripts/package-workspace-cli.mjs +34 -0
  133. package/vendor/aub/scripts/project.lib.d.mts +44 -0
  134. package/vendor/aub/scripts/project.lib.mjs +175 -0
  135. package/vendor/aub/scripts/project.mjs +332 -0
  136. package/vendor/aub/scripts/registry.lib.d.mts +52 -0
  137. package/vendor/aub/scripts/registry.lib.mjs +222 -0
  138. package/vendor/aub/scripts/run-agent-implementation.mjs +423 -0
  139. package/vendor/aub/scripts/run-agent-readability.mjs +145 -0
  140. package/vendor/aub/scripts/run-ollama-prompt.mjs +30 -0
  141. package/vendor/aub/scripts/scaffold-blueprint.lib.d.mts +38 -0
  142. package/vendor/aub/scripts/scaffold-blueprint.lib.mjs +316 -0
  143. package/vendor/aub/scripts/scaffold-blueprint.mjs +86 -0
  144. package/vendor/aub/scripts/score-agent-implementation.mjs +27 -0
  145. package/vendor/aub/scripts/score-agent-readability.mjs +54 -0
  146. package/vendor/aub/scripts/sync-brand-assets.mjs +33 -0
  147. package/vendor/aub/scripts/validate-blueprint.lib.d.mts +14 -0
  148. package/vendor/aub/scripts/validate-blueprint.lib.mjs +136 -0
  149. package/vendor/aub/scripts/validate.mjs +128 -0
  150. package/vendor/aub/scripts/verify-implementation-report.mjs +36 -0
  151. package/vendor/aub/scripts/workspace-loop.lib.d.mts +17 -0
  152. package/vendor/aub/scripts/workspace-loop.lib.mjs +674 -0
@@ -0,0 +1,139 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://github.com/HenryLau1103/AUB/schema/0.1.0/ui-project.schema.json",
4
+ "title": "UI Blueprint Project",
5
+ "description": "A reference-based multi-screen project. Lists member single-screen Blueprint files by relative path, declares a navigation graph between them, names an entry screen, and optionally carries a project-level shared design system. Member .ui.json files remain fully valid standalone Blueprints.",
6
+ "type": "object",
7
+ "additionalProperties": false,
8
+ "required": [
9
+ "version",
10
+ "id",
11
+ "name",
12
+ "screens",
13
+ "entry_screen"
14
+ ],
15
+ "properties": {
16
+ "$schema": {
17
+ "type": "string",
18
+ "description": "Optional path or URL to this JSON Schema. Lets editors provide validation and autocomplete for standalone .aub.project.json files. Ignored by AUB tooling."
19
+ },
20
+ "version": {
21
+ "type": "string",
22
+ "description": "Semantic version of the project schema this document conforms to.",
23
+ "pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
24
+ "enum": ["0.1.0"],
25
+ "examples": ["0.1.0"]
26
+ },
27
+ "id": {
28
+ "type": "string",
29
+ "description": "Unique project identifier (kebab or dot notation, e.g. 'acme-app').",
30
+ "pattern": "^[a-z0-9][a-z0-9._-]*$",
31
+ "minLength": 1,
32
+ "maxLength": 128
33
+ },
34
+ "name": {
35
+ "type": "string",
36
+ "description": "Human-readable project name shown in editors and exports.",
37
+ "minLength": 1,
38
+ "maxLength": 128
39
+ },
40
+ "description": {
41
+ "type": "string",
42
+ "description": "Optional one-paragraph summary of the product or flow this project describes.",
43
+ "maxLength": 1024
44
+ },
45
+ "screens": {
46
+ "type": "array",
47
+ "description": "Member screens. Each references a single-screen Blueprint file by relative path. At least one required.",
48
+ "minItems": 1,
49
+ "items": { "$ref": "#/$defs/screenRef" }
50
+ },
51
+ "entry_screen": {
52
+ "type": "string",
53
+ "description": "Screen id (from screens[].id) where the flow starts. MUST match one of the declared screens.",
54
+ "pattern": "^[a-z0-9][a-z0-9._-]*$",
55
+ "minLength": 1,
56
+ "maxLength": 128
57
+ },
58
+ "navigation": {
59
+ "type": "array",
60
+ "description": "Directed navigation edges between screens. from/to MUST reference declared screen ids.",
61
+ "items": { "$ref": "#/$defs/navigationEdge" }
62
+ },
63
+ "design_system": {
64
+ "type": "object",
65
+ "description": "Optional project-level shared design tokens. Member screens may override these in their own design_system. Free-form object mirroring a Blueprint design_system; not validated field-for-field here.",
66
+ "additionalProperties": true
67
+ },
68
+ "provenance": {
69
+ "type": "object",
70
+ "description": "Optional metadata about how this project document was produced.",
71
+ "additionalProperties": true
72
+ }
73
+ },
74
+ "$defs": {
75
+ "screenRef": {
76
+ "type": "object",
77
+ "additionalProperties": false,
78
+ "required": ["id", "path"],
79
+ "properties": {
80
+ "id": {
81
+ "type": "string",
82
+ "description": "Screen identifier. MUST match the referenced Blueprint's screen.id.",
83
+ "pattern": "^[a-z0-9][a-z0-9._-]*$",
84
+ "minLength": 1,
85
+ "maxLength": 128
86
+ },
87
+ "name": {
88
+ "type": "string",
89
+ "description": "Optional display name. Defaults to the referenced Blueprint's screen.name.",
90
+ "minLength": 1,
91
+ "maxLength": 128
92
+ },
93
+ "path": {
94
+ "type": "string",
95
+ "description": "Relative path (from the project file) to a single-screen .ui.json or .ui.yaml Blueprint.",
96
+ "minLength": 1,
97
+ "maxLength": 1024
98
+ }
99
+ }
100
+ },
101
+ "navigationEdge": {
102
+ "type": "object",
103
+ "additionalProperties": false,
104
+ "required": ["from", "to"],
105
+ "properties": {
106
+ "from": {
107
+ "type": "string",
108
+ "description": "Source screen id (must be a declared screen).",
109
+ "pattern": "^[a-z0-9][a-z0-9._-]*$",
110
+ "minLength": 1,
111
+ "maxLength": 128
112
+ },
113
+ "to": {
114
+ "type": "string",
115
+ "description": "Destination screen id (must be a declared screen).",
116
+ "pattern": "^[a-z0-9][a-z0-9._-]*$",
117
+ "minLength": 1,
118
+ "maxLength": 128
119
+ },
120
+ "trigger": {
121
+ "type": "string",
122
+ "description": "What causes the navigation.",
123
+ "enum": ["click", "submit", "change", "load", "system", "gesture"]
124
+ },
125
+ "interaction_id": {
126
+ "type": "string",
127
+ "description": "Optional id of the interaction on the source screen that triggers this navigation.",
128
+ "minLength": 1,
129
+ "maxLength": 128
130
+ },
131
+ "label": {
132
+ "type": "string",
133
+ "description": "Optional human-readable label for the edge (e.g. 'Open settings').",
134
+ "maxLength": 280
135
+ }
136
+ }
137
+ }
138
+ }
139
+ }
@@ -0,0 +1,125 @@
1
+ import { verifyImplementationReport } from './implementation-report.lib.mjs';
2
+
3
+ const STYLE_KEYS = [
4
+ 'position',
5
+ 'marginTop',
6
+ 'marginRight',
7
+ 'marginBottom',
8
+ 'marginLeft',
9
+ 'backgroundColor',
10
+ 'color',
11
+ 'borderRadius',
12
+ 'boxShadow',
13
+ 'fontFamily',
14
+ 'fontSize',
15
+ 'fontWeight',
16
+ 'lineHeight',
17
+ ];
18
+
19
+ const AUTO_LAYOUT_STYLE_KEYS = [
20
+ 'display',
21
+ 'flexDirection',
22
+ 'rowGap',
23
+ 'columnGap',
24
+ 'paddingTop',
25
+ 'paddingRight',
26
+ 'paddingBottom',
27
+ 'paddingLeft',
28
+ ];
29
+
30
+ export function scoreImplementationBenchmark(blueprint, candidate, reference, implementationReport) {
31
+ const checks = [];
32
+ const nodeIds = blueprint.nodes.map((node) => node.id);
33
+ const root = blueprint.nodes.find((node) => node.parent_id === null);
34
+
35
+ for (const viewport of blueprint.viewports) {
36
+ const candidateView = candidate.viewports[viewport.id];
37
+ const referenceView = reference.viewports[viewport.id];
38
+ add(checks, `${viewport.id}.screenshot`, Boolean(candidateView?.screenshot_bytes > 5000), '> 5000 bytes', candidateView?.screenshot_bytes ?? 0);
39
+ add(checks, `${viewport.id}.horizontal_overflow`, candidateView?.horizontal_overflow === false, false, candidateView?.horizontal_overflow);
40
+ add(checks, `${viewport.id}.root_children`, equal(candidateView?.root_children, root?.children ?? []), root?.children ?? [], candidateView?.root_children);
41
+
42
+ for (const nodeId of nodeIds) {
43
+ add(checks, `${viewport.id}.node.${nodeId}`, Boolean(candidateView?.nodes?.[nodeId]), true, Boolean(candidateView?.nodes?.[nodeId]));
44
+ }
45
+
46
+ for (const node of blueprint.nodes) {
47
+ const actualNode = candidateView?.nodes?.[node.id];
48
+ const referenceNode = referenceView?.nodes?.[node.id];
49
+ add(
50
+ checks,
51
+ `${viewport.id}.parent.${node.id}`,
52
+ actualNode?.parent_node_id === node.parent_id,
53
+ node.parent_id,
54
+ actualNode?.parent_node_id
55
+ );
56
+ const styleKeys = node.layout?.mode === 'auto'
57
+ ? [...STYLE_KEYS, ...AUTO_LAYOUT_STYLE_KEYS]
58
+ : STYLE_KEYS;
59
+ for (const key of styleKeys) {
60
+ if (referenceNode?.styles?.[key] === undefined) continue;
61
+ add(
62
+ checks,
63
+ `${viewport.id}.style.${node.id}.${key}`,
64
+ actualNode?.styles?.[key] === referenceNode.styles[key],
65
+ referenceNode.styles[key],
66
+ actualNode?.styles?.[key]
67
+ );
68
+ }
69
+
70
+ const placement = node.placements?.[viewport.id];
71
+ if (!placement) continue;
72
+ const actual = actualNode?.rect;
73
+ for (const key of ['x', 'y', 'width', 'height']) {
74
+ const pass = Number.isFinite(actual?.[key]) && Math.abs(actual[key] - placement[key]) <= 2;
75
+ add(checks, `${viewport.id}.geometry.${node.id}.${key}`, pass, placement[key], actual?.[key]);
76
+ }
77
+ add(checks, `${viewport.id}.geometry.${node.id}.z_index`, Number(actual?.z_index) === placement.z_index, placement.z_index, actual?.z_index);
78
+ }
79
+
80
+ const primary = candidateView?.nodes?.primary_cta?.rect;
81
+ const secondary = candidateView?.nodes?.secondary_cta?.rect;
82
+ add(checks, `${viewport.id}.cta_side_by_side`, Boolean(
83
+ primary && secondary && Math.abs(primary.y - secondary.y) <= 2 && primary.x + primary.width <= secondary.x
84
+ ), true, { primary, secondary });
85
+ }
86
+
87
+ for (const node of blueprint.nodes) {
88
+ const expectedText = node.content?.text ?? node.content?.label;
89
+ if (!expectedText) continue;
90
+ const actualText = candidate.viewports.desktop?.nodes?.[node.id]?.text?.trim();
91
+ add(checks, `content.${node.id}`, actualText === expectedText, expectedText, actualText);
92
+ }
93
+
94
+ for (const interaction of blueprint.interactions) {
95
+ add(
96
+ checks,
97
+ `interaction.${interaction.id}`,
98
+ candidate.interactions?.[interaction.source_node_id] === interaction.action,
99
+ interaction.action,
100
+ candidate.interactions?.[interaction.source_node_id]
101
+ );
102
+ }
103
+
104
+ add(checks, 'accessibility.focus_visible', candidate.has_focus_visible === true, true, candidate.has_focus_visible);
105
+ const reportVerification = verifyImplementationReport(blueprint, implementationReport);
106
+ add(checks, 'implementation_report.ready', reportVerification.ready, true, reportVerification);
107
+
108
+ const passed = checks.filter((check) => check.pass).length;
109
+ return {
110
+ score: Math.round((passed / checks.length) * 100),
111
+ passed,
112
+ total: checks.length,
113
+ ready: passed === checks.length,
114
+ checks,
115
+ report_verification: reportVerification,
116
+ };
117
+ }
118
+
119
+ function add(checks, path, pass, expected, actual) {
120
+ checks.push({ path, pass, expected, actual });
121
+ }
122
+
123
+ function equal(left, right) {
124
+ return JSON.stringify(left) === JSON.stringify(right);
125
+ }