rhales 0.4.0 → 0.5.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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/.github/renovate.json5 +52 -0
  3. data/.github/workflows/ci.yml +123 -0
  4. data/.github/workflows/claude-code-review.yml +69 -0
  5. data/.github/workflows/claude.yml +49 -0
  6. data/.github/workflows/code-smells.yml +146 -0
  7. data/.github/workflows/ruby-lint.yml +78 -0
  8. data/.github/workflows/yardoc.yml +126 -0
  9. data/.gitignore +55 -0
  10. data/.pr_agent.toml +63 -0
  11. data/.pre-commit-config.yaml +89 -0
  12. data/.prettierignore +8 -0
  13. data/.prettierrc +38 -0
  14. data/.reek.yml +98 -0
  15. data/.rubocop.yml +428 -0
  16. data/.serena/.gitignore +3 -0
  17. data/.yardopts +56 -0
  18. data/CHANGELOG.md +44 -0
  19. data/CLAUDE.md +1 -1
  20. data/Gemfile +29 -0
  21. data/Gemfile.lock +189 -0
  22. data/README.md +686 -868
  23. data/Rakefile +46 -0
  24. data/debug_context.rb +25 -0
  25. data/demo/rhales-roda-demo/.gitignore +7 -0
  26. data/demo/rhales-roda-demo/Gemfile +32 -0
  27. data/demo/rhales-roda-demo/Gemfile.lock +151 -0
  28. data/demo/rhales-roda-demo/MAIL.md +405 -0
  29. data/demo/rhales-roda-demo/README.md +376 -0
  30. data/demo/rhales-roda-demo/RODA-TEMPLATE-ENGINES.md +192 -0
  31. data/demo/rhales-roda-demo/Rakefile +49 -0
  32. data/demo/rhales-roda-demo/app.rb +325 -0
  33. data/demo/rhales-roda-demo/bin/rackup +26 -0
  34. data/demo/rhales-roda-demo/config.ru +13 -0
  35. data/demo/rhales-roda-demo/db/migrate/001_create_rodauth_tables.rb +266 -0
  36. data/demo/rhales-roda-demo/db/migrate/002_create_rodauth_password_tables.rb +79 -0
  37. data/demo/rhales-roda-demo/db/migrate/003_add_admin_account.rb +68 -0
  38. data/demo/rhales-roda-demo/templates/change_login.rue +31 -0
  39. data/demo/rhales-roda-demo/templates/change_password.rue +36 -0
  40. data/demo/rhales-roda-demo/templates/close_account.rue +31 -0
  41. data/demo/rhales-roda-demo/templates/create_account.rue +40 -0
  42. data/demo/rhales-roda-demo/templates/dashboard.rue +150 -0
  43. data/demo/rhales-roda-demo/templates/home.rue +78 -0
  44. data/demo/rhales-roda-demo/templates/layouts/main.rue +168 -0
  45. data/demo/rhales-roda-demo/templates/login.rue +65 -0
  46. data/demo/rhales-roda-demo/templates/logout.rue +25 -0
  47. data/demo/rhales-roda-demo/templates/reset_password.rue +26 -0
  48. data/demo/rhales-roda-demo/templates/verify_account.rue +27 -0
  49. data/demo/rhales-roda-demo/test_full_output.rb +27 -0
  50. data/demo/rhales-roda-demo/test_simple.rb +24 -0
  51. data/docs/.gitignore +9 -0
  52. data/docs/architecture/data-flow.md +499 -0
  53. data/examples/dashboard-with-charts.rue +271 -0
  54. data/examples/form-with-validation.rue +180 -0
  55. data/examples/simple-page.rue +61 -0
  56. data/examples/vue.rue +136 -0
  57. data/generate-json-schemas.ts +158 -0
  58. data/json_schemer_migration_summary.md +172 -0
  59. data/lib/rhales/adapters/base_auth.rb +2 -0
  60. data/lib/rhales/adapters/base_request.rb +2 -0
  61. data/lib/rhales/adapters/base_session.rb +2 -0
  62. data/lib/rhales/adapters.rb +7 -0
  63. data/lib/rhales/configuration.rb +47 -0
  64. data/lib/rhales/core/context.rb +354 -0
  65. data/lib/rhales/{rue_document.rb → core/rue_document.rb} +56 -38
  66. data/lib/rhales/{template_engine.rb → core/template_engine.rb} +66 -59
  67. data/lib/rhales/{view.rb → core/view.rb} +112 -135
  68. data/lib/rhales/{view_composition.rb → core/view_composition.rb} +78 -8
  69. data/lib/rhales/core.rb +9 -0
  70. data/lib/rhales/errors/hydration_collision_error.rb +2 -0
  71. data/lib/rhales/errors.rb +2 -0
  72. data/lib/rhales/{earliest_injection_detector.rb → hydration/earliest_injection_detector.rb} +4 -0
  73. data/lib/rhales/hydration/hydration_data_aggregator.rb +487 -0
  74. data/lib/rhales/{hydration_endpoint.rb → hydration/hydration_endpoint.rb} +16 -12
  75. data/lib/rhales/{hydration_injector.rb → hydration/hydration_injector.rb} +4 -0
  76. data/lib/rhales/{hydration_registry.rb → hydration/hydration_registry.rb} +2 -0
  77. data/lib/rhales/hydration/hydrator.rb +102 -0
  78. data/lib/rhales/{link_based_injection_detector.rb → hydration/link_based_injection_detector.rb} +4 -0
  79. data/lib/rhales/{mount_point_detector.rb → hydration/mount_point_detector.rb} +4 -0
  80. data/lib/rhales/{safe_injection_validator.rb → hydration/safe_injection_validator.rb} +4 -0
  81. data/lib/rhales/hydration.rb +13 -0
  82. data/lib/rhales/{refinements → integrations/refinements}/require_refinements.rb +3 -1
  83. data/lib/rhales/{tilt.rb → integrations/tilt.rb} +22 -15
  84. data/lib/rhales/integrations.rb +6 -0
  85. data/lib/rhales/middleware/json_responder.rb +191 -0
  86. data/lib/rhales/middleware/schema_validator.rb +300 -0
  87. data/lib/rhales/middleware.rb +6 -0
  88. data/lib/rhales/parsers/handlebars_parser.rb +2 -0
  89. data/lib/rhales/parsers/rue_format_parser.rb +9 -7
  90. data/lib/rhales/parsers.rb +9 -0
  91. data/lib/rhales/{csp.rb → security/csp.rb} +27 -3
  92. data/lib/rhales/utils/json_serializer.rb +114 -0
  93. data/lib/rhales/utils/logging_helpers.rb +75 -0
  94. data/lib/rhales/utils/schema_extractor.rb +132 -0
  95. data/lib/rhales/utils/schema_generator.rb +194 -0
  96. data/lib/rhales/utils.rb +40 -0
  97. data/lib/rhales/version.rb +3 -1
  98. data/lib/rhales.rb +41 -24
  99. data/lib/tasks/rhales_schema.rake +197 -0
  100. data/package.json +10 -0
  101. data/pnpm-lock.yaml +345 -0
  102. data/pnpm-workspace.yaml +2 -0
  103. data/proofs/error_handling.rb +79 -0
  104. data/proofs/expanded_object_inheritance.rb +82 -0
  105. data/proofs/partial_context_scoping_fix.rb +168 -0
  106. data/proofs/ui_context_partial_inheritance.rb +236 -0
  107. data/rhales.gemspec +14 -6
  108. data/schema_vs_data_comparison.md +254 -0
  109. data/test_direct_access.rb +36 -0
  110. metadata +141 -23
  111. data/CLAUDE.locale.txt +0 -7
  112. data/lib/rhales/context.rb +0 -239
  113. data/lib/rhales/hydration_data_aggregator.rb +0 -221
  114. data/lib/rhales/hydrator.rb +0 -141
  115. data/lib/rhales/parsers/handlebars-grammar-review.txt +0 -39
data/pnpm-lock.yaml ADDED
@@ -0,0 +1,345 @@
1
+ lockfileVersion: '9.0'
2
+
3
+ settings:
4
+ autoInstallPeers: true
5
+ excludeLinksFromLockfile: false
6
+
7
+ importers:
8
+
9
+ .:
10
+ dependencies:
11
+ zod:
12
+ specifier: ^4.1.12
13
+ version: 4.1.12
14
+ devDependencies:
15
+ '@prettier/plugin-ruby':
16
+ specifier: ^4.0.4
17
+ version: 4.0.4(prettier@3.6.2)
18
+ tsx:
19
+ specifier: ^4.20.6
20
+ version: 4.20.6
21
+
22
+ packages:
23
+
24
+ '@esbuild/aix-ppc64@0.25.11':
25
+ resolution: {integrity: sha512-Xt1dOL13m8u0WE8iplx9Ibbm+hFAO0GsU2P34UNoDGvZYkY8ifSiy6Zuc1lYxfG7svWE2fzqCUmFp5HCn51gJg==}
26
+ engines: {node: '>=18'}
27
+ cpu: [ppc64]
28
+ os: [aix]
29
+
30
+ '@esbuild/android-arm64@0.25.11':
31
+ resolution: {integrity: sha512-9slpyFBc4FPPz48+f6jyiXOx/Y4v34TUeDDXJpZqAWQn/08lKGeD8aDp9TMn9jDz2CiEuHwfhRmGBvpnd/PWIQ==}
32
+ engines: {node: '>=18'}
33
+ cpu: [arm64]
34
+ os: [android]
35
+
36
+ '@esbuild/android-arm@0.25.11':
37
+ resolution: {integrity: sha512-uoa7dU+Dt3HYsethkJ1k6Z9YdcHjTrSb5NUy66ZfZaSV8hEYGD5ZHbEMXnqLFlbBflLsl89Zke7CAdDJ4JI+Gg==}
38
+ engines: {node: '>=18'}
39
+ cpu: [arm]
40
+ os: [android]
41
+
42
+ '@esbuild/android-x64@0.25.11':
43
+ resolution: {integrity: sha512-Sgiab4xBjPU1QoPEIqS3Xx+R2lezu0LKIEcYe6pftr56PqPygbB7+szVnzoShbx64MUupqoE0KyRlN7gezbl8g==}
44
+ engines: {node: '>=18'}
45
+ cpu: [x64]
46
+ os: [android]
47
+
48
+ '@esbuild/darwin-arm64@0.25.11':
49
+ resolution: {integrity: sha512-VekY0PBCukppoQrycFxUqkCojnTQhdec0vevUL/EDOCnXd9LKWqD/bHwMPzigIJXPhC59Vd1WFIL57SKs2mg4w==}
50
+ engines: {node: '>=18'}
51
+ cpu: [arm64]
52
+ os: [darwin]
53
+
54
+ '@esbuild/darwin-x64@0.25.11':
55
+ resolution: {integrity: sha512-+hfp3yfBalNEpTGp9loYgbknjR695HkqtY3d3/JjSRUyPg/xd6q+mQqIb5qdywnDxRZykIHs3axEqU6l1+oWEQ==}
56
+ engines: {node: '>=18'}
57
+ cpu: [x64]
58
+ os: [darwin]
59
+
60
+ '@esbuild/freebsd-arm64@0.25.11':
61
+ resolution: {integrity: sha512-CmKjrnayyTJF2eVuO//uSjl/K3KsMIeYeyN7FyDBjsR3lnSJHaXlVoAK8DZa7lXWChbuOk7NjAc7ygAwrnPBhA==}
62
+ engines: {node: '>=18'}
63
+ cpu: [arm64]
64
+ os: [freebsd]
65
+
66
+ '@esbuild/freebsd-x64@0.25.11':
67
+ resolution: {integrity: sha512-Dyq+5oscTJvMaYPvW3x3FLpi2+gSZTCE/1ffdwuM6G1ARang/mb3jvjxs0mw6n3Lsw84ocfo9CrNMqc5lTfGOw==}
68
+ engines: {node: '>=18'}
69
+ cpu: [x64]
70
+ os: [freebsd]
71
+
72
+ '@esbuild/linux-arm64@0.25.11':
73
+ resolution: {integrity: sha512-Qr8AzcplUhGvdyUF08A1kHU3Vr2O88xxP0Tm8GcdVOUm25XYcMPp2YqSVHbLuXzYQMf9Bh/iKx7YPqECs6ffLA==}
74
+ engines: {node: '>=18'}
75
+ cpu: [arm64]
76
+ os: [linux]
77
+
78
+ '@esbuild/linux-arm@0.25.11':
79
+ resolution: {integrity: sha512-TBMv6B4kCfrGJ8cUPo7vd6NECZH/8hPpBHHlYI3qzoYFvWu2AdTvZNuU/7hsbKWqu/COU7NIK12dHAAqBLLXgw==}
80
+ engines: {node: '>=18'}
81
+ cpu: [arm]
82
+ os: [linux]
83
+
84
+ '@esbuild/linux-ia32@0.25.11':
85
+ resolution: {integrity: sha512-TmnJg8BMGPehs5JKrCLqyWTVAvielc615jbkOirATQvWWB1NMXY77oLMzsUjRLa0+ngecEmDGqt5jiDC6bfvOw==}
86
+ engines: {node: '>=18'}
87
+ cpu: [ia32]
88
+ os: [linux]
89
+
90
+ '@esbuild/linux-loong64@0.25.11':
91
+ resolution: {integrity: sha512-DIGXL2+gvDaXlaq8xruNXUJdT5tF+SBbJQKbWy/0J7OhU8gOHOzKmGIlfTTl6nHaCOoipxQbuJi7O++ldrxgMw==}
92
+ engines: {node: '>=18'}
93
+ cpu: [loong64]
94
+ os: [linux]
95
+
96
+ '@esbuild/linux-mips64el@0.25.11':
97
+ resolution: {integrity: sha512-Osx1nALUJu4pU43o9OyjSCXokFkFbyzjXb6VhGIJZQ5JZi8ylCQ9/LFagolPsHtgw6himDSyb5ETSfmp4rpiKQ==}
98
+ engines: {node: '>=18'}
99
+ cpu: [mips64el]
100
+ os: [linux]
101
+
102
+ '@esbuild/linux-ppc64@0.25.11':
103
+ resolution: {integrity: sha512-nbLFgsQQEsBa8XSgSTSlrnBSrpoWh7ioFDUmwo158gIm5NNP+17IYmNWzaIzWmgCxq56vfr34xGkOcZ7jX6CPw==}
104
+ engines: {node: '>=18'}
105
+ cpu: [ppc64]
106
+ os: [linux]
107
+
108
+ '@esbuild/linux-riscv64@0.25.11':
109
+ resolution: {integrity: sha512-HfyAmqZi9uBAbgKYP1yGuI7tSREXwIb438q0nqvlpxAOs3XnZ8RsisRfmVsgV486NdjD7Mw2UrFSw51lzUk1ww==}
110
+ engines: {node: '>=18'}
111
+ cpu: [riscv64]
112
+ os: [linux]
113
+
114
+ '@esbuild/linux-s390x@0.25.11':
115
+ resolution: {integrity: sha512-HjLqVgSSYnVXRisyfmzsH6mXqyvj0SA7pG5g+9W7ESgwA70AXYNpfKBqh1KbTxmQVaYxpzA/SvlB9oclGPbApw==}
116
+ engines: {node: '>=18'}
117
+ cpu: [s390x]
118
+ os: [linux]
119
+
120
+ '@esbuild/linux-x64@0.25.11':
121
+ resolution: {integrity: sha512-HSFAT4+WYjIhrHxKBwGmOOSpphjYkcswF449j6EjsjbinTZbp8PJtjsVK1XFJStdzXdy/jaddAep2FGY+wyFAQ==}
122
+ engines: {node: '>=18'}
123
+ cpu: [x64]
124
+ os: [linux]
125
+
126
+ '@esbuild/netbsd-arm64@0.25.11':
127
+ resolution: {integrity: sha512-hr9Oxj1Fa4r04dNpWr3P8QKVVsjQhqrMSUzZzf+LZcYjZNqhA3IAfPQdEh1FLVUJSiu6sgAwp3OmwBfbFgG2Xg==}
128
+ engines: {node: '>=18'}
129
+ cpu: [arm64]
130
+ os: [netbsd]
131
+
132
+ '@esbuild/netbsd-x64@0.25.11':
133
+ resolution: {integrity: sha512-u7tKA+qbzBydyj0vgpu+5h5AeudxOAGncb8N6C9Kh1N4n7wU1Xw1JDApsRjpShRpXRQlJLb9wY28ELpwdPcZ7A==}
134
+ engines: {node: '>=18'}
135
+ cpu: [x64]
136
+ os: [netbsd]
137
+
138
+ '@esbuild/openbsd-arm64@0.25.11':
139
+ resolution: {integrity: sha512-Qq6YHhayieor3DxFOoYM1q0q1uMFYb7cSpLD2qzDSvK1NAvqFi8Xgivv0cFC6J+hWVw2teCYltyy9/m/14ryHg==}
140
+ engines: {node: '>=18'}
141
+ cpu: [arm64]
142
+ os: [openbsd]
143
+
144
+ '@esbuild/openbsd-x64@0.25.11':
145
+ resolution: {integrity: sha512-CN+7c++kkbrckTOz5hrehxWN7uIhFFlmS/hqziSFVWpAzpWrQoAG4chH+nN3Be+Kzv/uuo7zhX716x3Sn2Jduw==}
146
+ engines: {node: '>=18'}
147
+ cpu: [x64]
148
+ os: [openbsd]
149
+
150
+ '@esbuild/openharmony-arm64@0.25.11':
151
+ resolution: {integrity: sha512-rOREuNIQgaiR+9QuNkbkxubbp8MSO9rONmwP5nKncnWJ9v5jQ4JxFnLu4zDSRPf3x4u+2VN4pM4RdyIzDty/wQ==}
152
+ engines: {node: '>=18'}
153
+ cpu: [arm64]
154
+ os: [openharmony]
155
+
156
+ '@esbuild/sunos-x64@0.25.11':
157
+ resolution: {integrity: sha512-nq2xdYaWxyg9DcIyXkZhcYulC6pQ2FuCgem3LI92IwMgIZ69KHeY8T4Y88pcwoLIjbed8n36CyKoYRDygNSGhA==}
158
+ engines: {node: '>=18'}
159
+ cpu: [x64]
160
+ os: [sunos]
161
+
162
+ '@esbuild/win32-arm64@0.25.11':
163
+ resolution: {integrity: sha512-3XxECOWJq1qMZ3MN8srCJ/QfoLpL+VaxD/WfNRm1O3B4+AZ/BnLVgFbUV3eiRYDMXetciH16dwPbbHqwe1uU0Q==}
164
+ engines: {node: '>=18'}
165
+ cpu: [arm64]
166
+ os: [win32]
167
+
168
+ '@esbuild/win32-ia32@0.25.11':
169
+ resolution: {integrity: sha512-3ukss6gb9XZ8TlRyJlgLn17ecsK4NSQTmdIXRASVsiS2sQ6zPPZklNJT5GR5tE/MUarymmy8kCEf5xPCNCqVOA==}
170
+ engines: {node: '>=18'}
171
+ cpu: [ia32]
172
+ os: [win32]
173
+
174
+ '@esbuild/win32-x64@0.25.11':
175
+ resolution: {integrity: sha512-D7Hpz6A2L4hzsRpPaCYkQnGOotdUpDzSGRIv9I+1ITdHROSFUWW95ZPZWQmGka1Fg7W3zFJowyn9WGwMJ0+KPA==}
176
+ engines: {node: '>=18'}
177
+ cpu: [x64]
178
+ os: [win32]
179
+
180
+ '@prettier/plugin-ruby@4.0.4':
181
+ resolution: {integrity: sha512-lCpvfS/dQU5WrwN3AQ5vR8qrvj2h5gE41X08NNzAAXvHdM4zwwGRcP2sHSxfu6n6No+ljWCVx95NvJPFTTjCTg==}
182
+ peerDependencies:
183
+ prettier: ^3.0.0
184
+
185
+ esbuild@0.25.11:
186
+ resolution: {integrity: sha512-KohQwyzrKTQmhXDW1PjCv3Tyspn9n5GcY2RTDqeORIdIJY8yKIF7sTSopFmn/wpMPW4rdPXI0UE5LJLuq3bx0Q==}
187
+ engines: {node: '>=18'}
188
+ hasBin: true
189
+
190
+ fsevents@2.3.3:
191
+ resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
192
+ engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
193
+ os: [darwin]
194
+
195
+ get-tsconfig@4.12.0:
196
+ resolution: {integrity: sha512-LScr2aNr2FbjAjZh2C6X6BxRx1/x+aTDExct/xyq2XKbYOiG5c0aK7pMsSuyc0brz3ibr/lbQiHD9jzt4lccJw==}
197
+
198
+ prettier@3.6.2:
199
+ resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==}
200
+ engines: {node: '>=14'}
201
+ hasBin: true
202
+
203
+ resolve-pkg-maps@1.0.0:
204
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
205
+
206
+ tsx@4.20.6:
207
+ resolution: {integrity: sha512-ytQKuwgmrrkDTFP4LjR0ToE2nqgy886GpvRSpU0JAnrdBYppuY5rLkRUYPU1yCryb24SsKBTL/hlDQAEFVwtZg==}
208
+ engines: {node: '>=18.0.0'}
209
+ hasBin: true
210
+
211
+ zod@4.1.12:
212
+ resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==}
213
+
214
+ snapshots:
215
+
216
+ '@esbuild/aix-ppc64@0.25.11':
217
+ optional: true
218
+
219
+ '@esbuild/android-arm64@0.25.11':
220
+ optional: true
221
+
222
+ '@esbuild/android-arm@0.25.11':
223
+ optional: true
224
+
225
+ '@esbuild/android-x64@0.25.11':
226
+ optional: true
227
+
228
+ '@esbuild/darwin-arm64@0.25.11':
229
+ optional: true
230
+
231
+ '@esbuild/darwin-x64@0.25.11':
232
+ optional: true
233
+
234
+ '@esbuild/freebsd-arm64@0.25.11':
235
+ optional: true
236
+
237
+ '@esbuild/freebsd-x64@0.25.11':
238
+ optional: true
239
+
240
+ '@esbuild/linux-arm64@0.25.11':
241
+ optional: true
242
+
243
+ '@esbuild/linux-arm@0.25.11':
244
+ optional: true
245
+
246
+ '@esbuild/linux-ia32@0.25.11':
247
+ optional: true
248
+
249
+ '@esbuild/linux-loong64@0.25.11':
250
+ optional: true
251
+
252
+ '@esbuild/linux-mips64el@0.25.11':
253
+ optional: true
254
+
255
+ '@esbuild/linux-ppc64@0.25.11':
256
+ optional: true
257
+
258
+ '@esbuild/linux-riscv64@0.25.11':
259
+ optional: true
260
+
261
+ '@esbuild/linux-s390x@0.25.11':
262
+ optional: true
263
+
264
+ '@esbuild/linux-x64@0.25.11':
265
+ optional: true
266
+
267
+ '@esbuild/netbsd-arm64@0.25.11':
268
+ optional: true
269
+
270
+ '@esbuild/netbsd-x64@0.25.11':
271
+ optional: true
272
+
273
+ '@esbuild/openbsd-arm64@0.25.11':
274
+ optional: true
275
+
276
+ '@esbuild/openbsd-x64@0.25.11':
277
+ optional: true
278
+
279
+ '@esbuild/openharmony-arm64@0.25.11':
280
+ optional: true
281
+
282
+ '@esbuild/sunos-x64@0.25.11':
283
+ optional: true
284
+
285
+ '@esbuild/win32-arm64@0.25.11':
286
+ optional: true
287
+
288
+ '@esbuild/win32-ia32@0.25.11':
289
+ optional: true
290
+
291
+ '@esbuild/win32-x64@0.25.11':
292
+ optional: true
293
+
294
+ '@prettier/plugin-ruby@4.0.4(prettier@3.6.2)':
295
+ dependencies:
296
+ prettier: 3.6.2
297
+
298
+ esbuild@0.25.11:
299
+ optionalDependencies:
300
+ '@esbuild/aix-ppc64': 0.25.11
301
+ '@esbuild/android-arm': 0.25.11
302
+ '@esbuild/android-arm64': 0.25.11
303
+ '@esbuild/android-x64': 0.25.11
304
+ '@esbuild/darwin-arm64': 0.25.11
305
+ '@esbuild/darwin-x64': 0.25.11
306
+ '@esbuild/freebsd-arm64': 0.25.11
307
+ '@esbuild/freebsd-x64': 0.25.11
308
+ '@esbuild/linux-arm': 0.25.11
309
+ '@esbuild/linux-arm64': 0.25.11
310
+ '@esbuild/linux-ia32': 0.25.11
311
+ '@esbuild/linux-loong64': 0.25.11
312
+ '@esbuild/linux-mips64el': 0.25.11
313
+ '@esbuild/linux-ppc64': 0.25.11
314
+ '@esbuild/linux-riscv64': 0.25.11
315
+ '@esbuild/linux-s390x': 0.25.11
316
+ '@esbuild/linux-x64': 0.25.11
317
+ '@esbuild/netbsd-arm64': 0.25.11
318
+ '@esbuild/netbsd-x64': 0.25.11
319
+ '@esbuild/openbsd-arm64': 0.25.11
320
+ '@esbuild/openbsd-x64': 0.25.11
321
+ '@esbuild/openharmony-arm64': 0.25.11
322
+ '@esbuild/sunos-x64': 0.25.11
323
+ '@esbuild/win32-arm64': 0.25.11
324
+ '@esbuild/win32-ia32': 0.25.11
325
+ '@esbuild/win32-x64': 0.25.11
326
+
327
+ fsevents@2.3.3:
328
+ optional: true
329
+
330
+ get-tsconfig@4.12.0:
331
+ dependencies:
332
+ resolve-pkg-maps: 1.0.0
333
+
334
+ prettier@3.6.2: {}
335
+
336
+ resolve-pkg-maps@1.0.0: {}
337
+
338
+ tsx@4.20.6:
339
+ dependencies:
340
+ esbuild: 0.25.11
341
+ get-tsconfig: 4.12.0
342
+ optionalDependencies:
343
+ fsevents: 2.3.3
344
+
345
+ zod@4.1.12: {}
@@ -0,0 +1,2 @@
1
+ onlyBuiltDependencies:
2
+ - esbuild
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env ruby
2
+ # proofs/error_handling.rb
3
+
4
+ require_relative 'lib/rhales'
5
+
6
+ # Example demonstrating the new error hierarchy
7
+
8
+ puts '=== Rhales Error Hierarchy Demo ==='
9
+ puts
10
+
11
+ # 1. Catching all Rhales errors
12
+ begin
13
+ template = '{{#if user}}Hello{{else' # Missing closing {{/if}}
14
+ context = Rhales::Context.minimal(client: { user: 'John' })
15
+ Rhales::TemplateEngine.render(template, context)
16
+ rescue Rhales::Error => ex
17
+ puts "Caught any Rhales error: #{ex.class} - #{ex.message}"
18
+ end
19
+
20
+ puts
21
+
22
+ # 2. Catching render errors (which wrap parse errors)
23
+ begin
24
+ template = '{{unclosed'
25
+ context = Rhales::Context.minimal(client: {})
26
+ Rhales::TemplateEngine.render(template, context)
27
+ rescue Rhales::TemplateEngine::RenderError => ex
28
+ puts 'Render error wrapping parse error:'
29
+ puts " Class: #{ex.class}"
30
+ puts " Message: #{ex.message}"
31
+ end
32
+
33
+ puts
34
+
35
+ # 2b. Catching parse errors directly from parser
36
+ begin
37
+ parser = Rhales::HandlebarsParser.new('{{unclosed')
38
+ parser.parse!
39
+ rescue Rhales::ParseError => ex
40
+ puts 'Direct parse error details:'
41
+ puts " Class: #{ex.class}"
42
+ puts " Message: #{ex.message}"
43
+ puts " Source type: #{ex.source_type}"
44
+ puts " Line: #{ex.line}, Column: #{ex.column}"
45
+ end
46
+
47
+ puts
48
+
49
+ # 3. Validation error from Parser
50
+ begin
51
+ template = <<~RUE
52
+ <logic>
53
+ # Missing required sections
54
+ </logic>
55
+ RUE
56
+
57
+ parser = Rhales::RueDocument.new(template)
58
+ parser.parse!
59
+ rescue Rhales::ValidationError => ex
60
+ puts "Validation error: #{ex.class} - #{ex.message}"
61
+ end
62
+
63
+ puts
64
+
65
+ # 4. Render error example
66
+ begin
67
+ template = '{{> missing_partial}}'
68
+ context = Rhales::Context.minimal(client: {})
69
+ Rhales::TemplateEngine.render(template, context)
70
+ rescue Rhales::RenderError => ex
71
+ puts "Render error: #{ex.class} - #{ex.message}"
72
+ end
73
+
74
+ puts
75
+ puts '=== Benefits of the new hierarchy ==='
76
+ puts '1. Single rescue for all Rhales errors: rescue Rhales::Error'
77
+ puts '2. Stage-specific handling: ParseError, ValidationError, RenderError'
78
+ puts '3. Consistent error attributes across all parse errors'
79
+ puts '4. Better error messages with source context'
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Proof that partials can now access parent's expanded object properties
4
+ # This specifically tests that {{authenticated}} and {{ui}} from {{{onetime_window}}} are accessible
5
+
6
+ require_relative '../lib/rhales'
7
+
8
+ puts "=== Testing Expanded Object Inheritance in Partials ==="
9
+ puts "Verifying that properties from parent's {{{onetime_window}}} are accessible in partials\n\n"
10
+
11
+ # Create context with onetime_window object that will be expanded
12
+ props = {
13
+ greeting: 'Hello World',
14
+ user: { name: 'John Doe' },
15
+ onetime_window: {
16
+ authenticated: true,
17
+ ui: {
18
+ theme: 'dark',
19
+ language: 'en'
20
+ },
21
+ features: {
22
+ account_creation: true,
23
+ social_login: false
24
+ }
25
+ }
26
+ }
27
+
28
+ # Test: Direct TemplateEngine test to show the inheritance
29
+ puts "Test: Direct test showing expanded context inheritance"
30
+ puts "-" * 50
31
+
32
+ # Simulate what View does - expand the onetime_window object into the context
33
+ expanded_props = props.dup
34
+ expanded_props.merge!(props[:onetime_window])
35
+
36
+ expanded_context = Rhales::Context.minimal(props: expanded_props)
37
+
38
+ simple_main = <<~RUE
39
+ <template>
40
+ <div>
41
+ <p>Main sees authenticated: {{authenticated}}</p>
42
+ {{> simple_partial}}
43
+ </div>
44
+ </template>
45
+ RUE
46
+
47
+ simple_partial = <<~RUE
48
+ <data>
49
+ {
50
+ "partial_var": "I'm from partial"
51
+ }
52
+ </data>
53
+
54
+ <template>
55
+ <div class="simple-partial">
56
+ <p>{{partial_var}}</p>
57
+ <p>Partial sees authenticated: {{authenticated}}</p>
58
+ <p>Partial sees ui.theme: {{ui.theme}}</p>
59
+ </div>
60
+ </template>
61
+ RUE
62
+
63
+ partial_resolver2 = proc do |name|
64
+ case name
65
+ when 'simple_partial' then simple_partial
66
+ else nil
67
+ end
68
+ end
69
+
70
+ engine = Rhales::TemplateEngine.new(simple_main, expanded_context, partial_resolver: partial_resolver2)
71
+ result2 = engine.render
72
+
73
+ puts result2
74
+ puts "\nDirect Test Checks:"
75
+ puts "✅ Partial inherits expanded authenticated" if result2.include?("Partial sees authenticated: true")
76
+ puts "✅ Partial inherits expanded ui.theme" if result2.include?("Partial sees ui.theme: dark")
77
+
78
+ puts "\n\n=== Summary ==="
79
+ puts "The fix confirms that partials can now access properties from the parent's"
80
+ puts "expanded objects (like {{{onetime_window}}}). This means {{authenticated}}"
81
+ puts "and {{ui}} are no longer empty in partials - they correctly inherit from"
82
+ puts "the parent's expanded context."
@@ -0,0 +1,168 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Proof that partials now correctly inherit parent context and can access their own data sections
4
+ # This demonstrates the fix for issue #16 - Context Scoping
5
+
6
+ require_relative '../lib/rhales'
7
+
8
+ puts "=== Testing Partial Context Scoping Fix ==="
9
+ puts "Issue #16: Partials should access both inherited context and local data\n\n"
10
+
11
+ # Create context with some props
12
+ props = {
13
+ greeting: 'Hello World',
14
+ user: { name: 'John Doe' },
15
+ authenticated: true,
16
+ main_message: 'From main props'
17
+ }
18
+
19
+ context = Rhales::Context.minimal(props: props)
20
+
21
+ # Test 1: Basic partial with data section
22
+ puts "Test 1: Basic partial with local data section"
23
+ puts "-" * 50
24
+
25
+ main_template = <<~RUE
26
+ <template>
27
+ <div class="main">
28
+ <h1>Main Template</h1>
29
+ <p>Props: {{greeting}}</p>
30
+ {{> basic_partial}}
31
+ </div>
32
+ </template>
33
+ RUE
34
+
35
+ basic_partial = <<~RUE
36
+ <data>
37
+ {
38
+ "partial_message": "I am from the partial's data section",
39
+ "computed_value": "Greeting is: {{greeting}}"
40
+ }
41
+ </data>
42
+
43
+ <template>
44
+ <div class="partial">
45
+ <p>Local data: {{partial_message}}</p>
46
+ <p>Computed: {{computed_value}}</p>
47
+ <p>Inherited: {{main_message}}</p>
48
+ </div>
49
+ </template>
50
+ RUE
51
+
52
+ partial_resolver = proc do |name|
53
+ case name
54
+ when 'basic_partial' then basic_partial
55
+ else nil
56
+ end
57
+ end
58
+
59
+ engine = Rhales::TemplateEngine.new(main_template, context, partial_resolver: partial_resolver)
60
+ result = engine.render
61
+
62
+ puts result
63
+ puts "\nChecks:"
64
+ puts "✅ Partial's local data accessible" if result.include?("I am from the partial&#39;s data section")
65
+ puts "✅ Partial can use parent context in data section" if result.include?("Greeting is: Hello World")
66
+ puts "✅ Partial inherits parent props" if result.include?("From main props")
67
+
68
+ # Test 2: Partial with window attribute
69
+ puts "\n\nTest 2: Partial with window attribute (head.rue scenario)"
70
+ puts "-" * 50
71
+
72
+ head_partial = <<~RUE
73
+ <data window="headData">
74
+ {
75
+ "page_title": "One Time Secret",
76
+ "theme_color": "#dc4a22"
77
+ }
78
+ </data>
79
+
80
+ <template>
81
+ <head>
82
+ <title>{{page_title}}</title>
83
+ <meta name="theme-color" content="{{theme_color}}">
84
+ <meta name="authenticated" content="{{authenticated}}">
85
+ </head>
86
+ </template>
87
+ RUE
88
+
89
+ main_with_head = <<~RUE
90
+ <template>
91
+ <html>
92
+ {{> head}}
93
+ <body>
94
+ <h1>{{greeting}}</h1>
95
+ </body>
96
+ </html>
97
+ </template>
98
+ RUE
99
+
100
+ partial_resolver2 = proc do |name|
101
+ case name
102
+ when 'head' then head_partial
103
+ else nil
104
+ end
105
+ end
106
+
107
+ engine2 = Rhales::TemplateEngine.new(main_with_head, context, partial_resolver: partial_resolver2)
108
+ result2 = engine2.render
109
+
110
+ puts result2
111
+ puts "\nChecks:"
112
+ puts "✅ page_title from partial's data section" if result2.include?("<title>One Time Secret</title>")
113
+ puts "✅ theme_color from partial's data section" if result2.include?('content="#dc4a22"')
114
+ puts "✅ authenticated from parent context" if result2.include?('content="true"')
115
+
116
+ # Test 3: Variable precedence (local data overrides parent)
117
+ puts "\n\nTest 3: Variable precedence (local overrides parent)"
118
+ puts "-" * 50
119
+
120
+ override_partial = <<~RUE
121
+ <data>
122
+ {
123
+ "greeting": "Overridden greeting from partial",
124
+ "new_var": "Only in partial"
125
+ }
126
+ </data>
127
+
128
+ <template>
129
+ <div class="override-test">
130
+ <p>Greeting: {{greeting}}</p>
131
+ <p>New var: {{new_var}}</p>
132
+ <p>User: {{user.name}}</p>
133
+ </div>
134
+ </template>
135
+ RUE
136
+
137
+ main_override = <<~RUE
138
+ <template>
139
+ <div>
140
+ <p>Main greeting: {{greeting}}</p>
141
+ {{> override_partial}}
142
+ </div>
143
+ </template>
144
+ RUE
145
+
146
+ partial_resolver3 = proc do |name|
147
+ case name
148
+ when 'override_partial' then override_partial
149
+ else nil
150
+ end
151
+ end
152
+
153
+ engine3 = Rhales::TemplateEngine.new(main_override, context, partial_resolver: partial_resolver3)
154
+ result3 = engine3.render
155
+
156
+ puts result3
157
+ puts "\nChecks:"
158
+ puts "✅ Main still has original greeting" if result3.include?("Main greeting: Hello World")
159
+ puts "✅ Partial overrides greeting locally" if result3.include?("Greeting: Overridden greeting from partial")
160
+ puts "✅ Partial has new local variable" if result3.include?("New var: Only in partial")
161
+ puts "✅ Partial inherits user from parent" if result3.include?("User: John Doe")
162
+
163
+ puts "\n\n=== Summary ==="
164
+ puts "The fix successfully implements the intended behavior from spec documents 080 & 082:"
165
+ puts "1. Partials can access their local <data> section variables"
166
+ puts "2. Partials inherit parent context (including expanded objects)"
167
+ puts "3. Local data takes precedence over inherited data"
168
+ puts "4. Window attributes work correctly for client-side hydration"