@selvajs/platform 0.14.2 → 0.15.0-beta.1
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/LICENSE +1 -1
- package/dist/auth.d.ts +60 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +12 -0
- package/dist/auth.js.map +1 -0
- package/dist/compute.d.ts +47 -0
- package/dist/compute.d.ts.map +1 -0
- package/dist/compute.js +11 -0
- package/dist/compute.js.map +1 -0
- package/dist/computeServer/index.d.ts +2 -0
- package/dist/computeServer/index.d.ts.map +1 -1
- package/dist/computeServer/index.js +6 -0
- package/dist/computeServer/index.js.map +1 -1
- package/dist/computeServer/interface.d.ts +10 -0
- package/dist/computeServer/interface.d.ts.map +1 -1
- package/dist/computeServer/secrets.d.ts +53 -0
- package/dist/computeServer/secrets.d.ts.map +1 -0
- package/dist/computeServer/secrets.js +67 -0
- package/dist/computeServer/secrets.js.map +1 -0
- package/dist/context.d.ts +7 -0
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +13 -0
- package/dist/context.js.map +1 -1
- package/dist/data/interface.d.ts +33 -0
- package/dist/data/interface.d.ts.map +1 -1
- package/dist/definitions/interface.d.ts +18 -0
- package/dist/definitions/interface.d.ts.map +1 -1
- package/dist/definitions/schemas.d.ts +1 -0
- package/dist/definitions/schemas.d.ts.map +1 -1
- package/dist/definitions/schemas.js +3 -0
- package/dist/definitions/schemas.js.map +1 -1
- package/dist/definitions/service.d.ts +62 -0
- package/dist/definitions/service.d.ts.map +1 -0
- package/dist/definitions/service.js +149 -0
- package/dist/definitions/service.js.map +1 -0
- package/dist/definitions/types.d.ts +24 -1
- package/dist/definitions/types.d.ts.map +1 -1
- package/dist/definitions.d.ts +146 -0
- package/dist/definitions.d.ts.map +1 -0
- package/dist/definitions.js +23 -0
- package/dist/definitions.js.map +1 -0
- package/dist/errors/interface.d.ts +44 -0
- package/dist/errors/interface.d.ts.map +1 -0
- package/dist/errors/interface.js +9 -0
- package/dist/errors/interface.js.map +1 -0
- package/dist/events/interface.d.ts +23 -0
- package/dist/events/interface.d.ts.map +1 -1
- package/dist/events/interface.js.map +1 -1
- package/dist/index.d.ts +14 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -2
- package/dist/index.js.map +1 -1
- package/dist/invites/schemas.d.ts +24 -0
- package/dist/invites/schemas.d.ts.map +1 -0
- package/dist/invites/schemas.js +17 -0
- package/dist/invites/schemas.js.map +1 -0
- package/dist/metrics/interface.d.ts +6 -0
- package/dist/metrics/interface.d.ts.map +1 -1
- package/dist/metrics/interface.js.map +1 -1
- package/dist/organizations/index.d.ts +4 -3
- package/dist/organizations/index.d.ts.map +1 -1
- package/dist/organizations/index.js +2 -1
- package/dist/organizations/index.js.map +1 -1
- package/dist/organizations/interface.d.ts +1 -1
- package/dist/organizations/interface.d.ts.map +1 -1
- package/dist/organizations/paths.d.ts +22 -0
- package/dist/organizations/paths.d.ts.map +1 -0
- package/dist/organizations/paths.js +43 -0
- package/dist/organizations/paths.js.map +1 -0
- package/dist/organizations/schemas.d.ts +10 -0
- package/dist/organizations/schemas.d.ts.map +1 -1
- package/dist/organizations/schemas.js +6 -0
- package/dist/organizations/schemas.js.map +1 -1
- package/dist/organizations/types.d.ts +10 -1
- package/dist/organizations/types.d.ts.map +1 -1
- package/dist/organizations.d.ts +93 -0
- package/dist/organizations.d.ts.map +1 -0
- package/dist/organizations.js +14 -0
- package/dist/organizations.js.map +1 -0
- package/dist/solveCache/interface.d.ts +86 -0
- package/dist/solveCache/interface.d.ts.map +1 -0
- package/dist/solveCache/interface.js +12 -0
- package/dist/solveCache/interface.js.map +1 -0
- package/dist/storage/assetClasses.d.ts +59 -0
- package/dist/storage/assetClasses.d.ts.map +1 -0
- package/dist/storage/assetClasses.js +86 -0
- package/dist/storage/assetClasses.js.map +1 -0
- package/dist/storage/cacheBust.d.ts +21 -0
- package/dist/storage/cacheBust.d.ts.map +1 -0
- package/dist/storage/cacheBust.js +29 -0
- package/dist/storage/cacheBust.js.map +1 -0
- package/dist/storage/image.d.ts +6 -1
- package/dist/storage/image.d.ts.map +1 -1
- package/dist/storage/image.js +8 -3
- package/dist/storage/image.js.map +1 -1
- package/dist/storage/index.d.ts +3 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +2 -0
- package/dist/storage/index.js.map +1 -1
- package/dist/testing/adapterSuite.d.ts +33 -0
- package/dist/testing/adapterSuite.d.ts.map +1 -0
- package/dist/testing/adapterSuite.js +166 -0
- package/dist/testing/adapterSuite.js.map +1 -0
- package/dist/testing/suites/definitionStoreSuite.d.ts.map +1 -1
- package/dist/testing/suites/definitionStoreSuite.js +87 -0
- package/dist/testing/suites/definitionStoreSuite.js.map +1 -1
- package/dist/testing/suites/eventSinkSuite.js +1 -0
- package/dist/testing/suites/eventSinkSuite.js.map +1 -1
- package/dist/testing/suites/orgStoreSuite.d.ts.map +1 -1
- package/dist/testing/suites/orgStoreSuite.js +12 -0
- package/dist/testing/suites/orgStoreSuite.js.map +1 -1
- package/dist/testing/suites/platformPermissionStoreSuite.d.ts +9 -0
- package/dist/testing/suites/platformPermissionStoreSuite.d.ts.map +1 -1
- package/dist/testing/suites/platformPermissionStoreSuite.js +34 -1
- package/dist/testing/suites/platformPermissionStoreSuite.js.map +1 -1
- package/dist/testing/suites/runner.d.ts +21 -0
- package/dist/testing/suites/runner.d.ts.map +1 -0
- package/dist/testing/suites/runner.js +7 -0
- package/dist/testing/suites/runner.js.map +1 -0
- package/dist/testing/suites/storageProviderSuite.d.ts.map +1 -1
- package/dist/testing/suites/storageProviderSuite.js +18 -0
- package/dist/testing/suites/storageProviderSuite.js.map +1 -1
- package/package.json +9 -24
- package/src/access/index.ts +0 -23
- package/src/access/rules.ts +0 -276
- package/src/auth/index.ts +0 -8
- package/src/auth/interface.ts +0 -279
- package/src/auth/types.ts +0 -32
- package/src/bindings/index.ts +0 -2
- package/src/bindings/interface.ts +0 -61
- package/src/computeServer/index.ts +0 -17
- package/src/computeServer/interface.ts +0 -62
- package/src/computeServer/types.ts +0 -74
- package/src/computeServer/utils.ts +0 -112
- package/src/config.ts +0 -99
- package/src/context.ts +0 -66
- package/src/data/index.ts +0 -1
- package/src/data/interface.ts +0 -70
- package/src/definitions/index.ts +0 -26
- package/src/definitions/interface.ts +0 -78
- package/src/definitions/paths.ts +0 -31
- package/src/definitions/schemas.ts +0 -64
- package/src/definitions/types.ts +0 -111
- package/src/errors.ts +0 -16
- package/src/events/audit.ts +0 -67
- package/src/events/index.ts +0 -9
- package/src/events/interface.ts +0 -102
- package/src/index.ts +0 -219
- package/src/invites/index.ts +0 -2
- package/src/invites/interface.ts +0 -38
- package/src/invites/types.ts +0 -39
- package/src/metrics/interface.ts +0 -86
- package/src/organizations/index.ts +0 -14
- package/src/organizations/interface.ts +0 -68
- package/src/organizations/schemas.ts +0 -52
- package/src/organizations/types.ts +0 -31
- package/src/pagination.ts +0 -45
- package/src/permissions/index.ts +0 -3
- package/src/permissions/interface.ts +0 -60
- package/src/permissions/types.ts +0 -21
- package/src/platformProjects/index.ts +0 -3
- package/src/platformProjects/interface.ts +0 -27
- package/src/platformProjects/schemas.ts +0 -13
- package/src/platformProjects/types.ts +0 -22
- package/src/projects/index.ts +0 -4
- package/src/projects/interface.ts +0 -58
- package/src/projects/schemas.ts +0 -28
- package/src/projects/types.ts +0 -36
- package/src/shareLinks/index.ts +0 -5
- package/src/shareLinks/interface.ts +0 -35
- package/src/shareLinks/schemas.ts +0 -21
- package/src/shareLinks/types.ts +0 -34
- package/src/storage/image.ts +0 -77
- package/src/storage/index.ts +0 -9
- package/src/storage/interface.ts +0 -27
- package/src/testing/index.ts +0 -47
- package/src/testing/suites/authProviderSuite.ts +0 -207
- package/src/testing/suites/computeServerStoreSuite.ts +0 -195
- package/src/testing/suites/definitionStoreSuite.ts +0 -585
- package/src/testing/suites/emailLinkAuthSuite.ts +0 -83
- package/src/testing/suites/eventSinkSuite.ts +0 -379
- package/src/testing/suites/helpers.ts +0 -87
- package/src/testing/suites/inviteStoreSuite.ts +0 -143
- package/src/testing/suites/orgStoreSuite.ts +0 -412
- package/src/testing/suites/platformPermissionStoreSuite.ts +0 -196
- package/src/testing/suites/platformProjectGrantStoreSuite.ts +0 -166
- package/src/testing/suites/projectStoreSuite.ts +0 -381
- package/src/testing/suites/shareLinkStoreSuite.ts +0 -203
- package/src/testing/suites/solveMetricSinkSuite.ts +0 -135
- package/src/testing/suites/storageProviderSuite.ts +0 -175
- package/src/testing/suites/userProfileStoreSuite.ts +0 -134
- package/src/userProfile/index.ts +0 -3
- package/src/userProfile/interface.ts +0 -48
- package/src/userProfile/types.ts +0 -24
- package/src/utils/audit.ts +0 -46
- package/src/utils/index.ts +0 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@selvajs/platform",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0-beta.1",
|
|
4
4
|
"description": "Pure TypeScript interfaces for Selva platform providers (auth, definitions, compute)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -8,106 +8,91 @@
|
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "https://github.com/VektorNode/selva.git",
|
|
11
|
+
"url": "git+https://github.com/VektorNode/selva.git",
|
|
12
12
|
"directory": "packages/platform"
|
|
13
13
|
},
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=22.0.0"
|
|
16
|
+
},
|
|
14
17
|
"type": "module",
|
|
15
18
|
"main": "./dist/index.js",
|
|
16
19
|
"types": "./dist/index.d.ts",
|
|
17
20
|
"exports": {
|
|
18
21
|
".": {
|
|
19
|
-
"source": "./src/index.ts",
|
|
20
22
|
"types": "./dist/index.d.ts",
|
|
21
23
|
"import": "./dist/index.js"
|
|
22
24
|
},
|
|
23
25
|
"./auth": {
|
|
24
|
-
"source": "./src/auth/index.ts",
|
|
25
26
|
"types": "./dist/auth/index.d.ts",
|
|
26
27
|
"import": "./dist/auth/index.js"
|
|
27
28
|
},
|
|
28
29
|
"./data": {
|
|
29
|
-
"source": "./src/data/index.ts",
|
|
30
30
|
"types": "./dist/data/index.d.ts",
|
|
31
31
|
"import": "./dist/data/index.js"
|
|
32
32
|
},
|
|
33
33
|
"./storage": {
|
|
34
|
-
"source": "./src/storage/index.ts",
|
|
35
34
|
"types": "./dist/storage/index.d.ts",
|
|
36
35
|
"import": "./dist/storage/index.js"
|
|
37
36
|
},
|
|
38
37
|
"./permissions": {
|
|
39
|
-
"source": "./src/permissions/index.ts",
|
|
40
38
|
"types": "./dist/permissions/index.d.ts",
|
|
41
39
|
"import": "./dist/permissions/index.js"
|
|
42
40
|
},
|
|
43
41
|
"./userProfile": {
|
|
44
|
-
"source": "./src/userProfile/index.ts",
|
|
45
42
|
"types": "./dist/userProfile/index.d.ts",
|
|
46
43
|
"import": "./dist/userProfile/index.js"
|
|
47
44
|
},
|
|
48
45
|
"./projects": {
|
|
49
|
-
"source": "./src/projects/index.ts",
|
|
50
46
|
"types": "./dist/projects/index.d.ts",
|
|
51
47
|
"import": "./dist/projects/index.js"
|
|
52
48
|
},
|
|
53
49
|
"./platformProjects": {
|
|
54
|
-
"source": "./src/platformProjects/index.ts",
|
|
55
50
|
"types": "./dist/platformProjects/index.d.ts",
|
|
56
51
|
"import": "./dist/platformProjects/index.js"
|
|
57
52
|
},
|
|
58
53
|
"./organizations": {
|
|
59
|
-
"source": "./src/organizations/index.ts",
|
|
60
54
|
"types": "./dist/organizations/index.d.ts",
|
|
61
55
|
"import": "./dist/organizations/index.js"
|
|
62
56
|
},
|
|
63
57
|
"./invites": {
|
|
64
|
-
"source": "./src/invites/index.ts",
|
|
65
58
|
"types": "./dist/invites/index.d.ts",
|
|
66
59
|
"import": "./dist/invites/index.js"
|
|
67
60
|
},
|
|
68
61
|
"./shareLinks": {
|
|
69
|
-
"source": "./src/shareLinks/index.ts",
|
|
70
62
|
"types": "./dist/shareLinks/index.d.ts",
|
|
71
63
|
"import": "./dist/shareLinks/index.js"
|
|
72
64
|
},
|
|
73
65
|
"./definitions": {
|
|
74
|
-
"source": "./src/definitions/index.ts",
|
|
75
66
|
"types": "./dist/definitions/index.d.ts",
|
|
76
67
|
"import": "./dist/definitions/index.js"
|
|
77
68
|
},
|
|
78
69
|
"./computeServer": {
|
|
79
|
-
"source": "./src/computeServer/index.ts",
|
|
80
70
|
"types": "./dist/computeServer/index.d.ts",
|
|
81
71
|
"import": "./dist/computeServer/index.js"
|
|
82
72
|
},
|
|
83
73
|
"./access": {
|
|
84
|
-
"source": "./src/access/index.ts",
|
|
85
74
|
"types": "./dist/access/index.d.ts",
|
|
86
75
|
"import": "./dist/access/index.js"
|
|
87
76
|
},
|
|
88
77
|
"./events": {
|
|
89
|
-
"source": "./src/events/index.ts",
|
|
90
78
|
"types": "./dist/events/index.d.ts",
|
|
91
79
|
"import": "./dist/events/index.js"
|
|
92
80
|
},
|
|
93
81
|
"./utils": {
|
|
94
|
-
"source": "./src/utils/index.ts",
|
|
95
82
|
"types": "./dist/utils/index.d.ts",
|
|
96
83
|
"import": "./dist/utils/index.js"
|
|
97
84
|
},
|
|
98
85
|
"./testing": {
|
|
99
|
-
"source": "./src/testing/index.ts",
|
|
100
86
|
"types": "./dist/testing/index.d.ts",
|
|
101
87
|
"import": "./dist/testing/index.js"
|
|
102
88
|
}
|
|
103
89
|
},
|
|
104
90
|
"files": [
|
|
105
|
-
"dist"
|
|
106
|
-
"src"
|
|
91
|
+
"dist"
|
|
107
92
|
],
|
|
108
93
|
"dependencies": {
|
|
109
94
|
"zod": "^4.3.6",
|
|
110
|
-
"@selvajs/schemas": "4.
|
|
95
|
+
"@selvajs/schemas": "^4.7.0-beta.0"
|
|
111
96
|
},
|
|
112
97
|
"peerDependencies": {
|
|
113
98
|
"sharp": "^0.34.0",
|
|
@@ -123,8 +108,8 @@
|
|
|
123
108
|
},
|
|
124
109
|
"devDependencies": {
|
|
125
110
|
"sharp": "^0.34.5",
|
|
126
|
-
"typescript": "^
|
|
127
|
-
"vitest": "^3.2.
|
|
111
|
+
"typescript": "^6.0.3",
|
|
112
|
+
"vitest": "^3.2.7"
|
|
128
113
|
},
|
|
129
114
|
"scripts": {
|
|
130
115
|
"build": "tsc",
|
package/src/access/index.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
export type {
|
|
2
|
-
ProjectAccessInput,
|
|
3
|
-
DefinitionAccessInput,
|
|
4
|
-
VisibilityChangeInput,
|
|
5
|
-
ReclaimAccessInput,
|
|
6
|
-
CreateProjectAccessInput,
|
|
7
|
-
OwnerRemovalInput,
|
|
8
|
-
OwnerRemovalCheck
|
|
9
|
-
} from './rules.js';
|
|
10
|
-
|
|
11
|
-
export {
|
|
12
|
-
canView,
|
|
13
|
-
canSolve,
|
|
14
|
-
canEdit,
|
|
15
|
-
canManage,
|
|
16
|
-
canEditProjectSettings,
|
|
17
|
-
canChangeVisibilityToPublic,
|
|
18
|
-
canEditDefinition,
|
|
19
|
-
canReclaim,
|
|
20
|
-
canCreateProject,
|
|
21
|
-
checkOwnerRemoval,
|
|
22
|
-
withAdminBypass
|
|
23
|
-
} from './rules.js';
|
package/src/access/rules.ts
DELETED
|
@@ -1,276 +0,0 @@
|
|
|
1
|
-
import type { PlatformPermission } from '../permissions/types.js';
|
|
2
|
-
import type { OrgPermission } from '../organizations/schemas.js';
|
|
3
|
-
import type { Project, ProjectMember } from '../projects/types.js';
|
|
4
|
-
import type { OrgMember } from '../organizations/types.js';
|
|
5
|
-
import type { DefinitionRecord } from '../definitions/types.js';
|
|
6
|
-
import type { PlatformProjectGrant } from '../platformProjects/types.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Pure access-control rules — single source of truth for UI gating across
|
|
10
|
-
* adapters. Mutating store methods MUST re-enforce the same predicate
|
|
11
|
-
* independently (RLS in SQL, code in local/JSON).
|
|
12
|
-
*
|
|
13
|
-
* `instance_admin` bypass is NOT baked into rule bodies — wrap calls with
|
|
14
|
-
* `withAdminBypass` at the call site.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
function isInstanceAdmin(platformPermissions: readonly PlatformPermission[]): boolean {
|
|
18
|
-
return platformPermissions.includes('instance_admin');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function withAdminBypass(
|
|
22
|
-
platformPermissions: readonly PlatformPermission[],
|
|
23
|
-
rule: () => boolean
|
|
24
|
-
): boolean {
|
|
25
|
-
if (isInstanceAdmin(platformPermissions)) return true;
|
|
26
|
-
return rule();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface ProjectAccessInput {
|
|
30
|
-
orgPermissions: readonly OrgPermission[];
|
|
31
|
-
platformPermissions: readonly PlatformPermission[];
|
|
32
|
-
project: Project | null;
|
|
33
|
-
member: ProjectMember | null;
|
|
34
|
-
orgMember: OrgMember | null;
|
|
35
|
-
/**
|
|
36
|
-
* `ALLOW_CROSS_ORG_PUBLIC` flag. When false, `public` visibility means
|
|
37
|
-
* "everyone in the parent org" rather than "everyone on the instance".
|
|
38
|
-
*/
|
|
39
|
-
allowCrossOrgPublic: boolean;
|
|
40
|
-
/**
|
|
41
|
-
* `ENABLE_PLATFORM_PROJECTS` flag. When false, every rule treats a
|
|
42
|
-
* `platform`-visibility project as inaccessible — even for instance
|
|
43
|
-
* admins — so the feature can be hidden silently without deleting data.
|
|
44
|
-
*/
|
|
45
|
-
enablePlatformProjects: boolean;
|
|
46
|
-
/**
|
|
47
|
-
* Grants for `platform`-visibility projects. Pass an empty array for
|
|
48
|
-
* non-platform projects — the rule only consults this for `platform`.
|
|
49
|
-
*/
|
|
50
|
-
platformGrants: readonly PlatformProjectGrant[];
|
|
51
|
-
/** `ctx.actingOrgId` — used to match org grants on platform projects. */
|
|
52
|
-
actingOrgId: string | null;
|
|
53
|
-
/** `ctx.userId` — used to match user grants on platform projects. */
|
|
54
|
-
userId: string;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Authenticated-user view rule. Anonymous access is delivered via share-link
|
|
59
|
-
* tokens — the route resolves a valid token before this rule runs.
|
|
60
|
-
*
|
|
61
|
-
* For `platform` projects: `instance_admin` always passes; otherwise any
|
|
62
|
-
* grant (view-only or canSolve) satisfies view access.
|
|
63
|
-
*/
|
|
64
|
-
export function canView(input: ProjectAccessInput): boolean {
|
|
65
|
-
const {
|
|
66
|
-
project,
|
|
67
|
-
member,
|
|
68
|
-
orgMember,
|
|
69
|
-
allowCrossOrgPublic,
|
|
70
|
-
enablePlatformProjects,
|
|
71
|
-
platformPermissions,
|
|
72
|
-
platformGrants,
|
|
73
|
-
actingOrgId,
|
|
74
|
-
userId
|
|
75
|
-
} = input;
|
|
76
|
-
if (!project) return false;
|
|
77
|
-
|
|
78
|
-
if (project.visibility === 'platform') {
|
|
79
|
-
if (!enablePlatformProjects) return false;
|
|
80
|
-
if (isInstanceAdmin(platformPermissions)) return true;
|
|
81
|
-
return platformGrants.some(
|
|
82
|
-
(g) =>
|
|
83
|
-
(g.granteeType === 'user' && g.granteeId === userId) ||
|
|
84
|
-
(g.granteeType === 'org' && g.granteeId === actingOrgId)
|
|
85
|
-
);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (project.visibility === 'private') return member !== null;
|
|
89
|
-
if (project.visibility === 'org') return orgMember !== null;
|
|
90
|
-
if (project.visibility === 'public') return allowCrossOrgPublic ? true : orgMember !== null;
|
|
91
|
-
return false;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* For non-platform projects: same as `canView` today — kept distinct for
|
|
96
|
-
* future cost gating.
|
|
97
|
-
*
|
|
98
|
-
* For `platform` projects: `instance_admin` always passes; a view-only grant
|
|
99
|
-
* satisfies `canView` but NOT `canSolve` — the grant must have `canSolve=true`.
|
|
100
|
-
*/
|
|
101
|
-
export function canSolve(input: ProjectAccessInput): boolean {
|
|
102
|
-
const {
|
|
103
|
-
project,
|
|
104
|
-
enablePlatformProjects,
|
|
105
|
-
platformPermissions,
|
|
106
|
-
platformGrants,
|
|
107
|
-
actingOrgId,
|
|
108
|
-
userId
|
|
109
|
-
} = input;
|
|
110
|
-
if (!project) return false;
|
|
111
|
-
|
|
112
|
-
if (project.visibility === 'platform') {
|
|
113
|
-
if (!enablePlatformProjects) return false;
|
|
114
|
-
if (isInstanceAdmin(platformPermissions)) return true;
|
|
115
|
-
return platformGrants.some(
|
|
116
|
-
(g) =>
|
|
117
|
-
g.canSolve &&
|
|
118
|
-
((g.granteeType === 'user' && g.granteeId === userId) ||
|
|
119
|
-
(g.granteeType === 'org' && g.granteeId === actingOrgId))
|
|
120
|
-
);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
return canView(input);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Platform projects: `instance_admin` only (they have no member rows).
|
|
128
|
-
* All other visibilities: project owner/editor.
|
|
129
|
-
*/
|
|
130
|
-
export function canEdit(input: ProjectAccessInput): boolean {
|
|
131
|
-
const { member, project, enablePlatformProjects, platformPermissions } = input;
|
|
132
|
-
if (project?.visibility === 'platform') {
|
|
133
|
-
if (!enablePlatformProjects) return false;
|
|
134
|
-
return isInstanceAdmin(platformPermissions);
|
|
135
|
-
}
|
|
136
|
-
return member?.role === 'owner' || member?.role === 'editor';
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
/** Platform projects: `instance_admin` only. All other visibilities: project owner. */
|
|
140
|
-
export function canManage(input: ProjectAccessInput): boolean {
|
|
141
|
-
if (input.project?.visibility === 'platform') {
|
|
142
|
-
if (!input.enablePlatformProjects) return false;
|
|
143
|
-
return isInstanceAdmin(input.platformPermissions);
|
|
144
|
-
}
|
|
145
|
-
return input.member?.role === 'owner';
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
/** Platform projects: `instance_admin` only. All other visibilities: project owner. */
|
|
149
|
-
export function canEditProjectSettings(input: ProjectAccessInput): boolean {
|
|
150
|
-
if (input.project?.visibility === 'platform') {
|
|
151
|
-
if (!input.enablePlatformProjects) return false;
|
|
152
|
-
return isInstanceAdmin(input.platformPermissions);
|
|
153
|
-
}
|
|
154
|
-
return input.member?.role === 'owner';
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export interface VisibilityChangeInput {
|
|
158
|
-
orgMember: OrgMember | null;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Authorization to flip a project to `public`. The `ALLOW_CROSS_ORG_PUBLIC`
|
|
163
|
-
* flag only changes what `public` means post-flip; the flip itself is gated
|
|
164
|
-
* by org owner/admin role.
|
|
165
|
-
*/
|
|
166
|
-
export function canChangeVisibilityToPublic(input: VisibilityChangeInput): boolean {
|
|
167
|
-
const role = input.orgMember?.role;
|
|
168
|
-
return role === 'owner' || role === 'admin';
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export interface DefinitionAccessInput {
|
|
172
|
-
project: Project | null;
|
|
173
|
-
definition: DefinitionRecord | null;
|
|
174
|
-
member: ProjectMember | null;
|
|
175
|
-
userId: string;
|
|
176
|
-
platformPermissions: readonly PlatformPermission[];
|
|
177
|
-
/**
|
|
178
|
-
* `ENABLE_PLATFORM_PROJECTS` flag. When false, definitions in
|
|
179
|
-
* platform-visibility projects are not editable by anyone, matching the
|
|
180
|
-
* other project rules.
|
|
181
|
-
*/
|
|
182
|
-
enablePlatformProjects: boolean;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* For platform projects: `instance_admin` only.
|
|
187
|
-
* For all other projects: project editor/owner can always edit (moderation).
|
|
188
|
-
* On commons projects (`autoJoinOnUpload=true`) the definition owner can edit their own.
|
|
189
|
-
*/
|
|
190
|
-
export function canEditDefinition(input: DefinitionAccessInput): boolean {
|
|
191
|
-
const { project, definition, member, userId, platformPermissions, enablePlatformProjects } =
|
|
192
|
-
input;
|
|
193
|
-
if (!project || !definition) return false;
|
|
194
|
-
|
|
195
|
-
if (project.visibility === 'platform') {
|
|
196
|
-
if (!enablePlatformProjects) return false;
|
|
197
|
-
return isInstanceAdmin(platformPermissions);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
if (member?.role === 'owner' || member?.role === 'editor') return true;
|
|
201
|
-
if (project.autoJoinOnUpload && userId === definition.ownerId) return true;
|
|
202
|
-
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export interface ReclaimAccessInput {
|
|
207
|
-
project: Project | null;
|
|
208
|
-
orgMember: OrgMember | null;
|
|
209
|
-
/** `ctx.actingOrgId` — must match `project.orgId`. */
|
|
210
|
-
actingOrgId: string | null;
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Org leadership escape hatch. Reclaim adds the actor as co-owner; it does
|
|
215
|
-
* NOT demote the existing owner. Tenancy must match.
|
|
216
|
-
*
|
|
217
|
-
* Platform projects cannot be reclaimed — `instance_admin` always has
|
|
218
|
-
* management access to them without needing the Reclaim mechanism.
|
|
219
|
-
*/
|
|
220
|
-
export function canReclaim(input: ReclaimAccessInput): boolean {
|
|
221
|
-
const { project, orgMember, actingOrgId } = input;
|
|
222
|
-
if (!project || !actingOrgId) return false;
|
|
223
|
-
if (project.visibility === 'platform') return false;
|
|
224
|
-
if (actingOrgId !== project.orgId) return false;
|
|
225
|
-
const role = orgMember?.role;
|
|
226
|
-
return role === 'owner' || role === 'admin';
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export interface CreateProjectAccessInput {
|
|
230
|
-
orgPermissions: readonly OrgPermission[];
|
|
231
|
-
orgMember: OrgMember | null;
|
|
232
|
-
actingOrgId: string | null;
|
|
233
|
-
targetOrgId: string;
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* Owner/admin can always create. A `member` needs `manage_projects` org
|
|
238
|
-
* permission. Tenancy is enforced.
|
|
239
|
-
*
|
|
240
|
-
* Platform projects (`visibility='platform'`) are created via the admin API
|
|
241
|
-
* and gated on `instance_admin` — this rule is not consulted for them.
|
|
242
|
-
*/
|
|
243
|
-
export function canCreateProject(input: CreateProjectAccessInput): boolean {
|
|
244
|
-
const { orgMember, orgPermissions, actingOrgId, targetOrgId } = input;
|
|
245
|
-
if (actingOrgId !== targetOrgId) return false;
|
|
246
|
-
const role = orgMember?.role;
|
|
247
|
-
if (role === 'owner' || role === 'admin') return true;
|
|
248
|
-
if (role === 'member' && orgPermissions.includes('manage_projects')) return true;
|
|
249
|
-
return false;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* Pre-flight check for project-owner removal. Pure function over already-
|
|
254
|
-
* loaded membership rows so route handlers can call it after `canManage`.
|
|
255
|
-
*
|
|
256
|
-
* - `ok` — proceed
|
|
257
|
-
* - `sole_owner` — last owner; route surfaces 409 + suggests reclaim
|
|
258
|
-
* - `needs_confirm` — owner-on-owner removal without `?confirm=true`
|
|
259
|
-
*
|
|
260
|
-
* Non-owner targets always return `ok`.
|
|
261
|
-
*/
|
|
262
|
-
export type OwnerRemovalCheck = 'ok' | 'sole_owner' | 'needs_confirm';
|
|
263
|
-
|
|
264
|
-
export interface OwnerRemovalInput {
|
|
265
|
-
target: { role: 'owner' | 'editor' | 'viewer' };
|
|
266
|
-
allMembers: readonly { role: 'owner' | 'editor' | 'viewer' }[];
|
|
267
|
-
confirmed: boolean;
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
export function checkOwnerRemoval(input: OwnerRemovalInput): OwnerRemovalCheck {
|
|
271
|
-
if (input.target.role !== 'owner') return 'ok';
|
|
272
|
-
const ownerCount = input.allMembers.filter((m) => m.role === 'owner').length;
|
|
273
|
-
if (ownerCount <= 1) return 'sole_owner';
|
|
274
|
-
if (!input.confirmed) return 'needs_confirm';
|
|
275
|
-
return 'ok';
|
|
276
|
-
}
|