assign-gingerly 0.0.53 → 0.0.55

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 (114) hide show
  1. package/README.md +847 -5
  2. package/assignFrom.js +229 -9
  3. package/assignFrom.ts +338 -9
  4. package/assignGingerly.js +34 -1
  5. package/assignGingerly.ts +54 -1
  6. package/beVigilant.js +73 -0
  7. package/beVigilant.ts +85 -0
  8. package/enhanceAll.js +106 -0
  9. package/enhanceAll.ts +138 -0
  10. package/handlers/join.js +74 -0
  11. package/handlers/join.ts +80 -0
  12. package/handlers/lazyLoad.js +212 -0
  13. package/handlers/lazyLoad.ts +307 -0
  14. package/handlers/lazyLoadSwitch.js +58 -0
  15. package/handlers/lazyLoadSwitch.ts +63 -0
  16. package/handlers/microDataJoin.js +184 -0
  17. package/handlers/microDataJoin.ts +270 -0
  18. package/inferencer/.gitmodules +3 -0
  19. package/inferencer/.vscode/settings.json +2 -0
  20. package/inferencer/InferencedPropagator.js +230 -0
  21. package/inferencer/InferencedPropagator.ts +269 -0
  22. package/inferencer/LICENSE +21 -0
  23. package/inferencer/README.md +524 -0
  24. package/inferencer/Requirements/SupportForPropagator.md +368 -0
  25. package/inferencer/imports.html +7 -0
  26. package/inferencer/inferencer.js +254 -0
  27. package/inferencer/inferencer.ts +292 -0
  28. package/inferencer/package-lock.json +129 -0
  29. package/inferencer/package.json +60 -0
  30. package/inferencer/playwright-report/data/507ad515125e13390ea07de92f22331c913fa068.md +55 -0
  31. package/inferencer/playwright-report/index.html +90 -0
  32. package/inferencer/playwright.config.ts +54 -0
  33. package/inferencer/test-results/.last-run.json +6 -0
  34. package/inferencer/test-results/inferencer-Inferencer-Enha-535bc-inferencer-tests-in-browser-chromium/error-context.md +55 -0
  35. package/inferencer/tests/inferencedPropagator.html +428 -0
  36. package/inferencer/tests/inferencedPropagator.spec.ts +18 -0
  37. package/inferencer/tests/inferencer.html +355 -0
  38. package/inferencer/tests/inferencer.spec.ts +19 -0
  39. package/inferencer/tsconfig.json +19 -0
  40. package/inferencer/types/.kiro/specs/conversion-template/README.md +128 -0
  41. package/inferencer/types/.kiro/specs/conversion-template/design.md +360 -0
  42. package/inferencer/types/.kiro/specs/conversion-template/requirements.md +191 -0
  43. package/inferencer/types/.kiro/specs/conversion-template/tasks.md +174 -0
  44. package/inferencer/types/.kiro/steering/coding-standards.md +53 -0
  45. package/inferencer/types/.kiro/steering/conversion-guide.md +108 -0
  46. package/inferencer/types/.kiro/steering/declarative-configuration.md +108 -0
  47. package/inferencer/types/.kiro/steering/emc-json-serializability.md +306 -0
  48. package/inferencer/types/EnhancementConversionInstructions.md +1854 -0
  49. package/inferencer/types/LICENSE +21 -0
  50. package/inferencer/types/NewCustomElement.md +388 -0
  51. package/inferencer/types/NewCustomElementFeature.md +683 -0
  52. package/inferencer/types/NewEnhancementInstructions.md +705 -0
  53. package/inferencer/types/README.md +2 -0
  54. package/inferencer/types/agrace/types.d.ts +11 -0
  55. package/inferencer/types/assign-gingerly/types.d.ts +572 -0
  56. package/inferencer/types/be-a-beacon/types.d.ts +17 -0
  57. package/inferencer/types/be-bound/types.d.ts +66 -0
  58. package/inferencer/types/be-buttoned-up/types.d.ts +19 -0
  59. package/inferencer/types/be-calculating/types.d.ts +54 -0
  60. package/inferencer/types/be-clonable/types.d.ts +38 -0
  61. package/inferencer/types/be-committed/types.d.ts +22 -0
  62. package/inferencer/types/be-consoling/types.d.ts +24 -0
  63. package/inferencer/types/be-decked-with/types.d.ts +26 -0
  64. package/inferencer/types/be-delible/types.d.ts +27 -0
  65. package/inferencer/types/be-dispatching/types.d.ts +34 -0
  66. package/inferencer/types/be-evanescent/types.d.ts +20 -0
  67. package/inferencer/types/be-flashy/types.d.ts +21 -0
  68. package/inferencer/types/be-gone/types.d.ts +25 -0
  69. package/inferencer/types/be-observing/types.d.ts +55 -0
  70. package/inferencer/types/be-reflective/types.d.ts +78 -0
  71. package/inferencer/types/be-reformable/types.d.ts +49 -0
  72. package/inferencer/types/be-render-neutral/types.d.ts +32 -0
  73. package/inferencer/types/be-switched/types.d.ts +146 -0
  74. package/inferencer/types/be-typed/types.d.ts +32 -0
  75. package/inferencer/types/be-valued/types.d.ts +22 -0
  76. package/inferencer/types/data-props/types.d.ts +34 -0
  77. package/inferencer/types/do-inc/types.d.ts +56 -0
  78. package/inferencer/types/do-invoke/types.d.ts +38 -0
  79. package/inferencer/types/do-merge/types.d.ts +28 -0
  80. package/inferencer/types/do-toggle/types.d.ts +31 -0
  81. package/inferencer/types/face-up/types.d.ts +100 -0
  82. package/inferencer/types/fetch-for/types.d.ts +36 -0
  83. package/inferencer/types/folder-picker/types.d.ts +21 -0
  84. package/inferencer/types/global.d.ts +29 -0
  85. package/inferencer/types/id-generation/types.d.ts +26 -0
  86. package/inferencer/types/inferencer/types.d.ts +46 -0
  87. package/inferencer/types/mount-observer/types.d.ts +363 -0
  88. package/inferencer/types/nested-regex-groups/types.d.ts +107 -0
  89. package/inferencer/types/pipe-in/types.d.ts +52 -0
  90. package/inferencer/types/roundabout/types.d.ts +268 -0
  91. package/inferencer/types/soak-up/types.d.ts +40 -0
  92. package/inferencer/types/templ-maker/types.d.ts +43 -0
  93. package/inferencer/types/time-ticker/types.d.ts +62 -0
  94. package/inferencer/types/truth-sourcer/types.d.ts +44 -0
  95. package/inferencer/upSearch.js +27 -0
  96. package/inferencer/upSearch.ts +26 -0
  97. package/inferencer/withScopePerimeter.js +27 -0
  98. package/inferencer/withScopePerimeter.ts +33 -0
  99. package/inferredAssignments.js +38 -0
  100. package/inferredAssignments.ts +65 -0
  101. package/isAllowedImportPath.js +42 -0
  102. package/isAllowedImportPath.ts +53 -0
  103. package/package.json +57 -3
  104. package/paths.js +231 -0
  105. package/paths.ts +413 -0
  106. package/processHandlerCommands.js +188 -0
  107. package/processHandlerCommands.ts +217 -0
  108. package/resolveIdRef.js +144 -0
  109. package/resolveIdRef.ts +170 -0
  110. package/resolveValues.js +41 -2
  111. package/resolveValues.ts +41 -1
  112. package/transitionHelper.js +109 -0
  113. package/transitionHelper.ts +132 -0
  114. package/types/assign-gingerly/types.d.ts +89 -0
@@ -0,0 +1,705 @@
1
+ # New Enhancement Instructions
2
+
3
+ ## Introduction
4
+
5
+ This document provides step-by-step instructions for creating a **brand new** "be-*" or "do-*" enhancement project using the modern architecture. Unlike the [Enhancement Conversion Instructions](./EnhancementConversionInstructions.md) (which covers migrating legacy projects), this guide starts from scratch.
6
+
7
+ **Note:** This guide is specifically for **enhancements** (declarative behaviors added to existing HTML elements via attributes). It does NOT apply to custom elements. Enhancements use `be-hive` and `mount-observer` to attach behavior to elements without requiring custom element registration.
8
+
9
+ ## Reference Implementations
10
+
11
+ - **[do-invoke](https://github.com/bahrus/do-invoke)** — Custom parser with nested paths and default values
12
+ - **[do-toggle](https://github.com/bahrus/do-toggle)** — Parser with infer pattern
13
+ - **[do-inc](https://github.com/bahrus/do-inc)** — Increment enhancement with parser
14
+ - **[be-clonable](https://github.com/bahrus/be-clonable)** — Most up-to-date conversion example (useful for architecture patterns)
15
+
16
+ ## Prerequisites
17
+
18
+ - Node.js installed
19
+ - npm installed
20
+ - `ncu` (npm-check-updates) installed globally: `npm install -g npm-check-updates`
21
+
22
+ ## Step 1: Initialize the Project
23
+
24
+ 1. Create a new repository (e.g., `do-merge` or `be-fancy`)
25
+ 2. Run `npm init` or create a `package.json` manually
26
+ 3. Add the `types` submodule:
27
+ ```bash
28
+ git submodule add https://github.com/bahrus/types.git types
29
+ ```
30
+
31
+ ## Step 2: Configure package.json
32
+
33
+ Set up the standard scripts and dependencies:
34
+
35
+ ```json
36
+ {
37
+ "name": "do-my-enhancement",
38
+ "version": "0.0.0",
39
+ "description": "Description of what the enhancement does",
40
+ "type": "module",
41
+ "main": "do-my-enhancement.js",
42
+ "scripts": {
43
+ "build": "node emc.mjs > emc.json && node [emoji].mjs > [emoji].json",
44
+ "serve": "node ./node_modules/spa-ssi/serve.js",
45
+ "test": "playwright test",
46
+ "safari": "npx playwright wk http://localhost:8000",
47
+ "update": "ncu -u && npm install"
48
+ },
49
+ "dependencies": {
50
+ "assign-gingerly": "0.0.5",
51
+ "be-hive": "0.1.9",
52
+ "inferencer": "0.0.1",
53
+ "mount-observer": "0.0.16",
54
+ "roundabout-lib": "0.0.2"
55
+ },
56
+ "devDependencies": {
57
+ "spa-ssi": "0.0.27",
58
+ "@playwright/test": "1.59.1"
59
+ }
60
+ }
61
+ ```
62
+
63
+ **Notes:**
64
+ - Replace `[emoji]` with your emoji shorthand (or remove that part of the build script if no emoji)
65
+ - Use exact versions, not ranges (no `^` or `~`)
66
+ - Add `nested-regex-groups` if you need custom attribute parsing
67
+ - Run `npm run update` after creating package.json to install dependencies
68
+
69
+ ## Step 3: Create Type Definitions
70
+
71
+ Create `types/[project-name]/types.d.ts` with the standard structure:
72
+
73
+ ```typescript
74
+ import { ElementEnhancementGateway, SpawnContext } from "../assign-gingerly/types";
75
+
76
+ export interface EndUserProps {
77
+ // Properties that end users configure via attributes
78
+ myProp: string;
79
+ }
80
+
81
+ export interface AllProps extends EndUserProps {
82
+ enhancedElement: Element & ElementEnhancementGateway;
83
+ resolved: boolean;
84
+ }
85
+
86
+ export type AP = AllProps;
87
+ export type PAP = Partial<AP>;
88
+ export type ProPAP = Promise<PAP>;
89
+
90
+ export interface Actions {
91
+ init(self: AP, enhancedElement: Element, ctx: SpawnContext, initVals: PAP): Promise<void>;
92
+ hydrate(self: AP): ProPAP;
93
+ }
94
+ ```
95
+
96
+ **Key points:**
97
+ - `EndUserProps` — what the user configures via HTML attributes
98
+ - `AllProps` — includes `enhancedElement` and any internal state like `resolved`
99
+ - `Actions` — methods the enhancement exposes; `init` always has the 4-parameter signature
100
+ - If using a custom parser, import `StatementsResult` from `../nested-regex-groups/types`
101
+
102
+ ## Step 4: Create emc.mjs (Build Configuration)
103
+
104
+ Create `emc.mjs` in the project root:
105
+
106
+ ```javascript
107
+ //@ts-check
108
+
109
+ /** @import {EMC} from './types/mount-observer/types' */;
110
+ /** @import {AllProps, Actions} from './types/[project-name]/types' */
111
+ /** @import {RAConfig} from './types/roundabout/types' */
112
+
113
+ /**
114
+ * @type {EMC<any, AllProps, Element, RAConfig<AllProps, Actions> >}
115
+ */
116
+ export const emc = {
117
+ enhConfig: {
118
+ enhKey: 'DoMyEnhancement',
119
+ spawn: '[project-name]/[project-name].js',
120
+ withAttrs: {
121
+ base: '[project-name]',
122
+ // For simple string/number properties:
123
+ // myProp: '${base}-my-prop',
124
+ // For the base attribute itself (JSON object):
125
+ // _base: { mapsTo: 'myProp', instanceOf: 'Object' }
126
+ // For boolean properties:
127
+ // _nudge: { instanceOf: 'Boolean' }
128
+ }
129
+ },
130
+ customData: {
131
+ weakRef: {
132
+ properties: ['enhancedElement']
133
+ },
134
+ actions: {
135
+ hydrate: {
136
+ ifAllOf: ['myProp', 'enhancedElement']
137
+ }
138
+ }
139
+ }
140
+ };
141
+
142
+ export function render(){
143
+ return JSON.stringify(emc, null, 4);
144
+ }
145
+
146
+ console.log(render());
147
+ ```
148
+
149
+ ### Attribute Parsing Options
150
+
151
+ **JSON attribute (parsed by browser):**
152
+ ```javascript
153
+ _base: { mapsTo: 'mergeParamSets', instanceOf: 'Object' }
154
+ ```
155
+ Use when the attribute value is valid JSON. The browser's JSON parser handles it.
156
+
157
+ **Custom parser (for string DSL syntax):**
158
+ ```javascript
159
+ _base: {
160
+ mapsTo: 'invokeParamSet',
161
+ parser: 'parse-pattern-statements',
162
+ instanceOf: 'Array',
163
+ parserConfig: parsePatterns
164
+ }
165
+ ```
166
+ Use when you need to parse a custom string syntax. See [Enhancement Conversion Instructions](./EnhancementConversionInstructions.md) Step 7a for details.
167
+
168
+ ## Step 5: Create Emoji Shorthand (Optional)
169
+
170
+ If your enhancement has an emoji shorthand, create `[emoji].mjs`:
171
+
172
+ ```javascript
173
+ import myJSON from './emc.json' with {type: 'json'};
174
+
175
+ /** @import {EMC} from './types/mount-observer/types' */;
176
+ /** @import {AllProps} from './types/[project-name]/types' */
177
+
178
+ /**
179
+ * @type {EMC<any, AllProps> }
180
+ */
181
+ const emc = {
182
+ ...myJSON,
183
+ enhConfig: {
184
+ ...myJSON.enhConfig,
185
+ enhKey: '[emoji]',
186
+ withAttrs: {
187
+ ...myJSON.enhConfig.withAttrs,
188
+ base: '[emoji]'
189
+ }
190
+ }
191
+ };
192
+
193
+ export function render(){
194
+ return JSON.stringify(emc, null, 4);
195
+ }
196
+
197
+ console.log(render());
198
+ ```
199
+
200
+ **Critical:** The `...myJSON` spread at the top level ensures `customData` is carried over.
201
+
202
+ ## Step 6: Create the Enhancement Class
203
+
204
+ Create `[project-name].js`:
205
+
206
+ ```javascript
207
+ // @ts-check
208
+ /** @import {Actions, PAP, AllProps, AP} from './types/[project-name]/types' */;
209
+ /** @import {RoundaboutOptions} from './types/roundabout/types' */;
210
+ /** @import {ElementEnhancementGateway, SpawnContext} from './types/assign-gingerly/types' */;
211
+ /** @import {EMC} from './types/mount-observer/types' */;
212
+ /** @import {RAConfig} from './types/roundabout/types' */;
213
+
214
+ /**
215
+ * @implements {Actions}
216
+ */
217
+ class DoMyEnhancement {
218
+
219
+ /**
220
+ * @this {AllProps & Actions}
221
+ * @param {Element & ElementEnhancementGateway} enhancedElement
222
+ * @param {SpawnContext} ctx
223
+ * @param {PAP} initVals
224
+ */
225
+ constructor(enhancedElement, ctx, initVals){
226
+ this.init(this, enhancedElement, ctx, initVals);
227
+ }
228
+
229
+ /**
230
+ * @param {AllProps} self
231
+ * @param {Element & ElementEnhancementGateway} enhancedElement
232
+ * @param {SpawnContext} ctx
233
+ * @param {PAP} initVals
234
+ */
235
+ async init(self, enhancedElement, ctx, initVals){
236
+ const {customData} = /** @type {EMC<any, AllProps, Element, RAConfig<AllProps, Actions>>} */ (ctx.emc);
237
+ /**
238
+ * @type {RoundaboutOptions}
239
+ */
240
+ const raOptions = {
241
+ ...customData,
242
+ vm: self,
243
+ initialPropVals: {
244
+ enhancedElement,
245
+ ...customData?.defaultPropVals,
246
+ ...initVals
247
+ }
248
+ };
249
+ (await import('roundabout-lib/roundabout.js')).roundabout(raOptions);
250
+ }
251
+
252
+ /**
253
+ * @param {AP} self
254
+ */
255
+ async hydrate(self){
256
+ // Your enhancement logic here
257
+ return /** @type {PAP} */ ({resolved: true});
258
+ }
259
+ }
260
+
261
+ export {DoMyEnhancement};
262
+ ```
263
+
264
+ **Key patterns:**
265
+ - No base class — plain JavaScript class
266
+ - Constructor delegates to `init` with 4 parameters
267
+ - `init` extracts `customData` from `ctx.emc` (no JSON import needed)
268
+ - Action methods receive `self` (the reactive proxy) as first parameter
269
+ - Return partial props from actions to trigger reactive updates
270
+
271
+ ## Step 7: Create imports.html
272
+
273
+ ```html
274
+ <script type=importmap >
275
+ {
276
+ "imports": {
277
+ "assign-gingerly/": "/node_modules/assign-gingerly/",
278
+ "[project-name]/": "/",
279
+ "be-hive/": "/node_modules/be-hive/",
280
+ "inferencer/": "/node_modules/inferencer/",
281
+ "mount-observer/": "/node_modules/mount-observer/",
282
+ "roundabout-lib/": "/node_modules/roundabout-lib/",
283
+ "id-generation/": "/node_modules/id-generation/"
284
+ }
285
+ }
286
+ </script>
287
+ ```
288
+
289
+ ## Step 8: Configure VS Code
290
+
291
+ Create `.vscode/settings.json`:
292
+
293
+ ```json
294
+ {
295
+ "explorer.fileNesting.patterns": {
296
+ "*.mjs": "${capture}.json"
297
+ },
298
+ "explorer.fileNesting.enabled": true
299
+ }
300
+ ```
301
+
302
+ ## Step 9: Set Up Auto-Build Hook
303
+
304
+ Create `.kiro/hooks/auto-build-config.kiro.hook`:
305
+
306
+ ```json
307
+ {
308
+ "name": "Auto-build Configuration",
309
+ "version": "1.0.0",
310
+ "description": "Automatically runs npm run build when emc.mjs or emoji .mjs files are saved",
311
+ "when": {
312
+ "type": "fileEdited",
313
+ "patterns": ["emc.mjs", "[emoji].mjs"]
314
+ },
315
+ "then": {
316
+ "type": "runCommand",
317
+ "command": "npm run build",
318
+ "timeout": 10000
319
+ }
320
+ }
321
+ ```
322
+
323
+ ## Step 10: Build and Verify
324
+
325
+ ```bash
326
+ npm run update # Install/update dependencies
327
+ npm run build # Generate emc.json and emoji.json
328
+ ```
329
+
330
+ Verify that `emc.json` (and `[emoji].json`) are generated with the expected structure.
331
+
332
+ ## Step 11: Create Test HTML
333
+
334
+ Create a test file (e.g., `tests/test1.html`):
335
+
336
+ ```html
337
+ <!DOCTYPE html>
338
+ <html lang="en">
339
+ <head>
340
+ <meta charset="UTF-8">
341
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
342
+ <title>Test</title>
343
+ <!-- #include virtual="/imports.html" -->
344
+ <be-hive>
345
+ <script type=emc src="[project-name]/emc.json"></script>
346
+ </be-hive>
347
+ <script type=module>
348
+ import 'be-hive/be-hive.js';
349
+ </script>
350
+ </head>
351
+ <body>
352
+ <!-- Your test markup here -->
353
+ </body>
354
+ </html>
355
+ ```
356
+
357
+ ## Architecture Overview
358
+
359
+ ```
360
+ [project-name]/
361
+ ├── .kiro/
362
+ │ └── hooks/
363
+ │ └── auto-build-config.kiro.hook
364
+ ├── .vscode/
365
+ │ └── settings.json
366
+ ├── types/ (git submodule)
367
+ │ └── [project-name]/
368
+ │ └── types.d.ts
369
+ ├── [project-name].js (enhancement class - browser code)
370
+ ├── emc.mjs (build script → emc.json)
371
+ ├── emc.json (generated - runtime config)
372
+ ├── [emoji].mjs (build script → [emoji].json)
373
+ ├── [emoji].json (generated - emoji variant config)
374
+ ├── imports.html (import map for browser)
375
+ ├── package.json
376
+ └── README.md
377
+ ```
378
+
379
+ ## Key Differences from Conversion
380
+
381
+ | Aspect | Conversion | New Enhancement |
382
+ |--------|-----------|-----------------|
383
+ | Legacy code | Move to `legacy/` folder | N/A — no legacy code |
384
+ | ts-refs | Remove and migrate to `types/` | Start directly in `types/` |
385
+ | Type cleanup | Remove IEnhancement extends, BAP | Write clean types from scratch |
386
+ | Static config | Migrate to emc.mjs customData | Write customData directly |
387
+ | Class refactor | Remove BE extends, bootUp | Write plain class from start |
388
+
389
+ ## Tips
390
+
391
+ - **Start simple**: Get the basic case working before adding complexity
392
+ - **Test incrementally**: Build one example at a time
393
+ - **Check generated JSON**: Run `node emc.mjs` to see the output
394
+ - **Use `@ts-check`**: Catches type errors early in `.js` files
395
+ - **Don't import emc.json in the class**: The config comes through `ctx.emc`
396
+
397
+ ## Parser Registration in HTML
398
+
399
+ When your enhancement uses a custom parser (see Step 4's custom parser section), you must also register the parser in your test/demo HTML files. The EMC script needs to wait for the parser to be available before processing attributes.
400
+
401
+ ```html
402
+ <be-hive>
403
+ <script type=emc-parser
404
+ src="be-hive/parsers/parse-pattern-statements.js"
405
+ parser-name=parse-pattern-statements></script>
406
+ <script type=emc
407
+ src="[project-name]/emc.json"
408
+ wait-for-parsers=parse-pattern-statements></script>
409
+ </be-hive>
410
+ <script type=module>
411
+ import 'be-hive/be-hive.js';
412
+ </script>
413
+ ```
414
+
415
+ **Key attributes:**
416
+ - `type=emc-parser` — Identifies this as a parser registration script
417
+ - `src` — Path to the parser module (e.g., `be-hive/parsers/parse-pattern-statements.js`)
418
+ - `parser-name` — Must match the `parser` string value in your `emc.mjs` `withAttrs` config
419
+ - `wait-for-parsers` — Comma-separated list of parser names to wait for before processing the EMC
420
+
421
+ If you omit the parser registration, you'll get "Parser not found" errors in the browser console.
422
+
423
+ ## Playwright Configuration
424
+
425
+ The modern architecture uses JSON imports with `with {type: 'json'}` syntax, which requires Chrome 146+. Update `playwright.config.ts` to only run Chromium tests:
426
+
427
+ ```typescript
428
+ projects: [
429
+ {
430
+ name: 'chromium',
431
+ use: { ...devices['Desktop Chrome'] },
432
+ },
433
+ // Commented out - requires Chrome 146+ features (JSON imports with type assertion)
434
+ // {
435
+ // name: 'firefox',
436
+ // use: { ...devices['Desktop Firefox'] },
437
+ // },
438
+ // {
439
+ // name: 'webkit',
440
+ // use: { ...devices['Desktop Safari'] },
441
+ // },
442
+ ],
443
+ ```
444
+
445
+ ## Using the Infer Pattern for Element Conventions
446
+
447
+ ### Overview
448
+
449
+ The modern architecture provides a standardized way to infer element properties and event types through the **`infer` pattern** from `inferencer`. This eliminates the need for hardcoded element type checks and provides a consistent, extensible approach to element conventions.
450
+
451
+ ### The Infer Function
452
+
453
+ Add this helper function at the bottom of your enhancement class file:
454
+
455
+ ```javascript
456
+ /** @import {Infer} from './types/inferencer/types' */
457
+
458
+ /**
459
+ * @param {Element & ElementEnhancementGateway} from
460
+ */
461
+ async function infer(from){
462
+ return /** @type {Infer} */ (
463
+ /** @type {any} */ (
464
+ from.enh.get((await import('inferencer/inferencer.js')).registryItem)
465
+ )
466
+ );
467
+ }
468
+ ```
469
+
470
+ ### What It Provides
471
+
472
+ The `infer` function returns an object with:
473
+ - **`eventType`**: The most appropriate event type for the element (e.g., 'click', 'input', 'change')
474
+ - **`propName`**: The most appropriate property name for the element (e.g., 'checked', 'value', 'textContent')
475
+ - **`value`**: The current value of the inferred property (getter/setter)
476
+ - **`getPropagator()`**: Returns a propagator EventTarget for observing property changes
477
+
478
+ ### Usage Examples
479
+
480
+ #### Inferring Event Type
481
+
482
+ ```javascript
483
+ // ❌ Hardcoded logic
484
+ if (localEventType === undefined) {
485
+ const tagName = enhancedElement.tagName.toLowerCase();
486
+ if(tagName === 'input' || tagName === 'textarea' || tagName === 'select'){
487
+ localEventType = 'input';
488
+ } else {
489
+ localEventType = 'click';
490
+ }
491
+ }
492
+
493
+ // ✅ Use infer
494
+ if (localEventType === undefined) {
495
+ localEventType = (await infer(enhancedElement)).eventType;
496
+ }
497
+ ```
498
+
499
+ #### Inferring Property Name
500
+
501
+ ```javascript
502
+ // ❌ Hardcoded logic
503
+ if(!propertyName){
504
+ const tagName = target.tagName.toLowerCase();
505
+ if(tagName === 'input'){
506
+ const inputType = target.getAttribute('type');
507
+ propertyName = (inputType === 'checkbox' || inputType === 'radio') ? 'checked' : 'value';
508
+ } else {
509
+ propertyName = 'textContent';
510
+ }
511
+ }
512
+
513
+ // ✅ Use infer
514
+ if(!propertyName){
515
+ propertyName = (await infer(target)).propName;
516
+ }
517
+ ```
518
+
519
+ #### Getting/Setting Inferred Property Value
520
+
521
+ ```javascript
522
+ const inference = await infer(target);
523
+ const currentValue = inference.value;
524
+ inference.value = !inference.value; // Toggle
525
+ ```
526
+
527
+ #### Inferring from Name Attribute (Empty Attribute Value)
528
+
529
+ When the attribute value is empty, infer both event type and property name:
530
+
531
+ ```javascript
532
+ if(statements.length === 0){
533
+ const name = enhancedElement.getAttribute('name');
534
+ if(name){
535
+ statements.push({
536
+ value: {
537
+ prop: name,
538
+ localEventType: (await infer(enhancedElement)).eventType,
539
+ }
540
+ });
541
+ }
542
+ }
543
+ ```
544
+
545
+ ### Benefits
546
+
547
+ 1. **Consistency**: All enhancements use the same inference logic
548
+ 2. **Extensibility**: New element types can be supported by updating inferencer, not each enhancement
549
+ 3. **Maintainability**: No duplicated element type checking code
550
+ 4. **Type Safety**: TypeScript definitions ensure correct usage
551
+ 5. **Future-proof**: Custom elements can provide their own inference hints
552
+
553
+ ## Binding Enhancement Patterns
554
+
555
+ If your enhancement needs to synchronize property values between elements (two-way data binding), the following patterns from be-bound apply.
556
+
557
+ ### The InferencedPropagator Pattern
558
+
559
+ Binding enhancements need to detect property changes on arbitrary elements — both custom elements (which may have roundabout propagators) and native elements (which don't). The `inferencer` package provides `InferencedPropagator` which handles this transparently:
560
+
561
+ ```javascript
562
+ const localInference = await infer(enhancedElement);
563
+ const localPropagator = await localInference.getPropagator();
564
+
565
+ // Works for both custom elements and built-in elements
566
+ localPropagator.addEventListener('value', () => {
567
+ // property changed
568
+ });
569
+ ```
570
+
571
+ **How `getPropagator()` works:**
572
+ - For **custom elements** with roundabout: returns the element's native `propagator` EventTarget
573
+ - For **built-in elements**: creates an `InferencedPropagator` that uses best-effort detection (native events, setter interception, attribute observation, polling fallback)
574
+
575
+ The propagator's `addEventListener` uses the **property name** as the event type (not a DOM event name).
576
+
577
+ ### contentEditable Elements
578
+
579
+ The `InferencedPropagator` supports contentEditable elements by detecting `element.isContentEditable` and listening for the native `input` event. The browser does NOT fire property setters when users type in a contentEditable element — it mutates the DOM directly. The `input` event is the only reliable signal.
580
+
581
+ ### upSearch: Resolving Remote Targets
582
+
583
+ The `inferencer/upSearch.js` function resolves remote binding targets:
584
+
585
+ ```javascript
586
+ const target = await upSearch(enhancedElement, remoteId);
587
+ ```
588
+
589
+ - If `remoteId` is truthy: calls `getRootNode().getElementById(remoteId)`
590
+ - If `remoteId` is falsy: traverses up to the nearest `[itemscope]` ancestor, or the shadow root's host
591
+
592
+ ### Path-Based Property Access
593
+
594
+ When binding to nested properties, use the `?.` path syntax to avoid conflicts with period-based statement separation:
595
+
596
+ ```html
597
+ <form 🪢="between ?.rating?.value@change and #alternativeRating.">
598
+ ```
599
+
600
+ ### Explicit Event Listening with `@` Syntax
601
+
602
+ When the inferred event isn't appropriate, allow users to specify the event by appending `@eventName`:
603
+
604
+ ```html
605
+ <form 🪢="between ?.rating?.value@change and #alternativeRating.">
606
+ ```
607
+
608
+ In `hydrate`, check for `localEvent` and use it instead of the inferred propagator when present.
609
+
610
+ ### Tie-Breaking for Initial Reconciliation
611
+
612
+ When two-way binding is first established, a `breakTie` function determines which value wins based on type specificity:
613
+
614
+ ```
615
+ object > function > symbol > bigint > number > boolean > string > null > undefined
616
+ ```
617
+
618
+ Within the same type, longer string representations win. Equal values result in no action.
619
+
620
+ ## Lessons Learned / Common Pitfalls
621
+
622
+ ### Compact/Action Conflicts
623
+
624
+ Do NOT define the same method in both `actions` and `compacts` in your `emc.mjs` `customData`:
625
+
626
+ - **Compacts** automatically call methods when properties change (e.g., `when_triggerInsertPosition_changes_call_addDeleteBtn`)
627
+ - **Actions** define when methods should be called based on property availability (e.g., `ifAllOf: ['prop1', 'prop2']`)
628
+
629
+ If a method is already invoked by a compact, adding it to `actions` will cause a "Conflict detected" error from roundabout.
630
+
631
+ ### Emoji Shorthand: Always Spread `...myJSON`
632
+
633
+ When creating emoji `.mjs` files, always spread `...myJSON` at the top level:
634
+
635
+ ```javascript
636
+ const emc = {
637
+ ...myJSON, // ← CRITICAL: carries over customData
638
+ enhConfig: {
639
+ ...myJSON.enhConfig,
640
+ enhKey: '⿻',
641
+ withAttrs: {
642
+ ...myJSON.enhConfig.withAttrs,
643
+ base: '⿻'
644
+ }
645
+ }
646
+ }
647
+ ```
648
+
649
+ Without `...myJSON`, the emoji JSON will be missing `customData` (actions, weakRef, defaultPropVals), and the enhancement will appear to load but won't respond to events.
650
+
651
+ ### Chained Accessor `?.` vs Period `.`
652
+
653
+ The parsers (`parse-pattern-statements`, `parse-grouped-capture-statements`) split statements on periods. If you need to reference properties using dot notation in attribute values, use `?.` instead:
654
+
655
+ ```html
656
+ <!-- ❌ Period conflicts with statement splitting -->
657
+ <button ⏻="[#myLight].isOn">Toggle</button>
658
+
659
+ <!-- ✅ Chained accessor avoids conflict -->
660
+ <button ⏻="[#myLight]?.isOn">Toggle</button>
661
+ ```
662
+
663
+ ### Parser Selection
664
+
665
+ | Use case | Parser |
666
+ |----------|--------|
667
+ | Flat target objects, no nesting | `parse-grouped-capture-statements` |
668
+ | Nested object structures via dot notation in capture groups | `parse-pattern-statements` |
669
+
670
+ Choose based on your data structure needs, not syntax complexity. The parser choice directly affects your type definitions.
671
+
672
+ ### Utility Import Paths
673
+
674
+ Common utilities live in `be-hive`, not `trans-render`:
675
+
676
+ ```javascript
677
+ // ✅ Correct
678
+ import { findAdjacentElement } from 'be-hive/findAdjacentElement.js';
679
+
680
+ // ❌ Wrong (legacy path)
681
+ import { findAdjacentElement } from 'trans-render/lib/findAdjacentElement.js';
682
+ ```
683
+
684
+ ### Debugging Tips
685
+
686
+ 1. **Check the generated JSON** — Run `node emc.mjs` and verify all sections (especially `customData`) are present
687
+ 2. **Console log parsedStatements** — Add `console.log({parsedStatements})` in `hydrate` to see parser output
688
+ 3. **Verify parser loading** — Check browser console for "Parser not found" errors
689
+ 4. **Test incrementally** — Get one example working before moving to the next
690
+ 5. **Compare with working examples** — Use be-clonable (architecture), do-invoke (custom parser), do-toggle (infer pattern) as references
691
+
692
+ ### Testing Strategy
693
+
694
+ Build incrementally:
695
+
696
+ 1. **Get the basic case working first** (simple property/action trigger)
697
+ 2. **Add inference** (infer from name attribute when attribute value is empty)
698
+ 3. **Add event customization** (custom event types)
699
+ 4. **Add selector/remote support** (target peer elements)
700
+
701
+ Don't try to implement all features at once.
702
+
703
+ ---
704
+
705
+ *Last updated: June 2026*
@@ -0,0 +1,2 @@
1
+ # types
2
+ Common Shared Type Definitions