Repository navigation
Use new typed RDF literals instead of resolveLanguage: literal where it makes sense
#621
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
1090536
0321e5a
0f2a2f3
9d45f98
f9ceed0
360bb1d
35f5b34
66a7fbb
63cfb73
6d944a9
acc1025
806742a
cb4efb4
33a63a0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| import { | ||
| stripNeighbourhoodPrefix, | ||
| restoreNeighbourhoodPrefix, | ||
| stripChannelPrefix, | ||
| restoreChannelPrefix, | ||
| } from '../routeUtils'; | ||
|
|
||
| describe('stripNeighbourhoodPrefix', () => { | ||
| it('strips neighbourhood:// prefix', () => { | ||
| expect(stripNeighbourhoodPrefix('neighbourhood://abc123')).toBe('abc123'); | ||
| }); | ||
|
|
||
| it('strips private:// prefix', () => { | ||
| expect(stripNeighbourhoodPrefix('private://abc123')).toBe('abc123'); | ||
| }); | ||
|
|
||
| it('returns bare strings unchanged', () => { | ||
| expect(stripNeighbourhoodPrefix('abc123')).toBe('abc123'); | ||
| }); | ||
|
|
||
| it('returns empty string for undefined/empty input', () => { | ||
| expect(stripNeighbourhoodPrefix(undefined as unknown as string)).toBe(''); | ||
| expect(stripNeighbourhoodPrefix('')).toBe(''); | ||
| }); | ||
| }); | ||
|
|
||
| describe('restoreNeighbourhoodPrefix', () => { | ||
| it('prepends neighbourhood:// to bare IDs', () => { | ||
| expect(restoreNeighbourhoodPrefix('abc123')).toBe('neighbourhood://abc123'); | ||
| }); | ||
|
|
||
| it('passes through neighbourhood:// IDs unchanged', () => { | ||
| expect(restoreNeighbourhoodPrefix('neighbourhood://abc123')).toBe('neighbourhood://abc123'); | ||
| }); | ||
|
|
||
| it('passes through private:// IDs unchanged', () => { | ||
| expect(restoreNeighbourhoodPrefix('private://abc123')).toBe('private://abc123'); | ||
| }); | ||
|
|
||
| it('returns empty string for undefined/empty input', () => { | ||
| expect(restoreNeighbourhoodPrefix(undefined as unknown as string)).toBe(''); | ||
| expect(restoreNeighbourhoodPrefix('')).toBe(''); | ||
| }); | ||
| }); | ||
|
|
||
| describe('stripChannelPrefix', () => { | ||
| it('strips literal:string: prefix', () => { | ||
| expect(stripChannelPrefix('literal:string:abc123')).toBe('abc123'); | ||
| }); | ||
|
|
||
| it('passes through ad4m://obj/ IDs unchanged', () => { | ||
| expect(stripChannelPrefix('ad4m://obj/abc123')).toBe('ad4m://obj/abc123'); | ||
| }); | ||
|
|
||
| it('passes through bare strings unchanged', () => { | ||
| expect(stripChannelPrefix('abc123')).toBe('abc123'); | ||
| }); | ||
|
|
||
| it('returns empty string for undefined/empty input', () => { | ||
| expect(stripChannelPrefix(undefined as unknown as string)).toBe(''); | ||
| expect(stripChannelPrefix('')).toBe(''); | ||
| }); | ||
| }); | ||
|
|
||
| describe('restoreChannelPrefix', () => { | ||
| it('prepends literal:string: to bare IDs', () => { | ||
| expect(restoreChannelPrefix('abc123')).toBe('literal:string:abc123'); | ||
| }); | ||
|
|
||
| it('passes through ad4m://obj/ IDs unchanged', () => { | ||
| expect(restoreChannelPrefix('ad4m://obj/abc123')).toBe('ad4m://obj/abc123'); | ||
| }); | ||
|
|
||
| it('passes through any :// scheme unchanged', () => { | ||
| expect(restoreChannelPrefix('neighbourhood://abc123')).toBe('neighbourhood://abc123'); | ||
| }); | ||
|
|
||
| it('returns empty string for undefined/empty input', () => { | ||
| expect(restoreChannelPrefix(undefined as unknown as string)).toBe(''); | ||
| expect(restoreChannelPrefix('')).toBe(''); | ||
| }); | ||
| }); | ||
|
|
||
| describe('round-trip: stripChannelPrefix → restoreChannelPrefix', () => { | ||
| it('round-trips legacy literal:string: IDs', () => { | ||
| const original = 'literal:string:abc123'; | ||
| expect(restoreChannelPrefix(stripChannelPrefix(original))).toBe(original); | ||
| }); | ||
|
|
||
| it('round-trips ad4m://obj/ IDs', () => { | ||
| const original = 'ad4m://obj/abc123'; | ||
| expect(restoreChannelPrefix(stripChannelPrefix(original))).toBe(original); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -86,8 +86,9 @@ import { useAppStore, useModalStore, useUiStore, useWebrtcStore } from '@/stores | |
| import { stripChannelPrefix } from '@/utils/routeUtils'; | ||
| import { App, Channel, ChannelSummary } from '@coasys/flux-api'; | ||
| import { useLiveQuery } from '@coasys/ad4m-vue-hooks'; | ||
| import { fluxDebug, fluxDebugWarn } from '@coasys/flux-utils'; | ||
| import { storeToRefs } from 'pinia'; | ||
| import { computed, onActivated, onMounted } from 'vue'; | ||
| import { computed, onActivated, onMounted, watch } from 'vue'; | ||
| import { useRouter } from 'vue-router'; | ||
|
|
||
| defineOptions({ name: 'Header' }); | ||
|
|
@@ -124,6 +125,31 @@ const { data: views } = useLiveQuery(App, perspective, { | |
| parent: { model: Channel, id: channel.value?.id ?? '' }, | ||
| }); | ||
|
|
||
| // Debug: trace every state transition on the channel-view (plugin tabs) query. | ||
| // Symptom we're chasing: on the AD4M typed-RDF-literals branch, the plugin | ||
| // tabs disappear silently — the header renders no <label class="tab"> entries | ||
| // because `views` is empty, and there is no error surfaced to the user. This | ||
| // lets us see whether App.parent=Channel resolution is returning [] and why. | ||
| watch( | ||
| [() => channel.value?.id, () => views.value?.length ?? -1], | ||
| ([channelId, count]) => { | ||
| fluxDebug('ChannelHeader.plugins', 'query.state', { | ||
| channelId, | ||
| perspectiveUuid: perspective?.uuid, | ||
| viewCount: count, | ||
| firstViewPkg: views.value?.[0]?.pkg, | ||
| firstViewName: views.value?.[0]?.name, | ||
| }); | ||
| if (channelId && count === 0) { | ||
| fluxDebugWarn('ChannelHeader.plugins', 'query.empty', { | ||
| channelId, | ||
| hint: 'useLiveQuery(App, parent=Channel) returned no plugin views — check that App @Property/@HasMany resolves through the has_child predicate and the SDNA subject class is loaded.', | ||
| }); | ||
| } | ||
| }, | ||
| { immediate: true }, | ||
|
Comment on lines
+134
to
+150
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Track query result identity, not only result count. Both diagnostics log representative result fields, but both subscriptions only react to count changes. A completed query can replace its results with the same count, and the log then reports stale data or misses the resolved state.
📍 Affects 2 files
🤖 Prompt for AI Agents |
||
| ); | ||
|
|
||
| function manageChannelPlugins() { | ||
| modalStore.showManageChannelPluginsModal = true; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
Repository: coasys/flux
Length of output: 8046
🏁 Script executed:
Repository: coasys/flux
Length of output: 20726
🏁 Script executed:
Repository: coasys/flux
Length of output: 11828
Align all route utility signatures with nullish input handling.
All four functions return
''for nullish input at runtime, butstrictNullChecksrejects those inputs. Declarestring | null | undefinedfor each parameter and add directnulltest cases. Remove the currentundefinedcasts.🤖 Prompt for AI Agents