Modules and Imports
Managing dependencies and organizing your PolicyFlow code
PolicyFlow uses a powerful module system inspired by modern development environments like Node.js to manage dependencies between files. This allows for a clean, scalable project structure. The unique identity of a policy or schema is determined by its file path, not a long, fully qualified name.
Import Syntax
The import
statement is used at the beginning of a .pf
or .pftest
file to bring schemas, types, or other policies into scope.
Path Structure
The import path is structured and flexible:
@/
is an alias for the project's root directory./
refers to the current directory../
refers to the parent directory- An optional
:schemaName
or:policyName
can be appended to a filename to specifically target one schema/policy within a file that may contain multiple definitions - File extensions (
.pf
,.pfs
,.pftest
) are required to disambiguate files
Import Patterns
Importing All Types from a Specific Schema
This is the most common pattern. It imports all types from a single, specified schema within a file and makes them available under an alias.
Syntax: import * as <Alias> from "<PathTo/Filename.ext>:<SchemaName>";
// Import all types from the `AuthSchema` inside the `auth.pfs` file.
import * as Auth from "@/schemas/auth.pfs:AuthSchema";
// Now you can use types like `Auth.CorporateUser`
policy UserAccess {
schemas {
User from Auth.CorporateUser
}
// ...
}
Importing Specific Named Types
If you only need a few types, you can import them directly by name. This can improve readability for simple policies.
Syntax: import { <Type1>, <Type2> } from "<PathTo/Filename.ext>:<SchemaName>";
import { CorporateUser, ServiceAccount } from "@/schemas/auth.pfs:AuthSchema";
// You can now use `CorporateUser` directly without an alias.
policy AccessControl {
schemas {
User from CorporateUser
}
// ...
}
Importing from a File with a Single Schema
If a .pfs
file contains only one schema definition, you can omit the :SchemaName
specifier for brevity.
Syntax: import * as <Alias> from "<PathTo/Filename.ext>";
// Assumes `document.pfs` contains only one schema.
import * as Doc from "@/schemas/document.pfs";
Importing Policies
The same syntax applies to importing policies for testing:
// Import a policy for testing - can omit :PolicyName if file has one policy
import * as DocPolicy from "@/policies/document-access.pf";
// Or explicitly specify the policy name
import * as DocPolicy from "@/policies/document-access.pf:DocumentAccessPolicy";
test DocumentPolicyTests for DocPolicy {
// test cases...
}
Module Organization Best Practices
1. Schema Organization
Group related types together in logical schema files:
schema AuthSchema {
// User-related types
User type CorporateUser {
id: UUID
email: Email
department: String
isActive: Boolean = true
}
User type ServiceAccount {
id: UUID
name: String
scopes: String[]
active: Boolean = true
}
// Authentication context
Context type AuthContext {
sessionId: String
authMethod: String
timestamp: DateTime = DateTime.Now()
}
}
schema ResourceSchema {
Resource type Document {
id: UUID
title: String
ownerId: UUID
createdAt: DateTime = DateTime.Now()
}
Resource type Database {
id: UUID
name: String
environment: String
isPublic: Boolean = false
}
}
2. Policy Organization
Organize policies by domain or feature:
policies/
├── core/
│ ├── authentication.pf
│ └── rate-limiting.pf
├── resources/
│ ├── document-access.pf
│ └── database-access.pf
└── compliance/
├── gdpr.pf
└── sox.pf
3. Shared Base Types
Create base schemas that other schemas can inherit from:
/**
* Defines foundational types used across the entire system.
*/
schema BaseSchema {
// A regular 'type' that serves as a base for other types.
type Entity {
id: UUID
createdAt: DateTime = DateTime.Now()
updatedAt: DateTime = DateTime.Now()
isActive: Boolean = true
}
}
// This schema imports 'base' to use its types.
import * as Base from "@/schemas/base.pfs";
schema AuthSchema {
/**
* CorporateUser inherits all properties from Base.Entity
* and adds its own. It is designated as a 'User' type.
*/
User type CorporateUser : Base.Entity {
email: Email
department: String
roles: String[]
emailVerified: Boolean = false
}
}
Import Resolution
File Path Examples
// Absolute imports (from project root)
import * as Auth from "@/schemas/auth.pfs:AuthSchema";
import * as Core from "@/policies/core/authentication.pf";
// Relative imports
import * as Sibling from "./sibling-schema.pfs:SchemaName";
import * as Parent from "../parent-schema.pfs:SchemaName";
// Import from subdirectory
import * as Sub from "./subdirectory/schema.pfs:SchemaName";
// Always use file extensions
import * as Auth from "@/schemas/auth.pfs:AuthSchema";
import * as Policy from "@/policies/document.pf";
Import Precedence
When multiple imports might conflict, the last import takes precedence:
import { User } from "@/schemas/old-auth.pfs:OldAuth";
import { User } from "@/schemas/new-auth.pfs:NewAuth"; // This User type wins
// User now refers to the type from new-auth
To avoid confusion, use aliases:
import * as OldAuth from "@/schemas/old-auth.pfs:OldAuth";
import * as NewAuth from "@/schemas/new-auth.pfs:NewAuth";
// Now you can be explicit: OldAuth.User vs NewAuth.User
Common Patterns
The Standard Import Pattern
Most PolicyFlow files follow this pattern:
// 1. Import schemas
import * as Auth from "@/schemas/auth.pfs:AuthSchema";
import * as Resources from "@/schemas/resources.pfs:ResourceSchema";
import * as Context from "@/schemas/context.pfs:ContextSchema";
// 2. Import shared utilities or constants (if needed)
import * as Constants from "@/lib/constants.pfs";
// 3. Define your policy
policy MyPolicy {
schemas {
User from Auth.CorporateUser
Resource from Resources.Document
Context from Context.WebContext
}
// ... rules ...
}
Cross-Domain Imports
When policies need to reference multiple domains:
// A compliance policy that needs to understand multiple resource types
import * as Documents from "@/schemas/documents.pfs:DocumentSchema";
import * as Databases from "@/schemas/databases.pfs:DatabaseSchema";
import * as Auth from "@/schemas/auth.pfs:AuthSchema";
policy GDPRCompliance {
// This policy can now handle multiple resource types
schemas {
User from Auth.CorporateUser where user.region == "EU"
Resource from Documents.PersonalDocument | Databases.CustomerDB
}
// ... GDPR-specific rules ...
}