mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-20 00:02:19 +00:00
revert: drop unsuccessful path-stabilization heuristics + leftover [draft-sync] logs
This commit is contained in:
@@ -78,7 +78,7 @@
|
||||
import { writable } from 'svelte/store'
|
||||
import { defaultScriptLanguages, processLangs } from '$lib/scripts'
|
||||
import DefaultScripts from './DefaultScripts.svelte'
|
||||
import { getContext, onMount, setContext, tick, untrack } from 'svelte'
|
||||
import { getContext, onMount, setContext, untrack } from 'svelte'
|
||||
import EditorHeader from './EditorHeader.svelte'
|
||||
import AutosaveIndicator from './AutosaveIndicator.svelte'
|
||||
import LabelsInput from './LabelsInput.svelte'
|
||||
@@ -345,12 +345,6 @@
|
||||
let pathError = $state('')
|
||||
let loadingSave = $state(false)
|
||||
|
||||
console.log('[draft-sync] ScriptBuilder script-tag: enter', {
|
||||
userDraftPath,
|
||||
initialPath,
|
||||
contentLen: script.content?.length ?? 0,
|
||||
isEmpty: script.content == ''
|
||||
})
|
||||
if (script.content == '') {
|
||||
// Suspend autosave around the bootstrap mutations — seeding the
|
||||
// editor with the template's `initialCode` is a programmatic
|
||||
@@ -361,7 +355,13 @@
|
||||
// new drafts). Resumed in the async `.finally` so language
|
||||
// switches AFTER bootstrap (which also call `initContent`) sync
|
||||
// normally.
|
||||
console.log('[draft-sync] ScriptBuilder: bootstrap branch START', userDraftPath)
|
||||
//
|
||||
// NOTE: this is a best-effort partial defense — on warm in-app
|
||||
// navs it suppresses the seed/template writes, but on a HARD
|
||||
// PAGE RELOAD the Path widget's `$userStore`-gated mutation
|
||||
// cascade lands AFTER our `restartSync` and still POSTs. Fixing
|
||||
// that robustly needs a different "first user edit" signal
|
||||
// (e.g. Monaco input event) that hasn't been wired up yet.
|
||||
UserDraft.stopSync('script', userDraftPath)
|
||||
if (template === 'wac_python') {
|
||||
script.modules = {
|
||||
@@ -378,50 +378,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
console.log('[draft-sync] ScriptBuilder: calling initContent', userDraftPath)
|
||||
initContent(script.language, script.kind, template).finally(async () => {
|
||||
console.log('[draft-sync] ScriptBuilder: initContent finally', userDraftPath, {
|
||||
pathAlreadySet: !!script.path
|
||||
})
|
||||
// The `Path` widget assigns `script.path` from its
|
||||
// `$effect.pre` gated on `$workspaceStore + $userStore` —
|
||||
// which on a HARD-REFRESH-then-first-nav can be unset when
|
||||
// we get here. Worse, even on a warm reload the widget's
|
||||
// `reset()` mutates `meta` in TWO steps (`{owner: ''}` then
|
||||
// `meta.owner = username`), producing two distinct path
|
||||
// writes — exiting on the first non-empty value lets the
|
||||
// second one POST as the user's "first edit".
|
||||
//
|
||||
// Wait for `script.path` to STABILIZE: same value across
|
||||
// two consecutive ticks. `tick()` returns only after Svelte
|
||||
// has flushed pending effects, so a stable read means the
|
||||
// Path widget's cascade settled and any intermediate
|
||||
// mutations were already swallowed under suspension.
|
||||
//
|
||||
// Bounded so a stuck mount can't disable autosave forever.
|
||||
let waited = 0
|
||||
let last: string | undefined = undefined
|
||||
let stable = 0
|
||||
while (waited < 30) {
|
||||
await tick()
|
||||
waited++
|
||||
const cur = script.path
|
||||
if (cur && cur === last) {
|
||||
stable++
|
||||
if (stable >= 2) break
|
||||
} else {
|
||||
last = cur
|
||||
stable = 0
|
||||
}
|
||||
}
|
||||
console.log('[draft-sync] ScriptBuilder: path stable → restartSync', userDraftPath, {
|
||||
ticks: waited,
|
||||
path: script.path
|
||||
})
|
||||
initContent(script.language, script.kind, template).finally(() => {
|
||||
UserDraft.restartSync('script', userDraftPath)
|
||||
})
|
||||
} else {
|
||||
console.log('[draft-sync] ScriptBuilder: bootstrap SKIPPED (content non-empty)', userDraftPath)
|
||||
}
|
||||
|
||||
async function isTemplateScript() {
|
||||
@@ -466,16 +425,8 @@
|
||||
// doesn't run `inferArgs` on an empty `script.content` and toast
|
||||
// "Could not parse code". If a template script is then loaded
|
||||
// below we re-seed with the `templateScript=true` variant.
|
||||
console.log('[draft-sync] initContent: SYNC seed content', userDraftPath, {
|
||||
language,
|
||||
template
|
||||
})
|
||||
script.content = initialCode(language, kind, template, false)
|
||||
console.log('[draft-sync] initContent: awaiting isTemplateScript', userDraftPath)
|
||||
const templateScript = await isTemplateScript()
|
||||
console.log('[draft-sync] initContent: post-await', userDraftPath, {
|
||||
hasTemplateScript: !!templateScript
|
||||
})
|
||||
if (templateScript) {
|
||||
script.content = initialCode(language, kind, template, true)
|
||||
}
|
||||
@@ -585,11 +536,6 @@
|
||||
try {
|
||||
script.schema = script.schema ?? emptySchema()
|
||||
try {
|
||||
console.log('[draft-sync] editScript: inferArgs', {
|
||||
language: script.language,
|
||||
contentLen: script.content?.length ?? 0,
|
||||
contentHead: script.content?.slice(0, 80)
|
||||
})
|
||||
const result = await inferArgs(
|
||||
script.language,
|
||||
script.content,
|
||||
@@ -604,12 +550,6 @@
|
||||
script.has_preprocessor = result?.has_preprocessor || undefined
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[draft-sync] editScript: inferArgs threw', {
|
||||
language: script.language,
|
||||
contentLen: script.content?.length ?? 0,
|
||||
contentHead: script.content?.slice(0, 80),
|
||||
error
|
||||
})
|
||||
sendUserToast(`Could not parse code, are you sure it is valid?`, true)
|
||||
}
|
||||
|
||||
|
||||
@@ -444,13 +444,8 @@ export const UserDraft = {
|
||||
const ws = resolveWorkspace(opts)
|
||||
const mk = mapKey(ws, itemKind, path)
|
||||
const entry = entries.get(mk)
|
||||
if (entry) {
|
||||
entry.syncSuspended = true
|
||||
console.log('[draft-sync] stopSync (entry live)', mk)
|
||||
} else {
|
||||
pendingSuspensions.add(mk)
|
||||
console.log('[draft-sync] stopSync (queued, no entry yet)', mk)
|
||||
}
|
||||
if (entry) entry.syncSuspended = true
|
||||
else pendingSuspensions.add(mk)
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -463,13 +458,9 @@ export const UserDraft = {
|
||||
restartSync(itemKind: UserDraftItemKind, path: string, opts?: UserDraftOptions): void {
|
||||
const ws = resolveWorkspace(opts)
|
||||
const mk = mapKey(ws, itemKind, path)
|
||||
const hadPending = pendingSuspensions.delete(mk)
|
||||
pendingSuspensions.delete(mk)
|
||||
const entry = entries.get(mk)
|
||||
if (entry) entry.syncSuspended = false
|
||||
console.log('[draft-sync] restartSync', mk, {
|
||||
entryLive: !!entry,
|
||||
clearedPending: hadPending
|
||||
})
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -651,13 +642,8 @@ function acquireEntry(
|
||||
const existing = entries.get(mk)
|
||||
if (existing) {
|
||||
existing.count++
|
||||
console.log('[draft-sync] acquireEntry: reuse', mk, { newCount: existing.count })
|
||||
return
|
||||
}
|
||||
console.log('[draft-sync] acquireEntry: NEW', mk, {
|
||||
hasSeed: defaultValue !== undefined,
|
||||
pendingSuspended: pendingSuspensions.has(mk)
|
||||
})
|
||||
// Seed the cell with the caller's `defaultValue` (deep-cloned so the
|
||||
// cell owns its copy and the caller's baseline can't alias it). This is
|
||||
// how editors report the deployed/draft state until the user edits —
|
||||
@@ -705,42 +691,22 @@ function acquireEntry(
|
||||
const stored = cell.val
|
||||
if (stored !== undefined) readFieldsRecursively(stored.value)
|
||||
const next = stored === undefined ? undefined : JSON.stringify(stored)
|
||||
if (next === lastSerialized) {
|
||||
console.log('[draft-sync] effect: no change', mk, {
|
||||
nextLen: next?.length ?? 0
|
||||
})
|
||||
return
|
||||
}
|
||||
const nextLen = next?.length ?? 0
|
||||
const diff = nextLen - (lastSerialized?.length ?? 0)
|
||||
if (next === lastSerialized) return
|
||||
lastSerialized = next
|
||||
if (skipNextWrite) {
|
||||
skipNextWrite = false
|
||||
console.log('[draft-sync] effect: SWALLOW (skipNextWrite seed)', mk, {
|
||||
nextLen,
|
||||
diff
|
||||
})
|
||||
return
|
||||
}
|
||||
const entry = entries.get(mk)
|
||||
if (entry?.skipNextSync) {
|
||||
entry.skipNextSync = false
|
||||
console.log('[draft-sync] effect: SWALLOW (skipNextSync)', mk, { nextLen, diff })
|
||||
return
|
||||
}
|
||||
// `syncSuspended` swallows the POST but still advances
|
||||
// `lastSerialized` (above) so when sync resumes the next
|
||||
// real change is detected as a change — only the writes
|
||||
// made during suspension are dropped from the server's view.
|
||||
if (entry?.syncSuspended) {
|
||||
console.log('[draft-sync] effect: SWALLOW (syncSuspended)', mk, { nextLen, diff })
|
||||
return
|
||||
}
|
||||
console.log('[draft-sync] effect: POST', mk, {
|
||||
nextLen,
|
||||
diff,
|
||||
stack: new Error().stack?.split('\n').slice(1, 6).join('\n')
|
||||
})
|
||||
if (entry?.syncSuspended) return
|
||||
void UserDraftDbSyncer.save({
|
||||
workspace,
|
||||
itemKind,
|
||||
|
||||
@@ -147,9 +147,6 @@ const pendingSaveOpts = new Map<string, UserDraftDbSyncerSaveOpts>()
|
||||
|
||||
async function postSave(opts: UserDraftDbSyncerSaveOpts): Promise<void> {
|
||||
const key = draftKey(opts.workspace, opts.itemKind, opts.path)
|
||||
console.log('[draft-sync] postSave START (sending POST)', key, {
|
||||
valueIsNull: opts.value === null
|
||||
})
|
||||
try {
|
||||
const resp = await DraftService.saveDraft({
|
||||
workspace: opts.workspace,
|
||||
@@ -172,9 +169,8 @@ async function postSave(opts: UserDraftDbSyncerSaveOpts): Promise<void> {
|
||||
// newer `save()` that arrived during the POST replaces the entry
|
||||
// and must survive for the next flush / debouncer round.
|
||||
if (pendingSaveOpts.get(key) === opts) pendingSaveOpts.delete(key)
|
||||
console.log('[draft-sync] postSave SUCCESS', key)
|
||||
} catch (e) {
|
||||
console.error('[draft-sync] postSave FAILED', key, e)
|
||||
console.error('UserDraftDbSyncer.save failed', e)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,10 +284,6 @@ export const UserDraftDbSyncer = {
|
||||
|
||||
async save(opts: UserDraftDbSyncerSaveOpts): Promise<void> {
|
||||
const key = draftKey(opts.workspace, opts.itemKind, opts.path)
|
||||
console.log('[draft-sync] UserDraftDbSyncer.save called', key, {
|
||||
immediate: !!opts.immediate,
|
||||
valueIsNull: opts.value === null
|
||||
})
|
||||
// Track the latest unconfirmed save BEFORE entering the pipeline
|
||||
// so the unload flush has something to send even if the page
|
||||
// hides before the debouncer fires.
|
||||
|
||||
@@ -150,7 +150,6 @@
|
||||
// fires. `initialPath = ''` also makes ScriptBuilder open the
|
||||
// metadata drawer on mount. Strip the single-use flag last.
|
||||
if (page.url.searchParams.get('new_draft') === 'true') {
|
||||
console.log('[draft-sync] route: new_draft branch START', draftPath)
|
||||
// Suspend autosave for the whole new-draft bootstrap: the seed
|
||||
// `setDraftAndMeta` AND ScriptBuilder's `initContent` (which
|
||||
// fills `script.content` from a template) are both
|
||||
@@ -172,11 +171,9 @@
|
||||
} as unknown as EditableScript
|
||||
initialPath = ''
|
||||
savedScript = structuredClone(empty)
|
||||
console.log('[draft-sync] route: about to setDraftAndMeta(empty)', draftPath)
|
||||
scriptHandle.setDraftAndMeta(empty, {})
|
||||
fullyLoaded = true
|
||||
renderEditor = true
|
||||
console.log('[draft-sync] route: new_draft branch DONE', draftPath)
|
||||
return
|
||||
}
|
||||
if (hash) {
|
||||
|
||||
Reference in New Issue
Block a user