mirror of
https://github.com/olehomelchenko/astrolabe.git
synced 2026-08-08 02:02:33 +00:00
Services: delete dead RelationshipService; rename persistence to snippet-persistence
This commit is contained in:
@@ -398,7 +398,7 @@ every keystroke. A startup subscriber observes the draft and debounces the expen
|
||||
work:
|
||||
|
||||
```ts
|
||||
// src/app/orchestration/persistence.ts
|
||||
// src/app/orchestration/snippet-persistence.ts
|
||||
import { useSnippetStore } from '../stores/SnippetStore';
|
||||
import { saveSnippet } from '../infrastructure/snippet-store'; // IndexedDB adapter
|
||||
|
||||
|
||||
@@ -451,7 +451,7 @@ export async function saveSnippet(s: Snippet): Promise<void> {
|
||||
fire-and-forget `void saveSnippet(n)` re-buries the very error the adapter took care to
|
||||
throw. Persistence write-backs are wired as store subscribers, so the surfacing path is:
|
||||
|
||||
`orchestration/persistence.ts` (write-through `.catch`) / `orchestration/startup.ts` (load
|
||||
`orchestration/snippet-persistence.ts` (write-through `.catch`) / `orchestration/startup.ts` (load
|
||||
`.catch`, then run in memory) → `services/storage-errors.ts` (pure error→message mapper) →
|
||||
`notify()` (`stores/NotificationStore`) → `Toaster`.
|
||||
|
||||
|
||||
@@ -260,22 +260,14 @@ export function datasetUsageCounts(
|
||||
): Map<string, number>;
|
||||
```
|
||||
|
||||
```ts
|
||||
// src/app/services/RelationshipService.ts — snapshot wrapper for non-reactive callers
|
||||
|
||||
import { snippetsReferencingDataset } from '../../core/relationships';
|
||||
import { useSnippetStore } from '../stores/SnippetStore';
|
||||
|
||||
export function findSnippetsReferencingDataset(name: string): Snippet[] {
|
||||
return snippetsReferencingDataset(useSnippetStore.getState().snippets, name);
|
||||
}
|
||||
```
|
||||
|
||||
The reactive UI does **not** go through the service: a component subscribed to
|
||||
`snippets` calls the core helper directly, so the badge and Linked Snippets list
|
||||
update the moment any snippet's draft changes its refs (auto-save) or it is
|
||||
published — no duplicated matching logic, no `getState()` snapshot that would
|
||||
miss updates.
|
||||
There is no app-layer wrapper module around the scan. The reactive UI — a
|
||||
component subscribed to `snippets` — calls the core helper directly, so the
|
||||
badge and Linked Snippets list update the moment any snippet's draft changes
|
||||
its refs (auto-save) or it is published. A non-reactive caller binds the same
|
||||
helper to a snapshot inline at its call site
|
||||
(`snippetsReferencingDataset(useSnippetStore.getState().snippets, name)`) —
|
||||
one matching implementation, no `getState()` wrapper that would go stale in
|
||||
reactive code.
|
||||
|
||||
**Do**
|
||||
|
||||
@@ -283,7 +275,7 @@ miss updates.
|
||||
reactive and snapshot callers share it. It is O(snippets) but the collections
|
||||
are small (library budget ~5 MB); clarity beats an index.
|
||||
- Call the core helper directly from a store-subscribed component for reactivity;
|
||||
use the service wrapper only from non-reactive (snapshot) code.
|
||||
bind it to a `getState()` snapshot inline for non-reactive code.
|
||||
|
||||
**Don't**
|
||||
|
||||
@@ -334,9 +326,10 @@ export function dedupeIncomingDatasetNames(
|
||||
```
|
||||
|
||||
> If imported snippets reference the renamed dataset, their `datasetRefs` and
|
||||
> specs must be rewritten to the new name too — reuse the rename machinery in
|
||||
> §6 over the imported snippet set, or run `renameDatasetEverywhere` per applied
|
||||
> rename after the import is committed.
|
||||
> specs must be rewritten to the new name too. The import flow does this purely,
|
||||
> before anything is committed: `applyDatasetRenamesToSnippets`
|
||||
> (`core/import-normalize`) rewrites the incoming snippet set per applied rename
|
||||
> using the same `renameDatasetInSpec` machinery as §6.
|
||||
|
||||
### 5.1 Where the import/export flow lives, and its rules
|
||||
|
||||
@@ -427,43 +420,22 @@ export function renameDatasetInSpec(spec: Json, oldName: string, newName: string
|
||||
}
|
||||
```
|
||||
|
||||
```ts
|
||||
// src/app/services/RelationshipService.ts — thin store coordinator
|
||||
|
||||
import { useDatasetStore } from '../stores/DatasetStore';
|
||||
import { useSnippetStore } from '../stores/SnippetStore';
|
||||
|
||||
/**
|
||||
* Renames a dataset and propagates the rename to every referencing snippet
|
||||
* (spec, draftSpec, and datasetRefs). Returns the number of snippets changed.
|
||||
* Caller is responsible for collision policy on `newName` (reject vs suffix).
|
||||
*/
|
||||
export function renameDatasetEverywhere(
|
||||
oldName: string,
|
||||
newName: string,
|
||||
now?: Date,
|
||||
): { updated: number } {
|
||||
if (oldName === newName) return { updated: 0 };
|
||||
|
||||
// 1. Rename the dataset record itself.
|
||||
const dataset = useDatasetStore.getState().datasets.find((d) => d.name === oldName);
|
||||
if (dataset) useDatasetStore.getState().update(dataset.id, { name: newName }, now);
|
||||
|
||||
// 2 + 3. Delegate the spec/draftSpec/refs rewrite to the ONE rename impl — the
|
||||
// store action that owns the snippet collection. It matches by spec+draft
|
||||
// *content* (not `datasetRefs`, which mirrors only the draft) and recomputes
|
||||
// `datasetRefs` from the rewritten draft. Keeping the loop in the store means
|
||||
// the reactive editor buffer is refreshed in the same atomic update when the
|
||||
// active snippet's draft is rewritten.
|
||||
return { updated: useSnippetStore.getState().renameDatasetRefs(oldName, newName, now) };
|
||||
}
|
||||
```
|
||||
The one rename implementation in the live app is the store action
|
||||
**`SnippetStore.renameDatasetRefs(oldName, newName, now)`** — the action that
|
||||
owns the snippet collection. It matches by spec+draft _content_ (not
|
||||
`datasetRefs`, which mirrors only the draft), rewrites `spec`/`draftSpec` via
|
||||
`renameDatasetInSpec`, recomputes `datasetRefs` from the rewritten draft, and
|
||||
returns the number of snippets changed. Keeping the loop in the store means the
|
||||
reactive editor buffer is refreshed in the same atomic update when the active
|
||||
snippet's draft is rewritten. `DatasetStore.update` calls it whenever a save
|
||||
changes a dataset's name, so a rename propagates everywhere as part of the one
|
||||
user action — there is no separate coordinator module to call.
|
||||
|
||||
> **Collision on rename.** The UI rename form rejects a name already in use via
|
||||
> `isNameTaken(newName, datasets, dataset.id)`. Programmatic renames (e.g. an
|
||||
> import flow) instead resolve with `makeUniqueName` before calling
|
||||
> `renameDatasetEverywhere`. The propagation function itself does not invent a
|
||||
> name — it assumes `newName` is the agreed target.
|
||||
> import flow) instead resolve with `makeUniqueName` first. The propagation
|
||||
> action itself does not invent a name — it assumes `newName` is the agreed
|
||||
> target.
|
||||
|
||||
**Do**
|
||||
|
||||
@@ -492,18 +464,17 @@ export function renameDatasetEverywhere(
|
||||
|
||||
## 7. Where things live
|
||||
|
||||
| Concern | Location | Pure? | Tested |
|
||||
| ------------------------------------------------------------------------ | ----------------------------------------- | ------------------- | ----------- |
|
||||
| `makeUniqueName`, `isNameTaken` | `src/core/naming.ts` | yes | unit |
|
||||
| `extractDatasetRefs`, `recomputeDatasetRefs` | `src/core/spec-refs.ts` | yes | unit |
|
||||
| `renameDatasetInSpec` | `src/core/spec-refs.ts` | yes | unit |
|
||||
| `snippetsReferencingDataset`, `datasetUsageCounts` (reverse-lookup scan) | `src/core/relationships.ts` | yes | unit |
|
||||
| `findSnippetsReferencingDataset`, usage count (snapshot wrappers) | `src/app/services/RelationshipService.ts` | no (reads store) | integration |
|
||||
| `renameDatasetEverywhere` → `{ updated }` | `src/app/services/RelationshipService.ts` | no (mutates stores) | integration |
|
||||
| `dedupeIncomingDatasetNames` | `src/core/import-normalize.ts` | yes | unit |
|
||||
| Concern | Location | Pure? | Tested |
|
||||
| ------------------------------------------------------------------------ | -------------------------------- | ------------------- | ----------- |
|
||||
| `makeUniqueName`, `isNameTaken` | `src/core/naming.ts` | yes | unit |
|
||||
| `extractDatasetRefs`, `recomputeDatasetRefs` | `src/core/spec-refs.ts` | yes | unit |
|
||||
| `renameDatasetInSpec` | `src/core/spec-refs.ts` | yes | unit |
|
||||
| `snippetsReferencingDataset`, `datasetUsageCounts` (reverse-lookup scan) | `src/core/relationships.ts` | yes | unit |
|
||||
| `renameDatasetRefs` → updated count (rename propagation) | `src/app/stores/SnippetStore.ts` | no (mutates stores) | integration |
|
||||
| `dedupeIncomingDatasetNames` | `src/core/import-normalize.ts` | yes | unit |
|
||||
|
||||
The dividing line: anything that takes plain data and returns plain data is
|
||||
**core** and unit-tested in isolation; anything that reaches into a Zustand store
|
||||
is an **app service**. The rule of thumb — _the **draft** spec is the source of
|
||||
is a **store action or app service**. The rule of thumb — _the **draft** spec is the source of
|
||||
truth, `datasetRefs` mirrors it, the reverse lookup is derived_ — is what keeps
|
||||
the bidirectional link from ever needing manual repair.
|
||||
|
||||
@@ -138,7 +138,7 @@ control and freedom," #5 "error prevention"; spec §10 Reliability).
|
||||
|
||||
- **No silent data loss.** Edits auto-save as a **draft**; a known-good **published**
|
||||
version is always preserved separately (§03D). A failed persist **surfaces as a toast**,
|
||||
never a swallowed promise (`orchestration/persistence.ts`).
|
||||
never a swallowed promise (`orchestration/snippet-persistence.ts`).
|
||||
- **A marked exit from every committed change.** Revert restores the published spec;
|
||||
Escape closes modals; delete/revert/reset require confirmation first.
|
||||
- **Resilient rendering recovers on its own.** An invalid spec shows a readable error and
|
||||
|
||||
Reference in New Issue
Block a user