@epic-web/workshop-presence 4.5.0 → 4.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,10 +2,21 @@
2
2
 
3
3
  Welcome to the party, pal!
4
4
 
5
- This is a [Partykit](https://partykit.io) project, which lets you create real-time collaborative applications with minimal coding effort.
5
+ This is a [Partykit](https://partykit.io) project, which lets you create
6
+ real-time collaborative applications with minimal coding effort.
6
7
 
7
- [`server.ts`](./src/server.ts) is the server-side code, which is responsible for handling WebSocket events and HTTP requests. [`presence.tsx`](../workshop-app/app/utils/presence.tsx) is the client-side code, which connects to the server and listens for events.
8
+ [`server.ts`](./src/server.ts) is the server-side code, which is responsible for
9
+ handling WebSocket events and HTTP requests.
10
+ [`presence.tsx`](../workshop-app/app/utils/presence.tsx) is the client-side
11
+ code, which connects to the server and listens for events.
8
12
 
9
- You can start developing by running `npm run dev` and opening [http://localhost:1999](http://localhost:1999) in your browser. When you're ready, you can deploy your application on to the PartyKit cloud with `npm run deploy`.
13
+ You can start developing by running `npm run dev` and opening
14
+ [http://localhost:1999](http://localhost:1999) in your browser. When you're
15
+ ready, you can deploy your application on to the PartyKit cloud with
16
+ `npm run deploy`.
10
17
 
11
- Refer to our docs for more information: https://github.com/partykit/partykit/blob/main/README.md. For more help, reach out to us on [Discord](https://discord.gg/g5uqHQJc3z), [GitHub](https://github.com/partykit/partykit), or [Twitter](https://twitter.com/partykit_io).
18
+ Refer to our docs for more information:
19
+ https://github.com/partykit/partykit/blob/main/README.md. For more help, reach
20
+ out to us on [Discord](https://discord.gg/g5uqHQJc3z),
21
+ [GitHub](https://github.com/partykit/partykit), or
22
+ [Twitter](https://twitter.com/partykit_io).
@@ -1 +1 @@
1
- {"version":3,"file":"presence.server.d.ts","sourceRoot":"","sources":["../../src/presence.server.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,wCAAwC,CAAA;AAGrE,OAAO,EAEN,KAAK,IAAI,EAGT,MAAM,eAAe,CAAA;AAEtB,eAAO,MAAM,aAAa;;;YAEpB,MAAM;mBACC,MAAM;cACX,MAAM,GAAG,IAAI,GAAG,SAAS;;YAF3B,MAAM;mBACC,MAAM;cACX,MAAM,GAAG,IAAI,GAAG,SAAS;;;YAF3B,MAAM;mBACC,MAAM;cACX,MAAM,GAAG,IAAI,GAAG,SAAS;;;CAEf,CAAA;AAElB,wBAAsB,eAAe,CACpC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,EAClB,EAAE,OAAO,EAAE,OAAO,EAAE,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO;;;;;;;;;;;;;KAgDnE"}
1
+ {"version":3,"file":"presence.server.d.ts","sourceRoot":"","sources":["../../src/presence.server.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,wCAAwC,CAAA;AAGrE,OAAO,EAIN,KAAK,IAAI,EACT,MAAM,eAAe,CAAA;AAEtB,eAAO,MAAM,aAAa;;;YAEpB,MAAM;mBACC,MAAM;cACX,MAAM,GAAG,IAAI,GAAG,SAAS;;YAF3B,MAAM;mBACC,MAAM;cACX,MAAM,GAAG,IAAI,GAAG,SAAS;;;YAF3B,MAAM;mBACC,MAAM;cACX,MAAM,GAAG,IAAI,GAAG,SAAS;;;CAEf,CAAA;AAElB,wBAAsB,eAAe,CACpC,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,EAClB,EAAE,OAAO,EAAE,OAAO,EAAE,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO;;;;;;;;;;;;;KAgDnE"}
@@ -22,7 +22,7 @@ export async function getPresentUsers(user, { timings, request } = {}) {
22
22
  throw new Error(`No internet connection`);
23
23
  return fetch(`${partykitBaseUrl}/presence`);
24
24
  })(),
25
- new Promise(resolve => setTimeout(() => resolve(new Response('Timeout', { status: 500 })), 200)),
25
+ new Promise((resolve) => setTimeout(() => resolve(new Response('Timeout', { status: 500 })), 200)),
26
26
  ]);
27
27
  if (response.statusText === 'Timeout') {
28
28
  throw new Error(`Timeout fetching partykit presence`);
@@ -34,7 +34,7 @@ export async function getPresentUsers(user, { timings, request } = {}) {
34
34
  const preferences = await getPreferences();
35
35
  const users = presence.users;
36
36
  if (preferences?.presence.optOut ?? !user) {
37
- return uniqueUsers(users.filter(u => u.id !== user?.id));
37
+ return uniqueUsers(users.filter((u) => u.id !== user?.id));
38
38
  }
39
39
  else {
40
40
  return uniqueUsers([...users, user]);
@@ -52,7 +52,7 @@ export async function getPresentUsers(user, { timings, request } = {}) {
52
52
  // so let's make sure we only show them once
53
53
  function uniqueUsers(users) {
54
54
  const seen = new Set();
55
- return users.filter(user => {
55
+ return users.filter((user) => {
56
56
  if (seen.has(user.id)) {
57
57
  return false;
58
58
  }
@@ -1 +1 @@
1
- {"version":3,"file":"presence.server.js","sourceRoot":"","sources":["../../src/presence.server.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tcachified,\n\tmakeSingletonCache,\n} from '@epic-web/workshop-utils/cache.server'\nimport { getPreferences } from '@epic-web/workshop-utils/db.server'\nimport { type Timings } from '@epic-web/workshop-utils/timing.server'\nimport { checkConnection } from '@epic-web/workshop-utils/utils.server'\nimport { z } from 'zod'\nimport {\n\tPresenceSchema,\n\ttype User,\n\tUserSchema,\n\tpartykitBaseUrl,\n} from './presence.js'\n\nexport const presenceCache = makeSingletonCache<\n\tArray<{\n\t\tid: string\n\t\tavatarUrl: string\n\t\tname: string | null | undefined\n\t}>\n>('PresenceCache')\n\nexport async function getPresentUsers(\n\tuser?: User | null,\n\t{ timings, request }: { timings?: Timings; request?: Request } = {},\n) {\n\treturn cachified({\n\t\tkey: 'presence',\n\t\tcache: presenceCache,\n\t\ttimings,\n\t\trequest,\n\t\tttl: 1000 * 60 * 5,\n\t\tswr: 1000 * 60 * 60 * 24,\n\t\tcheckValue: z.array(UserSchema),\n\t\tasync getFreshValue(context) {\n\t\t\ttry {\n\t\t\t\tconst response = await Promise.race([\n\t\t\t\t\t(async () => {\n\t\t\t\t\t\tconst connected = await checkConnection()\n\t\t\t\t\t\tif (!connected) throw new Error(`No internet connection`)\n\t\t\t\t\t\treturn fetch(`${partykitBaseUrl}/presence`)\n\t\t\t\t\t})(),\n\t\t\t\t\tnew Promise<Response>(resolve =>\n\t\t\t\t\t\tsetTimeout(\n\t\t\t\t\t\t\t() => resolve(new Response('Timeout', { status: 500 })),\n\t\t\t\t\t\t\t200,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t] as const)\n\t\t\t\tif (response.statusText === 'Timeout') {\n\t\t\t\t\tthrow new Error(`Timeout fetching partykit presence`)\n\t\t\t\t}\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Unexpected response from partykit: ${response.status} ${response.statusText}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tconst presence = PresenceSchema.parse(await response.json())\n\t\t\t\tconst preferences = await getPreferences()\n\t\t\t\tconst users = presence.users\n\t\t\t\tif (preferences?.presence.optOut ?? !user) {\n\t\t\t\t\treturn uniqueUsers(users.filter(u => u.id !== user?.id))\n\t\t\t\t} else {\n\t\t\t\t\treturn uniqueUsers([...users, user])\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// console.error(err)\n\t\t\t\tcontext.metadata.ttl = 300\n\t\t\t\treturn []\n\t\t\t}\n\t\t},\n\t})\n}\n\n// A user maybe on the same page in multiple tabs\n// so let's make sure we only show them once\nfunction uniqueUsers(users: Array<User>) {\n\tconst seen = new Set()\n\treturn users.filter(user => {\n\t\tif (seen.has(user.id)) {\n\t\t\treturn false\n\t\t}\n\t\tseen.add(user.id)\n\t\treturn true\n\t})\n}\n"]}
1
+ {"version":3,"file":"presence.server.js","sourceRoot":"","sources":["../../src/presence.server.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n\tcachified,\n\tmakeSingletonCache,\n} from '@epic-web/workshop-utils/cache.server'\nimport { getPreferences } from '@epic-web/workshop-utils/db.server'\nimport { type Timings } from '@epic-web/workshop-utils/timing.server'\nimport { checkConnection } from '@epic-web/workshop-utils/utils.server'\nimport { z } from 'zod'\nimport {\n\tPresenceSchema,\n\tUserSchema,\n\tpartykitBaseUrl,\n\ttype User,\n} from './presence.js'\n\nexport const presenceCache = makeSingletonCache<\n\tArray<{\n\t\tid: string\n\t\tavatarUrl: string\n\t\tname: string | null | undefined\n\t}>\n>('PresenceCache')\n\nexport async function getPresentUsers(\n\tuser?: User | null,\n\t{ timings, request }: { timings?: Timings; request?: Request } = {},\n) {\n\treturn cachified({\n\t\tkey: 'presence',\n\t\tcache: presenceCache,\n\t\ttimings,\n\t\trequest,\n\t\tttl: 1000 * 60 * 5,\n\t\tswr: 1000 * 60 * 60 * 24,\n\t\tcheckValue: z.array(UserSchema),\n\t\tasync getFreshValue(context) {\n\t\t\ttry {\n\t\t\t\tconst response = await Promise.race([\n\t\t\t\t\t(async () => {\n\t\t\t\t\t\tconst connected = await checkConnection()\n\t\t\t\t\t\tif (!connected) throw new Error(`No internet connection`)\n\t\t\t\t\t\treturn fetch(`${partykitBaseUrl}/presence`)\n\t\t\t\t\t})(),\n\t\t\t\t\tnew Promise<Response>((resolve) =>\n\t\t\t\t\t\tsetTimeout(\n\t\t\t\t\t\t\t() => resolve(new Response('Timeout', { status: 500 })),\n\t\t\t\t\t\t\t200,\n\t\t\t\t\t\t),\n\t\t\t\t\t),\n\t\t\t\t] as const)\n\t\t\t\tif (response.statusText === 'Timeout') {\n\t\t\t\t\tthrow new Error(`Timeout fetching partykit presence`)\n\t\t\t\t}\n\t\t\t\tif (!response.ok) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Unexpected response from partykit: ${response.status} ${response.statusText}`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\tconst presence = PresenceSchema.parse(await response.json())\n\t\t\t\tconst preferences = await getPreferences()\n\t\t\t\tconst users = presence.users\n\t\t\t\tif (preferences?.presence.optOut ?? !user) {\n\t\t\t\t\treturn uniqueUsers(users.filter((u) => u.id !== user?.id))\n\t\t\t\t} else {\n\t\t\t\t\treturn uniqueUsers([...users, user])\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// console.error(err)\n\t\t\t\tcontext.metadata.ttl = 300\n\t\t\t\treturn []\n\t\t\t}\n\t\t},\n\t})\n}\n\n// A user maybe on the same page in multiple tabs\n// so let's make sure we only show them once\nfunction uniqueUsers(users: Array<User>) {\n\tconst seen = new Set()\n\treturn users.filter((user) => {\n\t\tif (seen.has(user.id)) {\n\t\t\treturn false\n\t\t}\n\t\tseen.add(user.id)\n\t\treturn true\n\t})\n}\n"]}
@@ -80,7 +80,7 @@ export default (class Server {
80
80
  }
81
81
  });
82
82
  function shallowMergeConnectionState(connection, state) {
83
- setConnectionState(connection, prev => ({ ...prev, ...state }));
83
+ setConnectionState(connection, (prev) => ({ ...prev, ...state }));
84
84
  }
85
85
  function setConnectionState(connection, state) {
86
86
  if (typeof state !== 'function') {
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["import type * as Party from 'partykit/server'\nimport { z } from 'zod'\nimport { UserSchema } from './presence.js'\n\ntype User = z.infer<typeof UserSchema>\nconst ConnectionStateSchema = z\n\t.object({\n\t\tuser: UserSchema.nullable().optional(),\n\t})\n\t.nullable()\n\ntype ConnectionState = z.infer<typeof ConnectionStateSchema>\n\nconst MessageSchema = z\n\t.object({\n\t\ttype: z.literal('remove-user'),\n\t\tpayload: z.object({ id: z.string() }),\n\t})\n\t.or(z.object({ type: z.literal('add-user'), payload: UserSchema }))\n\t.or(\n\t\tz.object({\n\t\t\ttype: z.literal('add-anonymous-user'),\n\t\t\tpayload: z.object({ id: z.string() }),\n\t\t}),\n\t)\n\t.or(\n\t\tz.object({\n\t\t\ttype: z.literal('presence'),\n\t\t\tpayload: z.object({ users: z.array(UserSchema) }),\n\t\t}),\n\t)\ntype Message = z.infer<typeof MessageSchema>\n\nexport default (class Server implements Party.Server {\n\toptions: Party.ServerOptions = {\n\t\thibernate: true,\n\t}\n\n\treadonly party: Party.Party\n\n\tconstructor(party: Party.Party) {\n\t\tthis.party = party\n\t}\n\n\tonClose() {\n\t\tthis.updateUsers()\n\t}\n\n\tonError() {\n\t\tthis.updateUsers()\n\t}\n\n\tupdateUsers() {\n\t\tconst presenceMessage = JSON.stringify(this.getPresenceMessage())\n\t\tfor (const connection of this.party.getConnections()) {\n\t\t\tconnection.send(presenceMessage)\n\t\t}\n\t}\n\n\tgetPresenceMessage() {\n\t\treturn {\n\t\t\ttype: 'presence',\n\t\t\tpayload: { users: this.getUsers() },\n\t\t} satisfies Message\n\t}\n\n\tgetUsers() {\n\t\tconst users = new Map<string, z.infer<typeof UserSchema>>()\n\n\t\tfor (const connection of this.party.getConnections()) {\n\t\t\tconst state = getConnectionState(connection)\n\t\t\tif (state?.user) {\n\t\t\t\tusers.set(state.user.id, state.user)\n\t\t\t}\n\t\t}\n\n\t\treturn sortUsers(Array.from(users.values()))\n\t}\n\n\tonMessage(message: string, sender: Party.Connection) {\n\t\tconst result = MessageSchema.safeParse(JSON.parse(message))\n\t\tif (!result.success) return\n\n\t\tif (result.data.type === 'add-user') {\n\t\t\tshallowMergeConnectionState(sender, { user: result.data.payload })\n\t\t\tthis.updateUsers()\n\t\t} else if (result.data.type === 'remove-user') {\n\t\t\tsetConnectionState(sender, null)\n\t\t\tthis.updateUsers()\n\t\t} else if (result.data.type === 'add-anonymous-user') {\n\t\t\tsetConnectionState(sender, { user: result.data.payload })\n\t\t}\n\t}\n\n\tonRequest(req: Party.Request): Response | Promise<Response> {\n\t\tconst url = new URL(req.url)\n\t\tif (url.pathname.endsWith('/presence')) {\n\t\t\treturn Response.json(this.getPresenceMessage().payload)\n\t\t}\n\t\treturn new Response('not found', { status: 404 })\n\t}\n} satisfies Party.Worker)\n\nfunction shallowMergeConnectionState(\n\tconnection: Party.Connection,\n\tstate: ConnectionState,\n) {\n\tsetConnectionState(connection, prev => ({ ...prev, ...state }))\n}\n\nfunction setConnectionState(\n\tconnection: Party.Connection,\n\tstate:\n\t\t| ConnectionState\n\t\t| ((prev: ConnectionState | null) => ConnectionState | null),\n) {\n\tif (typeof state !== 'function') {\n\t\treturn connection.setState(state)\n\t}\n\tconnection.setState((prev: unknown) => {\n\t\tconst prevParseResult = ConnectionStateSchema.safeParse(prev)\n\t\tif (prevParseResult.success) {\n\t\t\treturn state(prevParseResult.data)\n\t\t} else {\n\t\t\treturn state(null)\n\t\t}\n\t})\n}\n\nfunction getConnectionState(connection: Party.Connection) {\n\tconst result = ConnectionStateSchema.safeParse(connection.state)\n\tif (result.success) {\n\t\treturn result.data\n\t} else {\n\t\tsetConnectionState(connection, null)\n\t\treturn null\n\t}\n}\n\nfunction sortUsers(users: Array<User>) {\n\treturn [...users].sort((a, b) => {\n\t\tconst aScore = getScore(a)\n\t\tconst bScore = getScore(b)\n\t\tif (aScore === bScore) return 0\n\t\treturn aScore > bScore ? -1 : 1\n\t})\n}\n\nfunction getScore(user: User) {\n\tlet score = 0\n\tif (user.avatarUrl) score += 1\n\tif (user.avatarUrl?.includes('discordapp')) score += 0.5\n\tif (user.name) score += 1\n\treturn score\n}\n"]}
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/server.ts"],"names":[],"mappings":"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","sourcesContent":["import type * as Party from 'partykit/server'\nimport { z } from 'zod'\nimport { UserSchema } from './presence.js'\n\ntype User = z.infer<typeof UserSchema>\nconst ConnectionStateSchema = z\n\t.object({\n\t\tuser: UserSchema.nullable().optional(),\n\t})\n\t.nullable()\n\ntype ConnectionState = z.infer<typeof ConnectionStateSchema>\n\nconst MessageSchema = z\n\t.object({\n\t\ttype: z.literal('remove-user'),\n\t\tpayload: z.object({ id: z.string() }),\n\t})\n\t.or(z.object({ type: z.literal('add-user'), payload: UserSchema }))\n\t.or(\n\t\tz.object({\n\t\t\ttype: z.literal('add-anonymous-user'),\n\t\t\tpayload: z.object({ id: z.string() }),\n\t\t}),\n\t)\n\t.or(\n\t\tz.object({\n\t\t\ttype: z.literal('presence'),\n\t\t\tpayload: z.object({ users: z.array(UserSchema) }),\n\t\t}),\n\t)\ntype Message = z.infer<typeof MessageSchema>\n\nexport default (class Server implements Party.Server {\n\toptions: Party.ServerOptions = {\n\t\thibernate: true,\n\t}\n\n\treadonly party: Party.Party\n\n\tconstructor(party: Party.Party) {\n\t\tthis.party = party\n\t}\n\n\tonClose() {\n\t\tthis.updateUsers()\n\t}\n\n\tonError() {\n\t\tthis.updateUsers()\n\t}\n\n\tupdateUsers() {\n\t\tconst presenceMessage = JSON.stringify(this.getPresenceMessage())\n\t\tfor (const connection of this.party.getConnections()) {\n\t\t\tconnection.send(presenceMessage)\n\t\t}\n\t}\n\n\tgetPresenceMessage() {\n\t\treturn {\n\t\t\ttype: 'presence',\n\t\t\tpayload: { users: this.getUsers() },\n\t\t} satisfies Message\n\t}\n\n\tgetUsers() {\n\t\tconst users = new Map<string, z.infer<typeof UserSchema>>()\n\n\t\tfor (const connection of this.party.getConnections()) {\n\t\t\tconst state = getConnectionState(connection)\n\t\t\tif (state?.user) {\n\t\t\t\tusers.set(state.user.id, state.user)\n\t\t\t}\n\t\t}\n\n\t\treturn sortUsers(Array.from(users.values()))\n\t}\n\n\tonMessage(message: string, sender: Party.Connection) {\n\t\tconst result = MessageSchema.safeParse(JSON.parse(message))\n\t\tif (!result.success) return\n\n\t\tif (result.data.type === 'add-user') {\n\t\t\tshallowMergeConnectionState(sender, { user: result.data.payload })\n\t\t\tthis.updateUsers()\n\t\t} else if (result.data.type === 'remove-user') {\n\t\t\tsetConnectionState(sender, null)\n\t\t\tthis.updateUsers()\n\t\t} else if (result.data.type === 'add-anonymous-user') {\n\t\t\tsetConnectionState(sender, { user: result.data.payload })\n\t\t}\n\t}\n\n\tonRequest(req: Party.Request): Response | Promise<Response> {\n\t\tconst url = new URL(req.url)\n\t\tif (url.pathname.endsWith('/presence')) {\n\t\t\treturn Response.json(this.getPresenceMessage().payload)\n\t\t}\n\t\treturn new Response('not found', { status: 404 })\n\t}\n} satisfies Party.Worker)\n\nfunction shallowMergeConnectionState(\n\tconnection: Party.Connection,\n\tstate: ConnectionState,\n) {\n\tsetConnectionState(connection, (prev) => ({ ...prev, ...state }))\n}\n\nfunction setConnectionState(\n\tconnection: Party.Connection,\n\tstate:\n\t\t| ConnectionState\n\t\t| ((prev: ConnectionState | null) => ConnectionState | null),\n) {\n\tif (typeof state !== 'function') {\n\t\treturn connection.setState(state)\n\t}\n\tconnection.setState((prev: unknown) => {\n\t\tconst prevParseResult = ConnectionStateSchema.safeParse(prev)\n\t\tif (prevParseResult.success) {\n\t\t\treturn state(prevParseResult.data)\n\t\t} else {\n\t\t\treturn state(null)\n\t\t}\n\t})\n}\n\nfunction getConnectionState(connection: Party.Connection) {\n\tconst result = ConnectionStateSchema.safeParse(connection.state)\n\tif (result.success) {\n\t\treturn result.data\n\t} else {\n\t\tsetConnectionState(connection, null)\n\t\treturn null\n\t}\n}\n\nfunction sortUsers(users: Array<User>) {\n\treturn [...users].sort((a, b) => {\n\t\tconst aScore = getScore(a)\n\t\tconst bScore = getScore(b)\n\t\tif (aScore === bScore) return 0\n\t\treturn aScore > bScore ? -1 : 1\n\t})\n}\n\nfunction getScore(user: User) {\n\tlet score = 0\n\tif (user.avatarUrl) score += 1\n\tif (user.avatarUrl?.includes('discordapp')) score += 0.5\n\tif (user.name) score += 1\n\treturn score\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epic-web/workshop-presence",
3
- "version": "4.5.0",
3
+ "version": "4.6.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -11,7 +11,7 @@
11
11
  "deploy": "partykit deploy"
12
12
  },
13
13
  "dependencies": {
14
- "@epic-web/workshop-utils": "4.5.0",
14
+ "@epic-web/workshop-utils": "4.6.0",
15
15
  "zod": "^3.23.8"
16
16
  },
17
17
  "devDependencies": {