fix(shared): keep the link contract free of DOM globals

The URL check used `new URL(...)`, and this package compiles against lib
ES2022 alone so that it carries neither DOM nor Node globals, which is why the
shared typecheck refused it. `z.url()` plus a protocol test does the same job
with names the package actually has; z.url() on its own would take mailto: and
javascript:.

The three collab:link events also needed their fixtures and their place in the
pinned inventory count, the guard that stops the fixture map drifting from the
registry.
This commit is contained in:
Maurice
2026-09-08 22:52:30 +02:00
committed by Maurice
parent 7e3b9fd039
commit 1e2a5e3862
2 changed files with 12 additions and 12 deletions
+5 -8
View File
@@ -48,14 +48,11 @@ export const collabPollVoteRequestSchema = z.object({
});
export type CollabPollVoteRequest = z.infer<typeof collabPollVoteRequestSchema>;
const httpUrl = z.string().trim().min(1).refine((value) => {
try {
const parsed = new URL(value);
return parsed.protocol === 'http:' || parsed.protocol === 'https:';
} catch {
return false;
}
}, 'A valid http(s) URL is required');
// `z.url()` rather than `new URL(...)`: this package compiles against lib
// ES2022 only, deliberately, so it stays free of both DOM and Node globals and
// `URL` is not one of the names it has. The protocol check stays, because
// z.url() alone would accept mailto: and javascript:.
const httpUrl = z.url().refine((value) => /^https?:\/\//i.test(value.trim()), 'A valid http(s) URL is required');
export const collabLinkCreateRequestSchema = z.object({
title: z.string().trim().min(1),
+7 -4
View File
@@ -73,6 +73,9 @@ const FIXTURES: Record<TrekWsEventName, Record<string, unknown>> = {
'collab:note:created': { note: { id: 3 } },
'collab:note:updated': { note: { id: 3 } },
'collab:note:deleted': { noteId: 3 },
'collab:link:created': { link: { id: 4, title: 'Ferry', url: 'https://example.com' } },
'collab:link:updated': { link: { id: 4, title: 'Ferry', url: 'https://example.com' } },
'collab:link:deleted': { linkId: 4 },
'collab:poll:created': { poll: { id: 2 } },
'collab:poll:voted': { poll: { id: 2 } },
'collab:poll:closed': { poll: { id: 2 } },
@@ -135,11 +138,11 @@ const DRIFT_VARIANTS: Partial<Record<TrekWsEventName, Record<string, unknown>[]>
};
describe('@trek/shared realtime event registry', () => {
it('WSEVT-REG-001: pins the authoritative inventory counts (66 trip + 32 user = 98)', () => {
// 66th trip event: packing:bag-totals (#2191).
expect(TREK_WS_TRIP_EVENT_NAMES).toHaveLength(66);
it('WSEVT-REG-001: pins the authoritative inventory counts (69 trip + 32 user = 101)', () => {
// 67th to 69th trip event: the three collab:link:* a shared link emits.
expect(TREK_WS_TRIP_EVENT_NAMES).toHaveLength(69);
expect(TREK_WS_USER_EVENT_NAMES).toHaveLength(32);
expect(TREK_WS_EVENT_NAMES).toHaveLength(98);
expect(TREK_WS_EVENT_NAMES).toHaveLength(101);
});
it('WSEVT-REG-002: every name is domain:action shaped and outside the reserved plugin: namespace', () => {