@@ -27,65 +27,6 @@ export interface WorkspaceAuthorizationOptions<C extends WorkspaceAuthorizationC
2727 executor ?: Pick < typeof db , 'select' >
2828 forUpdate ?: boolean
2929 delegation ?: WorkspaceDelegationPolicy < C >
30- /**
31- * Memo for the human-permission lookup, supplied by a caller that authorizes many items in one
32- * operation. Ignored alongside `executor` or `forUpdate` — see
33- * {@link createWorkspacePermissionCache}.
34- */
35- permissionCache ?: WorkspacePermissionCache
36- }
37-
38- export interface WorkspacePermissionCache {
39- resolve (
40- userId : string ,
41- workspaceId : string ,
42- workspaceOrganizationId : string | null
43- ) : Promise < PermissionType | null >
44- }
45-
46- /**
47- * Memoizes the effective-permission lookup across the items of one bulk operation.
48- *
49- * A batch authorizes every item separately — delegation scope is per-resource, so the check
50- * cannot simply be hoisted out of the loop — but the human-permission half of it reads the same
51- * `(user, workspace, organization)` triple every time, two queries deep. On a hundred-item
52- * request that is two hundred round trips for a value that cannot change within the batch.
53- *
54- * Caller-owned and request-scoped on purpose: nothing here outlives the operation that created
55- * it, so a permission changed between requests is always seen by the next one. Skipped entirely
56- * when the caller passes its own `executor` (a transaction has its own snapshot to honour) or
57- * `forUpdate` (that lookup takes a row lock, which is a side effect, not a read).
58- *
59- * Neither of the repo's two existing memo idioms fits. `coalesceLocally` evicts on settle, so a
60- * sequential per-item loop would re-query every item. React `cache()` cannot be skipped per call
61- * for the `executor`/`forUpdate` paths and has no request scope in the worker runtime. An
62- * implicit process-wide memo on an authorization read is a lifetime worth refusing outright.
63- */
64- export function createWorkspacePermissionCache ( ) : WorkspacePermissionCache {
65- const entries = new Map < string , Promise < PermissionType | null > > ( )
66- return {
67- resolve ( userId , workspaceId , workspaceOrganizationId ) {
68- /** Structural, so no id can run into the next and answer another workspace's question. */
69- const key = JSON . stringify ( [ userId , workspaceId , workspaceOrganizationId ] )
70- const cached = entries . get ( key )
71- if ( cached ) return cached
72- /**
73- * The in-flight promise is what gets stored, so concurrent items share one query rather
74- * than racing to start their own. Evicted if it rejects: a transient database failure must
75- * not become the permanent answer for the rest of the batch.
76- */
77- const pending = resolveEffectiveWorkspacePermission (
78- userId ,
79- workspaceId ,
80- workspaceOrganizationId
81- ) . catch ( ( error ) => {
82- entries . delete ( key )
83- throw error
84- } )
85- entries . set ( key , pending )
86- return pending
87- } ,
88- }
8930}
9031
9132export class InsufficientWorkspacePermissionsError extends ForbiddenOperationError {
@@ -210,16 +151,13 @@ async function requireCurrentHumanPermission<C extends WorkspaceAuthorizationCon
210151 required : PermissionType ,
211152 options ?: WorkspaceAuthorizationOptions < C >
212153) : Promise < void > {
213- const memo = options ?. executor || options ?. forUpdate ? undefined : options ?. permissionCache
214- const permission = memo
215- ? await memo . resolve ( userId , context . workspaceId , context . workspaceOrganizationId )
216- : await resolveEffectiveWorkspacePermission (
217- userId ,
218- context . workspaceId ,
219- context . workspaceOrganizationId ,
220- options ?. executor ,
221- { forUpdate : options ?. forUpdate }
222- )
154+ const permission = await resolveEffectiveWorkspacePermission (
155+ userId ,
156+ context . workspaceId ,
157+ context . workspaceOrganizationId ,
158+ options ?. executor ,
159+ { forUpdate : options ?. forUpdate }
160+ )
223161 requirePermission ( permission , required )
224162}
225163
0 commit comments