@epic-web/workshop-presence 5.4.1 → 5.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.
@@ -54,24 +54,8 @@ export declare const presenceCache: {
54
54
  }[]> | undefined;
55
55
  delete: (key: string) => boolean;
56
56
  };
57
- export declare function getPresentUsers(user?: User | null, { timings, request }?: {
57
+ export declare function getPresentUsers({ timings, request, }?: {
58
58
  timings?: Timings;
59
59
  request?: Request;
60
- }): Promise<{
61
- id: string;
62
- hasAccess?: boolean | null | undefined;
63
- avatarUrl?: string | null | undefined;
64
- imageUrlSmall?: string | null | undefined;
65
- imageUrlLarge?: string | null | undefined;
66
- name?: string | null | undefined;
67
- location?: {
68
- workshopTitle?: string | null | undefined;
69
- origin?: string | null | undefined;
70
- exercise?: {
71
- type?: "problem" | "solution" | null | undefined;
72
- exerciseNumber?: number | null | undefined;
73
- stepNumber?: number | null | undefined;
74
- } | null | undefined;
75
- } | null | undefined;
76
- }[]>;
60
+ }): Promise<Array<User>>;
77
61
  //# sourceMappingURL=presence.server.d.ts.map
@@ -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;AAErE,OAAO,EAIN,KAAK,IAAI,EACT,MAAM,eAAe,CAAA;AAEtB,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAC6C,CAAA;AAEvE,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;;;;;;;;;;;;;;;;KA2CnE"}
1
+ {"version":3,"file":"presence.server.d.ts","sourceRoot":"","sources":["../../src/presence.server.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,wCAAwC,CAAA;AAErE,OAAO,EAAmC,KAAK,IAAI,EAAE,MAAM,eAAe,CAAA;AAE1E,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAAmD,CAAA;AAE7E,wBAAsB,eAAe,CAAC,EACrC,OAAO,EACP,OAAO,GACP,GAAE;IAAE,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAO,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CA4FtE"}
@@ -1,17 +1,19 @@
1
1
  import { cachified, makeSingletonCache, } from '@epic-web/workshop-utils/cache.server';
2
- import { getPreferences } from '@epic-web/workshop-utils/db.server';
3
- import { z } from 'zod';
4
- import { PresenceSchema, UserSchema, partykitBaseUrl, } from './presence.js';
2
+ import { getWorkshopConfig } from '@epic-web/workshop-utils/config.server';
3
+ import { getAuthInfo, getPreferences } from '@epic-web/workshop-utils/db.server';
4
+ import { getUserInfo, userHasAccessToWorkshop, } from '@epic-web/workshop-utils/epic-api.server';
5
+ import { getUserId } from '@epic-web/workshop-utils/user.server';
6
+ import { PresenceSchema, partykitBaseUrl } from './presence.js';
5
7
  export const presenceCache = makeSingletonCache('PresenceCache');
6
- export async function getPresentUsers(user, { timings, request } = {}) {
7
- return cachified({
8
+ export async function getPresentUsers({ timings, request, } = {}) {
9
+ const presence = await cachified({
8
10
  key: 'presence',
9
11
  cache: presenceCache,
10
12
  timings,
11
13
  request,
12
- ttl: 1000 * 60 * 5,
14
+ ttl: 1000 * 2,
13
15
  swr: 1000 * 60 * 60 * 24,
14
- checkValue: z.array(UserSchema),
16
+ checkValue: PresenceSchema,
15
17
  async getFreshValue(context) {
16
18
  try {
17
19
  const response = await Promise.race([
@@ -27,22 +29,65 @@ export async function getPresentUsers(user, { timings, request } = {}) {
27
29
  throw new Error(`Unexpected response from partykit: ${response.status} ${response.statusText}`);
28
30
  }
29
31
  const presence = PresenceSchema.parse(await response.json());
30
- const preferences = await getPreferences();
31
- const users = presence.users;
32
- if (preferences?.presence.optOut ?? !user) {
33
- return uniqueUsers(users.filter((u) => u.id !== user?.id));
34
- }
35
- else {
36
- return uniqueUsers([...users, user]);
37
- }
32
+ return presence;
38
33
  }
39
34
  catch {
40
35
  // console.error(err)
41
36
  context.metadata.ttl = 300;
42
- return [];
37
+ return { users: [] };
43
38
  }
44
39
  },
45
40
  });
41
+ const { users } = presence;
42
+ const authInfo = await getAuthInfo();
43
+ const userId = request
44
+ ? (await getUserId({ request })).id
45
+ : (authInfo?.id ?? null);
46
+ const preferences = await getPreferences();
47
+ if (preferences?.presence.optOut || !userId) {
48
+ return uniqueUsers(users.filter((u) => u.id !== userId));
49
+ }
50
+ else {
51
+ const user = { id: userId };
52
+ const config = getWorkshopConfig();
53
+ const url = request ? new URL(request.url) : undefined;
54
+ user.location = {
55
+ workshopTitle: config.title,
56
+ origin: url ? url.origin : undefined,
57
+ };
58
+ if (url) {
59
+ if (url.pathname.startsWith('/exercise/')) {
60
+ const [exerciseNumber, stepNumber, type] = url.pathname
61
+ .split('/')
62
+ .slice(2);
63
+ user.location.exercise = {
64
+ exerciseNumber: isNaN(Number(exerciseNumber))
65
+ ? null
66
+ : Number(exerciseNumber),
67
+ stepNumber: isNaN(Number(stepNumber)) ? null : Number(stepNumber),
68
+ type: type === 'problem'
69
+ ? 'problem'
70
+ : type === 'solution'
71
+ ? 'solution'
72
+ : null,
73
+ };
74
+ }
75
+ }
76
+ if (authInfo) {
77
+ const [userInfo, hasAccess] = await Promise.all([
78
+ getUserInfo({ request, timings }),
79
+ userHasAccessToWorkshop({ request, timings }),
80
+ ]);
81
+ Object.assign(user, {
82
+ name: userInfo?.name,
83
+ avatarUrl: userInfo?.imageUrlLarge,
84
+ imageUrlSmall: userInfo?.imageUrlSmall,
85
+ imageUrlLarge: userInfo?.imageUrlLarge,
86
+ hasAccess,
87
+ });
88
+ }
89
+ return uniqueUsers([...users, user]);
90
+ }
46
91
  }
47
92
  // A user maybe on the same page in multiple tabs
48
93
  // so let's make sure we only show them once
@@ -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 { z } from 'zod'\nimport {\n\tPresenceSchema,\n\tUserSchema,\n\tpartykitBaseUrl,\n\ttype User,\n} from './presence.js'\n\nexport const presenceCache =\n\tmakeSingletonCache<Array<z.infer<typeof UserSchema>>>('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\tfetch(`${partykitBaseUrl}/presence`),\n\t\t\t\t\tnew Promise<Response>((resolve) =>\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tresolve(new Response('Timeout', { status: 500 }))\n\t\t\t\t\t\t}, 500),\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(Boolean).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 { getWorkshopConfig } from '@epic-web/workshop-utils/config.server'\nimport { getAuthInfo, getPreferences } from '@epic-web/workshop-utils/db.server'\nimport {\n\tgetUserInfo,\n\tuserHasAccessToWorkshop,\n} from '@epic-web/workshop-utils/epic-api.server'\nimport { type Timings } from '@epic-web/workshop-utils/timing.server'\nimport { getUserId } from '@epic-web/workshop-utils/user.server'\nimport { PresenceSchema, partykitBaseUrl, type User } from './presence.js'\n\nexport const presenceCache = makeSingletonCache<Array<User>>('PresenceCache')\n\nexport async function getPresentUsers({\n\ttimings,\n\trequest,\n}: { timings?: Timings; request?: Request } = {}): Promise<Array<User>> {\n\tconst presence = await cachified({\n\t\tkey: 'presence',\n\t\tcache: presenceCache,\n\t\ttimings,\n\t\trequest,\n\t\tttl: 1000 * 2,\n\t\tswr: 1000 * 60 * 60 * 24,\n\t\tcheckValue: PresenceSchema,\n\t\tasync getFreshValue(context) {\n\t\t\ttry {\n\t\t\t\tconst response = await Promise.race([\n\t\t\t\t\tfetch(`${partykitBaseUrl}/presence`),\n\t\t\t\t\tnew Promise<Response>((resolve) =>\n\t\t\t\t\t\tsetTimeout(() => {\n\t\t\t\t\t\t\tresolve(new Response('Timeout', { status: 500 }))\n\t\t\t\t\t\t}, 500),\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\treturn presence\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 { users: [] }\n\t\t\t}\n\t\t},\n\t})\n\tconst { users } = presence\n\n\tconst authInfo = await getAuthInfo()\n\tconst userId = request\n\t\t? (await getUserId({ request })).id\n\t\t: (authInfo?.id ?? null)\n\n\tconst preferences = await getPreferences()\n\n\tif (preferences?.presence.optOut || !userId) {\n\t\treturn uniqueUsers(users.filter((u) => u.id !== userId))\n\t} else {\n\t\tconst user: User = { id: userId }\n\t\tconst config = getWorkshopConfig()\n\t\tconst url = request ? new URL(request.url) : undefined\n\t\tuser.location = {\n\t\t\tworkshopTitle: config.title,\n\t\t\torigin: url ? url.origin : undefined,\n\t\t}\n\t\tif (url) {\n\t\t\tif (url.pathname.startsWith('/exercise/')) {\n\t\t\t\tconst [exerciseNumber, stepNumber, type] = url.pathname\n\t\t\t\t\t.split('/')\n\t\t\t\t\t.slice(2)\n\t\t\t\tuser.location.exercise = {\n\t\t\t\t\texerciseNumber: isNaN(Number(exerciseNumber))\n\t\t\t\t\t\t? null\n\t\t\t\t\t\t: Number(exerciseNumber),\n\t\t\t\t\tstepNumber: isNaN(Number(stepNumber)) ? null : Number(stepNumber),\n\t\t\t\t\ttype:\n\t\t\t\t\t\ttype === 'problem'\n\t\t\t\t\t\t\t? 'problem'\n\t\t\t\t\t\t\t: type === 'solution'\n\t\t\t\t\t\t\t\t? 'solution'\n\t\t\t\t\t\t\t\t: null,\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (authInfo) {\n\t\t\tconst [userInfo, hasAccess] = await Promise.all([\n\t\t\t\tgetUserInfo({ request, timings }),\n\t\t\t\tuserHasAccessToWorkshop({ request, timings }),\n\t\t\t])\n\n\t\t\tObject.assign(user, {\n\t\t\t\tname: userInfo?.name,\n\t\t\t\tavatarUrl: userInfo?.imageUrlLarge,\n\t\t\t\timageUrlSmall: userInfo?.imageUrlSmall,\n\t\t\t\timageUrlLarge: userInfo?.imageUrlLarge,\n\t\t\t\thasAccess,\n\t\t\t})\n\t\t}\n\n\t\treturn uniqueUsers([...users, user])\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(Boolean).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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@epic-web/workshop-presence",
3
- "version": "5.4.1",
3
+ "version": "5.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": "5.4.1",
14
+ "@epic-web/workshop-utils": "5.6.0",
15
15
  "zod": "^3.23.8"
16
16
  },
17
17
  "devDependencies": {