pi-lean-dimension 0.2.3 → 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 (176) hide show
  1. package/README.md +1 -1
  2. package/node_modules/pi-lean-portal/AGENTS.md +15 -23
  3. package/node_modules/pi-lean-portal/README.md +31 -1
  4. package/node_modules/pi-lean-portal/__tests__/browser-toggle-profile.test.ts +12 -1
  5. package/node_modules/pi-lean-portal/__tests__/browser-toggle.test.ts +263 -1102
  6. package/node_modules/pi-lean-portal/__tests__/fetch-backend.test.ts +6 -32
  7. package/node_modules/pi-lean-portal/__tests__/helpers/mock-python-bridge.py +1 -1
  8. package/node_modules/pi-lean-portal/__tests__/helpers/probe-user-backend.ts +1 -2
  9. package/node_modules/pi-lean-portal/__tests__/helpers/setup-temp-dir.ts +26 -0
  10. package/node_modules/pi-lean-portal/__tests__/plugin-config-browser.test.ts +1 -1
  11. package/node_modules/pi-lean-portal/__tests__/plugin-contract.test.ts +1 -1
  12. package/node_modules/pi-lean-portal/__tests__/plugin-loading.test.ts +1 -1
  13. package/node_modules/pi-lean-portal/__tests__/probe-user-backend.test.ts +1 -1
  14. package/node_modules/pi-lean-portal/__tests__/python-adapter.test.ts +65 -69
  15. package/node_modules/pi-lean-portal/__tests__/router-dispatch.test.ts +1 -14
  16. package/node_modules/pi-lean-portal/__tests__/run-contributed-suites.test.ts +31 -16
  17. package/node_modules/pi-lean-portal/__tests__/snapshot-cache.test.ts +1 -1
  18. package/node_modules/pi-lean-portal/__tests__/web-guides.test.ts +146 -19
  19. package/node_modules/pi-lean-portal/backends/chromium-py/__pycache__/bridge.cpython-313.pyc +0 -0
  20. package/node_modules/pi-lean-portal/backends/firefox-py/__pycache__/bridge.cpython-313.pyc +0 -0
  21. package/node_modules/pi-lean-portal/backends/playwright-base/playwright-plugin.ts +1 -1
  22. package/node_modules/pi-lean-portal/backends/python-adapter.ts +3 -3
  23. package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__init__.py +2 -4
  24. package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/__init__.cpython-312.pyc +0 -0
  25. package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/__init__.cpython-313.pyc +0 -0
  26. package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/bridge.cpython-312.pyc +0 -0
  27. package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/playwright_base.cpython-312.pyc +0 -0
  28. package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/playwright_base.cpython-313.pyc +0 -0
  29. package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/playwright_base.py +572 -81
  30. package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/conftest.cpython-313-pytest-9.1.1.pyc +0 -0
  31. package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_accessibility.cpython-313-pytest-9.1.1.pyc +0 -0
  32. package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_bot_detection.cpython-313-pytest-9.1.1.pyc +0 -0
  33. package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_browser_data.cpython-313-pytest-9.1.1.pyc +0 -0
  34. package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_playwright_base_quirks.cpython-313-pytest-9.1.1.pyc +0 -0
  35. package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_py_bridges.cpython-313-pytest-9.1.1.pyc +0 -0
  36. package/node_modules/pi-lean-portal/backends/python-base/tests/test_playwright_base_quirks.py +13 -43
  37. package/node_modules/pi-lean-portal/browser-profile.ts +2 -3
  38. package/node_modules/pi-lean-portal/browser-toggle.ts +168 -380
  39. package/node_modules/pi-lean-portal/contributed/CHOOSING.md +2 -2
  40. package/node_modules/pi-lean-portal/contributed/README.md +65 -6
  41. package/node_modules/pi-lean-portal/contributed/camoufox-py/__pycache__/bridge.cpython-312.pyc +0 -0
  42. package/node_modules/pi-lean-portal/contributed/camoufox-py/bridge.py +108 -14
  43. package/node_modules/pi-lean-portal/contributed/camoufox-py/pin.json +4 -0
  44. package/node_modules/pi-lean-portal/contributed/invisible-py/bridge.py +1 -1
  45. package/node_modules/pi-lean-portal/core/fetch-backend.ts +10 -30
  46. package/node_modules/pi-lean-portal/core/guides.ts +14 -18
  47. package/node_modules/pi-lean-portal/core/router.ts +2 -20
  48. package/node_modules/pi-lean-portal/core/shared/bot-detection.ts +19 -30
  49. package/node_modules/pi-lean-portal/core/shared/browser-data.ts +4 -4
  50. package/node_modules/pi-lean-portal/core/shared/nav-settle.ts +2 -2
  51. package/node_modules/pi-lean-portal/core/shared/paths.ts +16 -2
  52. package/node_modules/pi-lean-portal/core/shared/settings-reader.ts +2 -7
  53. package/node_modules/pi-lean-portal/core/shared/snapshot-cache.ts +1 -1
  54. package/node_modules/pi-lean-portal/index.ts +8 -1
  55. package/node_modules/pi-lean-portal/package.json +2 -1
  56. package/node_modules/pi-lean-portal/tools/web-learn.ts +1 -4
  57. package/node_modules/pi-lean-search/__tests__/web-search.test.ts +87 -0
  58. package/node_modules/pi-lean-search/index.ts +134 -64
  59. package/node_modules/pi-lean-search/package.json +2 -1
  60. package/node_modules/playwright/README.md +3 -3
  61. package/node_modules/playwright/lib/agents/copilot-setup-steps.yml +2 -2
  62. package/node_modules/playwright/lib/cli/reportActions.js +2 -2
  63. package/node_modules/playwright/lib/common/index.js +77 -52
  64. package/node_modules/playwright/lib/common/index.js.txt +10 -11
  65. package/node_modules/playwright/lib/errorContext.js +2 -3
  66. package/node_modules/playwright/lib/index.js +30 -8
  67. package/node_modules/playwright/lib/matchers/expect.js +4811 -4379
  68. package/node_modules/playwright/lib/matchers/expect.js.LICENSE +87 -140
  69. package/node_modules/playwright/lib/matchers/expect.js.txt +30 -35
  70. package/node_modules/playwright/lib/program.js +19 -0
  71. package/node_modules/playwright/lib/runner/index.js +309 -153
  72. package/node_modules/playwright/lib/runner/index.js.txt +13 -12
  73. package/node_modules/playwright/lib/transform/babelBundle.js +12899 -11654
  74. package/node_modules/playwright/lib/transform/babelBundle.js.LICENSE +379 -354
  75. package/node_modules/playwright/lib/transform/babelBundle.js.txt +39 -39
  76. package/node_modules/playwright/lib/transform/esmLoader.js +3129 -1116
  77. package/node_modules/playwright/lib/transform/esmLoader.js.LICENSE +81 -61
  78. package/node_modules/playwright/lib/transform/esmLoader.js.txt +21 -18
  79. package/node_modules/playwright/lib/util.js +2 -29
  80. package/node_modules/playwright/lib/worker/workerProcessEntry.js +14 -32
  81. package/node_modules/playwright/lib/worker/workerProcessEntry.js.txt +7 -8
  82. package/node_modules/playwright/package.json +3 -6
  83. package/node_modules/playwright/types/test.d.ts +391 -4
  84. package/node_modules/playwright/types/testReporter.d.ts +79 -4
  85. package/node_modules/playwright-core/browsers.json +12 -18
  86. package/node_modules/playwright-core/lib/bootstrap.js +1 -1
  87. package/node_modules/playwright-core/lib/coreBundle.js +11344 -9900
  88. package/node_modules/playwright-core/lib/server/electron/loader.js +5 -2
  89. package/node_modules/playwright-core/lib/serverRegistry.js +5 -1
  90. package/node_modules/playwright-core/lib/tools/cli-client/help.json +21 -6
  91. package/node_modules/playwright-core/lib/tools/cli-client/program.js +1 -1
  92. package/node_modules/playwright-core/lib/tools/cli-client/session.js +4 -0
  93. package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/SKILL.md +21 -5
  94. package/node_modules/playwright-core/lib/tools/{cli-client/skill/references/spec-driven-testing.md → skills/playwright-cli/references/test-generation.md} +135 -7
  95. package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/SKILL.md +143 -0
  96. package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/gallery-spec.md +144 -0
  97. package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/migration.md +85 -0
  98. package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/react.md +67 -0
  99. package/node_modules/playwright-core/lib/tools/skills/playwright-component-testing/references/vue.md +75 -0
  100. package/node_modules/playwright-core/lib/tools/utils/extension.js +31 -8
  101. package/node_modules/playwright-core/lib/utilsBundle.js +37698 -33670
  102. package/node_modules/playwright-core/lib/utilsBundle.js.LICENSE +391 -268
  103. package/node_modules/playwright-core/lib/vite/dashboard/assets/index-CyWAfh-p.js +11 -0
  104. package/node_modules/playwright-core/lib/vite/dashboard/assets/index-DhC616m4.css +1 -0
  105. package/node_modules/playwright-core/lib/vite/dashboard/index.html +2 -2
  106. package/node_modules/playwright-core/lib/vite/htmlReport/report.css +2 -1
  107. package/node_modules/playwright-core/lib/vite/htmlReport/report.js +15 -55
  108. package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule--QdMvsKi.css +1 -0
  109. package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-CwYYHbcZ.js +32 -0
  110. package/node_modules/playwright-core/lib/vite/recorder/assets/index-DYjdXIbE.js +129 -0
  111. package/node_modules/playwright-core/lib/vite/recorder/assets/index-l_lX622x.css +1 -0
  112. package/node_modules/playwright-core/lib/vite/recorder/index.html +2 -2
  113. package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-By56iMx7.js +32 -0
  114. package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-B34OrIms.js +181 -0
  115. package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-L3liM589.js +1 -0
  116. package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-COQkjINf.js +7 -0
  117. package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.-QdMvsKi.css +1 -0
  118. package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.BLFoOugd.css +1 -0
  119. package/node_modules/playwright-core/lib/vite/traceViewer/index.B_TqY17P.css +1 -0
  120. package/node_modules/playwright-core/lib/vite/traceViewer/index.Dl36UVQT.js +1 -0
  121. package/node_modules/playwright-core/lib/vite/traceViewer/index.html +5 -5
  122. package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.B_Jk1wbt.js +1 -0
  123. package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.html +2 -2
  124. package/node_modules/playwright-core/lib/vite/traceViewer/sw.bundle.js +3 -4
  125. package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.C7UW1sC9.css +1 -0
  126. package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.D962mr9b.js +5 -0
  127. package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +5 -5
  128. package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.kHJ-D0s7.css +1 -0
  129. package/node_modules/playwright-core/lib/webp_codec.LICENSE +173 -0
  130. package/node_modules/playwright-core/lib/webp_codec.wasm +0 -0
  131. package/node_modules/playwright-core/lib/xdg-open +338 -137
  132. package/node_modules/playwright-core/package.json +2 -2
  133. package/node_modules/playwright-core/types/protocol.d.ts +249 -36
  134. package/node_modules/playwright-core/types/structs.d.ts +3 -1
  135. package/node_modules/playwright-core/types/types.d.ts +2009 -266
  136. package/package.json +3 -3
  137. package/node_modules/pi-lean-portal/__tests__/url-safety.test.ts +0 -188
  138. package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/bridge.cpython-313.pyc +0 -0
  139. package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/__pycache__/patch_playwright.cpython-313.pyc +0 -0
  140. package/node_modules/pi-lean-portal/backends/python-base/pi_browser_bridge/bridge.py +0 -525
  141. package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_accessibility.cpython-313.pyc +0 -0
  142. package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_bot_detection.cpython-313.pyc +0 -0
  143. package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_browser_data.cpython-313.pyc +0 -0
  144. package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_chromium_py_bridge.cpython-313-pytest-9.1.1.pyc +0 -0
  145. package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_firefox_py_bridge.cpython-313-pytest-9.1.1.pyc +0 -0
  146. package/node_modules/pi-lean-portal/backends/python-base/tests/__pycache__/test_playwright_base_quirks.cpython-313.pyc +0 -0
  147. package/node_modules/pi-lean-portal/core/shared/url-safety.ts +0 -164
  148. package/node_modules/playwright-core/lib/server/deviceDescriptorsSource.json +0 -2739
  149. package/node_modules/playwright-core/lib/tools/cli-client/skill/references/test-generation.md +0 -134
  150. package/node_modules/playwright-core/lib/vite/dashboard/assets/index-BY2S1tHT.css +0 -1
  151. package/node_modules/playwright-core/lib/vite/dashboard/assets/index-C_5TMfeg.js +0 -52
  152. package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-DYBRYzYX.css +0 -1
  153. package/node_modules/playwright-core/lib/vite/recorder/assets/codeMirrorModule-DeBYQozu.js +0 -32
  154. package/node_modules/playwright-core/lib/vite/recorder/assets/index-4ZiSSCmn.css +0 -1
  155. package/node_modules/playwright-core/lib/vite/recorder/assets/index-Bq-mQf8S.js +0 -193
  156. package/node_modules/playwright-core/lib/vite/traceViewer/assets/codeMirrorModule-LEHpjmcn.js +0 -32
  157. package/node_modules/playwright-core/lib/vite/traceViewer/assets/defaultSettingsView-BNmKHKpQ.js +0 -264
  158. package/node_modules/playwright-core/lib/vite/traceViewer/assets/urlMatch-BYQrIQwR.js +0 -1
  159. package/node_modules/playwright-core/lib/vite/traceViewer/assets/xtermModule-CsJ4vdCR.js +0 -9
  160. package/node_modules/playwright-core/lib/vite/traceViewer/codeMirrorModule.DYBRYzYX.css +0 -1
  161. package/node_modules/playwright-core/lib/vite/traceViewer/defaultSettingsView.CjdS-WJx.css +0 -1
  162. package/node_modules/playwright-core/lib/vite/traceViewer/index.CzXZzn5A.css +0 -1
  163. package/node_modules/playwright-core/lib/vite/traceViewer/index.DMMX1gXU.js +0 -2
  164. package/node_modules/playwright-core/lib/vite/traceViewer/snapshot.v8KI4P3m.js +0 -2
  165. package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.BZQ54Kgt.css +0 -1
  166. package/node_modules/playwright-core/lib/vite/traceViewer/uiMode.Ut8wwJNp.js +0 -6
  167. package/node_modules/playwright-core/lib/vite/traceViewer/xtermModule.DYP7pi_n.css +0 -32
  168. /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/element-attributes.md +0 -0
  169. /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/playwright-tests.md +0 -0
  170. /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/request-mocking.md +0 -0
  171. /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/running-code.md +0 -0
  172. /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/session-management.md +0 -0
  173. /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/storage-state.md +0 -0
  174. /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/tracing.md +0 -0
  175. /package/node_modules/playwright-core/lib/tools/{cli-client/skill → skills/playwright-cli}/references/video-recording.md +0 -0
  176. /package/node_modules/playwright-core/lib/tools/{trace → skills/playwright-trace}/SKILL.md +0 -0
@@ -1,8 +1,8 @@
1
1
  """
2
- Tests for ``pi_browser_bridge.playwright_base.PlaywrightBridge`` Phase 0 work:
2
+ Tests for ``pi_browser_bridge.playwright_base.PlaywrightBridge`` stealth quirk flags:
3
3
 
4
4
  * ``plugin_config`` defaults to ``{}`` and is populated by the
5
- ``browser.init`` RPC handler (inherited from ``BrowserBridge``).
5
+ ``browser.init`` RPC handler.
6
6
  * The stealth quirks change base-class behavior:
7
7
  - ``_fingerprint_managed_context`` → viewport/user_agent omitted
8
8
  - ``_skip_default_viewport`` → ``no_viewport=True`` passed to avoid
@@ -22,7 +22,6 @@ from typing import Any
22
22
 
23
23
  import pytest
24
24
 
25
- from pi_browser_bridge.bridge import BrowserBridge
26
25
  from pi_browser_bridge.playwright_base import PlaywrightBridge
27
26
 
28
27
 
@@ -138,30 +137,6 @@ def _bind_session(bridge: _FakePlaywrightBridge, page: _FakePage) -> None:
138
137
  bridge.sessions["t"] = {"page": page, "context": _FakeContext()}
139
138
 
140
139
 
141
- class _BareBridge(BrowserBridge):
142
- """Trivial concrete ``BrowserBridge`` for base-class handler tests.
143
-
144
- Only exercises base-class handlers (``browser.init``,
145
- ``browser.describeQuirks``); the ``do_*`` stubs below are never
146
- invoked but satisfy the abstract contract so the class instantiates.
147
- """
148
-
149
- def create_browser_session(self, task_id, config): # pragma: no cover
150
- return {}
151
-
152
- def create_browser_context(self, config): # pragma: no cover
153
- return None
154
-
155
- def _do_stub(self, *args, **kwargs): # pragma: no cover
156
- return {}
157
-
158
- do_navigate = do_snapshot = do_click = do_type = do_scroll = _do_stub
159
- do_go_back = do_press = do_screenshot = _do_stub
160
- do_get_console_messages = do_clear_console = do_evaluate = _do_stub
161
- do_get_cookies = do_add_cookies = do_clear_cookies = _do_stub
162
- do_get_storage_state = _do_stub
163
-
164
-
165
140
  # ═══════════════════════════════════════════════════════════════════════
166
141
  # plugin_config (browser.init RPC)
167
142
  # ═══════════════════════════════════════════════════════════════════════
@@ -189,16 +164,14 @@ class TestPluginConfig:
189
164
  assert result["result"] == {"ok": True}
190
165
  assert bridge.plugin_config == {}
191
166
 
192
- def test_init_is_inherited_from_browser_bridge(self):
193
- """The handler lives on BrowserBridge (so non-Playwright bridges get it too)."""
194
- # A bare BrowserBridge can't be instantiated meaningfully (create_browser_session
195
- # is abstract), but the handler routing is on the base class.
196
- bare = _BareBridge()
197
- result = bare.handle_command(
167
+ def test_init_handler_on_playwright_bridge(self):
168
+ """The handler is available on a plain PlaywrightBridge without Playwright."""
169
+ bridge = PlaywrightBridge()
170
+ result = bridge.handle_command(
198
171
  "browser.init", {"config": {"x": 1}}, cmd_id=7
199
172
  )
200
173
  assert result["result"] == {"ok": True}
201
- assert bare.plugin_config == {"x": 1}
174
+ assert bridge.plugin_config == {"x": 1}
202
175
 
203
176
 
204
177
  # ═══════════════════════════════════════════════════════════════════════
@@ -465,12 +438,10 @@ class TestWrapMwEvalInEval:
465
438
 
466
439
  class TestDescribeQuirks:
467
440
  """Tests for the ``browser.describeQuirks`` introspection RPC handler
468
- on ``BrowserBridge`` (``bridge.py``).
441
+ on ``PlaywrightBridge``.
469
442
 
470
443
  Verifies the handler returns the bridge's class-attribute quirks
471
- flags correctly, and that the getattr-based defaulting handles both
472
- PlaywrightBridge instances (which have the quirks attrs) and bare
473
- BrowserBridge instances (which don't).
444
+ flags correctly, including default values.
474
445
  """
475
446
 
476
447
  @pytest.mark.parametrize("overrides,expected", [
@@ -496,11 +467,10 @@ class TestDescribeQuirks:
496
467
  assert "result" in result
497
468
  assert result["result"] == expected
498
469
 
499
- def test_bare_bridge_returns_all_defaults(self):
500
- """A bare ``BrowserBridge`` (no Playwright quirks attrs) also returns
501
- default values via ``getattr`` — the handler doesn't throw."""
502
- bare = _BareBridge()
503
- result = bare.handle_command("browser.describeQuirks", {}, 4)
470
+ def test_plain_playwright_bridge_returns_all_defaults(self):
471
+ """A plain ``PlaywrightBridge`` returns default quirks values."""
472
+ bridge = PlaywrightBridge()
473
+ result = bridge.handle_command("browser.describeQuirks", {}, 4)
504
474
  assert "result" in result
505
475
  q = result["result"]
506
476
  assert q["fingerprint_managed_context"] == False
@@ -21,6 +21,7 @@ import {
21
21
  sanitizeProfileName,
22
22
  profileDir,
23
23
  } from "./core/shared/storage-state.js";
24
+ import { formatBytes } from "./core/shared/paths.js";
24
25
 
25
26
  // ─── Profile helpers ─────────────────────────────────────────────
26
27
 
@@ -34,9 +35,7 @@ export function profileStateSize(profileName: string): string {
34
35
  if (!existsSync(path)) return "no state";
35
36
  const bytes = statSync(path).size;
36
37
  if (bytes === 0) return "empty";
37
- if (bytes < 1024) return `${bytes} B`;
38
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
39
- return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
38
+ return formatBytes(bytes);
40
39
  } catch {
41
40
  return "?";
42
41
  }