@jay-framework/runtime 0.9.0 → 0.11.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.
package/dist/index.d.ts CHANGED
@@ -44,6 +44,8 @@ interface JayEvent<EventType, ViewState> {
44
44
  coordinate: Coordinate;
45
45
  }
46
46
  type JayEventHandler<EventType, ViewState, Returns> = (event: JayEvent<EventType, ViewState>) => Returns;
47
+ /** Event type for ViewState change notifications */
48
+ declare const VIEW_STATE_CHANGE_EVENT = "viewStateChange";
47
49
  interface JayComponent<Props, ViewState, jayElement extends BaseJayElement<ViewState>> {
48
50
  element: jayElement;
49
51
  update: updateFunc<Props>;
@@ -51,6 +53,8 @@ interface JayComponent<Props, ViewState, jayElement extends BaseJayElement<ViewS
51
53
  unmount: MountFunc;
52
54
  addEventListener: (type: string, handler: JayEventHandler<any, ViewState, void>) => void;
53
55
  removeEventListener: (type: string, handler: JayEventHandler<any, ViewState, void>) => void;
56
+ /** Current ViewState (read-only, available when using @jay-framework/component) */
57
+ readonly viewState?: ViewState;
54
58
  }
55
59
  type PropsFrom<Type> = Type extends JayComponent<infer Props, any, any> ? Props : null;
56
60
  type ViewStateFrom<Type> = Type extends JayComponent<any, infer ViewState, any> ? ViewState : null;
@@ -467,6 +471,25 @@ declare function isForEach<ViewState, Item>(c: Conditional<ViewState> | When<Vie
467
471
  declare function isWhen<ViewState, Item>(c: Conditional<ViewState> | When<ViewState, any> | ForEach<ViewState, Item> | WithData<ViewState, any> | TextElement<ViewState> | BaseJayElement<ViewState>): c is When<ViewState, any>;
468
472
  declare function isWithData<ViewState, ChildViewState>(c: Conditional<ViewState> | When<ViewState, any> | ForEach<ViewState, any> | WithData<ViewState, ChildViewState> | TextElement<ViewState> | BaseJayElement<ViewState>): c is WithData<ViewState, ChildViewState>;
469
473
  declare function forEach<T, Item>(getItems: (T: any) => Array<Item>, elemCreator: (Item: any) => BaseJayElement<Item>, matchBy: string): ForEach<T, Item>;
474
+ /**
475
+ * Runtime support for pre-rendered slow arrays (slowForEach in jay-html).
476
+ *
477
+ * This function is used when a forEach loop has been pre-rendered at slow phase.
478
+ * The array items are statically embedded in the jay-html, but we still need to:
479
+ * 1. Set up the correct data context for each item (so bindings work)
480
+ * 2. Enable fast/interactive updates within each item
481
+ * 3. Support event handling with the correct item coordinates
482
+ *
483
+ * The key insight is that slowForEachItem switches the data context to the
484
+ * specific array item, so child bindings like {price} work correctly
485
+ * (they resolve to item.price, not viewState.price).
486
+ *
487
+ * @param arrayName - The property name of the array in the parent ViewState
488
+ * @param index - The jayIndex value (position in the pre-rendered array)
489
+ * @param trackByValue - The jayTrackBy value (identity for reconciliation)
490
+ * @param elementCreator - Function that creates the pre-rendered element (called within item context)
491
+ */
492
+ declare function slowForEachItem<ParentVS, ItemVS>(arrayName: keyof ParentVS, index: number, trackByValue: string, elementCreator: () => BaseJayElement<ItemVS>): BaseJayElement<ParentVS>;
470
493
  interface ForEach<ViewState, Item> {
471
494
  getItems: (T: any) => Array<Item>;
472
495
  elemCreator: (Item: any, String: any) => BaseJayElement<Item>;
@@ -502,6 +525,35 @@ interface ContextStack<ContextType> {
502
525
  marker: ContextMarker<ContextType>;
503
526
  parent?: ContextStack<any>;
504
527
  }
528
+ /**
529
+ * Registers a global context that will be available to all components.
530
+ * Global contexts are checked after the context stack, so component-provided
531
+ * contexts can override global ones.
532
+ *
533
+ * @param marker - The context marker created with createJayContext()
534
+ * @param context - The context value to register
535
+ *
536
+ * @example
537
+ * ```typescript
538
+ * // In lib/init.ts (using makeJayInit pattern)
539
+ * export const init = makeJayInit()
540
+ * .withServer(() => ({ itemsPerPage: 10 }))
541
+ * .withClient((serverData) => {
542
+ * registerGlobalContext(APP_CONFIG_CONTEXT, serverData);
543
+ * });
544
+ * ```
545
+ */
546
+ declare function registerGlobalContext<ContextType>(marker: ContextMarker<ContextType>, context: ContextType): void;
547
+ /**
548
+ * Clears all registered global contexts.
549
+ * Internal API for testing and hot reload.
550
+ */
551
+ declare function clearGlobalContextRegistry(): void;
552
+ /**
553
+ * Gets a global context by marker.
554
+ * Internal API used by findContext.
555
+ */
556
+ declare function useGlobalContext<ContextType>(marker: ContextMarker<ContextType>): ContextType | undefined;
505
557
  declare function createJayContext<ContextType = unknown>(): ContextMarker<ContextType>;
506
558
  declare function withContext<ContextType, Returns>(marker: ContextMarker<ContextType>, context: ContextType, callback: () => Returns): Returns;
507
559
  declare function useContext<ContextType>(marker: ContextMarker<ContextType>): ContextType;
@@ -521,4 +573,4 @@ declare class ConstructContext<ViewState> {
521
573
  static withRootContext<ViewState, Refs>(viewState: ViewState, refManager: ReferencesManager, elementConstructor: () => BaseJayElement<ViewState>): JayElement<ViewState, Refs>;
522
574
  }
523
575
 
524
- export { type Attribute, type Attributes, type BaseJayElement, BaseReferencesManager, type ComponentCollectionProxy, ComponentCollectionRefImpl, type ComponentProxy, ComponentRefImpl, ComponentRefsImpl, type Conditional, ConstructContext, type ContextMarker, type Coordinate, type DynamicAttributeOrProperty, EVENT_TRAP, type ElementFrom, type EventEmitter, type EventTypeFrom, type ExtractFastViewState, type ExtractInteractiveViewState, type ExtractRefs, type ExtractSlowViewState, type ExtractViewState, type ForEach, GetTrapProxy, type GlobalJayEvents, type HTMLElementCollectionProxy, type HTMLElementCollectionProxyTarget, type HTMLElementProxy, type HTMLElementProxyTarget, type HTMLNativeExec, type HeadLink, type JayComponent, type JayComponentConstructor, type JayContract, type JayElement, type JayEvent, type JayEventHandler, type JayEventHandlerWrapper, type JayLog, type JayNativeEventBuilder, type JayNativeFunction, LogType, type ManagedRefConstructor, ManagedRefType, type ManagedRefs, type MapEventEmitterViewState, type MountFunc, type OnlyEventEmitters, type PreRenderElement, type PrivateRef, type PrivateRefConstructor, PrivateRefs, type PropsFrom, ReferencesManager, type RenderElement, type RenderElementOptions, type TextElement, type ViewStateFrom, type When, WhenRole, type WithData, booleanAttribute, childComp, conditional, createJayContext, currentConstructionContext, defaultEventWrapper, dynamicAttribute, dynamicElement, dynamicElementNS, dynamicProperty, dynamicText, element, findContext, forEach, injectHeadLinks, isCondition, isForEach, isWhen, isWithData, jayLog, mathMLDynamicElement, mathMLElement, mkUpdateCollection, noop, noopMount, noopUpdate, normalizeMount, normalizeUpdates, pending, rejected, resolved, restoreContext, saveContext, svgDynamicElement, svgElement, type updateFunc, useContext, withContext, withData };
576
+ export { type Attribute, type Attributes, type BaseJayElement, BaseReferencesManager, type ComponentCollectionProxy, ComponentCollectionRefImpl, type ComponentProxy, ComponentRefImpl, ComponentRefsImpl, type Conditional, ConstructContext, type ContextMarker, type Coordinate, type DynamicAttributeOrProperty, EVENT_TRAP, type ElementFrom, type EventEmitter, type EventTypeFrom, type ExtractFastViewState, type ExtractInteractiveViewState, type ExtractRefs, type ExtractSlowViewState, type ExtractViewState, type ForEach, GetTrapProxy, type GlobalJayEvents, type HTMLElementCollectionProxy, type HTMLElementCollectionProxyTarget, type HTMLElementProxy, type HTMLElementProxyTarget, type HTMLNativeExec, type HeadLink, type JayComponent, type JayComponentConstructor, type JayContract, type JayElement, type JayEvent, type JayEventHandler, type JayEventHandlerWrapper, type JayLog, type JayNativeEventBuilder, type JayNativeFunction, LogType, type ManagedRefConstructor, ManagedRefType, type ManagedRefs, type MapEventEmitterViewState, type MountFunc, type OnlyEventEmitters, type PreRenderElement, type PrivateRef, type PrivateRefConstructor, PrivateRefs, type PropsFrom, ReferencesManager, type RenderElement, type RenderElementOptions, type TextElement, VIEW_STATE_CHANGE_EVENT, type ViewStateFrom, type When, WhenRole, type WithData, booleanAttribute, childComp, clearGlobalContextRegistry, conditional, createJayContext, currentConstructionContext, defaultEventWrapper, dynamicAttribute, dynamicElement, dynamicElementNS, dynamicProperty, dynamicText, element, findContext, forEach, injectHeadLinks, isCondition, isForEach, isWhen, isWithData, jayLog, mathMLDynamicElement, mathMLElement, mkUpdateCollection, noop, noopMount, noopUpdate, normalizeMount, normalizeUpdates, pending, registerGlobalContext, rejected, resolved, restoreContext, saveContext, slowForEachItem, svgDynamicElement, svgElement, type updateFunc, useContext, useGlobalContext, withContext, withData };
package/dist/index.js CHANGED
@@ -84,6 +84,7 @@ const noop = () => {
84
84
  };
85
85
  const noopUpdate = noop;
86
86
  const noopMount = noop;
87
+ const VIEW_STATE_CHANGE_EVENT = "viewStateChange";
87
88
  var LogType = /* @__PURE__ */ ((LogType2) => {
88
89
  LogType2[LogType2["ASYNC_ERROR"] = 0] = "ASYNC_ERROR";
89
90
  LogType2[LogType2["CONTEXT_NOT_FOUND"] = 1] = "CONTEXT_NOT_FOUND";
@@ -97,6 +98,16 @@ let currentContext = void 0;
97
98
  function NewContextStack(context, marker, parent) {
98
99
  return { context, marker, parent };
99
100
  }
101
+ const globalContextRegistry = /* @__PURE__ */ new Map();
102
+ function registerGlobalContext(marker, context) {
103
+ globalContextRegistry.set(marker, context);
104
+ }
105
+ function clearGlobalContextRegistry() {
106
+ globalContextRegistry.clear();
107
+ }
108
+ function useGlobalContext(marker) {
109
+ return globalContextRegistry.get(marker);
110
+ }
100
111
  function createJayContext() {
101
112
  return Symbol();
102
113
  }
@@ -122,6 +133,11 @@ function findContext(predicate) {
122
133
  return aContext.context;
123
134
  aContext = aContext.parent;
124
135
  }
136
+ for (const [marker, context] of globalContextRegistry.entries()) {
137
+ if (predicate(marker)) {
138
+ return context;
139
+ }
140
+ }
125
141
  return void 0;
126
142
  }
127
143
  function saveContext() {
@@ -352,6 +368,33 @@ function mkWhenPendingCondition(when2, group) {
352
368
  function forEach(getItems, elemCreator, matchBy) {
353
369
  return { getItems, elemCreator, trackBy: matchBy };
354
370
  }
371
+ function slowForEachItem(arrayName, index, trackByValue, elementCreator) {
372
+ const parentContext = currentConstructionContext();
373
+ const savedContext = saveContext();
374
+ const parentData = parentContext.currData;
375
+ const array = parentData[arrayName];
376
+ const initialItem = array?.[index];
377
+ const childContext = parentContext.forItem(initialItem, trackByValue);
378
+ const element2 = withContext(CONSTRUCTION_CONTEXT_MARKER, childContext, elementCreator);
379
+ const originalUpdate = element2.update;
380
+ const update = (newParentData) => {
381
+ const newArray = newParentData[arrayName];
382
+ const newItem = newArray?.[index];
383
+ const updateChildContext = parentContext.forItem(newItem, trackByValue);
384
+ restoreContext(
385
+ savedContext,
386
+ () => withContext(CONSTRUCTION_CONTEXT_MARKER, updateChildContext, () => {
387
+ originalUpdate(newItem);
388
+ })
389
+ );
390
+ };
391
+ return {
392
+ dom: element2.dom,
393
+ update,
394
+ mount: element2.mount,
395
+ unmount: element2.unmount
396
+ };
397
+ }
355
398
  function withData(accessor, elem) {
356
399
  return { accessor, elem };
357
400
  }
@@ -980,9 +1023,11 @@ export {
980
1023
  ManagedRefType,
981
1024
  PrivateRefs,
982
1025
  ReferencesManager,
1026
+ VIEW_STATE_CHANGE_EVENT,
983
1027
  WhenRole,
984
1028
  booleanAttribute,
985
1029
  childComp,
1030
+ clearGlobalContextRegistry,
986
1031
  conditional,
987
1032
  createJayContext,
988
1033
  currentConstructionContext,
@@ -1010,13 +1055,16 @@ export {
1010
1055
  normalizeMount,
1011
1056
  normalizeUpdates,
1012
1057
  pending,
1058
+ registerGlobalContext,
1013
1059
  rejected,
1014
1060
  resolved,
1015
1061
  restoreContext,
1016
1062
  saveContext,
1063
+ slowForEachItem,
1017
1064
  svgDynamicElement,
1018
1065
  svgElement,
1019
1066
  useContext,
1067
+ useGlobalContext,
1020
1068
  withContext,
1021
1069
  withData
1022
1070
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jay-framework/runtime",
3
- "version": "0.9.0",
3
+ "version": "0.11.0",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "main": "dist/index.js",
@@ -23,11 +23,11 @@
23
23
  "test:watch": "vitest"
24
24
  },
25
25
  "dependencies": {
26
- "@jay-framework/list-compare": "^0.9.0",
27
- "@jay-framework/reactive": "^0.9.0"
26
+ "@jay-framework/list-compare": "^0.11.0",
27
+ "@jay-framework/reactive": "^0.11.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@jay-framework/dev-environment": "^0.9.0",
30
+ "@jay-framework/dev-environment": "^0.11.0",
31
31
  "@testing-library/jest-dom": "^6.2.0",
32
32
  "@types/jsdom": "^21.1.6",
33
33
  "@types/node": "^20.11.5",
package/readme.md CHANGED
@@ -105,7 +105,57 @@ interface JayElement<ViewState, Refs> extends BaseJayElement<ViewState> {
105
105
  These references can be used to set event listeners, interact with child elements or component APIs.
106
106
  Read more about `refs` in [refs.md](./docs/refs.md).
107
107
 
108
- ## implementation details
108
+ ## Slow Rendering Support
109
+
110
+ The runtime supports pre-rendered "slow" arrays via the `slowForEachItem` function. This is used when a `forEach` loop has been pre-rendered at build time (slow phase), but still needs fast/interactive updates at runtime.
111
+
112
+ ### slowForEachItem
113
+
114
+ ```typescript
115
+ function slowForEachItem<ParentVS, ItemVS>(
116
+ arrayName: keyof ParentVS,
117
+ index: number,
118
+ trackByValue: string,
119
+ elementCreator: () => BaseJayElement<ItemVS>,
120
+ ): BaseJayElement<ParentVS>;
121
+ ```
122
+
123
+ **Parameters:**
124
+
125
+ - `arrayName` - The property name of the array in the parent ViewState
126
+ - `index` - The position in the pre-rendered array (jayIndex)
127
+ - `trackByValue` - The track-by value for client reconciliation (jayTrackBy)
128
+ - `elementCreator` - Function that creates the element (called within item context)
129
+
130
+ **How it works:**
131
+
132
+ 1. Sets the data context to `viewState[arrayName][index]`
133
+ 2. Calls `elementCreator()` within that context so bindings are item-scoped
134
+ 3. On update, retrieves the item from the new ViewState and updates with item context
135
+
136
+ **Example generated code:**
137
+
138
+ ```typescript
139
+ slowForEachItem('products', 0, 'p1', () =>
140
+ e('li', {}, [
141
+ e('span', { class: 'name' }, ['Widget A']), // static (pre-rendered)
142
+ e('span', { class: 'price' }, [
143
+ dt((vs) => vs.price), // dynamic (fast phase) - item-scoped
144
+ ]),
145
+ ]),
146
+ );
147
+ ```
148
+
149
+ This corresponds to pre-rendered jay-html:
150
+
151
+ ```html
152
+ <li slowForEach="products" trackBy="id" jayIndex="0" jayTrackBy="p1">
153
+ <span class="name">Widget A</span>
154
+ <span class="price">{price}</span>
155
+ </li>
156
+ ```
157
+
158
+ ## Implementation Details
109
159
 
110
160
  - See the [Generated JayElement](./docs/jay-element.md) for the details of the `jay-html` generated target.
111
161
  - See the [Generated JayElement creation Functions](./docs/runtime.md) for the details of the jay element creation functions used by the `jay-html` generated target.