mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 20:21:09 +02:00
fix(ai-missions): strip Svelte \$state Proxies before Dexie writes
`createMission`, `updateMission`, and `{start,finish}Iteration` all
received caller-supplied objects that can be Svelte 5 \$state Proxies
(MissionInputPicker binds the inputs array with \$state). IndexedDB's
structured-clone algorithm doesn't accept proxied arrays and throws
`DataCloneError: [object Array] could not be cloned` — visible to
users as "Mission anlegen" failing silently after clicking Create.
Wrap each proxy-carrying payload in `structuredClone()` at the store
boundary:
- createMission: `inputs` + `cadence`
- updateMission: whole `patch` (anything can be proxy)
- startIteration: `plan`
- finishIteration: `plan` (conditional)
`structuredClone` is the native browser / Bun helper; strips Proxies
while preserving Dates / Maps / Sets / nested plain data. Store stays
robust to any future caller that forgets to snapshot before passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
a6d51afbc9
commit
394931e3b3
1 changed files with 15 additions and 6 deletions
|
|
@ -38,6 +38,12 @@ export interface CreateMissionInput {
|
|||
|
||||
export async function createMission(input: CreateMissionInput): Promise<Mission> {
|
||||
const now = new Date().toISOString();
|
||||
// `structuredClone` strips Svelte 5 $state Proxies before the record
|
||||
// hits IndexedDB — without it, Dexie throws DataCloneError on the
|
||||
// proxied `inputs` array / `cadence` object that callers pass in
|
||||
// from `$state` bindings (e.g. the MissionInputPicker).
|
||||
const inputsPlain = structuredClone(input.inputs ?? []);
|
||||
const cadencePlain = structuredClone(input.cadence);
|
||||
const mission: Mission = {
|
||||
id: crypto.randomUUID(),
|
||||
createdAt: now,
|
||||
|
|
@ -45,10 +51,10 @@ export async function createMission(input: CreateMissionInput): Promise<Mission>
|
|||
title: input.title,
|
||||
conceptMarkdown: input.conceptMarkdown,
|
||||
objective: input.objective,
|
||||
inputs: input.inputs ?? [],
|
||||
cadence: input.cadence,
|
||||
inputs: inputsPlain,
|
||||
cadence: cadencePlain,
|
||||
state: 'active',
|
||||
nextRunAt: nextRunForCadence(input.cadence, new Date()),
|
||||
nextRunAt: nextRunForCadence(cadencePlain, new Date()),
|
||||
iterations: [],
|
||||
};
|
||||
await table().add(mission);
|
||||
|
|
@ -91,8 +97,9 @@ export interface MissionPatch {
|
|||
}
|
||||
|
||||
export async function updateMission(id: string, patch: MissionPatch): Promise<void> {
|
||||
// Same Proxy-stripping reason as createMission.
|
||||
const mods: Partial<Mission> = {
|
||||
...patch,
|
||||
...structuredClone(patch),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
if (patch.cadence) {
|
||||
|
|
@ -149,7 +156,9 @@ export async function startIteration(
|
|||
const iteration: MissionIteration = {
|
||||
id: crypto.randomUUID(),
|
||||
startedAt: new Date().toISOString(),
|
||||
plan: input.plan,
|
||||
// Strip $state Proxies from the plan array so structured-clone
|
||||
// doesn't fail when Dexie serialises the row.
|
||||
plan: structuredClone(input.plan),
|
||||
overallStatus: 'running',
|
||||
};
|
||||
await table().update(missionId, {
|
||||
|
|
@ -181,7 +190,7 @@ export async function finishIteration(
|
|||
finishedAt: new Date().toISOString(),
|
||||
overallStatus: input.overallStatus,
|
||||
...(input.summary !== undefined ? { summary: input.summary } : {}),
|
||||
...(input.plan !== undefined ? { plan: input.plan } : {}),
|
||||
...(input.plan !== undefined ? { plan: structuredClone(input.plan) } : {}),
|
||||
}
|
||||
: it
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue