rapydscript-ns 0.9.2 → 0.9.3

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 (151) hide show
  1. package/.agignore +1 -1
  2. package/.github/workflows/ci.yml +38 -38
  3. package/=template.pyj +5 -5
  4. package/CHANGELOG.md +19 -0
  5. package/HACKING.md +103 -103
  6. package/LICENSE +24 -24
  7. package/PYTHON_GAPS.md +420 -0
  8. package/README.md +153 -29
  9. package/TODO.md +16 -118
  10. package/add-toc-to-readme +2 -2
  11. package/bin/export +75 -75
  12. package/bin/rapydscript +70 -70
  13. package/bin/web-repl-export +102 -102
  14. package/build +2 -2
  15. package/language-service/index.js +237 -8
  16. package/memory/project_string_impl.md +43 -0
  17. package/package.json +1 -1
  18. package/publish.py +37 -37
  19. package/release/baselib-plain-pretty.js +248 -38
  20. package/release/baselib-plain-ugly.js +8 -8
  21. package/release/compiler.js +778 -277
  22. package/release/signatures.json +30 -30
  23. package/session.vim +4 -4
  24. package/setup.cfg +2 -2
  25. package/src/ast.pyj +4 -1
  26. package/src/baselib-builtins.pyj +56 -2
  27. package/src/baselib-containers.pyj +2 -0
  28. package/src/baselib-errors.pyj +7 -3
  29. package/src/baselib-internal.pyj +51 -6
  30. package/src/baselib-str.pyj +5 -3
  31. package/src/compiler.pyj +36 -36
  32. package/src/errors.pyj +30 -30
  33. package/src/lib/aes.pyj +646 -646
  34. package/src/lib/asyncio.pyj +534 -0
  35. package/src/lib/base64.pyj +399 -0
  36. package/src/lib/bisect.pyj +73 -0
  37. package/src/lib/collections.pyj +1 -1
  38. package/src/lib/copy.pyj +120 -120
  39. package/src/lib/csv.pyj +494 -0
  40. package/src/lib/elementmaker.pyj +83 -83
  41. package/src/lib/encodings.pyj +126 -126
  42. package/src/lib/gettext.pyj +569 -569
  43. package/src/lib/heapq.pyj +98 -0
  44. package/src/lib/html.pyj +382 -0
  45. package/src/lib/http/__init__.pyj +98 -0
  46. package/src/lib/http/client.pyj +304 -0
  47. package/src/lib/http/cookies.pyj +236 -0
  48. package/src/lib/itertools.pyj +580 -580
  49. package/src/lib/logging.pyj +672 -0
  50. package/src/lib/math.pyj +193 -193
  51. package/src/lib/operator.pyj +11 -11
  52. package/src/lib/pythonize.pyj +20 -20
  53. package/src/lib/random.pyj +118 -118
  54. package/src/lib/react.pyj +74 -74
  55. package/src/lib/string.pyj +357 -0
  56. package/src/lib/textwrap.pyj +329 -0
  57. package/src/lib/traceback.pyj +63 -63
  58. package/src/lib/urllib/__init__.pyj +14 -0
  59. package/src/lib/urllib/error.pyj +66 -0
  60. package/src/lib/urllib/parse.pyj +475 -0
  61. package/src/lib/urllib/request.pyj +86 -0
  62. package/src/lib/uuid.pyj +77 -77
  63. package/src/monaco-language-service/analyzer.js +5 -2
  64. package/src/monaco-language-service/completions.js +26 -0
  65. package/src/monaco-language-service/diagnostics.js +202 -3
  66. package/src/monaco-language-service/dts.js +550 -550
  67. package/src/monaco-language-service/scope.js +1 -0
  68. package/src/output/comments.pyj +45 -45
  69. package/src/output/exceptions.pyj +201 -201
  70. package/src/output/functions.pyj +152 -6
  71. package/src/output/jsx.pyj +164 -164
  72. package/src/output/loops.pyj +17 -2
  73. package/src/output/modules.pyj +1 -1
  74. package/src/output/operators.pyj +15 -0
  75. package/src/output/stream.pyj +0 -1
  76. package/src/output/treeshake.pyj +182 -182
  77. package/src/output/utils.pyj +72 -72
  78. package/src/parse.pyj +80 -17
  79. package/src/string_interpolation.pyj +72 -72
  80. package/src/tokenizer.pyj +1 -1
  81. package/src/unicode_aliases.pyj +576 -576
  82. package/src/utils.pyj +192 -192
  83. package/test/_import_one.pyj +37 -37
  84. package/test/_import_two/__init__.pyj +11 -11
  85. package/test/_import_two/level2/deep.pyj +4 -4
  86. package/test/_import_two/other.pyj +6 -6
  87. package/test/_import_two/sub.pyj +13 -13
  88. package/test/aes_vectors.pyj +421 -421
  89. package/test/annotations.pyj +80 -80
  90. package/test/async_generators.pyj +144 -0
  91. package/test/asyncio.pyj +307 -0
  92. package/test/base64.pyj +202 -0
  93. package/test/bisect.pyj +178 -0
  94. package/test/csv.pyj +405 -0
  95. package/test/decorators.pyj +77 -77
  96. package/test/docstrings.pyj +39 -39
  97. package/test/elementmaker_test.pyj +45 -45
  98. package/test/float_special.pyj +64 -0
  99. package/test/functions.pyj +151 -151
  100. package/test/generators.pyj +41 -41
  101. package/test/generic.pyj +370 -370
  102. package/test/heapq.pyj +174 -0
  103. package/test/html.pyj +212 -0
  104. package/test/http.pyj +259 -0
  105. package/test/imports.pyj +79 -72
  106. package/test/internationalization.pyj +73 -73
  107. package/test/lint.pyj +164 -164
  108. package/test/logging.pyj +356 -0
  109. package/test/long.pyj +130 -0
  110. package/test/loops.pyj +85 -85
  111. package/test/numpy.pyj +734 -734
  112. package/test/parenthesized_with.pyj +141 -0
  113. package/test/python_compat.pyj +3 -5
  114. package/test/python_modulo.pyj +76 -0
  115. package/test/python_modulo_off.pyj +21 -0
  116. package/test/repl.pyj +121 -121
  117. package/test/scoped_flags.pyj +76 -76
  118. package/test/str.pyj +14 -0
  119. package/test/string.pyj +245 -0
  120. package/test/textwrap.pyj +172 -0
  121. package/test/type_display.pyj +48 -0
  122. package/test/type_enforcement.pyj +164 -0
  123. package/test/unit/index.js +14 -6
  124. package/test/unit/language-service-completions.js +119 -0
  125. package/test/unit/language-service-dts.js +543 -543
  126. package/test/unit/language-service-hover.js +455 -455
  127. package/test/unit/language-service-scope.js +32 -0
  128. package/test/unit/language-service.js +127 -3
  129. package/test/unit/run-language-service.js +17 -3
  130. package/test/unit/web-repl.js +2094 -29
  131. package/test/urllib.pyj +193 -0
  132. package/tools/compile.js +1 -1
  133. package/tools/compiler.d.ts +367 -367
  134. package/tools/completer.js +131 -131
  135. package/tools/embedded_compiler.js +7 -7
  136. package/tools/gettext.js +185 -185
  137. package/tools/ini.js +65 -65
  138. package/tools/msgfmt.js +187 -187
  139. package/tools/repl.js +223 -223
  140. package/tools/test.js +118 -118
  141. package/tools/utils.js +128 -128
  142. package/tools/web_repl.js +95 -95
  143. package/try +41 -41
  144. package/web-repl/env.js +196 -196
  145. package/web-repl/index.html +163 -163
  146. package/web-repl/main.js +1 -1
  147. package/web-repl/prism.css +139 -139
  148. package/web-repl/prism.js +113 -113
  149. package/web-repl/rapydscript.js +224 -224
  150. package/web-repl/sha1.js +25 -25
  151. package/test/omit_function_metadata.pyj +0 -20
package/PYTHON_GAPS.md ADDED
@@ -0,0 +1,420 @@
1
+ # RapydScript-NS: Python Compatibility Gaps & Browser-Friendly Additions
2
+
3
+ This document identifies features an experienced Python developer would expect that are missing
4
+ or behave differently in RapydScript-NS, with a focus on what is relevant and useful in a
5
+ browser context. Server-side features (file I/O, subprocesses, sockets, threading, etc.)
6
+ are excluded as they do not apply.
7
+
8
+ ---
9
+
10
+ ## 1. Silent Behavioral Differences (Gotchas)
11
+
12
+ These features exist but behave differently from Python in ways that will silently produce
13
+ wrong results — no error is raised.
14
+
15
+ ### 1.1 `%` Modulo on Negative Numbers *(partially resolved)*
16
+
17
+ **Python:** `%` always returns a non-negative result (true modulo).
18
+ ```python
19
+ -7 % 3 # → 2 (Python)
20
+ -7 % 3 # → -1 (RapydScript default — JS remainder semantics)
21
+ ```
22
+ **Status:** Fixed when using `from __python__ import overload_operators` or
23
+ `from __python__ import python_modulo`. In bare mode (no flags) the JS remainder semantics
24
+ still apply.
25
+
26
+ **Impact:** Any algorithm relying on modular arithmetic (wrapping, circular indexing) may
27
+ produce wrong results in code that does not use the Python operator flags.
28
+
29
+ ---
30
+
31
+ ### 1.2 `is` / `is not` — Identity vs. Equality
32
+
33
+ **Python:** `is` tests object identity (pointer comparison).
34
+ **RapydScript:** `is` compiles to `===` (strict equality), so `x is y` is true whenever
35
+ `x === y`, including for equal primitive values that would be distinct Python objects.
36
+
37
+ ```python
38
+ a = 1000
39
+ b = 1000
40
+ a is b # False in Python (separate int objects)
41
+ # True in RapydScript (1000 === 1000)
42
+ ```
43
+ **Impact:** Code that checks `obj is None` or `obj is sentinel_value` works correctly.
44
+ Code that expects `is` to return `False` for equal-but-distinct numeric values will be wrong.
45
+
46
+ **Note:** This is already documented in the README, but it is subtle and trips up experts.
47
+ The recommended pattern — using a unique sentinel object — works correctly.
48
+
49
+ ---
50
+
51
+ ### 1.3 Function Arguments: `TypeError` on Wrong Count *(opt-in, not default)*
52
+
53
+ **Python:** Too few or too many positional arguments raises `TypeError` at call time.
54
+ **RapydScript default:** Extra args are silently discarded; missing args become `undefined`.
55
+
56
+ ```python
57
+ def f(a, b):
58
+ return a + b
59
+
60
+ f(1, 2, 3) # Python: TypeError. RapydScript default: silently returns 3
61
+ f(1) # Python: TypeError. RapydScript default: returns NaN (1 + undefined)
62
+ ```
63
+ **Status:** Argument count and type-annotation enforcement is now available via the
64
+ `type_enforce` flag on function definitions (emits runtime checks). Not enabled by default
65
+ because it adds overhead to every call. Developers writing library code with strict APIs
66
+ should opt in.
67
+
68
+ ---
69
+
70
+ ### 1.4 Positional-Only and Keyword-Only Parameters Not Enforced
71
+
72
+ **Python:** Positional-only params (`/`) cannot be passed by keyword; keyword-only params
73
+ (`*`) cannot be passed positionally. Both raise `TypeError` on violations.
74
+ **RapydScript:** Violations are silently discarded — the param gets `undefined` with no error.
75
+
76
+ ```python
77
+ def f(a, b, /, *, c):
78
+ return a + b + c
79
+
80
+ f(1, b=2, c=3) # Python: TypeError (b is positional-only)
81
+ # RapydScript: b silently becomes undefined, a=1, c=3
82
+ ```
83
+ **Impact:** API design contracts expressed via param ordering are not enforced.
84
+
85
+ ---
86
+
87
+ ### 1.5 String Encoding — UTF-16 Surrogate Pairs
88
+
89
+ **Python:** Strings are sequences of Unicode code points (full 21-bit range).
90
+ **RapydScript:** Strings are JS strings — UTF-16. Emoji and other non-BMP characters
91
+ (U+10000–U+10FFFF) are represented as surrogate pairs and count as length 2.
92
+
93
+ ```python
94
+ s = '😀'
95
+ len(s) # 1 in Python, 2 in RapydScript
96
+ s[0] # '😀' in Python, '\uD83D' (broken surrogate) in RapydScript
97
+ ```
98
+ **RapydScript provides:** `str.ulen()`, `str.uchrs()`, `str.uslice()` for code-point-aware
99
+ operations, but they must be used consciously.
100
+
101
+ **Impact:** Any code handling emoji, Asian CJK extension, or mathematical symbols may silently
102
+ produce wrong lengths or corrupt characters when sliced.
103
+
104
+ ---
105
+
106
+ ### 1.6 `global` Scoping in Nested Functions
107
+
108
+ **Python:** `global x` inside a nested function forces `x` to refer to the module-level
109
+ variable, bypassing any intermediate closure scopes.
110
+ **RapydScript:** If a variable named `x` exists in an intermediate outer function scope,
111
+ that scope takes precedence over the module-level scope, even with `global x`.
112
+
113
+ **Impact:** Code with complex nested closures + `global` declarations may write to the wrong
114
+ scope silently.
115
+
116
+ ---
117
+
118
+ ### 1.7 Numeric Dict Keys Are Coerced to Strings
119
+
120
+ **Python:** `d = {}; d[1] = 'a'; d['1'] = 'b'; len(d) == 2` (integer and string keys distinct).
121
+ **RapydScript:** The Python `dict` type (backed by ES6 `Map`) stores them distinctly, but
122
+ literal `{1: 'a', '1': 'b'}` and plain JS object interop may coerce integer keys to strings.
123
+
124
+ **Impact:** Code mixing numeric keys with the same string representation may produce collisions
125
+ when interoperating with JS APIs that return plain objects.
126
+
127
+ ---
128
+
129
+ ### 1.8 `Exception.args` vs `.message`
130
+
131
+ **Python:** `Exception('msg').args == ('msg',)` and `.message` is not a standard attribute.
132
+ **RapydScript:** `.message` is the primary attribute (JS `Error` convention). `.args` is
133
+ not populated as a tuple with the message.
134
+
135
+ **Impact:** Code accessing `.args[0]` to get the error message will get `undefined`.
136
+
137
+ ---
138
+
139
+ ### 1.9 Multiple Inheritance MRO
140
+
141
+ **Python:** C3 linearization guarantees a deterministic and consistent method resolution order.
142
+ **RapydScript:** Built on the JS prototype chain. In diamond inheritance or complex hierarchies
143
+ the order may differ from Python's C3 MRO.
144
+
145
+ **Impact:** Unexpected method is called when multiple parent classes define the same method
146
+ name. Hard to debug because no error is raised.
147
+
148
+ ---
149
+
150
+ ## 2. Missing Language Features
151
+
152
+ ### 2.1 `__slots__` Not Enforced
153
+
154
+ `__slots__ = ['x', 'y']` is parsed and accepted but has no runtime effect — arbitrary
155
+ attributes can still be set on instances. No `AttributeError` is raised for assignments to
156
+ undeclared attributes.
157
+
158
+ **Browser relevance:** Used frequently for memory efficiency and API documentation. Enforcement
159
+ via `Object.seal()` or a `Proxy`-based guard would be possible in modern browsers.
160
+
161
+ ---
162
+
163
+ ### 2.2 `__del__` Destructor — No Guaranteed Finalizer
164
+
165
+ `__del__` methods are not called reliably. JavaScript's GC is non-deterministic and provides
166
+ no equivalent to CPython's reference-counting finalizer.
167
+
168
+ **Browser relevance:** Low — most `__del__` usage is for file handles or network connections
169
+ that do not exist in the browser. However, the `FinalizationRegistry` API (available in all
170
+ modern browsers since 2021) could provide best-effort `__del__` support for cleanup of
171
+ external resources like WebGL buffers, WebSockets, etc. Worth adding with a clear caveat
172
+ that timing is not guaranteed.
173
+
174
+ ---
175
+
176
+ ### 2.3 `locals()` Always Returns Empty Dict
177
+
178
+ JavaScript provides no mechanism to introspect local variables at runtime. `locals()` always
179
+ returns an empty dict. Python code that uses `locals()` for string template substitution
180
+ (e.g., `'{x}'.format(**locals())`) will break silently.
181
+
182
+ ---
183
+
184
+ ### 2.4 `from module import *` Not Allowed
185
+
186
+ Star imports are intentionally unsupported to prevent namespace pollution. Python developers
187
+ who rely on them (e.g., `from math import *`) must enumerate imports explicitly.
188
+
189
+ **Impact:** Not a behavioral difference, but a friction point when porting code.
190
+
191
+ ---
192
+
193
+ ### 2.5 `asynccontextmanager` Not Available
194
+
195
+ `contextlib.asynccontextmanager` is absent. Only synchronous `@contextmanager` is implemented.
196
+
197
+ ---
198
+
199
+ ### 2.6 f-string Debugging Format `f'{x=}'` Not Supported
200
+
201
+ Python 3.8+ supports `f'{x=}'` which expands to `f'x={repr(x)}'`. This is not implemented.
202
+
203
+ ```python
204
+ x = 42
205
+ print(f'{x=}') # Python: "x=42". RapydScript: syntax error or wrong output
206
+ ```
207
+
208
+ ---
209
+
210
+ ### 2.7 Ellipsis Evaluates to `undefined`
211
+
212
+ `...` (Ellipsis) parses as a valid expression but evaluates to JS `undefined` rather than
213
+ Python's `Ellipsis` singleton object. Code that stores `...` in containers or checks
214
+ `x is Ellipsis` will behave incorrectly.
215
+
216
+ ---
217
+
218
+ ## 3. Missing Standard Library Modules (Browser-Relevant)
219
+
220
+ These are absent from `src/lib/` and have no substitute.
221
+
222
+ ### 3.1 `decimal` — Decimal Arithmetic
223
+
224
+ `Decimal` arithmetic avoids floating-point rounding errors. Essential for financial
225
+ calculations in browser apps (e-commerce, budgeting tools). JS does not have a built-in
226
+ equivalent; a pure-JS implementation would need to be compiled in.
227
+
228
+ ---
229
+
230
+ ### 3.2 `fractions` — Rational Arithmetic
231
+
232
+ `Fraction(numerator, denominator)` with full arithmetic. Useful for music theory apps,
233
+ math tutoring tools, and any domain requiring exact rational computation.
234
+
235
+ ---
236
+
237
+ ### 3.3 `statistics` — Statistical Functions
238
+
239
+ `mean`, `median`, `mode`, `stdev`, `variance`, `quantiles`. Very commonly needed in
240
+ data-visualization browser apps. Currently `numpy` covers much of this but `statistics`
241
+ is lighter-weight and doesn't require the full numpy import.
242
+
243
+ ---
244
+
245
+ ### 3.4 `difflib` — Sequence Comparison
246
+
247
+ `difflib.unified_diff`, `difflib.SequenceMatcher`, `difflib.get_close_matches`. Useful
248
+ for browser-based code editors, version comparison tools, and fuzzy matching UIs.
249
+
250
+ ---
251
+
252
+ ### 3.5 `pprint` — Pretty Printing
253
+
254
+ `pprint.pformat` / `pprint.pprint`. Primarily a debugging/REPL aid. Given the in-browser
255
+ REPL in this project, implementing `pprint` would make output more readable.
256
+
257
+ ---
258
+
259
+ ### 3.6 `hashlib` — Cryptographic Hashing
260
+
261
+ `hashlib.sha256`, `hashlib.md5`, etc. The Web Crypto API provides `crypto.subtle.digest`
262
+ but its async/buffer-based interface is awkward. A thin `hashlib`-compatible wrapper over
263
+ `crypto.subtle` with a synchronous-friendly API (using the sync `crypto.getRandomValues`)
264
+ for non-cryptographic hashes would be valuable.
265
+
266
+ ---
267
+
268
+ ### 3.7 `enum.IntEnum` and `enum.Flag`
269
+
270
+ The `enum` module provides `Enum` but not `IntEnum` (auto-comparable with integers),
271
+ `StrEnum` (Python 3.11+), or `Flag` (bitfield enums). These are common in protocol
272
+ implementations, permission systems, and state machines.
273
+
274
+ ```python
275
+ from enum import IntEnum
276
+ class Color(IntEnum):
277
+ RED = 1
278
+ GREEN = 2
279
+ Color.RED < Color.GREEN # True — comparison with int semantics
280
+ ```
281
+
282
+ ---
283
+
284
+ ### 3.8 `collections.ChainMap`
285
+
286
+ `ChainMap` provides a multi-level dict lookup (like layered config or scope chains) without
287
+ copying. Not currently in `collections.pyj`.
288
+
289
+ ---
290
+
291
+ ## 4. Tricky Patterns That Require Workarounds
292
+
293
+ These are not missing features but patterns that silently work differently or require
294
+ non-obvious syntax.
295
+
296
+ ### 4.1 Python String Methods on String Literals
297
+
298
+ String literals in RapydScript do NOT have Python methods (`.strip()`, `.join()`, etc.)
299
+ available by default in all contexts. Using string methods requires:
300
+
301
+ - `from pythonize import strings; strings()` (patches `String.prototype` at runtime), or
302
+ - the `--pythonize-strings` compiler option, or
303
+ - calling `str.strip(s)` (module-level form) instead of `s.strip()`
304
+
305
+ **Impact:** The most common pattern in Python — `s.strip().split(',')` — will throw a
306
+ `TypeError` in some contexts without the above setup.
307
+
308
+ ---
309
+
310
+ ### 4.2 Multi-line Anonymous Functions in Call Arguments
311
+
312
+ Multi-line `def` blocks cannot be used as inline arguments to function calls:
313
+
314
+ ```python
315
+ # Does NOT compile correctly:
316
+ result = map(def(x):
317
+ return x * 2
318
+ , my_list)
319
+
320
+ # Must use a named helper:
321
+ def double(x):
322
+ return x * 2
323
+ result = map(double, my_list)
324
+ ```
325
+
326
+ ---
327
+
328
+ ### 4.3 JavaScript Reserved Words as Identifiers
329
+
330
+ All JavaScript reserved words are also forbidden in RapydScript. Common Python identifiers
331
+ that break: `default`, `delete`, `switch`, `case`, `break`, `var`, `void`, `typeof`,
332
+ `instanceof`. Also cannot be used as keyword argument names in function calls.
333
+
334
+ ```python
335
+ def configure(default=None): # 'default' is reserved — compile error
336
+ ...
337
+ # Must rename: def configure(dflt=None):
338
+ ```
339
+
340
+ ---
341
+
342
+ ### 4.4 Class Named `Error` Shadows JS `Error`
343
+
344
+ Defining a class named `Error` (e.g., `class Error(Exception)`) compiles to a JS function
345
+ that shadows the global `JS Error` constructor. If that class is then imported in another
346
+ module context (e.g., the web REPL), the import line `var Error = ρσ_modules.mymod.Error`
347
+ shadows the native `Error`, causing infinite recursion in `Exception.__init__`.
348
+
349
+ **Workaround:** Never name a RapydScript class `Error`. Use `MyError`, `AppError`,
350
+ `ValueError` (which is already defined in baselib), etc.
351
+
352
+ ---
353
+
354
+ ### 4.5 `Cls.method(arg)` vs `@Cls.method`
355
+
356
+ `Cls.method(arg)` compiles to `Cls.prototype.method.call(arg)` (unbound Python 2 style).
357
+ `@Cls.method` as a decorator stores the constructor property and calls it differently.
358
+ These are different lookup paths. A method that must work both as a decorator and as a
359
+ direct class call needs to be installed on both `cls.property` and `cls.prototype.property`.
360
+
361
+ ---
362
+
363
+ ### 4.6 `range` Cannot Be Shadowed as a Parameter Name
364
+
365
+ ```python
366
+ def histogram(data, range): # compile/runtime error — range shadows builtin
367
+ ...
368
+ # Must rename: def histogram(data, data_range):
369
+ ```
370
+
371
+ This extends to other builtins: prefer prefixed parameter names when a parameter naturally
372
+ matches a builtin name.
373
+
374
+ ---
375
+
376
+ ### 4.7 Verbatim Blocks Are Truly Verbatim — No Escape Processing
377
+
378
+ Inside `v'...'` blocks, Python escape sequences are NOT processed. `\n` in a v-block
379
+ becomes a literal backslash-n in the JS output, not a newline.
380
+
381
+ ```python
382
+ # Wrong — \n is not a newline in the regex:
383
+ pat = v'/foo\nbar/'
384
+
385
+ # Right — write the exact JS you want:
386
+ pat = v'/foo\nbar/' # only works as a real newline if you have a literal newline
387
+ ```
388
+ For regex literals, write exactly the JS you want. Double-escaping (`\\n`) produces `\\n`
389
+ in JS (two characters), not a newline.
390
+
391
+ ---
392
+
393
+ ### 4.8 `jstype(x) is 'number'` for `typeof` Checks
394
+
395
+ Python's `type(x)` does not return a string. For JS-style `typeof` checks:
396
+
397
+ ```python
398
+ jstype(x) is 'number' # correct
399
+ type(x) is int # also works for pure RS objects
400
+ ```
401
+ The `jstype()` builtin is the RS equivalent of JS's `typeof`.
402
+
403
+ ---
404
+
405
+ ## 5. Summary Priority Table
406
+
407
+ | Priority | Feature | Effort | Impact |
408
+ |---|---|---|---|
409
+ | High | `enum.IntEnum`, `Flag` | Medium | Protocol and permission modeling |
410
+ | High | `statistics` module | Low | Data analysis without full numpy import |
411
+ | Medium | `pprint` module | Low | REPL output quality |
412
+ | Medium | `collections.ChainMap` | Low | Multi-level scope/config dicts |
413
+ | Medium | f-string `f'{x=}'` debugging format | Low | Developer experience |
414
+ | Medium | `__slots__` enforcement via `Proxy` | Medium | Memory + API documentation |
415
+ | Medium | `fractions` module | Medium | Exact rational arithmetic |
416
+ | Medium | `hashlib` shim over Web Crypto | Medium | Hashing without verbatim JS |
417
+ | Low | `decimal` module | High | Financial calculations |
418
+ | Low | `difflib` module | High | Text diff, fuzzy matching |
419
+ | Low | `asynccontextmanager` | Medium | Async resource management |
420
+ | Low | `__del__` via `FinalizationRegistry` | Medium | Resource cleanup (best-effort) |