@elevasis/core 0.28.0 → 0.30.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/dist/auth/index.d.ts +5289 -0
- package/dist/auth/index.js +595 -0
- package/dist/index.d.ts +11 -11
- package/dist/knowledge/index.d.ts +1 -1
- package/dist/organization-model/index.d.ts +11 -11
- package/dist/test-utils/index.d.ts +24 -1
- package/package.json +7 -3
- package/src/__tests__/publish.test.ts +8 -7
- package/src/auth/__tests__/access-key-coverage.test.ts +42 -0
- package/src/auth/__tests__/access-key-scan.ts +117 -0
- package/src/auth/__tests__/access-keys.test.ts +81 -0
- package/src/auth/__tests__/access-model.test.ts +257 -0
- package/src/auth/__tests__/access-test-fixtures.ts +50 -0
- package/src/auth/__tests__/key-catalog-drift.test.ts +33 -0
- package/src/auth/__tests__/platform-admin-bypass-parity.test.ts +67 -0
- package/src/auth/access-keys.ts +173 -0
- package/src/auth/access-model.ts +185 -0
- package/src/auth/index.ts +6 -2
- package/src/auth/multi-tenancy/memberships/membership.ts +2 -4
- package/src/auth/multi-tenancy/permissions.ts +1 -1
- package/src/auth/multi-tenancy/types.ts +3 -12
- package/src/business/acquisition/api-schemas.test.ts +59 -8
- package/src/business/acquisition/api-schemas.ts +10 -5
- package/src/business/acquisition/build-templates.test.ts +187 -240
- package/src/business/acquisition/build-templates.ts +87 -98
- package/src/business/acquisition/types.ts +390 -389
- package/src/execution/engine/index.ts +6 -4
- package/src/execution/engine/tools/lead-service-types.ts +63 -34
- package/src/execution/engine/tools/platform/acquisition/types.ts +7 -8
- package/src/execution/engine/tools/registry.ts +6 -4
- package/src/execution/engine/tools/tool-maps.ts +23 -1
- package/src/organization-model/domains/prospecting.ts +2 -327
- package/src/organization-model/migration-helpers.ts +16 -12
- package/src/reference/_generated/contracts.md +352 -328
- package/src/reference/glossary.md +8 -6
- package/src/supabase/database.types.ts +13 -0
package/dist/index.d.ts
CHANGED
|
@@ -2238,9 +2238,9 @@ declare const OmTopologyNodeKindSchema: z.ZodEnum<{
|
|
|
2238
2238
|
externalResource: "externalResource";
|
|
2239
2239
|
}>;
|
|
2240
2240
|
declare const OmTopologyRelationshipKindSchema: z.ZodEnum<{
|
|
2241
|
-
approval: "approval";
|
|
2242
2241
|
triggers: "triggers";
|
|
2243
2242
|
uses: "uses";
|
|
2243
|
+
approval: "approval";
|
|
2244
2244
|
}>;
|
|
2245
2245
|
declare const OmTopologyNodeRefSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
2246
2246
|
kind: z.ZodLiteral<"system">;
|
|
@@ -2295,9 +2295,9 @@ declare const OmTopologyRelationshipSchema: z.ZodObject<{
|
|
|
2295
2295
|
id: z.ZodString;
|
|
2296
2296
|
}, z.core.$strip>], "kind">;
|
|
2297
2297
|
kind: z.ZodEnum<{
|
|
2298
|
-
approval: "approval";
|
|
2299
2298
|
triggers: "triggers";
|
|
2300
2299
|
uses: "uses";
|
|
2300
|
+
approval: "approval";
|
|
2301
2301
|
}>;
|
|
2302
2302
|
to: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
2303
2303
|
kind: z.ZodLiteral<"system">;
|
|
@@ -2357,9 +2357,9 @@ declare const OmTopologyDomainSchema: z.ZodDefault<z.ZodObject<{
|
|
|
2357
2357
|
id: z.ZodString;
|
|
2358
2358
|
}, z.core.$strip>], "kind">;
|
|
2359
2359
|
kind: z.ZodEnum<{
|
|
2360
|
-
approval: "approval";
|
|
2361
2360
|
triggers: "triggers";
|
|
2362
2361
|
uses: "uses";
|
|
2362
|
+
approval: "approval";
|
|
2363
2363
|
}>;
|
|
2364
2364
|
to: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
2365
2365
|
kind: z.ZodLiteral<"system">;
|
|
@@ -2634,7 +2634,7 @@ declare const topologyRelationship: {
|
|
|
2634
2634
|
kind: "externalResource";
|
|
2635
2635
|
id: string;
|
|
2636
2636
|
};
|
|
2637
|
-
kind: "
|
|
2637
|
+
kind: "triggers" | "uses" | "approval";
|
|
2638
2638
|
to: {
|
|
2639
2639
|
kind: "system";
|
|
2640
2640
|
id: string;
|
|
@@ -2690,7 +2690,7 @@ declare const topologyRelationship: {
|
|
|
2690
2690
|
kind: "externalResource";
|
|
2691
2691
|
id: string;
|
|
2692
2692
|
};
|
|
2693
|
-
kind: "
|
|
2693
|
+
kind: "triggers" | "uses" | "approval";
|
|
2694
2694
|
to: {
|
|
2695
2695
|
kind: "system";
|
|
2696
2696
|
id: string;
|
|
@@ -2746,7 +2746,7 @@ declare const topologyRelationship: {
|
|
|
2746
2746
|
kind: "externalResource";
|
|
2747
2747
|
id: string;
|
|
2748
2748
|
};
|
|
2749
|
-
kind: "
|
|
2749
|
+
kind: "triggers" | "uses" | "approval";
|
|
2750
2750
|
to: {
|
|
2751
2751
|
kind: "system";
|
|
2752
2752
|
id: string;
|
|
@@ -2802,7 +2802,7 @@ declare const topologyRelationship: {
|
|
|
2802
2802
|
kind: "externalResource";
|
|
2803
2803
|
id: string;
|
|
2804
2804
|
};
|
|
2805
|
-
kind: "
|
|
2805
|
+
kind: "triggers" | "uses" | "approval";
|
|
2806
2806
|
to: {
|
|
2807
2807
|
kind: "system";
|
|
2808
2808
|
id: string;
|
|
@@ -2858,7 +2858,7 @@ declare const topologyRelationship: {
|
|
|
2858
2858
|
kind: "externalResource";
|
|
2859
2859
|
id: string;
|
|
2860
2860
|
};
|
|
2861
|
-
kind: "
|
|
2861
|
+
kind: "triggers" | "uses" | "approval";
|
|
2862
2862
|
to: {
|
|
2863
2863
|
kind: "system";
|
|
2864
2864
|
id: string;
|
|
@@ -2914,7 +2914,7 @@ declare const topologyRelationship: {
|
|
|
2914
2914
|
kind: "externalResource";
|
|
2915
2915
|
id: string;
|
|
2916
2916
|
};
|
|
2917
|
-
kind: "
|
|
2917
|
+
kind: "triggers" | "uses" | "approval";
|
|
2918
2918
|
to: {
|
|
2919
2919
|
kind: "system";
|
|
2920
2920
|
id: string;
|
|
@@ -3672,7 +3672,6 @@ declare function scaffoldOrganizationModel(model: OrganizationModel, spec: OmSca
|
|
|
3672
3672
|
declare const LinkSchema: z.ZodObject<{
|
|
3673
3673
|
nodeId: z.ZodString;
|
|
3674
3674
|
kind: z.ZodEnum<{
|
|
3675
|
-
approval: "approval";
|
|
3676
3675
|
affects: "affects";
|
|
3677
3676
|
actions: "actions";
|
|
3678
3677
|
effects: "effects";
|
|
@@ -3682,6 +3681,7 @@ declare const LinkSchema: z.ZodObject<{
|
|
|
3682
3681
|
emits: "emits";
|
|
3683
3682
|
triggers: "triggers";
|
|
3684
3683
|
uses: "uses";
|
|
3684
|
+
approval: "approval";
|
|
3685
3685
|
contains: "contains";
|
|
3686
3686
|
references: "references";
|
|
3687
3687
|
maps_to: "maps_to";
|
|
@@ -4528,9 +4528,9 @@ declare const OrganizationModelSchema: z.ZodObject<{
|
|
|
4528
4528
|
id: z.ZodString;
|
|
4529
4529
|
}, z.core.$strip>], "kind">;
|
|
4530
4530
|
kind: z.ZodEnum<{
|
|
4531
|
-
approval: "approval";
|
|
4532
4531
|
triggers: "triggers";
|
|
4533
4532
|
uses: "uses";
|
|
4533
|
+
approval: "approval";
|
|
4534
4534
|
}>;
|
|
4535
4535
|
to: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4536
4536
|
kind: z.ZodLiteral<"system">;
|
|
@@ -997,9 +997,9 @@ declare const OrganizationModelSchema: z.ZodObject<{
|
|
|
997
997
|
id: z.ZodString;
|
|
998
998
|
}, z.core.$strip>], "kind">;
|
|
999
999
|
kind: z.ZodEnum<{
|
|
1000
|
-
approval: "approval";
|
|
1001
1000
|
triggers: "triggers";
|
|
1002
1001
|
uses: "uses";
|
|
1002
|
+
approval: "approval";
|
|
1003
1003
|
}>;
|
|
1004
1004
|
to: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
1005
1005
|
kind: z.ZodLiteral<"system">;
|
|
@@ -2238,9 +2238,9 @@ declare const OmTopologyNodeKindSchema: z.ZodEnum<{
|
|
|
2238
2238
|
externalResource: "externalResource";
|
|
2239
2239
|
}>;
|
|
2240
2240
|
declare const OmTopologyRelationshipKindSchema: z.ZodEnum<{
|
|
2241
|
-
approval: "approval";
|
|
2242
2241
|
triggers: "triggers";
|
|
2243
2242
|
uses: "uses";
|
|
2243
|
+
approval: "approval";
|
|
2244
2244
|
}>;
|
|
2245
2245
|
declare const OmTopologyNodeRefSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
2246
2246
|
kind: z.ZodLiteral<"system">;
|
|
@@ -2295,9 +2295,9 @@ declare const OmTopologyRelationshipSchema: z.ZodObject<{
|
|
|
2295
2295
|
id: z.ZodString;
|
|
2296
2296
|
}, z.core.$strip>], "kind">;
|
|
2297
2297
|
kind: z.ZodEnum<{
|
|
2298
|
-
approval: "approval";
|
|
2299
2298
|
triggers: "triggers";
|
|
2300
2299
|
uses: "uses";
|
|
2300
|
+
approval: "approval";
|
|
2301
2301
|
}>;
|
|
2302
2302
|
to: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
2303
2303
|
kind: z.ZodLiteral<"system">;
|
|
@@ -2357,9 +2357,9 @@ declare const OmTopologyDomainSchema: z.ZodDefault<z.ZodObject<{
|
|
|
2357
2357
|
id: z.ZodString;
|
|
2358
2358
|
}, z.core.$strip>], "kind">;
|
|
2359
2359
|
kind: z.ZodEnum<{
|
|
2360
|
-
approval: "approval";
|
|
2361
2360
|
triggers: "triggers";
|
|
2362
2361
|
uses: "uses";
|
|
2362
|
+
approval: "approval";
|
|
2363
2363
|
}>;
|
|
2364
2364
|
to: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
2365
2365
|
kind: z.ZodLiteral<"system">;
|
|
@@ -2634,7 +2634,7 @@ declare const topologyRelationship: {
|
|
|
2634
2634
|
kind: "externalResource";
|
|
2635
2635
|
id: string;
|
|
2636
2636
|
};
|
|
2637
|
-
kind: "
|
|
2637
|
+
kind: "triggers" | "uses" | "approval";
|
|
2638
2638
|
to: {
|
|
2639
2639
|
kind: "system";
|
|
2640
2640
|
id: string;
|
|
@@ -2690,7 +2690,7 @@ declare const topologyRelationship: {
|
|
|
2690
2690
|
kind: "externalResource";
|
|
2691
2691
|
id: string;
|
|
2692
2692
|
};
|
|
2693
|
-
kind: "
|
|
2693
|
+
kind: "triggers" | "uses" | "approval";
|
|
2694
2694
|
to: {
|
|
2695
2695
|
kind: "system";
|
|
2696
2696
|
id: string;
|
|
@@ -2746,7 +2746,7 @@ declare const topologyRelationship: {
|
|
|
2746
2746
|
kind: "externalResource";
|
|
2747
2747
|
id: string;
|
|
2748
2748
|
};
|
|
2749
|
-
kind: "
|
|
2749
|
+
kind: "triggers" | "uses" | "approval";
|
|
2750
2750
|
to: {
|
|
2751
2751
|
kind: "system";
|
|
2752
2752
|
id: string;
|
|
@@ -2802,7 +2802,7 @@ declare const topologyRelationship: {
|
|
|
2802
2802
|
kind: "externalResource";
|
|
2803
2803
|
id: string;
|
|
2804
2804
|
};
|
|
2805
|
-
kind: "
|
|
2805
|
+
kind: "triggers" | "uses" | "approval";
|
|
2806
2806
|
to: {
|
|
2807
2807
|
kind: "system";
|
|
2808
2808
|
id: string;
|
|
@@ -2858,7 +2858,7 @@ declare const topologyRelationship: {
|
|
|
2858
2858
|
kind: "externalResource";
|
|
2859
2859
|
id: string;
|
|
2860
2860
|
};
|
|
2861
|
-
kind: "
|
|
2861
|
+
kind: "triggers" | "uses" | "approval";
|
|
2862
2862
|
to: {
|
|
2863
2863
|
kind: "system";
|
|
2864
2864
|
id: string;
|
|
@@ -2914,7 +2914,7 @@ declare const topologyRelationship: {
|
|
|
2914
2914
|
kind: "externalResource";
|
|
2915
2915
|
id: string;
|
|
2916
2916
|
};
|
|
2917
|
-
kind: "
|
|
2917
|
+
kind: "triggers" | "uses" | "approval";
|
|
2918
2918
|
to: {
|
|
2919
2919
|
kind: "system";
|
|
2920
2920
|
id: string;
|
|
@@ -3672,7 +3672,6 @@ declare function scaffoldOrganizationModel(model: OrganizationModel, spec: OmSca
|
|
|
3672
3672
|
declare const LinkSchema: z.ZodObject<{
|
|
3673
3673
|
nodeId: z.ZodString;
|
|
3674
3674
|
kind: z.ZodEnum<{
|
|
3675
|
-
approval: "approval";
|
|
3676
3675
|
affects: "affects";
|
|
3677
3676
|
actions: "actions";
|
|
3678
3677
|
effects: "effects";
|
|
@@ -3682,6 +3681,7 @@ declare const LinkSchema: z.ZodObject<{
|
|
|
3682
3681
|
emits: "emits";
|
|
3683
3682
|
triggers: "triggers";
|
|
3684
3683
|
uses: "uses";
|
|
3684
|
+
approval: "approval";
|
|
3685
3685
|
contains: "contains";
|
|
3686
3686
|
references: "references";
|
|
3687
3687
|
maps_to: "maps_to";
|
|
@@ -4528,9 +4528,9 @@ declare const OrganizationModelSchema: z.ZodObject<{
|
|
|
4528
4528
|
id: z.ZodString;
|
|
4529
4529
|
}, z.core.$strip>], "kind">;
|
|
4530
4530
|
kind: z.ZodEnum<{
|
|
4531
|
-
approval: "approval";
|
|
4532
4531
|
triggers: "triggers";
|
|
4533
4532
|
uses: "uses";
|
|
4533
|
+
approval: "approval";
|
|
4534
4534
|
}>;
|
|
4535
4535
|
to: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4536
4536
|
kind: z.ZodLiteral<"system">;
|
|
@@ -2786,6 +2786,7 @@ type Database = {
|
|
|
2786
2786
|
title: string | null;
|
|
2787
2787
|
updated_at: string;
|
|
2788
2788
|
user_id: string;
|
|
2789
|
+
visibility: string;
|
|
2789
2790
|
};
|
|
2790
2791
|
Insert: {
|
|
2791
2792
|
content: string;
|
|
@@ -2800,6 +2801,7 @@ type Database = {
|
|
|
2800
2801
|
title?: string | null;
|
|
2801
2802
|
updated_at?: string;
|
|
2802
2803
|
user_id: string;
|
|
2804
|
+
visibility?: string;
|
|
2803
2805
|
};
|
|
2804
2806
|
Update: {
|
|
2805
2807
|
content?: string;
|
|
@@ -2814,6 +2816,7 @@ type Database = {
|
|
|
2814
2816
|
title?: string | null;
|
|
2815
2817
|
updated_at?: string;
|
|
2816
2818
|
user_id?: string;
|
|
2819
|
+
visibility?: string;
|
|
2817
2820
|
};
|
|
2818
2821
|
Relationships: [
|
|
2819
2822
|
{
|
|
@@ -2982,6 +2985,12 @@ type Database = {
|
|
|
2982
2985
|
Args: never;
|
|
2983
2986
|
Returns: boolean;
|
|
2984
2987
|
};
|
|
2988
|
+
current_user_shares_org_with: {
|
|
2989
|
+
Args: {
|
|
2990
|
+
other_user_id: string;
|
|
2991
|
+
};
|
|
2992
|
+
Returns: boolean;
|
|
2993
|
+
};
|
|
2985
2994
|
current_user_supabase_id: {
|
|
2986
2995
|
Args: never;
|
|
2987
2996
|
Returns: string;
|
|
@@ -3021,6 +3030,20 @@ type Database = {
|
|
|
3021
3030
|
Args: never;
|
|
3022
3031
|
Returns: string;
|
|
3023
3032
|
};
|
|
3033
|
+
has_org_access: {
|
|
3034
|
+
Args: {
|
|
3035
|
+
action?: string;
|
|
3036
|
+
org_id: string;
|
|
3037
|
+
system_path: string;
|
|
3038
|
+
};
|
|
3039
|
+
Returns: boolean;
|
|
3040
|
+
} | {
|
|
3041
|
+
Args: {
|
|
3042
|
+
action?: string;
|
|
3043
|
+
system_path: string;
|
|
3044
|
+
};
|
|
3045
|
+
Returns: boolean;
|
|
3046
|
+
};
|
|
3024
3047
|
has_org_permission: {
|
|
3025
3048
|
Args: {
|
|
3026
3049
|
org_id: string;
|
|
@@ -4398,9 +4421,9 @@ declare const OrganizationModelSchema: z.ZodObject<{
|
|
|
4398
4421
|
id: z.ZodString;
|
|
4399
4422
|
}, z.core.$strip>], "kind">;
|
|
4400
4423
|
kind: z.ZodEnum<{
|
|
4401
|
-
approval: "approval";
|
|
4402
4424
|
triggers: "triggers";
|
|
4403
4425
|
uses: "uses";
|
|
4426
|
+
approval: "approval";
|
|
4404
4427
|
}>;
|
|
4405
4428
|
to: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
4406
4429
|
kind: z.ZodLiteral<"system">;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elevasis/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.30.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Minimal shared constants across Elevasis monorepo",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -16,6 +16,10 @@
|
|
|
16
16
|
"types": "./dist/index.d.ts",
|
|
17
17
|
"import": "./dist/index.js"
|
|
18
18
|
},
|
|
19
|
+
"./auth": {
|
|
20
|
+
"types": "./dist/auth/index.d.ts",
|
|
21
|
+
"import": "./dist/auth/index.js"
|
|
22
|
+
},
|
|
19
23
|
"./test-utils": {
|
|
20
24
|
"types": "./dist/test-utils/index.d.ts",
|
|
21
25
|
"import": "./dist/test-utils/index.js"
|
|
@@ -41,8 +45,8 @@
|
|
|
41
45
|
"rollup-plugin-dts": "^6.3.0",
|
|
42
46
|
"tsup": "^8.0.0",
|
|
43
47
|
"typescript": "5.9.2",
|
|
44
|
-
"@repo/
|
|
45
|
-
"@repo/
|
|
48
|
+
"@repo/eslint-config": "0.0.0",
|
|
49
|
+
"@repo/typescript-config": "0.0.0"
|
|
46
50
|
},
|
|
47
51
|
"dependencies": {
|
|
48
52
|
"@anthropic-ai/sdk": "^0.62.0",
|
|
@@ -13,12 +13,13 @@ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as {
|
|
|
13
13
|
describe('core publish surface', () => {
|
|
14
14
|
it('publishes the curated @elevasis/core organization-model wrapper', () => {
|
|
15
15
|
expect(packageJson.publishConfig?.name).toBe('@elevasis/core')
|
|
16
|
-
expect(Object.keys(packageJson.publishConfig?.exports ?? {})).toEqual([
|
|
17
|
-
'.',
|
|
18
|
-
'./
|
|
19
|
-
'./
|
|
20
|
-
'./
|
|
21
|
-
'./
|
|
22
|
-
|
|
16
|
+
expect(Object.keys(packageJson.publishConfig?.exports ?? {})).toEqual([
|
|
17
|
+
'.',
|
|
18
|
+
'./auth',
|
|
19
|
+
'./test-utils',
|
|
20
|
+
'./organization-model',
|
|
21
|
+
'./entities',
|
|
22
|
+
'./knowledge'
|
|
23
|
+
])
|
|
23
24
|
})
|
|
24
25
|
})
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { AccessKeys, deriveAccessKeyCatalog, findAccessCatalogEntry, normalizeAccessKey } from '../access-keys'
|
|
4
|
+
import { accessTestOrganizationModel } from './access-test-fixtures'
|
|
5
|
+
import { scanAccessKeyUsages } from './access-key-scan'
|
|
6
|
+
|
|
7
|
+
const INTENTIONALLY_UNCONSUMED_ACCESS_KEYS = new Set([
|
|
8
|
+
'secretsManage',
|
|
9
|
+
'operationsManage',
|
|
10
|
+
'acquisitionManage',
|
|
11
|
+
'projectsManage',
|
|
12
|
+
'clientsManage',
|
|
13
|
+
'operationsRecentExecutions'
|
|
14
|
+
])
|
|
15
|
+
|
|
16
|
+
describe('access key coverage', () => {
|
|
17
|
+
it('keeps every runtime consumer key present in the catalog', () => {
|
|
18
|
+
const catalog = deriveAccessKeyCatalog(accessTestOrganizationModel)
|
|
19
|
+
const usages = scanAccessKeyUsages()
|
|
20
|
+
const consumerKeys = [
|
|
21
|
+
...usages.stringLiterals.map((usage) => ({ file: usage.file, key: normalizeAccessKey(usage.key) })),
|
|
22
|
+
...usages.accessKeySymbols.map((usage) => ({
|
|
23
|
+
file: usage.file,
|
|
24
|
+
key: AccessKeys[usage.symbol as keyof typeof AccessKeys]
|
|
25
|
+
}))
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
const missingCatalogEntries = consumerKeys
|
|
29
|
+
.filter((usage) => findAccessCatalogEntry(catalog, usage.key) === undefined)
|
|
30
|
+
.map((usage) => `${usage.file}: ${usage.key.systemPath}:${usage.key.action}`)
|
|
31
|
+
|
|
32
|
+
expect(missingCatalogEntries).toEqual([])
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('keeps exported static AccessKeys either consumed or explicitly reserved', () => {
|
|
36
|
+
const usages = scanAccessKeyUsages()
|
|
37
|
+
const consumedSymbols = new Set(usages.accessKeySymbols.map((usage) => usage.symbol))
|
|
38
|
+
const unconsumedSymbols = Object.keys(AccessKeys).filter((symbol) => !consumedSymbols.has(symbol))
|
|
39
|
+
|
|
40
|
+
expect(unconsumedSymbols).toEqual([...INTENTIONALLY_UNCONSUMED_ACCESS_KEYS])
|
|
41
|
+
})
|
|
42
|
+
})
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { existsSync, readdirSync, readFileSync } from 'node:fs'
|
|
2
|
+
import { dirname, join, relative } from 'node:path'
|
|
3
|
+
import { fileURLToPath } from 'node:url'
|
|
4
|
+
|
|
5
|
+
import type { AccessKeyInput } from '../access-keys'
|
|
6
|
+
|
|
7
|
+
export interface AccessKeyUsage {
|
|
8
|
+
file: string
|
|
9
|
+
key: AccessKeyInput
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface AccessKeySymbolUsage {
|
|
13
|
+
file: string
|
|
14
|
+
symbol: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const repoRoot = findRepoRoot(dirname(fileURLToPath(import.meta.url)))
|
|
18
|
+
|
|
19
|
+
const RUNTIME_SOURCE_ROOTS = [
|
|
20
|
+
join(repoRoot, 'apps', 'command-center', 'src'),
|
|
21
|
+
join(repoRoot, 'packages', 'ui', 'src'),
|
|
22
|
+
join(repoRoot, 'apps', 'api', 'src'),
|
|
23
|
+
join(repoRoot, 'external', '_template', 'ui', 'src')
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
const STRING_LITERAL_PATTERNS = [
|
|
27
|
+
/<AccessGuard\b[\s\S]*?\baccessKey\s*=\s*["']([^"']+)["']/g,
|
|
28
|
+
/<AccessGuard\b[\s\S]*?\baccessKey\s*=\s*\{\s*["']([^"']+)["']\s*\}/g,
|
|
29
|
+
/\buseAccess\(\s*["']([^"']+)["']\s*\)/g,
|
|
30
|
+
/\brequireAccess\(\s*["']([^"']+)["']\s*[,)]/g
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
const ACCESS_KEYS_PATTERN = /\bAccessKeys\.([A-Za-z_$][A-Za-z0-9_$]*)\b/g
|
|
34
|
+
|
|
35
|
+
function findRepoRoot(startDirectory: string): string {
|
|
36
|
+
let current = startDirectory
|
|
37
|
+
|
|
38
|
+
while (current !== dirname(current)) {
|
|
39
|
+
if (existsSync(join(current, 'pnpm-workspace.yaml'))) return current
|
|
40
|
+
current = dirname(current)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
throw new Error('Could not find monorepo root from access-key scan test')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isRuntimeSourceFile(file: string): boolean {
|
|
47
|
+
if (!/\.(ts|tsx)$/.test(file)) return false
|
|
48
|
+
if (file.includes(`${join('', '__tests__').slice(1)}`)) return false
|
|
49
|
+
if (file.includes('__tests__')) return false
|
|
50
|
+
if (/\.(test|spec)\.(ts|tsx)$/.test(file)) return false
|
|
51
|
+
return !file.endsWith('routeTree.gen.ts')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function listRuntimeSourceFiles(directory: string): string[] {
|
|
55
|
+
if (!existsSync(directory)) return []
|
|
56
|
+
|
|
57
|
+
const entries = readdirSync(directory, { withFileTypes: true })
|
|
58
|
+
const files: string[] = []
|
|
59
|
+
|
|
60
|
+
for (const entry of entries) {
|
|
61
|
+
const entryPath = join(directory, entry.name)
|
|
62
|
+
|
|
63
|
+
if (entry.isDirectory()) {
|
|
64
|
+
if (['node_modules', 'dist', '.next', 'coverage'].includes(entry.name)) continue
|
|
65
|
+
files.push(...listRuntimeSourceFiles(entryPath))
|
|
66
|
+
continue
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (isRuntimeSourceFile(entryPath)) files.push(entryPath)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return files
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function relativePath(file: string): string {
|
|
76
|
+
return relative(repoRoot, file).replaceAll('\\', '/')
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function collectMatches<T>(
|
|
80
|
+
content: string,
|
|
81
|
+
pattern: RegExp,
|
|
82
|
+
build: (match: RegExpExecArray) => T
|
|
83
|
+
): T[] {
|
|
84
|
+
const matches: T[] = []
|
|
85
|
+
pattern.lastIndex = 0
|
|
86
|
+
|
|
87
|
+
for (let match = pattern.exec(content); match !== null; match = pattern.exec(content)) {
|
|
88
|
+
matches.push(build(match))
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return matches
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function scanAccessKeyUsages(): {
|
|
95
|
+
stringLiterals: AccessKeyUsage[]
|
|
96
|
+
accessKeySymbols: AccessKeySymbolUsage[]
|
|
97
|
+
} {
|
|
98
|
+
const stringLiterals: AccessKeyUsage[] = []
|
|
99
|
+
const accessKeySymbols: AccessKeySymbolUsage[] = []
|
|
100
|
+
|
|
101
|
+
for (const sourceRoot of RUNTIME_SOURCE_ROOTS) {
|
|
102
|
+
for (const file of listRuntimeSourceFiles(sourceRoot)) {
|
|
103
|
+
const content = readFileSync(file, 'utf8')
|
|
104
|
+
const path = relativePath(file)
|
|
105
|
+
|
|
106
|
+
for (const pattern of STRING_LITERAL_PATTERNS) {
|
|
107
|
+
stringLiterals.push(...collectMatches(content, pattern, (match) => ({ file: path, key: match[1] })))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
accessKeySymbols.push(
|
|
111
|
+
...collectMatches(content, ACCESS_KEYS_PATTERN, (match) => ({ file: path, symbol: match[1] }))
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return { stringLiterals, accessKeySymbols }
|
|
117
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import {
|
|
3
|
+
AccessKeys,
|
|
4
|
+
DIAGNOSTIC_VIEW_ACCESS_KEYS,
|
|
5
|
+
PLATFORM_ADMIN_ACCESS_KEY,
|
|
6
|
+
deriveAccessKeyCatalog,
|
|
7
|
+
findAccessCatalogEntry,
|
|
8
|
+
listAccessKeys,
|
|
9
|
+
normalizeAccessKey
|
|
10
|
+
} from '../access-keys'
|
|
11
|
+
import type { OrganizationModel } from '../../organization-model/types'
|
|
12
|
+
|
|
13
|
+
const model = {
|
|
14
|
+
systems: {
|
|
15
|
+
sales: {
|
|
16
|
+
id: 'sales',
|
|
17
|
+
label: 'Sales',
|
|
18
|
+
lifecycle: 'active',
|
|
19
|
+
order: 10,
|
|
20
|
+
systems: {
|
|
21
|
+
crm: {
|
|
22
|
+
id: 'crm',
|
|
23
|
+
label: 'CRM',
|
|
24
|
+
lifecycle: 'active',
|
|
25
|
+
order: 10
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
operations: {
|
|
30
|
+
id: 'operations',
|
|
31
|
+
label: 'Operations',
|
|
32
|
+
lifecycle: 'active',
|
|
33
|
+
order: 20
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
} as OrganizationModel
|
|
37
|
+
|
|
38
|
+
describe('access keys', () => {
|
|
39
|
+
it('normalizes string shorthand to view access keys', () => {
|
|
40
|
+
expect(normalizeAccessKey('sales.crm')).toEqual({ systemPath: 'sales.crm', action: 'view' })
|
|
41
|
+
expect(normalizeAccessKey({ systemPath: 'sales.crm' })).toEqual({ systemPath: 'sales.crm', action: 'view' })
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
it('normalizes platformAdmin string shorthand to the platform special-case key', () => {
|
|
45
|
+
expect(normalizeAccessKey('platformAdmin')).toEqual(AccessKeys.platformAdmin)
|
|
46
|
+
expect(normalizeAccessKey('platformAdmin').systemPath).toBe(PLATFORM_ADMIN_ACCESS_KEY)
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
it('derives view and manage access keys from nested Organization Model systems', () => {
|
|
50
|
+
const catalog = deriveAccessKeyCatalog(model)
|
|
51
|
+
const keys = listAccessKeys(catalog)
|
|
52
|
+
|
|
53
|
+
expect(keys).toContainEqual({ systemPath: 'sales', action: 'view' })
|
|
54
|
+
expect(keys).toContainEqual({ systemPath: 'sales.crm', action: 'view' })
|
|
55
|
+
expect(keys).toContainEqual({ systemPath: 'sales.crm', action: 'manage' })
|
|
56
|
+
expect(keys).toContainEqual({ systemPath: 'operations', action: 'manage' })
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('includes diagnostic allowlist keys and platformAdmin in the catalog', () => {
|
|
60
|
+
const catalog = deriveAccessKeyCatalog(model)
|
|
61
|
+
|
|
62
|
+
for (const systemPath of DIAGNOSTIC_VIEW_ACCESS_KEYS) {
|
|
63
|
+
expect(findAccessCatalogEntry(catalog, { systemPath, action: 'view' })?.source).toBe('diagnostic')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
expect(findAccessCatalogEntry(catalog, AccessKeys.platformAdmin)?.source).toBe('platform')
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
it('includes permission-only access keys with their legacy role-permission mapping', () => {
|
|
70
|
+
const catalog = deriveAccessKeyCatalog(model)
|
|
71
|
+
|
|
72
|
+
expect(findAccessCatalogEntry(catalog, AccessKeys.organizationManage)).toMatchObject({
|
|
73
|
+
source: 'permission',
|
|
74
|
+
rolePermission: 'org.manage'
|
|
75
|
+
})
|
|
76
|
+
expect(findAccessCatalogEntry(catalog, AccessKeys.membersManage)).toMatchObject({
|
|
77
|
+
source: 'permission',
|
|
78
|
+
rolePermission: 'members.manage'
|
|
79
|
+
})
|
|
80
|
+
})
|
|
81
|
+
})
|