@ossy/workspaces 3.4.0 → 3.5.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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ossy/workspaces",
3
3
  "description": "Workspaces feature package - create, select, and manage workspaces, users, and invitations",
4
- "version": "3.4.0",
4
+ "version": "3.5.0",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
7
7
  "module": "./src/index.js",
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "@ossy/config": "^3.0.9",
27
- "@ossy/schema": "^3.4.0"
27
+ "@ossy/schema": "^3.5.0"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "@ossy/design-system": ">=1.0.0",
@@ -45,5 +45,5 @@
45
45
  "/src",
46
46
  "README.md"
47
47
  ],
48
- "gitHead": "d36b69444268d172bc3e8e1dc77e67afc63f8650"
48
+ "gitHead": "23167600f991596f122765a53f2b9df08314e115"
49
49
  }
@@ -1,9 +1,12 @@
1
1
  import React, { useEffect } from 'react'
2
2
  import { AsyncStatus, cacheKey, useReadCacheStore } from '@ossy/sdk-react'
3
3
  import { metadata as GetSchemas } from './get-schemas.action.js'
4
+ import { mergeWorkspaceSchemas } from './merge-workspace-schemas.js'
4
5
 
5
6
  /**
6
7
  * Seed the GetSchemas read cache from SSR props so schema lookups work on first paint.
8
+ * If a client get-schemas response already landed but omitted system schemas (empty
9
+ * inlined registry), merge SSR schemas back in so create forms keep their fields.
7
10
  */
8
11
  export function SchemasReadBootstrap ({ schemas }) {
9
12
  const store = useReadCacheStore()
@@ -12,7 +15,17 @@ export function SchemasReadBootstrap ({ schemas }) {
12
15
  if (!Array.isArray(schemas) || schemas.length === 0) return
13
16
  const key = cacheKey(GetSchemas)
14
17
  const entry = store.getEntry(key)
15
- if (entry.status === AsyncStatus.Success) return
18
+ if (entry.status === AsyncStatus.Success && Array.isArray(entry.data)) {
19
+ const cachedIds = new Set(entry.data.map((s) => s?.id).filter(Boolean))
20
+ const missing = schemas.some((s) => s?.id && !cachedIds.has(s.id))
21
+ if (!missing) return
22
+ store.setEntry(key, {
23
+ status: AsyncStatus.Success,
24
+ data: mergeWorkspaceSchemas(schemas, entry.data),
25
+ error: null,
26
+ })
27
+ return
28
+ }
16
29
  store.setEntry(key, { status: AsyncStatus.Success, data: schemas, error: null })
17
30
  }, [schemas, store])
18
31
 
@@ -1,3 +1,4 @@
1
+ import { randomUUID } from 'node:crypto'
1
2
  import jwt from 'jsonwebtoken'
2
3
  import { Aggregate } from '@ossy/event-store'
3
4
  import { UserSchema } from '@ossy/users/server'
@@ -21,6 +22,26 @@ function verifyToken(token) {
21
22
  })
22
23
  }
23
24
 
25
+ function createWebAuthSession (userId) {
26
+ const resourceId = randomUUID()
27
+ const signInExpiresIn = ConfigService.TokenValidity
28
+ const signInExpiresAt = Date.now() + signInExpiresIn * 1000
29
+ const signInToken = jwt.sign(
30
+ { sub: userId, type: 'WebAuth', jti: resourceId },
31
+ ConfigService.TokenSecret,
32
+ { expiresIn: signInExpiresIn },
33
+ )
34
+ const signInEvent = TokenEvents.Created({
35
+ resourceId,
36
+ type: 'WebAuth',
37
+ subject: userId,
38
+ createdBy: userId,
39
+ token: signInToken,
40
+ expiresAt: signInExpiresAt,
41
+ })
42
+ return { signInToken, signInExpiresAt, signInEvent }
43
+ }
44
+
24
45
  export async function run({ payload, req }) {
25
46
  const workspaceId = payload?.workspaceId ?? req?.workspaceId
26
47
  const verificationToken = payload?.token ?? req?.query?.token
@@ -45,41 +66,28 @@ export async function run({ payload, req }) {
45
66
 
46
67
  if (existingUser) {
47
68
  userId = existingUser.id
48
- const signInExpiresIn = ConfigService.TokenValidity
49
- signInExpiresAt = Date.now() + signInExpiresIn * 1000
50
- signInToken = jwt.sign({ sub: userId, type: 'WebAuth' }, ConfigService.TokenSecret, { expiresIn: signInExpiresIn })
51
- const signInEvent = TokenEvents.Created({
52
- type: 'WebAuth',
53
- subject: userId,
54
- createdBy: userId,
55
- token: signInToken,
56
- expiresAt: signInExpiresAt,
57
- })
69
+ const session = createWebAuthSession(userId)
70
+ signInToken = session.signInToken
71
+ signInExpiresAt = session.signInExpiresAt
58
72
  const userInvitationAccepted = WorkspacesEvents.UserInvitationAccepted({ email, createdBy: userId })
59
73
  await Promise.all([
60
- Aggregate.Of(Token, signInEvent),
74
+ Aggregate.Of(Token, session.signInEvent),
61
75
  Aggregate.Of(Workspace, resolvedWorkspaceId).then(Aggregate.Add(userInvitationAccepted)),
62
76
  ])
63
77
  } else {
64
78
  const signUpEvent = UsersEvents.Created({ email })
65
79
  userId = signUpEvent.createdBy
66
- const signInExpiresIn = ConfigService.TokenValidity
67
- signInExpiresAt = Date.now() + signInExpiresIn * 1000
68
- signInToken = jwt.sign({ sub: userId, type: 'WebAuth' }, ConfigService.TokenSecret, { expiresIn: signInExpiresIn })
69
- const signInEvent = TokenEvents.Created({
70
- type: 'WebAuth',
71
- subject: userId,
72
- createdBy: userId,
73
- token: signInToken,
74
- expiresAt: signInExpiresAt,
75
- })
80
+ const session = createWebAuthSession(userId)
81
+ signInToken = session.signInToken
82
+ signInExpiresAt = session.signInExpiresAt
76
83
  const userInvitationAccepted = WorkspacesEvents.UserInvitationAccepted({ email, createdBy: userId })
77
84
  await Promise.all([
78
85
  Aggregate.Of(User, signUpEvent),
79
- Aggregate.Of(Token, signInEvent),
86
+ Aggregate.Of(Token, session.signInEvent),
80
87
  Aggregate.Of(Workspace, resolvedWorkspaceId).then(Aggregate.Add(userInvitationAccepted)),
81
88
  ])
82
89
  }
83
90
 
91
+ log.info('Invitation accepted; WebAuth session issued', { userId, workspaceId: resolvedWorkspaceId })
84
92
  return { authToken: signInToken, authExpiresAt: signInExpiresAt, workspaceId: resolvedWorkspaceId }
85
93
  }
@@ -1,6 +1,9 @@
1
1
  import { Aggregate } from '@ossy/event-store'
2
2
  import { Workspace } from '@ossy/workspaces/server'
3
- import { getSystemSchemas } from '@ossy/schema'
3
+ // Use the system-schemas subpath so Rollup can externalize the registry singleton
4
+ // (same module `registerSchema` fills at server boot). A bare `@ossy/schema` import
5
+ // gets inlined with an empty `systemSchemas` array and new manifest schemas vanish.
6
+ import { getSystemSchemas } from '@ossy/schema/system-schemas'
4
7
  import { mergeWorkspaceSchemas } from './merge-workspace-schemas.js'
5
8
 
6
9
  export const metadata = { id: '@ossy/workspaces/tasks/get-schemas' }
@@ -5,12 +5,11 @@ import { Workspace, WorkspacesEvents } from '@ossy/workspaces/server'
5
5
  import { Token, TokenEvents } from '@ossy/tokens/server'
6
6
  import { EmailRenderer } from '@ossy/email'
7
7
  import { ConfigService } from '@ossy/config'
8
+ import { isEmailLike } from '@ossy/schema'
8
9
  import WorkspaceInvitationEmail, { subject as workspaceInvitationSubject } from './workspace-invitation.email.jsx'
9
10
 
10
11
  export const metadata = { id: '@ossy/workspaces/tasks/invite-user' }
11
12
 
12
- const isEmailLike = maybeEmail => /.@.+\.../.test(maybeEmail)
13
-
14
13
  function createVerificationToken(workspaceId) {
15
14
  const expiresIn = ConfigService.TokenValidity
16
15
  return jwt.sign({ aud: workspaceId }, ConfigService.TokenSecret, { expiresIn })