@jay-framework/stack-client-runtime 0.10.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.cjs +281 -24
- package/dist/index.d.ts +7 -1
- package/dist/index.js +282 -25
- 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,7 +73,7 @@ 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
|
}
|
|
@@ -85,39 +86,42 @@ function makeSignals(obj) {
|
|
|
85
86
|
}, {});
|
|
86
87
|
}
|
|
87
88
|
function makeCompositeJayComponent(preRender, defaultViewState, fastCarryForward, parts, trackByMap = {}) {
|
|
89
|
+
const interactiveParts = parts.filter((part) => part.comp !== void 0);
|
|
88
90
|
const hasFastRendering = defaultViewState !== null && defaultViewState !== void 0;
|
|
89
91
|
const comp = (props, refs, ...contexts) => {
|
|
90
|
-
const instances =
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
92
|
+
const instances = interactiveParts.map(
|
|
93
|
+
(part) => {
|
|
94
|
+
const partRefs = part.key ? refs[part.key] : refs;
|
|
95
|
+
let partContexts;
|
|
96
|
+
if (hasFastRendering) {
|
|
97
|
+
const partViewState = part.key ? defaultViewState?.[part.key] : defaultViewState;
|
|
98
|
+
const partFastViewState = partViewState ? makeSignals(partViewState) : void 0;
|
|
99
|
+
const partCarryForward = part.key ? fastCarryForward?.[part.key] : fastCarryForward;
|
|
100
|
+
partContexts = [
|
|
101
|
+
partFastViewState,
|
|
102
|
+
partCarryForward,
|
|
103
|
+
...contexts.splice(0, part.contextMarkers.length)
|
|
104
|
+
];
|
|
105
|
+
} else {
|
|
106
|
+
partContexts = [...contexts.splice(0, part.contextMarkers.length)];
|
|
107
|
+
}
|
|
108
|
+
return [part.key, part.comp(props, partRefs, ...partContexts)];
|
|
104
109
|
}
|
|
105
|
-
|
|
106
|
-
});
|
|
110
|
+
);
|
|
107
111
|
return {
|
|
108
112
|
render: () => {
|
|
109
113
|
let viewState = defaultViewState;
|
|
110
114
|
instances.forEach(([key, instance]) => {
|
|
111
115
|
const rendered = component.materializeViewState(instance.render());
|
|
112
116
|
if (key) {
|
|
113
|
-
viewState[key] = deepMergeViewStates(
|
|
117
|
+
viewState[key] = deepMergeViewStates$1(
|
|
114
118
|
defaultViewState[key],
|
|
115
119
|
rendered,
|
|
116
120
|
trackByMap,
|
|
117
121
|
key
|
|
118
122
|
);
|
|
119
123
|
} else {
|
|
120
|
-
viewState = deepMergeViewStates(
|
|
124
|
+
viewState = deepMergeViewStates$1(
|
|
121
125
|
viewState,
|
|
122
126
|
rendered,
|
|
123
127
|
trackByMap
|
|
@@ -128,7 +132,7 @@ function makeCompositeJayComponent(preRender, defaultViewState, fastCarryForward
|
|
|
128
132
|
}
|
|
129
133
|
};
|
|
130
134
|
};
|
|
131
|
-
const contextMarkers =
|
|
135
|
+
const contextMarkers = interactiveParts.reduce((cm, part) => {
|
|
132
136
|
return [...cm, ...part.contextMarkers];
|
|
133
137
|
}, []);
|
|
134
138
|
return component.makeJayComponent(
|
|
@@ -230,7 +234,260 @@ function isSimpleObject(obj) {
|
|
|
230
234
|
}
|
|
231
235
|
return true;
|
|
232
236
|
}
|
|
237
|
+
var __defProp2 = Object.defineProperty;
|
|
238
|
+
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
239
|
+
var __publicField2 = (obj, key, value) => {
|
|
240
|
+
__defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
241
|
+
return value;
|
|
242
|
+
};
|
|
243
|
+
function deepMergeViewStates(base, overlay, trackByMap, path = "") {
|
|
244
|
+
if (!base && !overlay)
|
|
245
|
+
return {};
|
|
246
|
+
if (!base)
|
|
247
|
+
return overlay || {};
|
|
248
|
+
if (!overlay)
|
|
249
|
+
return base || {};
|
|
250
|
+
const result = {};
|
|
251
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(overlay)]);
|
|
252
|
+
for (const key of allKeys) {
|
|
253
|
+
const baseValue = base[key];
|
|
254
|
+
const overlayValue = overlay[key];
|
|
255
|
+
const currentPath = path ? `${path}.${key}` : key;
|
|
256
|
+
if (overlayValue === void 0) {
|
|
257
|
+
result[key] = baseValue;
|
|
258
|
+
} else if (baseValue === void 0) {
|
|
259
|
+
result[key] = overlayValue;
|
|
260
|
+
} else if (Array.isArray(baseValue) && Array.isArray(overlayValue)) {
|
|
261
|
+
const trackByField = trackByMap[currentPath];
|
|
262
|
+
if (trackByField) {
|
|
263
|
+
result[key] = mergeArraysByTrackBy(
|
|
264
|
+
baseValue,
|
|
265
|
+
overlayValue,
|
|
266
|
+
trackByField,
|
|
267
|
+
trackByMap,
|
|
268
|
+
currentPath
|
|
269
|
+
);
|
|
270
|
+
} else {
|
|
271
|
+
result[key] = overlayValue;
|
|
272
|
+
}
|
|
273
|
+
} else if (typeof baseValue === "object" && baseValue !== null && typeof overlayValue === "object" && overlayValue !== null && !Array.isArray(baseValue) && !Array.isArray(overlayValue)) {
|
|
274
|
+
result[key] = deepMergeViewStates(baseValue, overlayValue, trackByMap, currentPath);
|
|
275
|
+
} else {
|
|
276
|
+
result[key] = overlayValue;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return result;
|
|
280
|
+
}
|
|
281
|
+
function mergeArraysByTrackBy(baseArray, overlayArray, trackByField, trackByMap, arrayPath) {
|
|
282
|
+
const baseByKey = /* @__PURE__ */ new Map();
|
|
283
|
+
for (const item of baseArray) {
|
|
284
|
+
const key = item[trackByField];
|
|
285
|
+
if (key !== void 0 && key !== null) {
|
|
286
|
+
if (baseByKey.has(key)) {
|
|
287
|
+
console.warn(
|
|
288
|
+
`Duplicate trackBy key [${key}] in base array at path [${arrayPath}]. This may cause incorrect merging.`
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
baseByKey.set(key, item);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
const overlayByKey = /* @__PURE__ */ new Map();
|
|
295
|
+
for (const item of overlayArray) {
|
|
296
|
+
const key = item[trackByField];
|
|
297
|
+
if (key !== void 0 && key !== null) {
|
|
298
|
+
overlayByKey.set(key, item);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return baseArray.map((baseItem) => {
|
|
302
|
+
const key = baseItem[trackByField];
|
|
303
|
+
if (key === void 0 || key === null) {
|
|
304
|
+
return baseItem;
|
|
305
|
+
}
|
|
306
|
+
const overlayItem = overlayByKey.get(key);
|
|
307
|
+
if (overlayItem) {
|
|
308
|
+
return deepMergeViewStates(baseItem, overlayItem, trackByMap, arrayPath);
|
|
309
|
+
} else {
|
|
310
|
+
return baseItem;
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
function collectInteractions(refs) {
|
|
315
|
+
const interactions = [];
|
|
316
|
+
if (!refs)
|
|
317
|
+
return interactions;
|
|
318
|
+
collectInteractionsRecursive(refs, interactions);
|
|
319
|
+
return interactions;
|
|
320
|
+
}
|
|
321
|
+
function collectInteractionsRecursive(refs, interactions) {
|
|
322
|
+
if (!refs)
|
|
323
|
+
return;
|
|
324
|
+
for (const [refName, refImpl] of Object.entries(refs)) {
|
|
325
|
+
if (!refImpl)
|
|
326
|
+
continue;
|
|
327
|
+
if (refImpl.elements && refImpl.elements instanceof Set) {
|
|
328
|
+
for (const elem of refImpl.elements) {
|
|
329
|
+
if (elem.element) {
|
|
330
|
+
interactions.push({
|
|
331
|
+
refName,
|
|
332
|
+
coordinate: elem.coordinate || [refName],
|
|
333
|
+
element: elem.element,
|
|
334
|
+
elementType: getElementType(elem.element),
|
|
335
|
+
supportedEvents: getSupportedEvents(elem.element),
|
|
336
|
+
itemContext: elem.viewState
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
} else if (isNestedRefsObject(refImpl)) {
|
|
341
|
+
collectInteractionsRecursive(refImpl, interactions);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
function isNestedRefsObject(obj) {
|
|
346
|
+
if (!obj || typeof obj !== "object")
|
|
347
|
+
return false;
|
|
348
|
+
if (obj.elements instanceof Set)
|
|
349
|
+
return false;
|
|
350
|
+
const proto = Object.getPrototypeOf(obj);
|
|
351
|
+
if (proto !== Object.prototype && proto !== null)
|
|
352
|
+
return false;
|
|
353
|
+
return true;
|
|
354
|
+
}
|
|
355
|
+
function getElementType(element) {
|
|
356
|
+
return element.constructor.name;
|
|
357
|
+
}
|
|
358
|
+
function getSupportedEvents(element) {
|
|
359
|
+
const base = ["click", "focus", "blur"];
|
|
360
|
+
if (element instanceof HTMLInputElement) {
|
|
361
|
+
return [...base, "input", "change"];
|
|
362
|
+
}
|
|
363
|
+
if (element instanceof HTMLButtonElement) {
|
|
364
|
+
return ["click", "focus", "blur"];
|
|
365
|
+
}
|
|
366
|
+
if (element instanceof HTMLSelectElement) {
|
|
367
|
+
return [...base, "change"];
|
|
368
|
+
}
|
|
369
|
+
if (element instanceof HTMLTextAreaElement) {
|
|
370
|
+
return [...base, "input", "change"];
|
|
371
|
+
}
|
|
372
|
+
if (element instanceof HTMLAnchorElement) {
|
|
373
|
+
return ["click"];
|
|
374
|
+
}
|
|
375
|
+
if (element instanceof HTMLFormElement) {
|
|
376
|
+
return ["submit", "reset"];
|
|
377
|
+
}
|
|
378
|
+
return base;
|
|
379
|
+
}
|
|
380
|
+
const VIEW_STATE_CHANGE = "viewStateChange";
|
|
381
|
+
class AutomationAgent {
|
|
382
|
+
constructor(component2, options) {
|
|
383
|
+
__publicField2(this, "stateListeners", /* @__PURE__ */ new Set());
|
|
384
|
+
__publicField2(this, "cachedInteractions", null);
|
|
385
|
+
__publicField2(this, "viewStateHandler", null);
|
|
386
|
+
__publicField2(this, "mergedViewState");
|
|
387
|
+
__publicField2(this, "initialSlowViewState");
|
|
388
|
+
__publicField2(this, "trackByMap");
|
|
389
|
+
this.component = component2;
|
|
390
|
+
if (options) {
|
|
391
|
+
this.initialSlowViewState = options.initialViewState;
|
|
392
|
+
this.trackByMap = options.trackByMap;
|
|
393
|
+
this.mergedViewState = deepMergeViewStates(
|
|
394
|
+
options.initialViewState,
|
|
395
|
+
this.component.viewState || {},
|
|
396
|
+
options.trackByMap
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
this.subscribeToUpdates();
|
|
400
|
+
}
|
|
401
|
+
subscribeToUpdates() {
|
|
402
|
+
this.viewStateHandler = () => {
|
|
403
|
+
this.cachedInteractions = null;
|
|
404
|
+
if (this.initialSlowViewState && this.trackByMap) {
|
|
405
|
+
this.mergedViewState = deepMergeViewStates(
|
|
406
|
+
this.initialSlowViewState,
|
|
407
|
+
this.component.viewState || {},
|
|
408
|
+
this.trackByMap
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
this.notifyListeners();
|
|
412
|
+
};
|
|
413
|
+
this.component.addEventListener(VIEW_STATE_CHANGE, this.viewStateHandler);
|
|
414
|
+
}
|
|
415
|
+
notifyListeners() {
|
|
416
|
+
if (this.stateListeners.size === 0)
|
|
417
|
+
return;
|
|
418
|
+
const state = this.getPageState();
|
|
419
|
+
this.stateListeners.forEach((callback) => callback(state));
|
|
420
|
+
}
|
|
421
|
+
getPageState() {
|
|
422
|
+
if (!this.cachedInteractions) {
|
|
423
|
+
this.cachedInteractions = collectInteractions(this.component.element?.refs);
|
|
424
|
+
}
|
|
425
|
+
return {
|
|
426
|
+
// Use merged state if available (slow+fast), otherwise component's viewState
|
|
427
|
+
viewState: this.mergedViewState || this.component.viewState,
|
|
428
|
+
interactions: this.cachedInteractions,
|
|
429
|
+
customEvents: this.getCustomEvents()
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
triggerEvent(eventType, coordinate, eventData) {
|
|
433
|
+
const interaction = this.getInteraction(coordinate);
|
|
434
|
+
if (!interaction) {
|
|
435
|
+
throw new Error(`No element found at coordinate: ${coordinate.join("/")}`);
|
|
436
|
+
}
|
|
437
|
+
const event = new Event(eventType, { bubbles: true });
|
|
438
|
+
if (eventData) {
|
|
439
|
+
Object.assign(event, eventData);
|
|
440
|
+
}
|
|
441
|
+
interaction.element.dispatchEvent(event);
|
|
442
|
+
}
|
|
443
|
+
getInteraction(coordinate) {
|
|
444
|
+
const state = this.getPageState();
|
|
445
|
+
return state.interactions.find(
|
|
446
|
+
(i) => i.coordinate.length === coordinate.length && i.coordinate.every((c, idx) => c === coordinate[idx])
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
onStateChange(callback) {
|
|
450
|
+
this.stateListeners.add(callback);
|
|
451
|
+
return () => this.stateListeners.delete(callback);
|
|
452
|
+
}
|
|
453
|
+
getCustomEvents() {
|
|
454
|
+
const events = [];
|
|
455
|
+
const component2 = this.component;
|
|
456
|
+
for (const key in component2) {
|
|
457
|
+
if (component2[key]?.emit && typeof component2[key].emit === "function") {
|
|
458
|
+
const name = key.startsWith("on") ? key.slice(2) : key;
|
|
459
|
+
events.push({ name });
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
return events;
|
|
463
|
+
}
|
|
464
|
+
onComponentEvent(eventName, callback) {
|
|
465
|
+
const component2 = this.component;
|
|
466
|
+
const handlerKey = eventName.startsWith("on") ? eventName : `on${eventName}`;
|
|
467
|
+
const handler = component2[handlerKey];
|
|
468
|
+
if (!handler || typeof handler !== "function") {
|
|
469
|
+
throw new Error(`Unknown component event: ${eventName}`);
|
|
470
|
+
}
|
|
471
|
+
handler(({ event }) => callback(event));
|
|
472
|
+
return () => handler(void 0);
|
|
473
|
+
}
|
|
474
|
+
dispose() {
|
|
475
|
+
if (this.viewStateHandler) {
|
|
476
|
+
this.component.removeEventListener(VIEW_STATE_CHANGE, this.viewStateHandler);
|
|
477
|
+
this.viewStateHandler = null;
|
|
478
|
+
}
|
|
479
|
+
this.stateListeners.clear();
|
|
480
|
+
this.cachedInteractions = null;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
function wrapWithAutomation(component2, options) {
|
|
484
|
+
const agent = new AutomationAgent(component2, options);
|
|
485
|
+
return Object.assign(component2, { automation: agent });
|
|
486
|
+
}
|
|
487
|
+
const AUTOMATION_CONTEXT = runtime.createJayContext();
|
|
488
|
+
exports.AUTOMATION_CONTEXT = AUTOMATION_CONTEXT;
|
|
233
489
|
exports.ActionError = ActionError;
|
|
234
490
|
exports.createActionCaller = createActionCaller;
|
|
235
491
|
exports.makeCompositeJayComponent = makeCompositeJayComponent;
|
|
236
492
|
exports.setActionCallerOptions = setActionCallerOptions;
|
|
493
|
+
exports.wrapWithAutomation = wrapWithAutomation;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,9 +2,15 @@ import * as _jay_framework_component from '@jay-framework/component';
|
|
|
2
2
|
import { ComponentConstructor, ContextMarkers, JayComponentCore } from '@jay-framework/component';
|
|
3
3
|
import { JayElement, PreRenderElement } 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
|
}
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,8 @@ var __publicField = (obj, key, value) => {
|
|
|
5
5
|
return value;
|
|
6
6
|
};
|
|
7
7
|
import { makeJayComponent, materializeViewState, createSignal } from "@jay-framework/component";
|
|
8
|
-
|
|
8
|
+
import { createJayContext } 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,7 +71,7 @@ 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
|
}
|
|
@@ -83,39 +84,42 @@ function makeSignals(obj) {
|
|
|
83
84
|
}, {});
|
|
84
85
|
}
|
|
85
86
|
function makeCompositeJayComponent(preRender, defaultViewState, fastCarryForward, parts, trackByMap = {}) {
|
|
87
|
+
const interactiveParts = parts.filter((part) => part.comp !== void 0);
|
|
86
88
|
const hasFastRendering = defaultViewState !== null && defaultViewState !== void 0;
|
|
87
89
|
const comp = (props, refs, ...contexts) => {
|
|
88
|
-
const instances =
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
90
|
+
const instances = interactiveParts.map(
|
|
91
|
+
(part) => {
|
|
92
|
+
const partRefs = part.key ? refs[part.key] : refs;
|
|
93
|
+
let partContexts;
|
|
94
|
+
if (hasFastRendering) {
|
|
95
|
+
const partViewState = part.key ? defaultViewState?.[part.key] : defaultViewState;
|
|
96
|
+
const partFastViewState = partViewState ? makeSignals(partViewState) : void 0;
|
|
97
|
+
const partCarryForward = part.key ? fastCarryForward?.[part.key] : fastCarryForward;
|
|
98
|
+
partContexts = [
|
|
99
|
+
partFastViewState,
|
|
100
|
+
partCarryForward,
|
|
101
|
+
...contexts.splice(0, part.contextMarkers.length)
|
|
102
|
+
];
|
|
103
|
+
} else {
|
|
104
|
+
partContexts = [...contexts.splice(0, part.contextMarkers.length)];
|
|
105
|
+
}
|
|
106
|
+
return [part.key, part.comp(props, partRefs, ...partContexts)];
|
|
102
107
|
}
|
|
103
|
-
|
|
104
|
-
});
|
|
108
|
+
);
|
|
105
109
|
return {
|
|
106
110
|
render: () => {
|
|
107
111
|
let viewState = defaultViewState;
|
|
108
112
|
instances.forEach(([key, instance]) => {
|
|
109
113
|
const rendered = materializeViewState(instance.render());
|
|
110
114
|
if (key) {
|
|
111
|
-
viewState[key] = deepMergeViewStates(
|
|
115
|
+
viewState[key] = deepMergeViewStates$1(
|
|
112
116
|
defaultViewState[key],
|
|
113
117
|
rendered,
|
|
114
118
|
trackByMap,
|
|
115
119
|
key
|
|
116
120
|
);
|
|
117
121
|
} else {
|
|
118
|
-
viewState = deepMergeViewStates(
|
|
122
|
+
viewState = deepMergeViewStates$1(
|
|
119
123
|
viewState,
|
|
120
124
|
rendered,
|
|
121
125
|
trackByMap
|
|
@@ -126,7 +130,7 @@ function makeCompositeJayComponent(preRender, defaultViewState, fastCarryForward
|
|
|
126
130
|
}
|
|
127
131
|
};
|
|
128
132
|
};
|
|
129
|
-
const contextMarkers =
|
|
133
|
+
const contextMarkers = interactiveParts.reduce((cm, part) => {
|
|
130
134
|
return [...cm, ...part.contextMarkers];
|
|
131
135
|
}, []);
|
|
132
136
|
return makeJayComponent(
|
|
@@ -228,9 +232,262 @@ function isSimpleObject(obj) {
|
|
|
228
232
|
}
|
|
229
233
|
return true;
|
|
230
234
|
}
|
|
235
|
+
var __defProp2 = Object.defineProperty;
|
|
236
|
+
var __defNormalProp2 = (obj, key, value) => key in obj ? __defProp2(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
237
|
+
var __publicField2 = (obj, key, value) => {
|
|
238
|
+
__defNormalProp2(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
239
|
+
return value;
|
|
240
|
+
};
|
|
241
|
+
function deepMergeViewStates(base, overlay, trackByMap, path = "") {
|
|
242
|
+
if (!base && !overlay)
|
|
243
|
+
return {};
|
|
244
|
+
if (!base)
|
|
245
|
+
return overlay || {};
|
|
246
|
+
if (!overlay)
|
|
247
|
+
return base || {};
|
|
248
|
+
const result = {};
|
|
249
|
+
const allKeys = /* @__PURE__ */ new Set([...Object.keys(base), ...Object.keys(overlay)]);
|
|
250
|
+
for (const key of allKeys) {
|
|
251
|
+
const baseValue = base[key];
|
|
252
|
+
const overlayValue = overlay[key];
|
|
253
|
+
const currentPath = path ? `${path}.${key}` : key;
|
|
254
|
+
if (overlayValue === void 0) {
|
|
255
|
+
result[key] = baseValue;
|
|
256
|
+
} else if (baseValue === void 0) {
|
|
257
|
+
result[key] = overlayValue;
|
|
258
|
+
} else if (Array.isArray(baseValue) && Array.isArray(overlayValue)) {
|
|
259
|
+
const trackByField = trackByMap[currentPath];
|
|
260
|
+
if (trackByField) {
|
|
261
|
+
result[key] = mergeArraysByTrackBy(
|
|
262
|
+
baseValue,
|
|
263
|
+
overlayValue,
|
|
264
|
+
trackByField,
|
|
265
|
+
trackByMap,
|
|
266
|
+
currentPath
|
|
267
|
+
);
|
|
268
|
+
} else {
|
|
269
|
+
result[key] = overlayValue;
|
|
270
|
+
}
|
|
271
|
+
} else if (typeof baseValue === "object" && baseValue !== null && typeof overlayValue === "object" && overlayValue !== null && !Array.isArray(baseValue) && !Array.isArray(overlayValue)) {
|
|
272
|
+
result[key] = deepMergeViewStates(baseValue, overlayValue, trackByMap, currentPath);
|
|
273
|
+
} else {
|
|
274
|
+
result[key] = overlayValue;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
function mergeArraysByTrackBy(baseArray, overlayArray, trackByField, trackByMap, arrayPath) {
|
|
280
|
+
const baseByKey = /* @__PURE__ */ new Map();
|
|
281
|
+
for (const item of baseArray) {
|
|
282
|
+
const key = item[trackByField];
|
|
283
|
+
if (key !== void 0 && key !== null) {
|
|
284
|
+
if (baseByKey.has(key)) {
|
|
285
|
+
console.warn(
|
|
286
|
+
`Duplicate trackBy key [${key}] in base array at path [${arrayPath}]. This may cause incorrect merging.`
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
baseByKey.set(key, item);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
const overlayByKey = /* @__PURE__ */ new Map();
|
|
293
|
+
for (const item of overlayArray) {
|
|
294
|
+
const key = item[trackByField];
|
|
295
|
+
if (key !== void 0 && key !== null) {
|
|
296
|
+
overlayByKey.set(key, item);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return baseArray.map((baseItem) => {
|
|
300
|
+
const key = baseItem[trackByField];
|
|
301
|
+
if (key === void 0 || key === null) {
|
|
302
|
+
return baseItem;
|
|
303
|
+
}
|
|
304
|
+
const overlayItem = overlayByKey.get(key);
|
|
305
|
+
if (overlayItem) {
|
|
306
|
+
return deepMergeViewStates(baseItem, overlayItem, trackByMap, arrayPath);
|
|
307
|
+
} else {
|
|
308
|
+
return baseItem;
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
function collectInteractions(refs) {
|
|
313
|
+
const interactions = [];
|
|
314
|
+
if (!refs)
|
|
315
|
+
return interactions;
|
|
316
|
+
collectInteractionsRecursive(refs, interactions);
|
|
317
|
+
return interactions;
|
|
318
|
+
}
|
|
319
|
+
function collectInteractionsRecursive(refs, interactions) {
|
|
320
|
+
if (!refs)
|
|
321
|
+
return;
|
|
322
|
+
for (const [refName, refImpl] of Object.entries(refs)) {
|
|
323
|
+
if (!refImpl)
|
|
324
|
+
continue;
|
|
325
|
+
if (refImpl.elements && refImpl.elements instanceof Set) {
|
|
326
|
+
for (const elem of refImpl.elements) {
|
|
327
|
+
if (elem.element) {
|
|
328
|
+
interactions.push({
|
|
329
|
+
refName,
|
|
330
|
+
coordinate: elem.coordinate || [refName],
|
|
331
|
+
element: elem.element,
|
|
332
|
+
elementType: getElementType(elem.element),
|
|
333
|
+
supportedEvents: getSupportedEvents(elem.element),
|
|
334
|
+
itemContext: elem.viewState
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
} else if (isNestedRefsObject(refImpl)) {
|
|
339
|
+
collectInteractionsRecursive(refImpl, interactions);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function isNestedRefsObject(obj) {
|
|
344
|
+
if (!obj || typeof obj !== "object")
|
|
345
|
+
return false;
|
|
346
|
+
if (obj.elements instanceof Set)
|
|
347
|
+
return false;
|
|
348
|
+
const proto = Object.getPrototypeOf(obj);
|
|
349
|
+
if (proto !== Object.prototype && proto !== null)
|
|
350
|
+
return false;
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
function getElementType(element) {
|
|
354
|
+
return element.constructor.name;
|
|
355
|
+
}
|
|
356
|
+
function getSupportedEvents(element) {
|
|
357
|
+
const base = ["click", "focus", "blur"];
|
|
358
|
+
if (element instanceof HTMLInputElement) {
|
|
359
|
+
return [...base, "input", "change"];
|
|
360
|
+
}
|
|
361
|
+
if (element instanceof HTMLButtonElement) {
|
|
362
|
+
return ["click", "focus", "blur"];
|
|
363
|
+
}
|
|
364
|
+
if (element instanceof HTMLSelectElement) {
|
|
365
|
+
return [...base, "change"];
|
|
366
|
+
}
|
|
367
|
+
if (element instanceof HTMLTextAreaElement) {
|
|
368
|
+
return [...base, "input", "change"];
|
|
369
|
+
}
|
|
370
|
+
if (element instanceof HTMLAnchorElement) {
|
|
371
|
+
return ["click"];
|
|
372
|
+
}
|
|
373
|
+
if (element instanceof HTMLFormElement) {
|
|
374
|
+
return ["submit", "reset"];
|
|
375
|
+
}
|
|
376
|
+
return base;
|
|
377
|
+
}
|
|
378
|
+
const VIEW_STATE_CHANGE = "viewStateChange";
|
|
379
|
+
class AutomationAgent {
|
|
380
|
+
constructor(component, options) {
|
|
381
|
+
__publicField2(this, "stateListeners", /* @__PURE__ */ new Set());
|
|
382
|
+
__publicField2(this, "cachedInteractions", null);
|
|
383
|
+
__publicField2(this, "viewStateHandler", null);
|
|
384
|
+
__publicField2(this, "mergedViewState");
|
|
385
|
+
__publicField2(this, "initialSlowViewState");
|
|
386
|
+
__publicField2(this, "trackByMap");
|
|
387
|
+
this.component = component;
|
|
388
|
+
if (options) {
|
|
389
|
+
this.initialSlowViewState = options.initialViewState;
|
|
390
|
+
this.trackByMap = options.trackByMap;
|
|
391
|
+
this.mergedViewState = deepMergeViewStates(
|
|
392
|
+
options.initialViewState,
|
|
393
|
+
this.component.viewState || {},
|
|
394
|
+
options.trackByMap
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
this.subscribeToUpdates();
|
|
398
|
+
}
|
|
399
|
+
subscribeToUpdates() {
|
|
400
|
+
this.viewStateHandler = () => {
|
|
401
|
+
this.cachedInteractions = null;
|
|
402
|
+
if (this.initialSlowViewState && this.trackByMap) {
|
|
403
|
+
this.mergedViewState = deepMergeViewStates(
|
|
404
|
+
this.initialSlowViewState,
|
|
405
|
+
this.component.viewState || {},
|
|
406
|
+
this.trackByMap
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
this.notifyListeners();
|
|
410
|
+
};
|
|
411
|
+
this.component.addEventListener(VIEW_STATE_CHANGE, this.viewStateHandler);
|
|
412
|
+
}
|
|
413
|
+
notifyListeners() {
|
|
414
|
+
if (this.stateListeners.size === 0)
|
|
415
|
+
return;
|
|
416
|
+
const state = this.getPageState();
|
|
417
|
+
this.stateListeners.forEach((callback) => callback(state));
|
|
418
|
+
}
|
|
419
|
+
getPageState() {
|
|
420
|
+
if (!this.cachedInteractions) {
|
|
421
|
+
this.cachedInteractions = collectInteractions(this.component.element?.refs);
|
|
422
|
+
}
|
|
423
|
+
return {
|
|
424
|
+
// Use merged state if available (slow+fast), otherwise component's viewState
|
|
425
|
+
viewState: this.mergedViewState || this.component.viewState,
|
|
426
|
+
interactions: this.cachedInteractions,
|
|
427
|
+
customEvents: this.getCustomEvents()
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
triggerEvent(eventType, coordinate, eventData) {
|
|
431
|
+
const interaction = this.getInteraction(coordinate);
|
|
432
|
+
if (!interaction) {
|
|
433
|
+
throw new Error(`No element found at coordinate: ${coordinate.join("/")}`);
|
|
434
|
+
}
|
|
435
|
+
const event = new Event(eventType, { bubbles: true });
|
|
436
|
+
if (eventData) {
|
|
437
|
+
Object.assign(event, eventData);
|
|
438
|
+
}
|
|
439
|
+
interaction.element.dispatchEvent(event);
|
|
440
|
+
}
|
|
441
|
+
getInteraction(coordinate) {
|
|
442
|
+
const state = this.getPageState();
|
|
443
|
+
return state.interactions.find(
|
|
444
|
+
(i) => i.coordinate.length === coordinate.length && i.coordinate.every((c, idx) => c === coordinate[idx])
|
|
445
|
+
);
|
|
446
|
+
}
|
|
447
|
+
onStateChange(callback) {
|
|
448
|
+
this.stateListeners.add(callback);
|
|
449
|
+
return () => this.stateListeners.delete(callback);
|
|
450
|
+
}
|
|
451
|
+
getCustomEvents() {
|
|
452
|
+
const events = [];
|
|
453
|
+
const component = this.component;
|
|
454
|
+
for (const key in component) {
|
|
455
|
+
if (component[key]?.emit && typeof component[key].emit === "function") {
|
|
456
|
+
const name = key.startsWith("on") ? key.slice(2) : key;
|
|
457
|
+
events.push({ name });
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return events;
|
|
461
|
+
}
|
|
462
|
+
onComponentEvent(eventName, callback) {
|
|
463
|
+
const component = this.component;
|
|
464
|
+
const handlerKey = eventName.startsWith("on") ? eventName : `on${eventName}`;
|
|
465
|
+
const handler = component[handlerKey];
|
|
466
|
+
if (!handler || typeof handler !== "function") {
|
|
467
|
+
throw new Error(`Unknown component event: ${eventName}`);
|
|
468
|
+
}
|
|
469
|
+
handler(({ event }) => callback(event));
|
|
470
|
+
return () => handler(void 0);
|
|
471
|
+
}
|
|
472
|
+
dispose() {
|
|
473
|
+
if (this.viewStateHandler) {
|
|
474
|
+
this.component.removeEventListener(VIEW_STATE_CHANGE, this.viewStateHandler);
|
|
475
|
+
this.viewStateHandler = null;
|
|
476
|
+
}
|
|
477
|
+
this.stateListeners.clear();
|
|
478
|
+
this.cachedInteractions = null;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
function wrapWithAutomation(component, options) {
|
|
482
|
+
const agent = new AutomationAgent(component, options);
|
|
483
|
+
return Object.assign(component, { automation: agent });
|
|
484
|
+
}
|
|
485
|
+
const AUTOMATION_CONTEXT = createJayContext();
|
|
231
486
|
export {
|
|
487
|
+
AUTOMATION_CONTEXT,
|
|
232
488
|
ActionError,
|
|
233
489
|
createActionCaller,
|
|
234
490
|
makeCompositeJayComponent,
|
|
235
|
-
setActionCallerOptions
|
|
491
|
+
setActionCallerOptions,
|
|
492
|
+
wrapWithAutomation
|
|
236
493
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/stack-client-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.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.11.0",
|
|
31
|
+
"@jay-framework/fullstack-component": "^0.11.0",
|
|
32
|
+
"@jay-framework/runtime": "^0.11.0",
|
|
33
|
+
"@jay-framework/runtime-automation": "^0.11.0",
|
|
34
|
+
"@jay-framework/view-state-merge": "^0.11.0"
|
|
34
35
|
},
|
|
35
36
|
"devDependencies": {
|
|
36
|
-
"@jay-framework/dev-environment": "^0.
|
|
37
|
-
"@jay-framework/jay-cli": "^0.
|
|
37
|
+
"@jay-framework/dev-environment": "^0.11.0",
|
|
38
|
+
"@jay-framework/jay-cli": "^0.11.0",
|
|
38
39
|
"@types/express": "^5.0.2",
|
|
39
40
|
"@types/node": "^22.15.21",
|
|
40
41
|
"nodemon": "^3.0.3",
|