@zanzojs/core 0.1.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +123 -0
- package/dist/index.cjs +546 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +407 -0
- package/dist/index.d.ts +407 -0
- package/dist/index.js +533 -0
- package/dist/index.js.map +1 -0
- package/package.json +61 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ref/index.ts","../src/builder/index.ts","../src/engine/index.ts","../src/compiler/index.ts","../src/client/index.ts","../src/expander/walk.ts","../src/expander/index.ts","../src/expander/collapse.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Represents a parsed, validated Zanzo entity reference.\n * All entity identifiers in Zanzo follow the \"Type:ID\" convention.\n */\nexport interface EntityRef {\n readonly type: string;\n readonly id: string;\n}\n\n/**\n * The canonical separator used to join EntityRef parts into a string.\n * Both expandTuples and the Drizzle adapter depend on this constant.\n * Never hardcode ':' for entity refs anywhere else in the codebase.\n */\nexport const ENTITY_REF_SEPARATOR = ':' as const;\n\n/**\n * The canonical separator used to join nested relation path segments.\n * Both expandTuples and the Drizzle adapter depend on this constant.\n * Never hardcode '.' for relation paths anywhere else in the codebase.\n */\nexport const RELATION_PATH_SEPARATOR = '.' as const;\n\n/** @internal Shared control-character regex matching validateInput in ZanzoEngine */\nconst CONTROL_CHARS_REGEX = /[\\x00-\\x1F\\x7F]/;\n\n/**\n * Parses a \"Type:ID\" string into a structured EntityRef.\n * Validates format strictly at the boundary.\n *\n * @throws Error if the string does not contain exactly one ':' separator,\n * or if type or id segments are empty.\n */\nexport function parseEntityRef(raw: string): EntityRef {\n if (!raw || typeof raw !== 'string') {\n throw new Error(\n `[Zanzo] Invalid EntityRef: received ${raw === '' ? 'empty string' : String(raw)}. ` +\n `Expected a non-empty string in \"Type:ID\" format.`\n );\n }\n\n if (raw.length > 255) {\n throw new Error(\n `[Zanzo] Invalid EntityRef: input exceeds 255 characters (got ${raw.length}). ` +\n `Entity references must be under 255 characters.`\n );\n }\n\n if (CONTROL_CHARS_REGEX.test(raw)) {\n throw new Error(\n `[Zanzo] Invalid EntityRef: input contains illegal unprintable control characters. ` +\n `Sanitize the input before creating an EntityRef.`\n );\n }\n\n const sepIndex = raw.indexOf(ENTITY_REF_SEPARATOR);\n\n if (sepIndex === -1) {\n throw new Error(\n `[Zanzo] Invalid EntityRef: \"${raw}\" does not contain a '${ENTITY_REF_SEPARATOR}' separator. ` +\n `Expected format is \"Type:ID\" (e.g. \"User:123\").`\n );\n }\n\n // Check for more than one separator\n if (raw.indexOf(ENTITY_REF_SEPARATOR, sepIndex + 1) !== -1) {\n throw new Error(\n `[Zanzo] Invalid EntityRef: \"${raw}\" contains multiple '${ENTITY_REF_SEPARATOR}' separators. ` +\n `Expected exactly one separator in \"Type:ID\" format.`\n );\n }\n\n const type = raw.substring(0, sepIndex);\n const id = raw.substring(sepIndex + 1);\n\n if (type.length === 0) {\n throw new Error(\n `[Zanzo] Invalid EntityRef: \"${raw}\" has an empty type segment. ` +\n `The type before '${ENTITY_REF_SEPARATOR}' must be non-empty (e.g. \"User:123\").`\n );\n }\n\n if (id.length === 0) {\n throw new Error(\n `[Zanzo] Invalid EntityRef: \"${raw}\" has an empty id segment. ` +\n `The id after '${ENTITY_REF_SEPARATOR}' must be non-empty (e.g. \"User:123\").`\n );\n }\n\n return { type, id };\n}\n\n/**\n * Serializes an EntityRef back to its canonical \"Type:ID\" string form.\n */\nexport function serializeEntityRef(entityRef: EntityRef): string {\n return `${entityRef.type}${ENTITY_REF_SEPARATOR}${entityRef.id}`;\n}\n\n/**\n * Convenience factory. Equivalent to parseEntityRef but named for ergonomics.\n * Use this at API boundaries when constructing entity identifiers.\n *\n * @example\n * ref('User:123') // { type: 'User', id: '123' }\n * ref('Project:A') // { type: 'Project', id: 'A' }\n */\nexport function ref(raw: string): EntityRef {\n return parseEntityRef(raw);\n}\n","/**\n * Definition for an entity in the ReBAC schema.\n */\nexport interface EntityDefinition<\n A extends string = string,\n R extends Record<string, string> = Record<string, string>,\n> {\n actions: A[];\n relations?: R;\n permissions?: Partial<\n Record<A, Array<Extract<keyof R, string> | `${Extract<keyof R, string>}.${string}`>>\n >;\n}\n\n/**\n * Internal representation of the ReBAC schema.\n */\nexport type SchemaData = Record<string, EntityDefinition<any, any>>;\n\n/**\n * A fluent API builder for constructing a ReBAC schema.\n * Uses advanced generics to carry type information through chained calls\n * for maximum type safety and inference.\n */\nexport class ZanzoBuilder<TSchema extends SchemaData = {}> {\n private schema: TSchema;\n\n constructor(initialSchema?: TSchema) {\n this.schema = initialSchema ?? ({} as TSchema);\n }\n\n /**\n * Defines a new entity (resource) in the schema.\n *\n * @param name The name of the entity resource (e.g., 'User', 'Project')\n * @param definition The definition containing allowed actions and relations.\n * @returns A new ZanzoBuilder instance carrying the expanded type information.\n */\n public entity<\n TName extends string,\n TActions extends string,\n TRelations extends Record<string, keyof TSchema | string> = Record<never, never>,\n >(\n name: TName,\n definition: {\n actions: readonly TActions[];\n relations?: TRelations;\n permissions?: Partial<\n Record<\n TActions,\n readonly (keyof TRelations | `${Extract<keyof TRelations, string>}.${string}`)[]\n >\n >;\n },\n ): ZanzoBuilder<\n TSchema & {\n [K in TName]: {\n actions: TActions[];\n relations: TRelations;\n permissions: Partial<\n Record<TActions, (keyof TRelations | `${Extract<keyof TRelations, string>}.${string}`)[]>\n >;\n };\n }\n > {\n const newSchema = {\n ...this.schema,\n [name]: {\n actions: [...definition.actions],\n // Default to empty object if no relations provided to maintain stable structure\n relations: definition.relations ? { ...definition.relations } : {},\n permissions: definition.permissions\n ? Object.fromEntries(\n Object.entries(definition.permissions).map(([action, relations]) => [\n action,\n [...(relations as (keyof TRelations | string)[])],\n ]),\n )\n : {},\n },\n };\n\n return new ZanzoBuilder<any>(newSchema);\n }\n\n /**\n * Builds and freezes the schema, preventing further modifications.\n *\n * @returns The immutable, frozen ReBAC schema.\n */\n public build(): Readonly<TSchema> {\n // Deep freeze the schema to ensure immutability\n // ZANZO-BACKLOG (Issue #10): The generic constraint `<T extends Record<string, any>>` is\n // unnecessarily restrictive for arrays. Consider `<T extends object>` for broader coverage.\n const deepFreeze = <T extends Record<string, any>>(obj: T): Readonly<T> => {\n Object.keys(obj).forEach((prop) => {\n if (typeof obj[prop] === 'object' && obj[prop] !== null && !Object.isFrozen(obj[prop])) {\n deepFreeze(obj[prop]);\n }\n });\n return Object.freeze(obj);\n };\n\n return deepFreeze(this.schema);\n }\n}\n\n/**\n * UnionToIntersection is a TypeScript utility that converts a union of types into an intersection.\n * Example: `UnionToIntersection<{A: 1} | {B: 2}>` becomes `{A: 1} & {B: 2}`\n */\nexport type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (\n k: infer I,\n) => void\n ? I\n : never;\n\n/**\n * Extracts the tuple types array into a union of their elements.\n */\nexport type TupleTypes<T extends readonly any[]> = T[number];\n\n/**\n * Merges multiple frozen SchemaData objects into a single cohesive Schema.\n * It enforces strict typings so the resulting Schema is an Intersection of all provided subsets.\n *\n * It validates at runtime that no two domain-schemas define the same entity,\n * preventing silent overwrites and \"God Object\" collisions.\n *\n * @param schemas A rest array of individual built schemas to be merged.\n * @returns A strictly merged and deep-frozen unified SchemaData intersection.\n * @throws Error if overlapping entity definitions are found.\n */\nexport function mergeSchemas<T extends Readonly<SchemaData>[]>(\n ...schemas: T\n): Readonly<UnionToIntersection<TupleTypes<T>>> {\n const unified = {} as Record<string, any>;\n\n for (const schema of schemas) {\n for (const [entityName, definition] of Object.entries(schema)) {\n if (unified[entityName]) {\n throw new Error(\n `[Zanzo] Schema Merge Collision: The entity '${entityName}' is defined in multiple schemas. Please ensure your domain segments are uniquely scoped.`,\n );\n }\n unified[entityName] = definition;\n }\n }\n\n // ZANZO-BACKLOG (Issue #15): Only top-level Object.freeze is applied here.\n // Individual entity definitions are assumed frozen from build(). If non-frozen\n // schemas are passed, inner values remain mutable.\n return Object.freeze(unified) as Readonly<UnionToIntersection<TupleTypes<T>>>;\n}\n","import type { SchemaData } from '../builder/index';\nimport { parseEntityRef, RELATION_PATH_SEPARATOR } from '../ref/index';\n\n/**\n * Represents a logical ReBAC relational tuple binding a Subject to an Object via a Relation.\n * Example: User:1 is the 'owner' of Project:A\n */\nexport interface RelationTuple {\n /**\n * The actor or subject Entity, typically format 'Type:ID' (e.g. 'User:123')\n */\n subject: string;\n /**\n * The relationship linking the subject to the object (e.g. 'owner', 'viewer')\n */\n relation: string;\n /**\n * The target object Entity, typically format 'Type:ID' (e.g. 'Project:456')\n */\n object: string;\n}\n\n/**\n * Extracts all valid Entity Types (Resources) defined in the provided Schema\n */\nexport type ExtractSchemaResources<TSchema extends SchemaData> = keyof TSchema;\n\n/**\n * Extracts all Action string unions allowed for a specific Resource type\n * from the provided Schema.\n */\nexport type ExtractSchemaActions<\n TSchema extends SchemaData,\n TResource extends keyof TSchema,\n> = TSchema[TResource]['actions'][number];\n\n/**\n * Advanced Generic ReBAC Engine.\n * Takes a Schema initialized by ZanzoBuilder as its type base to offer strict autocomplete.\n */\nexport class ZanzoEngine<TSchema extends SchemaData> {\n private schema: Readonly<TSchema>;\n // Map<ObjectIdentifier, Map<Relation, Set<SubjectIdentifier>>>\n private index = new Map<string, Map<string, Set<string>>>();\n\n constructor(schema: Readonly<TSchema>) {\n this.schema = schema;\n }\n\n /**\n * Retreives the readonly schema structure.\n */\n public getSchema(): Readonly<TSchema> {\n return this.schema;\n }\n\n /**\n * Retrieves the read-only relation-graph maps indexing memory objects.\n * Exposing strictly for flat compilers.\n */\n public getIndex(): ReadonlyMap<string, ReadonlyMap<string, ReadonlySet<string>>> {\n return this.index as unknown as ReadonlyMap<string, ReadonlyMap<string, ReadonlySet<string>>>;\n }\n\n // ZANZO-REVIEW: Extraído según la especificación (validateActorInput). \n // Nota: hemos agrupado `resourceType` bajo su propia directriz, pero mantenemos esta abstracción idéntica\n // a cómo se extrae la validación limpia del actor tal y como solicitaste.\n // Issue #9: Unified validation — previously duplicated between actor and resource validators.\n private validateInput(input: string, label: string): void {\n if (!input || typeof input !== 'string' || input.length > 255) {\n throw new Error(`[Zanzo] Invalid ${label} input. Must be a non-empty string under 255 characters.`);\n }\n const controlCharsRegex = /[\\x00-\\x1F\\x7F]/;\n if (controlCharsRegex.test(input)) {\n throw new Error(`[Zanzo] Security Exception: ${label} input contains illegal unprintable control characters.`);\n }\n }\n\n /**\n * Injects a relation tuple into the in-memory store.\n * Issue #3: Validates all tuple fields before storing to prevent graph poisoning.\n */\n public addTuple(tuple: RelationTuple): void {\n this.validateInput(tuple.subject, 'subject');\n this.validateInput(tuple.object, 'object');\n this.validateInput(tuple.relation, 'relation');\n\n let objectRelations = this.index.get(tuple.object);\n if (!objectRelations) {\n objectRelations = new Map<string, Set<string>>();\n this.index.set(tuple.object, objectRelations);\n }\n\n let subjectsSet = objectRelations.get(tuple.relation);\n if (!subjectsSet) {\n subjectsSet = new Set<string>();\n objectRelations.set(tuple.relation, subjectsSet);\n }\n\n subjectsSet.add(tuple.subject);\n }\n\n /**\n * Injects multiple relation tuples into the in-memory store.\n */\n public addTuples(tuples: RelationTuple[]): void {\n for (const tuple of tuples) {\n this.addTuple(tuple);\n }\n }\n\n /**\n * Clears all relation tuples in the memory store.\n */\n public clearTuples(): void {\n this.index.clear();\n }\n\n /**\n * PERF-2: Evaluates ALL actions for a given actor on a specific resource in a\n * single pass. Returns the list of granted actions.\n *\n * This is more efficient than calling can() per action because:\n * - Identical routes shared by multiple actions are evaluated only once\n * - Early exit when all actions are already resolved\n * - Only one validation pass per (actor, resource) pair\n *\n * @internal This method is public solely because `createZanzoSnapshot` (in compiler/)\n * requires access to it. It is NOT part of the public API contract and may change\n * without notice in any minor version. Making it private would require moving\n * `createZanzoSnapshot` into ZanzoEngine as a method, which would break the current\n * modular architecture where the compiler is a standalone pure function.\n */\n public evaluateAllActions(actor: string, resource: string): string[] {\n this.validateInput(actor, 'actor');\n this.validateInput(resource, 'resource');\n\n const resourceType = parseEntityRef(resource).type;\n const resourceSchema = this.schema[resourceType as keyof TSchema];\n\n if (!resourceSchema) return [];\n\n const actions = resourceSchema.actions as string[];\n if (!actions || actions.length === 0) return [];\n\n const permissions = resourceSchema.permissions as Record<string, string[]> | undefined;\n if (!permissions) return [];\n\n // Deduplicate routes: group actions by their route string to avoid\n // traversing the same graph path multiple times (e.g. 'owner' used by view, edit, delete)\n const routeMap = new Map<string, { parts: string[]; actions: Set<string> }>();\n\n for (const action of actions) {\n const relationsForAction = permissions[action];\n if (!relationsForAction || relationsForAction.length === 0) continue;\n\n for (const route of relationsForAction) {\n let entry = routeMap.get(route);\n if (!entry) {\n entry = { parts: route.split(RELATION_PATH_SEPARATOR), actions: new Set() };\n routeMap.set(route, entry);\n }\n entry.actions.add(action);\n }\n }\n\n if (routeMap.size === 0) return [];\n\n // Evaluate each unique route once, mapping results to all associated actions\n const grantedActions = new Set<string>();\n\n for (const { parts, actions: routeActions } of routeMap.values()) {\n // Skip if all actions for this route are already granted\n const allAlreadyGranted = [...routeActions].every(a => grantedActions.has(a));\n if (allAlreadyGranted) continue;\n\n // Each unique route gets a fresh visited set to avoid cross-route interference\n const resolved = this.checkRelationsRecursive(\n actor,\n [parts],\n resource,\n new Set<string>(),\n 0,\n );\n\n if (resolved) {\n for (const action of routeActions) {\n grantedActions.add(action);\n }\n }\n\n // Early exit if all actions are granted\n if (grantedActions.size === actions.length) break;\n }\n\n // Return in original action order to maintain deterministic output\n return actions.filter(a => grantedActions.has(a));\n }\n\n /**\n * Evaluates if a given actor has permission to perform an action on a specific resource.\n * Leverages TypeScript assertions to provide strict autocompletion based on the schema.\n *\n * @param actor The subject entity string identifier (e.g., 'User:1')\n * @param action The specific action to perform (e.g., 'edit'), strictly typed.\n * @param resource The target resource entity string identifier (e.g., 'Project:A')\n * @returns boolean True if authorized, false otherwise.\n */\n public can<\n TResourceName extends Extract<ExtractSchemaResources<TSchema>, string>,\n TAction extends ExtractSchemaActions<TSchema, TResourceName>,\n >(actor: string, action: TAction, resource: `${TResourceName}:${string}`): boolean {\n this.validateInput(actor, 'actor');\n this.validateInput(resource, 'resource');\n\n const resourceType = parseEntityRef(resource).type as TResourceName;\n const resourceSchema = this.schema[resourceType];\n\n if (!resourceSchema || !resourceSchema.actions.includes(action as any)) {\n return false;\n }\n\n const allowedRelationsForAction = (resourceSchema.permissions?.[action] || []) as string[];\n\n if (allowedRelationsForAction.length === 0) {\n return false;\n }\n\n // Pre-split the allowed routes to avoid running String.split repeatedly during recursion\n const preSplitRoutes: string[][] = allowedRelationsForAction.map((route) => route.split(RELATION_PATH_SEPARATOR));\n \n // ZANZO-REVIEW: Decidí NO APLICAR la pre-computación de `routeKey` global solicitada en Tarea 3c.\n // Razón: En grafos combinatorios, si un nodo se alcanza por dos ramas requiriendo \"remainders\" distintos, \n // un hash global estático provocará un falso negativo en el caché `visited` y denegará permisos erróneamente.\n // (Esto causaba que stress.test.ts fallara). Mantenemos la concatenación selectiva pasada por recursión.\n\n // Call the recursive engine internal handler\n return this.checkRelationsRecursive(actor, preSplitRoutes, resource, new Set<string>(), 0);\n }\n\n /**\n * Internal recursive relation evaluation algorithm via Map Indexes.\n *\n * @param actor The original actor trying to accomplish the task\n * @param allowedRoutes Array of relation chains (pre-splitted parts) that grant access\n * @param currentTarget The current entity node in the graph being evaluated\n * @param visited Set of visited nodes to prevent cycles in graph evaluation\n * @returns True if relation path connects target to actor\n */\n private checkRelationsRecursive(\n actor: string,\n allowedRoutes: string[][],\n currentTarget: string,\n visited: Set<string>,\n depth: number = 0,\n parentSignature: string = '',\n ): boolean {\n if (depth > 50) {\n throw new Error(`[Zanzo] Security Exception: Maximum relationship depth of 50 exceeded. Graph might contain an infinite cycle or is too heavily nested.`);\n }\n \n // Memory Hotspot Optimization (GC Friendly):\n const visitedSignature = `${actor}|${currentTarget}|${parentSignature}`;\n\n if (visited.has(visitedSignature)) {\n return false;\n }\n visited.add(visitedSignature);\n\n const targetRelationsIndex = this.index.get(currentTarget);\n\n // If there's absolutely no relations associated with this target, abort the exploration to save cycles\n if (!targetRelationsIndex) {\n return false;\n }\n\n // Since we traverse allowedRoutes dynamically, pass down the identifier of the CURRENT route choice\n for (let i = 0; i < allowedRoutes.length; i++) {\n const routeParts = allowedRoutes[i] as string[];\n const currentRelation = routeParts[0] as string;\n const subjectsForRelation = targetRelationsIndex.get(currentRelation);\n\n // If no subjects possess this relation on the target, skip this route\n if (!subjectsForRelation || subjectsForRelation.size === 0) {\n continue;\n }\n\n if (routeParts.length === 1) {\n // Direct relation base case check O(1)\n if (subjectsForRelation.has(actor)) {\n return true;\n }\n } else {\n // Inherited nested relation graph exploration\n const remainingRoute = routeParts.slice(1);\n \n const nextSignature = parentSignature ? parentSignature + '.' + currentRelation + `[${i}]` : currentRelation + `[${i}]`;\n\n // Optimize: we execute branching recursively into subsets, and stop at first generic success.\n for (const intermediateSubject of subjectsForRelation) {\n const isGranted = this.checkRelationsRecursive(\n actor,\n [remainingRoute], // Pass down the remaining route only\n intermediateSubject,\n visited,\n depth + 1,\n nextSignature\n );\n\n if (isGranted) return true;\n }\n }\n }\n\n return false;\n }\n\n /**\n * Generates a database-agnostic Abstract Syntax Tree (AST) representing\n * the logical query needed to verify if the given actor is authorized to\n * perform action on a specific resourceType.\n *\n * Useful for \"Query Pushdown\", allowing ORMs or databases to evaluate permissions\n * directly across their own relational tables instead of loading data into memory.\n *\n * @param actor The subject entity string identifier (e.g., 'User:1')\n * @param action The specific action to perform (e.g., 'read'), strictly typed.\n * @param resourceType The target resource entity TYPE (e.g., 'Project')\n * @returns QueryAST block if action is valid and has mapped relations, null otherwise.\n */\n public buildDatabaseQuery<\n TResourceName extends Extract<ExtractSchemaResources<TSchema>, string>,\n TAction extends ExtractSchemaActions<TSchema, TResourceName>,\n >(\n actor: string,\n action: TAction,\n resourceType: TResourceName,\n ): import('../ast/index').QueryAST | null {\n this.validateInput(actor, 'actor');\n this.validateInput(resourceType as string, 'resource');\n\n const resourceSchema = this.schema[resourceType];\n\n if (!resourceSchema || !resourceSchema.actions.includes(action as any)) {\n return null;\n }\n\n const allowedRelationsForAction = (resourceSchema.permissions?.[action] || []) as string[];\n\n if (allowedRelationsForAction.length === 0) {\n return null;\n }\n\n // Build the underlying AST based on allowed relation paths\n const conditions = allowedRelationsForAction.map(\n (routeLine): import('../ast/index').Condition => {\n const parts = routeLine.split(RELATION_PATH_SEPARATOR);\n\n if (parts.length === 1) {\n return {\n type: 'direct',\n relation: parts[0] as string,\n targetSubject: actor,\n };\n }\n\n return {\n type: 'nested',\n relation: parts[0] as string,\n nextRelationPath: parts.slice(1),\n targetSubject: actor,\n };\n },\n );\n\n return {\n operator: 'OR', // ReBAC normally operates on union of granted authority paths\n conditions,\n };\n }\n}\n","import type { ZanzoEngine } from '../engine/index';\nimport type { SchemaData } from '../builder/index';\n\n/**\n * A compiled, flat JSON representation of authorized actions for a given actor over specific resources.\n * Output Format: Record<ResourceID, string[]>\n * Example: { \"Project:A\": [\"read\", \"write\"] }\n */\nexport type CompiledPermissions = Record<string, string[]>;\n\n/**\n * Compiles a flat JSON object containing all authorized actions a specific actor\n * can perform over every resource currently present in the engine's memory.\n *\n * This strips away all the relational ReBAC graph complexity, allowing lightweight\n * clients (Browsers, Mobile Apps, Edge workers) to do fast O(1) checks.\n *\n * @param engine The initialized ZanzoEngine containing the rules and graph memory\n * @param actor The subject entity string identifier (e.g., 'User:1')\n * @returns CompiledPermissions A flat JSON map answering \"what can I do and where?\"\n */\nexport function createZanzoSnapshot<TSchema extends SchemaData>(\n engine: ZanzoEngine<TSchema>,\n actor: string,\n): CompiledPermissions {\n const result: CompiledPermissions = Object.create(null);\n const index = engine.getIndex();\n\n // ZANZO-BACKLOG (Issue #2): Currently iterates only object keys from the index.\n // Entities appearing exclusively as subjects are NOT evaluated.\n // This is correct for the \"what can actor do to targets?\" use case,\n // but the JSDoc should be refined if broader coverage is needed.\n\n // PERF-2 (Issue #6): Uses evaluateAllActions for single-traversal-per-resource\n // instead of calling can() per action, eliminating redundant graph walks.\n for (const resource of index.keys()) {\n const allowedActions = engine.evaluateAllActions(actor, resource);\n\n // Only map it if the user actually has at least 1 allowed action\n if (allowedActions.length > 0) {\n result[resource] = allowedActions;\n }\n }\n\n return result;\n}\n","import type { CompiledPermissions } from '../compiler/index';\n\n/**\n * A lightweight, ultra-fast O(1) ReBAC Client intended for Frontend applications\n * or Edge Environments.\n *\n * It operates solely on a pre-compiled JSON mask. It forces 0 dependencies\n * and requires no knowledge of schemas, graph recursion, or Rebac Engines.\n *\n * Issue #5: Uses Map<string, Set<string>> internally for true O(1) lookups.\n * Issue #12: Deep-copies incoming snapshot to prevent external mutation.\n */\nexport class ZanzoClient {\n private permissions: Map<string, Set<string>>;\n private snapshotCache: CompiledPermissions | null = null;\n\n /**\n * Initializes the client with a strictly flat JSON representation of permissions.\n * The input is deep-copied internally to prevent Prototype Pollution\n * or external mutation attacks.\n *\n * @param compiledPermissions The Record<ResourceID, string[]> derived from `createZanzoSnapshot`\n */\n constructor(compiledPermissions: CompiledPermissions) {\n // Issue #12: Deep-copy to prevent external mutation of the source object\n this.permissions = new Map(\n Object.entries(compiledPermissions).map(([key, actions]) => [\n key,\n new Set(Array.isArray(actions) ? actions : []),\n ])\n );\n }\n\n /**\n * True O(1) constant time evaluation of permissions via Set.has().\n *\n * @param action The specific action to evaluate\n * @param resource The target resource entity identifier\n * @returns boolean True if authorized, False otherwise\n */\n public can(action: string, resource: string): boolean {\n const allowedActions = this.permissions.get(resource);\n if (!allowedActions) {\n return false;\n }\n\n return allowedActions.has(action);\n }\n\n /**\n * Returns the compiled snapshot state as a plain JSON object.\n * Result is cached after first call to avoid re-serialization.\n * Useful for persisting it locally or dumping to Redux/Vuex inside Client apps.\n */\n public getSnapshot(): CompiledPermissions {\n if (this.snapshotCache) {\n return this.snapshotCache;\n }\n\n const result: CompiledPermissions = Object.create(null);\n for (const [key, actions] of this.permissions) {\n result[key] = [...actions];\n }\n\n this.snapshotCache = result;\n return result;\n }\n}\n","import type { SchemaData } from '../builder/index';\nimport type { RelationTuple } from '../engine/index';\nimport type { FetchChildrenCallback } from './index';\nimport { parseEntityRef, RELATION_PATH_SEPARATOR } from '../ref/index';\n\n/**\n * Represents a single derived tuple discovered during graph traversal.\n * @internal\n */\nexport interface WalkResult {\n subject: string;\n relation: string;\n object: string;\n}\n\n/**\n * Shared traversal algorithm used by both `expandTuples` and `collapseTuples`.\n * Walks the schema graph starting from an initial tuple, discovering all\n * derived tuples that nested permission paths require.\n *\n * Uses a cursor-based queue for O(1) dequeue and a processedRelations Set\n * to prevent infinite loops and duplicate derivations.\n *\n * @internal Not part of the public API. Used exclusively by expand/collapse.\n */\nexport async function _walkExpansionGraph(\n schema: Readonly<SchemaData>,\n initialTuple: RelationTuple,\n fetchChildren: FetchChildrenCallback,\n maxSize: number,\n): Promise<WalkResult[]> {\n const results: WalkResult[] = [];\n const processedRelations = new Set<string>();\n\n // Cursor-based queue for O(1) dequeue (Issue #13)\n const queue: RelationTuple[] = [initialTuple];\n let cursor = 0;\n\n while (cursor < queue.length) {\n // Guard against unbounded expansion (Issue #14)\n if (results.length > maxSize) {\n throw new Error(\n `[Zanzo] Security Exception: Tuple expansion exceeded maximum size of ${maxSize}. ` +\n `Possible cycle in schema or data. Configure maxExpansionSize/maxCollapseSize to increase the limit.`\n );\n }\n\n const currentTuple = queue[cursor++]!;\n let objectType: string;\n try {\n objectType = parseEntityRef(currentTuple.object).type;\n } catch {\n continue;\n }\n\n for (const definition of Object.values(schema)) {\n if (!definition.relations || !definition.permissions) continue;\n\n const matchingRelations: string[] = [];\n for (const [relName, relTarget] of Object.entries(definition.relations)) {\n if (relTarget === objectType) {\n matchingRelations.push(relName);\n }\n }\n\n if (matchingRelations.length === 0) continue;\n\n for (const paths of Object.values(definition.permissions)) {\n if (!Array.isArray(paths)) continue;\n\n for (const path of paths) {\n if (typeof path !== 'string') continue;\n\n const parts = path.split(RELATION_PATH_SEPARATOR);\n if (parts.length >= 2) {\n for (const relName of matchingRelations) {\n if (parts[0] === relName && parts.slice(1).join(RELATION_PATH_SEPARATOR) === currentTuple.relation) {\n const derivedRelation = `${relName}${RELATION_PATH_SEPARATOR}${currentTuple.relation}`;\n\n const trackingSignature = `${currentTuple.object}|${derivedRelation}`;\n\n if (!processedRelations.has(trackingSignature)) {\n processedRelations.add(trackingSignature);\n\n const children = await fetchChildren(currentTuple.object, relName);\n if (Array.isArray(children)) {\n for (const child of children) {\n const result: WalkResult = {\n subject: currentTuple.subject,\n relation: derivedRelation,\n object: child,\n };\n\n results.push(result);\n // Enqueue for transitive expansion\n queue.push({\n subject: currentTuple.subject,\n relation: derivedRelation,\n object: child,\n });\n }\n }\n }\n }\n }\n }\n }\n }\n }\n }\n\n return results;\n}\n","import type { SchemaData } from '../builder/index';\nimport type { RelationTuple } from '../engine/index';\nimport { _walkExpansionGraph } from './walk';\n\n/**\n * Callback que el usuario debe implementar para proveer los objetos hijos\n * de un objeto padre dado una relación específica.\n * \n * Ejemplo: para el tuple `User:1 → admin → Org:A`, el engine necesita saber\n * qué Projects pertenecen a `Org:A` via la relación `org` para poder derivar\n * `User:1 → org.admin → Project:X`.\n * \n * @param parentObject El objeto padre (e.g. \"Org:A\")\n * @param relationToChildren El nombre de la relación inversa (e.g. \"org\")\n * @returns Array de identificadores de objetos hijos (e.g. [\"Project:1\", \"Project:2\"])\n */\nexport type FetchChildrenCallback = (\n parentObject: string,\n relationToChildren: string\n) => Promise<string[]> | string[];\n\nexport interface ExpansionContext {\n schema: Readonly<SchemaData>;\n newTuple: RelationTuple;\n fetchChildren: FetchChildrenCallback;\n /**\n * Maximum number of derived tuples allowed before aborting expansion.\n * Prevents denial-of-service via unbounded queue growth in pathological schemas.\n * @default 500\n */\n maxExpansionSize?: number;\n}\n\n/**\n * Dado un nuevo tuple base, calcula todas las tuplas derivadas implícitas\n * que deben ser materializadas en la base de datos para que las nested\n * permission paths funcionen correctamente en el adapter SQL.\n *\n * Esta función es PURA en su lógica: solo lee el schema y delega\n * el acceso a datos al callback provisto. No tiene side effects.\n *\n * @remarks\n * **Resolución Transitiva (Nested de múltiples niveles):** \n * Para schemas con paths de más de dos niveles (e.g. `parent.org.admin`), los \n * tuples intermedios también deben ser expandidos. Esta función maneja la \n * propagación completa automáticamente mediante una cola (queue) interna, \n * procesando iterativamente cada nuevo tuple derivado hasta agotar las rutas dinámicas.\n * No requiere llamadas manuales recurrentes por parte del desarrollador.\n *\n * @returns Array de RelationTuple derivados que deben ser insertados\n * junto al tuple original. Si no hay derivaciones, retorna [].\n */\nexport async function expandTuples(ctx: ExpansionContext): Promise<RelationTuple[]> {\n const { schema, newTuple, fetchChildren, maxExpansionSize = 500 } = ctx;\n\n const walkResults = await _walkExpansionGraph(\n schema,\n newTuple,\n fetchChildren,\n maxExpansionSize,\n );\n\n return walkResults.map(r => ({\n subject: r.subject,\n relation: r.relation,\n object: r.object,\n }));\n}\n\n","import type { SchemaData } from '../builder/index';\nimport type { RelationTuple } from '../engine/index';\nimport type { FetchChildrenCallback } from './index';\nimport { _walkExpansionGraph } from './walk';\n\nexport interface CollapseContext {\n schema: Readonly<SchemaData>;\n /**\n * El tuple base que se está revocando.\n * Debe ser idéntico al tuple base que se pasó a expandTuples originalmente.\n */\n revokedTuple: RelationTuple;\n fetchChildren: FetchChildrenCallback;\n /**\n * Límite máximo de tuples a procesar en la cola de colapso.\n * Debe ser igual al maxExpansionSize usado al expandir.\n * @default 500\n */\n maxCollapseSize?: number;\n}\n\n/**\n * Calcula todos los tuples derivados que deben ser eliminados de la DB\n * cuando se revoca un tuple base previamente expandido con `expandTuples`.\n *\n * Esta función es el inverso simétrico de `expandTuples`. Usa el mismo\n * algoritmo de queue-based traversal y el mismo `fetchChildren` callback\n * para reconstruir qué tuples derivados existen y deben ser borrados.\n *\n * Es PURA: no elimina nada. Retorna los tuples a eliminar para que\n * el caller decida cómo ejecutar el DELETE en su base de datos.\n *\n * @returns Array de RelationTuple que deben ser eliminados,\n * NO incluye el revokedTuple base (el caller lo elimina por separado).\n */\nexport async function collapseTuples(ctx: CollapseContext): Promise<RelationTuple[]> {\n const { schema, revokedTuple, fetchChildren, maxCollapseSize = 500 } = ctx;\n\n const walkResults = await _walkExpansionGraph(\n schema,\n revokedTuple,\n fetchChildren,\n maxCollapseSize,\n );\n\n return walkResults.map(r => ({\n subject: r.subject,\n relation: r.relation,\n object: r.object,\n }));\n}\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents a parsed, validated Zanzo entity reference.
|
|
3
|
+
* All entity identifiers in Zanzo follow the "Type:ID" convention.
|
|
4
|
+
*/
|
|
5
|
+
interface EntityRef {
|
|
6
|
+
readonly type: string;
|
|
7
|
+
readonly id: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The canonical separator used to join EntityRef parts into a string.
|
|
11
|
+
* Both expandTuples and the Drizzle adapter depend on this constant.
|
|
12
|
+
* Never hardcode ':' for entity refs anywhere else in the codebase.
|
|
13
|
+
*/
|
|
14
|
+
declare const ENTITY_REF_SEPARATOR: ":";
|
|
15
|
+
/**
|
|
16
|
+
* The canonical separator used to join nested relation path segments.
|
|
17
|
+
* Both expandTuples and the Drizzle adapter depend on this constant.
|
|
18
|
+
* Never hardcode '.' for relation paths anywhere else in the codebase.
|
|
19
|
+
*/
|
|
20
|
+
declare const RELATION_PATH_SEPARATOR: ".";
|
|
21
|
+
/**
|
|
22
|
+
* Parses a "Type:ID" string into a structured EntityRef.
|
|
23
|
+
* Validates format strictly at the boundary.
|
|
24
|
+
*
|
|
25
|
+
* @throws Error if the string does not contain exactly one ':' separator,
|
|
26
|
+
* or if type or id segments are empty.
|
|
27
|
+
*/
|
|
28
|
+
declare function parseEntityRef(raw: string): EntityRef;
|
|
29
|
+
/**
|
|
30
|
+
* Serializes an EntityRef back to its canonical "Type:ID" string form.
|
|
31
|
+
*/
|
|
32
|
+
declare function serializeEntityRef(entityRef: EntityRef): string;
|
|
33
|
+
/**
|
|
34
|
+
* Convenience factory. Equivalent to parseEntityRef but named for ergonomics.
|
|
35
|
+
* Use this at API boundaries when constructing entity identifiers.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ref('User:123') // { type: 'User', id: '123' }
|
|
39
|
+
* ref('Project:A') // { type: 'Project', id: 'A' }
|
|
40
|
+
*/
|
|
41
|
+
declare function ref(raw: string): EntityRef;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* ZANZO-REVIEW: Hemos eliminado los aliases opacos (Resource, Action, Relation, Role) en favor de `string` puro.
|
|
45
|
+
* Usar Branded Types habría roto la ergonomía del API Builder al exigir aserciones manuales
|
|
46
|
+
* de tipos literales en los genéricos (ej. '.entity("User" as Resource)').
|
|
47
|
+
* El formato esperado se documenta nativamente.
|
|
48
|
+
*/
|
|
49
|
+
/**
|
|
50
|
+
* Strict Permission format mapping a Resource to an Action.
|
|
51
|
+
* Automatically infers specific strings from string literals.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* type MyPerm = Permission<'Project', 'read' | 'write'>;
|
|
55
|
+
* // 'Project:read' | 'Project:write'
|
|
56
|
+
*/
|
|
57
|
+
type Permission<R extends string, A extends string> = `${R}:${A}`;
|
|
58
|
+
/**
|
|
59
|
+
* Extracts the Action type from a given Permission string type.
|
|
60
|
+
*/
|
|
61
|
+
type ExtractAction<P extends string> = P extends `${string}:${infer A}` ? A : never;
|
|
62
|
+
/**
|
|
63
|
+
* Extracts the Resource type from a given Permission string type.
|
|
64
|
+
*/
|
|
65
|
+
type ExtractResource<P extends string> = P extends `${infer R}:${string}` ? R : never;
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Definition for an entity in the ReBAC schema.
|
|
69
|
+
*/
|
|
70
|
+
interface EntityDefinition<A extends string = string, R extends Record<string, string> = Record<string, string>> {
|
|
71
|
+
actions: A[];
|
|
72
|
+
relations?: R;
|
|
73
|
+
permissions?: Partial<Record<A, Array<Extract<keyof R, string> | `${Extract<keyof R, string>}.${string}`>>>;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Internal representation of the ReBAC schema.
|
|
77
|
+
*/
|
|
78
|
+
type SchemaData = Record<string, EntityDefinition<any, any>>;
|
|
79
|
+
/**
|
|
80
|
+
* A fluent API builder for constructing a ReBAC schema.
|
|
81
|
+
* Uses advanced generics to carry type information through chained calls
|
|
82
|
+
* for maximum type safety and inference.
|
|
83
|
+
*/
|
|
84
|
+
declare class ZanzoBuilder<TSchema extends SchemaData = {}> {
|
|
85
|
+
private schema;
|
|
86
|
+
constructor(initialSchema?: TSchema);
|
|
87
|
+
/**
|
|
88
|
+
* Defines a new entity (resource) in the schema.
|
|
89
|
+
*
|
|
90
|
+
* @param name The name of the entity resource (e.g., 'User', 'Project')
|
|
91
|
+
* @param definition The definition containing allowed actions and relations.
|
|
92
|
+
* @returns A new ZanzoBuilder instance carrying the expanded type information.
|
|
93
|
+
*/
|
|
94
|
+
entity<TName extends string, TActions extends string, TRelations extends Record<string, keyof TSchema | string> = Record<never, never>>(name: TName, definition: {
|
|
95
|
+
actions: readonly TActions[];
|
|
96
|
+
relations?: TRelations;
|
|
97
|
+
permissions?: Partial<Record<TActions, readonly (keyof TRelations | `${Extract<keyof TRelations, string>}.${string}`)[]>>;
|
|
98
|
+
}): ZanzoBuilder<TSchema & {
|
|
99
|
+
[K in TName]: {
|
|
100
|
+
actions: TActions[];
|
|
101
|
+
relations: TRelations;
|
|
102
|
+
permissions: Partial<Record<TActions, (keyof TRelations | `${Extract<keyof TRelations, string>}.${string}`)[]>>;
|
|
103
|
+
};
|
|
104
|
+
}>;
|
|
105
|
+
/**
|
|
106
|
+
* Builds and freezes the schema, preventing further modifications.
|
|
107
|
+
*
|
|
108
|
+
* @returns The immutable, frozen ReBAC schema.
|
|
109
|
+
*/
|
|
110
|
+
build(): Readonly<TSchema>;
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* UnionToIntersection is a TypeScript utility that converts a union of types into an intersection.
|
|
114
|
+
* Example: `UnionToIntersection<{A: 1} | {B: 2}>` becomes `{A: 1} & {B: 2}`
|
|
115
|
+
*/
|
|
116
|
+
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
|
|
117
|
+
/**
|
|
118
|
+
* Extracts the tuple types array into a union of their elements.
|
|
119
|
+
*/
|
|
120
|
+
type TupleTypes<T extends readonly any[]> = T[number];
|
|
121
|
+
/**
|
|
122
|
+
* Merges multiple frozen SchemaData objects into a single cohesive Schema.
|
|
123
|
+
* It enforces strict typings so the resulting Schema is an Intersection of all provided subsets.
|
|
124
|
+
*
|
|
125
|
+
* It validates at runtime that no two domain-schemas define the same entity,
|
|
126
|
+
* preventing silent overwrites and "God Object" collisions.
|
|
127
|
+
*
|
|
128
|
+
* @param schemas A rest array of individual built schemas to be merged.
|
|
129
|
+
* @returns A strictly merged and deep-frozen unified SchemaData intersection.
|
|
130
|
+
* @throws Error if overlapping entity definitions are found.
|
|
131
|
+
*/
|
|
132
|
+
declare function mergeSchemas<T extends Readonly<SchemaData>[]>(...schemas: T): Readonly<UnionToIntersection<TupleTypes<T>>>;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Represents a logical combination of multiple conditions in the AST.
|
|
136
|
+
*/
|
|
137
|
+
interface QueryAST {
|
|
138
|
+
operator: 'OR' | 'AND';
|
|
139
|
+
conditions: Condition[];
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* A condition evaluating a single relation path towards a generic target subject.
|
|
143
|
+
*/
|
|
144
|
+
type Condition = DirectCondition | NestedCondition;
|
|
145
|
+
/**
|
|
146
|
+
* Represents a direct, 1-level relation requirement.
|
|
147
|
+
* e.g. "We need to find if this resource has the given `targetSubject` as its `relation`".
|
|
148
|
+
*/
|
|
149
|
+
interface DirectCondition {
|
|
150
|
+
type: 'direct';
|
|
151
|
+
relation: string;
|
|
152
|
+
targetSubject: string;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Represents an inherited relation requirement.
|
|
156
|
+
* e.g. "We need to find an intermediate entity linked via `relation` that can satisfy `nextRelationPath` to reach `targetSubject`".
|
|
157
|
+
*/
|
|
158
|
+
interface NestedCondition {
|
|
159
|
+
type: 'nested';
|
|
160
|
+
relation: string;
|
|
161
|
+
nextRelationPath: string[];
|
|
162
|
+
targetSubject: string;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Represents a logical ReBAC relational tuple binding a Subject to an Object via a Relation.
|
|
167
|
+
* Example: User:1 is the 'owner' of Project:A
|
|
168
|
+
*/
|
|
169
|
+
interface RelationTuple {
|
|
170
|
+
/**
|
|
171
|
+
* The actor or subject Entity, typically format 'Type:ID' (e.g. 'User:123')
|
|
172
|
+
*/
|
|
173
|
+
subject: string;
|
|
174
|
+
/**
|
|
175
|
+
* The relationship linking the subject to the object (e.g. 'owner', 'viewer')
|
|
176
|
+
*/
|
|
177
|
+
relation: string;
|
|
178
|
+
/**
|
|
179
|
+
* The target object Entity, typically format 'Type:ID' (e.g. 'Project:456')
|
|
180
|
+
*/
|
|
181
|
+
object: string;
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Extracts all valid Entity Types (Resources) defined in the provided Schema
|
|
185
|
+
*/
|
|
186
|
+
type ExtractSchemaResources<TSchema extends SchemaData> = keyof TSchema;
|
|
187
|
+
/**
|
|
188
|
+
* Extracts all Action string unions allowed for a specific Resource type
|
|
189
|
+
* from the provided Schema.
|
|
190
|
+
*/
|
|
191
|
+
type ExtractSchemaActions<TSchema extends SchemaData, TResource extends keyof TSchema> = TSchema[TResource]['actions'][number];
|
|
192
|
+
/**
|
|
193
|
+
* Advanced Generic ReBAC Engine.
|
|
194
|
+
* Takes a Schema initialized by ZanzoBuilder as its type base to offer strict autocomplete.
|
|
195
|
+
*/
|
|
196
|
+
declare class ZanzoEngine<TSchema extends SchemaData> {
|
|
197
|
+
private schema;
|
|
198
|
+
private index;
|
|
199
|
+
constructor(schema: Readonly<TSchema>);
|
|
200
|
+
/**
|
|
201
|
+
* Retreives the readonly schema structure.
|
|
202
|
+
*/
|
|
203
|
+
getSchema(): Readonly<TSchema>;
|
|
204
|
+
/**
|
|
205
|
+
* Retrieves the read-only relation-graph maps indexing memory objects.
|
|
206
|
+
* Exposing strictly for flat compilers.
|
|
207
|
+
*/
|
|
208
|
+
getIndex(): ReadonlyMap<string, ReadonlyMap<string, ReadonlySet<string>>>;
|
|
209
|
+
private validateInput;
|
|
210
|
+
/**
|
|
211
|
+
* Injects a relation tuple into the in-memory store.
|
|
212
|
+
* Issue #3: Validates all tuple fields before storing to prevent graph poisoning.
|
|
213
|
+
*/
|
|
214
|
+
addTuple(tuple: RelationTuple): void;
|
|
215
|
+
/**
|
|
216
|
+
* Injects multiple relation tuples into the in-memory store.
|
|
217
|
+
*/
|
|
218
|
+
addTuples(tuples: RelationTuple[]): void;
|
|
219
|
+
/**
|
|
220
|
+
* Clears all relation tuples in the memory store.
|
|
221
|
+
*/
|
|
222
|
+
clearTuples(): void;
|
|
223
|
+
/**
|
|
224
|
+
* PERF-2: Evaluates ALL actions for a given actor on a specific resource in a
|
|
225
|
+
* single pass. Returns the list of granted actions.
|
|
226
|
+
*
|
|
227
|
+
* This is more efficient than calling can() per action because:
|
|
228
|
+
* - Identical routes shared by multiple actions are evaluated only once
|
|
229
|
+
* - Early exit when all actions are already resolved
|
|
230
|
+
* - Only one validation pass per (actor, resource) pair
|
|
231
|
+
*
|
|
232
|
+
* @internal This method is public solely because `createZanzoSnapshot` (in compiler/)
|
|
233
|
+
* requires access to it. It is NOT part of the public API contract and may change
|
|
234
|
+
* without notice in any minor version. Making it private would require moving
|
|
235
|
+
* `createZanzoSnapshot` into ZanzoEngine as a method, which would break the current
|
|
236
|
+
* modular architecture where the compiler is a standalone pure function.
|
|
237
|
+
*/
|
|
238
|
+
evaluateAllActions(actor: string, resource: string): string[];
|
|
239
|
+
/**
|
|
240
|
+
* Evaluates if a given actor has permission to perform an action on a specific resource.
|
|
241
|
+
* Leverages TypeScript assertions to provide strict autocompletion based on the schema.
|
|
242
|
+
*
|
|
243
|
+
* @param actor The subject entity string identifier (e.g., 'User:1')
|
|
244
|
+
* @param action The specific action to perform (e.g., 'edit'), strictly typed.
|
|
245
|
+
* @param resource The target resource entity string identifier (e.g., 'Project:A')
|
|
246
|
+
* @returns boolean True if authorized, false otherwise.
|
|
247
|
+
*/
|
|
248
|
+
can<TResourceName extends Extract<ExtractSchemaResources<TSchema>, string>, TAction extends ExtractSchemaActions<TSchema, TResourceName>>(actor: string, action: TAction, resource: `${TResourceName}:${string}`): boolean;
|
|
249
|
+
/**
|
|
250
|
+
* Internal recursive relation evaluation algorithm via Map Indexes.
|
|
251
|
+
*
|
|
252
|
+
* @param actor The original actor trying to accomplish the task
|
|
253
|
+
* @param allowedRoutes Array of relation chains (pre-splitted parts) that grant access
|
|
254
|
+
* @param currentTarget The current entity node in the graph being evaluated
|
|
255
|
+
* @param visited Set of visited nodes to prevent cycles in graph evaluation
|
|
256
|
+
* @returns True if relation path connects target to actor
|
|
257
|
+
*/
|
|
258
|
+
private checkRelationsRecursive;
|
|
259
|
+
/**
|
|
260
|
+
* Generates a database-agnostic Abstract Syntax Tree (AST) representing
|
|
261
|
+
* the logical query needed to verify if the given actor is authorized to
|
|
262
|
+
* perform action on a specific resourceType.
|
|
263
|
+
*
|
|
264
|
+
* Useful for "Query Pushdown", allowing ORMs or databases to evaluate permissions
|
|
265
|
+
* directly across their own relational tables instead of loading data into memory.
|
|
266
|
+
*
|
|
267
|
+
* @param actor The subject entity string identifier (e.g., 'User:1')
|
|
268
|
+
* @param action The specific action to perform (e.g., 'read'), strictly typed.
|
|
269
|
+
* @param resourceType The target resource entity TYPE (e.g., 'Project')
|
|
270
|
+
* @returns QueryAST block if action is valid and has mapped relations, null otherwise.
|
|
271
|
+
*/
|
|
272
|
+
buildDatabaseQuery<TResourceName extends Extract<ExtractSchemaResources<TSchema>, string>, TAction extends ExtractSchemaActions<TSchema, TResourceName>>(actor: string, action: TAction, resourceType: TResourceName): QueryAST | null;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* A compiled, flat JSON representation of authorized actions for a given actor over specific resources.
|
|
277
|
+
* Output Format: Record<ResourceID, string[]>
|
|
278
|
+
* Example: { "Project:A": ["read", "write"] }
|
|
279
|
+
*/
|
|
280
|
+
type CompiledPermissions = Record<string, string[]>;
|
|
281
|
+
/**
|
|
282
|
+
* Compiles a flat JSON object containing all authorized actions a specific actor
|
|
283
|
+
* can perform over every resource currently present in the engine's memory.
|
|
284
|
+
*
|
|
285
|
+
* This strips away all the relational ReBAC graph complexity, allowing lightweight
|
|
286
|
+
* clients (Browsers, Mobile Apps, Edge workers) to do fast O(1) checks.
|
|
287
|
+
*
|
|
288
|
+
* @param engine The initialized ZanzoEngine containing the rules and graph memory
|
|
289
|
+
* @param actor The subject entity string identifier (e.g., 'User:1')
|
|
290
|
+
* @returns CompiledPermissions A flat JSON map answering "what can I do and where?"
|
|
291
|
+
*/
|
|
292
|
+
declare function createZanzoSnapshot<TSchema extends SchemaData>(engine: ZanzoEngine<TSchema>, actor: string): CompiledPermissions;
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* A lightweight, ultra-fast O(1) ReBAC Client intended for Frontend applications
|
|
296
|
+
* or Edge Environments.
|
|
297
|
+
*
|
|
298
|
+
* It operates solely on a pre-compiled JSON mask. It forces 0 dependencies
|
|
299
|
+
* and requires no knowledge of schemas, graph recursion, or Rebac Engines.
|
|
300
|
+
*
|
|
301
|
+
* Issue #5: Uses Map<string, Set<string>> internally for true O(1) lookups.
|
|
302
|
+
* Issue #12: Deep-copies incoming snapshot to prevent external mutation.
|
|
303
|
+
*/
|
|
304
|
+
declare class ZanzoClient {
|
|
305
|
+
private permissions;
|
|
306
|
+
private snapshotCache;
|
|
307
|
+
/**
|
|
308
|
+
* Initializes the client with a strictly flat JSON representation of permissions.
|
|
309
|
+
* The input is deep-copied internally to prevent Prototype Pollution
|
|
310
|
+
* or external mutation attacks.
|
|
311
|
+
*
|
|
312
|
+
* @param compiledPermissions The Record<ResourceID, string[]> derived from `createZanzoSnapshot`
|
|
313
|
+
*/
|
|
314
|
+
constructor(compiledPermissions: CompiledPermissions);
|
|
315
|
+
/**
|
|
316
|
+
* True O(1) constant time evaluation of permissions via Set.has().
|
|
317
|
+
*
|
|
318
|
+
* @param action The specific action to evaluate
|
|
319
|
+
* @param resource The target resource entity identifier
|
|
320
|
+
* @returns boolean True if authorized, False otherwise
|
|
321
|
+
*/
|
|
322
|
+
can(action: string, resource: string): boolean;
|
|
323
|
+
/**
|
|
324
|
+
* Returns the compiled snapshot state as a plain JSON object.
|
|
325
|
+
* Result is cached after first call to avoid re-serialization.
|
|
326
|
+
* Useful for persisting it locally or dumping to Redux/Vuex inside Client apps.
|
|
327
|
+
*/
|
|
328
|
+
getSnapshot(): CompiledPermissions;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* Callback que el usuario debe implementar para proveer los objetos hijos
|
|
333
|
+
* de un objeto padre dado una relación específica.
|
|
334
|
+
*
|
|
335
|
+
* Ejemplo: para el tuple `User:1 → admin → Org:A`, el engine necesita saber
|
|
336
|
+
* qué Projects pertenecen a `Org:A` via la relación `org` para poder derivar
|
|
337
|
+
* `User:1 → org.admin → Project:X`.
|
|
338
|
+
*
|
|
339
|
+
* @param parentObject El objeto padre (e.g. "Org:A")
|
|
340
|
+
* @param relationToChildren El nombre de la relación inversa (e.g. "org")
|
|
341
|
+
* @returns Array de identificadores de objetos hijos (e.g. ["Project:1", "Project:2"])
|
|
342
|
+
*/
|
|
343
|
+
type FetchChildrenCallback = (parentObject: string, relationToChildren: string) => Promise<string[]> | string[];
|
|
344
|
+
interface ExpansionContext {
|
|
345
|
+
schema: Readonly<SchemaData>;
|
|
346
|
+
newTuple: RelationTuple;
|
|
347
|
+
fetchChildren: FetchChildrenCallback;
|
|
348
|
+
/**
|
|
349
|
+
* Maximum number of derived tuples allowed before aborting expansion.
|
|
350
|
+
* Prevents denial-of-service via unbounded queue growth in pathological schemas.
|
|
351
|
+
* @default 500
|
|
352
|
+
*/
|
|
353
|
+
maxExpansionSize?: number;
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Dado un nuevo tuple base, calcula todas las tuplas derivadas implícitas
|
|
357
|
+
* que deben ser materializadas en la base de datos para que las nested
|
|
358
|
+
* permission paths funcionen correctamente en el adapter SQL.
|
|
359
|
+
*
|
|
360
|
+
* Esta función es PURA en su lógica: solo lee el schema y delega
|
|
361
|
+
* el acceso a datos al callback provisto. No tiene side effects.
|
|
362
|
+
*
|
|
363
|
+
* @remarks
|
|
364
|
+
* **Resolución Transitiva (Nested de múltiples niveles):**
|
|
365
|
+
* Para schemas con paths de más de dos niveles (e.g. `parent.org.admin`), los
|
|
366
|
+
* tuples intermedios también deben ser expandidos. Esta función maneja la
|
|
367
|
+
* propagación completa automáticamente mediante una cola (queue) interna,
|
|
368
|
+
* procesando iterativamente cada nuevo tuple derivado hasta agotar las rutas dinámicas.
|
|
369
|
+
* No requiere llamadas manuales recurrentes por parte del desarrollador.
|
|
370
|
+
*
|
|
371
|
+
* @returns Array de RelationTuple derivados que deben ser insertados
|
|
372
|
+
* junto al tuple original. Si no hay derivaciones, retorna [].
|
|
373
|
+
*/
|
|
374
|
+
declare function expandTuples(ctx: ExpansionContext): Promise<RelationTuple[]>;
|
|
375
|
+
|
|
376
|
+
interface CollapseContext {
|
|
377
|
+
schema: Readonly<SchemaData>;
|
|
378
|
+
/**
|
|
379
|
+
* El tuple base que se está revocando.
|
|
380
|
+
* Debe ser idéntico al tuple base que se pasó a expandTuples originalmente.
|
|
381
|
+
*/
|
|
382
|
+
revokedTuple: RelationTuple;
|
|
383
|
+
fetchChildren: FetchChildrenCallback;
|
|
384
|
+
/**
|
|
385
|
+
* Límite máximo de tuples a procesar en la cola de colapso.
|
|
386
|
+
* Debe ser igual al maxExpansionSize usado al expandir.
|
|
387
|
+
* @default 500
|
|
388
|
+
*/
|
|
389
|
+
maxCollapseSize?: number;
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Calcula todos los tuples derivados que deben ser eliminados de la DB
|
|
393
|
+
* cuando se revoca un tuple base previamente expandido con `expandTuples`.
|
|
394
|
+
*
|
|
395
|
+
* Esta función es el inverso simétrico de `expandTuples`. Usa el mismo
|
|
396
|
+
* algoritmo de queue-based traversal y el mismo `fetchChildren` callback
|
|
397
|
+
* para reconstruir qué tuples derivados existen y deben ser borrados.
|
|
398
|
+
*
|
|
399
|
+
* Es PURA: no elimina nada. Retorna los tuples a eliminar para que
|
|
400
|
+
* el caller decida cómo ejecutar el DELETE en su base de datos.
|
|
401
|
+
*
|
|
402
|
+
* @returns Array de RelationTuple que deben ser eliminados,
|
|
403
|
+
* NO incluye el revokedTuple base (el caller lo elimina por separado).
|
|
404
|
+
*/
|
|
405
|
+
declare function collapseTuples(ctx: CollapseContext): Promise<RelationTuple[]>;
|
|
406
|
+
|
|
407
|
+
export { type CollapseContext, type CompiledPermissions, type Condition, type DirectCondition, ENTITY_REF_SEPARATOR, type EntityDefinition, type EntityRef, type ExpansionContext, type ExtractAction, type ExtractResource, type ExtractSchemaActions, type ExtractSchemaResources, type FetchChildrenCallback, type NestedCondition, type Permission, type QueryAST, RELATION_PATH_SEPARATOR, type RelationTuple, type SchemaData, type TupleTypes, type UnionToIntersection, ZanzoBuilder, ZanzoClient, ZanzoEngine, collapseTuples, createZanzoSnapshot, expandTuples, mergeSchemas, parseEntityRef, ref, serializeEntityRef };
|