@jay-framework/stack-client-runtime 0.15.6 → 0.16.1
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 +185 -19
- package/dist/index.d.ts +29 -2
- package/dist/index.js +186 -20
- package/package.json +8 -8
package/dist/index.cjs
CHANGED
|
@@ -130,13 +130,20 @@ function makeHeadlessInstanceComponent(preRender, componentDef, coordinateKey) {
|
|
|
130
130
|
const clientVS = { ...resolvedFastVS, ...originalRender() };
|
|
131
131
|
if (!done) {
|
|
132
132
|
setHydrationDone(true);
|
|
133
|
+
if (instanceData)
|
|
134
|
+
instanceData.viewStates[resolvedKey] = component.materializeViewState(resolvedFastVS);
|
|
133
135
|
return resolvedFastVS;
|
|
134
136
|
}
|
|
137
|
+
if (instanceData)
|
|
138
|
+
instanceData.viewStates[resolvedKey] = component.materializeViewState(clientVS);
|
|
135
139
|
return clientVS;
|
|
136
140
|
};
|
|
137
141
|
} else {
|
|
138
142
|
compCore.render = () => {
|
|
139
|
-
|
|
143
|
+
const vs = { ...resolvedFastVS, ...originalRender() };
|
|
144
|
+
if (instanceData)
|
|
145
|
+
instanceData.viewStates[resolvedKey] = component.materializeViewState(vs);
|
|
146
|
+
return vs;
|
|
140
147
|
};
|
|
141
148
|
}
|
|
142
149
|
return compCore;
|
|
@@ -204,6 +211,10 @@ function makeCompositeJayComponent(preRender, defaultViewState, fastCarryForward
|
|
|
204
211
|
);
|
|
205
212
|
}
|
|
206
213
|
});
|
|
214
|
+
const ivs = instancesData.viewStates;
|
|
215
|
+
if (Object.keys(ivs).length > 0) {
|
|
216
|
+
viewState.__headlessInstances = ivs;
|
|
217
|
+
}
|
|
207
218
|
return viewState;
|
|
208
219
|
}
|
|
209
220
|
};
|
|
@@ -278,6 +289,10 @@ function hydrateCompositeJayComponent(hydratePreRender, defaultViewState, fastCa
|
|
|
278
289
|
);
|
|
279
290
|
}
|
|
280
291
|
});
|
|
292
|
+
const ivs = instancesData.viewStates;
|
|
293
|
+
if (Object.keys(ivs).length > 0) {
|
|
294
|
+
viewState.__headlessInstances = ivs;
|
|
295
|
+
}
|
|
281
296
|
return viewState;
|
|
282
297
|
}
|
|
283
298
|
};
|
|
@@ -317,19 +332,60 @@ let globalOptions = {};
|
|
|
317
332
|
function setActionCallerOptions(options) {
|
|
318
333
|
globalOptions = { ...globalOptions, ...options };
|
|
319
334
|
}
|
|
320
|
-
function
|
|
335
|
+
function buildFormData(input) {
|
|
336
|
+
const formData = new FormData();
|
|
337
|
+
const jsonFields = {};
|
|
338
|
+
for (const [key, value] of Object.entries(input)) {
|
|
339
|
+
if (value instanceof Blob) {
|
|
340
|
+
const name = value instanceof File ? value.name : `${key}.bin`;
|
|
341
|
+
formData.append(key, value, name);
|
|
342
|
+
} else if (Array.isArray(value) && value.some((v) => v instanceof Blob)) {
|
|
343
|
+
const nonFiles = [];
|
|
344
|
+
for (const item of value) {
|
|
345
|
+
if (item instanceof Blob) {
|
|
346
|
+
const name = item instanceof File ? item.name : `${key}.bin`;
|
|
347
|
+
formData.append(key, item, name);
|
|
348
|
+
} else {
|
|
349
|
+
nonFiles.push(item);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (nonFiles.length > 0) {
|
|
353
|
+
jsonFields[key] = nonFiles;
|
|
354
|
+
}
|
|
355
|
+
} else {
|
|
356
|
+
jsonFields[key] = value;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
if (Object.keys(jsonFields).length > 0) {
|
|
360
|
+
formData.append("_json", JSON.stringify(jsonFields));
|
|
361
|
+
}
|
|
362
|
+
return formData;
|
|
363
|
+
}
|
|
364
|
+
function hasFiles(input) {
|
|
365
|
+
if (typeof input !== "object" || input === null)
|
|
366
|
+
return false;
|
|
367
|
+
for (const value of Object.values(input)) {
|
|
368
|
+
if (value instanceof Blob)
|
|
369
|
+
return true;
|
|
370
|
+
if (Array.isArray(value) && value.some((v) => v instanceof Blob))
|
|
371
|
+
return true;
|
|
372
|
+
}
|
|
373
|
+
return false;
|
|
374
|
+
}
|
|
375
|
+
function createActionCaller(actionName, method = "POST", options) {
|
|
321
376
|
return async (input) => {
|
|
322
377
|
const baseUrl = globalOptions.baseUrl ?? "";
|
|
323
|
-
const
|
|
378
|
+
const useFormData = options?.acceptsFiles && hasFiles(input);
|
|
379
|
+
const url = useFormData ? `${baseUrl}${ACTION_ENDPOINT_BASE}/${actionName}` : buildActionUrl(baseUrl, actionName, method, input);
|
|
324
380
|
const fetchOptions = {
|
|
325
381
|
method,
|
|
326
382
|
headers: {
|
|
327
|
-
"Content-Type": "application/json",
|
|
383
|
+
...useFormData ? {} : { "Content-Type": "application/json" },
|
|
328
384
|
...globalOptions.headers
|
|
329
385
|
}
|
|
330
386
|
};
|
|
331
387
|
if (method !== "GET") {
|
|
332
|
-
fetchOptions.body = JSON.stringify(input);
|
|
388
|
+
fetchOptions.body = useFormData ? buildFormData(input) : JSON.stringify(input);
|
|
333
389
|
}
|
|
334
390
|
const timeout = globalOptions.timeout ?? 3e4;
|
|
335
391
|
const controller = new AbortController();
|
|
@@ -387,6 +443,113 @@ function buildActionUrl(baseUrl, actionName, method, input) {
|
|
|
387
443
|
}
|
|
388
444
|
return fullUrl;
|
|
389
445
|
}
|
|
446
|
+
function createStreamCaller(actionName, options) {
|
|
447
|
+
return (input) => {
|
|
448
|
+
return {
|
|
449
|
+
[Symbol.asyncIterator]() {
|
|
450
|
+
const baseUrl = globalOptions.baseUrl ?? "";
|
|
451
|
+
const url = `${baseUrl}${ACTION_ENDPOINT_BASE}/${actionName}`;
|
|
452
|
+
const useFormData = options?.acceptsFiles && hasFiles(input);
|
|
453
|
+
let reader = null;
|
|
454
|
+
let buffer = "";
|
|
455
|
+
let done = false;
|
|
456
|
+
let error = null;
|
|
457
|
+
const chunks = [];
|
|
458
|
+
let resolveNext = null;
|
|
459
|
+
const fetchPromise = fetch(url, {
|
|
460
|
+
method: "POST",
|
|
461
|
+
headers: {
|
|
462
|
+
...useFormData ? {} : { "Content-Type": "application/json" },
|
|
463
|
+
...globalOptions.headers
|
|
464
|
+
},
|
|
465
|
+
body: useFormData ? buildFormData(input) : JSON.stringify(input)
|
|
466
|
+
}).then((response) => {
|
|
467
|
+
if (!response.ok) {
|
|
468
|
+
throw new ActionError(
|
|
469
|
+
"STREAM_ERROR",
|
|
470
|
+
`Stream '${actionName}' failed with status ${response.status}`
|
|
471
|
+
);
|
|
472
|
+
}
|
|
473
|
+
reader = response.body.getReader();
|
|
474
|
+
pump();
|
|
475
|
+
}).catch((err) => {
|
|
476
|
+
error = err instanceof ActionError ? err : new ActionError(
|
|
477
|
+
"NETWORK_ERROR",
|
|
478
|
+
`Network error streaming '${actionName}': ${err.message}`
|
|
479
|
+
);
|
|
480
|
+
if (resolveNext)
|
|
481
|
+
resolveNext();
|
|
482
|
+
});
|
|
483
|
+
const decoder = new TextDecoder();
|
|
484
|
+
function pump() {
|
|
485
|
+
reader.read().then(({ done: readerDone, value }) => {
|
|
486
|
+
if (value) {
|
|
487
|
+
buffer += decoder.decode(value, { stream: true });
|
|
488
|
+
const lines = buffer.split("\n");
|
|
489
|
+
buffer = lines.pop();
|
|
490
|
+
for (const line of lines) {
|
|
491
|
+
if (!line.trim())
|
|
492
|
+
continue;
|
|
493
|
+
const parsed = JSON.parse(line);
|
|
494
|
+
if (parsed.error) {
|
|
495
|
+
error = new ActionError("STREAM_ERROR", parsed.error);
|
|
496
|
+
if (resolveNext)
|
|
497
|
+
resolveNext();
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
if (parsed.done) {
|
|
501
|
+
done = true;
|
|
502
|
+
if (resolveNext)
|
|
503
|
+
resolveNext();
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if ("chunk" in parsed) {
|
|
507
|
+
chunks.push(parsed.chunk);
|
|
508
|
+
if (resolveNext)
|
|
509
|
+
resolveNext();
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
if (readerDone) {
|
|
514
|
+
done = true;
|
|
515
|
+
if (resolveNext)
|
|
516
|
+
resolveNext();
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
pump();
|
|
520
|
+
}).catch((err) => {
|
|
521
|
+
error = new ActionError("STREAM_ERROR", err.message);
|
|
522
|
+
if (resolveNext)
|
|
523
|
+
resolveNext();
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
return {
|
|
527
|
+
async next() {
|
|
528
|
+
await fetchPromise;
|
|
529
|
+
while (true) {
|
|
530
|
+
if (chunks.length > 0) {
|
|
531
|
+
return { value: chunks.shift(), done: false };
|
|
532
|
+
}
|
|
533
|
+
if (error) {
|
|
534
|
+
throw error;
|
|
535
|
+
}
|
|
536
|
+
if (done) {
|
|
537
|
+
return { value: void 0, done: true };
|
|
538
|
+
}
|
|
539
|
+
await new Promise((resolve) => {
|
|
540
|
+
resolveNext = resolve;
|
|
541
|
+
});
|
|
542
|
+
resolveNext = null;
|
|
543
|
+
}
|
|
544
|
+
},
|
|
545
|
+
[Symbol.asyncIterator]() {
|
|
546
|
+
return this;
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
};
|
|
551
|
+
};
|
|
552
|
+
}
|
|
390
553
|
function isSimpleObject(obj) {
|
|
391
554
|
if (typeof obj !== "object" || obj === null) {
|
|
392
555
|
return false;
|
|
@@ -591,11 +754,6 @@ class AutomationAgent {
|
|
|
591
754
|
if (options) {
|
|
592
755
|
this.initialSlowViewState = options.initialViewState;
|
|
593
756
|
this.trackByMap = options.trackByMap;
|
|
594
|
-
this.mergedViewState = deepMergeViewStates(
|
|
595
|
-
options.initialViewState,
|
|
596
|
-
this.component.viewState || {},
|
|
597
|
-
options.trackByMap
|
|
598
|
-
);
|
|
599
757
|
}
|
|
600
758
|
this.subscribeToUpdates();
|
|
601
759
|
}
|
|
@@ -603,13 +761,7 @@ class AutomationAgent {
|
|
|
603
761
|
this.viewStateHandler = () => {
|
|
604
762
|
this.cachedRaw = null;
|
|
605
763
|
this.cachedGrouped = null;
|
|
606
|
-
|
|
607
|
-
this.mergedViewState = deepMergeViewStates(
|
|
608
|
-
this.initialSlowViewState,
|
|
609
|
-
this.component.viewState || {},
|
|
610
|
-
this.trackByMap
|
|
611
|
-
);
|
|
612
|
-
}
|
|
764
|
+
this.mergedViewState = null;
|
|
613
765
|
this.notifyListeners();
|
|
614
766
|
};
|
|
615
767
|
this.component.addEventListener(VIEW_STATE_CHANGE, this.viewStateHandler);
|
|
@@ -630,9 +782,22 @@ class AutomationAgent {
|
|
|
630
782
|
return this.cachedGrouped;
|
|
631
783
|
}
|
|
632
784
|
getPageState() {
|
|
785
|
+
let viewState;
|
|
786
|
+
if (this.initialSlowViewState && this.trackByMap) {
|
|
787
|
+
viewState = deepMergeViewStates(
|
|
788
|
+
this.initialSlowViewState,
|
|
789
|
+
this.component.viewState || {},
|
|
790
|
+
this.trackByMap
|
|
791
|
+
);
|
|
792
|
+
const componentInstances = this.component.viewState?.__headlessInstances;
|
|
793
|
+
if (componentInstances) {
|
|
794
|
+
viewState.__headlessInstances = componentInstances;
|
|
795
|
+
}
|
|
796
|
+
} else {
|
|
797
|
+
viewState = this.component.viewState;
|
|
798
|
+
}
|
|
633
799
|
return {
|
|
634
|
-
|
|
635
|
-
viewState: this.mergedViewState || this.component.viewState,
|
|
800
|
+
viewState,
|
|
636
801
|
interactions: this.getGrouped(),
|
|
637
802
|
customEvents: this.getCustomEvents()
|
|
638
803
|
};
|
|
@@ -702,6 +867,7 @@ exports.AUTOMATION_CONTEXT = AUTOMATION_CONTEXT;
|
|
|
702
867
|
exports.ActionError = ActionError;
|
|
703
868
|
exports.HEADLESS_INSTANCES = HEADLESS_INSTANCES;
|
|
704
869
|
exports.createActionCaller = createActionCaller;
|
|
870
|
+
exports.createStreamCaller = createStreamCaller;
|
|
705
871
|
exports.hydrateCompositeJayComponent = hydrateCompositeJayComponent;
|
|
706
872
|
exports.makeCompositeJayComponent = makeCompositeJayComponent;
|
|
707
873
|
exports.makeHeadlessInstanceComponent = makeHeadlessInstanceComponent;
|
package/dist/index.d.ts
CHANGED
|
@@ -145,6 +145,33 @@ declare function setActionCallerOptions(options: ActionCallerOptions): void;
|
|
|
145
145
|
* const addToCart = createActionCaller<{productId: string}, {cartCount: number}>('cart.addToCart', 'POST');
|
|
146
146
|
* ```
|
|
147
147
|
*/
|
|
148
|
-
|
|
148
|
+
/**
|
|
149
|
+
* Options for action callers.
|
|
150
|
+
*/
|
|
151
|
+
interface CreateActionCallerOptions {
|
|
152
|
+
/** Whether this action accepts file uploads (DL#131) */
|
|
153
|
+
acceptsFiles?: boolean;
|
|
154
|
+
}
|
|
155
|
+
declare function createActionCaller<Input, Output>(actionName: string, method?: HttpMethod, options?: CreateActionCallerOptions): (input: Input) => Promise<Output>;
|
|
156
|
+
/**
|
|
157
|
+
* Creates a client-side stream caller that makes an HTTP request and returns
|
|
158
|
+
* an async iterable of chunks via NDJSON streaming.
|
|
159
|
+
*
|
|
160
|
+
* This function is used by the build transform to replace server-side makeJayStream
|
|
161
|
+
* handlers with client-side HTTP stream consumers.
|
|
162
|
+
*
|
|
163
|
+
* @param actionName - The unique action name (matches server registration)
|
|
164
|
+
* @returns A callable function that returns an AsyncIterable of chunks
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```typescript
|
|
168
|
+
* // Build transform replaces:
|
|
169
|
+
* import { checkInventory } from './actions/inventory-check.actions';
|
|
170
|
+
*
|
|
171
|
+
* // With:
|
|
172
|
+
* const checkInventory = createStreamCaller<void, { name: string }>('inventory.check');
|
|
173
|
+
* ```
|
|
174
|
+
*/
|
|
175
|
+
declare function createStreamCaller<Input, Chunk>(actionName: string, options?: CreateActionCallerOptions): (input: Input) => AsyncIterable<Chunk>;
|
|
149
176
|
|
|
150
|
-
export { type ActionCallerOptions, ActionError, type CompositePart, HEADLESS_INSTANCES, type HeadlessComponentDef, type HeadlessInstancesData, type HttpMethod, createActionCaller, hydrateCompositeJayComponent, makeCompositeJayComponent, makeHeadlessInstanceComponent, setActionCallerOptions };
|
|
177
|
+
export { type ActionCallerOptions, ActionError, type CompositePart, type CreateActionCallerOptions, HEADLESS_INSTANCES, type HeadlessComponentDef, type HeadlessInstancesData, type HttpMethod, createActionCaller, createStreamCaller, hydrateCompositeJayComponent, makeCompositeJayComponent, makeHeadlessInstanceComponent, setActionCallerOptions };
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ var __publicField = (obj, key, value) => {
|
|
|
4
4
|
__defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
5
5
|
return value;
|
|
6
6
|
};
|
|
7
|
-
import { makeJayComponent, createSignal,
|
|
7
|
+
import { makeJayComponent, createSignal, materializeViewState, COMPONENT_CONTEXT } from "@jay-framework/component";
|
|
8
8
|
import { createJayContext, useContext, currentConstructionContext } from "@jay-framework/runtime";
|
|
9
9
|
function deepMergeViewStates$1(base, overlay, trackByMap, path = "") {
|
|
10
10
|
if (!base && !overlay)
|
|
@@ -128,13 +128,20 @@ function makeHeadlessInstanceComponent(preRender, componentDef, coordinateKey) {
|
|
|
128
128
|
const clientVS = { ...resolvedFastVS, ...originalRender() };
|
|
129
129
|
if (!done) {
|
|
130
130
|
setHydrationDone(true);
|
|
131
|
+
if (instanceData)
|
|
132
|
+
instanceData.viewStates[resolvedKey] = materializeViewState(resolvedFastVS);
|
|
131
133
|
return resolvedFastVS;
|
|
132
134
|
}
|
|
135
|
+
if (instanceData)
|
|
136
|
+
instanceData.viewStates[resolvedKey] = materializeViewState(clientVS);
|
|
133
137
|
return clientVS;
|
|
134
138
|
};
|
|
135
139
|
} else {
|
|
136
140
|
compCore.render = () => {
|
|
137
|
-
|
|
141
|
+
const vs = { ...resolvedFastVS, ...originalRender() };
|
|
142
|
+
if (instanceData)
|
|
143
|
+
instanceData.viewStates[resolvedKey] = materializeViewState(vs);
|
|
144
|
+
return vs;
|
|
138
145
|
};
|
|
139
146
|
}
|
|
140
147
|
return compCore;
|
|
@@ -202,6 +209,10 @@ function makeCompositeJayComponent(preRender, defaultViewState, fastCarryForward
|
|
|
202
209
|
);
|
|
203
210
|
}
|
|
204
211
|
});
|
|
212
|
+
const ivs = instancesData.viewStates;
|
|
213
|
+
if (Object.keys(ivs).length > 0) {
|
|
214
|
+
viewState.__headlessInstances = ivs;
|
|
215
|
+
}
|
|
205
216
|
return viewState;
|
|
206
217
|
}
|
|
207
218
|
};
|
|
@@ -276,6 +287,10 @@ function hydrateCompositeJayComponent(hydratePreRender, defaultViewState, fastCa
|
|
|
276
287
|
);
|
|
277
288
|
}
|
|
278
289
|
});
|
|
290
|
+
const ivs = instancesData.viewStates;
|
|
291
|
+
if (Object.keys(ivs).length > 0) {
|
|
292
|
+
viewState.__headlessInstances = ivs;
|
|
293
|
+
}
|
|
279
294
|
return viewState;
|
|
280
295
|
}
|
|
281
296
|
};
|
|
@@ -315,19 +330,60 @@ let globalOptions = {};
|
|
|
315
330
|
function setActionCallerOptions(options) {
|
|
316
331
|
globalOptions = { ...globalOptions, ...options };
|
|
317
332
|
}
|
|
318
|
-
function
|
|
333
|
+
function buildFormData(input) {
|
|
334
|
+
const formData = new FormData();
|
|
335
|
+
const jsonFields = {};
|
|
336
|
+
for (const [key, value] of Object.entries(input)) {
|
|
337
|
+
if (value instanceof Blob) {
|
|
338
|
+
const name = value instanceof File ? value.name : `${key}.bin`;
|
|
339
|
+
formData.append(key, value, name);
|
|
340
|
+
} else if (Array.isArray(value) && value.some((v) => v instanceof Blob)) {
|
|
341
|
+
const nonFiles = [];
|
|
342
|
+
for (const item of value) {
|
|
343
|
+
if (item instanceof Blob) {
|
|
344
|
+
const name = item instanceof File ? item.name : `${key}.bin`;
|
|
345
|
+
formData.append(key, item, name);
|
|
346
|
+
} else {
|
|
347
|
+
nonFiles.push(item);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
if (nonFiles.length > 0) {
|
|
351
|
+
jsonFields[key] = nonFiles;
|
|
352
|
+
}
|
|
353
|
+
} else {
|
|
354
|
+
jsonFields[key] = value;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
if (Object.keys(jsonFields).length > 0) {
|
|
358
|
+
formData.append("_json", JSON.stringify(jsonFields));
|
|
359
|
+
}
|
|
360
|
+
return formData;
|
|
361
|
+
}
|
|
362
|
+
function hasFiles(input) {
|
|
363
|
+
if (typeof input !== "object" || input === null)
|
|
364
|
+
return false;
|
|
365
|
+
for (const value of Object.values(input)) {
|
|
366
|
+
if (value instanceof Blob)
|
|
367
|
+
return true;
|
|
368
|
+
if (Array.isArray(value) && value.some((v) => v instanceof Blob))
|
|
369
|
+
return true;
|
|
370
|
+
}
|
|
371
|
+
return false;
|
|
372
|
+
}
|
|
373
|
+
function createActionCaller(actionName, method = "POST", options) {
|
|
319
374
|
return async (input) => {
|
|
320
375
|
const baseUrl = globalOptions.baseUrl ?? "";
|
|
321
|
-
const
|
|
376
|
+
const useFormData = options?.acceptsFiles && hasFiles(input);
|
|
377
|
+
const url = useFormData ? `${baseUrl}${ACTION_ENDPOINT_BASE}/${actionName}` : buildActionUrl(baseUrl, actionName, method, input);
|
|
322
378
|
const fetchOptions = {
|
|
323
379
|
method,
|
|
324
380
|
headers: {
|
|
325
|
-
"Content-Type": "application/json",
|
|
381
|
+
...useFormData ? {} : { "Content-Type": "application/json" },
|
|
326
382
|
...globalOptions.headers
|
|
327
383
|
}
|
|
328
384
|
};
|
|
329
385
|
if (method !== "GET") {
|
|
330
|
-
fetchOptions.body = JSON.stringify(input);
|
|
386
|
+
fetchOptions.body = useFormData ? buildFormData(input) : JSON.stringify(input);
|
|
331
387
|
}
|
|
332
388
|
const timeout = globalOptions.timeout ?? 3e4;
|
|
333
389
|
const controller = new AbortController();
|
|
@@ -385,6 +441,113 @@ function buildActionUrl(baseUrl, actionName, method, input) {
|
|
|
385
441
|
}
|
|
386
442
|
return fullUrl;
|
|
387
443
|
}
|
|
444
|
+
function createStreamCaller(actionName, options) {
|
|
445
|
+
return (input) => {
|
|
446
|
+
return {
|
|
447
|
+
[Symbol.asyncIterator]() {
|
|
448
|
+
const baseUrl = globalOptions.baseUrl ?? "";
|
|
449
|
+
const url = `${baseUrl}${ACTION_ENDPOINT_BASE}/${actionName}`;
|
|
450
|
+
const useFormData = options?.acceptsFiles && hasFiles(input);
|
|
451
|
+
let reader = null;
|
|
452
|
+
let buffer = "";
|
|
453
|
+
let done = false;
|
|
454
|
+
let error = null;
|
|
455
|
+
const chunks = [];
|
|
456
|
+
let resolveNext = null;
|
|
457
|
+
const fetchPromise = fetch(url, {
|
|
458
|
+
method: "POST",
|
|
459
|
+
headers: {
|
|
460
|
+
...useFormData ? {} : { "Content-Type": "application/json" },
|
|
461
|
+
...globalOptions.headers
|
|
462
|
+
},
|
|
463
|
+
body: useFormData ? buildFormData(input) : JSON.stringify(input)
|
|
464
|
+
}).then((response) => {
|
|
465
|
+
if (!response.ok) {
|
|
466
|
+
throw new ActionError(
|
|
467
|
+
"STREAM_ERROR",
|
|
468
|
+
`Stream '${actionName}' failed with status ${response.status}`
|
|
469
|
+
);
|
|
470
|
+
}
|
|
471
|
+
reader = response.body.getReader();
|
|
472
|
+
pump();
|
|
473
|
+
}).catch((err) => {
|
|
474
|
+
error = err instanceof ActionError ? err : new ActionError(
|
|
475
|
+
"NETWORK_ERROR",
|
|
476
|
+
`Network error streaming '${actionName}': ${err.message}`
|
|
477
|
+
);
|
|
478
|
+
if (resolveNext)
|
|
479
|
+
resolveNext();
|
|
480
|
+
});
|
|
481
|
+
const decoder = new TextDecoder();
|
|
482
|
+
function pump() {
|
|
483
|
+
reader.read().then(({ done: readerDone, value }) => {
|
|
484
|
+
if (value) {
|
|
485
|
+
buffer += decoder.decode(value, { stream: true });
|
|
486
|
+
const lines = buffer.split("\n");
|
|
487
|
+
buffer = lines.pop();
|
|
488
|
+
for (const line of lines) {
|
|
489
|
+
if (!line.trim())
|
|
490
|
+
continue;
|
|
491
|
+
const parsed = JSON.parse(line);
|
|
492
|
+
if (parsed.error) {
|
|
493
|
+
error = new ActionError("STREAM_ERROR", parsed.error);
|
|
494
|
+
if (resolveNext)
|
|
495
|
+
resolveNext();
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
if (parsed.done) {
|
|
499
|
+
done = true;
|
|
500
|
+
if (resolveNext)
|
|
501
|
+
resolveNext();
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
if ("chunk" in parsed) {
|
|
505
|
+
chunks.push(parsed.chunk);
|
|
506
|
+
if (resolveNext)
|
|
507
|
+
resolveNext();
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (readerDone) {
|
|
512
|
+
done = true;
|
|
513
|
+
if (resolveNext)
|
|
514
|
+
resolveNext();
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
pump();
|
|
518
|
+
}).catch((err) => {
|
|
519
|
+
error = new ActionError("STREAM_ERROR", err.message);
|
|
520
|
+
if (resolveNext)
|
|
521
|
+
resolveNext();
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
return {
|
|
525
|
+
async next() {
|
|
526
|
+
await fetchPromise;
|
|
527
|
+
while (true) {
|
|
528
|
+
if (chunks.length > 0) {
|
|
529
|
+
return { value: chunks.shift(), done: false };
|
|
530
|
+
}
|
|
531
|
+
if (error) {
|
|
532
|
+
throw error;
|
|
533
|
+
}
|
|
534
|
+
if (done) {
|
|
535
|
+
return { value: void 0, done: true };
|
|
536
|
+
}
|
|
537
|
+
await new Promise((resolve) => {
|
|
538
|
+
resolveNext = resolve;
|
|
539
|
+
});
|
|
540
|
+
resolveNext = null;
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
[Symbol.asyncIterator]() {
|
|
544
|
+
return this;
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
};
|
|
549
|
+
};
|
|
550
|
+
}
|
|
388
551
|
function isSimpleObject(obj) {
|
|
389
552
|
if (typeof obj !== "object" || obj === null) {
|
|
390
553
|
return false;
|
|
@@ -589,11 +752,6 @@ class AutomationAgent {
|
|
|
589
752
|
if (options) {
|
|
590
753
|
this.initialSlowViewState = options.initialViewState;
|
|
591
754
|
this.trackByMap = options.trackByMap;
|
|
592
|
-
this.mergedViewState = deepMergeViewStates(
|
|
593
|
-
options.initialViewState,
|
|
594
|
-
this.component.viewState || {},
|
|
595
|
-
options.trackByMap
|
|
596
|
-
);
|
|
597
755
|
}
|
|
598
756
|
this.subscribeToUpdates();
|
|
599
757
|
}
|
|
@@ -601,13 +759,7 @@ class AutomationAgent {
|
|
|
601
759
|
this.viewStateHandler = () => {
|
|
602
760
|
this.cachedRaw = null;
|
|
603
761
|
this.cachedGrouped = null;
|
|
604
|
-
|
|
605
|
-
this.mergedViewState = deepMergeViewStates(
|
|
606
|
-
this.initialSlowViewState,
|
|
607
|
-
this.component.viewState || {},
|
|
608
|
-
this.trackByMap
|
|
609
|
-
);
|
|
610
|
-
}
|
|
762
|
+
this.mergedViewState = null;
|
|
611
763
|
this.notifyListeners();
|
|
612
764
|
};
|
|
613
765
|
this.component.addEventListener(VIEW_STATE_CHANGE, this.viewStateHandler);
|
|
@@ -628,9 +780,22 @@ class AutomationAgent {
|
|
|
628
780
|
return this.cachedGrouped;
|
|
629
781
|
}
|
|
630
782
|
getPageState() {
|
|
783
|
+
let viewState;
|
|
784
|
+
if (this.initialSlowViewState && this.trackByMap) {
|
|
785
|
+
viewState = deepMergeViewStates(
|
|
786
|
+
this.initialSlowViewState,
|
|
787
|
+
this.component.viewState || {},
|
|
788
|
+
this.trackByMap
|
|
789
|
+
);
|
|
790
|
+
const componentInstances = this.component.viewState?.__headlessInstances;
|
|
791
|
+
if (componentInstances) {
|
|
792
|
+
viewState.__headlessInstances = componentInstances;
|
|
793
|
+
}
|
|
794
|
+
} else {
|
|
795
|
+
viewState = this.component.viewState;
|
|
796
|
+
}
|
|
631
797
|
return {
|
|
632
|
-
|
|
633
|
-
viewState: this.mergedViewState || this.component.viewState,
|
|
798
|
+
viewState,
|
|
634
799
|
interactions: this.getGrouped(),
|
|
635
800
|
customEvents: this.getCustomEvents()
|
|
636
801
|
};
|
|
@@ -701,6 +866,7 @@ export {
|
|
|
701
866
|
ActionError,
|
|
702
867
|
HEADLESS_INSTANCES,
|
|
703
868
|
createActionCaller,
|
|
869
|
+
createStreamCaller,
|
|
704
870
|
hydrateCompositeJayComponent,
|
|
705
871
|
makeCompositeJayComponent,
|
|
706
872
|
makeHeadlessInstanceComponent,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/stack-client-runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -27,15 +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/runtime-automation": "^0.
|
|
34
|
-
"@jay-framework/view-state-merge": "^0.
|
|
30
|
+
"@jay-framework/component": "^0.16.1",
|
|
31
|
+
"@jay-framework/fullstack-component": "^0.16.1",
|
|
32
|
+
"@jay-framework/runtime": "^0.16.1",
|
|
33
|
+
"@jay-framework/runtime-automation": "^0.16.1",
|
|
34
|
+
"@jay-framework/view-state-merge": "^0.16.1"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@jay-framework/dev-environment": "^0.
|
|
38
|
-
"@jay-framework/jay-cli": "^0.
|
|
37
|
+
"@jay-framework/dev-environment": "^0.16.1",
|
|
38
|
+
"@jay-framework/jay-cli": "^0.16.1",
|
|
39
39
|
"@types/express": "^5.0.2",
|
|
40
40
|
"@types/node": "^22.15.21",
|
|
41
41
|
"nodemon": "^3.0.3",
|