@alexkroman1/aai-ui 6.8.0 → 6.10.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/_upload-files.d.ts +19 -3
- package/dist/_upload-recall.d.ts +67 -0
- package/dist/hooks.d.ts +37 -1
- package/dist/hooks.js +4 -1
- package/dist/index.js +197 -9
- package/package.json +2 -2
package/dist/_upload-files.d.ts
CHANGED
|
@@ -27,17 +27,33 @@ import type { WorkflowApi } from "./workflow-client.ts";
|
|
|
27
27
|
* minted per attempt would make each round a fresh upload of the whole file.
|
|
28
28
|
*/
|
|
29
29
|
export type UploadSession = {
|
|
30
|
+
/**
|
|
31
|
+
* What this form's remembered ids are filed under (`_upload-recall.ts`).
|
|
32
|
+
*
|
|
33
|
+
* The workflow's name, so two forms on one page do not read each other's
|
|
34
|
+
* entries. It is not a SAFETY boundary and does not need to be — a recalled id
|
|
35
|
+
* is checked against the agent before anything is sent to it — it just keeps a
|
|
36
|
+
* form from spending a round trip on another form's upload.
|
|
37
|
+
*/
|
|
38
|
+
scope: string;
|
|
30
39
|
/** The id each file is being stored under, minted once. */
|
|
31
40
|
ids: Map<File, string>;
|
|
32
41
|
/** Files whose every byte has landed, by the id they landed under. */
|
|
33
42
|
stored: Map<File, string>;
|
|
34
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Files that have had an attempt, so the next one must claim the id as its own.
|
|
45
|
+
*
|
|
46
|
+
* `sendThroughGate` tracks this itself WITHIN one file's attempts. What this
|
|
47
|
+
* carries is the attempt made by a page load that is gone: an id recalled from
|
|
48
|
+
* storage was claimed by whoever minted it, so the first attempt of this load is
|
|
49
|
+
* a resume even though this load has sent nothing.
|
|
50
|
+
*/
|
|
35
51
|
tried: Set<File>;
|
|
36
52
|
/** The person's pause. */
|
|
37
53
|
gate: UploadGate;
|
|
38
54
|
};
|
|
39
|
-
/** A fresh session for one submission
|
|
40
|
-
export declare function createUploadSession(): UploadSession;
|
|
55
|
+
/** A fresh session for one submission of `workflow`. */
|
|
56
|
+
export declare function createUploadSession(workflow: string): UploadSession;
|
|
41
57
|
/**
|
|
42
58
|
* Replace every `File` in a submitted form with the id of a stored upload,
|
|
43
59
|
* reporting how far each one has got.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an upload's ID survives a page RELOAD.
|
|
3
|
+
*
|
|
4
|
+
* A streamed upload is resumable because its id outlives the attempt that began
|
|
5
|
+
* it — `_upload-files.ts` says so, and `_upload-session.ts` turns that into a
|
|
6
|
+
* pause a person can press. Both of them hold the id in MEMORY: the walk's
|
|
7
|
+
* `UploadSession` lives in a `useRef`, so a reload was the one interruption the
|
|
8
|
+
* mechanism could not survive. Everything else was already in place — the windows
|
|
9
|
+
* were still in the store, the agent could still name them
|
|
10
|
+
* (`UploadInfo.ranges`), and the id was minted in the browser — and the browser
|
|
11
|
+
* had thrown away the only name for them. So a person who refreshed at 90% of a
|
|
12
|
+
* 200 MB recording sent the whole file again, which is the one interruption they
|
|
13
|
+
* are most likely to cause on purpose.
|
|
14
|
+
*
|
|
15
|
+
* This is that name, written down. It is what tus-js-client's `urlStorage` and
|
|
16
|
+
* Uppy's Golden Retriever sell, in the shape `session-resume-store.ts` already
|
|
17
|
+
* uses for a session id.
|
|
18
|
+
*
|
|
19
|
+
* ## A FINGERPRINT, because a `File` has no name a page can address
|
|
20
|
+
*
|
|
21
|
+
* A file from a picker carries no path and no handle, so the key is what
|
|
22
|
+
* tus-js-client fingerprints on: size, last-modified, type and name. Two
|
|
23
|
+
* different files agreeing on all four is the case this cannot tell apart — and
|
|
24
|
+
* the reason NOTHING here decides to resume. `_upload-files.ts` asks the agent
|
|
25
|
+
* what the id actually holds before sending a byte to it, so a wrong hit costs
|
|
26
|
+
* one `GET` and a fresh id rather than a corrupted upload.
|
|
27
|
+
*
|
|
28
|
+
* ## `sessionStorage`, deliberately
|
|
29
|
+
*
|
|
30
|
+
* The same call `session-resume-store.ts` makes, for a reason that happens to be
|
|
31
|
+
* stronger here: a reload and a same-tab navigation are exactly what this is for,
|
|
32
|
+
* and an id from yesterday names an upload the agent's sweep has very likely
|
|
33
|
+
* already collected. A tab is also the boundary the walk itself has — two tabs
|
|
34
|
+
* uploading the same recording are two submissions.
|
|
35
|
+
*
|
|
36
|
+
* Every access is guarded. Storage throws outright in Safari private mode and
|
|
37
|
+
* under a blocking policy, and an upload that cannot be REMEMBERED must degrade
|
|
38
|
+
* to the upload we would have done anyway rather than failing to start.
|
|
39
|
+
*/
|
|
40
|
+
/**
|
|
41
|
+
* The id this file was last being stored under in this tab, if any.
|
|
42
|
+
*
|
|
43
|
+
* A hit is a CANDIDATE and never a decision — see the module doc.
|
|
44
|
+
*
|
|
45
|
+
* @internal
|
|
46
|
+
*/
|
|
47
|
+
export declare function recallUploadId(scope: string, file: File): string | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* Remember the id this file is being stored under.
|
|
50
|
+
*
|
|
51
|
+
* Called before the first byte leaves rather than after the last one lands: the
|
|
52
|
+
* reload this exists for happens in between, and an id written at the end is an
|
|
53
|
+
* id written for the one case that did not need it.
|
|
54
|
+
*
|
|
55
|
+
* @internal
|
|
56
|
+
*/
|
|
57
|
+
export declare function rememberUploadId(scope: string, file: File, id: string): void;
|
|
58
|
+
/**
|
|
59
|
+
* Forget it: the agent holds nothing resumable under this id.
|
|
60
|
+
*
|
|
61
|
+
* The other half of the agent deciding. Without it a swept upload is re-read on
|
|
62
|
+
* every submission of the same file for the life of the tab, which is a round
|
|
63
|
+
* trip spent learning the same 404.
|
|
64
|
+
*
|
|
65
|
+
* @internal
|
|
66
|
+
*/
|
|
67
|
+
export declare function forgetUploadId(scope: string, file: File): void;
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DefaultToolResult } from "@alexkroman1/aai";
|
|
1
|
+
import type { DefaultToolResult, StateProjection } from "@alexkroman1/aai";
|
|
2
2
|
import type { ToolCallInfo } from "./types.ts";
|
|
3
3
|
/**
|
|
4
4
|
* Fire a callback when a tool call settles, with the tool's JSON result.
|
|
@@ -39,6 +39,42 @@ export declare function useToolResult<R = DefaultToolResult>(callback: (name: st
|
|
|
39
39
|
* @public
|
|
40
40
|
*/
|
|
41
41
|
export declare function useAgentState<S = DefaultToolResult>(): S | null;
|
|
42
|
+
/**
|
|
43
|
+
* The agent's projected session state, typed and defaulted by the SAME
|
|
44
|
+
* projection the agent pushes — pass `slot.projection(view)` and there is no
|
|
45
|
+
* type argument to restate and no empty frame to derive.
|
|
46
|
+
*
|
|
47
|
+
* This is the overload to reach for whenever `syncState` is a slot projection,
|
|
48
|
+
* because it closes the round-trip the other two leave open. A projection is
|
|
49
|
+
* callable, so the pre-first-push frame is what `projection()` returns — the
|
|
50
|
+
* `fallback` overload's own doc tells you to build it that way — and the
|
|
51
|
+
* projection's return type is the state's type, so `useAgentState<CartView>`
|
|
52
|
+
* was restating what `cartView` already knew. Both halves came out of the same
|
|
53
|
+
* declaration and both were written by hand:
|
|
54
|
+
*
|
|
55
|
+
* ```tsx no-check
|
|
56
|
+
* // `no-check`: the projection lives with the agent, in another file.
|
|
57
|
+
* // Before — the empty frame derived by hand, the type named three times:
|
|
58
|
+
* const EMPTY: CartView = cartSlot.projection(cartView)(undefined);
|
|
59
|
+
* const cart = useAgentState<CartView>(EMPTY);
|
|
60
|
+
*
|
|
61
|
+
* // After — `shared.ts` exports the projection once, both ends import it:
|
|
62
|
+
* const cart = useAgentState(cartProjection);
|
|
63
|
+
* ```
|
|
64
|
+
*
|
|
65
|
+
* The empty frame is memoized on the projection's identity, so a module-scope
|
|
66
|
+
* projection (the normal case) produces ONE frame for the life of the
|
|
67
|
+
* component — which the `fallback` overload can only ask you to arrange by
|
|
68
|
+
* hoisting, and which a `slot.projection(view)` spelled inline in the render
|
|
69
|
+
* body silently got wrong.
|
|
70
|
+
*
|
|
71
|
+
* @param projection - The same `slot.projection(view)` the agent declares as
|
|
72
|
+
* `syncState`. Export it from the module that declares the slot so the two
|
|
73
|
+
* ends cannot drift.
|
|
74
|
+
*
|
|
75
|
+
* @public
|
|
76
|
+
*/
|
|
77
|
+
export declare function useAgentState<V>(projection: StateProjection<V>): V;
|
|
42
78
|
/**
|
|
43
79
|
* The agent's projected session state, falling back to `fallback` before the
|
|
44
80
|
* first push — so the return is never `null` and a sidebar needs no branch for
|
package/dist/hooks.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useSessionSelector } from "./context.js";
|
|
2
2
|
import { i as tryParseJSON } from "./_utils-B6498_bm.js";
|
|
3
|
-
import { useEffect, useRef } from "react";
|
|
3
|
+
import { useEffect, useMemo, useRef } from "react";
|
|
4
4
|
//#region hooks.ts
|
|
5
5
|
/** Index of the first item whose sequence number is above the watermark (tail scan from the end). */
|
|
6
6
|
function tailStart(items, seqOf, watermark) {
|
|
@@ -80,7 +80,10 @@ function useToolResult(...args) {
|
|
|
80
80
|
}
|
|
81
81
|
function useAgentState(fallback) {
|
|
82
82
|
const state = useSessionSelector((snapshot) => snapshot.agentState);
|
|
83
|
+
const isProjection = typeof fallback === "function";
|
|
84
|
+
const projected = useMemo(() => isProjection ? fallback() : void 0, [fallback, isProjection]);
|
|
83
85
|
if (state !== null) return state;
|
|
86
|
+
if (isProjection) return projected;
|
|
84
87
|
return fallback === void 0 ? null : fallback;
|
|
85
88
|
}
|
|
86
89
|
/**
|
package/dist/index.js
CHANGED
|
@@ -675,6 +675,131 @@ function UploadProgressBar({ upload, onPause, onResume, className }) {
|
|
|
675
675
|
});
|
|
676
676
|
}
|
|
677
677
|
//#endregion
|
|
678
|
+
//#region _upload-recall.ts
|
|
679
|
+
/**
|
|
680
|
+
* Where an upload's ID survives a page RELOAD.
|
|
681
|
+
*
|
|
682
|
+
* A streamed upload is resumable because its id outlives the attempt that began
|
|
683
|
+
* it — `_upload-files.ts` says so, and `_upload-session.ts` turns that into a
|
|
684
|
+
* pause a person can press. Both of them hold the id in MEMORY: the walk's
|
|
685
|
+
* `UploadSession` lives in a `useRef`, so a reload was the one interruption the
|
|
686
|
+
* mechanism could not survive. Everything else was already in place — the windows
|
|
687
|
+
* were still in the store, the agent could still name them
|
|
688
|
+
* (`UploadInfo.ranges`), and the id was minted in the browser — and the browser
|
|
689
|
+
* had thrown away the only name for them. So a person who refreshed at 90% of a
|
|
690
|
+
* 200 MB recording sent the whole file again, which is the one interruption they
|
|
691
|
+
* are most likely to cause on purpose.
|
|
692
|
+
*
|
|
693
|
+
* This is that name, written down. It is what tus-js-client's `urlStorage` and
|
|
694
|
+
* Uppy's Golden Retriever sell, in the shape `session-resume-store.ts` already
|
|
695
|
+
* uses for a session id.
|
|
696
|
+
*
|
|
697
|
+
* ## A FINGERPRINT, because a `File` has no name a page can address
|
|
698
|
+
*
|
|
699
|
+
* A file from a picker carries no path and no handle, so the key is what
|
|
700
|
+
* tus-js-client fingerprints on: size, last-modified, type and name. Two
|
|
701
|
+
* different files agreeing on all four is the case this cannot tell apart — and
|
|
702
|
+
* the reason NOTHING here decides to resume. `_upload-files.ts` asks the agent
|
|
703
|
+
* what the id actually holds before sending a byte to it, so a wrong hit costs
|
|
704
|
+
* one `GET` and a fresh id rather than a corrupted upload.
|
|
705
|
+
*
|
|
706
|
+
* ## `sessionStorage`, deliberately
|
|
707
|
+
*
|
|
708
|
+
* The same call `session-resume-store.ts` makes, for a reason that happens to be
|
|
709
|
+
* stronger here: a reload and a same-tab navigation are exactly what this is for,
|
|
710
|
+
* and an id from yesterday names an upload the agent's sweep has very likely
|
|
711
|
+
* already collected. A tab is also the boundary the walk itself has — two tabs
|
|
712
|
+
* uploading the same recording are two submissions.
|
|
713
|
+
*
|
|
714
|
+
* Every access is guarded. Storage throws outright in Safari private mode and
|
|
715
|
+
* under a blocking policy, and an upload that cannot be REMEMBERED must degrade
|
|
716
|
+
* to the upload we would have done anyway rather than failing to start.
|
|
717
|
+
*/
|
|
718
|
+
const PREFIX = "aai:upload:";
|
|
719
|
+
/**
|
|
720
|
+
* How many ids one form keeps.
|
|
721
|
+
*
|
|
722
|
+
* A cap rather than an expiry, because `sessionStorage` already expires with the
|
|
723
|
+
* tab and an entry is ~80 bytes. What it bounds is the long-lived tab that
|
|
724
|
+
* submits a hundred files: the oldest go first, and the id most likely to be
|
|
725
|
+
* worth resuming is the one written last.
|
|
726
|
+
*/
|
|
727
|
+
const MAX_REMEMBERED = 32;
|
|
728
|
+
/** One form's slot in storage. */
|
|
729
|
+
function keyFor(scope) {
|
|
730
|
+
return `${PREFIX}${scope}`;
|
|
731
|
+
}
|
|
732
|
+
/**
|
|
733
|
+
* What names this file across a reload.
|
|
734
|
+
*
|
|
735
|
+
* The four fields a browser gives a picked file that do not change between loads.
|
|
736
|
+
* `name` last because it is the one a person can read in a debugger.
|
|
737
|
+
*/
|
|
738
|
+
function fingerprint(file) {
|
|
739
|
+
return `${file.size}:${file.lastModified}:${file.type}:${file.name}`;
|
|
740
|
+
}
|
|
741
|
+
/** This scope's remembered ids, or nothing at all — a parse failure is nothing. */
|
|
742
|
+
function read(scope) {
|
|
743
|
+
try {
|
|
744
|
+
const raw = globalThis.sessionStorage?.getItem(keyFor(scope));
|
|
745
|
+
if (raw === null || raw === void 0) return {};
|
|
746
|
+
const parsed = JSON.parse(raw);
|
|
747
|
+
return isRecord(parsed) ? parsed : {};
|
|
748
|
+
} catch {
|
|
749
|
+
return {};
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
function write(scope, entries) {
|
|
753
|
+
try {
|
|
754
|
+
globalThis.sessionStorage?.setItem(keyFor(scope), JSON.stringify(entries));
|
|
755
|
+
} catch {}
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* The id this file was last being stored under in this tab, if any.
|
|
759
|
+
*
|
|
760
|
+
* A hit is a CANDIDATE and never a decision — see the module doc.
|
|
761
|
+
*
|
|
762
|
+
* @internal
|
|
763
|
+
*/
|
|
764
|
+
function recallUploadId(scope, file) {
|
|
765
|
+
const found = read(scope)[fingerprint(file)];
|
|
766
|
+
return typeof found === "string" ? found : void 0;
|
|
767
|
+
}
|
|
768
|
+
/**
|
|
769
|
+
* Remember the id this file is being stored under.
|
|
770
|
+
*
|
|
771
|
+
* Called before the first byte leaves rather than after the last one lands: the
|
|
772
|
+
* reload this exists for happens in between, and an id written at the end is an
|
|
773
|
+
* id written for the one case that did not need it.
|
|
774
|
+
*
|
|
775
|
+
* @internal
|
|
776
|
+
*/
|
|
777
|
+
function rememberUploadId(scope, file, id) {
|
|
778
|
+
const entries = read(scope);
|
|
779
|
+
const key = fingerprint(file);
|
|
780
|
+
delete entries[key];
|
|
781
|
+
entries[key] = id;
|
|
782
|
+
const keys = Object.keys(entries);
|
|
783
|
+
for (const stale of keys.slice(0, Math.max(0, keys.length - MAX_REMEMBERED))) delete entries[stale];
|
|
784
|
+
write(scope, entries);
|
|
785
|
+
}
|
|
786
|
+
/**
|
|
787
|
+
* Forget it: the agent holds nothing resumable under this id.
|
|
788
|
+
*
|
|
789
|
+
* The other half of the agent deciding. Without it a swept upload is re-read on
|
|
790
|
+
* every submission of the same file for the life of the tab, which is a round
|
|
791
|
+
* trip spent learning the same 404.
|
|
792
|
+
*
|
|
793
|
+
* @internal
|
|
794
|
+
*/
|
|
795
|
+
function forgetUploadId(scope, file) {
|
|
796
|
+
const entries = read(scope);
|
|
797
|
+
const key = fingerprint(file);
|
|
798
|
+
if (!(key in entries)) return;
|
|
799
|
+
delete entries[key];
|
|
800
|
+
write(scope, entries);
|
|
801
|
+
}
|
|
802
|
+
//#endregion
|
|
678
803
|
//#region _upload-session.ts
|
|
679
804
|
/** Whether this rejection is an abort, in either of the two shapes runtimes throw. */
|
|
680
805
|
function isAbortError(err) {
|
|
@@ -822,9 +947,10 @@ function fileFields(input) {
|
|
|
822
947
|
* one file rather than walking an input and shares only the gate underneath both
|
|
823
948
|
* (`_upload-session.ts`).
|
|
824
949
|
*/
|
|
825
|
-
/** A fresh session for one submission
|
|
826
|
-
function createUploadSession() {
|
|
950
|
+
/** A fresh session for one submission of `workflow`. */
|
|
951
|
+
function createUploadSession(workflow) {
|
|
827
952
|
return {
|
|
953
|
+
scope: workflow,
|
|
828
954
|
ids: /* @__PURE__ */ new Map(),
|
|
829
955
|
stored: /* @__PURE__ */ new Map(),
|
|
830
956
|
tried: /* @__PURE__ */ new Set(),
|
|
@@ -832,6 +958,52 @@ function createUploadSession() {
|
|
|
832
958
|
};
|
|
833
959
|
}
|
|
834
960
|
/**
|
|
961
|
+
* The id to store this file under: the one a previous page load was using, or a
|
|
962
|
+
* fresh one.
|
|
963
|
+
*
|
|
964
|
+
* **The AGENT decides, never the fingerprint.** Two files can agree on every
|
|
965
|
+
* field `_upload-recall.ts` keys by, so the recalled id is a candidate that has to
|
|
966
|
+
* be checked before a byte is sent to it — and the check is cheap and exact,
|
|
967
|
+
* because `uploadInfo` is the same record a resume already reads.
|
|
968
|
+
*
|
|
969
|
+
* Three answers, and the third is why this is not just a storage lookup:
|
|
970
|
+
*
|
|
971
|
+
* - **Complete.** Every byte is in from a load that is gone, so there is nothing
|
|
972
|
+
* to send: the caller takes the id and starts the run. This is the refresh that
|
|
973
|
+
* costs one `GET` instead of a second 200 MB upload.
|
|
974
|
+
* - **Unfinished, with windows.** `UploadInfo.ranges` is what makes an
|
|
975
|
+
* upload resumable at all, so the id is reused and the attempt claims it.
|
|
976
|
+
* - **Anything else.** A 404 (swept, or never seen), a failure, or an unfinished
|
|
977
|
+
* upload reporting NO windows — which is a partial single `PUT`, and a second
|
|
978
|
+
* `PUT` to that id is a 409 rather than an append (`streamUploadFile`). Reusing
|
|
979
|
+
* it would turn a reload into a failure the person cannot clear, so the entry is
|
|
980
|
+
* dropped and the file gets a fresh id.
|
|
981
|
+
*/
|
|
982
|
+
async function claimId(api, session, file) {
|
|
983
|
+
const remembered = recallUploadId(session.scope, file);
|
|
984
|
+
if (remembered === void 0) return {
|
|
985
|
+
id: randomUploadId(),
|
|
986
|
+
complete: false
|
|
987
|
+
};
|
|
988
|
+
const info = await api.uploadInfo(remembered).catch(() => void 0);
|
|
989
|
+
if (info?.complete === true) return {
|
|
990
|
+
id: remembered,
|
|
991
|
+
complete: true
|
|
992
|
+
};
|
|
993
|
+
if (info !== void 0 && (info.ranges?.length ?? 0) > 0) {
|
|
994
|
+
session.tried.add(file);
|
|
995
|
+
return {
|
|
996
|
+
id: remembered,
|
|
997
|
+
complete: false
|
|
998
|
+
};
|
|
999
|
+
}
|
|
1000
|
+
forgetUploadId(session.scope, file);
|
|
1001
|
+
return {
|
|
1002
|
+
id: randomUploadId(),
|
|
1003
|
+
complete: false
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
835
1007
|
* Replace every `File` in a submitted form with the id of a stored upload,
|
|
836
1008
|
* reporting how far each one has got.
|
|
837
1009
|
*
|
|
@@ -866,16 +1038,32 @@ async function uploadFiles(api, input, report, parallel, session) {
|
|
|
866
1038
|
index += 1;
|
|
867
1039
|
const done = session.stored.get(file);
|
|
868
1040
|
if (done !== void 0) return done;
|
|
869
|
-
let id = session.ids.get(file);
|
|
870
|
-
if (id === void 0) {
|
|
871
|
-
id = randomUploadId();
|
|
872
|
-
session.ids.set(file, id);
|
|
873
|
-
}
|
|
874
1041
|
const position = {
|
|
875
1042
|
name: file.name,
|
|
876
1043
|
index,
|
|
877
1044
|
count
|
|
878
1045
|
};
|
|
1046
|
+
const known = session.ids.get(file);
|
|
1047
|
+
const claimed = known === void 0 ? await claimId(api, session, file) : {
|
|
1048
|
+
id: known,
|
|
1049
|
+
complete: false
|
|
1050
|
+
};
|
|
1051
|
+
const id = claimed.id;
|
|
1052
|
+
if (known === void 0) {
|
|
1053
|
+
session.ids.set(file, id);
|
|
1054
|
+
rememberUploadId(session.scope, file, id);
|
|
1055
|
+
}
|
|
1056
|
+
if (claimed.complete) {
|
|
1057
|
+
report({
|
|
1058
|
+
...position,
|
|
1059
|
+
loaded: file.size,
|
|
1060
|
+
total: file.size,
|
|
1061
|
+
fraction: 1,
|
|
1062
|
+
paused: false
|
|
1063
|
+
});
|
|
1064
|
+
session.stored.set(file, id);
|
|
1065
|
+
return id;
|
|
1066
|
+
}
|
|
879
1067
|
await sendThroughGate(session.gate, async (resume) => {
|
|
880
1068
|
await api.uploadStream(id, file, {
|
|
881
1069
|
name: file.name,
|
|
@@ -887,7 +1075,7 @@ async function uploadFiles(api, input, report, parallel, session) {
|
|
|
887
1075
|
}),
|
|
888
1076
|
...omitUndefined({
|
|
889
1077
|
parallel,
|
|
890
|
-
resume: resume ? true : void 0
|
|
1078
|
+
resume: resume || session.tried.has(file) ? true : void 0
|
|
891
1079
|
})
|
|
892
1080
|
});
|
|
893
1081
|
});
|
|
@@ -1427,7 +1615,7 @@ function useWorkflowSubmit(workflow, opts = {}) {
|
|
|
1427
1615
|
setStartError(void 0);
|
|
1428
1616
|
setRunId(void 0);
|
|
1429
1617
|
session.current?.gate.cancel();
|
|
1430
|
-
const current = createUploadSession();
|
|
1618
|
+
const current = createUploadSession(workflow);
|
|
1431
1619
|
session.current = current;
|
|
1432
1620
|
try {
|
|
1433
1621
|
const options = omitUndefined({ key });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alexkroman1/aai-ui",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.10.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"remark-gfm": "^4.0.1",
|
|
30
30
|
"use-stick-to-bottom": "^1.1.6",
|
|
31
31
|
"use-sync-external-store": "^1.6.0",
|
|
32
|
-
"@alexkroman1/aai": "6.
|
|
32
|
+
"@alexkroman1/aai": "6.10.0"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
35
|
"react": "^19.0.0",
|