diff --git a/events.go b/events.go index 80aca8a..845626c 100644 --- a/events.go +++ b/events.go @@ -197,6 +197,9 @@ func (storeInstance *Store) notify(event Event) { if storeInstance == nil { return } + if event.Timestamp.IsZero() { + event.Timestamp = time.Now() + } storeInstance.closeLock.Lock() closed := storeInstance.closed diff --git a/events_test.go b/events_test.go index f644fbb..2dbf6ea 100644 --- a/events_test.go +++ b/events_test.go @@ -177,6 +177,25 @@ func TestEvents_OnChange_Good_GroupFilteredCallback(t *testing.T) { assert.Equal(t, []string{"theme=dark"}, seen) } +func TestEvents_Notify_Good_PopulatesTimestamp(t *testing.T) { + storeInstance, _ := New(":memory:") + defer storeInstance.Close() + + events := storeInstance.Watch("config") + defer storeInstance.Unwatch("config", events) + + storeInstance.notify(Event{Type: EventSet, Group: "config", Key: "theme", Value: "dark"}) + + select { + case event := <-events: + assert.False(t, event.Timestamp.IsZero()) + assert.Equal(t, "config", event.Group) + assert.Equal(t, "theme", event.Key) + case <-time.After(time.Second): + t.Fatal("timed out waiting for timestamped event") + } +} + func TestEvents_Watch_Good_BufferDrops(t *testing.T) { storeInstance, _ := New(":memory:") defer storeInstance.Close() diff --git a/workspace.go b/workspace.go index c44f53b..7f50a9c 100644 --- a/workspace.go +++ b/workspace.go @@ -32,8 +32,8 @@ FROM workspace_entries` var defaultWorkspaceStateDirectory = ".core/state/" -// Workspace buffers mutable work-in-progress in `.core/state/scroll-session.duckdb` -// until Commit or Discard removes the file. +// Workspace buffers mutable work-in-progress in a SQLite file under +// `.core/state/scroll-session.duckdb` until Commit or Discard removes it. // // Usage example: `workspace, err := storeInstance.NewWorkspace("scroll-session-2026-03-30"); if err != nil { return }; defer workspace.Discard(); _ = workspace.Put("like", map[string]any{"user": "@alice"})` type Workspace struct { @@ -63,8 +63,8 @@ func (workspace *Workspace) DatabasePath() string { return workspace.databasePath } -// Close leaves `.core/state/scroll-session-2026-03-30.duckdb` on disk so a -// later store instance can recover it as an orphan. +// Close leaves the SQLite workspace file `.core/state/scroll-session-2026-03-30.duckdb` +// on disk so a later store instance can recover it as an orphan. // // Usage example: `if err := workspace.Close(); err != nil { return }; orphans := storeInstance.RecoverOrphans(".core/state"); _ = orphans` func (workspace *Workspace) Close() error { @@ -98,8 +98,9 @@ func (workspace *Workspace) ensureReady(operation string) error { return nil } -// NewWorkspace creates `.core/state/scroll-session-2026-03-30.duckdb` and -// removes it when the workspace is committed or discarded. +// NewWorkspace creates a SQLite workspace file at +// `.core/state/scroll-session-2026-03-30.duckdb` and removes it when the +// workspace is committed or discarded. // // Usage example: `workspace, err := storeInstance.NewWorkspace("scroll-session-2026-03-30"); if err != nil { return }; defer workspace.Discard()` func (storeInstance *Store) NewWorkspace(name string) (*Workspace, error) { @@ -135,8 +136,8 @@ func (storeInstance *Store) NewWorkspace(name string) (*Workspace, error) { }, nil } -// discoverOrphanWorkspacePaths(".core/state") returns leftover workspace files -// such as `scroll-session.duckdb` without opening them. +// discoverOrphanWorkspacePaths(".core/state") returns leftover SQLite workspace +// files such as `scroll-session.duckdb` without opening them. func discoverOrphanWorkspacePaths(stateDirectory string) []string { filesystem := (&core.Fs{}).NewUnrestricted() if stateDirectory == "" {