├── test ├── prompts │ ├── javascript.txt │ ├── phoenix.txt │ ├── bun-elysia.txt │ └── ash-framework.txt └── docs │ ├── ash-docs │ ├── ash_appsignal.md │ ├── ash_csv.md │ ├── ash_rbac.md │ ├── ash_money.md │ ├── ash_cloak.md │ ├── ash_archival.md │ └── ash_cubdb.md │ └── javascript-docs │ └── sample.md ├── tsconfig.json ├── package.json ├── Dockerfile ├── .gitignore ├── src ├── logger.ts ├── types.ts ├── db.ts ├── gemini.ts └── index.ts ├── smithery.yaml ├── prompts └── default.txt ├── README.md └── bun.lock /test/prompts/javascript.txt: -------------------------------------------------------------------------------- 1 | You are an expert JavaScript programmer with extensive knowledge about JavaScript's core features, best practices, and modern development patterns. -------------------------------------------------------------------------------- /test/prompts/phoenix.txt: -------------------------------------------------------------------------------- 1 | You are an expert Elixir programmer with deep knowledge about the Phoenix web framework. You understand the intricacies of building scalable, real-time web applications using Phoenix's features and OTP patterns. -------------------------------------------------------------------------------- /test/prompts/bun-elysia.txt: -------------------------------------------------------------------------------- 1 | You are an expert programmer with deep knowledge about Bun and the Elysia web framework. You understand the intricacies of building high-performance web applications using Bun's runtime features and Elysia's type-safe approach. -------------------------------------------------------------------------------- /test/prompts/ash-framework.txt: -------------------------------------------------------------------------------- 1 | You are an expert Elixir programmer with extensive and detailed knowledge about the Ash application framework. You understand the intricacies of building sophisticated applications using Ash's resource-oriented approach, including domain modeling, data persistence, authorization, and real-time features. -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2021", 4 | "module": "ESNext", 5 | "moduleResolution": "bundler", 6 | "types": ["bun-types"], 7 | "allowJs": true, 8 | "strict": true, 9 | "noEmit": true, 10 | "allowImportingTsExtensions": true, 11 | "skipLibCheck": true, 12 | "jsx": "react-jsx" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gemini-docs-mcp", 3 | "module": "index.ts", 4 | "type": "module", 5 | "private": true, 6 | "devDependencies": { 7 | "@types/bun": "latest", 8 | "@types/js-yaml": "^4.0.9" 9 | }, 10 | "peerDependencies": { 11 | "typescript": "^5" 12 | }, 13 | "dependencies": { 14 | "@google/generative-ai": "^0.22.0", 15 | "@modelcontextprotocol/sdk": "^1.6.0", 16 | "js-yaml": "^4.1.0", 17 | "zod": "^3.24.2" 18 | } 19 | } -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # Generated by https://smithery.ai. See: https://smithery.ai/docs/config#dockerfile 2 | FROM oven/bun:latest 3 | 4 | WORKDIR /app 5 | 6 | # Copy the entire repository into a subdirectory to match expected structure 7 | COPY . /app/gemini-docs-mcp 8 | 9 | # Install dependencies in the subdirectory 10 | WORKDIR /app/gemini-docs-mcp 11 | RUN bun install --ignore-scripts 12 | 13 | # Set workdir back to /app for running the command 14 | WORKDIR /app 15 | 16 | CMD ["bun", "/app/gemini-docs-mcp/src/index.ts"] 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies (bun install) 2 | node_modules 3 | 4 | # output 5 | out 6 | dist 7 | *.tgz 8 | 9 | # code coverage 10 | coverage 11 | *.lcov 12 | 13 | # logs 14 | logs 15 | *.log 16 | _.log 17 | report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json 18 | 19 | # dotenv environment variable files 20 | .env 21 | .env.development.local 22 | .env.test.local 23 | .env.production.local 24 | .env.local 25 | 26 | # caches 27 | .eslintcache 28 | .cache 29 | *.tsbuildinfo 30 | 31 | # IntelliJ based IDEs 32 | .idea 33 | 34 | # Finder (MacOS) folder config 35 | .DS_Store 36 | 37 | # Local DB 38 | gemini_docs.sqlite -------------------------------------------------------------------------------- /src/logger.ts: -------------------------------------------------------------------------------- 1 | import { join } from 'path'; 2 | import type { FileSink } from 'bun'; 3 | 4 | export class Logger { 5 | private static writer: FileSink | null = null; 6 | private static enabled = false; 7 | 8 | static enable() { 9 | this.enabled = true; 10 | this.writer = Bun.file(join(import.meta.dir, '..', 'debug.log')).writer(); 11 | } 12 | 13 | static log(message: string) { 14 | if (!this.enabled) return; 15 | 16 | const timestamp = new Date().toISOString(); 17 | this.writer?.write(`[${timestamp}] ${message}\n`); 18 | this.writer?.flush(); 19 | } 20 | 21 | static close() { 22 | if (this.writer) { 23 | this.writer.end(); 24 | this.writer = null; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /test/docs/ash-docs/ash_appsignal.md: -------------------------------------------------------------------------------- 1 | [![ash_appsignal](assets/logo.png)](AshAppsignal.html) 2 | 3 | [ash\_appsignal](AshAppsignal.html) 4 | 5 | v0.1.3 6 | 7 | - GUIDES 8 | - Modules 9 | 10 | 11 | 12 | 13 | 14 | Search documentation of ash\_appsignal 15 | 16 | Settings 17 | 18 | # AshAppsignal (ash\_appsignal v0.1.3) 19 | 20 | Documentation for [`AshAppsignal`](AshAppsignal.html#content). 21 | 22 | [Hex Package](https://hex.pm/packages/ash_appsignal/0.1.3) [Hex Preview](https://preview.hex.pm/preview/ash_appsignal/0.1.3) Search HexDocs [Download ePub version](ash_appsignal.epub "ePub version") 23 | 24 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 25 | -------------------------------------------------------------------------------- /smithery.yaml: -------------------------------------------------------------------------------- 1 | # Smithery configuration file: https://smithery.ai/docs/config#smitheryyaml 2 | 3 | startCommand: 4 | type: stdio 5 | configSchema: 6 | # JSON Schema defining the configuration options for the MCP. 7 | type: object 8 | required: 9 | - geminiApiKey 10 | properties: 11 | geminiApiKey: 12 | type: string 13 | description: API key for authenticating with the Gemini API. 14 | description: Configuration for Gemini Docs MCP server. 15 | commandFunction: 16 | # A JS function that produces the CLI command based on the given config to start the MCP on stdio. 17 | |- 18 | (config) => ({ command: 'bun', args: ['/app/gemini-docs-mcp/src/index.ts', '--verbose'], env: { GEMINI_API_KEY: config.geminiApiKey } }) 19 | exampleConfig: 20 | geminiApiKey: dummy_api_key_here 21 | -------------------------------------------------------------------------------- /test/docs/javascript-docs/sample.md: -------------------------------------------------------------------------------- 1 | # Sample Documentation 2 | 3 | ## Functions 4 | 5 | ### Array Operations 6 | 7 | `map()` creates a new array populated with the results of calling a provided function on every element in the array. 8 | 9 | ```javascript 10 | const numbers = [1, 2, 3]; 11 | const doubled = numbers.map(x => x * 2); 12 | // doubled: [2, 4, 6] 13 | ``` 14 | 15 | `filter()` creates a new array with all elements that pass the test implemented by the provided function. 16 | 17 | ```javascript 18 | const numbers = [1, 2, 3, 4, 5]; 19 | const evens = numbers.filter(x => x % 2 === 0); 20 | // evens: [2, 4] 21 | ``` 22 | 23 | `reduce()` executes a reducer function on each element of the array, resulting in a single output value. 24 | 25 | ```javascript 26 | const numbers = [1, 2, 3, 4]; 27 | const sum = numbers.reduce((acc, curr) => acc + curr, 0); 28 | // sum: 10 29 | ``` 30 | 31 | ## Best Practices 32 | 33 | ### Array Operations 34 | 35 | 1. Use `map()` when you want to transform each element in an array 36 | 2. Use `filter()` when you want to select elements that meet certain criteria 37 | 3. Use `reduce()` when you want to combine all elements into a single value 38 | 4. Prefer array methods over loops for better readability and maintainability 39 | 5. Chain array methods for complex operations instead of nesting them -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | // Base file record types 2 | export interface FileRecord { 3 | file_path: string; 4 | file_id: string; 5 | file_uri: string; 6 | } 7 | 8 | // Gemini API types for file handling 9 | export interface GeminiFileData { 10 | fileUri: string; 11 | } 12 | 13 | export interface GeminiContentPart { 14 | text?: string; 15 | fileData?: GeminiFileData; 16 | } 17 | 18 | export interface GeminiContent { 19 | parts: GeminiContentPart[]; 20 | } 21 | 22 | // Response types for different queries 23 | export interface GeminiResponse { 24 | success: boolean; 25 | data: CanXBeResponse | HowToDoResponse | HintsProblemResponse | GoodPracticeResponse; 26 | } 27 | 28 | export interface CanXBeResponse { 29 | yes?: { 30 | example: string; 31 | betterApproach?: string; 32 | }; 33 | no?: { 34 | reason: string; 35 | alternative: string; 36 | }; 37 | } 38 | 39 | export interface HowToDoResponse { 40 | examples: Array<{ 41 | content: string; 42 | description: string; 43 | }>; 44 | alternativeApproaches: Array<{ 45 | description: string; 46 | content: string; 47 | }>; 48 | } 49 | 50 | export interface HintsProblemResponse { 51 | cantSayForSure?: { 52 | topics: string[]; 53 | }; 54 | try?: Array<{ 55 | possibleReason: string; 56 | possibleSolution: string; 57 | }>; 58 | seeAlso?: string[]; 59 | } 60 | 61 | export interface GoodPracticeResponse { 62 | isGoodPractice: { 63 | yes?: { 64 | cosmeticImprovements?: string[]; 65 | }; 66 | no?: { 67 | betterAlternatives: string[]; 68 | }; 69 | }; 70 | } 71 | 72 | // Tool parameter types 73 | export interface ToolParams { 74 | docs: string; 75 | prompt: string; 76 | } 77 | 78 | export interface CanXBeDoneParams extends ToolParams { 79 | x: string; 80 | technology: string; 81 | } 82 | 83 | export interface HowToDoXParams extends ToolParams { 84 | x: string; 85 | technology: string; 86 | } 87 | 88 | export interface HintsForProblemParams extends ToolParams { 89 | problem: string; 90 | context: string; 91 | environment: Record; 92 | } 93 | 94 | export interface IsThisGoodPracticeParams extends ToolParams { 95 | snippet: string; 96 | context: string; 97 | } -------------------------------------------------------------------------------- /prompts/default.txt: -------------------------------------------------------------------------------- 1 | When answering queries: 2 | 3 | 1. Ground your responses in the provided documentation 4 | - Base your answers on the actual documentation content 5 | - Stay within the parameters of what the docs describe 6 | - If multiple approaches exist in the docs, present them all 7 | 8 | 2. Be comprehensive and detailed 9 | - Provide complete, working examples 10 | - Include explanations for key concepts 11 | - Cover edge cases and considerations 12 | - Explain the rationale behind recommendations 13 | 14 | 3. Format responses in YAML matching the query type structure: 15 | - can_x_be_done: {success, yes:{example, betterApproach} | no:{reason, alternative}} 16 | - how_to_do_x: {success, examples[], alternativeApproaches[]} 17 | - hints_for_problem: {success, cantSayForSure:{topics[]} | try:[{possibleReason, possibleSolution}], seeAlso[]} 18 | - is_this_good_practice: {success, isGoodPractice:{yes:{cosmeticImprovements[]} | no:{betterAlternatives[]}}} 19 | 20 | Format all responses as YAML with this structure for different query types: 21 | 22 | For "can X be done": 23 | success: true 24 | data: 25 | yes: 26 | example: "" 27 | betterApproach: "" 28 | # OR 29 | no: 30 | reason: "" 31 | alternative: "" 32 | 33 | For "how to do X": 34 | success: true 35 | data: 36 | examples: 37 | - content: "" 38 | description: "" 39 | - content: "" 40 | description: "" 41 | alternativeApproaches: 42 | - description: "" 43 | content: "" 44 | - description: "" 45 | content: "" 46 | 47 | For "hints for problem": 48 | success: true 49 | data: 50 | cantSayForSure: 51 | topics: 52 | - "" 53 | - "" 54 | # OR 55 | try: 56 | - possibleReason: "" 57 | possibleSolution: "" 58 | - possibleReason: "" 59 | possibleSolution: "" 60 | seeAlso: 61 | - "" 62 | - "" 63 | 64 | For "is this good practice": 65 | success: true 66 | data: 67 | isGoodPractice: 68 | yes: 69 | cosmeticImprovements: 70 | - "" 71 | - "" 72 | # OR 73 | no: 74 | betterAlternatives: 75 | - "" 76 | - "" 77 | 78 | IMPORTANT: All code snippets must be properly escaped strings in YAML. Do not use literal blocks or code fences. 79 | 80 | Remember: Keep your response as a clean YAML document with no extra formatting. -------------------------------------------------------------------------------- /src/db.ts: -------------------------------------------------------------------------------- 1 | import { Database } from 'bun:sqlite'; 2 | import type { FileRecord } from './types'; 3 | import { join, resolve } from 'path'; 4 | import { Logger } from './logger'; 5 | 6 | export class GeminiDocsDB { 7 | private db: Database; 8 | private dbPath: string; 9 | 10 | constructor() { 11 | try { 12 | this.dbPath = resolve(join(import.meta.dir, '..', 'gemini_docs.sqlite')); 13 | this.db = new Database(this.dbPath, { create: true }); 14 | this.init(); 15 | } catch (error) { 16 | console.error('[DB] Failed to initialize database:', error); 17 | Logger.log(`[DBDebug] Failed to initialize database: ${error}`); 18 | throw error; 19 | } 20 | } 21 | 22 | private readonly TABLE_SQL = ` 23 | CREATE TABLE gemini_files ( 24 | file_id TEXT PRIMARY KEY, 25 | file_path TEXT NOT NULL, 26 | file_uri TEXT NOT NULL, 27 | created_at INTEGER NOT NULL DEFAULT (unixepoch()), 28 | UNIQUE(file_path) 29 | ) 30 | `; 31 | 32 | private migrateSchema() { 33 | Logger.log('[DBDebug] Schema migration not implemented - using existing schema'); 34 | } 35 | 36 | private init() { 37 | try { 38 | const stmt = this.db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='gemini_files'"); 39 | if (stmt.get()) { 40 | Logger.log('[DBDebug] Found existing table, migrating schema...'); 41 | this.migrateSchema(); 42 | } else { 43 | Logger.log('[DBDebug] Creating new database with latest schema...'); 44 | this.db.run(this.TABLE_SQL); 45 | } 46 | Logger.log('[DBDebug] Database schema initialized/verified'); 47 | } catch (error) { 48 | console.error('[DB] Error during initialization:', error); 49 | Logger.log(`[DBDebug] Error during initialization: ${error}`); 50 | throw error; 51 | } 52 | } 53 | 54 | async saveFileId( 55 | filePath: string, 56 | fileId: string, 57 | fileUri: string 58 | ): Promise { 59 | try { 60 | const stmt = this.db.prepare( 61 | 'INSERT OR REPLACE INTO gemini_files (file_id, file_path, file_uri) VALUES (?, ?, ?)' 62 | ); 63 | stmt.run(fileId, filePath, fileUri); 64 | Logger.log(`[DBDebug] Saved/Updated file record: 65 | ID: ${fileId} 66 | Path: ${filePath} 67 | URI: ${fileUri}`); 68 | } catch (error) { 69 | Logger.log(`[DBDebug] Error saving file record: 70 | Error: ${error}`); 71 | console.error('[DB] Error saving file:', error); 72 | throw error; 73 | } 74 | } 75 | 76 | async getFile(fileHash: string): Promise<{ 77 | fileUri: string; 78 | } | null> { 79 | try { 80 | const stmt = this.db.prepare( 81 | 'SELECT file_uri FROM gemini_files WHERE file_id = ? LIMIT 1' 82 | ); 83 | const result = stmt.get(fileHash) as { 84 | file_uri: string; 85 | } | null; 86 | 87 | if (result) { 88 | Logger.log(`[DBDebug] Retrieved file record for hash ${fileHash}: 89 | Hash: ${fileHash} 90 | URI: ${result.file_uri}`); 91 | } 92 | return result 93 | ? { 94 | fileUri: result.file_uri 95 | } 96 | : null; 97 | } catch (error) { 98 | Logger.log(`[DBDebug] Error retrieving file record for ${fileHash}: ${error}`); 99 | console.error('[DB] Error getting file:', error); 100 | throw error; 101 | } 102 | } 103 | 104 | async getAllFiles(): Promise { 105 | try { 106 | const stmt = this.db.prepare( 107 | 'SELECT file_path, file_id, file_uri FROM gemini_files ORDER BY file_id, file_path' 108 | ); 109 | Logger.log('[DBDebug] Retrieved all file records from database'); 110 | return stmt.all() as FileRecord[]; 111 | } catch (error) { 112 | console.error('[DB] Error getting files:', error); 113 | Logger.log(`[DBDebug] Error retrieving all file records: ${error}`); 114 | throw error; 115 | } 116 | } 117 | 118 | close(): void { 119 | this.db.close(); 120 | } 121 | } -------------------------------------------------------------------------------- /src/gemini.ts: -------------------------------------------------------------------------------- 1 | import { GoogleGenerativeAI } from '@google/generative-ai'; 2 | import { load } from 'js-yaml'; 3 | import { join } from 'path'; 4 | import { readFileSync } from 'fs'; 5 | import type { GeminiResponse, GeminiContent, GeminiContentPart, GeminiFileData } from './types'; 6 | 7 | // Error codes that should trigger a retry 8 | const RETRYABLE_STATUS_CODES = new Set(['429', '503', '500', '502', '504']); 9 | 10 | export class GeminiAPI { 11 | private gemini: GoogleGenerativeAI; 12 | private model: any; 13 | private defaultPrompt: string; 14 | private rateLimitDelay = 1000; 15 | private maxRetries = 3; 16 | private lastRequestTime = 0; 17 | 18 | constructor() { 19 | const API_KEY = process.env.GEMINI_API_KEY; 20 | if (!API_KEY) { 21 | throw new Error('GEMINI_API_KEY environment variable is required'); 22 | } 23 | 24 | try { 25 | this.gemini = new GoogleGenerativeAI(API_KEY); 26 | this.model = this.gemini.getGenerativeModel({ model: 'gemini-1.5-pro' }); 27 | // Load default prompt using full path from project root 28 | const defaultPromptPath = join(process.cwd(), 'gemini-docs-mcp', 'prompts', 'default.txt'); 29 | this.defaultPrompt = readFileSync(defaultPromptPath, 'utf-8'); 30 | } catch (error) { 31 | console.error('[Gemini] Failed to initialize:', error); 32 | throw error; 33 | } 34 | } 35 | 36 | private async waitForRateLimit() { 37 | const now = Date.now(); 38 | const timeSinceLastRequest = now - this.lastRequestTime; 39 | 40 | if (timeSinceLastRequest < this.rateLimitDelay) { 41 | await Bun.sleep(this.rateLimitDelay - timeSinceLastRequest); 42 | } 43 | 44 | this.lastRequestTime = Date.now(); 45 | } 46 | 47 | private shouldRetry(error: any): boolean { 48 | // Check if error message includes status code 49 | for (const code of RETRYABLE_STATUS_CODES) { 50 | if (error.message?.includes(`[${code}`)) { 51 | return true; 52 | } 53 | } 54 | 55 | // Check for quota or availability keywords 56 | return ( 57 | error.message?.includes('quota') || 58 | error.message?.includes('unavailable') || 59 | error.message?.includes('try again') 60 | ); 61 | } 62 | 63 | private formatPrompt( 64 | query: string, 65 | docRefs: GeminiFileData[], 66 | context: Record = {}, 67 | promptFile?: string 68 | ): GeminiContent[] { 69 | let prompt = ''; 70 | 71 | // Add custom prompt if provided 72 | if (promptFile) { 73 | prompt = readFileSync(promptFile, 'utf-8') + '\n\n'; 74 | } 75 | 76 | // Add default prompt and query 77 | prompt += `${this.defaultPrompt}\n\nUSER QUERY: ${query}\nCONTEXT: ${JSON.stringify(context)}`; 78 | const parts: GeminiContentPart[] = [{ text: prompt.trim() }]; 79 | 80 | for (const doc of docRefs) { 81 | parts.push({ fileData: doc }); 82 | } 83 | 84 | return [{ parts }]; 85 | } 86 | 87 | private async retryWithBackoff( 88 | operation: () => Promise, 89 | retries = this.maxRetries, 90 | initialDelay = this.rateLimitDelay 91 | ): Promise { 92 | let lastError: Error; 93 | let delay = initialDelay; 94 | 95 | for (let i = 0; i < retries; i++) { 96 | try { 97 | await this.waitForRateLimit(); 98 | return await operation(); 99 | } catch (error: any) { 100 | lastError = error; 101 | 102 | if (this.shouldRetry(error)) { 103 | console.error(`[Gemini] Request failed (attempt ${i + 1}/${retries}), retrying in ${delay}ms:`, error.message); 104 | await Bun.sleep(delay); 105 | // Use more aggressive backoff for service issues 106 | delay *= 3; 107 | continue; 108 | } 109 | 110 | throw error; 111 | } 112 | } 113 | 114 | throw lastError!; 115 | } 116 | 117 | private async query(contents: GeminiContent[]): Promise { 118 | return await this.retryWithBackoff(async () => { 119 | const response = await this.model.generateContent({ contents }); 120 | return response.response.text(); 121 | }); 122 | } 123 | 124 | private parseResponse(text: string): GeminiResponse { 125 | try { 126 | // Extract just the YAML portion by finding the first "success: true" 127 | let yamlContent = text; 128 | const successIndex = text.indexOf('success: true'); 129 | if (successIndex !== -1) { 130 | yamlContent = text.slice(successIndex); 131 | } 132 | 133 | // Further clean up the text 134 | yamlContent = yamlContent 135 | .split('\n') 136 | .map(line => line.replace(/^```\s*ya?ml\s*$/, '').replace(/^```\s*$/, '')) 137 | .join('\n') 138 | .trim(); 139 | 140 | const response = load(yamlContent) as GeminiResponse; 141 | 142 | if (!response.success || !response.data) { 143 | throw new Error('Invalid response format'); 144 | } 145 | 146 | return response; 147 | } catch (error) { 148 | console.error('[Gemini] Failed to parse response:', error); 149 | throw error; 150 | } 151 | } 152 | 153 | async canXBeDone( 154 | docRefs: GeminiFileData[], 155 | x: string, 156 | technology: string, 157 | prompt?: string 158 | ): Promise { 159 | const contents = this.formatPrompt( 160 | `Can I "${x}" in ${technology}? Show example if possible.`, 161 | docRefs, 162 | { technology }, 163 | prompt 164 | ); 165 | 166 | const result = await this.query(contents); 167 | return this.parseResponse(result); 168 | } 169 | 170 | async howToDoX( 171 | docRefs: GeminiFileData[], 172 | x: string, 173 | technology: string, 174 | prompt?: string 175 | ): Promise { 176 | const contents = this.formatPrompt( 177 | `How do I "${x}" in ${technology}? Show multiple examples and alternative approaches if available.`, 178 | docRefs, 179 | { technology }, 180 | prompt 181 | ); 182 | 183 | const result = await this.query(contents); 184 | return this.parseResponse(result); 185 | } 186 | 187 | async hintsForProblem( 188 | docRefs: GeminiFileData[], 189 | problem: string, 190 | context: string, 191 | environment: Record, 192 | prompt?: string 193 | ): Promise { 194 | const contents = this.formatPrompt( 195 | problem, 196 | docRefs, 197 | { context, environment }, 198 | prompt 199 | ); 200 | 201 | const result = await this.query(contents); 202 | return this.parseResponse(result); 203 | } 204 | 205 | async isThisGoodPractice( 206 | docRefs: GeminiFileData[], 207 | snippet: string, 208 | context: string, 209 | prompt?: string 210 | ): Promise { 211 | const contents = this.formatPrompt( 212 | `Is this a good practice:\n${snippet}\nContext: ${context}`, 213 | docRefs, 214 | { context }, 215 | prompt 216 | ); 217 | 218 | const result = await this.query(contents); 219 | return this.parseResponse(result); 220 | } 221 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gemini Docs MCP Server 2 | 3 | [![smithery badge](https://smithery.ai/badge/@M-Gonzalo/cosa-sai)](https://smithery.ai/server/@M-Gonzalo/cosa-sai) 4 | 5 | ## Description 6 | 7 | This project implements an MCP server that enables access to documentation for various technologies using the Gemini API with its gigantic context window of 2M tokens. It should work for any client, but is targeted especially to the Roo/Cline environment. 8 | 9 | This approach offers several advantages over simply browsing the web or using a search engine: 10 | 11 | * **Access to a curated knowledge base:** The LLM uses a specific set of documentation, avoiding junk results and false positives that can confuse the model. 12 | * **Overcomes context window limitations:** By providing the documentation directly, the LLM can access more information than would be possible with web search alone. 13 | * **Tailored and well-thought-out responses:** The LLM doesn't just provide snippets from the documentation, but crafts well-reasoned answers that take into consideration the entire specification for the technology in question. This allows for more complex questions like "what alternative ways of doing X are there?" or "is this snippet idiomatic?". 14 | 15 | It also overcomes some problemmatic hurdles of traditional RAG systems: 16 | 17 | * **No need for chunking:** The LLM can access the entire documentation in one go, without needing to chunk it into smaller pieces, and having to painfully test and choose between all the possible ways of doing so. 18 | * **No need for a retriever:** The Gemini API itself serves as a powerful retriever that can access the entire documentation, so there's no need to implement a custom one. 19 | * **No vectorization, vector DBs, or other complex systems:** We work directly with plain text, and since we can see everything at once, we don't need vectors for similarity search. If it's relevant, we know about it. 20 | 21 | There are some limitations, though: 22 | 23 | * **No real-time updates:** The documentation is static and won't be updated in real time. This means that the LLM might not know about the latest features or changes in the technology unless we manually update the documentation or provide an automated way of doing so. 24 | * **A lot of tokens is not the same as an infinite context window:** The LLM can only see about 2 million tokens at a time, so it might not be able to see the entire documentation for some technologies. This is especially true for large and complex stacks with copious amounts of documentation. 25 | * **It's not that fast:** We're using Gemini 1.5 Pro (not Flash), and we're loading it with a whole bunch of documentation, so it might take a while to get a response. This is especially true for the first query, as the server needs to upload the documentation to the API. 26 | 27 | ## Features 28 | 29 | * Enables clients to take an "ask your docs" approach to learning and debugging for an arbitrary number of technologies, including some obscure or lesser-known ones. 30 | * Uses the Gemini API to answer questions about the documentation. 31 | * Supports multiple tools for querying the documentation: 32 | * `can_x_be_done`: Check if a specific task can be done in a given technology. 33 | * `hints_for_problem`: Get hints for solving a specific problem. 34 | * `is_this_good_practice`: Check if a code snippet follows good practices. 35 | * `how_to_do_x`: Get examples and alternative approaches for a specific task. 36 | * Provides a logging system for debugging (enabled with the `--verbose` flag). 37 | 38 | ## Getting Started 39 | 40 | ### Installing via Smithery 41 | 42 | To install Gemini Docs Server for Claude Desktop automatically via [Smithery](https://smithery.ai/server/@M-Gonzalo/cosa-sai): 43 | 44 | ```bash 45 | npx -y @smithery/cli install @M-Gonzalo/cosa-sai --client claude 46 | ``` 47 | 48 | This MCP server is automatically started and managed by the client. To enable it, you need to configure it in your settings file (for example, `~/.config/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/cline_mcp_settings.json`). There's usually a button for opening up the settings file in the client. 49 | 50 | Here's the configuration for this server: 51 | 52 | ```json 53 | { 54 | "command": "bun", 55 | "args": [ 56 | "--watch", 57 | "path/to/repo/cosa-sai-mcp/src/index.ts", 58 | "--verbose" 59 | ], 60 | "env": { 61 | "GEMINI_API_KEY": "" 62 | }, 63 | "disabled": false, 64 | "alwaysAllow": [ 65 | "can_x_be_done", 66 | "hints_for_problem", 67 | "is_this_good_practice", 68 | "how_to_do_x" 69 | ], 70 | "timeout": 60 // in seconds 71 | } 72 | ``` 73 | 74 | ## Procuring and Sanitizing the Knowledge Base 75 | 76 | This MCP server requires a knowledge base of documentation to answer questions. You must manually procure this knowledge base, either by downloading a public repository, scraping a website, or using other methods. 77 | 78 | An optional sanitation process can be performed to clean up the original documentation from styling and other unnecessary content. 79 | 80 | Here are some basic tools for doing so. Better solutions are encouraged: 81 | 82 | **Naive Scrapper:** 83 | 84 | ```bash 85 | wget --mirror --convert-links --adjust-extension --page-requisites --no-parent --directory-prefix=./local_copy --no-verbose --show-progress $1 86 | ``` 87 | 88 | **Quick and Dirty Conversor to Markdown-ish:** 89 | 90 | ```bash 91 | #!/bin/bash 92 | 93 | directory="${1:-.}" # Default to current directory if no argument is provided 94 | output_file="${2:-concatenated.md}" # Default output file name 95 | 96 | echo "Concatenating files in '$directory' into '$output_file'..." 97 | 98 | # Clear output file if it exists 99 | truncate -s 0 "$output_file" 100 | 101 | # Find all files (excluding directories) and process them 102 | find "$directory" -type f -name '*.html' | while IFS= read -r file; do 103 | echo "=== ${file#./} ===" >> "$output_file" 104 | cat "$file" \ 105 | | grep -v 'base64' \ 106 | | html2markdown >> "$output_file" 107 | echo -e "\n" >> "$output_file" 108 | done 109 | 110 | echo "Done! Output saved to '$output_file'" 111 | ``` 112 | 113 | ## Usage 114 | 115 | This server provides the following tools: 116 | 117 | * **can\_x\_be\_done:** Checks if a specific task can be done in a given technology. 118 | * **Input:** `docs`, `prompt`, `x`, `technology` 119 | * **Output:** `success`, `data` 120 | * **hints\_for\_problem:** Gets hints for solving a specific problem. 121 | * **Input:** `docs`, `prompt`, `problem`, `context`, `environment` 122 | * **Output:** `success`, `data` 123 | * **is\_this\_good\_practice:** Checks if a code snippet follows good practices. 124 | * **Input:** `docs`, `prompt`, `snippet`, `context` 125 | * **Output:** `success`, `data` 126 | * **how\_to\_do\_x:** Gets examples and alternative approaches for a specific task. 127 | * **Input:** `docs`, `prompt`, `x`, `technology` 128 | * **Output:** `success`, `data` 129 | 130 | ## Contributing 131 | 132 | Contributions are welcome! Please follow these guidelines: 133 | 134 | 1. Fork the repository. 135 | 2. Create a new branch for your feature or bug fix. 136 | 3. Make your changes and commit them with descriptive commit messages. 137 | 4. Submit a pull request. 138 | 139 | ## License 140 | 141 | This project is licensed under the MIT License. 142 | 143 | ## Disclaimer 144 | 145 | This is a very early version of the project, and it's likely to have bugs and limitations. Please report any issues you find, and feel free to suggest improvements or new features. -------------------------------------------------------------------------------- /test/docs/ash-docs/ash_csv.md: -------------------------------------------------------------------------------- 1 | [![ash_csv](assets/logo.png)](https://github.com/ash-project/ash_csv) 2 | 3 | [ash\_csv](https://github.com/ash-project/ash_csv) 4 | 5 | v0.9.7 6 | 7 | - Pages 8 | - Modules 9 | 10 | 11 | 12 | 13 | 14 | Search documentation of ash\_csv 15 | 16 | Settings 17 | 18 | # [View Source](https://github.com/ash-project/ash_csv "View Source") API Reference ash\_csv v0.9.7 19 | 20 | ## [](api-reference.html#modules)Modules 21 | 22 | [AshCsv](AshCsv.html) 23 | 24 | A CSV datalayer for the Ash framework 25 | 26 | [AshCsv.DataLayer](AshCsv.DataLayer.html) 27 | 28 | The data layer implementation for AshCsv 29 | 30 | [AshCsv.DataLayer.Info](AshCsv.DataLayer.Info.html) 31 | 32 | Introspection helpers for AshCsv.DataLayer 33 | 34 | [Next Page → Home](readme.html) 35 | 36 | [Hex Package](https://hex.pm/packages/ash_csv/0.9.7) [Hex Preview](https://preview.hex.pm/preview/ash_csv/0.9.7) Search HexDocs [Download ePub version](ash_csv.epub "ePub version") 37 | 38 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 39 | [![ash_csv](assets/logo.png)](https://github.com/ash-project/ash_csv) 40 | 41 | [ash\_csv](https://github.com/ash-project/ash_csv) 42 | 43 | v0.9.7 44 | 45 | - Pages 46 | - Modules 47 | 48 | 49 | 50 | 51 | 52 | Search documentation of ash\_csv 53 | 54 | Settings 55 | 56 | # [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L1 "View Source") AshCsv.DataLayer (ash\_csv v0.9.7) 57 | 58 | The data layer implementation for AshCsv 59 | 60 | # [](AshCsv.DataLayer.html#summary)Summary 61 | 62 | ## [Functions](AshCsv.DataLayer.html#functions) 63 | 64 | [columns(resource)](AshCsv.DataLayer.html#columns/1) deprecated 65 | 66 | See [`AshCsv.DataLayer.Info.columns/1`](AshCsv.DataLayer.Info.html#columns/1). 67 | 68 | [create?(resource)](AshCsv.DataLayer.html#create?/1) deprecated 69 | 70 | See [`AshCsv.DataLayer.Info.create?/1`](AshCsv.DataLayer.Info.html#create?/1). 71 | 72 | [file(resource)](AshCsv.DataLayer.html#file/1) deprecated 73 | 74 | See [`AshCsv.DataLayer.Info.file/1`](AshCsv.DataLayer.Info.html#file/1). 75 | 76 | [filter\_matches(records, filter, domain)](AshCsv.DataLayer.html#filter_matches/3) 77 | 78 | [header?(resource)](AshCsv.DataLayer.html#header?/1) deprecated 79 | 80 | See [`AshCsv.DataLayer.Info.header?/1`](AshCsv.DataLayer.Info.html#header?/1). 81 | 82 | [separator(resource)](AshCsv.DataLayer.html#separator/1) deprecated 83 | 84 | See [`AshCsv.DataLayer.Info.separator/1`](AshCsv.DataLayer.Info.html#separator/1). 85 | 86 | # [](AshCsv.DataLayer.html#functions)Functions 87 | 88 | [Link to this function](AshCsv.DataLayer.html#columns/1 "Link to this function") 89 | 90 | # columns(resource) 91 | 92 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L73 "View Source") 93 | 94 | This function is deprecated. See \`AshCsv.DataLayer.Info.columns/1. 95 | 96 | See [`AshCsv.DataLayer.Info.columns/1`](AshCsv.DataLayer.Info.html#columns/1). 97 | 98 | [Link to this function](AshCsv.DataLayer.html#create?/1 "Link to this function") 99 | 100 | # create?(resource) 101 | 102 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L82 "View Source") 103 | 104 | This function is deprecated. See \`AshCsv.DataLayer.Info.create?/1. 105 | 106 | See [`AshCsv.DataLayer.Info.create?/1`](AshCsv.DataLayer.Info.html#create?/1). 107 | 108 | [Link to this function](AshCsv.DataLayer.html#file/1 "Link to this function") 109 | 110 | # file(resource) 111 | 112 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L70 "View Source") 113 | 114 | This function is deprecated. See \`AshCsv.DataLayer.Info.file/1. 115 | 116 | See [`AshCsv.DataLayer.Info.file/1`](AshCsv.DataLayer.Info.html#file/1). 117 | 118 | [Link to this function](AshCsv.DataLayer.html#filter_matches/3 "Link to this function") 119 | 120 | # filter\_matches(records, filter, domain) 121 | 122 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L397 "View Source") 123 | 124 | [Link to this function](AshCsv.DataLayer.html#header?/1 "Link to this function") 125 | 126 | # header?(resource) 127 | 128 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L79 "View Source") 129 | 130 | This function is deprecated. See \`AshCsv.DataLayer.Info.header?/1. 131 | 132 | See [`AshCsv.DataLayer.Info.header?/1`](AshCsv.DataLayer.Info.html#header?/1). 133 | 134 | [Link to this function](AshCsv.DataLayer.html#separator/1 "Link to this function") 135 | 136 | # separator(resource) 137 | 138 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/data_layer.ex#L76 "View Source") 139 | 140 | This function is deprecated. See \`AshCsv.DataLayer.Info.separator/1. 141 | 142 | See [`AshCsv.DataLayer.Info.separator/1`](AshCsv.DataLayer.Info.html#separator/1). 143 | 144 | [Hex Package](https://hex.pm/packages/ash_csv/0.9.7) [Hex Preview](https://preview.hex.pm/preview/ash_csv/0.9.7) Search HexDocs [Download ePub version](ash_csv.epub "ePub version") 145 | 146 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 147 | [![ash_csv](assets/logo.png)](https://github.com/ash-project/ash_csv) 148 | 149 | [ash\_csv](https://github.com/ash-project/ash_csv) 150 | 151 | v0.9.7 152 | 153 | - Pages 154 | - Modules 155 | 156 | 157 | 158 | 159 | 160 | Search documentation of ash\_csv 161 | 162 | Settings 163 | 164 | # [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L1 "View Source") AshCsv.DataLayer.Info (ash\_csv v0.9.7) 165 | 166 | Introspection helpers for AshCsv.DataLayer 167 | 168 | # [](AshCsv.DataLayer.Info.html#summary)Summary 169 | 170 | ## [Functions](AshCsv.DataLayer.Info.html#functions) 171 | 172 | [columns(resource)](AshCsv.DataLayer.Info.html#columns/1) 173 | 174 | [create?(resource)](AshCsv.DataLayer.Info.html#create?/1) 175 | 176 | [file(resource)](AshCsv.DataLayer.Info.html#file/1) 177 | 178 | [header?(resource)](AshCsv.DataLayer.Info.html#header?/1) 179 | 180 | [separator(resource)](AshCsv.DataLayer.Info.html#separator/1) 181 | 182 | # [](AshCsv.DataLayer.Info.html#functions)Functions 183 | 184 | [Link to this function](AshCsv.DataLayer.Info.html#columns/1 "Link to this function") 185 | 186 | # columns(resource) 187 | 188 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L12 "View Source") 189 | 190 | [Link to this function](AshCsv.DataLayer.Info.html#create?/1 "Link to this function") 191 | 192 | # create?(resource) 193 | 194 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L24 "View Source") 195 | 196 | [Link to this function](AshCsv.DataLayer.Info.html#file/1 "Link to this function") 197 | 198 | # file(resource) 199 | 200 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L6 "View Source") 201 | 202 | [Link to this function](AshCsv.DataLayer.Info.html#header?/1 "Link to this function") 203 | 204 | # header?(resource) 205 | 206 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L20 "View Source") 207 | 208 | [Link to this function](AshCsv.DataLayer.Info.html#separator/1 "Link to this function") 209 | 210 | # separator(resource) 211 | 212 | [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv/info.ex#L16 "View Source") 213 | 214 | [Hex Package](https://hex.pm/packages/ash_csv/0.9.7) [Hex Preview](https://preview.hex.pm/preview/ash_csv/0.9.7) Search HexDocs [Download ePub version](ash_csv.epub "ePub version") 215 | 216 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 217 | [![ash_csv](assets/logo.png)](https://github.com/ash-project/ash_csv) 218 | 219 | [ash\_csv](https://github.com/ash-project/ash_csv) 220 | 221 | v0.9.7 222 | 223 | - Pages 224 | - Modules 225 | 226 | 227 | 228 | 229 | 230 | Search documentation of ash\_csv 231 | 232 | Settings 233 | 234 | # [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/lib/ash_csv.ex#L1 "View Source") AshCsv (ash\_csv v0.9.7) 235 | 236 | A CSV datalayer for the Ash framework 237 | 238 | For DSL documentation, see [`AshCsv.DataLayer`](AshCsv.DataLayer.html) 239 | 240 | [Hex Package](https://hex.pm/packages/ash_csv/0.9.7) [Hex Preview](https://preview.hex.pm/preview/ash_csv/0.9.7) Search HexDocs [Download ePub version](ash_csv.epub "ePub version") 241 | 242 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 243 | [![ash_csv](assets/logo.png)](https://github.com/ash-project/ash_csv) 244 | 245 | [ash\_csv](https://github.com/ash-project/ash_csv) 246 | 247 | v0.9.7 248 | 249 | - Pages 250 | - Modules 251 | 252 | 253 | 254 | 255 | 256 | Search documentation of ash\_csv 257 | 258 | Settings 259 | 260 | # [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/documentation/dsls/DSL:-AshCsv.DataLayer.md#L1 "View Source") DSL: AshCsv.DataLayer 261 | 262 | The data layer implementation for AshCsv 263 | 264 | ## [](dsl-ashcsv-datalayer.html#csv)csv 265 | 266 | ### [](dsl-ashcsv-datalayer.html#examples)Examples 267 | 268 | ``` 269 | csv do 270 | file "priv/data/tags.csv" 271 | create? true 272 | header? true 273 | separator '-' 274 | columns [:id, :name] 275 | end 276 | ``` 277 | 278 | ### [](dsl-ashcsv-datalayer.html#options)Options 279 | 280 | NameTypeDefaultDocs[`file`](dsl-ashcsv-datalayer.html#csv-file)`String.t`The file to read the data from[`create?`](dsl-ashcsv-datalayer.html#csv-create?)`boolean``false`Whether or not the file should be created if it does not exist (this will only happen on writes)[`header?`](dsl-ashcsv-datalayer.html#csv-header?)`boolean``false`If the csv file has a header that should be skipped[`separator`](dsl-ashcsv-datalayer.html#csv-separator)`any``44`The separator to use, defaults to a comma. Pass in a character (not a string).[`columns`](dsl-ashcsv-datalayer.html#csv-columns)`any`The order that the attributes appear in the columns of the CSV 281 | 282 | [← Previous Page Getting Started with CSV](getting-started-with-ash-csv.html) 283 | 284 | [Next Page → Change Log](changelog.html) 285 | 286 | [Hex Package](https://hex.pm/packages/ash_csv/0.9.7) [Hex Preview](https://preview.hex.pm/preview/ash_csv/0.9.7) ([current file](https://preview.hex.pm/preview/ash_csv/0.9.7/show/documentation/dsls/DSL:-AshCsv.DataLayer.md)) Search HexDocs [Download ePub version](ash_csv.epub "ePub version") 287 | 288 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 289 | [![ash_csv](assets/logo.png)](https://github.com/ash-project/ash_csv) 290 | 291 | [ash\_csv](https://github.com/ash-project/ash_csv) 292 | 293 | v0.9.7 294 | 295 | - Pages 296 | - Modules 297 | 298 | 299 | 300 | 301 | 302 | Search documentation of ash\_csv 303 | 304 | Settings 305 | 306 | # [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/documentation/tutorials/getting-started-with-ash-csv.md#L1 "View Source") Getting Started with CSV 307 | 308 | AshCsv offers basic support for storing and reading resources from csv files. 309 | 310 | ## [](getting-started-with-ash-csv.html#installation)Installation 311 | 312 | Add `ash_csv` to your list of dependencies in `mix.exs`: 313 | 314 | ``` 315 | {:ash_csv, "~> 0.9.7-rc.0"} 316 | ``` 317 | 318 | For information on how to configure it, see the [DSL documentation.](dsl-ashcsv-datalayer.html) 319 | 320 | [← Previous Page Home](readme.html) 321 | 322 | [Next Page → DSL: AshCsv.DataLayer](dsl-ashcsv-datalayer.html) 323 | 324 | [Hex Package](https://hex.pm/packages/ash_csv/0.9.7) [Hex Preview](https://preview.hex.pm/preview/ash_csv/0.9.7) ([current file](https://preview.hex.pm/preview/ash_csv/0.9.7/show/documentation/tutorials/getting-started-with-ash-csv.md)) Search HexDocs [Download ePub version](ash_csv.epub "ePub version") 325 | 326 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 327 | [![ash_csv](assets/logo.png)](https://github.com/ash-project/ash_csv) 328 | 329 | [ash\_csv](https://github.com/ash-project/ash_csv) 330 | 331 | v0.9.7 332 | 333 | - Pages 334 | - Modules 335 | 336 | 337 | 338 | 339 | 340 | Search documentation of ash\_csv 341 | 342 | Settings 343 | 344 | # [View Source](https://github.com/ash-project/ash_csv/blob/v0.9.7/README.md#L1 "View Source") Home 345 | 346 | ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-black-text.png?raw=true#gh-light-mode-only) ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-white-text.png?raw=true#gh-dark-mode-only) 347 | 348 | ![Elixir CI](https://github.com/ash-project/ash_csv/workflows/CI/badge.svg) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Hex version badge](https://img.shields.io/hexpm/v/ash_csv.svg)](https://hex.pm/packages/ash_csv) [![Hexdocs badge](https://img.shields.io/badge/docs-hexdocs-purple)](../ash_csv.html) 349 | 350 | # AshCsv 351 | 352 | Welcome! This is the CSV Data Layer for [Ash Framework](../ash.html). 353 | 354 | ## [](readme.html#tutorials)Tutorials 355 | 356 | - [Getting Started with AshCsv](getting-started-with-ash-csv.html) 357 | 358 | ## [](readme.html#reference)Reference 359 | 360 | - [AshCsv.DataLayer DSL](dsl-ashcsv-datalayer.html) 361 | 362 | [← Previous Page API Reference](api-reference.html) 363 | 364 | [Next Page → Getting Started with CSV](getting-started-with-ash-csv.html) 365 | 366 | [Hex Package](https://hex.pm/packages/ash_csv/0.9.7) [Hex Preview](https://preview.hex.pm/preview/ash_csv/0.9.7) ([current file](https://preview.hex.pm/preview/ash_csv/0.9.7/show/README.md)) Search HexDocs [Download ePub version](ash_csv.epub "ePub version") 367 | 368 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 369 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bun 2 | import { Server } from '@modelcontextprotocol/sdk/server/index.js'; 3 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; 4 | import { ListToolsRequestSchema, CallToolRequestSchema, Tool } from '@modelcontextprotocol/sdk/types.js'; 5 | import { GoogleAIFileManager } from '@google/generative-ai/server'; 6 | import { z } from 'zod'; 7 | import { readdirSync, readFileSync } from 'fs'; 8 | import { join, resolve } from 'path'; 9 | import { GeminiAPI } from './gemini'; 10 | import { GeminiDocsDB } from './db'; 11 | import { Logger } from './logger'; 12 | import type { CanXBeDoneParams, HowToDoXParams, HintsForProblemParams, IsThisGoodPracticeParams, GeminiFileData } from './types'; 13 | 14 | // Tool names as constants to ensure consistency 15 | const TOOL_NAMES = { 16 | CAN_X_BE_DONE: 'can_x_be_done', 17 | HOW_TO_DO_X: 'how_to_do_x', 18 | HINTS_FOR_PROBLEM: 'hints_for_problem', 19 | IS_THIS_GOOD_PRACTICE: 'is_this_good_practice' 20 | } as const; 21 | 22 | const canXBeDoneSchema = z.object({ 23 | docs: z.string().describe('Path to documentation directory'), 24 | prompt: z.string().describe('Path to prompt file'), 25 | x: z.string().describe('The task to check'), 26 | technology: z.string().describe('The technology (programming language/framework/tool)'), 27 | }); 28 | 29 | const howToDoXSchema = z.object({ 30 | docs: z.string().describe('Path to documentation directory'), 31 | prompt: z.string().describe('Path to prompt file'), 32 | x: z.string().describe('The task to implement'), 33 | technology: z.string().describe('The technology (programming language/framework/tool)'), 34 | }); 35 | 36 | const hintsForProblemSchema = z.object({ 37 | docs: z.string().describe('Path to documentation directory'), 38 | prompt: z.string().describe('Path to prompt file'), 39 | problem: z.string().describe('The problem description'), 40 | context: z.string().describe('The context of the problem'), 41 | environment: z.record(z.unknown()).describe('Environment information'), 42 | }); 43 | 44 | const isThisGoodPracticeSchema = z.object({ 45 | docs: z.string().describe('Path to documentation directory'), 46 | prompt: z.string().describe('Path to prompt file'), 47 | snippet: z.string().describe('The code snippet to check'), 48 | context: z.string().describe('The context in which the code is used'), 49 | }); 50 | 51 | // Define Tool objects following the MCP specification 52 | const TOOLS: Tool[] = [ 53 | { 54 | name: TOOL_NAMES.CAN_X_BE_DONE, 55 | description: 'Check if a specific task can be done in a given language/framework/tool', 56 | inputSchema: { 57 | type: 'object', 58 | properties: { 59 | docs: { type: 'string', description: 'Path to documentation directory' }, 60 | prompt: { type: 'string', description: 'Path to prompt file' }, 61 | x: { type: 'string', description: 'The task to check' }, 62 | technology: { type: 'string', description: 'The technology (programming language/framework/tool)' }, 63 | }, 64 | required: ['docs', 'x', 'technology'], 65 | }, 66 | }, 67 | { 68 | name: TOOL_NAMES.HOW_TO_DO_X, 69 | description: 'Get examples and alternative approaches for a specific task', 70 | inputSchema: { 71 | type: 'object', 72 | properties: { 73 | docs: { type: 'string', description: 'Path to documentation directory' }, 74 | prompt: { type: 'string', description: 'Path to prompt file' }, 75 | x: { type: 'string', description: 'The task to implement' }, 76 | technology: { type: 'string', description: 'The technology (programming language/framework/tool)' }, 77 | }, 78 | required: ['docs', 'x', 'technology'], 79 | }, 80 | }, 81 | { 82 | name: TOOL_NAMES.HINTS_FOR_PROBLEM, 83 | description: 'Get hints for solving a specific problem', 84 | inputSchema: { 85 | type: 'object', 86 | properties: { 87 | docs: { type: 'string', description: 'Path to documentation directory' }, 88 | prompt: { type: 'string', description: 'Path to prompt file' }, 89 | problem: { type: 'string', description: 'The problem description' }, 90 | context: { type: 'string', description: 'The context of the problem' }, 91 | environment: { type: 'object', description: 'Environment information' }, 92 | }, 93 | required: ['docs', 'problem', 'context'], 94 | }, 95 | }, 96 | { 97 | name: TOOL_NAMES.IS_THIS_GOOD_PRACTICE, 98 | description: 'Check if a code snippet follows good practices', 99 | inputSchema: { 100 | type: 'object', 101 | properties: { 102 | docs: { type: 'string', description: 'Path to documentation directory' }, 103 | prompt: { type: 'string', description: 'Path to prompt file' }, 104 | snippet: { type: 'string', description: 'The code snippet to check' }, 105 | context: { type: 'string', description: 'The context in which the code is used' }, 106 | }, 107 | required: ['docs', 'snippet', 'context'], 108 | }, 109 | } 110 | ]; 111 | 112 | class GeminiDocsServer { 113 | private server: Server; 114 | private gemini: GeminiAPI; 115 | private db: GeminiDocsDB; 116 | private fileManager: GoogleAIFileManager; 117 | 118 | constructor() { 119 | if (!process.env.GEMINI_API_KEY) { 120 | throw new Error('GEMINI_API_KEY environment variable is required'); 121 | } 122 | 123 | const toolsEnabled = TOOLS.reduce((acc, tool) => { 124 | acc[tool.name] = true; 125 | return acc; 126 | }, {} as Record); 127 | 128 | this.server = new Server( 129 | { 130 | name: 'gemini-docs', 131 | version: '0.1.0', 132 | }, 133 | { 134 | capabilities: { 135 | tools: toolsEnabled, 136 | }, 137 | } 138 | ); 139 | 140 | try { 141 | this.db = new GeminiDocsDB(); 142 | this.fileManager = new GoogleAIFileManager(process.env.GEMINI_API_KEY); 143 | this.gemini = new GeminiAPI(); 144 | } catch (error) { 145 | console.error('[Server] Failed to initialize:', error); 146 | Logger.log(`[Server] Failed to initialize: ${error}`); 147 | throw error; 148 | } 149 | 150 | this.setupTools(); 151 | this.setupErrorHandler(); 152 | } 153 | 154 | private setupErrorHandler() { 155 | this.server.onerror = (error) => { 156 | console.error('[MCP Error]', error); 157 | }; 158 | } 159 | 160 | private generateFileId(content: string): string { 161 | const hash = Bun.hash(content).toString(16); 162 | return hash; 163 | } 164 | 165 | private async uploadFileWithRetry( 166 | fullPath: string, 167 | displayName: string, 168 | maxRetries: number = 3, 169 | retryDelay: number = 1000 170 | ): Promise<{ uri: string }> { 171 | let lastError: Error; 172 | 173 | for (let attempt = 1; attempt <= maxRetries; attempt++) { 174 | try { 175 | const result = await this.fileManager.uploadFile(fullPath, { 176 | mimeType: 'text/markdown', 177 | displayName 178 | }); 179 | return { uri: result.file.uri }; 180 | } catch (error: any) { 181 | lastError = error; 182 | if (attempt < maxRetries) { 183 | Logger.log(`[FileURIDebug] Upload attempt ${attempt} failed for ${fullPath}, retrying in ${retryDelay}ms...`); 184 | await new Promise(resolve => setTimeout(resolve, retryDelay * attempt)); 185 | } 186 | } 187 | } 188 | throw lastError!; 189 | } 190 | 191 | private async processFileBatch( 192 | absPath: string, 193 | files: string[], 194 | batchSize: number = 10, 195 | stats: { cached: number; uploaded: number; } = { cached: 0, uploaded: 0 } 196 | ): Promise { 197 | const docRefs: GeminiFileData[] = []; 198 | const batches = []; 199 | 200 | const logProgress = () => { 201 | Logger.log(`[FileURIDebug] Progress Report: 202 | Total files: ${files.length} 203 | Processed: ${stats.cached + stats.uploaded} 204 | Cached: ${stats.cached} 205 | Uploaded: ${stats.uploaded} 206 | `); 207 | }; 208 | 209 | // Split files into batches 210 | for (let i = 0; i < files.length; i += batchSize) { 211 | batches.push(files.slice(i, i + batchSize)); 212 | } 213 | 214 | Logger.log(`[FileURIDebug] Processing ${files.length} files in ${batches.length} batches of ${batchSize}`); 215 | 216 | // Process batches sequentially to avoid overwhelming the API 217 | for (const batch of batches) { 218 | Logger.log(`[FileURIDebug] Processing batch of ${batch.length} files...`); 219 | const batchResults = await Promise.all( 220 | batch.map(async (file) => { 221 | const fullPath = join(absPath, file); 222 | 223 | try { 224 | // Calculate content hash first 225 | const content = readFileSync(fullPath, 'utf-8'); 226 | const fileHash = this.generateFileId(content); 227 | 228 | // Check if we have this content's URI 229 | const existingFile = await this.db.getFile(fileHash); 230 | 231 | if (existingFile) { 232 | Logger.log(`[FileURIDebug] Found existing content (hash: ${fileHash}) for ${fullPath}`); 233 | stats.cached++; 234 | logProgress(); 235 | return { 236 | fileUri: existingFile.fileUri 237 | }; 238 | } 239 | 240 | // Upload new file to Gemini 241 | Logger.log(`[FileURIDebug] Uploading file ${fullPath} (${file})`); 242 | 243 | const uploadResult = await this.uploadFileWithRetry(fullPath, file); 244 | 245 | // Store the Gemini file URI 246 | await this.db.saveFileId(fullPath, fileHash, uploadResult.uri); 247 | Logger.log(`[FileURIDebug] Successfully uploaded and saved ${fullPath}`); 248 | stats.uploaded++; 249 | logProgress(); 250 | return { fileUri: uploadResult.uri }; 251 | } catch (error) { 252 | Logger.log(`[FileURIDebug] Error processing file ${fullPath}: ${error}`); 253 | throw error; 254 | } 255 | }) 256 | ); 257 | docRefs.push(...batchResults); 258 | } 259 | return docRefs; 260 | } 261 | 262 | private async readDocs(dirPath: string): Promise { 263 | try { 264 | const absPath = resolve(dirPath); 265 | const files = readdirSync(absPath, { recursive: true }) as string[]; 266 | const mdFiles = files.filter(file => file.endsWith('.md')); 267 | Logger.log(`[FileURIDebug] Found ${mdFiles.length} markdown files to process`); 268 | 269 | // Track cached vs uploaded files 270 | const stats = { cached: 0, uploaded: 0 }; 271 | const docRefs = await this.processFileBatch(absPath, mdFiles, 10, stats); 272 | 273 | Logger.log(`[FileURIDebug] Processing completed: 274 | ${stats.cached} files loaded from cache 275 | ${stats.uploaded} files uploaded to Gemini`); 276 | return docRefs; 277 | } catch (error) { 278 | console.error('[Server] Error reading docs'); 279 | Logger.log(`[FileURIDebug] Error reading docs: ${error}`); 280 | throw error; 281 | } 282 | } 283 | 284 | private setupTools() { 285 | // List available tools 286 | this.server.setRequestHandler(ListToolsRequestSchema, async () => ({ 287 | tools: TOOLS, 288 | })); 289 | 290 | // Handle tool calls 291 | this.server.setRequestHandler(CallToolRequestSchema, async (request) => { 292 | const { name, arguments: args } = request.params; 293 | 294 | switch (name) { 295 | case TOOL_NAMES.CAN_X_BE_DONE: { 296 | const params = canXBeDoneSchema.parse(args) as CanXBeDoneParams; 297 | const docs = await this.readDocs(params.docs); 298 | const response = await this.gemini.canXBeDone( 299 | docs, 300 | params.x, 301 | params.technology, 302 | params.prompt 303 | ); 304 | return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] }; 305 | } 306 | case TOOL_NAMES.HOW_TO_DO_X: { 307 | const params = howToDoXSchema.parse(args) as HowToDoXParams; 308 | const docs = await this.readDocs(params.docs); 309 | const response = await this.gemini.howToDoX( 310 | docs, 311 | params.x, 312 | params.technology, 313 | params.prompt 314 | ); 315 | return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] }; 316 | } 317 | case TOOL_NAMES.HINTS_FOR_PROBLEM: { 318 | const params = hintsForProblemSchema.parse(args) as HintsForProblemParams; 319 | const docs = await this.readDocs(params.docs); 320 | const response = await this.gemini.hintsForProblem( 321 | docs, 322 | params.problem, 323 | params.context, 324 | params.environment, 325 | params.prompt 326 | ); 327 | return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] }; 328 | } 329 | case TOOL_NAMES.IS_THIS_GOOD_PRACTICE: { 330 | const params = isThisGoodPracticeSchema.parse(args) as IsThisGoodPracticeParams; 331 | const docs = await this.readDocs(params.docs); 332 | const response = await this.gemini.isThisGoodPractice( 333 | docs, 334 | params.snippet, 335 | params.context, 336 | params.prompt 337 | ); 338 | return { content: [{ type: 'text', text: JSON.stringify(response, null, 2) }] }; 339 | } 340 | default: 341 | throw new Error(`Unknown tool: ${name}`); 342 | } 343 | }); 344 | } 345 | 346 | async start() { 347 | const transport = new StdioServerTransport(); 348 | await this.server.connect(transport); 349 | } 350 | 351 | async stop() { 352 | await this.server.close(); 353 | this.db.close(); 354 | Logger.close(); 355 | } 356 | } 357 | 358 | // Handle graceful shutdown 359 | const server = new GeminiDocsServer(); 360 | 361 | // Parse command line arguments 362 | const args = process.argv.slice(2); 363 | if (args.includes('--verbose')) { 364 | Logger.enable(); 365 | } 366 | 367 | process.on('SIGINT', async () => { 368 | await server.stop(); 369 | process.exit(0); 370 | }); 371 | 372 | server.start().catch(error => { 373 | console.error('Failed to start server:', error); 374 | process.exit(1); 375 | }); -------------------------------------------------------------------------------- /bun.lock: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1, 3 | "workspaces": { 4 | "": { 5 | "name": "gemini-docs-mcp", 6 | "dependencies": { 7 | "@google/generative-ai": "^0.22.0", 8 | "@modelcontextprotocol/sdk": "^1.6.0", 9 | "js-yaml": "^4.1.0", 10 | "zod": "^3.24.2", 11 | }, 12 | "devDependencies": { 13 | "@types/bun": "latest", 14 | "@types/js-yaml": "^4.0.9", 15 | }, 16 | "peerDependencies": { 17 | "typescript": "^5", 18 | }, 19 | }, 20 | }, 21 | "packages": { 22 | "@google/generative-ai": ["@google/generative-ai@0.22.0", "", {}, "sha512-mLR3PDWCk5O/BWNyDvFDIiwKeXQmFGZ+kJFd9m73QrUPCFREttJyVbBPTW4y9CwTbaltLMDaLDfroCrRv5Bl8Q=="], 23 | 24 | "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.6.0", "", { "dependencies": { "content-type": "^1.0.5", "cors": "^2.8.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^4.1.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-585s8g+jzuGBomzgzDeP5l8gEyiSs+KhoAHbA2ZZ24Zgm83IZsyCLl/fmWhPHbfYsuLG8NE6SWGZA5ZBql8jSw=="], 25 | 26 | "@types/bun": ["@types/bun@1.2.3", "", { "dependencies": { "bun-types": "1.2.3" } }, "sha512-054h79ipETRfjtsCW9qJK8Ipof67Pw9bodFWmkfkaUaRiIQ1dIV2VTlheshlBx3mpKr0KeK8VqnMMCtgN9rQtw=="], 27 | 28 | "@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="], 29 | 30 | "@types/node": ["@types/node@22.13.5", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg=="], 31 | 32 | "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], 33 | 34 | "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], 35 | 36 | "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], 37 | 38 | "body-parser": ["body-parser@2.1.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.5.2", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-/hPxh61E+ll0Ujp24Ilm64cykicul1ypfwjVttduAiEdtnJFvLePSrIPk+HMImtNv5270wOGCb1Tns2rybMkoQ=="], 39 | 40 | "bun-types": ["bun-types@1.2.3", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-P7AeyTseLKAvgaZqQrvp3RqFM3yN9PlcLuSTe7SoJOfZkER73mLdT2vEQi8U64S1YvM/ldcNiQjn0Sn7H9lGgg=="], 41 | 42 | "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], 43 | 44 | "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], 45 | 46 | "call-bound": ["call-bound@1.0.3", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "get-intrinsic": "^1.2.6" } }, "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA=="], 47 | 48 | "content-disposition": ["content-disposition@1.0.0", "", { "dependencies": { "safe-buffer": "5.2.1" } }, "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg=="], 49 | 50 | "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], 51 | 52 | "cookie": ["cookie@0.7.1", "", {}, "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w=="], 53 | 54 | "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], 55 | 56 | "cors": ["cors@2.8.5", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g=="], 57 | 58 | "debug": ["debug@4.3.6", "", { "dependencies": { "ms": "2.1.2" } }, "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg=="], 59 | 60 | "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], 61 | 62 | "destroy": ["destroy@1.2.0", "", {}, "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg=="], 63 | 64 | "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], 65 | 66 | "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], 67 | 68 | "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], 69 | 70 | "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], 71 | 72 | "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], 73 | 74 | "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], 75 | 76 | "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], 77 | 78 | "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], 79 | 80 | "eventsource": ["eventsource@3.0.5", "", { "dependencies": { "eventsource-parser": "^3.0.0" } }, "sha512-LT/5J605bx5SNyE+ITBDiM3FxffBiq9un7Vx0EwMDM3vg8sWKx/tO2zC+LMqZ+smAM0F2hblaDZUVZF0te2pSw=="], 81 | 82 | "eventsource-parser": ["eventsource-parser@3.0.0", "", {}, "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA=="], 83 | 84 | "express": ["express@5.0.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.0.1", "content-disposition": "^1.0.0", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "^1.2.1", "debug": "4.3.6", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "^2.0.0", "fresh": "2.0.0", "http-errors": "2.0.0", "merge-descriptors": "^2.0.0", "methods": "~1.1.2", "mime-types": "^3.0.0", "on-finished": "2.4.1", "once": "1.4.0", "parseurl": "~1.3.3", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "router": "^2.0.0", "safe-buffer": "5.2.1", "send": "^1.1.0", "serve-static": "^2.1.0", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "^2.0.0", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-ORF7g6qGnD+YtUG9yx4DFoqCShNMmUKiXuT5oWMHiOvt/4WFbHC6yCwQMTSBMno7AqntNCAzzcnnjowRkTL9eQ=="], 85 | 86 | "express-rate-limit": ["express-rate-limit@7.5.0", "", { "peerDependencies": { "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg=="], 87 | 88 | "finalhandler": ["finalhandler@2.0.0", "", { "dependencies": { "debug": "2.6.9", "encodeurl": "~1.0.2", "escape-html": "~1.0.3", "on-finished": "2.4.1", "parseurl": "~1.3.3", "statuses": "2.0.1", "unpipe": "~1.0.0" } }, "sha512-MX6Zo2adDViYh+GcxxB1dpO43eypOGUOL12rLCOTMQv/DfIbpSJUy4oQIIZhVZkH9e+bZWKMon0XHFEju16tkQ=="], 89 | 90 | "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], 91 | 92 | "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], 93 | 94 | "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], 95 | 96 | "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], 97 | 98 | "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], 99 | 100 | "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], 101 | 102 | "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], 103 | 104 | "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], 105 | 106 | "http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="], 107 | 108 | "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], 109 | 110 | "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], 111 | 112 | "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], 113 | 114 | "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], 115 | 116 | "js-yaml": ["js-yaml@4.1.0", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA=="], 117 | 118 | "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], 119 | 120 | "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], 121 | 122 | "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], 123 | 124 | "methods": ["methods@1.1.2", "", {}, "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w=="], 125 | 126 | "mime-db": ["mime-db@1.53.0", "", {}, "sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg=="], 127 | 128 | "mime-types": ["mime-types@3.0.0", "", { "dependencies": { "mime-db": "^1.53.0" } }, "sha512-XqoSHeCGjVClAmoGFG3lVFqQFRIrTVw2OH3axRqAcfaw+gHWIfnASS92AV+Rl/mk0MupgZTRHQOjxY6YVnzK5w=="], 129 | 130 | "ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="], 131 | 132 | "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], 133 | 134 | "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], 135 | 136 | "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], 137 | 138 | "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], 139 | 140 | "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], 141 | 142 | "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], 143 | 144 | "path-to-regexp": ["path-to-regexp@8.2.0", "", {}, "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ=="], 145 | 146 | "pkce-challenge": ["pkce-challenge@4.1.0", "", {}, "sha512-ZBmhE1C9LcPoH9XZSdwiPtbPHZROwAnMy+kIFQVrnMCxY4Cudlz3gBOpzilgc0jOgRaiT3sIWfpMomW2ar2orQ=="], 147 | 148 | "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], 149 | 150 | "qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="], 151 | 152 | "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], 153 | 154 | "raw-body": ["raw-body@3.0.0", "", { "dependencies": { "bytes": "3.1.2", "http-errors": "2.0.0", "iconv-lite": "0.6.3", "unpipe": "1.0.0" } }, "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g=="], 155 | 156 | "router": ["router@2.1.0", "", { "dependencies": { "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-/m/NSLxeYEgWNtyC+WtNHCF7jbGxOibVWKnn+1Psff4dJGOfoXP+MuC/f2CwSmyiHdOIzYnYFp4W6GxWfekaLA=="], 157 | 158 | "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], 159 | 160 | "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], 161 | 162 | "send": ["send@1.1.0", "", { "dependencies": { "debug": "^4.3.5", "destroy": "^1.2.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^0.5.2", "http-errors": "^2.0.0", "mime-types": "^2.1.35", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-v67WcEouB5GxbTWL/4NeToqcZiAWEq90N888fczVArY8A79J0L4FD7vj5hm3eUMua5EpoQ59wa/oovY6TLvRUA=="], 163 | 164 | "serve-static": ["serve-static@2.1.0", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.0.0" } }, "sha512-A3We5UfEjG8Z7VkDv6uItWw6HY2bBSBJT1KtVESn6EOoOr2jAxNhxWCLY3jDE2WcuHXByWju74ck3ZgLwL8xmA=="], 165 | 166 | "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], 167 | 168 | "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], 169 | 170 | "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], 171 | 172 | "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], 173 | 174 | "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], 175 | 176 | "statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="], 177 | 178 | "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], 179 | 180 | "type-is": ["type-is@2.0.0", "", { "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-gd0sGezQYCbWSbkZr75mln4YBidWUN60+devscpLF5mtRDUpiaTvKpBNrdaCvel1NdR2k6vclXybU5fBd2i+nw=="], 181 | 182 | "typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="], 183 | 184 | "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], 185 | 186 | "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], 187 | 188 | "utils-merge": ["utils-merge@1.0.1", "", {}, "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA=="], 189 | 190 | "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], 191 | 192 | "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], 193 | 194 | "zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="], 195 | 196 | "zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="], 197 | 198 | "body-parser/debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], 199 | 200 | "body-parser/iconv-lite": ["iconv-lite@0.5.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3" } }, "sha512-kERHXvpSaB4aU3eANwidg79K8FlrN77m8G9V+0vOR3HYaRifrlwMEpT7ZBJqLSEIHnEgJTHcWK82wwLwwKwtag=="], 201 | 202 | "body-parser/qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], 203 | 204 | "finalhandler/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], 205 | 206 | "finalhandler/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="], 207 | 208 | "send/fresh": ["fresh@0.5.2", "", {}, "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q=="], 209 | 210 | "send/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], 211 | 212 | "send/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 213 | 214 | "body-parser/debug/ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], 215 | 216 | "finalhandler/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], 217 | 218 | "send/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /test/docs/ash-docs/ash_rbac.md: -------------------------------------------------------------------------------- 1 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac) 2 | 3 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac) 4 | 5 | v0.6.1 6 | 7 | - Pages 8 | - Modules 9 | 10 | 11 | 12 | 13 | 14 | Search documentation of ash\_rbac 15 | 16 | Settings 17 | 18 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac "View Source") API Reference ash\_rbac v0.6.1 19 | 20 | ## [](api-reference.html#modules)Modules 21 | 22 | [AshRbac](AshRbac.html) 23 | 24 | [AshRbac.Actions](AshRbac.Actions.html) 25 | 26 | Adds the policies for actions to the dsl\_state 27 | 28 | [AshRbac.Fields](AshRbac.Fields.html) 29 | 30 | Adds the policies for fields to the dsl\_state 31 | 32 | [AshRbac.HasRole](AshRbac.HasRole.html) 33 | 34 | Check to determine if the actor has a specific role or if the actor has any of the roles in a list 35 | 36 | [AshRbac.Info](AshRbac.Info.html) 37 | 38 | Introspection functions for the Rbac Extension 39 | 40 | [AshRbac.Policies](AshRbac.Policies.html) 41 | 42 | Adds the configured policies to the resource 43 | 44 | [AshRbac.Role](AshRbac.Role.html) 45 | 46 | The Role entity for the DSL of the rbac extension 47 | 48 | [Next Page → Getting Started](getting_started.html) 49 | 50 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version") 51 | 52 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 53 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac) 54 | 55 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac) 56 | 57 | v0.6.1 58 | 59 | - Pages 60 | - Modules 61 | 62 | 63 | 64 | 65 | 66 | Search documentation of ash\_rbac 67 | 68 | Settings 69 | 70 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/actions.ex#L1 "View Source") AshRbac.Actions (ash\_rbac v0.6.1) 71 | 72 | Adds the policies for actions to the dsl\_state 73 | 74 | # [](AshRbac.Actions.html#summary)Summary 75 | 76 | ## [Functions](AshRbac.Actions.html#functions) 77 | 78 | [after?(\_)](AshRbac.Actions.html#after?/1) 79 | 80 | Callback implementation for [`Spark.Dsl.Transformer.after?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after?/1). 81 | 82 | [after\_compile?()](AshRbac.Actions.html#after_compile?/0) 83 | 84 | Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0). 85 | 86 | [before?(\_)](AshRbac.Actions.html#before?/1) 87 | 88 | Callback implementation for [`Spark.Dsl.Transformer.before?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:before?/1). 89 | 90 | # [](AshRbac.Actions.html#functions)Functions 91 | 92 | [Link to this function](AshRbac.Actions.html#after?/1 "Link to this function") 93 | 94 | # after?(\_) 95 | 96 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/actions.ex#L5 "View Source") 97 | 98 | Callback implementation for [`Spark.Dsl.Transformer.after?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after?/1). 99 | 100 | [Link to this function](AshRbac.Actions.html#after_compile?/0 "Link to this function") 101 | 102 | # after\_compile?() 103 | 104 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/actions.ex#L5 "View Source") 105 | 106 | Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0). 107 | 108 | [Link to this function](AshRbac.Actions.html#before?/1 "Link to this function") 109 | 110 | # before?(\_) 111 | 112 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/actions.ex#L5 "View Source") 113 | 114 | Callback implementation for [`Spark.Dsl.Transformer.before?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:before?/1). 115 | 116 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version") 117 | 118 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 119 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac) 120 | 121 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac) 122 | 123 | v0.6.1 124 | 125 | - Pages 126 | - Modules 127 | 128 | 129 | 130 | 131 | 132 | Search documentation of ash\_rbac 133 | 134 | Settings 135 | 136 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/fields/fields.ex#L1 "View Source") AshRbac.Fields (ash\_rbac v0.6.1) 137 | 138 | Adds the policies for fields to the dsl\_state 139 | 140 | # [](AshRbac.Fields.html#summary)Summary 141 | 142 | ## [Functions](AshRbac.Fields.html#functions) 143 | 144 | [after?(\_)](AshRbac.Fields.html#after?/1) 145 | 146 | Callback implementation for [`Spark.Dsl.Transformer.after?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after?/1). 147 | 148 | [after\_compile?()](AshRbac.Fields.html#after_compile?/0) 149 | 150 | Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0). 151 | 152 | [before?(\_)](AshRbac.Fields.html#before?/1) 153 | 154 | Callback implementation for [`Spark.Dsl.Transformer.before?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:before?/1). 155 | 156 | # [](AshRbac.Fields.html#functions)Functions 157 | 158 | [Link to this function](AshRbac.Fields.html#after?/1 "Link to this function") 159 | 160 | # after?(\_) 161 | 162 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/fields/fields.ex#L6 "View Source") 163 | 164 | Callback implementation for [`Spark.Dsl.Transformer.after?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after?/1). 165 | 166 | [Link to this function](AshRbac.Fields.html#after_compile?/0 "Link to this function") 167 | 168 | # after\_compile?() 169 | 170 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/fields/fields.ex#L6 "View Source") 171 | 172 | Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0). 173 | 174 | [Link to this function](AshRbac.Fields.html#before?/1 "Link to this function") 175 | 176 | # before?(\_) 177 | 178 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/fields/fields.ex#L6 "View Source") 179 | 180 | Callback implementation for [`Spark.Dsl.Transformer.before?/1`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:before?/1). 181 | 182 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version") 183 | 184 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 185 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac) 186 | 187 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac) 188 | 189 | v0.6.1 190 | 191 | - Pages 192 | - Modules 193 | 194 | 195 | 196 | 197 | 198 | Search documentation of ash\_rbac 199 | 200 | Settings 201 | 202 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/has_role.ex#L1 "View Source") AshRbac.HasRole (ash\_rbac v0.6.1) 203 | 204 | Check to determine if the actor has a specific role or if the actor has any of the roles in a list 205 | 206 | # [](AshRbac.HasRole.html#summary)Summary 207 | 208 | ## [Functions](AshRbac.HasRole.html#functions) 209 | 210 | [prefer\_expanded\_description?()](AshRbac.HasRole.html#prefer_expanded_description?/0) 211 | 212 | Callback implementation for [`Ash.Policy.Check.prefer_expanded_description?/0`](../ash/3.4.21/Ash.Policy.Check.html#c:prefer_expanded_description?/0). 213 | 214 | [requires\_original\_data?(\_, \_)](AshRbac.HasRole.html#requires_original_data?/2) 215 | 216 | Callback implementation for [`Ash.Policy.Check.requires_original_data?/2`](../ash/3.4.21/Ash.Policy.Check.html#c:requires_original_data?/2). 217 | 218 | [strict\_check(actor, context, opts)](AshRbac.HasRole.html#strict_check/3) 219 | 220 | Callback implementation for [`Ash.Policy.Check.strict_check/3`](../ash/3.4.21/Ash.Policy.Check.html#c:strict_check/3). 221 | 222 | [type()](AshRbac.HasRole.html#type/0) 223 | 224 | Callback implementation for [`Ash.Policy.Check.type/0`](../ash/3.4.21/Ash.Policy.Check.html#c:type/0). 225 | 226 | # [](AshRbac.HasRole.html#functions)Functions 227 | 228 | [Link to this function](AshRbac.HasRole.html#prefer_expanded_description?/0 "Link to this function") 229 | 230 | # prefer\_expanded\_description?() 231 | 232 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/has_role.ex#L5 "View Source") 233 | 234 | Callback implementation for [`Ash.Policy.Check.prefer_expanded_description?/0`](../ash/3.4.21/Ash.Policy.Check.html#c:prefer_expanded_description?/0). 235 | 236 | [Link to this function](AshRbac.HasRole.html#requires_original_data?/2 "Link to this function") 237 | 238 | # requires\_original\_data?(\_, \_) 239 | 240 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/has_role.ex#L5 "View Source") 241 | 242 | Callback implementation for [`Ash.Policy.Check.requires_original_data?/2`](../ash/3.4.21/Ash.Policy.Check.html#c:requires_original_data?/2). 243 | 244 | [Link to this function](AshRbac.HasRole.html#strict_check/3 "Link to this function") 245 | 246 | # strict\_check(actor, context, opts) 247 | 248 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/has_role.ex#L5 "View Source") 249 | 250 | Callback implementation for [`Ash.Policy.Check.strict_check/3`](../ash/3.4.21/Ash.Policy.Check.html#c:strict_check/3). 251 | 252 | [Link to this function](AshRbac.HasRole.html#type/0 "Link to this function") 253 | 254 | # type() 255 | 256 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/has_role.ex#L5 "View Source") 257 | 258 | Callback implementation for [`Ash.Policy.Check.type/0`](../ash/3.4.21/Ash.Policy.Check.html#c:type/0). 259 | 260 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version") 261 | 262 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 263 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac) 264 | 265 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac) 266 | 267 | v0.6.1 268 | 269 | - Pages 270 | - Modules 271 | 272 | 273 | 274 | 275 | 276 | Search documentation of ash\_rbac 277 | 278 | Settings 279 | 280 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L1 "View Source") AshRbac (ash\_rbac v0.6.1) 281 | 282 | # [](AshRbac.html#summary)Summary 283 | 284 | ## [Functions](AshRbac.html#functions) 285 | 286 | [rbac(body)](AshRbac.html#rbac/1) 287 | 288 | # [](AshRbac.html#functions)Functions 289 | 290 | [Link to this macro](AshRbac.html#rbac/1 "Link to this macro") 291 | 292 | # rbac(body) 293 | 294 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L177 "View Source") (macro) 295 | 296 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version") 297 | 298 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 299 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac) 300 | 301 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac) 302 | 303 | v0.6.1 304 | 305 | - Pages 306 | - Modules 307 | 308 | 309 | 310 | 311 | 312 | Search documentation of ash\_rbac 313 | 314 | Settings 315 | 316 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L127 "View Source") AshRbac.Info (ash\_rbac v0.6.1) 317 | 318 | Introspection functions for the Rbac Extension 319 | 320 | # [](AshRbac.Info.html#summary)Summary 321 | 322 | ## [Functions](AshRbac.Info.html#functions) 323 | 324 | [bypass(resource)](AshRbac.Info.html#bypass/1) 325 | 326 | [bypass\_roles\_field(resource)](AshRbac.Info.html#bypass_roles_field/1) 327 | 328 | [public?(resource)](AshRbac.Info.html#public?/1) 329 | 330 | [roles(resource)](AshRbac.Info.html#roles/1) 331 | 332 | # [](AshRbac.Info.html#functions)Functions 333 | 334 | [Link to this function](AshRbac.Info.html#bypass/1 "Link to this function") 335 | 336 | # bypass(resource) 337 | 338 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L133 "View Source") 339 | 340 | [Link to this function](AshRbac.Info.html#bypass_roles_field/1 "Link to this function") 341 | 342 | # bypass\_roles\_field(resource) 343 | 344 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L137 "View Source") 345 | 346 | [Link to this function](AshRbac.Info.html#public?/1 "Link to this function") 347 | 348 | # public?(resource) 349 | 350 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L141 "View Source") 351 | 352 | [Link to this function](AshRbac.Info.html#roles/1 "Link to this function") 353 | 354 | # roles(resource) 355 | 356 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac.ex#L145 "View Source") 357 | 358 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version") 359 | 360 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 361 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac) 362 | 363 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac) 364 | 365 | v0.6.1 366 | 367 | - Pages 368 | - Modules 369 | 370 | 371 | 372 | 373 | 374 | Search documentation of ash\_rbac 375 | 376 | Settings 377 | 378 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/policies.ex#L1 "View Source") AshRbac.Policies (ash\_rbac v0.6.1) 379 | 380 | Adds the configured policies to the resource 381 | 382 | # [](AshRbac.Policies.html#summary)Summary 383 | 384 | ## [Functions](AshRbac.Policies.html#functions) 385 | 386 | [after\_compile?()](AshRbac.Policies.html#after_compile?/0) 387 | 388 | Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0). 389 | 390 | # [](AshRbac.Policies.html#functions)Functions 391 | 392 | [Link to this function](AshRbac.Policies.html#after_compile?/0 "Link to this function") 393 | 394 | # after\_compile?() 395 | 396 | [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/policies.ex#L6 "View Source") 397 | 398 | Callback implementation for [`Spark.Dsl.Transformer.after_compile?/0`](../spark/2.2.31/Spark.Dsl.Transformer.html#c:after_compile?/0). 399 | 400 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version") 401 | 402 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 403 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac) 404 | 405 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac) 406 | 407 | v0.6.1 408 | 409 | - Pages 410 | - Modules 411 | 412 | 413 | 414 | 415 | 416 | Search documentation of ash\_rbac 417 | 418 | Settings 419 | 420 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/lib/ash_rbac/role.ex#L1 "View Source") AshRbac.Role (ash\_rbac v0.6.1) 421 | 422 | The Role entity for the DSL of the rbac extension 423 | 424 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version") 425 | 426 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 427 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac) 428 | 429 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac) 430 | 431 | v0.6.1 432 | 433 | - Pages 434 | - Modules 435 | 436 | 437 | 438 | 439 | 440 | Search documentation of ash\_rbac 441 | 442 | Settings 443 | 444 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/documentation/getting_started.md#L1 "View Source") Getting Started 445 | 446 | ## [](getting_started.html#installation)Installation 447 | 448 | Add the ash\_rbac dependency to your mix.exs 449 | 450 | ``` 451 | defp deps do 452 | [ 453 | {:ash_rbac, "~> 0.6.1"} 454 | ] 455 | end 456 | ``` 457 | 458 | ## [](getting_started.html#adding-ashrbac-to-your-resource)Adding AshRbac to your resource 459 | 460 | First, the authorizer and the extension need to be added. 461 | 462 | ``` 463 | defmodule RootResource do 464 | @moduledoc false 465 | use Ash.Resource, 466 | data_layer: Ash.DataLayer.Ets, 467 | authorizers: [Ash.Policy.Authorizer], # Add the authorizer 468 | extensions: [AshRbac] # Add the extension 469 | ... 470 | end 471 | ``` 472 | 473 | Afterwards, you can add a rbac block to your resource. 474 | 475 | ``` 476 | rbac do 477 | role :user do 478 | fields [:name, :email] 479 | actions [:read] 480 | end 481 | end 482 | ``` 483 | 484 | The options defined in the rbac block are transformed into policies during compile time. 485 | 486 | The previous example will generate the following policies: 487 | 488 | ``` 489 | field_policies do 490 | field_policy [:name, :email], [{AshRbac.HasRole, [role: [:user]]}] do 491 | authorize_if always() 492 | end 493 | 494 | # it also adds a policy for all other fields like this 495 | field_policy [:other_fields, ...] do 496 | forbid_if always() 497 | end 498 | end 499 | 500 | policies do 501 | policy [action(:read), {AshRbac.HasRole, [role: [:user]]}] do 502 | authorize_if always() 503 | end 504 | end 505 | ``` 506 | 507 | It is possible to add extra conditions to fields and actions: 508 | 509 | ``` 510 | rbac do 511 | role :user do 512 | fields [:name, {:email, actor_attribute_equals(:field, "value")}] 513 | actions [{:read, accessing_from(RelatedResource, :path)}] 514 | end 515 | end 516 | ``` 517 | 518 | The conditions are added to the generated policies as well. 519 | 520 | ``` 521 | field_policies do 522 | field_policy [:name], [{AshRbac.HasRole, [role: [:user]]}] do 523 | authorize_if always() 524 | end 525 | 526 | field_policy [:email], [{AshRbac.HasRole, [role: [:user], actor_attribute_equals(:field, "value")]}] do 527 | authorize_if always() 528 | end 529 | 530 | # it also adds a policy for all other fields like this 531 | field_policy [:other_fields, ...] do 532 | forbid_if always() 533 | end 534 | end 535 | 536 | policies do 537 | policy [action(:read), {hAshRbac.HasRole, [role: [:user]]}, accessing_from(RelatedResource, :path)] do 538 | authorize_if always() 539 | end 540 | end 541 | ``` 542 | 543 | [← Previous Page API Reference](api-reference.html) 544 | 545 | [Next Page → Relationships](relationships.html) 546 | 547 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) ([current file](https://preview.hex.pm/preview/ash_rbac/0.6.1/show/documentation/getting_started.md)) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version") 548 | 549 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 550 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac) 551 | 552 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac) 553 | 554 | v0.6.1 555 | 556 | - Pages 557 | - Modules 558 | 559 | 560 | 561 | 562 | 563 | Search documentation of ash\_rbac 564 | 565 | Settings 566 | 567 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/README.md#L1 "View Source") AshRbac 568 | 569 | A small extension that allows for easier application of policies 570 | 571 | ``` 572 | rbac do 573 | role :user do 574 | fields [:fields, :user, :can, :see] 575 | actions [:actions, :user, :can :use] 576 | end 577 | end 578 | ``` 579 | 580 | ## [](readme.html#installation)Installation 581 | 582 | The package can be installed by adding `ash_rbac` to your list of dependencies in `mix.exs`: 583 | 584 | ``` 585 | def deps do 586 | [ 587 | {:ash_rbac, "~> 0.6.1"}, 588 | ] 589 | end 590 | ``` 591 | 592 | Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc) and published on [HexDocs](../index.html). Once published, the docs can be found at [https://hexdocs.pm/ash\_rbac](../ash_rbac.html). 593 | 594 | [← Previous Page Relationships](relationships.html) 595 | 596 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) ([current file](https://preview.hex.pm/preview/ash_rbac/0.6.1/show/README.md)) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version") 597 | 598 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 599 | [![ash_rbac](assets/logo.png)](https://github.com/traveltechdeluxe/ash-rbac) 600 | 601 | [ash\_rbac](https://github.com/traveltechdeluxe/ash-rbac) 602 | 603 | v0.6.1 604 | 605 | - Pages 606 | - Modules 607 | 608 | 609 | 610 | 611 | 612 | Search documentation of ash\_rbac 613 | 614 | Settings 615 | 616 | # [View Source](https://github.com/traveltechdeluxe/ash-rbac/blob/main/documentation/relationships.md#L1 "View Source") Relationships 617 | 618 | As relationships are not part of field policies it is necessary to protect them with an action policy. This can be done by passing a custom condition to the action. 619 | 620 | ``` 621 | # only allow read access if accessed from a parent 622 | rbac do 623 | role :user do 624 | actions [ 625 | {:read, accessing_from(Parent, :child)} 626 | ] 627 | end 628 | end 629 | 630 | # result 631 | policies do 632 | policy [action(:read), accessing_from(Parent, :child)] do 633 | authorize_if {AshRbac.HasRole, [role: :user]} 634 | end 635 | end 636 | ``` 637 | 638 | [← Previous Page Getting Started](getting_started.html) 639 | 640 | [Next Page → Readme](readme.html) 641 | 642 | [Hex Package](https://hex.pm/packages/ash_rbac/0.6.1) [Hex Preview](https://preview.hex.pm/preview/ash_rbac/0.6.1) ([current file](https://preview.hex.pm/preview/ash_rbac/0.6.1/show/documentation/relationships.md)) Search HexDocs [Download ePub version](ash_rbac.epub "ePub version") 643 | 644 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.34.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 645 | -------------------------------------------------------------------------------- /test/docs/ash-docs/ash_money.md: -------------------------------------------------------------------------------- 1 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money) 2 | 3 | [ash\_money](https://github.com/ash-project/ash_money) 4 | 5 | v0.1.15 6 | 7 | - GUIDES 8 | - Modules 9 | - Mix Tasks 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Search documentation of ash\_money 18 | 19 | Settings 20 | 21 | # [View Source](https://github.com/ash-project/ash_money "View Source") API Reference ash\_money v0.1.15 22 | 23 | ## [](api-reference.html#modules)Modules 24 | 25 | [AshMoney](AshMoney.html) 26 | 27 | [`AshMoney`](AshMoney.html#content) provides a type for working with money in your Ash resources. 28 | 29 | [AshMoney.AshPostgresExtension](AshMoney.AshPostgresExtension.html) 30 | 31 | Installs the `money_with_currency` type and operators/functions for Postgres. 32 | 33 | [AshMoney.Types.Money](AshMoney.Types.Money.html) 34 | 35 | A money type for Ash that uses the `ex_money` library. 36 | 37 | [Comparable.Type.Decimal.To.Money](Comparable.Type.Decimal.To.Money.html) 38 | 39 | [Comparable.Type.Float.To.Money](Comparable.Type.Float.To.Money.html) 40 | 41 | [Comparable.Type.Integer.To.Money](Comparable.Type.Integer.To.Money.html) 42 | 43 | [Comparable.Type.Money.To.Decimal](Comparable.Type.Money.To.Decimal.html) 44 | 45 | [Comparable.Type.Money.To.Float](Comparable.Type.Money.To.Float.html) 46 | 47 | [Comparable.Type.Money.To.Integer](Comparable.Type.Money.To.Integer.html) 48 | 49 | [Comparable.Type.Money.To.Money](Comparable.Type.Money.To.Money.html) 50 | 51 | ## [](api-reference.html#mix-tasks)Mix Tasks 52 | 53 | [mix ash\_money.add\_to\_ash\_postgres](Mix.Tasks.AshMoney.AddToAshPostgres.html) 54 | 55 | Adds AshMoney.AshPostgresExtension to installed\_extensions and installs :ex\_money\_sql. 56 | 57 | [mix ash\_money.install](Mix.Tasks.AshMoney.Install.html) 58 | 59 | Installs AshMoney. Should be run with `mix igniter.install ash_money` 60 | 61 | [Next Page → Home](readme.html) 62 | 63 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version") 64 | 65 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 66 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money) 67 | 68 | [ash\_money](https://github.com/ash-project/ash_money) 69 | 70 | v0.1.15 71 | 72 | - GUIDES 73 | - Modules 74 | - Mix Tasks 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | Search documentation of ash\_money 83 | 84 | Settings 85 | 86 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/ash_postgres_extension.ex#L2 "View Source") AshMoney.AshPostgresExtension (ash\_money v0.1.15) 87 | 88 | Installs the `money_with_currency` type and operators/functions for Postgres. 89 | 90 | # [](AshMoney.AshPostgresExtension.html#summary)Summary 91 | 92 | ## [Functions](AshMoney.AshPostgresExtension.html#functions) 93 | 94 | [extension()](AshMoney.AshPostgresExtension.html#extension/0) 95 | 96 | [install(int)](AshMoney.AshPostgresExtension.html#install/1) 97 | 98 | Callback implementation for [`AshPostgres.CustomExtension.install/1`](../ash_postgres/2.4.18/AshPostgres.CustomExtension.html#c:install/1). 99 | 100 | [uninstall(v)](AshMoney.AshPostgresExtension.html#uninstall/1) 101 | 102 | Callback implementation for [`AshPostgres.CustomExtension.uninstall/1`](../ash_postgres/2.4.18/AshPostgres.CustomExtension.html#c:uninstall/1). 103 | 104 | # [](AshMoney.AshPostgresExtension.html#functions)Functions 105 | 106 | [Link to this function](AshMoney.AshPostgresExtension.html#extension/0 "Link to this function") 107 | 108 | # extension() 109 | 110 | [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/ash_postgres_extension.ex#L6 "View Source") 111 | 112 | [Link to this function](AshMoney.AshPostgresExtension.html#install/1 "Link to this function") 113 | 114 | # install(int) 115 | 116 | [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/ash_postgres_extension.ex#L8 "View Source") 117 | 118 | Callback implementation for [`AshPostgres.CustomExtension.install/1`](../ash_postgres/2.4.18/AshPostgres.CustomExtension.html#c:install/1). 119 | 120 | [Link to this function](AshMoney.AshPostgresExtension.html#uninstall/1 "Link to this function") 121 | 122 | # uninstall(v) 123 | 124 | [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/ash_postgres_extension.ex#L56 "View Source") 125 | 126 | Callback implementation for [`AshPostgres.CustomExtension.uninstall/1`](../ash_postgres/2.4.18/AshPostgres.CustomExtension.html#c:uninstall/1). 127 | 128 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version") 129 | 130 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 131 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money) 132 | 133 | [ash\_money](https://github.com/ash-project/ash_money) 134 | 135 | v0.1.15 136 | 137 | - GUIDES 138 | - Modules 139 | - Mix Tasks 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | Search documentation of ash\_money 148 | 149 | Settings 150 | 151 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money.ex#L1 "View Source") AshMoney (ash\_money v0.1.15) 152 | 153 | [`AshMoney`](AshMoney.html#content) provides a type for working with money in your Ash resources. 154 | 155 | It also provides an `AshPostgres.Extension` that can be used to add support for money types and operators to your Postgres database. 156 | 157 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version") 158 | 159 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 160 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money) 161 | 162 | [ash\_money](https://github.com/ash-project/ash_money) 163 | 164 | v0.1.15 165 | 166 | - GUIDES 167 | - Modules 168 | - Mix Tasks 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | Search documentation of ash\_money 177 | 178 | Settings 179 | 180 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/types/money.ex#L1 "View Source") AshMoney.Types.Money (ash\_money v0.1.15) 181 | 182 | A money type for Ash that uses the `ex_money` library. 183 | 184 | When constructing a composite type, use a tuple in the following structure: 185 | 186 | `composite_type(%{currency: "USD", amount: Decimal.new("0")}}, AshMoney.Types.Money)` 187 | 188 | If you've added a custom type, like `:money`: 189 | 190 | ``` 191 | composite_type(%{currency: "USD", amount: Decimal.new("0")}}, :money) 192 | ``` 193 | 194 | # [](AshMoney.Types.Money.html#summary)Summary 195 | 196 | ## [Functions](AshMoney.Types.Money.html#functions) 197 | 198 | [handle\_change?()](AshMoney.Types.Money.html#handle_change?/0) 199 | 200 | [prepare\_change?()](AshMoney.Types.Money.html#prepare_change?/0) 201 | 202 | # [](AshMoney.Types.Money.html#functions)Functions 203 | 204 | [Link to this function](AshMoney.Types.Money.html#handle_change?/0 "Link to this function") 205 | 206 | # handle\_change?() 207 | 208 | [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/types/money.ex#L1 "View Source") 209 | 210 | [Link to this function](AshMoney.Types.Money.html#prepare_change?/0 "Link to this function") 211 | 212 | # prepare\_change?() 213 | 214 | [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/types/money.ex#L1 "View Source") 215 | 216 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version") 217 | 218 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 219 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money) 220 | 221 | [ash\_money](https://github.com/ash-project/ash_money) 222 | 223 | v0.1.15 224 | 225 | - GUIDES 226 | - Modules 227 | - Mix Tasks 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | Search documentation of ash\_money 236 | 237 | Settings 238 | 239 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/comp.ex#L11 "View Source") Comparable.Type.Decimal.To.Money (ash\_money v0.1.15) 240 | 241 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version") 242 | 243 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 244 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money) 245 | 246 | [ash\_money](https://github.com/ash-project/ash_money) 247 | 248 | v0.1.15 249 | 250 | - GUIDES 251 | - Modules 252 | - Mix Tasks 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | Search documentation of ash\_money 261 | 262 | Settings 263 | 264 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/comp.ex#L15 "View Source") Comparable.Type.Float.To.Money (ash\_money v0.1.15) 265 | 266 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version") 267 | 268 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 269 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money) 270 | 271 | [ash\_money](https://github.com/ash-project/ash_money) 272 | 273 | v0.1.15 274 | 275 | - GUIDES 276 | - Modules 277 | - Mix Tasks 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | Search documentation of ash\_money 286 | 287 | Settings 288 | 289 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/comp.ex#L3 "View Source") Comparable.Type.Integer.To.Money (ash\_money v0.1.15) 290 | 291 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version") 292 | 293 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 294 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money) 295 | 296 | [ash\_money](https://github.com/ash-project/ash_money) 297 | 298 | v0.1.15 299 | 300 | - GUIDES 301 | - Modules 302 | - Mix Tasks 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | Search documentation of ash\_money 311 | 312 | Settings 313 | 314 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/comp.ex#L11 "View Source") Comparable.Type.Money.To.Decimal (ash\_money v0.1.15) 315 | 316 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version") 317 | 318 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 319 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money) 320 | 321 | [ash\_money](https://github.com/ash-project/ash_money) 322 | 323 | v0.1.15 324 | 325 | - GUIDES 326 | - Modules 327 | - Mix Tasks 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | Search documentation of ash\_money 336 | 337 | Settings 338 | 339 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/comp.ex#L15 "View Source") Comparable.Type.Money.To.Float (ash\_money v0.1.15) 340 | 341 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version") 342 | 343 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 344 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money) 345 | 346 | [ash\_money](https://github.com/ash-project/ash_money) 347 | 348 | v0.1.15 349 | 350 | - GUIDES 351 | - Modules 352 | - Mix Tasks 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | Search documentation of ash\_money 361 | 362 | Settings 363 | 364 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/comp.ex#L3 "View Source") Comparable.Type.Money.To.Integer (ash\_money v0.1.15) 365 | 366 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version") 367 | 368 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 369 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money) 370 | 371 | [ash\_money](https://github.com/ash-project/ash_money) 372 | 373 | v0.1.15 374 | 375 | - GUIDES 376 | - Modules 377 | - Mix Tasks 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | Search documentation of ash\_money 386 | 387 | Settings 388 | 389 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/ash_money/comp.ex#L7 "View Source") Comparable.Type.Money.To.Money (ash\_money v0.1.15) 390 | 391 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version") 392 | 393 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 394 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money) 395 | 396 | [ash\_money](https://github.com/ash-project/ash_money) 397 | 398 | v0.1.15 399 | 400 | - GUIDES 401 | - Modules 402 | - Mix Tasks 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | Search documentation of ash\_money 411 | 412 | Settings 413 | 414 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/documentation/tutorials/getting-started-with-ash-money.md#L1 "View Source") Getting Started With AshMoney 415 | 416 | ## [](getting-started-with-ash-money.html#bring-in-the-ash_money-dependency)Bring in the ash\_money dependency 417 | 418 | ``` 419 | def deps() 420 | [ 421 | ... 422 | {:ash_money, "~> 0.1.15"} 423 | ] 424 | end 425 | ``` 426 | 427 | ## [](getting-started-with-ash-money.html#setup)Setup 428 | 429 | ### [](getting-started-with-ash-money.html#using-igniter-recommended)Using Igniter (recommended) 430 | 431 | ``` 432 | mix igniter.install ash_money 433 | ``` 434 | 435 | ### [](getting-started-with-ash-money.html#manual)Manual 436 | 437 | The primary thing that AshMoney provides is [`AshMoney.Types.Money`](AshMoney.Types.Money.html). This is backed by `ex_money`. You can use it out of the box like so: 438 | 439 | ``` 440 | attribute :balance, AshMoney.Types.Money 441 | ``` 442 | 443 | ## [](getting-started-with-ash-money.html#add-to-known-types)Add to known types 444 | 445 | To support money operations in runtime expressions, which use [`Ash`](../ash/3.4.48/Ash.html)'s operator overloading feature, we have to tell Ash about the `Ash.Type.Money` using the `known_type` configuration. 446 | 447 | ``` 448 | config :ash, :known_types, [AshMoney.Types.Money] 449 | ``` 450 | 451 | ## [](getting-started-with-ash-money.html#referencing-with-money)Referencing with `:money` 452 | 453 | You can add it to your compile time list of types for easier reference: 454 | 455 | ``` 456 | config :ash, :custom_types, money: AshMoney.Types.Money 457 | ``` 458 | 459 | Then compile ash again, `mix deps.compile ash --force`, and refer to it like so: 460 | 461 | ``` 462 | attribute :balance, :money 463 | ``` 464 | 465 | ## [](getting-started-with-ash-money.html#adding-ashpostgres-support)Adding AshPostgres Support 466 | 467 | First, add the `:ex_money_sql` dependency to your `mix.exs` file. 468 | 469 | Then add [`AshMoney.AshPostgresExtension`](AshMoney.AshPostgresExtension.html) to your list of `installed_extensions` in your repo, and generate migrations. 470 | 471 | ``` 472 | defmodule YourRepo do 473 | def installed_extensions do 474 | [..., AshMoney.AshPostgresExtension] 475 | end 476 | end 477 | ``` 478 | 479 | ## [](getting-started-with-ash-money.html#ashpostgres-support)AshPostgres Support 480 | 481 | Thanks to `ex_money_sql`, there are excellent tools for lowering support for money into your postgres database. This allows for things like aggregates that sum amounts, and referencing money in expressions: 482 | 483 | ``` 484 | sum :sum_of_usd_balances, :accounts, :balance do 485 | filter expr(balance[:currency_code] == "USD") 486 | end 487 | ``` 488 | 489 | ## [](getting-started-with-ash-money.html#ashgraphql-support)AshGraphql Support 490 | 491 | Add the following to your schema file: 492 | 493 | ``` 494 | object :money do 495 | field(:amount, non_null(:decimal)) 496 | field(:currency, non_null(:string)) 497 | end 498 | 499 | input_object :money_input do 500 | field(:amount, non_null(:decimal)) 501 | field(:currency, non_null(:string)) 502 | end 503 | ``` 504 | 505 | [← Previous Page Home](readme.html) 506 | 507 | [Next Page → Change Log](changelog.html) 508 | 509 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) ([current file](https://preview.hex.pm/preview/ash_money/0.1.15/show/documentation/tutorials/getting-started-with-ash-money.md)) Search HexDocs [Download ePub version](ash_money.epub "ePub version") 510 | 511 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 512 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money) 513 | 514 | [ash\_money](https://github.com/ash-project/ash_money) 515 | 516 | v0.1.15 517 | 518 | - GUIDES 519 | - Modules 520 | - Mix Tasks 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | Search documentation of ash\_money 529 | 530 | Settings 531 | 532 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/mix/tasks/ash_money.add_to_ash_postgres.ex#L2 "View Source") mix ash\_money.add\_to\_ash\_postgres (ash\_money v0.1.15) 533 | 534 | Adds AshMoney.AshPostgresExtension to installed\_extensions and installs :ex\_money\_sql. 535 | 536 | This is called automatically by `mix igniter.install ash_money` if [`AshPostgres`](../ash_postgres/2.4.18/AshPostgres.html) is installed at the time. This task is useful if you install `ash_postgres` *after* installing `ash_money`. 537 | 538 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version") 539 | 540 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 541 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money) 542 | 543 | [ash\_money](https://github.com/ash-project/ash_money) 544 | 545 | v0.1.15 546 | 547 | - GUIDES 548 | - Modules 549 | - Mix Tasks 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | Search documentation of ash\_money 558 | 559 | Settings 560 | 561 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/mix/tasks/ash_money.install.ex#L2 "View Source") mix ash\_money.install (ash\_money v0.1.15) 562 | 563 | Installs AshMoney. Should be run with `mix igniter.install ash_money` 564 | 565 | # [](Mix.Tasks.AshMoney.Install.html#summary)Summary 566 | 567 | ## [Functions](Mix.Tasks.AshMoney.Install.html#functions) 568 | 569 | [igniter(igniter, argv)](Mix.Tasks.AshMoney.Install.html#igniter/2) 570 | 571 | Callback implementation for [`Igniter.Mix.Task.igniter/2`](../igniter/0.5.0/Igniter.Mix.Task.html#c:igniter/2). 572 | 573 | # [](Mix.Tasks.AshMoney.Install.html#functions)Functions 574 | 575 | [Link to this function](Mix.Tasks.AshMoney.Install.html#igniter/2 "Link to this function") 576 | 577 | # igniter(igniter, argv) 578 | 579 | [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/lib/mix/tasks/ash_money.install.ex#L8 "View Source") 580 | 581 | Callback implementation for [`Igniter.Mix.Task.igniter/2`](../igniter/0.5.0/Igniter.Mix.Task.html#c:igniter/2). 582 | 583 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) Search HexDocs [Download ePub version](ash_money.epub "ePub version") 584 | 585 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 586 | [![ash_money](assets/logo.png)](https://github.com/ash-project/ash_money) 587 | 588 | [ash\_money](https://github.com/ash-project/ash_money) 589 | 590 | v0.1.15 591 | 592 | - GUIDES 593 | - Modules 594 | - Mix Tasks 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | Search documentation of ash\_money 603 | 604 | Settings 605 | 606 | # [View Source](https://github.com/ash-project/ash_money/blob/v0.1.15/README.md#L1 "View Source") Home 607 | 608 | ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-black-text.png?raw=true#gh-light-mode-only) ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-white-text.png?raw=true#gh-dark-mode-only) 609 | 610 | ![Elixir CI](https://github.com/ash-project/ash_money/workflows/CI/badge.svg) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Hex version badge](https://img.shields.io/hexpm/v/ash_money.svg)](https://hex.pm/packages/ash_money) [![Hexdocs badge](https://img.shields.io/badge/docs-hexdocs-purple)](../ash_money.html) 611 | 612 | # AshMoney 613 | 614 | Welcome! This is the extension for working with money types in [Ash](../ash.html). This is a thin wrapper around the very excellent [ex\_money](../ex_money.html). It provides: 615 | 616 | - An [`Ash.Type`](../ash/3.4.48/Ash.Type.html) for representing [`Money`](../ex_money/5.18.0/Money.html) 617 | - An `AshPostgres.Extension` for supporting common money operations directly in the database 618 | - An implementation of [`Comp`](../ash/3.4.48/Comp.html) for `%Money{}`, allowing Ash to compare them. 619 | 620 | ## [](readme.html#tutorials)Tutorials 621 | 622 | - [Getting Started with AshMoney](getting-started-with-ash-money.html) 623 | 624 | [← Previous Page API Reference](api-reference.html) 625 | 626 | [Next Page → Getting Started With AshMoney](getting-started-with-ash-money.html) 627 | 628 | [Hex Package](https://hex.pm/packages/ash_money/0.1.15) [Hex Preview](https://preview.hex.pm/preview/ash_money/0.1.15) ([current file](https://preview.hex.pm/preview/ash_money/0.1.15/show/README.md)) Search HexDocs [Download ePub version](ash_money.epub "ePub version") 629 | 630 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 631 | -------------------------------------------------------------------------------- /test/docs/ash-docs/ash_cloak.md: -------------------------------------------------------------------------------- 1 | [ash\_cloak](https://github.com/ash-project/ash_cloak) 2 | 3 | v0.1.2 4 | 5 | - GUIDES 6 | - Modules 7 | 8 | 9 | 10 | 11 | 12 | Search documentation of ash\_cloak 13 | 14 | Settings 15 | 16 | # [View Source](https://github.com/ash-project/ash_cloak "View Source") API Reference ash\_cloak v0.1.2 17 | 18 | ## [](api-reference.html#modules)Modules 19 | 20 | [AshCloak](AshCloak.html) 21 | 22 | An extension for encrypting attributes of a resource. 23 | 24 | [AshCloak.Changes.Encrypt](AshCloak.Changes.Encrypt.html) 25 | 26 | Takes an argument, and encrypts it into an attribute called `encrypted_{attribute}` 27 | 28 | [AshCloak.Cloak.Options](AshCloak.Cloak.Options.html) 29 | 30 | Mod Docs 31 | 32 | [AshCloak.Info](AshCloak.Info.html) 33 | 34 | Introspection functions for the [`AshCloak`](AshCloak.html) extension. 35 | 36 | [Next Page → Home](readme.html) 37 | 38 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version") 39 | 40 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 41 | [ash\_cloak](https://github.com/ash-project/ash_cloak) 42 | 43 | v0.1.2 44 | 45 | - GUIDES 46 | - Modules 47 | 48 | 49 | 50 | 51 | 52 | Search documentation of ash\_cloak 53 | 54 | Settings 55 | 56 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/changes/encrypt.ex#L1 "View Source") AshCloak.Changes.Encrypt (ash\_cloak v0.1.2) 57 | 58 | Takes an argument, and encrypts it into an attribute called `encrypted_{attribute}` 59 | 60 | # [](AshCloak.Changes.Encrypt.html#summary)Summary 61 | 62 | ## [Functions](AshCloak.Changes.Encrypt.html#functions) 63 | 64 | [atomic(changeset, opts, \_)](AshCloak.Changes.Encrypt.html#atomic/3) 65 | 66 | Callback implementation for [`Ash.Resource.Change.atomic/3`](../ash/3.4.1/Ash.Resource.Change.html#c:atomic/3). 67 | 68 | [change(changeset, opts, \_)](AshCloak.Changes.Encrypt.html#change/3) 69 | 70 | Callback implementation for [`Ash.Resource.Change.change/3`](../ash/3.4.1/Ash.Resource.Change.html#c:change/3). 71 | 72 | # [](AshCloak.Changes.Encrypt.html#functions)Functions 73 | 74 | [Link to this function](AshCloak.Changes.Encrypt.html#atomic/3 "Link to this function") 75 | 76 | # atomic(changeset, opts, \_) 77 | 78 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/changes/encrypt.ex#L19 "View Source") 79 | 80 | Callback implementation for [`Ash.Resource.Change.atomic/3`](../ash/3.4.1/Ash.Resource.Change.html#c:atomic/3). 81 | 82 | [Link to this function](AshCloak.Changes.Encrypt.html#change/3 "Link to this function") 83 | 84 | # change(changeset, opts, \_) 85 | 86 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/changes/encrypt.ex#L5 "View Source") 87 | 88 | Callback implementation for [`Ash.Resource.Change.change/3`](../ash/3.4.1/Ash.Resource.Change.html#c:change/3). 89 | 90 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version") 91 | 92 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 93 | [ash\_cloak](https://github.com/ash-project/ash_cloak) 94 | 95 | v0.1.2 96 | 97 | - GUIDES 98 | - Modules 99 | 100 | 101 | 102 | 103 | 104 | Search documentation of ash\_cloak 105 | 106 | Settings 107 | 108 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/deps/spark/lib/spark/dsl/extension.ex#L1022 "View Source") AshCloak.Cloak.Options (ash\_cloak v0.1.2) 109 | 110 | Mod Docs 111 | 112 | # [](AshCloak.Cloak.Options.html#summary)Summary 113 | 114 | ## [Functions](AshCloak.Cloak.Options.html#functions) 115 | 116 | [attributes(value)](AshCloak.Cloak.Options.html#attributes/1) 117 | 118 | Hello 3 119 | 120 | [decrypt\_by\_default(value)](AshCloak.Cloak.Options.html#decrypt_by_default/1) 121 | 122 | Hello 3 123 | 124 | [on\_decrypt(value)](AshCloak.Cloak.Options.html#on_decrypt/1) 125 | 126 | Hello 3 127 | 128 | [vault(value)](AshCloak.Cloak.Options.html#vault/1) 129 | 130 | Hello 3 131 | 132 | # [](AshCloak.Cloak.Options.html#functions)Functions 133 | 134 | [Link to this macro](AshCloak.Cloak.Options.html#attributes/1 "Link to this macro") 135 | 136 | # attributes(value) 137 | 138 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/deps/spark/lib/spark/dsl/extension.ex#L1022 "View Source") (macro) 139 | 140 | Hello 3 141 | 142 | [Link to this macro](AshCloak.Cloak.Options.html#decrypt_by_default/1 "Link to this macro") 143 | 144 | # decrypt\_by\_default(value) 145 | 146 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/deps/spark/lib/spark/dsl/extension.ex#L1022 "View Source") (macro) 147 | 148 | Hello 3 149 | 150 | [Link to this macro](AshCloak.Cloak.Options.html#on_decrypt/1 "Link to this macro") 151 | 152 | # on\_decrypt(value) 153 | 154 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/deps/spark/lib/spark/dsl/extension.ex#L1022 "View Source") (macro) 155 | 156 | Hello 3 157 | 158 | [Link to this macro](AshCloak.Cloak.Options.html#vault/1 "Link to this macro") 159 | 160 | # vault(value) 161 | 162 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/deps/spark/lib/spark/dsl/extension.ex#L1022 "View Source") (macro) 163 | 164 | Hello 3 165 | 166 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version") 167 | 168 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 169 | [ash\_cloak](https://github.com/ash-project/ash_cloak) 170 | 171 | v0.1.2 172 | 173 | - GUIDES 174 | - Modules 175 | 176 | 177 | 178 | 179 | 180 | Search documentation of ash\_cloak 181 | 182 | Settings 183 | 184 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak.ex#L1 "View Source") AshCloak (ash\_cloak v0.1.2) 185 | 186 | An extension for encrypting attributes of a resource. 187 | 188 | See the getting started guide for more information. 189 | 190 | # [](AshCloak.html#summary)Summary 191 | 192 | ## [Functions](AshCloak.html#functions) 193 | 194 | [cloak(body)](AshCloak.html#cloak/1) 195 | 196 | Hello! 197 | 198 | [encrypt\_and\_set(changeset, key, value)](AshCloak.html#encrypt_and_set/3) 199 | 200 | Encrypts and writes to an encrypted attribute. 201 | 202 | # [](AshCloak.html#functions)Functions 203 | 204 | [Link to this macro](AshCloak.html#cloak/1 "Link to this macro") 205 | 206 | # cloak(body) 207 | 208 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak.ex#L41 "View Source") (macro) 209 | 210 | Hello! 211 | 212 | [Link to this function](AshCloak.html#encrypt_and_set/3 "Link to this function") 213 | 214 | # encrypt\_and\_set(changeset, key, value) 215 | 216 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak.ex#L50 "View Source") 217 | 218 | ``` 219 | @spec encrypt_and_set(Ash.Changeset.t(), attr :: atom(), term :: term()) :: 220 | Ash.Changeset.t() 221 | ``` 222 | 223 | Encrypts and writes to an encrypted attribute. 224 | 225 | If the changeset is pending (i.e not currently running the action), then it is added as a before\_action hook. Otherwise, it is run immediately 226 | 227 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version") 228 | 229 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 230 | [ash\_cloak](https://github.com/ash-project/ash_cloak) 231 | 232 | v0.1.2 233 | 234 | - GUIDES 235 | - Modules 236 | 237 | 238 | 239 | 240 | 241 | Search documentation of ash\_cloak 242 | 243 | Settings 244 | 245 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L1 "View Source") AshCloak.Info (ash\_cloak v0.1.2) 246 | 247 | Introspection functions for the [`AshCloak`](AshCloak.html) extension. 248 | 249 | # [](AshCloak.Info.html#summary)Summary 250 | 251 | ## [Functions](AshCloak.Info.html#functions) 252 | 253 | [cloak\_attributes(dsl\_or\_extended)](AshCloak.Info.html#cloak_attributes/1) 254 | 255 | The attribute or attributes to encrypt. The attribute will be renamed to `encrypted_{attribute}`, and a calculation with the same name will be added. 256 | 257 | [cloak\_attributes!(dsl\_or\_extended)](AshCloak.Info.html#cloak_attributes!/1) 258 | 259 | The attribute or attributes to encrypt. The attribute will be renamed to `encrypted_{attribute}`, and a calculation with the same name will be added. 260 | 261 | [cloak\_decrypt\_by\_default(dsl\_or\_extended)](AshCloak.Info.html#cloak_decrypt_by_default/1) 262 | 263 | A list of attributes that should be decrypted (their calculation should be loaded) by default. 264 | 265 | [cloak\_decrypt\_by\_default!(dsl\_or\_extended)](AshCloak.Info.html#cloak_decrypt_by_default!/1) 266 | 267 | A list of attributes that should be decrypted (their calculation should be loaded) by default. 268 | 269 | [cloak\_on\_decrypt(dsl\_or\_extended)](AshCloak.Info.html#cloak_on_decrypt/1) 270 | 271 | A function to call when decrypting any value. Takes the resource, field, records, and calculation context. Must return `:ok` or `{:error, error}` 272 | 273 | [cloak\_on\_decrypt!(dsl\_or\_extended)](AshCloak.Info.html#cloak_on_decrypt!/1) 274 | 275 | A function to call when decrypting any value. Takes the resource, field, records, and calculation context. Must return `:ok` or `{:error, error}` 276 | 277 | [cloak\_options(dsl\_or\_extended)](AshCloak.Info.html#cloak_options/1) 278 | 279 | cloak DSL options 280 | 281 | [cloak\_vault(dsl\_or\_extended)](AshCloak.Info.html#cloak_vault/1) 282 | 283 | The vault to use to encrypt & decrypt the value 284 | 285 | [cloak\_vault!(dsl\_or\_extended)](AshCloak.Info.html#cloak_vault!/1) 286 | 287 | The vault to use to encrypt & decrypt the value 288 | 289 | # [](AshCloak.Info.html#functions)Functions 290 | 291 | [Link to this function](AshCloak.Info.html#cloak_attributes/1 "Link to this function") 292 | 293 | # cloak\_attributes(dsl\_or\_extended) 294 | 295 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source") 296 | 297 | ``` 298 | @spec cloak_attributes(dsl_or_extended :: module() | map()) :: 299 | {:ok, [atom()]} | :error 300 | ``` 301 | 302 | The attribute or attributes to encrypt. The attribute will be renamed to `encrypted_{attribute}`, and a calculation with the same name will be added. 303 | 304 | [Link to this function](AshCloak.Info.html#cloak_attributes!/1 "Link to this function") 305 | 306 | # cloak\_attributes!(dsl\_or\_extended) 307 | 308 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source") 309 | 310 | ``` 311 | @spec cloak_attributes!(dsl_or_extended :: module() | map()) :: [atom()] | no_return() 312 | ``` 313 | 314 | The attribute or attributes to encrypt. The attribute will be renamed to `encrypted_{attribute}`, and a calculation with the same name will be added. 315 | 316 | [Link to this function](AshCloak.Info.html#cloak_decrypt_by_default/1 "Link to this function") 317 | 318 | # cloak\_decrypt\_by\_default(dsl\_or\_extended) 319 | 320 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source") 321 | 322 | ``` 323 | @spec cloak_decrypt_by_default(dsl_or_extended :: module() | map()) :: 324 | {:ok, [atom()]} | :error 325 | ``` 326 | 327 | A list of attributes that should be decrypted (their calculation should be loaded) by default. 328 | 329 | [Link to this function](AshCloak.Info.html#cloak_decrypt_by_default!/1 "Link to this function") 330 | 331 | # cloak\_decrypt\_by\_default!(dsl\_or\_extended) 332 | 333 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source") 334 | 335 | ``` 336 | @spec cloak_decrypt_by_default!(dsl_or_extended :: module() | map()) :: 337 | [atom()] | no_return() 338 | ``` 339 | 340 | A list of attributes that should be decrypted (their calculation should be loaded) by default. 341 | 342 | [Link to this function](AshCloak.Info.html#cloak_on_decrypt/1 "Link to this function") 343 | 344 | # cloak\_on\_decrypt(dsl\_or\_extended) 345 | 346 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source") 347 | 348 | ``` 349 | @spec cloak_on_decrypt(dsl_or_extended :: module() | map()) :: 350 | {:ok, (any(), any(), any(), any() -> any()) | mfa()} | :error 351 | ``` 352 | 353 | A function to call when decrypting any value. Takes the resource, field, records, and calculation context. Must return `:ok` or `{:error, error}` 354 | 355 | [Link to this function](AshCloak.Info.html#cloak_on_decrypt!/1 "Link to this function") 356 | 357 | # cloak\_on\_decrypt!(dsl\_or\_extended) 358 | 359 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source") 360 | 361 | ``` 362 | @spec cloak_on_decrypt!(dsl_or_extended :: module() | map()) :: 363 | ((any(), any(), any(), any() -> any()) | mfa()) | no_return() 364 | ``` 365 | 366 | A function to call when decrypting any value. Takes the resource, field, records, and calculation context. Must return `:ok` or `{:error, error}` 367 | 368 | [Link to this function](AshCloak.Info.html#cloak_options/1 "Link to this function") 369 | 370 | # cloak\_options(dsl\_or\_extended) 371 | 372 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source") 373 | 374 | ``` 375 | @spec cloak_options(dsl_or_extended :: module() | map()) :: %{ 376 | required(atom()) => any() 377 | } 378 | ``` 379 | 380 | cloak DSL options 381 | 382 | Returns a map containing the and any configured or default values. 383 | 384 | [Link to this function](AshCloak.Info.html#cloak_vault/1 "Link to this function") 385 | 386 | # cloak\_vault(dsl\_or\_extended) 387 | 388 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source") 389 | 390 | ``` 391 | @spec cloak_vault(dsl_or_extended :: module() | map()) :: {:ok, module()} | :error 392 | ``` 393 | 394 | The vault to use to encrypt & decrypt the value 395 | 396 | [Link to this function](AshCloak.Info.html#cloak_vault!/1 "Link to this function") 397 | 398 | # cloak\_vault!(dsl\_or\_extended) 399 | 400 | [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/lib/ash_cloak/info.ex#L3 "View Source") 401 | 402 | ``` 403 | @spec cloak_vault!(dsl_or_extended :: module() | map()) :: module() | no_return() 404 | ``` 405 | 406 | The vault to use to encrypt & decrypt the value 407 | 408 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version") 409 | 410 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 411 | [ash\_cloak](https://github.com/ash-project/ash_cloak) 412 | 413 | v0.1.2 414 | 415 | - GUIDES 416 | - Modules 417 | 418 | 419 | 420 | 421 | 422 | Search documentation of ash\_cloak 423 | 424 | Settings 425 | 426 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/documentation/dsls/DSL:-AshCloak.md#L1 "View Source") DSL: AshCloak 427 | 428 | An extension for encrypting attributes of a resource. 429 | 430 | See the getting started guide for more information. 431 | 432 | ## [](dsl-ashcloak.html#cloak)cloak 433 | 434 | Encrypt attributes of a resource 435 | 436 | ### [](dsl-ashcloak.html#options)Options 437 | 438 | NameTypeDefaultDocs[`vault`](dsl-ashcloak.html#cloak-vault)`module`The vault to use to encrypt & decrypt the value[`attributes`](dsl-ashcloak.html#cloak-attributes)`atom | list(atom)``[]`The attribute or attributes to encrypt. The attribute will be renamed to `encrypted_{attribute}`, and a calculation with the same name will be added.[`decrypt_by_default`](dsl-ashcloak.html#cloak-decrypt_by_default)`atom | list(atom)``[]`A list of attributes that should be decrypted (their calculation should be loaded) by default.[`on_decrypt`](dsl-ashcloak.html#cloak-on_decrypt)`(any, any, any, any -> any) | mfa`A function to call when decrypting any value. Takes the resource, field, records, and calculation context. Must return `:ok` or `{:error, error}` 439 | 440 | [← Previous Page How does AshCloak work?](how-does-ash-cloak-work.html) 441 | 442 | [Next Page → Change Log](changelog.html) 443 | 444 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) ([current file](https://preview.hex.pm/preview/ash_cloak/0.1.2/show/documentation/dsls/DSL:-AshCloak.md)) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version") 445 | 446 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 447 | [ash\_cloak](https://github.com/ash-project/ash_cloak) 448 | 449 | v0.1.2 450 | 451 | - GUIDES 452 | - Modules 453 | 454 | 455 | 456 | 457 | 458 | Search documentation of ash\_cloak 459 | 460 | Settings 461 | 462 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/documentation/tutorials/getting-started-with-ash-cloak.md#L1 "View Source") Get Started with AshCloak 463 | 464 | ## [](getting-started-with-ash-cloak.html#installation)Installation 465 | 466 | Add `ash_cloak` to your list of dependencies in `mix.exs`: 467 | 468 | ``` 469 | {:ash_cloak, "~> 0.1.2"} 470 | ``` 471 | 472 | Follow [the cloak getting started guide](../cloak/readme.html) to add `cloak` as a dependency, as AshCloak does not add a vault implementation for you. Note that you do not need `cloak_ecto` because your Ash data layer will take care of this. 473 | 474 | Alternatively you could use your own vault module that implements `encrypt!` and `decrypt!`, but we recommend using `Cloak` to achieve that goal. See the [cloak vault guide](../cloak/install.html#create-a-vault) 475 | 476 | ### [](getting-started-with-ash-cloak.html#add-the-ashcloak-extension-to-your-resource)Add the [`AshCloak`](AshCloak.html) extension to your resource 477 | 478 | ``` 479 | defmodule User do 480 | use Ash.Resource, extensions: [AshCloak] 481 | 482 | cloak do 483 | # the vault to use to encrypt them 484 | vault MyApp.Vault 485 | 486 | # the attributes to encrypt 487 | attributes [:address, :phone_number] 488 | 489 | # This is just equivalent to always providing `load: fields` on all calls 490 | decrypt_by_default [:address] 491 | 492 | # An MFA or function to be invoked beforce any decryption 493 | on_decrypt fn records, field, context -> 494 | # Ash has policies that allow forbidding certain users to load data. 495 | # You should generally use those for authorization rules, and 496 | # only use this callback for auditing/logging. 497 | Audit.user_accessed_encrypted_field(records, field, context) 498 | 499 | if context.user.name == "marty" do 500 | {:error, "No martys at the party!"} 501 | else 502 | :ok 503 | end 504 | end 505 | end 506 | end 507 | ``` 508 | 509 | [← Previous Page Home](readme.html) 510 | 511 | [Next Page → How does AshCloak work?](how-does-ash-cloak-work.html) 512 | 513 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) ([current file](https://preview.hex.pm/preview/ash_cloak/0.1.2/show/documentation/tutorials/getting-started-with-ash-cloak.md)) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version") 514 | 515 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 516 | [ash\_cloak](https://github.com/ash-project/ash_cloak) 517 | 518 | v0.1.2 519 | 520 | - GUIDES 521 | - Modules 522 | 523 | 524 | 525 | 526 | 527 | Search documentation of ash\_cloak 528 | 529 | Settings 530 | 531 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/documentation/topics/how-does-ash-cloak-work.md#L1 "View Source") How does AshCloak work? 532 | 533 | ## [](how-does-ash-cloak-work.html#rewrite-attributes-to-calculations)Rewrite attributes to calculations 534 | 535 | First, AshCloak changes the name of each cloaked attribute to `encrypted_`, and sets `public?: false` and `sensitive?: true`. 536 | 537 | Then it adds a *calculation* matching the original attribute that, when loaded, will decrypt the given attribute and call any configured `on_decrypt` callbacks. 538 | 539 | ## [](how-does-ash-cloak-work.html#modify-actions)Modify Actions 540 | 541 | AshCloak then goes through each action that accepts the attribute and removes the attribute from the accept list. 542 | 543 | Then it adds an argument by the same name, and a `change` that encrypts the attribute value. 544 | 545 | This `change` also deletes the argument from the arguments list and from the params. This is a small extra layer of security to prevent accidental leakage of the value. 546 | 547 | ## [](how-does-ash-cloak-work.html#add-preparation-and-change)Add `preparation` and `change` 548 | 549 | Finally, it add a `preparation` and a `change` that will automatically load the corresponding calculations for any attribute in the `decrypt_by_default` list. 550 | 551 | ## [](how-does-ash-cloak-work.html#the-result)The result 552 | 553 | The cloaked attribute will now seamlessly encrypt when writing and decrypt on request. 554 | 555 | [← Previous Page Get Started with AshCloak](getting-started-with-ash-cloak.html) 556 | 557 | [Next Page → DSL: AshCloak](dsl-ashcloak.html) 558 | 559 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) ([current file](https://preview.hex.pm/preview/ash_cloak/0.1.2/show/documentation/topics/how-does-ash-cloak-work.md)) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version") 560 | 561 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 562 | [ash\_cloak](https://github.com/ash-project/ash_cloak) 563 | 564 | v0.1.2 565 | 566 | - GUIDES 567 | - Modules 568 | 569 | 570 | 571 | 572 | 573 | Search documentation of ash\_cloak 574 | 575 | Settings 576 | 577 | # [View Source](https://github.com/ash-project/ash_cloak/blob/v0.1.2/README.md#L1 "View Source") Home 578 | 579 | ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-black-text.png?raw=true#gh-light-mode-only) ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-white-text.png?raw=true#gh-dark-mode-only) 580 | 581 | ![Elixir CI](https://github.com/ash-project/ash_cloak/workflows/CI/badge.svg) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Hex version badge](https://img.shields.io/hexpm/v/ash_cloak.svg)](https://hex.pm/packages/ash_cloak) [![Hexdocs badge](https://img.shields.io/badge/docs-hexdocs-purple)](../ash_cloak.html) 582 | 583 | # AshCloak 584 | 585 | AshCloak is an [Ash](../ash.html) extension to seamlessly encrypt and decrypt attributes of your resources. 586 | 587 | Encrypting attributes ensures that sensitive fields are not stored in the clear in your data layer. 588 | 589 | AshCloak can be used with any Ash data layer. 590 | 591 | It's recommended to use [Cloak](https://github.com/danielberkompas/cloak) as a vault implementation but you could also build your own. 592 | 593 | ## [](readme.html#tutorials)Tutorials 594 | 595 | - [Get Started with AshCloak](getting-started-with-ash-cloak.html) 596 | 597 | ## [](readme.html#topics)Topics 598 | 599 | - [How does AshCloak work?](how-does-ash-cloak-work.html) 600 | 601 | ## [](readme.html#reference)Reference 602 | 603 | - [AshCloak DSL](dsl-ashcloak.html) 604 | 605 | [← Previous Page API Reference](api-reference.html) 606 | 607 | [Next Page → Get Started with AshCloak](getting-started-with-ash-cloak.html) 608 | 609 | [Hex Package](https://hex.pm/packages/ash_cloak/0.1.2) [Hex Preview](https://preview.hex.pm/preview/ash_cloak/0.1.2) ([current file](https://preview.hex.pm/preview/ash_cloak/0.1.2/show/README.md)) Search HexDocs [Download ePub version](ash_cloak.epub "ePub version") 610 | 611 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 612 | -------------------------------------------------------------------------------- /test/docs/ash-docs/ash_archival.md: -------------------------------------------------------------------------------- 1 | [ash\_archival](https://github.com/ash-project/ash_archival) 2 | 3 | v1.0.4 4 | 5 | - Pages 6 | - Modules 7 | 8 | 9 | 10 | 11 | 12 | Search documentation of ash\_archival 13 | 14 | Settings 15 | 16 | # [View Source](https://github.com/ash-project/ash_archival "View Source") API Reference ash\_archival v1.0.4 17 | 18 | ## [](api-reference.html#modules)Modules 19 | 20 | [AshArchival](AshArchival.html) 21 | 22 | An Archival extension for [`Ash.Resource`](../ash/3.4.23/Ash.Resource.html) 23 | 24 | [AshArchival.Resource](AshArchival.Resource.html) 25 | 26 | Configures a resource to be archived instead of destroyed for all destroy actions. 27 | 28 | [AshArchival.Resource.Info](AshArchival.Resource.Info.html) 29 | 30 | Introspection helpers for [`AshArchival.Resource`](AshArchival.Resource.html) 31 | 32 | [Next Page → Home](readme.html) 33 | 34 | [Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) Search HexDocs [Download ePub version](ash_archival.epub "ePub version") 35 | 36 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 37 | [ash\_archival](https://github.com/ash-project/ash_archival) 38 | 39 | v1.0.4 40 | 41 | - Pages 42 | - Modules 43 | 44 | 45 | 46 | 47 | 48 | Search documentation of ash\_archival 49 | 50 | Settings 51 | 52 | # [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival.ex#L1 "View Source") AshArchival (ash\_archival v1.0.4) 53 | 54 | An Archival extension for [`Ash.Resource`](../ash/3.4.23/Ash.Resource.html) 55 | 56 | [Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) Search HexDocs [Download ePub version](ash_archival.epub "ePub version") 57 | 58 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 59 | [ash\_archival](https://github.com/ash-project/ash_archival) 60 | 61 | v1.0.4 62 | 63 | - Pages 64 | - Modules 65 | 66 | 67 | 68 | 69 | 70 | Search documentation of ash\_archival 71 | 72 | Settings 73 | 74 | # [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/resource.ex#L1 "View Source") AshArchival.Resource (ash\_archival v1.0.4) 75 | 76 | Configures a resource to be archived instead of destroyed for all destroy actions. 77 | 78 | For more information, see [the getting started guide](get-started-with-ash-archival.html) 79 | 80 | # [](AshArchival.Resource.html#summary)Summary 81 | 82 | ## [Functions](AshArchival.Resource.html#functions) 83 | 84 | [archive(body)](AshArchival.Resource.html#archive/1) 85 | 86 | # [](AshArchival.Resource.html#functions)Functions 87 | 88 | [Link to this macro](AshArchival.Resource.html#archive/1 "Link to this macro") 89 | 90 | # archive(body) 91 | 92 | [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/resource.ex#L60 "View Source") (macro) 93 | 94 | [Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) Search HexDocs [Download ePub version](ash_archival.epub "ePub version") 95 | 96 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 97 | [ash\_archival](https://github.com/ash-project/ash_archival) 98 | 99 | v1.0.4 100 | 101 | - Pages 102 | - Modules 103 | 104 | 105 | 106 | 107 | 108 | Search documentation of ash\_archival 109 | 110 | Settings 111 | 112 | # [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L1 "View Source") AshArchival.Resource.Info (ash\_archival v1.0.4) 113 | 114 | Introspection helpers for [`AshArchival.Resource`](AshArchival.Resource.html) 115 | 116 | # [](AshArchival.Resource.Info.html#summary)Summary 117 | 118 | ## [Functions](AshArchival.Resource.Info.html#functions) 119 | 120 | [archive\_archive\_related(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_archive_related/1) 121 | 122 | A list of relationships that should have all related items archived when this is archived. Notifications are not sent for this operation. 123 | 124 | [archive\_archive\_related!(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_archive_related!/1) 125 | 126 | A list of relationships that should have all related items archived when this is archived. Notifications are not sent for this operation. 127 | 128 | [archive\_attribute(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_attribute/1) 129 | 130 | The attribute in which to store the archival flag (the current datetime). 131 | 132 | [archive\_attribute!(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_attribute!/1) 133 | 134 | The attribute in which to store the archival flag (the current datetime). 135 | 136 | [archive\_attribute\_type(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_attribute_type/1) 137 | 138 | The attribute type. 139 | 140 | [archive\_attribute\_type!(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_attribute_type!/1) 141 | 142 | The attribute type. 143 | 144 | [archive\_base\_filter?(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_base_filter?/1) 145 | 146 | Whether or not a base filter exists that applies the `is_nil(archived_at)` rule. 147 | 148 | [archive\_exclude\_destroy\_actions(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_exclude_destroy_actions/1) 149 | 150 | A destroy action or actions that should *not* archive, but instead be left alone. This allows for having a destroy *or* archive pattern. 151 | 152 | [archive\_exclude\_destroy\_actions!(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_exclude_destroy_actions!/1) 153 | 154 | A destroy action or actions that should *not* archive, but instead be left alone. This allows for having a destroy *or* archive pattern. 155 | 156 | [archive\_exclude\_read\_actions(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_exclude_read_actions/1) 157 | 158 | A read action or actions that should show archived items. They will not get the automatic `is_nil(archived_at)` filter. 159 | 160 | [archive\_exclude\_read\_actions!(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_exclude_read_actions!/1) 161 | 162 | A read action or actions that should show archived items. They will not get the automatic `is_nil(archived_at)` filter. 163 | 164 | [archive\_exclude\_upsert\_actions(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_exclude_upsert_actions/1) 165 | 166 | This option is deprecated as it no longer has any effect. Upserts are handled according to the upsert identity. See the upserts guide for more. 167 | 168 | [archive\_exclude\_upsert\_actions!(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_exclude_upsert_actions!/1) 169 | 170 | This option is deprecated as it no longer has any effect. Upserts are handled according to the upsert identity. See the upserts guide for more. 171 | 172 | [archive\_options(dsl\_or\_extended)](AshArchival.Resource.Info.html#archive_options/1) 173 | 174 | archive DSL options 175 | 176 | # [](AshArchival.Resource.Info.html#functions)Functions 177 | 178 | [Link to this function](AshArchival.Resource.Info.html#archive_archive_related/1 "Link to this function") 179 | 180 | # archive\_archive\_related(dsl\_or\_extended) 181 | 182 | [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source") 183 | 184 | ``` 185 | @spec archive_archive_related(dsl_or_extended :: module() | map()) :: 186 | {:ok, [atom()]} | :error 187 | ``` 188 | 189 | A list of relationships that should have all related items archived when this is archived. Notifications are not sent for this operation. 190 | 191 | [Link to this function](AshArchival.Resource.Info.html#archive_archive_related!/1 "Link to this function") 192 | 193 | # archive\_archive\_related!(dsl\_or\_extended) 194 | 195 | [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source") 196 | 197 | ``` 198 | @spec archive_archive_related!(dsl_or_extended :: module() | map()) :: 199 | [atom()] | no_return() 200 | ``` 201 | 202 | A list of relationships that should have all related items archived when this is archived. Notifications are not sent for this operation. 203 | 204 | [Link to this function](AshArchival.Resource.Info.html#archive_attribute/1 "Link to this function") 205 | 206 | # archive\_attribute(dsl\_or\_extended) 207 | 208 | [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source") 209 | 210 | ``` 211 | @spec archive_attribute(dsl_or_extended :: module() | map()) :: {:ok, atom()} | :error 212 | ``` 213 | 214 | The attribute in which to store the archival flag (the current datetime). 215 | 216 | [Link to this function](AshArchival.Resource.Info.html#archive_attribute!/1 "Link to this function") 217 | 218 | # archive\_attribute!(dsl\_or\_extended) 219 | 220 | [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source") 221 | 222 | ``` 223 | @spec archive_attribute!(dsl_or_extended :: module() | map()) :: atom() | no_return() 224 | ``` 225 | 226 | The attribute in which to store the archival flag (the current datetime). 227 | 228 | [Link to this function](AshArchival.Resource.Info.html#archive_attribute_type/1 "Link to this function") 229 | 230 | # archive\_attribute\_type(dsl\_or\_extended) 231 | 232 | [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source") 233 | 234 | ``` 235 | @spec archive_attribute_type(dsl_or_extended :: module() | map()) :: 236 | {:ok, atom()} | :error 237 | ``` 238 | 239 | The attribute type. 240 | 241 | [Link to this function](AshArchival.Resource.Info.html#archive_attribute_type!/1 "Link to this function") 242 | 243 | # archive\_attribute\_type!(dsl\_or\_extended) 244 | 245 | [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source") 246 | 247 | ``` 248 | @spec archive_attribute_type!(dsl_or_extended :: module() | map()) :: 249 | atom() | no_return() 250 | ``` 251 | 252 | The attribute type. 253 | 254 | [Link to this function](AshArchival.Resource.Info.html#archive_base_filter?/1 "Link to this function") 255 | 256 | # archive\_base\_filter?(dsl\_or\_extended) 257 | 258 | [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source") 259 | 260 | ``` 261 | @spec archive_base_filter?(dsl_or_extended :: module() | map()) :: atom() 262 | ``` 263 | 264 | Whether or not a base filter exists that applies the `is_nil(archived_at)` rule. 265 | 266 | [Link to this function](AshArchival.Resource.Info.html#archive_exclude_destroy_actions/1 "Link to this function") 267 | 268 | # archive\_exclude\_destroy\_actions(dsl\_or\_extended) 269 | 270 | [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source") 271 | 272 | ``` 273 | @spec archive_exclude_destroy_actions(dsl_or_extended :: module() | map()) :: 274 | {:ok, [atom()]} | :error 275 | ``` 276 | 277 | A destroy action or actions that should *not* archive, but instead be left alone. This allows for having a destroy *or* archive pattern. 278 | 279 | [Link to this function](AshArchival.Resource.Info.html#archive_exclude_destroy_actions!/1 "Link to this function") 280 | 281 | # archive\_exclude\_destroy\_actions!(dsl\_or\_extended) 282 | 283 | [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source") 284 | 285 | ``` 286 | @spec archive_exclude_destroy_actions!(dsl_or_extended :: module() | map()) :: 287 | [atom()] | no_return() 288 | ``` 289 | 290 | A destroy action or actions that should *not* archive, but instead be left alone. This allows for having a destroy *or* archive pattern. 291 | 292 | [Link to this function](AshArchival.Resource.Info.html#archive_exclude_read_actions/1 "Link to this function") 293 | 294 | # archive\_exclude\_read\_actions(dsl\_or\_extended) 295 | 296 | [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source") 297 | 298 | ``` 299 | @spec archive_exclude_read_actions(dsl_or_extended :: module() | map()) :: 300 | {:ok, [atom()]} | :error 301 | ``` 302 | 303 | A read action or actions that should show archived items. They will not get the automatic `is_nil(archived_at)` filter. 304 | 305 | [Link to this function](AshArchival.Resource.Info.html#archive_exclude_read_actions!/1 "Link to this function") 306 | 307 | # archive\_exclude\_read\_actions!(dsl\_or\_extended) 308 | 309 | [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source") 310 | 311 | ``` 312 | @spec archive_exclude_read_actions!(dsl_or_extended :: module() | map()) :: 313 | [atom()] | no_return() 314 | ``` 315 | 316 | A read action or actions that should show archived items. They will not get the automatic `is_nil(archived_at)` filter. 317 | 318 | [Link to this function](AshArchival.Resource.Info.html#archive_exclude_upsert_actions/1 "Link to this function") 319 | 320 | # archive\_exclude\_upsert\_actions(dsl\_or\_extended) 321 | 322 | [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source") 323 | 324 | ``` 325 | @spec archive_exclude_upsert_actions(dsl_or_extended :: module() | map()) :: 326 | {:ok, [atom()]} | :error 327 | ``` 328 | 329 | This option is deprecated as it no longer has any effect. Upserts are handled according to the upsert identity. See the upserts guide for more. 330 | 331 | [Link to this function](AshArchival.Resource.Info.html#archive_exclude_upsert_actions!/1 "Link to this function") 332 | 333 | # archive\_exclude\_upsert\_actions!(dsl\_or\_extended) 334 | 335 | [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source") 336 | 337 | ``` 338 | @spec archive_exclude_upsert_actions!(dsl_or_extended :: module() | map()) :: 339 | [atom()] | no_return() 340 | ``` 341 | 342 | This option is deprecated as it no longer has any effect. Upserts are handled according to the upsert identity. See the upserts guide for more. 343 | 344 | [Link to this function](AshArchival.Resource.Info.html#archive_options/1 "Link to this function") 345 | 346 | # archive\_options(dsl\_or\_extended) 347 | 348 | [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/lib/ash_archival/resource/info.ex#L3 "View Source") 349 | 350 | ``` 351 | @spec archive_options(dsl_or_extended :: module() | map()) :: %{ 352 | required(atom()) => any() 353 | } 354 | ``` 355 | 356 | archive DSL options 357 | 358 | Returns a map containing the and any configured or default values. 359 | 360 | [Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) Search HexDocs [Download ePub version](ash_archival.epub "ePub version") 361 | 362 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 363 | [ash\_archival](https://github.com/ash-project/ash_archival) 364 | 365 | v1.0.4 366 | 367 | - Pages 368 | - Modules 369 | 370 | 371 | 372 | 373 | 374 | Search documentation of ash\_archival 375 | 376 | Settings 377 | 378 | # [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/documentation/dsls/DSL:-AshArchival.Resource.md#L1 "View Source") DSL: AshArchival.Resource 379 | 380 | Configures a resource to be archived instead of destroyed for all destroy actions. 381 | 382 | For more information, see [the getting started guide](get-started-with-ash-archival.html) 383 | 384 | ## [](dsl-asharchival-resource.html#archive)archive 385 | 386 | A section for configuring how archival is configured for a resource. 387 | 388 | ### [](dsl-asharchival-resource.html#options)Options 389 | 390 | NameTypeDefaultDocs[`attribute`](dsl-asharchival-resource.html#archive-attribute)`atom``:archived_at`The attribute in which to store the archival flag (the current datetime).[`attribute_type`](dsl-asharchival-resource.html#archive-attribute_type)`atom``:utc_datetime_usec`The attribute type.[`base_filter?`](dsl-asharchival-resource.html#archive-base_filter?)`atom``false`Whether or not a base filter exists that applies the `is_nil(archived_at)` rule.[`exclude_read_actions`](dsl-asharchival-resource.html#archive-exclude_read_actions)`atom | list(atom)``[]`A read action or actions that should show archived items. They will not get the automatic `is_nil(archived_at)` filter.[`exclude_upsert_actions`](dsl-asharchival-resource.html#archive-exclude_upsert_actions)`atom | list(atom)``[]`This option is deprecated as it no longer has any effect. Upserts are handled according to the upsert identity. See the upserts guide for more.[`exclude_destroy_actions`](dsl-asharchival-resource.html#archive-exclude_destroy_actions)`atom | list(atom)``[]`A destroy action or actions that should *not* archive, but instead be left alone. This allows for having a destroy *or* archive pattern.[`archive_related`](dsl-asharchival-resource.html#archive-archive_related)`list(atom)``[]`A list of relationships that should have all related items archived when this is archived. Notifications are not sent for this operation. 391 | 392 | [← Previous Page Upserts & Identities](upserts-and-identities.html) 393 | 394 | [Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) ([current file](https://preview.hex.pm/preview/ash_archival/1.0.4/show/documentation/dsls/DSL:-AshArchival.Resource.md)) Search HexDocs [Download ePub version](ash_archival.epub "ePub version") 395 | 396 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 397 | [ash\_archival](https://github.com/ash-project/ash_archival) 398 | 399 | v1.0.4 400 | 401 | - Pages 402 | - Modules 403 | 404 | 405 | 406 | 407 | 408 | Search documentation of ash\_archival 409 | 410 | Settings 411 | 412 | # [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/documentation/tutorials/get-started-with-ash-archival.md#L1 "View Source") Get Started with AshArchival 413 | 414 | ## [](get-started-with-ash-archival.html#installation)Installation 415 | 416 | First, add the dependency to your `mix.exs` file 417 | 418 | ``` 419 | {:ash_archival, "~> 1.0.4"} 420 | ``` 421 | 422 | and add `:ash_archival` to your `.formatter.exs` 423 | 424 | ``` 425 | import_deps: [..., :ash_archival] 426 | ``` 427 | 428 | ## [](get-started-with-ash-archival.html#adding-to-a-resource)Adding to a resource 429 | 430 | To add archival to a resource, add the extension to the resource: 431 | 432 | ``` 433 | use Ash.Resource, 434 | extensions: [..., AshArchival.Resource] 435 | ``` 436 | 437 | And thats it! Now, when you destroy a record, it will be archived instead, using an `archived_at` attribute. 438 | 439 | See [How Does Ash Archival Work?](get-started-with-ash-archival.html) for what modifications are made to a resource, and read on for info on the tradeoffs of leveraging `Ash.Resource.Dsl.resource.base_filter`. 440 | 441 | ## [](get-started-with-ash-archival.html#base-filter)Base Filter 442 | 443 | Using a `Ash.Resource.Dsl.resource.base_filter` for your `archived_at` field has a lot of benefits if you are using `ash_postgres`, but comes with one major drawback, which is that it is not possible to exclude certain read actions from archival. If you wish to use a base filter, you will need to create a separate resource to read from the archived items. We may introduce a way to bypass the base filter at some point in the future. 444 | 445 | To add a `base_filter` and `base_filter_sql` to your resource: 446 | 447 | ``` 448 | resource do 449 | base_filter expr(is_nil(archived_at)) 450 | end 451 | 452 | postgres do 453 | ... 454 | base_filter_sql "(archived_at IS NULL)" 455 | end 456 | ``` 457 | 458 | Add `base_filter? true` to the `archive` configuration of your resource to tell it that it doesn't need to add the filter itself. 459 | 460 | ### [](get-started-with-ash-archival.html#benefits-of-base_filter)Benefits of base\_filter 461 | 462 | 1. unique indexes will exclude archived items 463 | 2. custom indexes will exclude archived items 464 | 3. check constraints will not be applied to archived items 465 | 466 | If you want these benefits, add the appropriate `base_filter`. 467 | 468 | ## [](get-started-with-ash-archival.html#more)More 469 | 470 | See the [Unarchiving guide](unarchiving.html) For more. 471 | 472 | [← Previous Page Change Log](changelog.html) 473 | 474 | [Next Page → Un-archiving](unarchiving.html) 475 | 476 | [Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) ([current file](https://preview.hex.pm/preview/ash_archival/1.0.4/show/documentation/tutorials/get-started-with-ash-archival.md)) Search HexDocs [Download ePub version](ash_archival.epub "ePub version") 477 | 478 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 479 | [ash\_archival](https://github.com/ash-project/ash_archival) 480 | 481 | v1.0.4 482 | 483 | - Pages 484 | - Modules 485 | 486 | 487 | 488 | 489 | 490 | Search documentation of ash\_archival 491 | 492 | Settings 493 | 494 | # [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/documentation/topics/how-does-ash-archival-work.md#L1 "View Source") How does Archival Work? 495 | 496 | We make modifications to the resource to enable soft deletes. Here's a breakdown of what the extension does: 497 | 498 | ## [](how-does-ash-archival-work.html#resource-modifications)Resource Modifications 499 | 500 | 1. Adds a private `archived_at` `utc_datetime_usec` attribute. 501 | 2. Adds a preparation that filters each action for `is_nil(archived_at)` (except for excluded actions, or if you have `base_filter?` set to `true`). 502 | 3. Marks all destroy actions as `soft?`, turning them into updates (except for excluded actions) 503 | 4. Adds a change to all destroy actions that sets `archived_at` to the current timestamp 504 | 5. Adds a change that will iteratively load and destroy anything configured in `AshArchival.Resource.archive.archive_related` 505 | 506 | [← Previous Page Un-archiving](unarchiving.html) 507 | 508 | [Next Page → Upserts & Identities](upserts-and-identities.html) 509 | 510 | [Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) ([current file](https://preview.hex.pm/preview/ash_archival/1.0.4/show/documentation/topics/how-does-ash-archival-work.md)) Search HexDocs [Download ePub version](ash_archival.epub "ePub version") 511 | 512 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 513 | [ash\_archival](https://github.com/ash-project/ash_archival) 514 | 515 | v1.0.4 516 | 517 | - Pages 518 | - Modules 519 | 520 | 521 | 522 | 523 | 524 | Search documentation of ash\_archival 525 | 526 | Settings 527 | 528 | # [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/README.md#L1 "View Source") Home 529 | 530 | ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-black-text.png?raw=true#gh-light-mode-only) ![Logo](https://github.com/ash-project/ash/blob/main/logos/cropped-for-header-white-text.png?raw=true#gh-dark-mode-only) 531 | 532 | ![Elixir CI](https://github.com/ash-project/ash_archival/workflows/CI/badge.svg) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Hex version badge](https://img.shields.io/hexpm/v/ash_archival.svg)](https://hex.pm/packages/ash_archival) [![Hexdocs badge](https://img.shields.io/badge/docs-hexdocs-purple)](../ash_archival.html) 533 | 534 | # AshArchival 535 | 536 | AshArchival is an [Ash](../ash.html) extension that provides a push-button solution for soft deleting records, instead of destroying them. 537 | 538 | ## [](readme.html#tutorials)Tutorials 539 | 540 | - [Get Started with AshArchival](get-started-with-ash-archival.html) 541 | 542 | ## [](readme.html#topics)Topics 543 | 544 | - [How does AshArchival work?](how-does-ash-archival-work.html) 545 | - [Unarchiving](unarchiving.html) 546 | 547 | ## [](readme.html#reference)Reference 548 | 549 | - [AshArchival DSL](dsl-asharchival-resource.html) 550 | 551 | [← Previous Page API Reference](api-reference.html) 552 | 553 | [Next Page → Change Log](changelog.html) 554 | 555 | [Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) ([current file](https://preview.hex.pm/preview/ash_archival/1.0.4/show/README.md)) Search HexDocs [Download ePub version](ash_archival.epub "ePub version") 556 | 557 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 558 | [ash\_archival](https://github.com/ash-project/ash_archival) 559 | 560 | v1.0.4 561 | 562 | - Pages 563 | - Modules 564 | 565 | 566 | 567 | 568 | 569 | Search documentation of ash\_archival 570 | 571 | Settings 572 | 573 | # [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/documentation/topics/unarchiving.md#L1 "View Source") Un-archiving 574 | 575 | If you want to unarchive a resource that uses a base filter, you will need to define a separate resource that uses the same storage and has no base filter. The rest of this guide applies for folks who *aren't* using a `base_filter`. 576 | 577 | Un-archiving can be accomplished by creating a read action that is skipped, using `exclude_read_actions`. Then, you can create an update action that sets that attribute to `nil`. For example: 578 | 579 | ``` 580 | archive do 581 | ... 582 | exclude_read_actions :archived 583 | end 584 | 585 | actions do 586 | read :archived do 587 | filter expr(not is_nil(archived_at)) 588 | end 589 | 590 | update :unarchive do 591 | change set_attribute(:archived_at, nil) 592 | # if an individual record is used to unarchive 593 | # it must use the `archived` read action for its atomic upgrade 594 | atomic_upgrade_with :archived 595 | end 596 | end 597 | ``` 598 | 599 | You could then do something like this: 600 | 601 | ``` 602 | Resource 603 | |> Ash.get!(id, action: :archived) 604 | |> Ash.Changeset.for_update(:unarchive, %{}) 605 | |> Ash.update!() 606 | ``` 607 | 608 | More idiomatically, you would define a code interfaceon the domain, and call that: 609 | 610 | ``` 611 | # to unarchive by `id` 612 | Resource 613 | |> Ash.Query.for_read(:archived, %{}) 614 | |> Ash.Query.filter(id == ^id) 615 | |> Domain.unarchive!() 616 | ``` 617 | 618 | [← Previous Page Get Started with AshArchival](get-started-with-ash-archival.html) 619 | 620 | [Next Page → How does Archival Work?](how-does-ash-archival-work.html) 621 | 622 | [Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) ([current file](https://preview.hex.pm/preview/ash_archival/1.0.4/show/documentation/topics/unarchiving.md)) Search HexDocs [Download ePub version](ash_archival.epub "ePub version") 623 | 624 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 625 | [ash\_archival](https://github.com/ash-project/ash_archival) 626 | 627 | v1.0.4 628 | 629 | - Pages 630 | - Modules 631 | 632 | 633 | 634 | 635 | 636 | Search documentation of ash\_archival 637 | 638 | Settings 639 | 640 | # [View Source](https://github.com/ash-project/ash_archival/blob/v1.0.4/documentation/topics/upserts-and-identities.md#L1 "View Source") Upserts & Identities 641 | 642 | Its important to consider identities when using AshArchival *without* a `base_filter` set up. 643 | 644 | If you are using a `base_filter`, then all identities implicitly include that `base_filter` in their `where` (handled by the data layer). 645 | 646 | Take the following identities, for example: 647 | 648 | ``` 649 | identities do 650 | identity :unique_email, [:email], where: expr(is_nil(archived_at)) 651 | # and 652 | identity :unique_email, [:email] 653 | end 654 | ``` 655 | 656 | ## [](upserts-and-identities.html#with-is_nil-archived_at)With `is_nil(archived_at)` 657 | 658 | Using this identity allows multiple archived records with the same email, but only one *non-archived* record per email. It enables reuse of archived email addresses for new active records, maintaining data integrity by preventing duplicate active records while preserving archived data. 659 | 660 | When you upsert a record using this identity, it will only consider active records. 661 | 662 | ## [](upserts-and-identities.html#without-is_nil-archived_at)Without `is_nil(archived_at)` 663 | 664 | This identity configuration enforces strict email uniqueness across all records. Once an email is used, it can't be used again, even after that record is archived. 665 | 666 | When you upsert a record using this identity, it will consider all records. 667 | 668 | [← Previous Page How does Archival Work?](how-does-ash-archival-work.html) 669 | 670 | [Next Page → DSL: AshArchival.Resource](dsl-asharchival-resource.html) 671 | 672 | [Hex Package](https://hex.pm/packages/ash_archival/1.0.4) [Hex Preview](https://preview.hex.pm/preview/ash_archival/1.0.4) ([current file](https://preview.hex.pm/preview/ash_archival/1.0.4/show/documentation/topics/upserts-and-identities.md)) Search HexDocs [Download ePub version](ash_archival.epub "ePub version") 673 | 674 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.31.1) for the [Elixir programming language](https://elixir-lang.org "Elixir") 675 | -------------------------------------------------------------------------------- /test/docs/ash-docs/ash_cubdb.md: -------------------------------------------------------------------------------- 1 | [ash\_cubdb](https://harton.dev/james/ash_cubdb) 2 | 3 | v0.6.2 4 | 5 | - Guides 6 | - Modules 7 | 8 | 9 | 10 | 11 | 12 | Search documentation of ash\_cubdb 13 | 14 | Settings 15 | 16 | # [View Source](https://harton.dev/james/ash_cubdb "View Source") API Reference ash\_cubdb v0.6.2 17 | 18 | ## [](api-reference.html#modules)Modules 19 | 20 | [AshCubDB](AshCubDB.html) 21 | 22 | [`AshCubDB`](AshCubDB.html#content) is an [Ash DataLayer](https://ash-hq.org/docs/module/ash/latest/ash-datalayer) which adds support for persisting Ash resources with [CubDB](https://hex.pm/packages/cubdb). 23 | 24 | [AshCubDB.DataLayer](AshCubDB.DataLayer.html) 25 | 26 | A CubDB data layer for Ash. 27 | 28 | [AshCubDB.Dir](AshCubDB.Dir.html) 29 | 30 | Utilities for working with the underlying data directory. 31 | 32 | [AshCubDB.Info](AshCubDB.Info.html) 33 | 34 | Auto-generated introspection for the AshCubDB DSL. 35 | 36 | [AshCubDB.Migration](AshCubDB.Migration.html) 37 | 38 | We store and check metadata when opening a database to ensure that the resource and attributes match, and possibly perform migrations. 39 | 40 | [AshCubDB.Query](AshCubDB.Query.html) 41 | 42 | A struct which holds information about a resource query as it is being built. 43 | 44 | [AshCubDB.Serde](AshCubDB.Serde.html) 45 | 46 | Handle serialising and deserialising of records into CubDB. 47 | 48 | [Next Page → AshCubDB](readme.html) 49 | 50 | [Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs 51 | 52 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 53 | [ash\_cubdb](https://harton.dev/james/ash_cubdb) 54 | 55 | v0.6.2 56 | 57 | - Guides 58 | - Modules 59 | 60 | 61 | 62 | 63 | 64 | Search documentation of ash\_cubdb 65 | 66 | Settings 67 | 68 | # [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/data_layer.ex#L1 "View Source") AshCubDB.DataLayer (ash\_cubdb v0.6.2) 69 | 70 | A CubDB data layer for Ash. 71 | 72 | ## [](AshCubDB.DataLayer.html#module-dsl-documentation)DSL Documentation 73 | 74 | ### [](AshCubDB.DataLayer.html#module-index)Index 75 | 76 | - cubdb 77 | 78 | ### [](AshCubDB.DataLayer.html#module-docs)Docs 79 | 80 | ## [](AshCubDB.DataLayer.html#module-cubdb)cubdb 81 | 82 | CubDB data layer configuration. 83 | 84 | Examples: 85 | 86 | ``` 87 | cubdb do 88 | directory "/opt/storage/my_awesome_resource" 89 | auto_compact? true 90 | auto_file_sync? true 91 | name :my_awesome_resource 92 | end 93 | ``` 94 | 95 | * * * 96 | 97 | - `:directory` - The directory within which to store the CubDB data. 98 | If none is supplied, then one will be automatically generated in the `priv` directory of the parent OTP application. 99 | - `:otp_app` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The OTP application in whose `priv` directory data should be stored. 100 | Only used if `directory` is not supplied. When not provided [`Application.get_application/1`](../elixir/Application.html#get_application/1) will be called for the resource. 101 | - `:auto_compact?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Whether or not to automatically compact the CubDB database. 102 | See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-is-compaction) for more information. The default value is `true`. 103 | - `:auto_file_sync?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Whether or not to automatically flush the buffer to disk on write. 104 | See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-does-file-sync-mean) The default value is `true`. 105 | - `:name` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The name of the CubDB database. 106 | By default this is the name of the resource module, however in some (rare) circumstances you may wish to specifically name the database. 107 | 108 | [Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs 109 | 110 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 111 | [ash\_cubdb](https://harton.dev/james/ash_cubdb) 112 | 113 | v0.6.2 114 | 115 | - Guides 116 | - Modules 117 | 118 | 119 | 120 | 121 | 122 | Search documentation of ash\_cubdb 123 | 124 | Settings 125 | 126 | # [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/dir.ex#L1 "View Source") AshCubDB.Dir (ash\_cubdb v0.6.2) 127 | 128 | Utilities for working with the underlying data directory. 129 | 130 | # [](AshCubDB.Dir.html#summary)Summary 131 | 132 | ## [Functions](AshCubDB.Dir.html#functions) 133 | 134 | [readable?(resource)](AshCubDB.Dir.html#readable?/1) 135 | 136 | Is the directory able to be read from by the current user? 137 | 138 | [writable?(resource)](AshCubDB.Dir.html#writable?/1) 139 | 140 | Is the directory able to be written to by the current user? 141 | 142 | # [](AshCubDB.Dir.html#functions)Functions 143 | 144 | [Link to this function](AshCubDB.Dir.html#readable?/1 "Link to this function") 145 | 146 | # readable?(resource) 147 | 148 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/dir.ex#L23 "View Source") 149 | 150 | Is the directory able to be read from by the current user? 151 | 152 | [Link to this function](AshCubDB.Dir.html#writable?/1 "Link to this function") 153 | 154 | # writable?(resource) 155 | 156 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/dir.ex#L11 "View Source") 157 | 158 | Is the directory able to be written to by the current user? 159 | 160 | [Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs 161 | 162 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 163 | [ash\_cubdb](https://harton.dev/james/ash_cubdb) 164 | 165 | v0.6.2 166 | 167 | - Guides 168 | - Modules 169 | 170 | 171 | 172 | 173 | 174 | Search documentation of ash\_cubdb 175 | 176 | Settings 177 | 178 | # [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L1 "View Source") AshCubDB (ash\_cubdb v0.6.2) 179 | 180 | [`AshCubDB`](AshCubDB.html#content) is an [Ash DataLayer](https://ash-hq.org/docs/module/ash/latest/ash-datalayer) which adds support for persisting Ash resources with [CubDB](https://hex.pm/packages/cubdb). 181 | 182 | CubDB is an Elixir-based key value store which supports all Erlang-native terms. More information can be found in [the CubDB readme](https://hexdocs.pm/cubdb/readme.html). 183 | 184 | # [](AshCubDB.html#summary)Summary 185 | 186 | ## [Functions](AshCubDB.html#functions) 187 | 188 | [back\_up(resource, target\_path)](AshCubDB.html#back_up/2) 189 | 190 | Creates a backup of the database into the target directory path. 191 | 192 | [clear(resource)](AshCubDB.html#clear/1) 193 | 194 | Deletes all entries, resulting in an empty database. 195 | 196 | [compact(resource)](AshCubDB.html#compact/1) 197 | 198 | Runs a database compaction. 199 | 200 | [compacting?(resource)](AshCubDB.html#compacting?/1) 201 | 202 | Returns true if a compaction operation is currently running, false otherwise. 203 | 204 | [current\_db\_file(resource)](AshCubDB.html#current_db_file/1) 205 | 206 | Returns the path of the current database file. 207 | 208 | [data\_dir(resource)](AshCubDB.html#data_dir/1) 209 | 210 | Returns the path of the data directory, as given when the [`CubDB`](../cubdb/2.0.2/CubDB.html) process was started. 211 | 212 | [dirt\_factor(resource)](AshCubDB.html#dirt_factor/1) 213 | 214 | Returns the dirt factor. 215 | 216 | [file\_sync(resource)](AshCubDB.html#file_sync/1) 217 | 218 | Performs an `fsync`, forcing to flush all data that might be buffered by the OS to disk. 219 | 220 | [halt\_compaction(resource)](AshCubDB.html#halt_compaction/1) 221 | 222 | Stops a running compaction. 223 | 224 | [start(resource)](AshCubDB.html#start/1) 225 | 226 | Ensure that the CubDB process is running for the specified resource. 227 | 228 | [stop(resource)](AshCubDB.html#stop/1) 229 | 230 | Stop the CubDB process running for a specific resource. 231 | 232 | # [](AshCubDB.html#functions)Functions 233 | 234 | [Link to this function](AshCubDB.html#back_up/2 "Link to this function") 235 | 236 | # back\_up(resource, target\_path) 237 | 238 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L54 "View Source") 239 | 240 | ``` 241 | @spec back_up(Ash.Resource.t(), Path.t()) :: :ok | {:error, any()} 242 | ``` 243 | 244 | Creates a backup of the database into the target directory path. 245 | 246 | Wrapper around [`CubDB.back_up/2`](../cubdb/2.0.2/CubDB.html#back_up/2) 247 | 248 | [Link to this function](AshCubDB.html#clear/1 "Link to this function") 249 | 250 | # clear(resource) 251 | 252 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L67 "View Source") 253 | 254 | ``` 255 | @spec clear(Ash.Resource.t()) :: :ok 256 | ``` 257 | 258 | Deletes all entries, resulting in an empty database. 259 | 260 | Wrapper around [`CubDB.clear/1`](../cubdb/2.0.2/CubDB.html#clear/1) 261 | 262 | [Link to this function](AshCubDB.html#compact/1 "Link to this function") 263 | 264 | # compact(resource) 265 | 266 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L80 "View Source") 267 | 268 | ``` 269 | @spec compact(Ash.Resource.t()) :: :ok | {:error, any()} 270 | ``` 271 | 272 | Runs a database compaction. 273 | 274 | Wrapper around [`CubDB.compact/1`](../cubdb/2.0.2/CubDB.html#compact/1) 275 | 276 | [Link to this function](AshCubDB.html#compacting?/1 "Link to this function") 277 | 278 | # compacting?(resource) 279 | 280 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L93 "View Source") 281 | 282 | ``` 283 | @spec compacting?(Ash.Resource.t()) :: boolean() 284 | ``` 285 | 286 | Returns true if a compaction operation is currently running, false otherwise. 287 | 288 | Wrapper around [`CubDB.compacting?/1`](../cubdb/2.0.2/CubDB.html#compacting?/1) 289 | 290 | [Link to this function](AshCubDB.html#current_db_file/1 "Link to this function") 291 | 292 | # current\_db\_file(resource) 293 | 294 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L106 "View Source") 295 | 296 | ``` 297 | @spec current_db_file(Ash.Resource.t()) :: String.t() 298 | ``` 299 | 300 | Returns the path of the current database file. 301 | 302 | Wrapper around [`CubDB.current_db_file/1`](../cubdb/2.0.2/CubDB.html#current_db_file/1) 303 | 304 | [Link to this function](AshCubDB.html#data_dir/1 "Link to this function") 305 | 306 | # data\_dir(resource) 307 | 308 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L118 "View Source") 309 | 310 | ``` 311 | @spec data_dir(Ash.Resource.t()) :: String.t() 312 | ``` 313 | 314 | Returns the path of the data directory, as given when the [`CubDB`](../cubdb/2.0.2/CubDB.html) process was started. 315 | 316 | Wrapper around [`CubDB.data_dir/1`](../cubdb/2.0.2/CubDB.html#data_dir/1) 317 | 318 | [Link to this function](AshCubDB.html#dirt_factor/1 "Link to this function") 319 | 320 | # dirt\_factor(resource) 321 | 322 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L130 "View Source") 323 | 324 | ``` 325 | @spec dirt_factor(Ash.Resource.t()) :: float() 326 | ``` 327 | 328 | Returns the dirt factor. 329 | 330 | Wrapper around [`CubDB.dirt_factor/1`](../cubdb/2.0.2/CubDB.html#dirt_factor/1) 331 | 332 | [Link to this function](AshCubDB.html#file_sync/1 "Link to this function") 333 | 334 | # file\_sync(resource) 335 | 336 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L142 "View Source") 337 | 338 | ``` 339 | @spec file_sync(Ash.Resource.t()) :: :ok 340 | ``` 341 | 342 | Performs an `fsync`, forcing to flush all data that might be buffered by the OS to disk. 343 | 344 | Wrapper around [`CubDB.file_sync/1`](../cubdb/2.0.2/CubDB.html#file_sync/1) 345 | 346 | [Link to this function](AshCubDB.html#halt_compaction/1 "Link to this function") 347 | 348 | # halt\_compaction(resource) 349 | 350 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L154 "View Source") 351 | 352 | ``` 353 | @spec halt_compaction(Ash.Resource.t()) :: :ok | {:error, :no_compaction_running} 354 | ``` 355 | 356 | Stops a running compaction. 357 | 358 | Wrapper around [`CubDB.halt_compaction/1`](../cubdb/2.0.2/CubDB.html#halt_compaction/1) 359 | 360 | [Link to this function](AshCubDB.html#start/1 "Link to this function") 361 | 362 | # start(resource) 363 | 364 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L17 "View Source") 365 | 366 | ``` 367 | @spec start(Ash.Resource.t()) :: {:ok, pid()} | {:error, any()} 368 | ``` 369 | 370 | Ensure that the CubDB process is running for the specified resource. 371 | 372 | [Link to this function](AshCubDB.html#stop/1 "Link to this function") 373 | 374 | # stop(resource) 375 | 376 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db.ex#L42 "View Source") 377 | 378 | ``` 379 | @spec stop(Ash.Resource.t()) :: :ok 380 | ``` 381 | 382 | Stop the CubDB process running for a specific resource. 383 | 384 | [Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs 385 | 386 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 387 | [ash\_cubdb](https://harton.dev/james/ash_cubdb) 388 | 389 | v0.6.2 390 | 391 | - Guides 392 | - Modules 393 | 394 | 395 | 396 | 397 | 398 | Search documentation of ash\_cubdb 399 | 400 | Settings 401 | 402 | # [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L1 "View Source") AshCubDB.Info (ash\_cubdb v0.6.2) 403 | 404 | Auto-generated introspection for the AshCubDB DSL. 405 | 406 | # [](AshCubDB.Info.html#summary)Summary 407 | 408 | ## [Functions](AshCubDB.Info.html#functions) 409 | 410 | [cubdb\_auto\_compact?(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_auto_compact?/1) 411 | 412 | Whether or not to automatically compact the CubDB database. 413 | 414 | [cubdb\_auto\_file\_sync?(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_auto_file_sync?/1) 415 | 416 | Whether or not to automatically flush the buffer to disk on write. 417 | 418 | [cubdb\_directory(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_directory/1) 419 | 420 | The directory within which to store the CubDB data. 421 | 422 | [cubdb\_directory!(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_directory!/1) 423 | 424 | The directory within which to store the CubDB data. 425 | 426 | [cubdb\_name(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_name/1) 427 | 428 | The name of the CubDB database. 429 | 430 | [cubdb\_name!(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_name!/1) 431 | 432 | The name of the CubDB database. 433 | 434 | [cubdb\_options(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_options/1) 435 | 436 | cubdb DSL options 437 | 438 | [cubdb\_otp\_app(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_otp_app/1) 439 | 440 | The OTP application in whose `priv` directory data should be stored. 441 | 442 | [cubdb\_otp\_app!(dsl\_or\_extended)](AshCubDB.Info.html#cubdb_otp_app!/1) 443 | 444 | The OTP application in whose `priv` directory data should be stored. 445 | 446 | [field\_layout(resource\_or\_dsl\_state)](AshCubDB.Info.html#field_layout/1) 447 | 448 | Retrieve the cached field layout for the resource. 449 | 450 | # [](AshCubDB.Info.html#functions)Functions 451 | 452 | [Link to this function](AshCubDB.Info.html#cubdb_auto_compact?/1 "Link to this function") 453 | 454 | # cubdb\_auto\_compact?(dsl\_or\_extended) 455 | 456 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source") 457 | 458 | ``` 459 | @spec cubdb_auto_compact?(dsl_or_extended :: module() | map()) :: boolean() 460 | ``` 461 | 462 | Whether or not to automatically compact the CubDB database. 463 | 464 | See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-is-compaction) for more information. 465 | 466 | [Link to this function](AshCubDB.Info.html#cubdb_auto_file_sync?/1 "Link to this function") 467 | 468 | # cubdb\_auto\_file\_sync?(dsl\_or\_extended) 469 | 470 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source") 471 | 472 | ``` 473 | @spec cubdb_auto_file_sync?(dsl_or_extended :: module() | map()) :: boolean() 474 | ``` 475 | 476 | Whether or not to automatically flush the buffer to disk on write. 477 | 478 | See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-does-file-sync-mean) 479 | 480 | [Link to this function](AshCubDB.Info.html#cubdb_directory/1 "Link to this function") 481 | 482 | # cubdb\_directory(dsl\_or\_extended) 483 | 484 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source") 485 | 486 | ``` 487 | @spec cubdb_directory(dsl_or_extended :: module() | map()) :: 488 | {:ok, nil | String.t()} | :error 489 | ``` 490 | 491 | The directory within which to store the CubDB data. 492 | 493 | If none is supplied, then one will be automatically generated in the `priv` directory of the parent OTP application. 494 | 495 | [Link to this function](AshCubDB.Info.html#cubdb_directory!/1 "Link to this function") 496 | 497 | # cubdb\_directory!(dsl\_or\_extended) 498 | 499 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source") 500 | 501 | ``` 502 | @spec cubdb_directory!(dsl_or_extended :: module() | map()) :: 503 | (nil | String.t()) | no_return() 504 | ``` 505 | 506 | The directory within which to store the CubDB data. 507 | 508 | If none is supplied, then one will be automatically generated in the `priv` directory of the parent OTP application. 509 | 510 | [Link to this function](AshCubDB.Info.html#cubdb_name/1 "Link to this function") 511 | 512 | # cubdb\_name(dsl\_or\_extended) 513 | 514 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source") 515 | 516 | ``` 517 | @spec cubdb_name(dsl_or_extended :: module() | map()) :: {:ok, atom()} | :error 518 | ``` 519 | 520 | The name of the CubDB database. 521 | 522 | By default this is the name of the resource module, however in some (rare) circumstances you may wish to specifically name the database. 523 | 524 | [Link to this function](AshCubDB.Info.html#cubdb_name!/1 "Link to this function") 525 | 526 | # cubdb\_name!(dsl\_or\_extended) 527 | 528 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source") 529 | 530 | ``` 531 | @spec cubdb_name!(dsl_or_extended :: module() | map()) :: atom() | no_return() 532 | ``` 533 | 534 | The name of the CubDB database. 535 | 536 | By default this is the name of the resource module, however in some (rare) circumstances you may wish to specifically name the database. 537 | 538 | [Link to this function](AshCubDB.Info.html#cubdb_options/1 "Link to this function") 539 | 540 | # cubdb\_options(dsl\_or\_extended) 541 | 542 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source") 543 | 544 | ``` 545 | @spec cubdb_options(dsl_or_extended :: module() | map()) :: %{ 546 | required(atom()) => any() 547 | } 548 | ``` 549 | 550 | cubdb DSL options 551 | 552 | Returns a map containing the and any configured or default values. 553 | 554 | [Link to this function](AshCubDB.Info.html#cubdb_otp_app/1 "Link to this function") 555 | 556 | # cubdb\_otp\_app(dsl\_or\_extended) 557 | 558 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source") 559 | 560 | ``` 561 | @spec cubdb_otp_app(dsl_or_extended :: module() | map()) :: {:ok, atom()} | :error 562 | ``` 563 | 564 | The OTP application in whose `priv` directory data should be stored. 565 | 566 | Only used if `directory` is not supplied. When not provided [`Application.get_application/1`](../elixir/Application.html#get_application/1) will be called for the resource. 567 | 568 | [Link to this function](AshCubDB.Info.html#cubdb_otp_app!/1 "Link to this function") 569 | 570 | # cubdb\_otp\_app!(dsl\_or\_extended) 571 | 572 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L6 "View Source") 573 | 574 | ``` 575 | @spec cubdb_otp_app!(dsl_or_extended :: module() | map()) :: atom() | no_return() 576 | ``` 577 | 578 | The OTP application in whose `priv` directory data should be stored. 579 | 580 | Only used if `directory` is not supplied. When not provided [`Application.get_application/1`](../elixir/Application.html#get_application/1) will be called for the resource. 581 | 582 | [Link to this function](AshCubDB.Info.html#field_layout/1 "Link to this function") 583 | 584 | # field\_layout(resource\_or\_dsl\_state) 585 | 586 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/info.ex#L13 "View Source") 587 | 588 | ``` 589 | @spec field_layout(Ash.Resource.t() | Spark.Dsl.t()) :: nil | {tuple(), tuple()} 590 | ``` 591 | 592 | Retrieve the cached field layout for the resource. 593 | 594 | [Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs 595 | 596 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 597 | [ash\_cubdb](https://harton.dev/james/ash_cubdb) 598 | 599 | v0.6.2 600 | 601 | - Guides 602 | - Modules 603 | 604 | 605 | 606 | 607 | 608 | Search documentation of ash\_cubdb 609 | 610 | Settings 611 | 612 | # [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/migration.ex#L1 "View Source") AshCubDB.Migration (ash\_cubdb v0.6.2) 613 | 614 | We store and check metadata when opening a database to ensure that the resource and attributes match, and possibly perform migrations. 615 | 616 | # [](AshCubDB.Migration.html#summary)Summary 617 | 618 | ## [Functions](AshCubDB.Migration.html#functions) 619 | 620 | [check(db, resource)](AshCubDB.Migration.html#check/2) 621 | 622 | Check that a newly opened database doesn't need to be migrated. 623 | 624 | # [](AshCubDB.Migration.html#functions)Functions 625 | 626 | [Link to this function](AshCubDB.Migration.html#check/2 "Link to this function") 627 | 628 | # check(db, resource) 629 | 630 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/migration.ex#L13 "View Source") 631 | 632 | ``` 633 | @spec check(GenServer.server(), Ash.Resource.t()) :: :ok | {:error, any()} 634 | ``` 635 | 636 | Check that a newly opened database doesn't need to be migrated. 637 | 638 | [Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs 639 | 640 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 641 | [ash\_cubdb](https://harton.dev/james/ash_cubdb) 642 | 643 | v0.6.2 644 | 645 | - Guides 646 | - Modules 647 | 648 | 649 | 650 | 651 | 652 | Search documentation of ash\_cubdb 653 | 654 | Settings 655 | 656 | # [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/query.ex#L1 "View Source") AshCubDB.Query (ash\_cubdb v0.6.2) 657 | 658 | A struct which holds information about a resource query as it is being built. 659 | 660 | # [](AshCubDB.Query.html#summary)Summary 661 | 662 | ## [Types](AshCubDB.Query.html#types) 663 | 664 | [t()](AshCubDB.Query.html#t:t/0) 665 | 666 | # [](AshCubDB.Query.html#types)Types 667 | 668 | [Link to this type](AshCubDB.Query.html#t:t/0 "Link to this type") 669 | 670 | # t() 671 | 672 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/query.ex#L21 "View Source") 673 | 674 | ``` 675 | @type t() :: %AshCubDB.Query{ 676 | aggregates: [Ash.Resource.Aggregate.t()], 677 | calculations: [Ash.Resource.Calculation.t()], 678 | distinct: Ash.Sort.t(), 679 | distinct_sort: Ash.Sort.t(), 680 | domain: Ash.Domain.t(), 681 | filter: nil | Ash.Filter.t(), 682 | limit: :infinity | non_neg_integer(), 683 | offset: non_neg_integer(), 684 | relationships: %{ 685 | optional(atom()) => Ash.Resource.Relationships.relationship() 686 | }, 687 | resource: Ash.Resource.t(), 688 | sort: nil | Ash.Sort.t(), 689 | tenant: any() 690 | } 691 | ``` 692 | 693 | [Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs 694 | 695 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 696 | [ash\_cubdb](https://harton.dev/james/ash_cubdb) 697 | 698 | v0.6.2 699 | 700 | - Guides 701 | - Modules 702 | 703 | 704 | 705 | 706 | 707 | Search documentation of ash\_cubdb 708 | 709 | Settings 710 | 711 | # [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/serde.ex#L1 "View Source") AshCubDB.Serde (ash\_cubdb v0.6.2) 712 | 713 | Handle serialising and deserialising of records into CubDB. 714 | 715 | # [](AshCubDB.Serde.html#summary)Summary 716 | 717 | ## [Functions](AshCubDB.Serde.html#functions) 718 | 719 | [deserialise(resource, key, data)](AshCubDB.Serde.html#deserialise/3) 720 | 721 | Convert the key and data back into a record.. 722 | 723 | [serialise(record)](AshCubDB.Serde.html#serialise/1) 724 | 725 | Serialise the record into key and value tuples for storage in CubDB. 726 | 727 | # [](AshCubDB.Serde.html#functions)Functions 728 | 729 | [Link to this function](AshCubDB.Serde.html#deserialise/3 "Link to this function") 730 | 731 | # deserialise(resource, key, data) 732 | 733 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/serde.ex#L38 "View Source") 734 | 735 | ``` 736 | @spec deserialise(Ash.Resource.t(), tuple(), tuple()) :: 737 | {:ok, Ash.Resource.record()} | {:error, any()} 738 | ``` 739 | 740 | Convert the key and data back into a record.. 741 | 742 | [Link to this function](AshCubDB.Serde.html#serialise/1 "Link to this function") 743 | 744 | # serialise(record) 745 | 746 | [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/lib/ash_cub_db/serde.ex#L14 "View Source") 747 | 748 | ``` 749 | @spec serialise(Ash.Resource.record()) :: {:ok, tuple(), tuple()} | {:error, any()} 750 | ``` 751 | 752 | Serialise the record into key and value tuples for storage in CubDB. 753 | 754 | [Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) Search HexDocs 755 | 756 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 757 | [ash\_cubdb](https://harton.dev/james/ash_cubdb) 758 | 759 | v0.6.2 760 | 761 | - Guides 762 | - Modules 763 | 764 | 765 | 766 | 767 | 768 | Search documentation of ash\_cubdb 769 | 770 | Settings 771 | 772 | # [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/documentation/dsls/DSL:-AshCubDB.DataLayer.md#L1 "View Source") DSL: AshCubDB.DataLayer 773 | 774 | A CubDB data layer for Ash. 775 | 776 | ## [](dsl-ashcubdb-datalayer.html#dsl-documentation)DSL Documentation 777 | 778 | ### [](dsl-ashcubdb-datalayer.html#index)Index 779 | 780 | - cubdb 781 | 782 | ### [](dsl-ashcubdb-datalayer.html#docs)Docs 783 | 784 | ## [](dsl-ashcubdb-datalayer.html#cubdb)cubdb 785 | 786 | CubDB data layer configuration. 787 | 788 | Examples: 789 | 790 | ``` 791 | cubdb do 792 | directory "/opt/storage/my_awesome_resource" 793 | auto_compact? true 794 | auto_file_sync? true 795 | name :my_awesome_resource 796 | end 797 | ``` 798 | 799 | * * * 800 | 801 | - `:directory` - The directory within which to store the CubDB data. 802 | If none is supplied, then one will be automatically generated in the `priv` directory of the parent OTP application. 803 | - `:otp_app` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The OTP application in whose `priv` directory data should be stored. 804 | Only used if `directory` is not supplied. When not provided [`Application.get_application/1`](../elixir/Application.html#get_application/1) will be called for the resource. 805 | - `:auto_compact?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Whether or not to automatically compact the CubDB database. 806 | See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-is-compaction) for more information. The default value is `true`. 807 | - `:auto_file_sync?` ([`boolean/0`](../elixir/typespecs.html#built-in-types)) - Whether or not to automatically flush the buffer to disk on write. 808 | See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-does-file-sync-mean) The default value is `true`. 809 | - `:name` ([`atom/0`](../elixir/typespecs.html#basic-types)) - The name of the CubDB database. 810 | By default this is the name of the resource module, however in some (rare) circumstances you may wish to specifically name the database. 811 | 812 | ## [](dsl-ashcubdb-datalayer.html#cubdb-1)cubdb 813 | 814 | CubDB data layer configuration. 815 | 816 | ### [](dsl-ashcubdb-datalayer.html#examples)Examples 817 | 818 | ``` 819 | cubdb do 820 | directory "/opt/storage/my_awesome_resource" 821 | auto_compact? true 822 | auto_file_sync? true 823 | name :my_awesome_resource 824 | end 825 | ``` 826 | 827 | ### [](dsl-ashcubdb-datalayer.html#options)Options 828 | 829 | NameTypeDefaultDocs[`directory`](dsl-ashcubdb-datalayer.html#cubdb-directory)`nil | String.t`The directory within which to store the CubDB data. If none is supplied, then one will be automatically generated in the `priv` directory of the parent OTP application.[`otp_app`](dsl-ashcubdb-datalayer.html#cubdb-otp_app)`atom`The OTP application in whose `priv` directory data should be stored. Only used if `directory` is not supplied. When not provided [`Application.get_application/1`](../elixir/Application.html#get_application/1) will be called for the resource.[`auto_compact?`](dsl-ashcubdb-datalayer.html#cubdb-auto_compact?)`boolean``true`Whether or not to automatically compact the CubDB database. See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-is-compaction) for more information.[`auto_file_sync?`](dsl-ashcubdb-datalayer.html#cubdb-auto_file_sync?)`boolean``true`Whether or not to automatically flush the buffer to disk on write. See [the CubDB documentation](https://hexdocs.pm/cubdb/faq.html#what-does-file-sync-mean)[`name`](dsl-ashcubdb-datalayer.html#cubdb-name)`atom`The name of the CubDB database. By default this is the name of the resource module, however in some (rare) circumstances you may wish to specifically name the database. 830 | 831 | [← Previous Page Change Log](changelog.html) 832 | 833 | [Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) ([current file](https://preview.hex.pm/preview/ash_cubdb/0.6.2/show/documentation/dsls/DSL:-AshCubDB.DataLayer.md)) Search HexDocs 834 | 835 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 836 | [ash\_cubdb](https://harton.dev/james/ash_cubdb) 837 | 838 | v0.6.2 839 | 840 | - Guides 841 | - Modules 842 | 843 | 844 | 845 | 846 | 847 | Search documentation of ash\_cubdb 848 | 849 | Settings 850 | 851 | # [View Source](https://harton.dev/james/ash_cub_db/src/branch/main/README.md#L1 "View Source") AshCubDB 852 | 853 | [![Build Status](https://drone.harton.dev/api/badges/james/ash_cubdb/status.svg?ref=refs%2Fheads%2Fmain)](https://drone.harton.dev/james/ash_cubdb) [![Hex.pm](https://img.shields.io/hexpm/v/ash_cubdb.svg)](https://hex.pm/packages/ash_cubdb) [![Hippocratic License HL3-FULL](https://img.shields.io/static/v1?label=Hippocratic%20License&message=HL3-FULL&labelColor=5e2751&color=bc8c3d)](https://firstdonoharm.dev/version/3/0/full.html) 854 | 855 | An [Ash DataLayer](https://ash-hq.org/docs/module/ash/latest/ash-datalayer) which adds support for [CubDB](https://hex.pm/packages/cubdb). 856 | 857 | ## [](readme.html#status)Status 858 | 859 | AshCubDb is still a work in progress. Feel free to give it a go. 860 | 861 | FeatureStatusCreate✅Upsert (by primary key)✅Upsert (by identity)❌Read (all)✅Read (by primary key)✅Read (filters)✅Read (sort)✅Read (distinct sort)✅Read (calculations)✅Read (aggregates)❌Update✅Destroy✅Transactions❌ 862 | 863 | ## [](readme.html#github-mirror)Github Mirror 864 | 865 | This repository is mirrored [on Github](https://github.com/jimsynz/ash_cubdb) from it's primary location [on my Forgejo instance](https://harton.dev/james/ash_cubdb). Feel free to raise issues and open PRs on Github. 866 | 867 | ## [](readme.html#installation)Installation 868 | 869 | AshCubDB is [available in Hex](https://hex.pm/packages/ash_cubdb), the package can be installed by adding `ash_cubdb` to your list of dependencies in `mix.exs`: 870 | 871 | ``` 872 | def deps do 873 | [ 874 | {:ash_cubdb, "~> 0.6.2"} 875 | ] 876 | end 877 | ``` 878 | 879 | Documentation for the latest release can be found on [HexDocs](../ash_cubdb.html) and for the `main` branch on [docs.harton.nz](https://docs.harton.nz/james/ash_cubdb). 880 | 881 | ## [](readme.html#license)License 882 | 883 | This software is licensed under the terms of the [HL3-FULL](https://firstdonoharm.dev), see the `LICENSE.md` file included with this package for the terms. 884 | 885 | This license actively proscribes this software being used by and for some industries, countries and activities. If your usage of this software doesn't comply with the terms of this license, then [contact me](mailto:james@harton.nz) with the details of your use-case to organise the purchase of a license - the cost of which may include a donation to a suitable charity or NGO. 886 | 887 | [← Previous Page API Reference](api-reference.html) 888 | 889 | [Next Page → Change Log](changelog.html) 890 | 891 | [Hex Package](https://hex.pm/packages/ash_cubdb/0.6.2) [Hex Preview](https://preview.hex.pm/preview/ash_cubdb/0.6.2) ([current file](https://preview.hex.pm/preview/ash_cubdb/0.6.2/show/README.md)) Search HexDocs 892 | 893 | Built using [ExDoc](https://github.com/elixir-lang/ex_doc "ExDoc") (v0.32.2) for the [Elixir programming language](https://elixir-lang.org "Elixir") 894 | --------------------------------------------------------------------------------