@jay-framework/stack-client-runtime 0.16.0 → 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 +49 -7
- package/dist/index.d.ts +10 -3
- package/dist/index.js +49 -7
- package/package.json +8 -8
package/dist/index.cjs
CHANGED
|
@@ -332,19 +332,60 @@ let globalOptions = {};
|
|
|
332
332
|
function setActionCallerOptions(options) {
|
|
333
333
|
globalOptions = { ...globalOptions, ...options };
|
|
334
334
|
}
|
|
335
|
-
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) {
|
|
336
376
|
return async (input) => {
|
|
337
377
|
const baseUrl = globalOptions.baseUrl ?? "";
|
|
338
|
-
const
|
|
378
|
+
const useFormData = options?.acceptsFiles && hasFiles(input);
|
|
379
|
+
const url = useFormData ? `${baseUrl}${ACTION_ENDPOINT_BASE}/${actionName}` : buildActionUrl(baseUrl, actionName, method, input);
|
|
339
380
|
const fetchOptions = {
|
|
340
381
|
method,
|
|
341
382
|
headers: {
|
|
342
|
-
"Content-Type": "application/json",
|
|
383
|
+
...useFormData ? {} : { "Content-Type": "application/json" },
|
|
343
384
|
...globalOptions.headers
|
|
344
385
|
}
|
|
345
386
|
};
|
|
346
387
|
if (method !== "GET") {
|
|
347
|
-
fetchOptions.body = JSON.stringify(input);
|
|
388
|
+
fetchOptions.body = useFormData ? buildFormData(input) : JSON.stringify(input);
|
|
348
389
|
}
|
|
349
390
|
const timeout = globalOptions.timeout ?? 3e4;
|
|
350
391
|
const controller = new AbortController();
|
|
@@ -402,12 +443,13 @@ function buildActionUrl(baseUrl, actionName, method, input) {
|
|
|
402
443
|
}
|
|
403
444
|
return fullUrl;
|
|
404
445
|
}
|
|
405
|
-
function createStreamCaller(actionName) {
|
|
446
|
+
function createStreamCaller(actionName, options) {
|
|
406
447
|
return (input) => {
|
|
407
448
|
return {
|
|
408
449
|
[Symbol.asyncIterator]() {
|
|
409
450
|
const baseUrl = globalOptions.baseUrl ?? "";
|
|
410
451
|
const url = `${baseUrl}${ACTION_ENDPOINT_BASE}/${actionName}`;
|
|
452
|
+
const useFormData = options?.acceptsFiles && hasFiles(input);
|
|
411
453
|
let reader = null;
|
|
412
454
|
let buffer = "";
|
|
413
455
|
let done = false;
|
|
@@ -417,10 +459,10 @@ function createStreamCaller(actionName) {
|
|
|
417
459
|
const fetchPromise = fetch(url, {
|
|
418
460
|
method: "POST",
|
|
419
461
|
headers: {
|
|
420
|
-
"Content-Type": "application/json",
|
|
462
|
+
...useFormData ? {} : { "Content-Type": "application/json" },
|
|
421
463
|
...globalOptions.headers
|
|
422
464
|
},
|
|
423
|
-
body: JSON.stringify(input)
|
|
465
|
+
body: useFormData ? buildFormData(input) : JSON.stringify(input)
|
|
424
466
|
}).then((response) => {
|
|
425
467
|
if (!response.ok) {
|
|
426
468
|
throw new ActionError(
|
package/dist/index.d.ts
CHANGED
|
@@ -145,7 +145,14 @@ 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>;
|
|
149
156
|
/**
|
|
150
157
|
* Creates a client-side stream caller that makes an HTTP request and returns
|
|
151
158
|
* an async iterable of chunks via NDJSON streaming.
|
|
@@ -165,6 +172,6 @@ declare function createActionCaller<Input, Output>(actionName: string, method?:
|
|
|
165
172
|
* const checkInventory = createStreamCaller<void, { name: string }>('inventory.check');
|
|
166
173
|
* ```
|
|
167
174
|
*/
|
|
168
|
-
declare function createStreamCaller<Input, Chunk>(actionName: string): (input: Input) => AsyncIterable<Chunk>;
|
|
175
|
+
declare function createStreamCaller<Input, Chunk>(actionName: string, options?: CreateActionCallerOptions): (input: Input) => AsyncIterable<Chunk>;
|
|
169
176
|
|
|
170
|
-
export { type ActionCallerOptions, ActionError, type CompositePart, HEADLESS_INSTANCES, type HeadlessComponentDef, type HeadlessInstancesData, type HttpMethod, createActionCaller, createStreamCaller, 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
|
@@ -330,19 +330,60 @@ let globalOptions = {};
|
|
|
330
330
|
function setActionCallerOptions(options) {
|
|
331
331
|
globalOptions = { ...globalOptions, ...options };
|
|
332
332
|
}
|
|
333
|
-
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) {
|
|
334
374
|
return async (input) => {
|
|
335
375
|
const baseUrl = globalOptions.baseUrl ?? "";
|
|
336
|
-
const
|
|
376
|
+
const useFormData = options?.acceptsFiles && hasFiles(input);
|
|
377
|
+
const url = useFormData ? `${baseUrl}${ACTION_ENDPOINT_BASE}/${actionName}` : buildActionUrl(baseUrl, actionName, method, input);
|
|
337
378
|
const fetchOptions = {
|
|
338
379
|
method,
|
|
339
380
|
headers: {
|
|
340
|
-
"Content-Type": "application/json",
|
|
381
|
+
...useFormData ? {} : { "Content-Type": "application/json" },
|
|
341
382
|
...globalOptions.headers
|
|
342
383
|
}
|
|
343
384
|
};
|
|
344
385
|
if (method !== "GET") {
|
|
345
|
-
fetchOptions.body = JSON.stringify(input);
|
|
386
|
+
fetchOptions.body = useFormData ? buildFormData(input) : JSON.stringify(input);
|
|
346
387
|
}
|
|
347
388
|
const timeout = globalOptions.timeout ?? 3e4;
|
|
348
389
|
const controller = new AbortController();
|
|
@@ -400,12 +441,13 @@ function buildActionUrl(baseUrl, actionName, method, input) {
|
|
|
400
441
|
}
|
|
401
442
|
return fullUrl;
|
|
402
443
|
}
|
|
403
|
-
function createStreamCaller(actionName) {
|
|
444
|
+
function createStreamCaller(actionName, options) {
|
|
404
445
|
return (input) => {
|
|
405
446
|
return {
|
|
406
447
|
[Symbol.asyncIterator]() {
|
|
407
448
|
const baseUrl = globalOptions.baseUrl ?? "";
|
|
408
449
|
const url = `${baseUrl}${ACTION_ENDPOINT_BASE}/${actionName}`;
|
|
450
|
+
const useFormData = options?.acceptsFiles && hasFiles(input);
|
|
409
451
|
let reader = null;
|
|
410
452
|
let buffer = "";
|
|
411
453
|
let done = false;
|
|
@@ -415,10 +457,10 @@ function createStreamCaller(actionName) {
|
|
|
415
457
|
const fetchPromise = fetch(url, {
|
|
416
458
|
method: "POST",
|
|
417
459
|
headers: {
|
|
418
|
-
"Content-Type": "application/json",
|
|
460
|
+
...useFormData ? {} : { "Content-Type": "application/json" },
|
|
419
461
|
...globalOptions.headers
|
|
420
462
|
},
|
|
421
|
-
body: JSON.stringify(input)
|
|
463
|
+
body: useFormData ? buildFormData(input) : JSON.stringify(input)
|
|
422
464
|
}).then((response) => {
|
|
423
465
|
if (!response.ok) {
|
|
424
466
|
throw new ActionError(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jay-framework/stack-client-runtime",
|
|
3
|
-
"version": "0.16.
|
|
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.16.
|
|
31
|
-
"@jay-framework/fullstack-component": "^0.16.
|
|
32
|
-
"@jay-framework/runtime": "^0.16.
|
|
33
|
-
"@jay-framework/runtime-automation": "^0.16.
|
|
34
|
-
"@jay-framework/view-state-merge": "^0.16.
|
|
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.16.
|
|
38
|
-
"@jay-framework/jay-cli": "^0.16.
|
|
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",
|