@zanzojs/core 0.1.0-beta.0 → 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -86
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,123 +1,91 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @zanzojs/core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@zanzojs/core)
|
|
4
|
+
[](https://www.typescriptlang.org)
|
|
5
|
+
[](https://vercel.com/docs/concepts/functions/edge-functions)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
The core engine of the Zanzo ReBAC ecosystem.
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
This package provides the core algorithms for schema building, relationship graph traversal, and AST compilation. It has **0 dependencies** and is strictly typed.
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
- 🔒 **Type-Safe (Generics Avanzados):** Inferencia estricta de strings y acciones usando TypeScript avanzado. El compilador sabe exactamente qué puedes hacer y dónde.
|
|
11
|
-
- 🌳 **Query Pushdown (AST):** No cargues miles de registros en memoria. Genera un Abstract Syntax Tree (AST) diagnóstico para que tu base de datos (Prisma, Mongo, SQL) haga el trabajo pesado de filtrar permisos.
|
|
12
|
-
- ⚡ **Frontend Ultraligero:** Evaluación `O(1)` asíncrona o estática en el cliente usando mapeos planos JSON (o Bitmasks).
|
|
13
|
-
- 🔄 **Graph Resolution & Relational Tuples:** Permite notación de puntos (ej. `team.member`) analizando tuplas en memoria de manera recursiva sin caer en bucles infinitos (Ciclic Safe).
|
|
11
|
+
## Features
|
|
14
12
|
|
|
15
|
-
|
|
13
|
+
- **Fluent Schema Builder:** Define your access control policies with auto-completing generics. No string typos.
|
|
14
|
+
- **Isomorphic Engine:** Run the exact same permission checks on your backend server or your Vercel Edge functions.
|
|
15
|
+
- **Client Snapshots:** Compile a user's entire permission graph on the server and export it as a flat `O(1)` JSON dictionary for frontend hydration.
|
|
16
|
+
- **Transitive Expansion:** Safely compute and materialize nested relationship paths at write-time to guarantee read-time performance.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
16
19
|
|
|
17
20
|
```bash
|
|
18
|
-
|
|
21
|
+
pnpm add @zanzojs/core
|
|
19
22
|
```
|
|
20
23
|
|
|
21
|
-
##
|
|
22
|
-
|
|
23
|
-
Zanzo se diseña para abarcar todo el ecosistema de tu aplicación, conectando Frontend, Backend y Bases de Datos a través del mismo contrato de tipos.
|
|
24
|
+
## Usage Guide
|
|
24
25
|
|
|
25
|
-
###
|
|
26
|
+
### 1. Defining your Schema
|
|
26
27
|
|
|
27
|
-
|
|
28
|
+
The core concept of ReBAC is mapping actions to relationships. Use the `ZanzoBuilder` to define your domain entities.
|
|
28
29
|
|
|
29
30
|
```typescript
|
|
30
|
-
import { ZanzoBuilder } from '@zanzojs/core';
|
|
31
|
+
import { ZanzoBuilder, ZanzoEngine } from '@zanzojs/core';
|
|
31
32
|
|
|
32
|
-
const schema = new ZanzoBuilder()
|
|
33
|
-
.entity('User', { actions: [] as const })
|
|
34
|
-
.entity('Group', {
|
|
35
|
-
actions: [] as const,
|
|
36
|
-
relations: { member: 'User' } as const,
|
|
37
|
-
})
|
|
33
|
+
export const schema = new ZanzoBuilder()
|
|
38
34
|
.entity('Document', {
|
|
39
|
-
actions: ['read', 'write'
|
|
40
|
-
relations: { owner: 'User',
|
|
35
|
+
actions: ['read', 'write'],
|
|
36
|
+
relations: { owner: 'User', viewer: 'User', container: 'Folder' },
|
|
41
37
|
permissions: {
|
|
42
|
-
//
|
|
43
|
-
read: ['owner', 'creator', 'team.member'],
|
|
38
|
+
// Users can write if they are the direct owner
|
|
44
39
|
write: ['owner'],
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
// Users can read if they are a viewer, owner, OR if they have read
|
|
41
|
+
// access to the parent folder.
|
|
42
|
+
read: ['viewer', 'owner', 'container.read'],
|
|
43
|
+
},
|
|
44
|
+
})
|
|
45
|
+
.entity('Folder', {
|
|
46
|
+
actions: ['read'],
|
|
47
|
+
relations: { admin: 'User' },
|
|
48
|
+
permissions: {
|
|
49
|
+
read: ['admin'],
|
|
50
|
+
}
|
|
47
51
|
})
|
|
52
|
+
.entity('User', { actions: [], relations: {} })
|
|
48
53
|
.build();
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Paso 2: Valida en el Backend (`ZanzoEngine`)
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
```typescript
|
|
56
|
-
import { ZanzoEngine } from '@zanzojs/core';
|
|
57
|
-
|
|
58
|
-
const engine = new ZanzoEngine(schema);
|
|
59
|
-
|
|
60
|
-
// Inyecta tuplas desde tu DB (Relation Tuples)
|
|
61
|
-
engine.addTuple({ subject: 'User:Ana', relation: 'owner', object: 'Document:123' });
|
|
62
|
-
engine.addTuple({ subject: 'User:Carlos', relation: 'member', object: 'Group:Devs' });
|
|
63
|
-
engine.addTuple({ subject: 'Group:Devs', relation: 'team', object: 'Document:456' });
|
|
64
|
-
|
|
65
|
-
// TS Autocomplete estricto: ¿Puede Ana editar el documento?
|
|
66
|
-
engine.can('User:Ana', 'write', 'Document:123'); // true
|
|
67
|
-
|
|
68
|
-
// ¿Puede Carlos leer el documento dev a través del grupo?
|
|
69
|
-
engine.can('User:Carlos', 'read', 'Document:456'); // true
|
|
55
|
+
export const engine = new ZanzoEngine(schema);
|
|
70
56
|
```
|
|
71
57
|
|
|
72
|
-
###
|
|
58
|
+
### 2. Basic In-Memory Evaluation
|
|
73
59
|
|
|
74
|
-
|
|
60
|
+
If you already know the specific tuples (relationships) involved, you can pass them to the engine for an instant, synchronous check.
|
|
75
61
|
|
|
76
62
|
```typescript
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
/* Result:
|
|
81
|
-
{
|
|
82
|
-
operator: 'OR',
|
|
83
|
-
conditions: [
|
|
84
|
-
{ type: 'direct', relation: 'owner', targetSubject: 'User:Ana' },
|
|
85
|
-
{ type: 'direct', relation: 'creator', targetSubject: 'User:Ana' },
|
|
86
|
-
{ type: 'nested', relation: 'team', nextRelationPath: ['member'], targetSubject: 'User:Ana' }
|
|
87
|
-
]
|
|
88
|
-
}
|
|
89
|
-
*/
|
|
90
|
-
```
|
|
63
|
+
// Register the relationships
|
|
64
|
+
engine.addTuple({ subject: 'User:1', relation: 'viewer', object: 'Document:xyz' });
|
|
91
65
|
|
|
92
|
-
|
|
66
|
+
// Ask the decisive question
|
|
67
|
+
const canRead = engine.can('User:1', 'read', 'Document:xyz');
|
|
68
|
+
console.log(canRead); // true
|
|
69
|
+
```
|
|
93
70
|
|
|
94
|
-
###
|
|
71
|
+
### 3. Creating Frontend Snapshots
|
|
95
72
|
|
|
96
|
-
|
|
73
|
+
For React/Vue SPAs, you don't want to traverse graphs locally. Instead, you extract a "flat snapshot" on your server and send it over an API block.
|
|
97
74
|
|
|
98
75
|
```typescript
|
|
99
|
-
import { createZanzoSnapshot
|
|
100
|
-
|
|
101
|
-
// Backend:
|
|
102
|
-
const flatPermissions = createZanzoSnapshot(engine, 'User:Carlos');
|
|
103
|
-
// flatPermissions = { "Document:456": ["read"] }
|
|
104
|
-
|
|
105
|
-
// --- Envía el JSON por API ---
|
|
76
|
+
import { createZanzoSnapshot } from '@zanzojs/core';
|
|
106
77
|
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
const client = new ZanzoClient(flatPermissions);
|
|
78
|
+
// This output is what you would pass to the @zanzojs/react Provider
|
|
79
|
+
const snapshot = createZanzoSnapshot(engine, 'User:1');
|
|
110
80
|
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
// Show UI Component
|
|
114
|
-
}
|
|
81
|
+
// Looks like:
|
|
82
|
+
// { "Document:xyz": ["read", "write"] }
|
|
115
83
|
```
|
|
116
84
|
|
|
117
|
-
##
|
|
85
|
+
## Security Limits
|
|
118
86
|
|
|
119
|
-
|
|
87
|
+
Unrestricted graphs can cause memory exhaustion (DoS). The `ZanzoEngine` natively prevents circular dependencies and caps graph traversal depth to 50 levels by default.
|
|
120
88
|
|
|
121
|
-
##
|
|
89
|
+
## Documentation
|
|
122
90
|
|
|
123
|
-
[
|
|
91
|
+
For integration with databases and React, see the [ZanzoJS Monorepo](https://github.com/GonzaloJeria/zanzo).
|