@assistant-ui/core 0.3.17 → 0.3.18
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/adapters/attachment.d.ts.map +1 -1
- package/dist/adapters/attachment.js +5 -4
- package/dist/adapters/attachment.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +3 -2
- package/dist/internal.js +3 -2
- package/dist/model-context/frame/host.d.ts.map +1 -1
- package/dist/model-context/frame/host.js +10 -4
- package/dist/model-context/frame/host.js.map +1 -1
- package/dist/model-context/frame/provider.d.ts +2 -0
- package/dist/model-context/frame/provider.d.ts.map +1 -1
- package/dist/model-context/frame/provider.js +30 -7
- package/dist/model-context/frame/provider.js.map +1 -1
- package/dist/model-context/interactable-composer-metadata.d.ts.map +1 -1
- package/dist/model-context/interactable-composer-metadata.js +16 -9
- package/dist/model-context/interactable-composer-metadata.js.map +1 -1
- package/dist/model-context/registry.d.ts.map +1 -1
- package/dist/model-context/registry.js +3 -5
- package/dist/model-context/registry.js.map +1 -1
- package/dist/model-context/types.d.ts.map +1 -1
- package/dist/model-context/types.js +5 -4
- package/dist/model-context/types.js.map +1 -1
- package/dist/react/client/InMemoryThreadList.d.ts.map +1 -1
- package/dist/react/client/InMemoryThreadList.js +62 -56
- package/dist/react/client/InMemoryThreadList.js.map +1 -1
- package/dist/react/client/Interactables.d.ts.map +1 -1
- package/dist/react/client/Interactables.js +24 -27
- package/dist/react/client/Interactables.js.map +1 -1
- package/dist/react/client/RemoteThreadList.d.ts +1 -0
- package/dist/react/client/RemoteThreadList.d.ts.map +1 -1
- package/dist/react/client/RemoteThreadList.js +151 -141
- package/dist/react/client/RemoteThreadList.js.map +1 -1
- package/dist/react/client/Tools.d.ts.map +1 -1
- package/dist/react/client/Tools.js +18 -16
- package/dist/react/client/Tools.js.map +1 -1
- package/dist/react/interactables-legacy/Interactables.js +242 -121
- package/dist/react/interactables-legacy/Interactables.js.map +1 -1
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.d.ts.map +1 -1
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.js +11 -9
- package/dist/react/interactables-shared/useInteractablePersistenceQueue.js.map +1 -1
- package/dist/react/model-context/makeAssistantTool.d.ts +2 -2
- package/dist/react/model-context/makeAssistantTool.js +1 -1
- package/dist/react/model-context/makeAssistantTool.js.map +1 -1
- package/dist/react/model-context/useAssistantTool.d.ts +2 -2
- package/dist/react/model-context/useAssistantTool.js +1 -1
- package/dist/react/model-context/useAssistantTool.js.map +1 -1
- package/dist/react/model-context/useToolArgsStatus.d.ts.map +1 -1
- package/dist/react/model-context/useToolArgsStatus.js +2 -1
- package/dist/react/model-context/useToolArgsStatus.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js +16 -8
- package/dist/react/runtimes/RemoteThreadListHookInstanceManager.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts +5 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js +103 -103
- package/dist/react/runtimes/RemoteThreadListThreadListRuntimeCore.js.map +1 -1
- package/dist/react/runtimes/RemoteThreadResource.d.ts +1 -0
- package/dist/react/runtimes/RemoteThreadResource.d.ts.map +1 -1
- package/dist/react/runtimes/RemoteThreadResource.js +13 -11
- package/dist/react/runtimes/RemoteThreadResource.js.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts +4 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js +401 -34
- package/dist/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.js.map +1 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.d.ts.map +1 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js +2 -1
- package/dist/react/runtimes/cloud/createCloudThreadListAdapter.js.map +1 -1
- package/dist/react/runtimes/useExternalStoreRuntime.js +75 -46
- package/dist/react/runtimes/useExternalStoreRuntime.js.map +1 -1
- package/dist/react/runtimes/useRemoteThreadListRuntime.d.ts.map +1 -1
- package/dist/react/runtimes/useRemoteThreadListRuntime.js +63 -23
- package/dist/react/runtimes/useRemoteThreadListRuntime.js.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.d.ts +7 -3
- package/dist/runtime/api/thread-list-item-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-item-runtime.js +2 -2
- package/dist/runtime/api/thread-list-item-runtime.js.map +1 -1
- package/dist/runtime/api/thread-list-runtime.d.ts +3 -0
- package/dist/runtime/api/thread-list-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-list-runtime.js +18 -14
- package/dist/runtime/api/thread-list-runtime.js.map +1 -1
- package/dist/runtime/api/thread-runtime.d.ts +4 -0
- package/dist/runtime/api/thread-runtime.d.ts.map +1 -1
- package/dist/runtime/api/thread-runtime.js +1 -1
- package/dist/runtime/api/thread-runtime.js.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-composer-runtime-core.js +16 -12
- package/dist/runtime/base/base-composer-runtime-core.js.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.d.ts.map +1 -1
- package/dist/runtime/base/base-thread-runtime-core.js +6 -5
- package/dist/runtime/base/base-thread-runtime-core.js.map +1 -1
- package/dist/runtime/interfaces/composer-runtime-core.d.ts +12 -5
- package/dist/runtime/interfaces/composer-runtime-core.d.ts.map +1 -1
- package/dist/runtime/interfaces/thread-list-runtime-core.d.ts +4 -1
- package/dist/runtime/interfaces/thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtime/utils/external-message-conversion.js +1 -1
- package/dist/runtime/utils/external-message-conversion.js.map +1 -1
- package/dist/runtime/utils/message-repository.d.ts +1 -0
- package/dist/runtime/utils/message-repository.d.ts.map +1 -1
- package/dist/runtime/utils/message-repository.js +15 -6
- package/dist/runtime/utils/message-repository.js.map +1 -1
- package/dist/runtime/utils/thread-message-like.js +1 -1
- package/dist/runtime/utils/thread-message-like.js.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.d.ts.map +1 -1
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js +21 -27
- package/dist/runtimes/external-store/external-store-thread-list-runtime-core.js.map +1 -1
- package/dist/runtimes/internal.d.ts +2 -2
- package/dist/runtimes/internal.js +2 -2
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts +18 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/remote-thread-state.js +130 -42
- package/dist/runtimes/remote-thread-list/remote-thread-state.js.map +1 -1
- package/dist/runtimes/remote-thread-list/title-generation.d.ts +49 -0
- package/dist/runtimes/remote-thread-list/title-generation.d.ts.map +1 -0
- package/dist/runtimes/remote-thread-list/title-generation.js +197 -0
- package/dist/runtimes/remote-thread-list/title-generation.js.map +1 -0
- package/dist/runtimes/remote-thread-list/title.d.ts +3 -1
- package/dist/runtimes/remote-thread-list/title.d.ts.map +1 -1
- package/dist/runtimes/remote-thread-list/title.js +9 -1
- package/dist/runtimes/remote-thread-list/title.js.map +1 -1
- package/dist/runtimes/remote-thread-list/types.d.ts +10 -0
- package/dist/runtimes/remote-thread-list/types.d.ts.map +1 -1
- package/dist/store/clients/external-thread.d.ts.map +1 -1
- package/dist/store/clients/external-thread.js +19 -3
- package/dist/store/clients/external-thread.js.map +1 -1
- package/dist/store/clients/model-context-client.d.ts.map +1 -1
- package/dist/store/clients/model-context-client.js +2 -1
- package/dist/store/clients/model-context-client.js.map +1 -1
- package/dist/store/clients/single-thread-list.d.ts.map +1 -1
- package/dist/store/clients/single-thread-list.js +1 -0
- package/dist/store/clients/single-thread-list.js.map +1 -1
- package/dist/store/clients/suggestions.d.ts.map +1 -1
- package/dist/store/clients/suggestions.js +5 -4
- package/dist/store/clients/suggestions.js.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.d.ts +1 -0
- package/dist/store/runtime-clients/composer-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/composer-runtime-client.js +97 -61
- package/dist/store/runtime-clients/composer-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/message-runtime-client.d.ts +1 -0
- package/dist/store/runtime-clients/message-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/message-runtime-client.js +221 -144
- package/dist/store/runtime-clients/message-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/thread-list-item-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-list-item-runtime-client.js +1 -1
- package/dist/store/runtime-clients/thread-list-item-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/thread-list-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-list-runtime-client.js +54 -52
- package/dist/store/runtime-clients/thread-list-runtime-client.js.map +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.d.ts.map +1 -1
- package/dist/store/runtime-clients/thread-runtime-client.js +193 -121
- package/dist/store/runtime-clients/thread-runtime-client.js.map +1 -1
- package/dist/store/scopes/composer.d.ts +10 -2
- package/dist/store/scopes/composer.d.ts.map +1 -1
- package/dist/store/scopes/message.d.ts +30 -1
- package/dist/store/scopes/message.d.ts.map +1 -1
- package/dist/store/scopes/thread-list-item.d.ts +2 -2
- package/dist/store/scopes/thread-list-item.d.ts.map +1 -1
- package/dist/store/scopes/thread.d.ts +8 -0
- package/dist/store/scopes/thread.d.ts.map +1 -1
- package/dist/store/scopes/threads.d.ts +1 -0
- package/dist/store/scopes/threads.d.ts.map +1 -1
- package/dist/types/attachment.d.ts +2 -1
- package/dist/types/attachment.d.ts.map +1 -1
- package/dist/types/attachment.js +2 -1
- package/dist/types/attachment.js.map +1 -1
- package/dist/utils/abortable-iterable.d.ts +19 -0
- package/dist/utils/abortable-iterable.d.ts.map +1 -0
- package/dist/utils/abortable-iterable.js +72 -0
- package/dist/utils/abortable-iterable.js.map +1 -0
- package/dist/utils/composite-context-provider.d.ts.map +1 -1
- package/dist/utils/composite-context-provider.js +20 -2
- package/dist/utils/composite-context-provider.js.map +1 -1
- package/dist/utils/record.d.ts +11 -0
- package/dist/utils/record.d.ts.map +1 -0
- package/dist/utils/record.js +12 -0
- package/dist/utils/record.js.map +1 -0
- package/package.json +10 -10
- package/src/adapters/attachment.test.ts +50 -1
- package/src/adapters/attachment.ts +7 -4
- package/src/index.ts +1 -0
- package/src/internal.ts +4 -0
- package/src/model-context/frame/host.test.ts +24 -0
- package/src/model-context/frame/host.ts +10 -4
- package/src/model-context/frame/provider.test.ts +71 -0
- package/src/model-context/frame/provider.ts +37 -7
- package/src/model-context/interactable-composer-metadata.test.ts +28 -0
- package/src/model-context/interactable-composer-metadata.ts +9 -3
- package/src/model-context/registry.test.ts +12 -0
- package/src/model-context/registry.ts +3 -2
- package/src/model-context/types.test.ts +25 -0
- package/src/model-context/types.ts +9 -4
- package/src/react/RuntimeAdapter.test.ts +34 -0
- package/src/react/client/InMemoryThreadList.ts +1 -0
- package/src/react/client/Interactables.test.ts +28 -0
- package/src/react/client/Interactables.ts +15 -13
- package/src/react/client/RemoteThreadList.background.test.ts +148 -6
- package/src/react/client/RemoteThreadList.concurrent.test.tsx +42 -1
- package/src/react/client/RemoteThreadList.test.ts +345 -3
- package/src/react/client/RemoteThreadList.ts +166 -110
- package/src/react/client/Tools.ts +22 -11
- package/src/react/interactables-legacy/Interactables.ts +16 -16
- package/src/react/interactables-shared/useInteractablePersistenceQueue.ts +9 -10
- package/src/react/model-context/makeAssistantTool.ts +2 -2
- package/src/react/model-context/useAssistantTool.ts +2 -2
- package/src/react/model-context/useToolArgsStatus.test.tsx +30 -0
- package/src/react/model-context/useToolArgsStatus.ts +2 -1
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.destroy.test.tsx +172 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.running.test.tsx +33 -0
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.test.tsx +7 -1
- package/src/react/runtimes/RemoteThreadListHookInstanceManager.tsx +28 -5
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.load-error.test.ts +132 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.switch-delete.test.ts +105 -0
- package/src/react/runtimes/RemoteThreadListThreadListRuntimeCore.tsx +136 -111
- package/src/react/runtimes/RemoteThreadResource.test.ts +9 -0
- package/src/react/runtimes/RemoteThreadResource.ts +19 -12
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.test.tsx +483 -6
- package/src/react/runtimes/cloud/AssistantCloudThreadHistoryAdapter.ts +504 -68
- package/src/react/runtimes/cloud/createCloudThreadListAdapter.test.ts +15 -0
- package/src/react/runtimes/cloud/createCloudThreadListAdapter.ts +1 -0
- package/src/react/runtimes/useExternalStoreRuntime.lifecycle.test.tsx +41 -0
- package/src/react/runtimes/useExternalStoreRuntime.ts +10 -4
- package/src/react/runtimes/useLocalRuntime.test.tsx +20 -0
- package/src/react/runtimes/useRemoteThreadListRuntime.load-error.test.tsx +108 -0
- package/src/react/runtimes/useRemoteThreadListRuntime.ts +33 -0
- package/src/runtime/api/message-runtime.test.ts +75 -0
- package/src/runtime/api/thread-list-item-runtime.ts +10 -3
- package/src/runtime/api/thread-list-runtime.ts +21 -11
- package/src/runtime/api/thread-runtime.test.ts +78 -0
- package/src/runtime/api/thread-runtime.ts +8 -1
- package/src/runtime/base/base-composer-runtime-core.ts +36 -8
- package/src/runtime/base/base-thread-runtime-core.test.ts +55 -1
- package/src/runtime/base/base-thread-runtime-core.ts +6 -4
- package/src/runtime/interfaces/composer-runtime-core.ts +10 -5
- package/src/runtime/interfaces/thread-list-runtime-core.ts +5 -1
- package/src/runtime/utils/external-message-conversion.test.ts +68 -0
- package/src/runtime/utils/external-message-conversion.ts +1 -1
- package/src/runtime/utils/message-repository.ts +29 -17
- package/src/runtime/utils/thread-message-like.ts +1 -1
- package/src/runtimes/external-store/external-store-thread-list-runtime-core.ts +18 -13
- package/src/runtimes/internal.ts +1 -0
- package/src/runtimes/remote-thread-list/remote-thread-state.test.ts +128 -2
- package/src/runtimes/remote-thread-list/remote-thread-state.ts +182 -42
- package/src/runtimes/remote-thread-list/title-generation.test.ts +602 -0
- package/src/runtimes/remote-thread-list/title-generation.ts +329 -0
- package/src/runtimes/remote-thread-list/title.test.ts +62 -0
- package/src/runtimes/remote-thread-list/title.ts +13 -0
- package/src/runtimes/remote-thread-list/types.ts +10 -0
- package/src/store/clients/external-thread.ts +48 -3
- package/src/store/clients/model-context-client.ts +5 -1
- package/src/store/clients/single-thread-list.ts +1 -0
- package/src/store/clients/suggestions.test.ts +40 -0
- package/src/store/clients/suggestions.ts +13 -10
- package/src/store/runtime-clients/composer-runtime-client.ts +42 -9
- package/src/store/runtime-clients/message-runtime-client.ts +54 -6
- package/src/store/runtime-clients/thread-list-item-runtime-client.ts +4 -2
- package/src/store/runtime-clients/thread-list-runtime-client.ts +1 -0
- package/src/store/runtime-clients/thread-runtime-client.ts +58 -10
- package/src/store/scopes/composer.ts +16 -4
- package/src/store/scopes/message.ts +14 -0
- package/src/store/scopes/thread-list-item.ts +5 -2
- package/src/store/scopes/thread.ts +4 -0
- package/src/store/scopes/threads.ts +1 -0
- package/src/tests/MessageRepository.test.ts +116 -0
- package/src/tests/OptimisticState-list-race.test.ts +1 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-initialize-adapter-change.test.ts +38 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-load-race.test.ts +47 -0
- package/src/tests/RemoteThreadListThreadListRuntimeCore-title-race.test.ts +89 -0
- package/src/tests/attachment-adapters.test.ts +14 -0
- package/src/tests/base-composer-runtime-core.test.ts +39 -0
- package/src/tests/external-store-thread-list-runtime-core.test.ts +83 -0
- package/src/tests/external-store-thread-runtime-core-adapter.test.ts +27 -18
- package/src/tests/external-thread-attachments.test.tsx +139 -0
- package/src/tests/external-thread-parity.test.tsx +23 -0
- package/src/tests/remote-thread-list-isLoading.test.ts +1 -0
- package/src/tests/thread-list-runtime-getLoadThreadsPromise.test.ts +1 -0
- package/src/tests/thread-switch-events.test.tsx +78 -0
- package/src/tests/tools-scope-migration.test.tsx +28 -0
- package/src/types/attachment.ts +4 -0
- package/src/utils/abortable-iterable.test.ts +213 -0
- package/src/utils/abortable-iterable.ts +93 -0
- package/src/utils/composite-context-provider.test.ts +25 -0
- package/src/utils/composite-context-provider.ts +23 -2
- package/src/utils/record.test.ts +48 -0
- package/src/utils/record.ts +11 -0
|
@@ -43,6 +43,7 @@ type ClassifyAccumulator = {
|
|
|
43
43
|
declare const classifyThreads: (threads: readonly RemoteThreadMetadata[], acc: ClassifyAccumulator) => ClassifyAccumulator;
|
|
44
44
|
type RemoteThreadState = {
|
|
45
45
|
readonly isLoading: boolean;
|
|
46
|
+
readonly loadError: unknown;
|
|
46
47
|
readonly isLoadingMore: boolean;
|
|
47
48
|
readonly cursor: string | undefined;
|
|
48
49
|
readonly newThreadId: string | undefined;
|
|
@@ -59,7 +60,23 @@ declare const seedNewThread: (state: RemoteThreadState) => {
|
|
|
59
60
|
declare const preserveMidLoadTransitions: (state: RemoteThreadState, priorOrder: Pick<RemoteThreadState, "threadIds" | "archivedThreadIds">, statusAtRequest: ReadonlyMap<string, RemoteThreadData["status"]>) => RemoteThreadState;
|
|
60
61
|
declare const statusSnapshot: (state: RemoteThreadState) => ReadonlyMap<string, RemoteThreadData["status"]>;
|
|
61
62
|
declare const getThreadData: (state: RemoteThreadState, threadIdOrRemoteId: string) => RemoteThreadData | undefined;
|
|
63
|
+
declare const reconcileInitializedThread: (state: RemoteThreadState, threadId: string, remoteId: string, externalId: string | undefined, retainedThreadId?: string) => {
|
|
64
|
+
state: RemoteThreadState;
|
|
65
|
+
removedMappingId: THREAD_MAPPING_ID | undefined;
|
|
66
|
+
survivorMappingId: THREAD_MAPPING_ID;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Transitions a thread that already exists remotely. Promoting the local draft
|
|
70
|
+
* off `new` needs the initialization promise its destination variant requires,
|
|
71
|
+
* so that goes through `promoteNewThreadReducer`; asking for it here leaves the
|
|
72
|
+
* state unchanged.
|
|
73
|
+
*/
|
|
62
74
|
declare const updateStatusReducer: (state: RemoteThreadState, threadIdOrRemoteId: string, newStatus: "regular" | "archived" | "deleted") => RemoteThreadState;
|
|
75
|
+
/**
|
|
76
|
+
* Promotes the local draft to `regular`, carrying the initialization promise
|
|
77
|
+
* onto the slot.
|
|
78
|
+
*/
|
|
79
|
+
declare const promoteNewThreadReducer: (state: RemoteThreadState, threadIdOrRemoteId: string, initializeTask: Promise<RemoteThreadInitializeResponse>) => RemoteThreadState;
|
|
63
80
|
//#endregion
|
|
64
|
-
export { ClassifyAccumulator, LOCAL_THREAD_ID_PREFIX, RemoteThreadData, RemoteThreadState, THREAD_MAPPING_ID, classifyThreads, createEmptyRemoteThreadState, createThreadMappingId, getThreadData, normalizeCursor, preserveMidLoadTransitions, seedNewThread, statusSnapshot, updateStatusReducer };
|
|
81
|
+
export { ClassifyAccumulator, LOCAL_THREAD_ID_PREFIX, RemoteThreadData, RemoteThreadState, THREAD_MAPPING_ID, classifyThreads, createEmptyRemoteThreadState, createThreadMappingId, getThreadData, normalizeCursor, preserveMidLoadTransitions, promoteNewThreadReducer, reconcileInitializedThread, seedNewThread, statusSnapshot, updateStatusReducer };
|
|
65
82
|
//# sourceMappingURL=remote-thread-state.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-thread-state.d.ts","names":[],"sources":["../../../src/runtimes/remote-thread-list/remote-thread-state.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"remote-thread-state.d.ts","names":[],"sources":["../../../src/runtimes/remote-thread-list/remote-thread-state.ts"],"mappings":";;KAOY;WAEG;WACA;WACA;WACA;WACA;WACA;WACA;;WAGA;WACA,gBAAgB,QAAQ;WACxB;WACA;WACA;WACA;WACA;WACA;;WAGA;WACA,gBAAgB,QAAQ;WACxB;WACA;WACA;WACA;WACA,gBAAgB;WAChB,SAAS;WACT;;KAGH;EAA+B;;iBAE3B,sBAAsB,aAAa;cAItC;cAEA,kBAAmB;KAGpB;EACV;EACA;EACA,aAAa,eAAe;EAC5B,YAAY,OAAO,mBAAmB;;cAS3B,kBACX,kBAAkB,wBAClB,KAAK,wBACJ;KAqES;WACD;WACA;WACA;WACA;WACA;WACA;WACA;WACA,aAAa,SAAS,eAAe;WACrC,YAAY,SAAS,OAAO,mBAAmB;;cAG7C,oCAAmC;cAYnC,gBACX,OAAO;EACJ;EAAY,OAAO;;cAyCX,6BACX,OAAO,mBACP,YAAY,KAAK,uDACjB,iBAAiB,oBAAoB,gCACpC;cAoDU,iBACX,OAAO,sBACN,oBAAoB;cAGV,gBACX,OAAO,mBACP,+BAA0B;cAWf,6BACX,OAAO,mBACP,kBACA,kBACA,gCACA;EAEA,OAAO;EACP,kBAAkB;EAClB,mBAAmB;;;;;;;;cAuLR,sBACX,OAAO,mBACP,4BACA,kDAA6C;;;;;cAOlC,0BACX,OAAO,mBACP,4BACA,gBAAgB,QAAQ,oCAA+B"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { nullProtoRecord } from "../../utils/record.js";
|
|
1
2
|
import { generateId } from "../../utils/id.js";
|
|
2
3
|
//#region src/runtimes/remote-thread-list/remote-thread-state.ts
|
|
3
4
|
function createThreadMappingId(id) {
|
|
@@ -6,7 +7,11 @@ function createThreadMappingId(id) {
|
|
|
6
7
|
const LOCAL_THREAD_ID_PREFIX = "__LOCALID_";
|
|
7
8
|
const normalizeCursor = (c) => c || void 0;
|
|
8
9
|
const classifyThreads = (threads, acc) => {
|
|
9
|
-
|
|
10
|
+
let threadIds = [...acc.threadIds];
|
|
11
|
+
let archivedThreadIds = [...acc.archivedThreadIds];
|
|
12
|
+
const threadIdMap = nullProtoRecord(acc.threadIdMap);
|
|
13
|
+
const threadData = nullProtoRecord(acc.threadData);
|
|
14
|
+
const listed = /* @__PURE__ */ new Set([...threadIds, ...archivedThreadIds]);
|
|
10
15
|
for (const thread of threads) {
|
|
11
16
|
switch (thread.status) {
|
|
12
17
|
case "regular":
|
|
@@ -16,27 +21,27 @@ const classifyThreads = (threads, acc) => {
|
|
|
16
21
|
throw new Error(`Unsupported state: ${_exhaustiveCheck}`);
|
|
17
22
|
}
|
|
18
23
|
}
|
|
19
|
-
const existingMappingId =
|
|
20
|
-
const existing = existingMappingId !== void 0 ?
|
|
24
|
+
const existingMappingId = threadIdMap[thread.remoteId];
|
|
25
|
+
const existing = existingMappingId !== void 0 ? threadData[existingMappingId] : void 0;
|
|
21
26
|
const id = existing?.id ?? thread.remoteId;
|
|
22
27
|
const mappingId = existingMappingId ?? createThreadMappingId(id);
|
|
23
28
|
const existingTask = existing !== void 0 && existing.status !== "new" ? existing.initializeTask : void 0;
|
|
24
29
|
if (!listed.has(id)) {
|
|
25
30
|
listed.add(id);
|
|
26
|
-
if (thread.status === "regular")
|
|
27
|
-
else
|
|
31
|
+
if (thread.status === "regular") threadIds.push(id);
|
|
32
|
+
else archivedThreadIds.push(id);
|
|
28
33
|
} else if (existing !== void 0 && existing.status !== thread.status) {
|
|
29
34
|
if (thread.status === "regular") {
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
archivedThreadIds = archivedThreadIds.filter((t) => t !== id);
|
|
36
|
+
threadIds.push(id);
|
|
32
37
|
} else {
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
threadIds = threadIds.filter((t) => t !== id);
|
|
39
|
+
archivedThreadIds.push(id);
|
|
35
40
|
}
|
|
36
41
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
42
|
+
threadIdMap[id] = mappingId;
|
|
43
|
+
threadIdMap[thread.remoteId] = mappingId;
|
|
44
|
+
threadData[mappingId] = {
|
|
40
45
|
...existing?.localOrigin === true ? { localOrigin: true } : {},
|
|
41
46
|
id,
|
|
42
47
|
remoteId: thread.remoteId,
|
|
@@ -51,17 +56,23 @@ const classifyThreads = (threads, acc) => {
|
|
|
51
56
|
})
|
|
52
57
|
};
|
|
53
58
|
}
|
|
54
|
-
return
|
|
59
|
+
return {
|
|
60
|
+
threadIds,
|
|
61
|
+
archivedThreadIds,
|
|
62
|
+
threadIdMap,
|
|
63
|
+
threadData
|
|
64
|
+
};
|
|
55
65
|
};
|
|
56
66
|
const createEmptyRemoteThreadState = () => ({
|
|
57
67
|
isLoading: true,
|
|
68
|
+
loadError: void 0,
|
|
58
69
|
isLoadingMore: false,
|
|
59
70
|
cursor: void 0,
|
|
60
71
|
newThreadId: void 0,
|
|
61
72
|
threadIds: [],
|
|
62
73
|
archivedThreadIds: [],
|
|
63
|
-
threadIdMap:
|
|
64
|
-
threadData:
|
|
74
|
+
threadIdMap: nullProtoRecord(),
|
|
75
|
+
threadData: nullProtoRecord()
|
|
65
76
|
});
|
|
66
77
|
const seedNewThread = (state) => {
|
|
67
78
|
let id;
|
|
@@ -74,22 +85,16 @@ const seedNewThread = (state) => {
|
|
|
74
85
|
state: {
|
|
75
86
|
...state,
|
|
76
87
|
newThreadId: id,
|
|
77
|
-
threadIdMap: {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
externalId: void 0,
|
|
88
|
-
title: void 0,
|
|
89
|
-
custom: void 0,
|
|
90
|
-
localOrigin: true
|
|
91
|
-
}
|
|
92
|
-
}
|
|
88
|
+
threadIdMap: nullProtoRecord(state.threadIdMap, { [id]: mappingId }),
|
|
89
|
+
threadData: nullProtoRecord(state.threadData, { [mappingId]: {
|
|
90
|
+
status: "new",
|
|
91
|
+
id,
|
|
92
|
+
remoteId: void 0,
|
|
93
|
+
externalId: void 0,
|
|
94
|
+
title: void 0,
|
|
95
|
+
custom: void 0,
|
|
96
|
+
localOrigin: true
|
|
97
|
+
} })
|
|
93
98
|
}
|
|
94
99
|
};
|
|
95
100
|
};
|
|
@@ -128,15 +133,85 @@ const preserveMidLoadTransitions = (state, priorOrder, statusAtRequest) => {
|
|
|
128
133
|
};
|
|
129
134
|
const statusSnapshot = (state) => new Map(Object.values(state.threadData).map((d) => [d.id, d.status]));
|
|
130
135
|
const getThreadData = (state, threadIdOrRemoteId) => {
|
|
131
|
-
const idx = state.threadIdMap[threadIdOrRemoteId];
|
|
136
|
+
const idx = Object.hasOwn(state.threadIdMap, threadIdOrRemoteId) ? state.threadIdMap[threadIdOrRemoteId] : void 0;
|
|
132
137
|
if (idx === void 0) return void 0;
|
|
133
|
-
return state.threadData[idx];
|
|
138
|
+
return Object.hasOwn(state.threadData, idx) ? state.threadData[idx] : void 0;
|
|
134
139
|
};
|
|
135
|
-
const
|
|
140
|
+
const reconcileInitializedThread = (state, threadId, remoteId, externalId, retainedThreadId) => {
|
|
141
|
+
const mappingId = createThreadMappingId(threadId);
|
|
142
|
+
const data = Object.hasOwn(state.threadData, mappingId) ? state.threadData[mappingId] : void 0;
|
|
143
|
+
if (!data) return {
|
|
144
|
+
state,
|
|
145
|
+
removedMappingId: void 0,
|
|
146
|
+
survivorMappingId: mappingId
|
|
147
|
+
};
|
|
148
|
+
const listedMappingId = Object.hasOwn(state.threadIdMap, remoteId) ? state.threadIdMap[remoteId] : void 0;
|
|
149
|
+
const listedData = listedMappingId !== void 0 && listedMappingId !== mappingId && Object.hasOwn(state.threadData, listedMappingId) ? state.threadData[listedMappingId] : void 0;
|
|
150
|
+
const listedSlot = listedMappingId !== void 0 && listedData !== void 0 ? {
|
|
151
|
+
mappingId: listedMappingId,
|
|
152
|
+
data: listedData
|
|
153
|
+
} : void 0;
|
|
154
|
+
const retainedMappingId = retainedThreadId !== void 0 && Object.hasOwn(state.threadIdMap, retainedThreadId) ? state.threadIdMap[retainedThreadId] : void 0;
|
|
155
|
+
const survivorMappingId = listedSlot !== void 0 && retainedMappingId === listedSlot.mappingId ? listedSlot.mappingId : mappingId;
|
|
156
|
+
const removedMappingId = listedSlot === void 0 ? void 0 : survivorMappingId === mappingId ? listedSlot.mappingId : mappingId;
|
|
157
|
+
const resolvedExternalId = externalId ?? listedSlot?.data.externalId;
|
|
158
|
+
const threadData = nullProtoRecord(state.threadData);
|
|
159
|
+
if (removedMappingId !== void 0) delete threadData[removedMappingId];
|
|
160
|
+
const initializedData = {
|
|
161
|
+
...data,
|
|
162
|
+
id: survivorMappingId,
|
|
163
|
+
initializeTask: Promise.resolve({
|
|
164
|
+
remoteId,
|
|
165
|
+
externalId: resolvedExternalId
|
|
166
|
+
}),
|
|
167
|
+
remoteId,
|
|
168
|
+
externalId: resolvedExternalId
|
|
169
|
+
};
|
|
170
|
+
threadData[survivorMappingId] = {
|
|
171
|
+
...listedSlot?.data,
|
|
172
|
+
...initializedData,
|
|
173
|
+
title: data.title ?? listedSlot?.data.title,
|
|
174
|
+
lastMessageAt: ("lastMessageAt" in data ? data.lastMessageAt : void 0) ?? (listedSlot && "lastMessageAt" in listedSlot.data ? listedSlot.data.lastMessageAt : void 0),
|
|
175
|
+
custom: data.custom ?? listedSlot?.data.custom
|
|
176
|
+
};
|
|
177
|
+
const threadIdMap = nullProtoRecord(state.threadIdMap);
|
|
178
|
+
if (removedMappingId !== void 0) {
|
|
179
|
+
for (const [id, target] of Object.entries(threadIdMap)) if (target === removedMappingId) threadIdMap[id] = survivorMappingId;
|
|
180
|
+
}
|
|
181
|
+
threadIdMap[threadId] = survivorMappingId;
|
|
182
|
+
threadIdMap[remoteId] = survivorMappingId;
|
|
183
|
+
const rewire = (ids) => listedSlot === void 0 ? ids : ids.filter((id) => id !== listedSlot.data.id).map((id) => id === data.id ? survivorMappingId : id);
|
|
184
|
+
return {
|
|
185
|
+
state: {
|
|
186
|
+
...state,
|
|
187
|
+
threadIds: rewire(state.threadIds),
|
|
188
|
+
archivedThreadIds: rewire(state.archivedThreadIds),
|
|
189
|
+
threadIdMap,
|
|
190
|
+
threadData
|
|
191
|
+
},
|
|
192
|
+
removedMappingId,
|
|
193
|
+
survivorMappingId
|
|
194
|
+
};
|
|
195
|
+
};
|
|
196
|
+
const transitionReducer = (state, threadIdOrRemoteId, newStatus, initializeTask) => {
|
|
136
197
|
const data = getThreadData(state, threadIdOrRemoteId);
|
|
137
198
|
if (!data) return state;
|
|
138
199
|
const { id, status: lastStatus } = data;
|
|
139
200
|
if (lastStatus === newStatus) return state;
|
|
201
|
+
let nextData;
|
|
202
|
+
if (newStatus !== "deleted") {
|
|
203
|
+
if (data.status === "new") {
|
|
204
|
+
if (initializeTask === void 0) return state;
|
|
205
|
+
nextData = {
|
|
206
|
+
...data,
|
|
207
|
+
initializeTask,
|
|
208
|
+
status: newStatus
|
|
209
|
+
};
|
|
210
|
+
} else nextData = {
|
|
211
|
+
...data,
|
|
212
|
+
status: newStatus
|
|
213
|
+
};
|
|
214
|
+
}
|
|
140
215
|
const newState = { ...state };
|
|
141
216
|
switch (lastStatus) {
|
|
142
217
|
case "new":
|
|
@@ -159,22 +234,35 @@ const updateStatusReducer = (state, threadIdOrRemoteId, newStatus) => {
|
|
|
159
234
|
break;
|
|
160
235
|
case "deleted": {
|
|
161
236
|
const mappingId = state.threadIdMap[threadIdOrRemoteId];
|
|
162
|
-
|
|
163
|
-
|
|
237
|
+
const threadData = nullProtoRecord(newState.threadData);
|
|
238
|
+
delete threadData[mappingId];
|
|
239
|
+
newState.threadData = threadData;
|
|
240
|
+
const threadIdMap = nullProtoRecord(newState.threadIdMap);
|
|
241
|
+
for (const [key, value] of Object.entries(threadIdMap)) if (value === mappingId) delete threadIdMap[key];
|
|
242
|
+
newState.threadIdMap = threadIdMap;
|
|
164
243
|
break;
|
|
165
244
|
}
|
|
166
245
|
default: throw new Error(`Unsupported state: ${newStatus}`);
|
|
167
246
|
}
|
|
168
|
-
if (
|
|
247
|
+
if (nextData !== void 0) newState.threadData = {
|
|
169
248
|
...newState.threadData,
|
|
170
|
-
[id]:
|
|
171
|
-
...data,
|
|
172
|
-
status: newStatus
|
|
173
|
-
}
|
|
249
|
+
[id]: nextData
|
|
174
250
|
};
|
|
175
251
|
return newState;
|
|
176
252
|
};
|
|
253
|
+
/**
|
|
254
|
+
* Transitions a thread that already exists remotely. Promoting the local draft
|
|
255
|
+
* off `new` needs the initialization promise its destination variant requires,
|
|
256
|
+
* so that goes through `promoteNewThreadReducer`; asking for it here leaves the
|
|
257
|
+
* state unchanged.
|
|
258
|
+
*/
|
|
259
|
+
const updateStatusReducer = (state, threadIdOrRemoteId, newStatus) => transitionReducer(state, threadIdOrRemoteId, newStatus, void 0);
|
|
260
|
+
/**
|
|
261
|
+
* Promotes the local draft to `regular`, carrying the initialization promise
|
|
262
|
+
* onto the slot.
|
|
263
|
+
*/
|
|
264
|
+
const promoteNewThreadReducer = (state, threadIdOrRemoteId, initializeTask) => transitionReducer(state, threadIdOrRemoteId, "regular", initializeTask);
|
|
177
265
|
//#endregion
|
|
178
|
-
export { LOCAL_THREAD_ID_PREFIX, classifyThreads, createEmptyRemoteThreadState, createThreadMappingId, getThreadData, normalizeCursor, preserveMidLoadTransitions, seedNewThread, statusSnapshot, updateStatusReducer };
|
|
266
|
+
export { LOCAL_THREAD_ID_PREFIX, classifyThreads, createEmptyRemoteThreadState, createThreadMappingId, getThreadData, normalizeCursor, preserveMidLoadTransitions, promoteNewThreadReducer, reconcileInitializedThread, seedNewThread, statusSnapshot, updateStatusReducer };
|
|
179
267
|
|
|
180
268
|
//# sourceMappingURL=remote-thread-state.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remote-thread-state.js","names":["RemoteThreadInitializeResponse","RemoteThreadMetadata","generateId","RemoteThreadData","id","remoteId","externalId","status","title","custom","localOrigin","initializeTask","Promise","lastMessageAt","Date","Record","THREAD_MAPPING_ID","__brand","createThreadMappingId","LOCAL_THREAD_ID_PREFIX","normalizeCursor","c","undefined","ClassifyAccumulator","threadIds","archivedThreadIds","threadIdMap","threadData","classifyThreads","threads","acc","listed","Set","thread","_exhaustiveCheck","Error","existingMappingId","existing","mappingId","existingTask","has","add","push","filter","t","const","resolve","RemoteThreadState","isLoading","isLoadingMore","cursor","newThreadId","Readonly","createEmptyRemoteThreadState","seedNewThread","state","preserveMidLoadTransitions","priorOrder","Pick","statusAtRequest","ReadonlyMap","regular","archived","rescuedRegular","rescuedArchived","contains","ids","ReadonlySet","data","Object","values","before","get","length","position","byId","indexOf","byRemoteId","Number","MAX_SAFE_INTEGER","sort","a","b","map","d","statusSnapshot","Map","getThreadData","threadIdOrRemoteId","idx","updateStatusReducer","newStatus","lastStatus","newState","fromEntries","entries","key","value"],"sources":["../../../src/runtimes/remote-thread-list/remote-thread-state.ts"],"sourcesContent":["import type {\n RemoteThreadInitializeResponse,\n RemoteThreadMetadata,\n} from \"./types\";\nimport { generateId } from \"../../utils/id\";\n\nexport type RemoteThreadData =\n | {\n readonly id: string;\n readonly remoteId: undefined;\n readonly externalId: undefined;\n readonly status: \"new\";\n readonly title: undefined;\n readonly custom: undefined;\n readonly localOrigin?: true;\n }\n | {\n readonly id: string;\n readonly initializeTask: Promise<RemoteThreadInitializeResponse>;\n readonly remoteId: undefined;\n readonly externalId: undefined;\n readonly status: \"regular\" | \"archived\";\n readonly title?: string | undefined;\n readonly custom: undefined;\n readonly localOrigin?: true;\n }\n | {\n readonly id: string;\n readonly initializeTask: Promise<RemoteThreadInitializeResponse>;\n readonly remoteId: string;\n readonly externalId: string | undefined;\n readonly status: \"regular\" | \"archived\";\n readonly title?: string | undefined;\n readonly lastMessageAt?: Date | undefined;\n readonly custom?: Record<string, unknown> | undefined;\n readonly localOrigin?: true;\n };\n\nexport type THREAD_MAPPING_ID = string & { __brand: \"THREAD_MAPPING_ID\" };\n\nexport function createThreadMappingId(id: string): THREAD_MAPPING_ID {\n return id as THREAD_MAPPING_ID;\n}\n\nexport const LOCAL_THREAD_ID_PREFIX = \"__LOCALID_\";\n\nexport const normalizeCursor = (c: string | undefined): string | undefined =>\n c || undefined;\n\nexport type ClassifyAccumulator = {\n threadIds: string[];\n archivedThreadIds: string[];\n threadIdMap: Record<string, THREAD_MAPPING_ID>;\n threadData: Record<THREAD_MAPPING_ID, RemoteThreadData>;\n};\n\n// A slot's `id` is fixed when it is minted: the hook instance manager keys live\n// thread runtimes by it, so renaming a slot would detach the runtime the user is\n// chatting in. A listed thread whose `remoteId` already maps to a slot therefore\n// refreshes that slot instead of minting a second one, and a slot minted by a\n// list() that raced ahead of the remote id collapses when `initialize()` settles.\n// `threadIds`/`archivedThreadIds` carry slot ids, never remote ids.\nexport const classifyThreads = (\n threads: readonly RemoteThreadMetadata[],\n acc: ClassifyAccumulator,\n): ClassifyAccumulator => {\n const listed = new Set([...acc.threadIds, ...acc.archivedThreadIds]);\n\n for (const thread of threads) {\n switch (thread.status) {\n case \"regular\":\n case \"archived\":\n break;\n default: {\n const _exhaustiveCheck: never = thread.status;\n throw new Error(`Unsupported state: ${_exhaustiveCheck}`);\n }\n }\n\n const existingMappingId = acc.threadIdMap[thread.remoteId];\n const existing =\n existingMappingId !== undefined\n ? acc.threadData[existingMappingId]\n : undefined;\n const id = existing?.id ?? thread.remoteId;\n const mappingId = existingMappingId ?? createThreadMappingId(id);\n const existingTask =\n existing !== undefined && existing.status !== \"new\"\n ? existing.initializeTask\n : undefined;\n\n if (!listed.has(id)) {\n listed.add(id);\n if (thread.status === \"regular\") {\n acc.threadIds.push(id);\n } else {\n acc.archivedThreadIds.push(id);\n }\n } else if (existing !== undefined && existing.status !== thread.status) {\n if (thread.status === \"regular\") {\n acc.archivedThreadIds = acc.archivedThreadIds.filter((t) => t !== id);\n acc.threadIds.push(id);\n } else {\n acc.threadIds = acc.threadIds.filter((t) => t !== id);\n acc.archivedThreadIds.push(id);\n }\n }\n\n acc.threadIdMap[id] = mappingId;\n acc.threadIdMap[thread.remoteId] = mappingId;\n acc.threadData[mappingId] = {\n ...(existing?.localOrigin === true ? { localOrigin: true as const } : {}),\n id,\n remoteId: thread.remoteId,\n externalId: thread.externalId,\n status: thread.status,\n title: thread.title,\n lastMessageAt: thread.lastMessageAt,\n custom: thread.custom,\n initializeTask:\n existingTask ??\n Promise.resolve({\n remoteId: thread.remoteId,\n externalId: thread.externalId,\n }),\n };\n }\n return acc;\n};\n\nexport type RemoteThreadState = {\n readonly isLoading: boolean;\n readonly isLoadingMore: boolean;\n readonly cursor: string | undefined;\n readonly newThreadId: string | undefined;\n readonly threadIds: readonly string[];\n readonly archivedThreadIds: readonly string[];\n readonly threadIdMap: Readonly<Record<string, THREAD_MAPPING_ID>>;\n readonly threadData: Readonly<Record<THREAD_MAPPING_ID, RemoteThreadData>>;\n};\n\nexport const createEmptyRemoteThreadState = (): RemoteThreadState => ({\n isLoading: true,\n isLoadingMore: false,\n cursor: undefined,\n newThreadId: undefined,\n threadIds: [],\n archivedThreadIds: [],\n threadIdMap: {},\n threadData: {},\n});\n\nexport const seedNewThread = (\n state: RemoteThreadState,\n): { id: string; state: RemoteThreadState } => {\n let id: string;\n do {\n id = `${LOCAL_THREAD_ID_PREFIX}${generateId()}`;\n } while (state.threadIdMap[id]);\n const mappingId = createThreadMappingId(id);\n return {\n id,\n state: {\n ...state,\n newThreadId: id,\n threadIdMap: {\n ...state.threadIdMap,\n [id]: mappingId,\n },\n threadData: {\n ...state.threadData,\n [mappingId]: {\n status: \"new\",\n id,\n remoteId: undefined,\n externalId: undefined,\n title: undefined,\n custom: undefined,\n localOrigin: true,\n } satisfies RemoteThreadData,\n },\n },\n };\n};\n\n// A list() response predating a mid-flight local transition (a thread\n// initialized while the request was running) omits that thread, and the\n// completed-optimistic replay cannot re-add it because the merged threadData\n// already carries the final status. Threads whose status moved from new (or\n// nonexistent) at request time to regular/archived are re-inserted at the\n// top, in the order the pre-response lists carried them: updateStatusReducer\n// prepends as transitions happen, so the pre-response lists hold newest\n// first, which threadData insertion order need not match. Threads the server already knew stay governed by the response, so\n// a server-side deletion is not resurrected — and a thread unknown to the\n// snapshot is only rescued when it carries the localOrigin marker stamped at\n// draft creation, so a deep-linked thread the fetch path deliberately\n// appended at the tail is not hoisted, whatever its remote id looks like.\nexport const preserveMidLoadTransitions = (\n state: RemoteThreadState,\n priorOrder: Pick<RemoteThreadState, \"threadIds\" | \"archivedThreadIds\">,\n statusAtRequest: ReadonlyMap<string, RemoteThreadData[\"status\"]>,\n): RemoteThreadState => {\n const regular = new Set(state.threadIds);\n const archived = new Set(state.archivedThreadIds);\n const rescuedRegular: RemoteThreadData[] = [];\n const rescuedArchived: RemoteThreadData[] = [];\n\n const contains = (ids: ReadonlySet<string>, data: RemoteThreadData) =>\n ids.has(data.id) || (data.remoteId !== undefined && ids.has(data.remoteId));\n\n for (const data of Object.values(state.threadData)) {\n const before = statusAtRequest.get(data.id);\n if (before !== undefined && before !== \"new\") continue;\n if (before === undefined && data.localOrigin !== true) continue;\n\n if (data.status === \"regular\" && !contains(regular, data)) {\n rescuedRegular.push(data);\n regular.add(data.id);\n } else if (data.status === \"archived\" && !contains(archived, data)) {\n rescuedArchived.push(data);\n archived.add(data.id);\n }\n }\n\n if (rescuedRegular.length === 0 && rescuedArchived.length === 0) return state;\n\n const position = (ids: readonly string[], data: RemoteThreadData) => {\n const byId = ids.indexOf(data.id);\n if (byId !== -1) return byId;\n const byRemoteId =\n data.remoteId !== undefined ? ids.indexOf(data.remoteId) : -1;\n return byRemoteId !== -1 ? byRemoteId : Number.MAX_SAFE_INTEGER;\n };\n rescuedRegular.sort(\n (a, b) =>\n position(priorOrder.threadIds, a) - position(priorOrder.threadIds, b),\n );\n rescuedArchived.sort(\n (a, b) =>\n position(priorOrder.archivedThreadIds, a) -\n position(priorOrder.archivedThreadIds, b),\n );\n\n return {\n ...state,\n threadIds: [...rescuedRegular.map((d) => d.id), ...state.threadIds],\n archivedThreadIds: [\n ...rescuedArchived.map((d) => d.id),\n ...state.archivedThreadIds,\n ],\n };\n};\n\nexport const statusSnapshot = (\n state: RemoteThreadState,\n): ReadonlyMap<string, RemoteThreadData[\"status\"]> =>\n new Map(Object.values(state.threadData).map((d) => [d.id, d.status]));\n\nexport const getThreadData = (\n state: RemoteThreadState,\n threadIdOrRemoteId: string,\n) => {\n const idx = state.threadIdMap[threadIdOrRemoteId];\n if (idx === undefined) return undefined;\n return state.threadData[idx];\n};\n\nexport const updateStatusReducer = (\n state: RemoteThreadState,\n threadIdOrRemoteId: string,\n newStatus: \"regular\" | \"archived\" | \"deleted\",\n) => {\n const data = getThreadData(state, threadIdOrRemoteId);\n if (!data) return state;\n\n const { id, status: lastStatus } = data;\n if (lastStatus === newStatus) return state;\n\n const newState = { ...state };\n\n // lastStatus\n switch (lastStatus) {\n case \"new\":\n newState.newThreadId = undefined;\n break;\n case \"regular\":\n newState.threadIds = newState.threadIds.filter((t) => t !== id);\n break;\n case \"archived\":\n newState.archivedThreadIds = newState.archivedThreadIds.filter(\n (t) => t !== id,\n );\n break;\n\n default: {\n const _exhaustiveCheck: never = lastStatus;\n throw new Error(`Unsupported state: ${_exhaustiveCheck}`);\n }\n }\n\n // newStatus\n switch (newStatus) {\n case \"regular\":\n newState.threadIds = [id, ...newState.threadIds];\n break;\n\n case \"archived\":\n newState.archivedThreadIds = [id, ...newState.archivedThreadIds];\n break;\n\n case \"deleted\": {\n const mappingId = state.threadIdMap[threadIdOrRemoteId]!;\n newState.threadData = Object.fromEntries(\n Object.entries(newState.threadData).filter(\n ([key]) => key !== mappingId,\n ),\n );\n newState.threadIdMap = Object.fromEntries(\n Object.entries(newState.threadIdMap).filter(\n ([, value]) => value !== mappingId,\n ),\n );\n break;\n }\n\n default: {\n const _exhaustiveCheck: never = newStatus;\n throw new Error(`Unsupported state: ${_exhaustiveCheck}`);\n }\n }\n\n if (newStatus !== \"deleted\") {\n newState.threadData = {\n ...newState.threadData,\n [id]: {\n ...data,\n status: newStatus,\n },\n };\n }\n\n return newState;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"remote-thread-state.js","names":["RemoteThreadInitializeResponse","RemoteThreadMetadata","generateId","nullProtoRecord","RemoteThreadData","id","remoteId","externalId","status","title","custom","localOrigin","initializeTask","Promise","lastMessageAt","Date","Record","THREAD_MAPPING_ID","__brand","createThreadMappingId","LOCAL_THREAD_ID_PREFIX","normalizeCursor","c","undefined","ClassifyAccumulator","threadIds","archivedThreadIds","threadIdMap","threadData","classifyThreads","threads","acc","listed","Set","thread","_exhaustiveCheck","Error","existingMappingId","existing","mappingId","existingTask","has","add","push","filter","t","const","resolve","RemoteThreadState","isLoading","loadError","isLoadingMore","cursor","newThreadId","Readonly","createEmptyRemoteThreadState","seedNewThread","state","preserveMidLoadTransitions","priorOrder","Pick","statusAtRequest","ReadonlyMap","regular","archived","rescuedRegular","rescuedArchived","contains","ids","ReadonlySet","data","Object","values","before","get","length","position","byId","indexOf","byRemoteId","Number","MAX_SAFE_INTEGER","sort","a","b","map","d","statusSnapshot","Map","getThreadData","threadIdOrRemoteId","idx","hasOwn","reconcileInitializedThread","threadId","retainedThreadId","removedMappingId","survivorMappingId","listedMappingId","listedData","listedSlot","retainedMappingId","resolvedExternalId","initializedData","target","entries","rewire","transitionReducer","newStatus","lastStatus","nextData","newState","key","value","updateStatusReducer","promoteNewThreadReducer"],"sources":["../../../src/runtimes/remote-thread-list/remote-thread-state.ts"],"sourcesContent":["import type {\n RemoteThreadInitializeResponse,\n RemoteThreadMetadata,\n} from \"./types\";\nimport { generateId } from \"../../utils/id\";\nimport { nullProtoRecord } from \"../../utils/record\";\n\nexport type RemoteThreadData =\n | {\n readonly id: string;\n readonly remoteId: undefined;\n readonly externalId: undefined;\n readonly status: \"new\";\n readonly title: undefined;\n readonly custom: undefined;\n readonly localOrigin?: true;\n }\n | {\n readonly id: string;\n readonly initializeTask: Promise<RemoteThreadInitializeResponse>;\n readonly remoteId: undefined;\n readonly externalId: undefined;\n readonly status: \"regular\" | \"archived\";\n readonly title?: string | undefined;\n readonly custom: undefined;\n readonly localOrigin?: true;\n }\n | {\n readonly id: string;\n readonly initializeTask: Promise<RemoteThreadInitializeResponse>;\n readonly remoteId: string;\n readonly externalId: string | undefined;\n readonly status: \"regular\" | \"archived\";\n readonly title?: string | undefined;\n readonly lastMessageAt?: Date | undefined;\n readonly custom?: Record<string, unknown> | undefined;\n readonly localOrigin?: true;\n };\n\nexport type THREAD_MAPPING_ID = string & { __brand: \"THREAD_MAPPING_ID\" };\n\nexport function createThreadMappingId(id: string): THREAD_MAPPING_ID {\n return id as THREAD_MAPPING_ID;\n}\n\nexport const LOCAL_THREAD_ID_PREFIX = \"__LOCALID_\";\n\nexport const normalizeCursor = (c: string | undefined): string | undefined =>\n c || undefined;\n\nexport type ClassifyAccumulator = {\n threadIds: string[];\n archivedThreadIds: string[];\n threadIdMap: Record<string, THREAD_MAPPING_ID>;\n threadData: Record<THREAD_MAPPING_ID, RemoteThreadData>;\n};\n\n// A slot's `id` is fixed when it is minted: the hook instance manager keys live\n// thread runtimes by it, so renaming a slot would detach the runtime the user is\n// chatting in. A listed thread whose `remoteId` already maps to a slot therefore\n// refreshes that slot instead of minting a second one, and a slot minted by a\n// list() that raced ahead of the remote id collapses when `initialize()` settles.\n// `threadIds`/`archivedThreadIds` carry slot ids, never remote ids.\nexport const classifyThreads = (\n threads: readonly RemoteThreadMetadata[],\n acc: ClassifyAccumulator,\n): ClassifyAccumulator => {\n let threadIds = [...acc.threadIds];\n let archivedThreadIds = [...acc.archivedThreadIds];\n const threadIdMap = nullProtoRecord(acc.threadIdMap);\n const threadData = nullProtoRecord(acc.threadData);\n const listed = new Set([...threadIds, ...archivedThreadIds]);\n\n for (const thread of threads) {\n switch (thread.status) {\n case \"regular\":\n case \"archived\":\n break;\n default: {\n const _exhaustiveCheck: never = thread.status;\n throw new Error(`Unsupported state: ${_exhaustiveCheck}`);\n }\n }\n\n const existingMappingId = threadIdMap[thread.remoteId];\n const existing =\n existingMappingId !== undefined\n ? threadData[existingMappingId]\n : undefined;\n const id = existing?.id ?? thread.remoteId;\n const mappingId = existingMappingId ?? createThreadMappingId(id);\n const existingTask =\n existing !== undefined && existing.status !== \"new\"\n ? existing.initializeTask\n : undefined;\n\n if (!listed.has(id)) {\n listed.add(id);\n if (thread.status === \"regular\") {\n threadIds.push(id);\n } else {\n archivedThreadIds.push(id);\n }\n } else if (existing !== undefined && existing.status !== thread.status) {\n if (thread.status === \"regular\") {\n archivedThreadIds = archivedThreadIds.filter((t) => t !== id);\n threadIds.push(id);\n } else {\n threadIds = threadIds.filter((t) => t !== id);\n archivedThreadIds.push(id);\n }\n }\n\n threadIdMap[id] = mappingId;\n threadIdMap[thread.remoteId] = mappingId;\n threadData[mappingId] = {\n ...(existing?.localOrigin === true ? { localOrigin: true as const } : {}),\n id,\n remoteId: thread.remoteId,\n externalId: thread.externalId,\n status: thread.status,\n title: thread.title,\n lastMessageAt: thread.lastMessageAt,\n custom: thread.custom,\n initializeTask:\n existingTask ??\n Promise.resolve({\n remoteId: thread.remoteId,\n externalId: thread.externalId,\n }),\n };\n }\n return { threadIds, archivedThreadIds, threadIdMap, threadData };\n};\n\nexport type RemoteThreadState = {\n readonly isLoading: boolean;\n readonly loadError: unknown;\n readonly isLoadingMore: boolean;\n readonly cursor: string | undefined;\n readonly newThreadId: string | undefined;\n readonly threadIds: readonly string[];\n readonly archivedThreadIds: readonly string[];\n readonly threadIdMap: Readonly<Record<string, THREAD_MAPPING_ID>>;\n readonly threadData: Readonly<Record<THREAD_MAPPING_ID, RemoteThreadData>>;\n};\n\nexport const createEmptyRemoteThreadState = (): RemoteThreadState => ({\n isLoading: true,\n loadError: undefined,\n isLoadingMore: false,\n cursor: undefined,\n newThreadId: undefined,\n threadIds: [],\n archivedThreadIds: [],\n threadIdMap: nullProtoRecord(),\n threadData: nullProtoRecord(),\n});\n\nexport const seedNewThread = (\n state: RemoteThreadState,\n): { id: string; state: RemoteThreadState } => {\n let id: string;\n do {\n id = `${LOCAL_THREAD_ID_PREFIX}${generateId()}`;\n } while (state.threadIdMap[id]);\n const mappingId = createThreadMappingId(id);\n return {\n id,\n state: {\n ...state,\n newThreadId: id,\n threadIdMap: nullProtoRecord(state.threadIdMap, {\n [id]: mappingId,\n }),\n threadData: nullProtoRecord(state.threadData, {\n [mappingId]: {\n status: \"new\",\n id,\n remoteId: undefined,\n externalId: undefined,\n title: undefined,\n custom: undefined,\n localOrigin: true,\n } satisfies RemoteThreadData,\n }),\n },\n };\n};\n\n// A list() response predating a mid-flight local transition (a thread\n// initialized while the request was running) omits that thread, and the\n// completed-optimistic replay cannot re-add it because the merged threadData\n// already carries the final status. Threads whose status moved from new (or\n// nonexistent) at request time to regular/archived are re-inserted at the\n// top, in the order the pre-response lists carried them: updateStatusReducer\n// prepends as transitions happen, so the pre-response lists hold newest\n// first, which threadData insertion order need not match. Threads the server already knew stay governed by the response, so\n// a server-side deletion is not resurrected — and a thread unknown to the\n// snapshot is only rescued when it carries the localOrigin marker stamped at\n// draft creation, so a deep-linked thread the fetch path deliberately\n// appended at the tail is not hoisted, whatever its remote id looks like.\nexport const preserveMidLoadTransitions = (\n state: RemoteThreadState,\n priorOrder: Pick<RemoteThreadState, \"threadIds\" | \"archivedThreadIds\">,\n statusAtRequest: ReadonlyMap<string, RemoteThreadData[\"status\"]>,\n): RemoteThreadState => {\n const regular = new Set(state.threadIds);\n const archived = new Set(state.archivedThreadIds);\n const rescuedRegular: RemoteThreadData[] = [];\n const rescuedArchived: RemoteThreadData[] = [];\n\n const contains = (ids: ReadonlySet<string>, data: RemoteThreadData) =>\n ids.has(data.id) || (data.remoteId !== undefined && ids.has(data.remoteId));\n\n for (const data of Object.values(state.threadData)) {\n const before = statusAtRequest.get(data.id);\n if (before !== undefined && before !== \"new\") continue;\n if (before === undefined && data.localOrigin !== true) continue;\n\n if (data.status === \"regular\" && !contains(regular, data)) {\n rescuedRegular.push(data);\n regular.add(data.id);\n } else if (data.status === \"archived\" && !contains(archived, data)) {\n rescuedArchived.push(data);\n archived.add(data.id);\n }\n }\n\n if (rescuedRegular.length === 0 && rescuedArchived.length === 0) return state;\n\n const position = (ids: readonly string[], data: RemoteThreadData) => {\n const byId = ids.indexOf(data.id);\n if (byId !== -1) return byId;\n const byRemoteId =\n data.remoteId !== undefined ? ids.indexOf(data.remoteId) : -1;\n return byRemoteId !== -1 ? byRemoteId : Number.MAX_SAFE_INTEGER;\n };\n rescuedRegular.sort(\n (a, b) =>\n position(priorOrder.threadIds, a) - position(priorOrder.threadIds, b),\n );\n rescuedArchived.sort(\n (a, b) =>\n position(priorOrder.archivedThreadIds, a) -\n position(priorOrder.archivedThreadIds, b),\n );\n\n return {\n ...state,\n threadIds: [...rescuedRegular.map((d) => d.id), ...state.threadIds],\n archivedThreadIds: [\n ...rescuedArchived.map((d) => d.id),\n ...state.archivedThreadIds,\n ],\n };\n};\n\nexport const statusSnapshot = (\n state: RemoteThreadState,\n): ReadonlyMap<string, RemoteThreadData[\"status\"]> =>\n new Map(Object.values(state.threadData).map((d) => [d.id, d.status]));\n\nexport const getThreadData = (\n state: RemoteThreadState,\n threadIdOrRemoteId: string,\n) => {\n const idx = Object.hasOwn(state.threadIdMap, threadIdOrRemoteId)\n ? state.threadIdMap[threadIdOrRemoteId]\n : undefined;\n if (idx === undefined) return undefined;\n return Object.hasOwn(state.threadData, idx)\n ? state.threadData[idx]\n : undefined;\n};\n\nexport const reconcileInitializedThread = (\n state: RemoteThreadState,\n threadId: string,\n remoteId: string,\n externalId: string | undefined,\n retainedThreadId?: string,\n): {\n state: RemoteThreadState;\n removedMappingId: THREAD_MAPPING_ID | undefined;\n survivorMappingId: THREAD_MAPPING_ID;\n} => {\n const mappingId = createThreadMappingId(threadId);\n const data = Object.hasOwn(state.threadData, mappingId)\n ? state.threadData[mappingId]\n : undefined;\n if (!data) {\n return { state, removedMappingId: undefined, survivorMappingId: mappingId };\n }\n\n // A concurrent list cannot associate the remote ID with this initializing\n // slot and may mint a duplicate. Retain the slot whose mounted runtime owns\n // local state; initialized metadata remains authoritative over list metadata.\n const listedMappingId = Object.hasOwn(state.threadIdMap, remoteId)\n ? state.threadIdMap[remoteId]\n : undefined;\n const listedData =\n listedMappingId !== undefined &&\n listedMappingId !== mappingId &&\n Object.hasOwn(state.threadData, listedMappingId)\n ? state.threadData[listedMappingId]\n : undefined;\n const listedSlot =\n listedMappingId !== undefined && listedData !== undefined\n ? { mappingId: listedMappingId, data: listedData }\n : undefined;\n const retainedMappingId =\n retainedThreadId !== undefined &&\n Object.hasOwn(state.threadIdMap, retainedThreadId)\n ? state.threadIdMap[retainedThreadId]\n : undefined;\n const survivorMappingId =\n listedSlot !== undefined && retainedMappingId === listedSlot.mappingId\n ? listedSlot.mappingId\n : mappingId;\n const removedMappingId =\n listedSlot === undefined\n ? undefined\n : survivorMappingId === mappingId\n ? listedSlot.mappingId\n : mappingId;\n const resolvedExternalId = externalId ?? listedSlot?.data.externalId;\n const threadData = nullProtoRecord(state.threadData);\n if (removedMappingId !== undefined) delete threadData[removedMappingId];\n const initializedData = {\n ...data,\n id: survivorMappingId,\n initializeTask: Promise.resolve({\n remoteId,\n externalId: resolvedExternalId,\n }),\n remoteId,\n externalId: resolvedExternalId,\n } as RemoteThreadData;\n threadData[survivorMappingId] = {\n ...listedSlot?.data,\n ...initializedData,\n title: data.title ?? listedSlot?.data.title,\n lastMessageAt:\n (\"lastMessageAt\" in data ? data.lastMessageAt : undefined) ??\n (listedSlot && \"lastMessageAt\" in listedSlot.data\n ? listedSlot.data.lastMessageAt\n : undefined),\n custom: data.custom ?? listedSlot?.data.custom,\n } as RemoteThreadData;\n\n const threadIdMap = nullProtoRecord(state.threadIdMap);\n if (removedMappingId !== undefined) {\n for (const [id, target] of Object.entries(threadIdMap)) {\n if (target === removedMappingId) threadIdMap[id] = survivorMappingId;\n }\n }\n threadIdMap[threadId] = survivorMappingId;\n threadIdMap[remoteId] = survivorMappingId;\n\n const rewire = (ids: readonly string[]) =>\n listedSlot === undefined\n ? ids\n : ids\n .filter((id) => id !== listedSlot.data.id)\n .map((id) => (id === data.id ? survivorMappingId : id));\n\n return {\n state: {\n ...state,\n threadIds: rewire(state.threadIds),\n archivedThreadIds: rewire(state.archivedThreadIds),\n threadIdMap,\n threadData,\n },\n removedMappingId,\n survivorMappingId,\n };\n};\n\nconst transitionReducer = (\n state: RemoteThreadState,\n threadIdOrRemoteId: string,\n newStatus: \"regular\" | \"archived\" | \"deleted\",\n initializeTask: Promise<RemoteThreadInitializeResponse> | undefined,\n) => {\n const data = getThreadData(state, threadIdOrRemoteId);\n if (!data) return state;\n\n const { id, status: lastStatus } = data;\n if (lastStatus === newStatus) return state;\n\n let nextData: RemoteThreadData | undefined;\n if (newStatus !== \"deleted\") {\n if (data.status === \"new\") {\n // The new variant holds no initialization promise, so the destination\n // union member cannot be built unless the caller supplies one.\n if (initializeTask === undefined) return state;\n nextData = { ...data, initializeTask, status: newStatus };\n } else {\n nextData = { ...data, status: newStatus };\n }\n }\n\n const newState = { ...state };\n\n // lastStatus\n switch (lastStatus) {\n case \"new\":\n newState.newThreadId = undefined;\n break;\n case \"regular\":\n newState.threadIds = newState.threadIds.filter((t) => t !== id);\n break;\n case \"archived\":\n newState.archivedThreadIds = newState.archivedThreadIds.filter(\n (t) => t !== id,\n );\n break;\n\n default: {\n const _exhaustiveCheck: never = lastStatus;\n throw new Error(`Unsupported state: ${_exhaustiveCheck}`);\n }\n }\n\n // newStatus\n switch (newStatus) {\n case \"regular\":\n newState.threadIds = [id, ...newState.threadIds];\n break;\n\n case \"archived\":\n newState.archivedThreadIds = [id, ...newState.archivedThreadIds];\n break;\n\n case \"deleted\": {\n const mappingId = state.threadIdMap[threadIdOrRemoteId]!;\n const threadData = nullProtoRecord(newState.threadData);\n delete threadData[mappingId];\n newState.threadData = threadData;\n const threadIdMap = nullProtoRecord(newState.threadIdMap);\n for (const [key, value] of Object.entries(threadIdMap)) {\n if (value === mappingId) delete threadIdMap[key];\n }\n newState.threadIdMap = threadIdMap;\n break;\n }\n\n default: {\n const _exhaustiveCheck: never = newStatus;\n throw new Error(`Unsupported state: ${_exhaustiveCheck}`);\n }\n }\n\n if (nextData !== undefined) {\n newState.threadData = { ...newState.threadData, [id]: nextData };\n }\n\n return newState;\n};\n\n/**\n * Transitions a thread that already exists remotely. Promoting the local draft\n * off `new` needs the initialization promise its destination variant requires,\n * so that goes through `promoteNewThreadReducer`; asking for it here leaves the\n * state unchanged.\n */\nexport const updateStatusReducer = (\n state: RemoteThreadState,\n threadIdOrRemoteId: string,\n newStatus: \"regular\" | \"archived\" | \"deleted\",\n) => transitionReducer(state, threadIdOrRemoteId, newStatus, undefined);\n\n/**\n * Promotes the local draft to `regular`, carrying the initialization promise\n * onto the slot.\n */\nexport const promoteNewThreadReducer = (\n state: RemoteThreadState,\n threadIdOrRemoteId: string,\n initializeTask: Promise<RemoteThreadInitializeResponse>,\n) => transitionReducer(state, threadIdOrRemoteId, \"regular\", initializeTask);\n"],"mappings":";;;AAyCA,SAAgBmB,sBAAsBd,IAA+B;CACnE,OAAOA;AACT;AAEA,MAAae,yBAAyB;AAEtC,MAAaC,mBAAmBC,MAC9BA,KAAKC,KAAAA;AAeP,MAAaM,mBACXC,SACAC,QACwB;CACxB,IAAIN,YAAY,CAAC,GAAGM,IAAIN,SAAS;CACjC,IAAIC,oBAAoB,CAAC,GAAGK,IAAIL,iBAAiB;CACjD,MAAMC,cAAcxB,gBAAgB4B,IAAIJ,WAAW;CACnD,MAAMC,aAAazB,gBAAgB4B,IAAIH,UAAU;CACjD,MAAMI,yBAAS,IAAIC,IAAI,CAAC,GAAGR,WAAW,GAAGC,iBAAiB,CAAC;CAE3D,KAAK,MAAMQ,UAAUJ,SAAS;EAC5B,QAAQI,OAAO1B,QAAf;GACE,KAAK;GACL,KAAK,YACH;GACF,SAAS;IACP,MAAM2B,mBAA0BD,OAAO1B;IACvC,MAAM,IAAI4B,MAAM,sBAAsBD,kBAAkB;GAC1D;EACF;EAEA,MAAME,oBAAoBV,YAAYO,OAAO5B;EAC7C,MAAMgC,WACJD,sBAAsBd,KAAAA,IAClBK,WAAWS,qBACXd,KAAAA;EACN,MAAMlB,KAAKiC,UAAUjC,MAAM6B,OAAO5B;EAClC,MAAMiC,YAAYF,qBAAqBlB,sBAAsBd,EAAE;EAC/D,MAAMmC,eACJF,aAAaf,KAAAA,KAAae,SAAS9B,WAAW,QAC1C8B,SAAS1B,iBACTW,KAAAA;EAEN,IAAI,CAACS,OAAOS,IAAIpC,EAAE,GAAG;GACnB2B,OAAOU,IAAIrC,EAAE;GACb,IAAI6B,OAAO1B,WAAW,WACpBiB,UAAUkB,KAAKtC,EAAE;QAEjBqB,kBAAkBiB,KAAKtC,EAAE;EAE7B,OAAO,IAAIiC,aAAaf,KAAAA,KAAae,SAAS9B,WAAW0B,OAAO1B,QAAQ;GACtE,IAAI0B,OAAO1B,WAAW,WAAW;IAC/BkB,oBAAoBA,kBAAkBkB,QAAQC,MAAMA,MAAMxC,EAAE;IAC5DoB,UAAUkB,KAAKtC,EAAE;GACnB,OAAO;IACLoB,YAAYA,UAAUmB,QAAQC,MAAMA,MAAMxC,EAAE;IAC5CqB,kBAAkBiB,KAAKtC,EAAE;GAC3B;EACF;EAEAsB,YAAYtB,MAAMkC;EAClBZ,YAAYO,OAAO5B,YAAYiC;EAC/BX,WAAWW,aAAa;GACtB,GAAID,UAAU3B,gBAAgB,OAAO,EAAEA,aAAa,KAAc,IAAI,CAAC;GACvEN;GACAC,UAAU4B,OAAO5B;GACjBC,YAAY2B,OAAO3B;GACnBC,QAAQ0B,OAAO1B;GACfC,OAAOyB,OAAOzB;GACdK,eAAeoB,OAAOpB;GACtBJ,QAAQwB,OAAOxB;GACfE,gBACE4B,gBACA3B,QAAQkC,QAAQ;IACdzC,UAAU4B,OAAO5B;IACjBC,YAAY2B,OAAO3B;GACrB,CAAC;EACL;CACF;CACA,OAAO;EAAEkB;EAAWC;EAAmBC;EAAaC;CAAW;AACjE;AAcA,MAAa2B,sCAAyD;CACpEN,WAAW;CACXC,WAAW3B,KAAAA;CACX4B,eAAe;CACfC,QAAQ7B,KAAAA;CACR8B,aAAa9B,KAAAA;CACbE,WAAW,CAAA;CACXC,mBAAmB,CAAA;CACnBC,aAAaxB,gBAAgB;CAC7ByB,YAAYzB,gBAAgB;AAC9B;AAEA,MAAaqD,iBACXC,UAC6C;CAC7C,IAAIpD;CACJ;EACEA,KAAK,GAAGe,yBAAyBlB,WAAW;QACrCuD,MAAM9B,YAAYtB;CAC3B,MAAMkC,YAAYpB,sBAAsBd,EAAE;CAC1C,OAAO;EACLA;EACAoD,OAAO;GACL,GAAGA;GACHJ,aAAahD;GACbsB,aAAaxB,gBAAgBsD,MAAM9B,aAAa,GAC7CtB,KAAKkC,UACR,CAAC;GACDX,YAAYzB,gBAAgBsD,MAAM7B,YAAY,GAC3CW,YAAY;IACX/B,QAAQ;IACRH;IACAC,UAAUiB,KAAAA;IACVhB,YAAYgB,KAAAA;IACZd,OAAOc,KAAAA;IACPb,QAAQa,KAAAA;IACRZ,aAAa;GACf,EACF,CAAC;EACH;CACF;AACF;AAcA,MAAa+C,8BACXD,OACAE,YACAE,oBACsB;CACtB,MAAME,UAAU,IAAI9B,IAAIwB,MAAMhC,SAAS;CACvC,MAAMuC,WAAW,IAAI/B,IAAIwB,MAAM/B,iBAAiB;CAChD,MAAMuC,iBAAqC,CAAA;CAC3C,MAAMC,kBAAsC,CAAA;CAE5C,MAAMC,YAAYC,KAA0BE,SAC1CF,IAAI3B,IAAI6B,KAAKjE,EAAE,KAAMiE,KAAKhE,aAAaiB,KAAAA,KAAa6C,IAAI3B,IAAI6B,KAAKhE,QAAQ;CAE3E,KAAK,MAAMgE,QAAQC,OAAOC,OAAOf,MAAM7B,UAAU,GAAG;EAClD,MAAM6C,SAASZ,gBAAgBa,IAAIJ,KAAKjE,EAAE;EAC1C,IAAIoE,WAAWlD,KAAAA,KAAakD,WAAW,OAAO;EAC9C,IAAIA,WAAWlD,KAAAA,KAAa+C,KAAK3D,gBAAgB,MAAM;EAEvD,IAAI2D,KAAK9D,WAAW,aAAa,CAAC2D,SAASJ,SAASO,IAAI,GAAG;GACzDL,eAAetB,KAAK2B,IAAI;GACxBP,QAAQrB,IAAI4B,KAAKjE,EAAE;EACrB,OAAO,IAAIiE,KAAK9D,WAAW,cAAc,CAAC2D,SAASH,UAAUM,IAAI,GAAG;GAClEJ,gBAAgBvB,KAAK2B,IAAI;GACzBN,SAAStB,IAAI4B,KAAKjE,EAAE;EACtB;CACF;CAEA,IAAI4D,eAAeU,WAAW,KAAKT,gBAAgBS,WAAW,GAAG,OAAOlB;CAExE,MAAMmB,YAAYR,KAAwBE,SAA2B;EACnE,MAAMO,OAAOT,IAAIU,QAAQR,KAAKjE,EAAE;EAChC,IAAIwE,SAAS,IAAI,OAAOA;EACxB,MAAME,aACJT,KAAKhE,aAAaiB,KAAAA,IAAY6C,IAAIU,QAAQR,KAAKhE,QAAQ,IAAI;EAC7D,OAAOyE,eAAe,KAAKA,aAAaC,OAAOC;CACjD;CACAhB,eAAeiB,MACZC,GAAGC,MACFR,SAASjB,WAAWlC,WAAW0D,CAAC,IAAIP,SAASjB,WAAWlC,WAAW2D,CAAC,CACxE;CACAlB,gBAAgBgB,MACbC,GAAGC,MACFR,SAASjB,WAAWjC,mBAAmByD,CAAC,IACxCP,SAASjB,WAAWjC,mBAAmB0D,CAAC,CAC5C;CAEA,OAAO;EACL,GAAG3B;EACHhC,WAAW,CAAC,GAAGwC,eAAeoB,KAAKC,MAAMA,EAAEjF,EAAE,GAAG,GAAGoD,MAAMhC,SAAS;EAClEC,mBAAmB,CACjB,GAAGwC,gBAAgBmB,KAAKC,MAAMA,EAAEjF,EAAE,GAClC,GAAGoD,MAAM/B,iBAAiB;CAE9B;AACF;AAEA,MAAa6D,kBACX9B,UAEA,IAAI+B,IAAIjB,OAAOC,OAAOf,MAAM7B,UAAU,CAAC,CAACyD,KAAKC,MAAM,CAACA,EAAEjF,IAAIiF,EAAE9E,MAAM,CAAC,CAAC;AAEtE,MAAaiF,iBACXhC,OACAiC,uBACG;CACH,MAAMC,MAAMpB,OAAOqB,OAAOnC,MAAM9B,aAAa+D,kBAAkB,IAC3DjC,MAAM9B,YAAY+D,sBAClBnE,KAAAA;CACJ,IAAIoE,QAAQpE,KAAAA,GAAW,OAAOA,KAAAA;CAC9B,OAAOgD,OAAOqB,OAAOnC,MAAM7B,YAAY+D,GAAG,IACtClC,MAAM7B,WAAW+D,OACjBpE,KAAAA;AACN;AAEA,MAAasE,8BACXpC,OACAqC,UACAxF,UACAC,YACAwF,qBAKG;CACH,MAAMxD,YAAYpB,sBAAsB2E,QAAQ;CAChD,MAAMxB,OAAOC,OAAOqB,OAAOnC,MAAM7B,YAAYW,SAAS,IAClDkB,MAAM7B,WAAWW,aACjBhB,KAAAA;CACJ,IAAI,CAAC+C,MACH,OAAO;EAAEb;EAAOuC,kBAAkBzE,KAAAA;EAAW0E,mBAAmB1D;CAAU;CAM5E,MAAM2D,kBAAkB3B,OAAOqB,OAAOnC,MAAM9B,aAAarB,QAAQ,IAC7DmD,MAAM9B,YAAYrB,YAClBiB,KAAAA;CACJ,MAAM4E,aACJD,oBAAoB3E,KAAAA,KACpB2E,oBAAoB3D,aACpBgC,OAAOqB,OAAOnC,MAAM7B,YAAYsE,eAAe,IAC3CzC,MAAM7B,WAAWsE,mBACjB3E,KAAAA;CACN,MAAM6E,aACJF,oBAAoB3E,KAAAA,KAAa4E,eAAe5E,KAAAA,IAC5C;EAAEgB,WAAW2D;EAAiB5B,MAAM6B;CAAW,IAC/C5E,KAAAA;CACN,MAAM8E,oBACJN,qBAAqBxE,KAAAA,KACrBgD,OAAOqB,OAAOnC,MAAM9B,aAAaoE,gBAAgB,IAC7CtC,MAAM9B,YAAYoE,oBAClBxE,KAAAA;CACN,MAAM0E,oBACJG,eAAe7E,KAAAA,KAAa8E,sBAAsBD,WAAW7D,YACzD6D,WAAW7D,YACXA;CACN,MAAMyD,mBACJI,eAAe7E,KAAAA,IACXA,KAAAA,IACA0E,sBAAsB1D,YACpB6D,WAAW7D,YACXA;CACR,MAAM+D,qBAAqB/F,cAAc6F,YAAY9B,KAAK/D;CAC1D,MAAMqB,aAAazB,gBAAgBsD,MAAM7B,UAAU;CACnD,IAAIoE,qBAAqBzE,KAAAA,GAAW,OAAOK,WAAWoE;CACtD,MAAMO,kBAAkB;EACtB,GAAGjC;EACHjE,IAAI4F;EACJrF,gBAAgBC,QAAQkC,QAAQ;GAC9BzC;GACAC,YAAY+F;EACd,CAAC;EACDhG;EACAC,YAAY+F;CACd;CACA1E,WAAWqE,qBAAqB;EAC9B,GAAGG,YAAY9B;EACf,GAAGiC;EACH9F,OAAO6D,KAAK7D,SAAS2F,YAAY9B,KAAK7D;EACtCK,gBACG,mBAAmBwD,OAAOA,KAAKxD,gBAAgBS,KAAAA,OAC/C6E,cAAc,mBAAmBA,WAAW9B,OACzC8B,WAAW9B,KAAKxD,gBAChBS,KAAAA;EACNb,QAAQ4D,KAAK5D,UAAU0F,YAAY9B,KAAK5D;CAC1C;CAEA,MAAMiB,cAAcxB,gBAAgBsD,MAAM9B,WAAW;CACrD,IAAIqE,qBAAqBzE,KAAAA,GAClB;OAAA,MAAM,CAAClB,IAAImG,WAAWjC,OAAOkC,QAAQ9E,WAAW,GACnD,IAAI6E,WAAWR,kBAAkBrE,YAAYtB,MAAM4F;CAAAA;CAGvDtE,YAAYmE,YAAYG;CACxBtE,YAAYrB,YAAY2F;CAExB,MAAMS,UAAUtC,QACdgC,eAAe7E,KAAAA,IACX6C,MACAA,IACGxB,QAAQvC,OAAOA,OAAO+F,WAAW9B,KAAKjE,EAAE,CAAC,CACzCgF,KAAKhF,OAAQA,OAAOiE,KAAKjE,KAAK4F,oBAAoB5F,EAAG;CAE9D,OAAO;EACLoD,OAAO;GACL,GAAGA;GACHhC,WAAWiF,OAAOjD,MAAMhC,SAAS;GACjCC,mBAAmBgF,OAAOjD,MAAM/B,iBAAiB;GACjDC;GACAC;EACF;EACAoE;EACAC;CACF;AACF;AAEA,MAAMU,qBACJlD,OACAiC,oBACAkB,WACAhG,mBACG;CACH,MAAM0D,OAAOmB,cAAchC,OAAOiC,kBAAkB;CACpD,IAAI,CAACpB,MAAM,OAAOb;CAElB,MAAM,EAAEpD,IAAIG,QAAQqG,eAAevC;CACnC,IAAIuC,eAAeD,WAAW,OAAOnD;CAErC,IAAIqD;CACJ,IAAIF,cAAc,WAAW;EAC3B,IAAItC,KAAK9D,WAAW,OAAO;GAGzB,IAAII,mBAAmBW,KAAAA,GAAW,OAAOkC;GACzCqD,WAAW;IAAE,GAAGxC;IAAM1D;IAAgBJ,QAAQoG;GAAU;EAC1D,OACEE,WAAW;GAAE,GAAGxC;GAAM9D,QAAQoG;EAAU;CAE5C;CAEA,MAAMG,WAAW,EAAE,GAAGtD,MAAM;CAG5B,QAAQoD,YAAR;EACE,KAAK;GACHE,SAAS1D,cAAc9B,KAAAA;GACvB;EACF,KAAK;GACHwF,SAAStF,YAAYsF,SAAStF,UAAUmB,QAAQC,MAAMA,MAAMxC,EAAE;GAC9D;EACF,KAAK;GACH0G,SAASrF,oBAAoBqF,SAASrF,kBAAkBkB,QACrDC,MAAMA,MAAMxC,EACf;GACA;EAEF,SAEE,MAAM,IAAI+B,MAAM,sBAAsBD,YAAkB;CAE5D;CAGA,QAAQyE,WAAR;EACE,KAAK;GACHG,SAAStF,YAAY,CAACpB,IAAI,GAAG0G,SAAStF,SAAS;GAC/C;EAEF,KAAK;GACHsF,SAASrF,oBAAoB,CAACrB,IAAI,GAAG0G,SAASrF,iBAAiB;GAC/D;EAEF,KAAK,WAAW;GACd,MAAMa,YAAYkB,MAAM9B,YAAY+D;GACpC,MAAM9D,aAAazB,gBAAgB4G,SAASnF,UAAU;GACtD,OAAOA,WAAWW;GAClBwE,SAASnF,aAAaA;GACtB,MAAMD,cAAcxB,gBAAgB4G,SAASpF,WAAW;GACxD,KAAK,MAAM,CAACqF,KAAKC,UAAU1C,OAAOkC,QAAQ9E,WAAW,GACnD,IAAIsF,UAAU1E,WAAW,OAAOZ,YAAYqF;GAE9CD,SAASpF,cAAcA;GACvB;EACF;EAEA,SAEE,MAAM,IAAIS,MAAM,sBAAsBD,WAAkB;CAE5D;CAEA,IAAI2E,aAAavF,KAAAA,GACfwF,SAASnF,aAAa;EAAE,GAAGmF,SAASnF;GAAavB,KAAKyG;CAAS;CAGjE,OAAOC;AACT;;;;;;;AAQA,MAAaG,uBACXzD,OACAiC,oBACAkB,cACGD,kBAAkBlD,OAAOiC,oBAAoBkB,WAAWrF,KAAAA,CAAS;;;;;AAMtE,MAAa4F,2BACX1D,OACAiC,oBACA9E,mBACG+F,kBAAkBlD,OAAOiC,oBAAoB,WAAW9E,cAAc"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//#region src/runtimes/remote-thread-list/title-generation.d.ts
|
|
2
|
+
type ThreadTitleClaim = {
|
|
3
|
+
readonly title: string;
|
|
4
|
+
readonly order: number;
|
|
5
|
+
readonly settled: Promise<boolean>;
|
|
6
|
+
readonly settle: (renamed: boolean) => void;
|
|
7
|
+
};
|
|
8
|
+
type ThreadTitleGeneration = {
|
|
9
|
+
readonly automatic: boolean;
|
|
10
|
+
readonly order: number;
|
|
11
|
+
claim: ThreadTitleClaim | null;
|
|
12
|
+
beforeGenerationClaims: readonly ThreadTitleClaim[];
|
|
13
|
+
superseded: boolean;
|
|
14
|
+
readonly persisted: Promise<string | undefined>;
|
|
15
|
+
readonly settlePersisted: (title: string | undefined) => void;
|
|
16
|
+
};
|
|
17
|
+
type ThreadTitleState = {
|
|
18
|
+
generations: Set<ThreadTitleGeneration>;
|
|
19
|
+
pendingClaim: ThreadTitleClaim | null;
|
|
20
|
+
inFlightClaims: Set<ThreadTitleClaim>;
|
|
21
|
+
manualTitle: string | undefined;
|
|
22
|
+
latestExplicit: ThreadTitleGeneration | null;
|
|
23
|
+
nextOrder: number;
|
|
24
|
+
};
|
|
25
|
+
type ThreadTitleGenerationRun = {
|
|
26
|
+
states: Map<string, ThreadTitleState>;
|
|
27
|
+
threadId: string;
|
|
28
|
+
automatic: boolean;
|
|
29
|
+
generate: (onTitle: (title: string | undefined) => Promise<void>) => Promise<void>;
|
|
30
|
+
rename: (title: string) => Promise<void>;
|
|
31
|
+
applyTitle: (title: string | undefined) => Promise<void>;
|
|
32
|
+
};
|
|
33
|
+
declare function startThreadTitleRename(states: Map<string, ThreadTitleState>, threadId: string, title: string): ThreadTitleClaim;
|
|
34
|
+
declare function finishThreadTitleRename(states: Map<string, ThreadTitleState>, threadId: string, claim: ThreadTitleClaim, renamed: boolean): void;
|
|
35
|
+
declare function clearThreadTitleState(states: Map<string, ThreadTitleState>, threadId: string): void;
|
|
36
|
+
/**
|
|
37
|
+
* Runs one title generation under the per-thread rename claims and generation
|
|
38
|
+
* ordering, so the title that wins locally is also the title left on the
|
|
39
|
+
* server.
|
|
40
|
+
*
|
|
41
|
+
* A generated run persists the title itself, and the adapter exposes no
|
|
42
|
+
* compare-and-set, so the run that loses reasserts the winner through `rename`
|
|
43
|
+
* only after `generate` has resolved. Reasserting mid-stream would race the
|
|
44
|
+
* run's own write and lose the winning title on the server.
|
|
45
|
+
*/
|
|
46
|
+
declare function runThreadTitleGeneration({ states, threadId, automatic, generate, rename, applyTitle }: ThreadTitleGenerationRun): Promise<void>;
|
|
47
|
+
//#endregion
|
|
48
|
+
export { ThreadTitleClaim, ThreadTitleGenerationRun, ThreadTitleState, clearThreadTitleState, finishThreadTitleRename, runThreadTitleGeneration, startThreadTitleRename };
|
|
49
|
+
//# sourceMappingURL=title-generation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"title-generation.d.ts","names":[],"sources":["../../../src/runtimes/remote-thread-list/title-generation.ts"],"mappings":";KAAY;WACD;WACA;WACA,SAAS;WACT,SAAS;;KAGf;WACM;WACA;EACT,OAAO;EACP,iCAAiC;EACjC;WACS,WAAW;WACX,kBAAkB;;KAGjB;EACV,aAAa,IAAI;EACjB,cAAc;EACd,gBAAgB,IAAI;EACpB;EACA,gBAAgB;EAChB;;KAGU;EACV,QAAQ,YAAY;EACpB;EACA;EACA,WACE,UAAU,8BAA8B,kBACrC;EACL,SAAS,kBAAkB;EAC3B,aAAa,8BAA8B;;iBAuD7B,uBACd,QAAQ,YAAY,mBACpB,kBACA,gBACC;iBAoBa,wBACd,QAAQ,YAAY,mBACpB,kBACA,OAAO,kBACP;iBA2Bc,sBACd,QAAQ,YAAY,mBACpB;;;;;;;;;;;iBAsDoB,2BACpB,QACA,UACA,WACA,UACA,QACA,cACC,2BAA2B"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
//#region src/runtimes/remote-thread-list/title-generation.ts
|
|
2
|
+
function getThreadTitleState(states, threadId) {
|
|
3
|
+
let state = states.get(threadId);
|
|
4
|
+
if (state === void 0) {
|
|
5
|
+
state = {
|
|
6
|
+
generations: /* @__PURE__ */ new Set(),
|
|
7
|
+
pendingClaim: null,
|
|
8
|
+
inFlightClaims: /* @__PURE__ */ new Set(),
|
|
9
|
+
manualTitle: void 0,
|
|
10
|
+
latestExplicit: null,
|
|
11
|
+
nextOrder: 0
|
|
12
|
+
};
|
|
13
|
+
states.set(threadId, state);
|
|
14
|
+
}
|
|
15
|
+
return state;
|
|
16
|
+
}
|
|
17
|
+
function pruneThreadTitleState(states, threadId, state) {
|
|
18
|
+
if (state.generations.size === 0 && state.pendingClaim === null && state.inFlightClaims.size === 0 && state.manualTitle === void 0 && states.get(threadId) === state) states.delete(threadId);
|
|
19
|
+
}
|
|
20
|
+
function isCurrentGeneration(state, generation) {
|
|
21
|
+
return !generation.superseded && (state.latestExplicit?.order ?? 0) <= generation.order;
|
|
22
|
+
}
|
|
23
|
+
function isCurrentClaim(state, claim) {
|
|
24
|
+
return (state.latestExplicit?.order ?? 0) < claim.order;
|
|
25
|
+
}
|
|
26
|
+
function startThreadTitleRename(states, threadId, title) {
|
|
27
|
+
const state = getThreadTitleState(states, threadId);
|
|
28
|
+
let settle;
|
|
29
|
+
const settled = new Promise((resolve) => {
|
|
30
|
+
settle = resolve;
|
|
31
|
+
});
|
|
32
|
+
const claim = {
|
|
33
|
+
title,
|
|
34
|
+
order: ++state.nextOrder,
|
|
35
|
+
settled,
|
|
36
|
+
settle
|
|
37
|
+
};
|
|
38
|
+
state.pendingClaim = claim;
|
|
39
|
+
state.inFlightClaims.add(claim);
|
|
40
|
+
for (const generation of state.generations) if (generation.order < claim.order) generation.claim = claim;
|
|
41
|
+
return claim;
|
|
42
|
+
}
|
|
43
|
+
function finishThreadTitleRename(states, threadId, claim, renamed) {
|
|
44
|
+
claim.settle(renamed);
|
|
45
|
+
const state = states.get(threadId);
|
|
46
|
+
if (state === void 0) return;
|
|
47
|
+
state.inFlightClaims.delete(claim);
|
|
48
|
+
if (state.pendingClaim === claim) {
|
|
49
|
+
state.pendingClaim = null;
|
|
50
|
+
if (renamed) state.manualTitle = claim.title;
|
|
51
|
+
}
|
|
52
|
+
pruneThreadTitleState(states, threadId, state);
|
|
53
|
+
}
|
|
54
|
+
function takeManualTitle(states, threadId, state) {
|
|
55
|
+
const retained = state.manualTitle;
|
|
56
|
+
if (retained === void 0) return void 0;
|
|
57
|
+
state.manualTitle = void 0;
|
|
58
|
+
pruneThreadTitleState(states, threadId, state);
|
|
59
|
+
return retained;
|
|
60
|
+
}
|
|
61
|
+
function clearThreadTitleState(states, threadId) {
|
|
62
|
+
states.delete(threadId);
|
|
63
|
+
}
|
|
64
|
+
function startThreadTitleGeneration(states, threadId, automatic) {
|
|
65
|
+
const state = getThreadTitleState(states, threadId);
|
|
66
|
+
if (automatic && state.pendingClaim === null && takeManualTitle(states, threadId, state) !== void 0) return null;
|
|
67
|
+
let settlePersisted;
|
|
68
|
+
const persisted = new Promise((resolve) => {
|
|
69
|
+
settlePersisted = resolve;
|
|
70
|
+
});
|
|
71
|
+
const generation = {
|
|
72
|
+
automatic,
|
|
73
|
+
order: ++state.nextOrder,
|
|
74
|
+
claim: automatic ? state.pendingClaim : null,
|
|
75
|
+
beforeGenerationClaims: automatic ? [] : [...state.inFlightClaims],
|
|
76
|
+
superseded: false,
|
|
77
|
+
persisted,
|
|
78
|
+
settlePersisted
|
|
79
|
+
};
|
|
80
|
+
if (!automatic) {
|
|
81
|
+
state.pendingClaim = null;
|
|
82
|
+
state.manualTitle = void 0;
|
|
83
|
+
state.latestExplicit = generation;
|
|
84
|
+
for (const active of state.generations) if (active.automatic) active.superseded = true;
|
|
85
|
+
}
|
|
86
|
+
state.generations.add(generation);
|
|
87
|
+
return generation;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Runs one title generation under the per-thread rename claims and generation
|
|
91
|
+
* ordering, so the title that wins locally is also the title left on the
|
|
92
|
+
* server.
|
|
93
|
+
*
|
|
94
|
+
* A generated run persists the title itself, and the adapter exposes no
|
|
95
|
+
* compare-and-set, so the run that loses reasserts the winner through `rename`
|
|
96
|
+
* only after `generate` has resolved. Reasserting mid-stream would race the
|
|
97
|
+
* run's own write and lose the winning title on the server.
|
|
98
|
+
*/
|
|
99
|
+
async function runThreadTitleGeneration({ states, threadId, automatic, generate, rename, applyTitle }) {
|
|
100
|
+
const state = getThreadTitleState(states, threadId);
|
|
101
|
+
const generation = startThreadTitleGeneration(states, threadId, automatic);
|
|
102
|
+
if (generation === null) return;
|
|
103
|
+
let persistedTitle;
|
|
104
|
+
let persistedOrder = generation.order;
|
|
105
|
+
const settleClaim = async (claim) => {
|
|
106
|
+
const renamed = await claim.settled;
|
|
107
|
+
if (generation.claim !== claim) return void 0;
|
|
108
|
+
if (!renamed) {
|
|
109
|
+
generation.claim = null;
|
|
110
|
+
if (state.pendingClaim === claim) state.pendingClaim = null;
|
|
111
|
+
}
|
|
112
|
+
return renamed;
|
|
113
|
+
};
|
|
114
|
+
const retainManualTitle = () => automatic && takeManualTitle(states, threadId, state) !== void 0;
|
|
115
|
+
const repairLostRace = async () => {
|
|
116
|
+
if (persistedTitle === void 0) return;
|
|
117
|
+
while (true) {
|
|
118
|
+
const claim = generation.claim;
|
|
119
|
+
if (claim !== null && claim.order > persistedOrder && isCurrentClaim(state, claim)) {
|
|
120
|
+
if (await settleClaim(claim) === true) {
|
|
121
|
+
persistedTitle = claim.title;
|
|
122
|
+
persistedOrder = claim.order;
|
|
123
|
+
await rename(claim.title);
|
|
124
|
+
}
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const winner = state.latestExplicit;
|
|
128
|
+
if (winner === null || winner.order <= persistedOrder) return;
|
|
129
|
+
const title = await winner.persisted;
|
|
130
|
+
if (state.latestExplicit !== winner) continue;
|
|
131
|
+
persistedOrder = winner.order;
|
|
132
|
+
if (title === void 0 || title === persistedTitle) return;
|
|
133
|
+
persistedTitle = title;
|
|
134
|
+
await rename(title);
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
const runGeneration = async () => {
|
|
138
|
+
if (generation.beforeGenerationClaims.length > 0) await Promise.all(generation.beforeGenerationClaims.map((claim) => claim.settled));
|
|
139
|
+
if (generation.claim !== null) {
|
|
140
|
+
const renamed = await settleClaim(generation.claim);
|
|
141
|
+
if (renamed === true) {
|
|
142
|
+
generation.superseded = true;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (renamed === false && retainManualTitle()) return;
|
|
146
|
+
}
|
|
147
|
+
if (!isCurrentGeneration(state, generation)) return;
|
|
148
|
+
let sawTitle = false;
|
|
149
|
+
let lastTitle;
|
|
150
|
+
await generate(async (title) => {
|
|
151
|
+
sawTitle = true;
|
|
152
|
+
lastTitle = title;
|
|
153
|
+
if (title !== void 0) persistedTitle = title;
|
|
154
|
+
const claim = generation.claim;
|
|
155
|
+
if (claim !== null) {
|
|
156
|
+
if (await settleClaim(claim) !== false) return;
|
|
157
|
+
if (retainManualTitle()) {
|
|
158
|
+
generation.superseded = true;
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (isCurrentGeneration(state, generation)) await applyTitle(lastTitle);
|
|
163
|
+
});
|
|
164
|
+
while (true) {
|
|
165
|
+
const claim = generation.claim;
|
|
166
|
+
if (claim === null) return;
|
|
167
|
+
const renamed = await settleClaim(claim);
|
|
168
|
+
if (renamed === void 0) continue;
|
|
169
|
+
if (renamed === false) {
|
|
170
|
+
if (retainManualTitle()) return;
|
|
171
|
+
if (sawTitle && isCurrentGeneration(state, generation)) await applyTitle(lastTitle);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (!isCurrentClaim(state, claim)) return;
|
|
175
|
+
await rename(claim.title);
|
|
176
|
+
persistedTitle = claim.title;
|
|
177
|
+
persistedOrder = claim.order;
|
|
178
|
+
if (generation.claim !== claim) continue;
|
|
179
|
+
if (!isCurrentGeneration(state, generation)) return;
|
|
180
|
+
await applyTitle(claim.title);
|
|
181
|
+
generation.superseded = true;
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
try {
|
|
186
|
+
await runGeneration();
|
|
187
|
+
await repairLostRace();
|
|
188
|
+
} finally {
|
|
189
|
+
generation.settlePersisted(persistedTitle);
|
|
190
|
+
state.generations.delete(generation);
|
|
191
|
+
pruneThreadTitleState(states, threadId, state);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
//#endregion
|
|
195
|
+
export { clearThreadTitleState, finishThreadTitleRename, runThreadTitleGeneration, startThreadTitleRename };
|
|
196
|
+
|
|
197
|
+
//# sourceMappingURL=title-generation.js.map
|