@jay-framework/stack-client-runtime 0.10.0 → 0.12.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.cjs +314 -24
- package/dist/index.d.ts +50 -3
- package/dist/index.js +316 -26
- package/package.json +8 -7
package/dist/index.cjs
CHANGED
|
@@ -7,7 +7,8 @@ var __publicField = (obj, key, value) => {
|
|
|
7
7
|
};
|
|
8
8
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
9
9
|
const component = require("@jay-framework/component");
|
|
10
|
-
|
|
10
|
+
const runtime = require("@jay-framework/runtime");
|
|
11
|
+
function deepMergeViewStates$1(base, overlay, trackByMap, path = "") {
|
|
11
12
|
if (!base && !overlay)
|
|
12
13
|
return {};
|
|
13
14
|
if (!base)
|
|
@@ -27,7 +28,7 @@ function deepMergeViewStates(base, overlay, trackByMap, path = "") {
|
|
|
27
28
|
} else if (Array.isArray(baseValue) && Array.isArray(overlayValue)) {
|
|
28
29
|
const trackByField = trackByMap[currentPath];
|
|
29
30
|
if (trackByField) {
|
|
30
|
-
result[key] = mergeArraysByTrackBy(
|
|
31
|
+
result[key] = mergeArraysByTrackBy$1(
|
|
31
32
|
baseValue,
|
|
32
33
|
overlayValue,
|
|
33
34
|
trackByField,
|
|
@@ -38,14 +39,14 @@ function deepMergeViewStates(base, overlay, trackByMap, path = "") {
|
|
|
38
39
|
result[key] = overlayValue;
|
|
39
40
|
}
|
|
40
41
|
} else if (typeof baseValue === "object" && baseValue !== null && typeof overlayValue === "object" && overlayValue !== null && !Array.isArray(baseValue) && !Array.isArray(overlayValue)) {
|
|
41
|
-
result[key] = deepMergeViewStates(baseValue, overlayValue, trackByMap, currentPath);
|
|
42
|
+
result[key] = deepMergeViewStates$1(baseValue, overlayValue, trackByMap, currentPath);
|
|
42
43
|
} else {
|
|
43
44
|
result[key] = overlayValue;
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
return result;
|
|
47
48
|
}
|
|
48
|
-
function mergeArraysByTrackBy(baseArray, overlayArray, trackByField, trackByMap, arrayPath) {
|
|
49
|
+
function mergeArraysByTrackBy$1(baseArray, overlayArray, trackByField, trackByMap, arrayPath) {
|
|
49
50
|
const baseByKey = /* @__PURE__ */ new Map();
|
|
50
51
|
for (const item of baseArray) {
|
|
51
52
|
const key = item[trackByField];
|
|
@@ -72,12 +73,29 @@ function mergeArraysByTrackBy(baseArray, overlayArray, trackByField, trackByMap,
|
|
|
72
73
|
}
|
|
73
74
|
const overlayItem = overlayByKey.get(key);
|
|
74
75
|
if (overlayItem) {
|
|
75
|
-
return deepMergeViewStates(baseItem, overlayItem, trackByMap, arrayPath);
|
|
76
|
+
return deepMergeViewStates$1(baseItem, overlayItem, trackByMap, arrayPath);
|
|
76
77
|
} else {
|
|
77
78
|
return baseItem;
|
|
78
79
|
}
|
|
79
80
|
});
|
|
80
81
|
}
|
|
82
|
+
const HEADLESS_INSTANCES = runtime.createJayContext();
|
|
83
|
+
function makeSignals$1(obj) {
|
|
84
|
+
return Object.keys(obj).reduce((signals, key) => {
|
|
85
|
+
signals[key] = component.createSignal(obj[key]);
|
|
86
|
+
return signals;
|
|
87
|
+
}, {});
|
|
88
|
+
}
|
|
89
|
+
function makeHeadlessInstanceComponent(preRender, interactiveConstructor, coordinateKey, pluginContexts = []) {
|
|
90
|
+
const wrappedConstructor = (props, refs, ...pluginResolvedContexts) => {
|
|
91
|
+
const instanceData = runtime.useContext(HEADLESS_INSTANCES);
|
|
92
|
+
const fastVS = instanceData?.viewStates?.[coordinateKey];
|
|
93
|
+
const cf = instanceData?.carryForwards?.[coordinateKey] || {};
|
|
94
|
+
const signalVS = fastVS ? makeSignals$1(fastVS) : void 0;
|
|
95
|
+
return interactiveConstructor(props, refs, signalVS, cf, ...pluginResolvedContexts);
|
|
96
|
+
};
|
|
97
|
+
return component.makeJayComponent(preRender, wrappedConstructor, ...pluginContexts);
|
|
98
|
+
}
|
|
81
99
|
function makeSignals(obj) {
|
|
82
100
|
return Object.keys(obj).reduce((signals, key) => {
|
|
83
101
|
signals[key] = component.createSignal(obj[key]);
|
|
@@ -85,39 +103,56 @@ function makeSignals(obj) {
|
|
|
85
103
|
}, {});
|
|
86
104
|
}
|
|
87
105
|
function makeCompositeJayComponent(preRender, defaultViewState, fastCarryForward, parts, trackByMap = {}) {
|
|
106
|
+
const interactiveParts = parts.filter((part) => part.comp !== void 0);
|
|
88
107
|
const hasFastRendering = defaultViewState !== null && defaultViewState !== void 0;
|
|
108
|
+
const headlessInstanceViewStates = defaultViewState?.__headlessInstances;
|
|
109
|
+
const headlessInstanceCarryForwards = fastCarryForward?.__headlessInstances;
|
|
110
|
+
if (headlessInstanceViewStates)
|
|
111
|
+
delete defaultViewState.__headlessInstances;
|
|
112
|
+
if (headlessInstanceCarryForwards)
|
|
113
|
+
delete fastCarryForward.__headlessInstances;
|
|
89
114
|
const comp = (props, refs, ...contexts) => {
|
|
90
|
-
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
115
|
+
if (headlessInstanceViewStates || headlessInstanceCarryForwards) {
|
|
116
|
+
const componentContext = runtime.useContext(component.COMPONENT_CONTEXT);
|
|
117
|
+
const instancesData = {
|
|
118
|
+
viewStates: headlessInstanceViewStates || {},
|
|
119
|
+
carryForwards: headlessInstanceCarryForwards || {}
|
|
120
|
+
};
|
|
121
|
+
componentContext.provideContexts.push([HEADLESS_INSTANCES, instancesData]);
|
|
122
|
+
}
|
|
123
|
+
const instances = interactiveParts.map(
|
|
124
|
+
(part) => {
|
|
125
|
+
const partRefs = part.key ? refs[part.key] : refs;
|
|
126
|
+
let partContexts;
|
|
127
|
+
if (hasFastRendering) {
|
|
128
|
+
const partViewState = part.key ? defaultViewState?.[part.key] : defaultViewState;
|
|
129
|
+
const partFastViewState = partViewState ? makeSignals(partViewState) : void 0;
|
|
130
|
+
const partCarryForward = part.key ? fastCarryForward?.[part.key] : fastCarryForward;
|
|
131
|
+
partContexts = [
|
|
132
|
+
partFastViewState,
|
|
133
|
+
partCarryForward,
|
|
134
|
+
...contexts.splice(0, part.contextMarkers.length)
|
|
135
|
+
];
|
|
136
|
+
} else {
|
|
137
|
+
partContexts = [...contexts.splice(0, part.contextMarkers.length)];
|
|
138
|
+
}
|
|
139
|
+
return [part.key, part.comp(props, partRefs, ...partContexts)];
|
|
104
140
|
}
|
|
105
|
-
|
|
106
|
-
});
|
|
141
|
+
);
|
|
107
142
|
return {
|
|
108
143
|
render: () => {
|
|
109
144
|
let viewState = defaultViewState;
|
|
110
145
|
instances.forEach(([key, instance]) => {
|
|
111
146
|
const rendered = component.materializeViewState(instance.render());
|
|
112
147
|
if (key) {
|
|
113
|
-
viewState[key] = deepMergeViewStates(
|
|
148
|
+
viewState[key] = deepMergeViewStates$1(
|
|
114
149
|
defaultViewState[key],
|
|
115
150
|
rendered,
|
|
116
151
|
trackByMap,
|
|
117
152
|
key
|
|
118
153
|
);
|
|
119
154
|
} else {
|
|
120
|
-
viewState = deepMergeViewStates(
|
|
155
|
+
viewState = deepMergeViewStates$1(
|
|
121
156
|
viewState,
|
|
122
157
|
rendered,
|
|
123
158
|
trackByMap
|
|
@@ -128,7 +163,7 @@ function makeCompositeJayComponent(preRender, defaultViewState, fastCarryForward
|
|
|
128
163
|
}
|
|
129
164
|
};
|
|
130
165
|
};
|
|
131
|
-
const contextMarkers =
|
|
166
|
+
const contextMarkers = interactiveParts.reduce((cm, part) => {
|
|
132
167
|
return [...cm, ...part.contextMarkers];
|
|
133
168
|
}, []);
|
|
134
169
|
return component.makeJayComponent(
|
|
@@ -230,7 +265,262 @@ function isSimpleObject(obj) {
|
|
|
230
265
|
}
|
|
231
266
|
return true;
|
|
232
267
|
}
|
|
268
|
+
var __defProp2 = Object.defineProperty;
|
|
269
|
+
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
270
|
+
var __publicField2 = (obj, key, value) => {
|
|
271
|
+
__defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
272
|
+
return value;
|
|
273
|
+
};
|
|
274
|
+
function deepMergeViewStates(base, overlay, trackByMap, path = "") {
|
|
275
|
+
if (!base && !overlay)
|
|
276
|
+
return {};
|
|
277
|
+
if (!base)
|
|
278
|
+
return overlay || {};
|
|
279
|
+
if (!overlay)
|
|
280
|
+
return base || {};
|
|
281
|
+
const result = {};
|
|
282
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(overlay)]);
|
|
283
|
+
for (const key of allKeys) {
|
|
284
|
+
const baseValue = base[key];
|
|
285
|
+
const overlayValue = overlay[key];
|
|
286
|
+
const currentPath = path ? `${path}.${key}` : key;
|
|
287
|
+
if (overlayValue === void 0) {
|
|
288
|
+
result[key] = baseValue;
|
|
289
|
+
} else if (baseValue === void 0) {
|
|
290
|
+
result[key] = overlayValue;
|
|
291
|
+
} else if (Array.isArray(baseValue) && Array.isArray(overlayValue)) {
|
|
292
|
+
const trackByField = trackByMap[currentPath];
|
|
293
|
+
if (trackByField) {
|
|
294
|
+
result[key] = mergeArraysByTrackBy(
|
|
295
|
+
baseValue,
|
|
296
|
+
overlayValue,
|
|
297
|
+
trackByField,
|
|
298
|
+
trackByMap,
|
|
299
|
+
currentPath
|
|
300
|
+
);
|
|
301
|
+
} else {
|
|
302
|
+
result[key] = overlayValue;
|
|
303
|
+
}
|
|
304
|
+
} else if (typeof baseValue === "object" && baseValue !== null && typeof overlayValue === "object" && overlayValue !== null && !Array.isArray(baseValue) && !Array.isArray(overlayValue)) {
|
|
305
|
+
result[key] = deepMergeViewStates(baseValue, overlayValue, trackByMap, currentPath);
|
|
306
|
+
} else {
|
|
307
|
+
result[key] = overlayValue;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return result;
|
|
311
|
+
}
|
|
312
|
+
function mergeArraysByTrackBy(baseArray, overlayArray, trackByField, trackByMap, arrayPath) {
|
|
313
|
+
const baseByKey = /* @__PURE__ */ new Map();
|
|
314
|
+
for (const item of baseArray) {
|
|
315
|
+
const key = item[trackByField];
|
|
316
|
+
if (key !== void 0 && key !== null) {
|
|
317
|
+
if (baseByKey.has(key)) {
|
|
318
|
+
console.warn(
|
|
319
|
+
`Duplicate trackBy key [${key}] in base array at path [${arrayPath}]. This may cause incorrect merging.`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
baseByKey.set(key, item);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
const overlayByKey = /* @__PURE__ */ new Map();
|
|
326
|
+
for (const item of overlayArray) {
|
|
327
|
+
const key = item[trackByField];
|
|
328
|
+
if (key !== void 0 && key !== null) {
|
|
329
|
+
overlayByKey.set(key, item);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return baseArray.map((baseItem) => {
|
|
333
|
+
const key = baseItem[trackByField];
|
|
334
|
+
if (key === void 0 || key === null) {
|
|
335
|
+
return baseItem;
|
|
336
|
+
}
|
|
337
|
+
const overlayItem = overlayByKey.get(key);
|
|
338
|
+
if (overlayItem) {
|
|
339
|
+
return deepMergeViewStates(baseItem, overlayItem, trackByMap, arrayPath);
|
|
340
|
+
} else {
|
|
341
|
+
return baseItem;
|
|
342
|
+
}
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
function collectInteractions(refs) {
|
|
346
|
+
const interactions = [];
|
|
347
|
+
if (!refs)
|
|
348
|
+
return interactions;
|
|
349
|
+
collectInteractionsRecursive(refs, interactions);
|
|
350
|
+
return interactions;
|
|
351
|
+
}
|
|
352
|
+
function collectInteractionsRecursive(refs, interactions) {
|
|
353
|
+
if (!refs)
|
|
354
|
+
return;
|
|
355
|
+
for (const [refName, refImpl] of Object.entries(refs)) {
|
|
356
|
+
if (!refImpl)
|
|
357
|
+
continue;
|
|
358
|
+
if (refImpl.elements && refImpl.elements instanceof Set) {
|
|
359
|
+
for (const elem of refImpl.elements) {
|
|
360
|
+
if (elem.element) {
|
|
361
|
+
interactions.push({
|
|
362
|
+
refName,
|
|
363
|
+
coordinate: elem.coordinate || [refName],
|
|
364
|
+
element: elem.element,
|
|
365
|
+
elementType: getElementType(elem.element),
|
|
366
|
+
supportedEvents: getSupportedEvents(elem.element),
|
|
367
|
+
itemContext: elem.viewState
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
} else if (isNestedRefsObject(refImpl)) {
|
|
372
|
+
collectInteractionsRecursive(refImpl, interactions);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function isNestedRefsObject(obj) {
|
|
377
|
+
if (!obj || typeof obj !== "object")
|
|
378
|
+
return false;
|
|
379
|
+
if (obj.elements instanceof Set)
|
|
380
|
+
return false;
|
|
381
|
+
const proto = Object.getPrototypeOf(obj);
|
|
382
|
+
if (proto !== Object.prototype && proto !== null)
|
|
383
|
+
return false;
|
|
384
|
+
return true;
|
|
385
|
+
}
|
|
386
|
+
function getElementType(element) {
|
|
387
|
+
return element.constructor.name;
|
|
388
|
+
}
|
|
389
|
+
function getSupportedEvents(element) {
|
|
390
|
+
const base = ["click", "focus", "blur"];
|
|
391
|
+
if (element instanceof HTMLInputElement) {
|
|
392
|
+
return [...base, "input", "change"];
|
|
393
|
+
}
|
|
394
|
+
if (element instanceof HTMLButtonElement) {
|
|
395
|
+
return ["click", "focus", "blur"];
|
|
396
|
+
}
|
|
397
|
+
if (element instanceof HTMLSelectElement) {
|
|
398
|
+
return [...base, "change"];
|
|
399
|
+
}
|
|
400
|
+
if (element instanceof HTMLTextAreaElement) {
|
|
401
|
+
return [...base, "input", "change"];
|
|
402
|
+
}
|
|
403
|
+
if (element instanceof HTMLAnchorElement) {
|
|
404
|
+
return ["click"];
|
|
405
|
+
}
|
|
406
|
+
if (element instanceof HTMLFormElement) {
|
|
407
|
+
return ["submit", "reset"];
|
|
408
|
+
}
|
|
409
|
+
return base;
|
|
410
|
+
}
|
|
411
|
+
const VIEW_STATE_CHANGE = "viewStateChange";
|
|
412
|
+
class AutomationAgent {
|
|
413
|
+
constructor(component2, options) {
|
|
414
|
+
__publicField2(this, "stateListeners", /* @__PURE__ */ new Set());
|
|
415
|
+
__publicField2(this, "cachedInteractions", null);
|
|
416
|
+
__publicField2(this, "viewStateHandler", null);
|
|
417
|
+
__publicField2(this, "mergedViewState");
|
|
418
|
+
__publicField2(this, "initialSlowViewState");
|
|
419
|
+
__publicField2(this, "trackByMap");
|
|
420
|
+
this.component = component2;
|
|
421
|
+
if (options) {
|
|
422
|
+
this.initialSlowViewState = options.initialViewState;
|
|
423
|
+
this.trackByMap = options.trackByMap;
|
|
424
|
+
this.mergedViewState = deepMergeViewStates(
|
|
425
|
+
options.initialViewState,
|
|
426
|
+
this.component.viewState || {},
|
|
427
|
+
options.trackByMap
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
this.subscribeToUpdates();
|
|
431
|
+
}
|
|
432
|
+
subscribeToUpdates() {
|
|
433
|
+
this.viewStateHandler = () => {
|
|
434
|
+
this.cachedInteractions = null;
|
|
435
|
+
if (this.initialSlowViewState && this.trackByMap) {
|
|
436
|
+
this.mergedViewState = deepMergeViewStates(
|
|
437
|
+
this.initialSlowViewState,
|
|
438
|
+
this.component.viewState || {},
|
|
439
|
+
this.trackByMap
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
this.notifyListeners();
|
|
443
|
+
};
|
|
444
|
+
this.component.addEventListener(VIEW_STATE_CHANGE, this.viewStateHandler);
|
|
445
|
+
}
|
|
446
|
+
notifyListeners() {
|
|
447
|
+
if (this.stateListeners.size === 0)
|
|
448
|
+
return;
|
|
449
|
+
const state = this.getPageState();
|
|
450
|
+
this.stateListeners.forEach((callback) => callback(state));
|
|
451
|
+
}
|
|
452
|
+
getPageState() {
|
|
453
|
+
if (!this.cachedInteractions) {
|
|
454
|
+
this.cachedInteractions = collectInteractions(this.component.element?.refs);
|
|
455
|
+
}
|
|
456
|
+
return {
|
|
457
|
+
// Use merged state if available (slow+fast), otherwise component's viewState
|
|
458
|
+
viewState: this.mergedViewState || this.component.viewState,
|
|
459
|
+
interactions: this.cachedInteractions,
|
|
460
|
+
customEvents: this.getCustomEvents()
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
triggerEvent(eventType, coordinate, eventData) {
|
|
464
|
+
const interaction = this.getInteraction(coordinate);
|
|
465
|
+
if (!interaction) {
|
|
466
|
+
throw new Error(`No element found at coordinate: ${coordinate.join("/")}`);
|
|
467
|
+
}
|
|
468
|
+
const event = new Event(eventType, { bubbles: true });
|
|
469
|
+
if (eventData) {
|
|
470
|
+
Object.assign(event, eventData);
|
|
471
|
+
}
|
|
472
|
+
interaction.element.dispatchEvent(event);
|
|
473
|
+
}
|
|
474
|
+
getInteraction(coordinate) {
|
|
475
|
+
const state = this.getPageState();
|
|
476
|
+
return state.interactions.find(
|
|
477
|
+
(i) => i.coordinate.length === coordinate.length && i.coordinate.every((c, idx) => c === coordinate[idx])
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
onStateChange(callback) {
|
|
481
|
+
this.stateListeners.add(callback);
|
|
482
|
+
return () => this.stateListeners.delete(callback);
|
|
483
|
+
}
|
|
484
|
+
getCustomEvents() {
|
|
485
|
+
const events = [];
|
|
486
|
+
const component2 = this.component;
|
|
487
|
+
for (const key in component2) {
|
|
488
|
+
if (component2[key]?.emit && typeof component2[key].emit === "function") {
|
|
489
|
+
const name = key.startsWith("on") ? key.slice(2) : key;
|
|
490
|
+
events.push({ name });
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return events;
|
|
494
|
+
}
|
|
495
|
+
onComponentEvent(eventName, callback) {
|
|
496
|
+
const component2 = this.component;
|
|
497
|
+
const handlerKey = eventName.startsWith("on") ? eventName : `on${eventName}`;
|
|
498
|
+
const handler = component2[handlerKey];
|
|
499
|
+
if (!handler || typeof handler !== "function") {
|
|
500
|
+
throw new Error(`Unknown component event: ${eventName}`);
|
|
501
|
+
}
|
|
502
|
+
handler(({ event }) => callback(event));
|
|
503
|
+
return () => handler(void 0);
|
|
504
|
+
}
|
|
505
|
+
dispose() {
|
|
506
|
+
if (this.viewStateHandler) {
|
|
507
|
+
this.component.removeEventListener(VIEW_STATE_CHANGE, this.viewStateHandler);
|
|
508
|
+
this.viewStateHandler = null;
|
|
509
|
+
}
|
|
510
|
+
this.stateListeners.clear();
|
|
511
|
+
this.cachedInteractions = null;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
function wrapWithAutomation(component2, options) {
|
|
515
|
+
const agent = new AutomationAgent(component2, options);
|
|
516
|
+
return Object.assign(component2, { automation: agent });
|
|
517
|
+
}
|
|
518
|
+
const AUTOMATION_CONTEXT = runtime.createJayContext();
|
|
519
|
+
exports.AUTOMATION_CONTEXT = AUTOMATION_CONTEXT;
|
|
233
520
|
exports.ActionError = ActionError;
|
|
521
|
+
exports.HEADLESS_INSTANCES = HEADLESS_INSTANCES;
|
|
234
522
|
exports.createActionCaller = createActionCaller;
|
|
235
523
|
exports.makeCompositeJayComponent = makeCompositeJayComponent;
|
|
524
|
+
exports.makeHeadlessInstanceComponent = makeHeadlessInstanceComponent;
|
|
236
525
|
exports.setActionCallerOptions = setActionCallerOptions;
|
|
526
|
+
exports.wrapWithAutomation = wrapWithAutomation;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,16 +1,63 @@
|
|
|
1
1
|
import * as _jay_framework_component from '@jay-framework/component';
|
|
2
2
|
import { ComponentConstructor, ContextMarkers, JayComponentCore } from '@jay-framework/component';
|
|
3
|
-
import { JayElement, PreRenderElement } from '@jay-framework/runtime';
|
|
3
|
+
import { JayElement, PreRenderElement, ContextMarker } from '@jay-framework/runtime';
|
|
4
4
|
import { TrackByMap } from '@jay-framework/view-state-merge';
|
|
5
|
+
export { AUTOMATION_CONTEXT, AutomationAPI, AutomationWrappedComponent, Coordinate, Interaction, PageState, wrapWithAutomation } from '@jay-framework/runtime-automation';
|
|
5
6
|
|
|
6
7
|
interface CompositePart {
|
|
7
|
-
|
|
8
|
+
/**
|
|
9
|
+
* The interactive component constructor.
|
|
10
|
+
* May be undefined if the component has no interactive phase (only slow/fast phases).
|
|
11
|
+
* See Design Log #72.
|
|
12
|
+
*/
|
|
13
|
+
comp?: ComponentConstructor<any, any, any, any, any>;
|
|
8
14
|
contextMarkers: ContextMarkers<any>;
|
|
9
15
|
key?: string;
|
|
10
16
|
}
|
|
11
17
|
|
|
12
18
|
declare function makeCompositeJayComponent<PropsT extends object, ViewState extends object, Refs extends object, JayElementT extends JayElement<ViewState, Refs>, CompCore extends JayComponentCore<PropsT, ViewState>>(preRender: PreRenderElement<ViewState, Refs, JayElementT>, defaultViewState: ViewState, fastCarryForward: object, parts: Array<CompositePart>, trackByMap?: TrackByMap): (props: PropsT) => _jay_framework_component.ConcreteJayComponent<PropsT, ViewState, Refs, CompCore, JayElementT>;
|
|
13
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Client-side context and helpers for headless component instances.
|
|
22
|
+
*
|
|
23
|
+
* Provides the mechanism to deliver server-produced fast ViewState and carryForward
|
|
24
|
+
* to headless component instances on the client.
|
|
25
|
+
*
|
|
26
|
+
* Flow:
|
|
27
|
+
* 1. makeCompositeJayComponent extracts __headlessInstances from ViewState/carryForward
|
|
28
|
+
* 2. Registers HEADLESS_INSTANCES context during component construction
|
|
29
|
+
* 3. makeHeadlessInstanceComponent creates instance components that resolve their data
|
|
30
|
+
* from this context by coordinate key
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Data structure for headless instance ViewStates and carryForwards.
|
|
35
|
+
* Keyed by coordinate path (e.g., "product-card:0", "p1/product-card:0").
|
|
36
|
+
*/
|
|
37
|
+
interface HeadlessInstancesData {
|
|
38
|
+
viewStates: Record<string, object>;
|
|
39
|
+
carryForwards: Record<string, object>;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Context marker for headless instance data.
|
|
43
|
+
* Provided by makeCompositeJayComponent, consumed by makeHeadlessInstanceComponent.
|
|
44
|
+
*/
|
|
45
|
+
declare const HEADLESS_INSTANCES: ContextMarker<HeadlessInstancesData>;
|
|
46
|
+
/**
|
|
47
|
+
* Create a headless instance component that receives its fast ViewState from the
|
|
48
|
+
* HEADLESS_INSTANCES context, matched by coordinate key.
|
|
49
|
+
*
|
|
50
|
+
* This replaces makeJayComponent for headless instances. It wraps the plugin's
|
|
51
|
+
* interactive constructor to inject the instance's fast ViewState signals and
|
|
52
|
+
* carryForward before any plugin-defined context markers.
|
|
53
|
+
*
|
|
54
|
+
* @param preRender - The inline template's render function
|
|
55
|
+
* @param interactiveConstructor - The plugin's interactive constructor
|
|
56
|
+
* @param coordinateKey - The coordinate key for this instance (e.g., "product-card:0")
|
|
57
|
+
* @param pluginContexts - Additional context markers from the plugin (if any)
|
|
58
|
+
*/
|
|
59
|
+
declare function makeHeadlessInstanceComponent<PropsT extends object, ViewState extends object, Refs extends object, JayElementT extends JayElement<ViewState, Refs>, CompCore extends JayComponentCore<PropsT, ViewState>>(preRender: PreRenderElement<ViewState, Refs, JayElementT>, interactiveConstructor: ComponentConstructor<PropsT, Refs, ViewState, any, CompCore>, coordinateKey: string, pluginContexts?: ContextMarkers<any>): any;
|
|
60
|
+
|
|
14
61
|
/**
|
|
15
62
|
* Client-side action caller for Jay Stack.
|
|
16
63
|
*
|
|
@@ -76,4 +123,4 @@ declare function setActionCallerOptions(options: ActionCallerOptions): void;
|
|
|
76
123
|
*/
|
|
77
124
|
declare function createActionCaller<Input, Output>(actionName: string, method?: HttpMethod): (input: Input) => Promise<Output>;
|
|
78
125
|
|
|
79
|
-
export { type ActionCallerOptions, ActionError, type CompositePart, type HttpMethod, createActionCaller, makeCompositeJayComponent, setActionCallerOptions };
|
|
126
|
+
export { type ActionCallerOptions, ActionError, type CompositePart, HEADLESS_INSTANCES, type HeadlessInstancesData, type HttpMethod, createActionCaller, makeCompositeJayComponent, makeHeadlessInstanceComponent, setActionCallerOptions };
|
package/dist/index.js
CHANGED
|
@@ -4,8 +4,9 @@ var __publicField = (obj, key, value) => {
|
|
|
4
4
|
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
5
|
return value;
|
|
6
6
|
};
|
|
7
|
-
import { makeJayComponent,
|
|
8
|
-
|
|
7
|
+
import { makeJayComponent, createSignal, COMPONENT_CONTEXT, materializeViewState } from "@jay-framework/component";
|
|
8
|
+
import { createJayContext, useContext } from "@jay-framework/runtime";
|
|
9
|
+
function deepMergeViewStates$1(base, overlay, trackByMap, path = "") {
|
|
9
10
|
if (!base && !overlay)
|
|
10
11
|
return {};
|
|
11
12
|
if (!base)
|
|
@@ -25,7 +26,7 @@ function deepMergeViewStates(base, overlay, trackByMap, path = "") {
|
|
|
25
26
|
} else if (Array.isArray(baseValue) && Array.isArray(overlayValue)) {
|
|
26
27
|
const trackByField = trackByMap[currentPath];
|
|
27
28
|
if (trackByField) {
|
|
28
|
-
result[key] = mergeArraysByTrackBy(
|
|
29
|
+
result[key] = mergeArraysByTrackBy$1(
|
|
29
30
|
baseValue,
|
|
30
31
|
overlayValue,
|
|
31
32
|
trackByField,
|
|
@@ -36,14 +37,14 @@ function deepMergeViewStates(base, overlay, trackByMap, path = "") {
|
|
|
36
37
|
result[key] = overlayValue;
|
|
37
38
|
}
|
|
38
39
|
} else if (typeof baseValue === "object" && baseValue !== null && typeof overlayValue === "object" && overlayValue !== null && !Array.isArray(baseValue) && !Array.isArray(overlayValue)) {
|
|
39
|
-
result[key] = deepMergeViewStates(baseValue, overlayValue, trackByMap, currentPath);
|
|
40
|
+
result[key] = deepMergeViewStates$1(baseValue, overlayValue, trackByMap, currentPath);
|
|
40
41
|
} else {
|
|
41
42
|
result[key] = overlayValue;
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
return result;
|
|
45
46
|
}
|
|
46
|
-
function mergeArraysByTrackBy(baseArray, overlayArray, trackByField, trackByMap, arrayPath) {
|
|
47
|
+
function mergeArraysByTrackBy$1(baseArray, overlayArray, trackByField, trackByMap, arrayPath) {
|
|
47
48
|
const baseByKey = /* @__PURE__ */ new Map();
|
|
48
49
|
for (const item of baseArray) {
|
|
49
50
|
const key = item[trackByField];
|
|
@@ -70,12 +71,29 @@ function mergeArraysByTrackBy(baseArray, overlayArray, trackByField, trackByMap,
|
|
|
70
71
|
}
|
|
71
72
|
const overlayItem = overlayByKey.get(key);
|
|
72
73
|
if (overlayItem) {
|
|
73
|
-
return deepMergeViewStates(baseItem, overlayItem, trackByMap, arrayPath);
|
|
74
|
+
return deepMergeViewStates$1(baseItem, overlayItem, trackByMap, arrayPath);
|
|
74
75
|
} else {
|
|
75
76
|
return baseItem;
|
|
76
77
|
}
|
|
77
78
|
});
|
|
78
79
|
}
|
|
80
|
+
const HEADLESS_INSTANCES = createJayContext();
|
|
81
|
+
function makeSignals$1(obj) {
|
|
82
|
+
return Object.keys(obj).reduce((signals, key) => {
|
|
83
|
+
signals[key] = createSignal(obj[key]);
|
|
84
|
+
return signals;
|
|
85
|
+
}, {});
|
|
86
|
+
}
|
|
87
|
+
function makeHeadlessInstanceComponent(preRender, interactiveConstructor, coordinateKey, pluginContexts = []) {
|
|
88
|
+
const wrappedConstructor = (props, refs, ...pluginResolvedContexts) => {
|
|
89
|
+
const instanceData = useContext(HEADLESS_INSTANCES);
|
|
90
|
+
const fastVS = instanceData?.viewStates?.[coordinateKey];
|
|
91
|
+
const cf = instanceData?.carryForwards?.[coordinateKey] || {};
|
|
92
|
+
const signalVS = fastVS ? makeSignals$1(fastVS) : void 0;
|
|
93
|
+
return interactiveConstructor(props, refs, signalVS, cf, ...pluginResolvedContexts);
|
|
94
|
+
};
|
|
95
|
+
return makeJayComponent(preRender, wrappedConstructor, ...pluginContexts);
|
|
96
|
+
}
|
|
79
97
|
function makeSignals(obj) {
|
|
80
98
|
return Object.keys(obj).reduce((signals, key) => {
|
|
81
99
|
signals[key] = createSignal(obj[key]);
|
|
@@ -83,39 +101,56 @@ function makeSignals(obj) {
|
|
|
83
101
|
}, {});
|
|
84
102
|
}
|
|
85
103
|
function makeCompositeJayComponent(preRender, defaultViewState, fastCarryForward, parts, trackByMap = {}) {
|
|
104
|
+
const interactiveParts = parts.filter((part) => part.comp !== void 0);
|
|
86
105
|
const hasFastRendering = defaultViewState !== null && defaultViewState !== void 0;
|
|
106
|
+
const headlessInstanceViewStates = defaultViewState?.__headlessInstances;
|
|
107
|
+
const headlessInstanceCarryForwards = fastCarryForward?.__headlessInstances;
|
|
108
|
+
if (headlessInstanceViewStates)
|
|
109
|
+
delete defaultViewState.__headlessInstances;
|
|
110
|
+
if (headlessInstanceCarryForwards)
|
|
111
|
+
delete fastCarryForward.__headlessInstances;
|
|
87
112
|
const comp = (props, refs, ...contexts) => {
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
113
|
+
if (headlessInstanceViewStates || headlessInstanceCarryForwards) {
|
|
114
|
+
const componentContext = useContext(COMPONENT_CONTEXT);
|
|
115
|
+
const instancesData = {
|
|
116
|
+
viewStates: headlessInstanceViewStates || {},
|
|
117
|
+
carryForwards: headlessInstanceCarryForwards || {}
|
|
118
|
+
};
|
|
119
|
+
componentContext.provideContexts.push([HEADLESS_INSTANCES, instancesData]);
|
|
120
|
+
}
|
|
121
|
+
const instances = interactiveParts.map(
|
|
122
|
+
(part) => {
|
|
123
|
+
const partRefs = part.key ? refs[part.key] : refs;
|
|
124
|
+
let partContexts;
|
|
125
|
+
if (hasFastRendering) {
|
|
126
|
+
const partViewState = part.key ? defaultViewState?.[part.key] : defaultViewState;
|
|
127
|
+
const partFastViewState = partViewState ? makeSignals(partViewState) : void 0;
|
|
128
|
+
const partCarryForward = part.key ? fastCarryForward?.[part.key] : fastCarryForward;
|
|
129
|
+
partContexts = [
|
|
130
|
+
partFastViewState,
|
|
131
|
+
partCarryForward,
|
|
132
|
+
...contexts.splice(0, part.contextMarkers.length)
|
|
133
|
+
];
|
|
134
|
+
} else {
|
|
135
|
+
partContexts = [...contexts.splice(0, part.contextMarkers.length)];
|
|
136
|
+
}
|
|
137
|
+
return [part.key, part.comp(props, partRefs, ...partContexts)];
|
|
102
138
|
}
|
|
103
|
-
|
|
104
|
-
});
|
|
139
|
+
);
|
|
105
140
|
return {
|
|
106
141
|
render: () => {
|
|
107
142
|
let viewState = defaultViewState;
|
|
108
143
|
instances.forEach(([key, instance]) => {
|
|
109
144
|
const rendered = materializeViewState(instance.render());
|
|
110
145
|
if (key) {
|
|
111
|
-
viewState[key] = deepMergeViewStates(
|
|
146
|
+
viewState[key] = deepMergeViewStates$1(
|
|
112
147
|
defaultViewState[key],
|
|
113
148
|
rendered,
|
|
114
149
|
trackByMap,
|
|
115
150
|
key
|
|
116
151
|
);
|
|
117
152
|
} else {
|
|
118
|
-
viewState = deepMergeViewStates(
|
|
153
|
+
viewState = deepMergeViewStates$1(
|
|
119
154
|
viewState,
|
|
120
155
|
rendered,
|
|
121
156
|
trackByMap
|
|
@@ -126,7 +161,7 @@ function makeCompositeJayComponent(preRender, defaultViewState, fastCarryForward
|
|
|
126
161
|
}
|
|
127
162
|
};
|
|
128
163
|
};
|
|
129
|
-
const contextMarkers =
|
|
164
|
+
const contextMarkers = interactiveParts.reduce((cm, part) => {
|
|
130
165
|
return [...cm, ...part.contextMarkers];
|
|
131
166
|
}, []);
|
|
132
167
|
return makeJayComponent(
|
|
@@ -228,9 +263,264 @@ function isSimpleObject(obj) {
|
|
|
228
263
|
}
|
|
229
264
|
return true;
|
|
230
265
|
}
|
|
266
|
+
var __defProp2 = Object.defineProperty;
|
|
267
|
+
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
268
|
+
var __publicField2 = (obj, key, value) => {
|
|
269
|
+
__defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
270
|
+
return value;
|
|
271
|
+
};
|
|
272
|
+
function deepMergeViewStates(base, overlay, trackByMap, path = "") {
|
|
273
|
+
if (!base && !overlay)
|
|
274
|
+
return {};
|
|
275
|
+
if (!base)
|
|
276
|
+
return overlay || {};
|
|
277
|
+
if (!overlay)
|
|
278
|
+
return base || {};
|
|
279
|
+
const result = {};
|
|
280
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(overlay)]);
|
|
281
|
+
for (const key of allKeys) {
|
|
282
|
+
const baseValue = base[key];
|
|
283
|
+
const overlayValue = overlay[key];
|
|
284
|
+
const currentPath = path ? `${path}.${key}` : key;
|
|
285
|
+
if (overlayValue === void 0) {
|
|
286
|
+
result[key] = baseValue;
|
|
287
|
+
} else if (baseValue === void 0) {
|
|
288
|
+
result[key] = overlayValue;
|
|
289
|
+
} else if (Array.isArray(baseValue) && Array.isArray(overlayValue)) {
|
|
290
|
+
const trackByField = trackByMap[currentPath];
|
|
291
|
+
if (trackByField) {
|
|
292
|
+
result[key] = mergeArraysByTrackBy(
|
|
293
|
+
baseValue,
|
|
294
|
+
overlayValue,
|
|
295
|
+
trackByField,
|
|
296
|
+
trackByMap,
|
|
297
|
+
currentPath
|
|
298
|
+
);
|
|
299
|
+
} else {
|
|
300
|
+
result[key] = overlayValue;
|
|
301
|
+
}
|
|
302
|
+
} else if (typeof baseValue === "object" && baseValue !== null && typeof overlayValue === "object" && overlayValue !== null && !Array.isArray(baseValue) && !Array.isArray(overlayValue)) {
|
|
303
|
+
result[key] = deepMergeViewStates(baseValue, overlayValue, trackByMap, currentPath);
|
|
304
|
+
} else {
|
|
305
|
+
result[key] = overlayValue;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return result;
|
|
309
|
+
}
|
|
310
|
+
function mergeArraysByTrackBy(baseArray, overlayArray, trackByField, trackByMap, arrayPath) {
|
|
311
|
+
const baseByKey = /* @__PURE__ */ new Map();
|
|
312
|
+
for (const item of baseArray) {
|
|
313
|
+
const key = item[trackByField];
|
|
314
|
+
if (key !== void 0 && key !== null) {
|
|
315
|
+
if (baseByKey.has(key)) {
|
|
316
|
+
console.warn(
|
|
317
|
+
`Duplicate trackBy key [${key}] in base array at path [${arrayPath}]. This may cause incorrect merging.`
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
baseByKey.set(key, item);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
const overlayByKey = /* @__PURE__ */ new Map();
|
|
324
|
+
for (const item of overlayArray) {
|
|
325
|
+
const key = item[trackByField];
|
|
326
|
+
if (key !== void 0 && key !== null) {
|
|
327
|
+
overlayByKey.set(key, item);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return baseArray.map((baseItem) => {
|
|
331
|
+
const key = baseItem[trackByField];
|
|
332
|
+
if (key === void 0 || key === null) {
|
|
333
|
+
return baseItem;
|
|
334
|
+
}
|
|
335
|
+
const overlayItem = overlayByKey.get(key);
|
|
336
|
+
if (overlayItem) {
|
|
337
|
+
return deepMergeViewStates(baseItem, overlayItem, trackByMap, arrayPath);
|
|
338
|
+
} else {
|
|
339
|
+
return baseItem;
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
function collectInteractions(refs) {
|
|
344
|
+
const interactions = [];
|
|
345
|
+
if (!refs)
|
|
346
|
+
return interactions;
|
|
347
|
+
collectInteractionsRecursive(refs, interactions);
|
|
348
|
+
return interactions;
|
|
349
|
+
}
|
|
350
|
+
function collectInteractionsRecursive(refs, interactions) {
|
|
351
|
+
if (!refs)
|
|
352
|
+
return;
|
|
353
|
+
for (const [refName, refImpl] of Object.entries(refs)) {
|
|
354
|
+
if (!refImpl)
|
|
355
|
+
continue;
|
|
356
|
+
if (refImpl.elements && refImpl.elements instanceof Set) {
|
|
357
|
+
for (const elem of refImpl.elements) {
|
|
358
|
+
if (elem.element) {
|
|
359
|
+
interactions.push({
|
|
360
|
+
refName,
|
|
361
|
+
coordinate: elem.coordinate || [refName],
|
|
362
|
+
element: elem.element,
|
|
363
|
+
elementType: getElementType(elem.element),
|
|
364
|
+
supportedEvents: getSupportedEvents(elem.element),
|
|
365
|
+
itemContext: elem.viewState
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
} else if (isNestedRefsObject(refImpl)) {
|
|
370
|
+
collectInteractionsRecursive(refImpl, interactions);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
function isNestedRefsObject(obj) {
|
|
375
|
+
if (!obj || typeof obj !== "object")
|
|
376
|
+
return false;
|
|
377
|
+
if (obj.elements instanceof Set)
|
|
378
|
+
return false;
|
|
379
|
+
const proto = Object.getPrototypeOf(obj);
|
|
380
|
+
if (proto !== Object.prototype && proto !== null)
|
|
381
|
+
return false;
|
|
382
|
+
return true;
|
|
383
|
+
}
|
|
384
|
+
function getElementType(element) {
|
|
385
|
+
return element.constructor.name;
|
|
386
|
+
}
|
|
387
|
+
function getSupportedEvents(element) {
|
|
388
|
+
const base = ["click", "focus", "blur"];
|
|
389
|
+
if (element instanceof HTMLInputElement) {
|
|
390
|
+
return [...base, "input", "change"];
|
|
391
|
+
}
|
|
392
|
+
if (element instanceof HTMLButtonElement) {
|
|
393
|
+
return ["click", "focus", "blur"];
|
|
394
|
+
}
|
|
395
|
+
if (element instanceof HTMLSelectElement) {
|
|
396
|
+
return [...base, "change"];
|
|
397
|
+
}
|
|
398
|
+
if (element instanceof HTMLTextAreaElement) {
|
|
399
|
+
return [...base, "input", "change"];
|
|
400
|
+
}
|
|
401
|
+
if (element instanceof HTMLAnchorElement) {
|
|
402
|
+
return ["click"];
|
|
403
|
+
}
|
|
404
|
+
if (element instanceof HTMLFormElement) {
|
|
405
|
+
return ["submit", "reset"];
|
|
406
|
+
}
|
|
407
|
+
return base;
|
|
408
|
+
}
|
|
409
|
+
const VIEW_STATE_CHANGE = "viewStateChange";
|
|
410
|
+
class AutomationAgent {
|
|
411
|
+
constructor(component, options) {
|
|
412
|
+
__publicField2(this, "stateListeners", /* @__PURE__ */ new Set());
|
|
413
|
+
__publicField2(this, "cachedInteractions", null);
|
|
414
|
+
__publicField2(this, "viewStateHandler", null);
|
|
415
|
+
__publicField2(this, "mergedViewState");
|
|
416
|
+
__publicField2(this, "initialSlowViewState");
|
|
417
|
+
__publicField2(this, "trackByMap");
|
|
418
|
+
this.component = component;
|
|
419
|
+
if (options) {
|
|
420
|
+
this.initialSlowViewState = options.initialViewState;
|
|
421
|
+
this.trackByMap = options.trackByMap;
|
|
422
|
+
this.mergedViewState = deepMergeViewStates(
|
|
423
|
+
options.initialViewState,
|
|
424
|
+
this.component.viewState || {},
|
|
425
|
+
options.trackByMap
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
this.subscribeToUpdates();
|
|
429
|
+
}
|
|
430
|
+
subscribeToUpdates() {
|
|
431
|
+
this.viewStateHandler = () => {
|
|
432
|
+
this.cachedInteractions = null;
|
|
433
|
+
if (this.initialSlowViewState && this.trackByMap) {
|
|
434
|
+
this.mergedViewState = deepMergeViewStates(
|
|
435
|
+
this.initialSlowViewState,
|
|
436
|
+
this.component.viewState || {},
|
|
437
|
+
this.trackByMap
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
this.notifyListeners();
|
|
441
|
+
};
|
|
442
|
+
this.component.addEventListener(VIEW_STATE_CHANGE, this.viewStateHandler);
|
|
443
|
+
}
|
|
444
|
+
notifyListeners() {
|
|
445
|
+
if (this.stateListeners.size === 0)
|
|
446
|
+
return;
|
|
447
|
+
const state = this.getPageState();
|
|
448
|
+
this.stateListeners.forEach((callback) => callback(state));
|
|
449
|
+
}
|
|
450
|
+
getPageState() {
|
|
451
|
+
if (!this.cachedInteractions) {
|
|
452
|
+
this.cachedInteractions = collectInteractions(this.component.element?.refs);
|
|
453
|
+
}
|
|
454
|
+
return {
|
|
455
|
+
// Use merged state if available (slow+fast), otherwise component's viewState
|
|
456
|
+
viewState: this.mergedViewState || this.component.viewState,
|
|
457
|
+
interactions: this.cachedInteractions,
|
|
458
|
+
customEvents: this.getCustomEvents()
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
triggerEvent(eventType, coordinate, eventData) {
|
|
462
|
+
const interaction = this.getInteraction(coordinate);
|
|
463
|
+
if (!interaction) {
|
|
464
|
+
throw new Error(`No element found at coordinate: ${coordinate.join("/")}`);
|
|
465
|
+
}
|
|
466
|
+
const event = new Event(eventType, { bubbles: true });
|
|
467
|
+
if (eventData) {
|
|
468
|
+
Object.assign(event, eventData);
|
|
469
|
+
}
|
|
470
|
+
interaction.element.dispatchEvent(event);
|
|
471
|
+
}
|
|
472
|
+
getInteraction(coordinate) {
|
|
473
|
+
const state = this.getPageState();
|
|
474
|
+
return state.interactions.find(
|
|
475
|
+
(i) => i.coordinate.length === coordinate.length && i.coordinate.every((c, idx) => c === coordinate[idx])
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
onStateChange(callback) {
|
|
479
|
+
this.stateListeners.add(callback);
|
|
480
|
+
return () => this.stateListeners.delete(callback);
|
|
481
|
+
}
|
|
482
|
+
getCustomEvents() {
|
|
483
|
+
const events = [];
|
|
484
|
+
const component = this.component;
|
|
485
|
+
for (const key in component) {
|
|
486
|
+
if (component[key]?.emit && typeof component[key].emit === "function") {
|
|
487
|
+
const name = key.startsWith("on") ? key.slice(2) : key;
|
|
488
|
+
events.push({ name });
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
return events;
|
|
492
|
+
}
|
|
493
|
+
onComponentEvent(eventName, callback) {
|
|
494
|
+
const component = this.component;
|
|
495
|
+
const handlerKey = eventName.startsWith("on") ? eventName : `on${eventName}`;
|
|
496
|
+
const handler = component[handlerKey];
|
|
497
|
+
if (!handler || typeof handler !== "function") {
|
|
498
|
+
throw new Error(`Unknown component event: ${eventName}`);
|
|
499
|
+
}
|
|
500
|
+
handler(({ event }) => callback(event));
|
|
501
|
+
return () => handler(void 0);
|
|
502
|
+
}
|
|
503
|
+
dispose() {
|
|
504
|
+
if (this.viewStateHandler) {
|
|
505
|
+
this.component.removeEventListener(VIEW_STATE_CHANGE, this.viewStateHandler);
|
|
506
|
+
this.viewStateHandler = null;
|
|
507
|
+
}
|
|
508
|
+
this.stateListeners.clear();
|
|
509
|
+
this.cachedInteractions = null;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
function wrapWithAutomation(component, options) {
|
|
513
|
+
const agent = new AutomationAgent(component, options);
|
|
514
|
+
return Object.assign(component, { automation: agent });
|
|
515
|
+
}
|
|
516
|
+
const AUTOMATION_CONTEXT = createJayContext();
|
|
231
517
|
export {
|
|
518
|
+
AUTOMATION_CONTEXT,
|
|
232
519
|
ActionError,
|
|
520
|
+
HEADLESS_INSTANCES,
|
|
233
521
|
createActionCaller,
|
|
234
522
|
makeCompositeJayComponent,
|
|
235
|
-
|
|
523
|
+
makeHeadlessInstanceComponent,
|
|
524
|
+
setActionCallerOptions,
|
|
525
|
+
wrapWithAutomation
|
|
236
526
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/stack-client-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -27,14 +27,15 @@
|
|
|
27
27
|
"test:watch": "vitest"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@jay-framework/component": "^0.
|
|
31
|
-
"@jay-framework/fullstack-component": "^0.
|
|
32
|
-
"@jay-framework/runtime": "^0.
|
|
33
|
-
"@jay-framework/
|
|
30
|
+
"@jay-framework/component": "^0.12.0",
|
|
31
|
+
"@jay-framework/fullstack-component": "^0.12.0",
|
|
32
|
+
"@jay-framework/runtime": "^0.12.0",
|
|
33
|
+
"@jay-framework/runtime-automation": "^0.12.0",
|
|
34
|
+
"@jay-framework/view-state-merge": "^0.12.0"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
|
-
"@jay-framework/dev-environment": "^0.
|
|
37
|
-
"@jay-framework/jay-cli": "^0.
|
|
37
|
+
"@jay-framework/dev-environment": "^0.12.0",
|
|
38
|
+
"@jay-framework/jay-cli": "^0.12.0",
|
|
38
39
|
"@types/express": "^5.0.2",
|
|
39
40
|
"@types/node": "^22.15.21",
|
|
40
41
|
"nodemon": "^3.0.3",
|