toride 0.1.0 → 0.3.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.
@@ -54,6 +54,7 @@ var FieldAccessDefSchema = v.object({
54
54
  var ResourceBlockSchema = v.object({
55
55
  roles: v.array(v.string()),
56
56
  permissions: v.array(v.string()),
57
+ attributes: v.optional(v.record(v.string(), AttributeTypeSchema)),
57
58
  relations: v.optional(v.record(v.string(), v.string())),
58
59
  grants: v.optional(v.record(v.string(), v.array(v.string()))),
59
60
  derived_roles: v.optional(v.array(DerivedRoleEntrySchema)),
@@ -2000,16 +2001,17 @@ async function permittedFields(engine, actor, operation, resource, fieldAccess,
2000
2001
  for (const fieldName of fieldNames) {
2001
2002
  const fieldDef = fieldAccess[fieldName];
2002
2003
  const allowedRoles = fieldDef?.[operation];
2004
+ const typedFieldName = fieldName;
2003
2005
  if (allowedRoles) {
2004
2006
  if (actorRoles.some((role) => allowedRoles.includes(role))) {
2005
- permitted.push(fieldName);
2007
+ permitted.push(typedFieldName);
2006
2008
  }
2007
2009
  } else {
2008
2010
  if (hasResourcePermission === void 0) {
2009
2011
  hasResourcePermission = await engine.can(actor, operation, resource, options);
2010
2012
  }
2011
2013
  if (hasResourcePermission) {
2012
- permitted.push(fieldName);
2014
+ permitted.push(typedFieldName);
2013
2015
  }
2014
2016
  }
2015
2017
  }
@@ -2031,8 +2033,11 @@ var Toride = class {
2031
2033
  * Default-deny: returns false if resource type is unknown or no grants match.
2032
2034
  */
2033
2035
  async can(actor, action, resource, options) {
2034
- const result = await this.evaluateInternal(actor, action, resource, options);
2035
- this.fireDecisionEvent(actor, action, resource, result);
2036
+ const a = actor;
2037
+ const r = resource;
2038
+ const act = action;
2039
+ const result = await this.evaluateInternal(a, act, r, options);
2040
+ this.fireDecisionEvent(a, act, r, result);
2036
2041
  return result.allowed;
2037
2042
  }
2038
2043
  /**
@@ -2048,8 +2053,11 @@ var Toride = class {
2048
2053
  * granted permissions, matched rules, and human-readable final decision.
2049
2054
  */
2050
2055
  async explain(actor, action, resource, options) {
2051
- const result = await this.evaluateInternal(actor, action, resource, options);
2052
- this.fireDecisionEvent(actor, action, resource, result);
2056
+ const a = actor;
2057
+ const r = resource;
2058
+ const act = action;
2059
+ const result = await this.evaluateInternal(a, act, r, options);
2060
+ this.fireDecisionEvent(a, act, r, result);
2053
2061
  return result;
2054
2062
  }
2055
2063
  /**
@@ -2057,7 +2065,9 @@ var Toride = class {
2057
2065
  * Uses a shared cache across all per-action evaluations.
2058
2066
  */
2059
2067
  async permittedActions(actor, resource, options) {
2060
- const resourceBlock = this.policy.resources[resource.type];
2068
+ const a = actor;
2069
+ const r = resource;
2070
+ const resourceBlock = this.policy.resources[r.type];
2061
2071
  if (!resourceBlock) {
2062
2072
  return [];
2063
2073
  }
@@ -2065,9 +2075,9 @@ var Toride = class {
2065
2075
  const permitted = [];
2066
2076
  for (const action of resourceBlock.permissions) {
2067
2077
  const result = await this.evaluateInternal(
2068
- actor,
2078
+ a,
2069
2079
  action,
2070
- resource,
2080
+ r,
2071
2081
  options,
2072
2082
  sharedCache
2073
2083
  );
@@ -2084,7 +2094,12 @@ var Toride = class {
2084
2094
  * Suitable for serializing to the client via TorideClient.
2085
2095
  */
2086
2096
  async snapshot(actor, resources, options) {
2087
- return snapshot(this, actor, resources, options);
2097
+ return snapshot(
2098
+ this,
2099
+ actor,
2100
+ resources,
2101
+ options
2102
+ );
2088
2103
  }
2089
2104
  /**
2090
2105
  * T095: Check if an actor can perform a field-level operation on a specific field.
@@ -2092,33 +2107,52 @@ var Toride = class {
2092
2107
  * Unlisted fields are unrestricted: any actor with the resource-level permission can access them.
2093
2108
  */
2094
2109
  async canField(actor, operation, resource, field, options) {
2095
- const resourceBlock = this.policy.resources[resource.type];
2110
+ const r = resource;
2111
+ const resourceBlock = this.policy.resources[r.type];
2096
2112
  if (!resourceBlock) {
2097
2113
  return false;
2098
2114
  }
2099
- return canField(this, actor, operation, resource, field, resourceBlock.field_access, options);
2115
+ return canField(
2116
+ this,
2117
+ actor,
2118
+ operation,
2119
+ r,
2120
+ field,
2121
+ resourceBlock.field_access,
2122
+ options
2123
+ );
2100
2124
  }
2101
2125
  /**
2102
2126
  * T095: Return the list of declared field_access field names the actor can access
2103
2127
  * for the given operation. Only returns explicitly declared fields.
2104
2128
  */
2105
2129
  async permittedFields(actor, operation, resource, options) {
2106
- const resourceBlock = this.policy.resources[resource.type];
2130
+ const r = resource;
2131
+ const resourceBlock = this.policy.resources[r.type];
2107
2132
  if (!resourceBlock) {
2108
2133
  return [];
2109
2134
  }
2110
- return permittedFields(this, actor, operation, resource, resourceBlock.field_access, options);
2135
+ return permittedFields(
2136
+ this,
2137
+ actor,
2138
+ operation,
2139
+ r,
2140
+ resourceBlock.field_access,
2141
+ options
2142
+ );
2111
2143
  }
2112
2144
  /**
2113
2145
  * T070: Return flat deduplicated list of all resolved roles (direct + derived).
2114
2146
  */
2115
2147
  async resolvedRoles(actor, resource, options) {
2116
- const resourceBlock = this.policy.resources[resource.type];
2148
+ const a = actor;
2149
+ const r = resource;
2150
+ const resourceBlock = this.policy.resources[r.type];
2117
2151
  if (!resourceBlock) {
2118
2152
  return [];
2119
2153
  }
2120
2154
  const action = resourceBlock.permissions[0] ?? "__resolvedRoles__";
2121
- const result = await this.evaluateInternal(actor, action, resource, options);
2155
+ const result = await this.evaluateInternal(a, action, r, options);
2122
2156
  const directRoles = result.resolvedRoles.direct;
2123
2157
  const derivedRoleNames = result.resolvedRoles.derived.map((d) => d.role);
2124
2158
  return [.../* @__PURE__ */ new Set([...directRoles, ...derivedRoleNames])];
@@ -2131,17 +2165,20 @@ var Toride = class {
2131
2165
  if (checks.length === 0) {
2132
2166
  return [];
2133
2167
  }
2168
+ const a = actor;
2134
2169
  const sharedCache = new AttributeCache(this.resolvers);
2135
2170
  const results = [];
2136
2171
  for (const check of checks) {
2172
+ const r = check.resource;
2173
+ const act = check.action;
2137
2174
  const result = await this.evaluateInternal(
2138
- actor,
2139
- check.action,
2140
- check.resource,
2175
+ a,
2176
+ act,
2177
+ r,
2141
2178
  options,
2142
2179
  sharedCache
2143
2180
  );
2144
- this.fireDecisionEvent(actor, check.action, check.resource, result);
2181
+ this.fireDecisionEvent(a, act, r, result);
2145
2182
  results.push(result.allowed);
2146
2183
  }
2147
2184
  return results;
@@ -2151,11 +2188,14 @@ var Toride = class {
2151
2188
  * Returns ConstraintResult with unrestricted/forbidden sentinels or constraint AST.
2152
2189
  */
2153
2190
  async buildConstraints(actor, action, resourceType, options) {
2191
+ const a = actor;
2192
+ const act = action;
2193
+ const rt = resourceType;
2154
2194
  const cache = new AttributeCache(this.resolvers);
2155
2195
  const constraintResult = await buildConstraints(
2156
- actor,
2157
- action,
2158
- resourceType,
2196
+ a,
2197
+ act,
2198
+ rt,
2159
2199
  cache,
2160
2200
  this.policy,
2161
2201
  {
@@ -2164,15 +2204,24 @@ var Toride = class {
2164
2204
  customEvaluators: this.options.customEvaluators
2165
2205
  }
2166
2206
  );
2167
- this.fireQueryEvent(actor, action, resourceType, constraintResult);
2207
+ this.fireQueryEvent(a, act, rt, constraintResult);
2168
2208
  return constraintResult;
2169
2209
  }
2170
2210
  /**
2171
2211
  * T064: Translate constraint AST using an adapter.
2172
2212
  * Dispatches each constraint node to the adapter's methods.
2213
+ *
2214
+ * Accepts ConstraintResult<R> from buildConstraints() and returns
2215
+ * TQueryMap[R] — the adapter's mapped output type for resource R.
2216
+ * The resource type R is inferred from the ConstraintResult phantom type.
2173
2217
  */
2174
2218
  translateConstraints(constraints, adapter) {
2175
- return translateConstraints(constraints, adapter);
2219
+ if ("unrestricted" in constraints || "forbidden" in constraints) {
2220
+ throw new Error(
2221
+ "Cannot translate unrestricted or forbidden ConstraintResult. Check for 'constraints' property before calling translateConstraints()."
2222
+ );
2223
+ }
2224
+ return translateConstraints(constraints.constraints, adapter);
2176
2225
  }
2177
2226
  /**
2178
2227
  * T072: Fire onDecision audit callback via microtask (non-blocking).
@@ -0,0 +1,38 @@
1
+ // src/client.ts
2
+ var CLIENT_VERSION = "0.0.1";
3
+ var TorideClient = class {
4
+ permissions;
5
+ constructor(snapshot) {
6
+ this.permissions = /* @__PURE__ */ new Map();
7
+ const entries = snapshot;
8
+ for (const [key, actions] of Object.entries(entries)) {
9
+ this.permissions.set(key, new Set(actions));
10
+ }
11
+ }
12
+ /**
13
+ * Synchronous permission check.
14
+ * Returns true if the action is permitted for the resource, false otherwise.
15
+ * Unknown resources return false (default-deny).
16
+ */
17
+ can(action, resource) {
18
+ const key = `${resource.type}:${resource.id}`;
19
+ const actions = this.permissions.get(key);
20
+ if (!actions) return false;
21
+ return actions.has(action);
22
+ }
23
+ /**
24
+ * Return the list of permitted actions for a resource.
25
+ * Returns empty array for unknown resources.
26
+ */
27
+ permittedActions(resource) {
28
+ const key = `${resource.type}:${resource.id}`;
29
+ const actions = this.permissions.get(key);
30
+ if (!actions) return [];
31
+ return [...actions];
32
+ }
33
+ };
34
+
35
+ export {
36
+ CLIENT_VERSION,
37
+ TorideClient
38
+ };
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import {
7
7
  runTestCases,
8
8
  validatePolicyResult,
9
9
  validatePolicyStrict
10
- } from "./chunk-QE4FJZEG.js";
10
+ } from "./chunk-2AWXNP37.js";
11
11
 
12
12
  // src/cli.ts
13
13
  import { readFileSync, readdirSync, statSync } from "fs";
@@ -0,0 +1,373 @@
1
+ /**
2
+ * Shape constraint for Toride's type parameter.
3
+ * Each property is a union or mapped type that codegen fills with literals.
4
+ */
5
+ interface TorideSchema {
6
+ /** Union of all resource type names (e.g., "Document" | "Organization") */
7
+ resources: string;
8
+ /** Global union of all action/permission names across all resources */
9
+ actions: string;
10
+ /** Union of all actor type names (e.g., "User" | "ServiceAccount") */
11
+ actorTypes: string;
12
+ /** Per-resource permission unions: { Document: "read" | "write"; ... } */
13
+ permissionMap: {
14
+ [R in string]: string;
15
+ };
16
+ /** Per-resource role unions: { Document: "admin" | "editor"; ... } */
17
+ roleMap: {
18
+ [R in string]: string;
19
+ };
20
+ /** Per-resource attribute shapes: { Document: { status: string; ownerId: string }; ... } */
21
+ resourceAttributeMap: {
22
+ [R in string]: Record<string, unknown>;
23
+ };
24
+ /** Per-actor attribute shapes: { User: { email: string; is_admin: boolean }; ... } */
25
+ actorAttributeMap: {
26
+ [A in string]: Record<string, unknown>;
27
+ };
28
+ /** Per-resource relation maps: { Document: { org: "Organization" }; ... } */
29
+ relationMap: {
30
+ [R in string]: Record<string, string>;
31
+ };
32
+ }
33
+ /**
34
+ * Default schema where everything is string / Record<string, unknown>.
35
+ * Used when Toride is instantiated without a type parameter.
36
+ * Provides full backward compatibility with the current untyped API.
37
+ */
38
+ interface DefaultSchema extends TorideSchema {
39
+ resources: string;
40
+ actions: string;
41
+ actorTypes: string;
42
+ permissionMap: Record<string, string>;
43
+ roleMap: Record<string, string>;
44
+ resourceAttributeMap: Record<string, Record<string, unknown>>;
45
+ actorAttributeMap: Record<string, Record<string, unknown>>;
46
+ relationMap: Record<string, Record<string, string>>;
47
+ }
48
+ /**
49
+ * Represents an entity performing actions.
50
+ * Generic discriminated union over actor types in S.
51
+ * When S = DefaultSchema, collapses to the original untyped shape.
52
+ */
53
+ type ActorRef<S extends TorideSchema = DefaultSchema> = {
54
+ [A in S["actorTypes"]]: {
55
+ readonly type: A;
56
+ readonly id: string;
57
+ readonly attributes: S["actorAttributeMap"][A];
58
+ };
59
+ }[S["actorTypes"]];
60
+ /**
61
+ * Represents a protected entity being accessed.
62
+ * Generic over schema S and resource type R.
63
+ * When S = DefaultSchema, collapses to the original untyped shape.
64
+ */
65
+ type ResourceRef<S extends TorideSchema = DefaultSchema, R extends S["resources"] = S["resources"]> = {
66
+ readonly type: R;
67
+ readonly id: string;
68
+ /** Pre-fetched attributes. Inline values take precedence over resolver results. */
69
+ readonly attributes?: S["resourceAttributeMap"][R];
70
+ };
71
+ /** Optional per-check configuration. */
72
+ interface CheckOptions {
73
+ readonly env?: Record<string, unknown>;
74
+ }
75
+ /** A single item in a canBatch() call. Action narrowed to global actions union. */
76
+ interface BatchCheckItem<S extends TorideSchema = DefaultSchema> {
77
+ readonly action: S["actions"];
78
+ readonly resource: ResourceRef<S>;
79
+ }
80
+ /**
81
+ * Per-type resolver function.
82
+ * Called when the engine needs attributes not available inline.
83
+ * Called at most once per unique resource per evaluation (cached).
84
+ *
85
+ * Registering a resolver is **optional** per resource type. When no resolver is
86
+ * registered, inline {@link ResourceRef.attributes} are used as the sole data
87
+ * source — this is referred to as "default resolver" behavior (analogous to
88
+ * GraphQL's default field resolver, which returns `parent[fieldName]`).
89
+ *
90
+ * A resolver is only needed when attributes must be fetched from an external
91
+ * source (e.g., a database). When both inline attributes and a resolver are
92
+ * present, inline attributes take precedence field-by-field over resolver
93
+ * results.
94
+ */
95
+ type ResourceResolver<S extends TorideSchema = DefaultSchema, R extends S["resources"] = S["resources"]> = (ref: ResourceRef<S, R>) => Promise<Record<string, unknown>>;
96
+ /**
97
+ * Map of resource type names to their resolver functions.
98
+ *
99
+ * Not all types need resolvers. Types without a registered resolver use
100
+ * **default resolver** behavior (also called "trivial resolution"): the engine
101
+ * reads attribute values directly from the inline {@link ResourceRef.attributes}
102
+ * passed at the call site. Fields not present inline resolve to `undefined`,
103
+ * causing conditions that reference them to fail (default-deny).
104
+ *
105
+ * This mirrors GraphQL's default field resolver pattern, where an unresolved
106
+ * field simply returns `parent[fieldName]` — here, inline attributes play the
107
+ * role of the `parent` object.
108
+ */
109
+ type Resolvers<S extends TorideSchema = DefaultSchema> = {
110
+ [R in S["resources"]]?: ResourceResolver<S, R>;
111
+ };
112
+ /** Custom evaluator function signature. */
113
+ type EvaluatorFn = (actor: ActorRef, resource: ResourceRef, env: Record<string, unknown>) => Promise<boolean>;
114
+ /** Engine construction options. */
115
+ interface TorideOptions<S extends TorideSchema = DefaultSchema> {
116
+ readonly policy: Policy;
117
+ /**
118
+ * Per-type resolver map.
119
+ *
120
+ * Optional — the engine works without any resolvers when all required data is
121
+ * provided inline via {@link ResourceRef.attributes}. This "default resolver"
122
+ * mode is the simplest way to use toride and requires no async data fetching.
123
+ *
124
+ * When both inline attributes and a resolver are present for the same resource
125
+ * type, **inline attributes take precedence** over resolver results on a
126
+ * field-by-field basis.
127
+ *
128
+ * @example
129
+ * ```ts
130
+ * // Inline-only mode — no resolvers needed
131
+ * const toride = new Toride({ policy });
132
+ *
133
+ * const allowed = await toride.can(actor, "read", {
134
+ * type: "Document",
135
+ * id: "doc-1",
136
+ * attributes: { status: "published", ownerId: "user-42" },
137
+ * });
138
+ * ```
139
+ */
140
+ readonly resolvers?: Resolvers<S>;
141
+ readonly maxConditionDepth?: number;
142
+ readonly maxDerivedRoleDepth?: number;
143
+ readonly customEvaluators?: Record<string, EvaluatorFn>;
144
+ readonly onDecision?: (event: DecisionEvent) => void;
145
+ readonly onQuery?: (event: QueryEvent) => void;
146
+ }
147
+ /** Attribute type for actor declarations. */
148
+ type AttributeType = "string" | "number" | "boolean";
149
+ /** Actor type declaration with attribute schema. */
150
+ interface ActorDeclaration {
151
+ readonly attributes: Record<string, AttributeType>;
152
+ }
153
+ /** Global role definition derived from actor attributes. */
154
+ interface GlobalRole {
155
+ readonly actor_type: string;
156
+ readonly when: ConditionExpression;
157
+ }
158
+ /**
159
+ * Derived role entry. Exactly one derivation pattern per entry.
160
+ * Patterns:
161
+ * 1. from_global_role
162
+ * 2. from_role + on_relation
163
+ * 3. from_relation
164
+ * 4. actor_type + when (conditional)
165
+ * 5. when only
166
+ */
167
+ interface DerivedRoleEntry {
168
+ readonly role: string;
169
+ readonly from_global_role?: string;
170
+ readonly from_role?: string;
171
+ readonly on_relation?: string;
172
+ readonly from_relation?: string;
173
+ readonly actor_type?: string;
174
+ readonly when?: ConditionExpression;
175
+ }
176
+ /** Conditional rule (permit or forbid). */
177
+ interface Rule {
178
+ readonly effect: "permit" | "forbid";
179
+ readonly roles?: string[];
180
+ readonly permissions: string[];
181
+ readonly when: ConditionExpression;
182
+ }
183
+ /** Field-level access control definition. */
184
+ interface FieldAccessDef {
185
+ readonly read?: string[];
186
+ readonly update?: string[];
187
+ }
188
+ /** Resource block definition. */
189
+ interface ResourceBlock {
190
+ readonly roles: string[];
191
+ readonly permissions: string[];
192
+ /** Optional typed attribute declarations for this resource type. */
193
+ readonly attributes?: Record<string, AttributeType>;
194
+ /** Relations map field names to target resource type names (simplified). */
195
+ readonly relations?: Record<string, string>;
196
+ readonly grants?: Record<string, string[]>;
197
+ readonly derived_roles?: DerivedRoleEntry[];
198
+ readonly rules?: Rule[];
199
+ readonly field_access?: Record<string, FieldAccessDef>;
200
+ }
201
+ /** Operator-based condition value. */
202
+ type ConditionOperator = {
203
+ readonly eq: unknown;
204
+ } | {
205
+ readonly neq: unknown;
206
+ } | {
207
+ readonly gt: unknown;
208
+ } | {
209
+ readonly gte: unknown;
210
+ } | {
211
+ readonly lt: unknown;
212
+ } | {
213
+ readonly lte: unknown;
214
+ } | {
215
+ readonly in: unknown[] | string;
216
+ } | {
217
+ readonly includes: unknown;
218
+ } | {
219
+ readonly exists: boolean;
220
+ } | {
221
+ readonly startsWith: string;
222
+ } | {
223
+ readonly endsWith: string;
224
+ } | {
225
+ readonly contains: string;
226
+ } | {
227
+ readonly custom: string;
228
+ };
229
+ /**
230
+ * Condition value: either a primitive (equality shorthand),
231
+ * a cross-reference string ($actor.x, $resource.x, $env.x),
232
+ * or an operator object.
233
+ */
234
+ type ConditionValue = string | number | boolean | ConditionOperator;
235
+ /** Simple conditions: all key-value pairs ANDed together. */
236
+ type SimpleConditions = Record<string, ConditionValue>;
237
+ /**
238
+ * Recursive condition expression.
239
+ * Either simple conditions (Record<string, ConditionValue>),
240
+ * or a logical combinator ({ any: ... } or { all: ... }).
241
+ */
242
+ type ConditionExpression = SimpleConditions | {
243
+ readonly any: ConditionExpression[];
244
+ } | {
245
+ readonly all: ConditionExpression[];
246
+ };
247
+ /** Test case for declarative YAML tests. */
248
+ interface TestCase {
249
+ readonly name: string;
250
+ readonly actor: ActorRef;
251
+ /** Mock resolver data: keyed by "Type:id", values are attribute objects. */
252
+ readonly resolvers?: Record<string, Record<string, unknown>>;
253
+ readonly action: string;
254
+ readonly resource: ResourceRef;
255
+ readonly expected: "allow" | "deny";
256
+ }
257
+ /** Top-level policy object. */
258
+ interface Policy {
259
+ readonly version: "1";
260
+ readonly actors: Record<string, ActorDeclaration>;
261
+ readonly global_roles?: Record<string, GlobalRole>;
262
+ readonly resources: Record<string, ResourceBlock>;
263
+ readonly tests?: TestCase[];
264
+ }
265
+ /** Trace for a derived role showing derivation path. */
266
+ interface DerivedRoleTrace {
267
+ readonly role: string;
268
+ readonly via: string;
269
+ }
270
+ /** Resolved roles detail with direct and derived breakdown. */
271
+ interface ResolvedRolesDetail {
272
+ readonly direct: string[];
273
+ readonly derived: DerivedRoleTrace[];
274
+ }
275
+ /** A matched rule with evaluation context. */
276
+ interface MatchedRule {
277
+ readonly effect: "permit" | "forbid";
278
+ readonly matched: boolean;
279
+ readonly rule: Rule;
280
+ readonly resolvedValues: Record<string, unknown>;
281
+ }
282
+ /** Full decision trace from explain(). */
283
+ interface ExplainResult<S extends TorideSchema = DefaultSchema, R extends S["resources"] = S["resources"]> {
284
+ readonly allowed: boolean;
285
+ readonly resolvedRoles: ResolvedRolesDetail;
286
+ readonly grantedPermissions: S["permissionMap"][R][];
287
+ readonly matchedRules: MatchedRule[];
288
+ readonly finalDecision: string;
289
+ }
290
+ /** Audit event for authorization checks. */
291
+ interface DecisionEvent {
292
+ readonly actor: ActorRef;
293
+ readonly action: string;
294
+ readonly resource: ResourceRef;
295
+ readonly allowed: boolean;
296
+ readonly resolvedRoles: string[];
297
+ readonly matchedRules: {
298
+ effect: string;
299
+ matched: boolean;
300
+ }[];
301
+ readonly timestamp: Date;
302
+ }
303
+ /** Audit event for constraint queries. */
304
+ interface QueryEvent {
305
+ readonly actor: ActorRef;
306
+ readonly action: string;
307
+ readonly resourceType: string;
308
+ readonly resultType: "unrestricted" | "forbidden" | "constrained";
309
+ readonly timestamp: Date;
310
+ }
311
+ /** Thrown when policy validation fails. */
312
+ declare class ValidationError extends Error {
313
+ readonly path: string;
314
+ constructor(message: string, path: string);
315
+ }
316
+ /** Thrown when a cycle is detected in relation traversal. */
317
+ declare class CycleError extends Error {
318
+ readonly path: string[];
319
+ constructor(message: string, path: string[]);
320
+ }
321
+ /** Thrown when depth limit is exceeded. */
322
+ declare class DepthLimitError extends Error {
323
+ readonly limit: number;
324
+ readonly limitType: "condition" | "derivation";
325
+ constructor(message: string, limit: number, limitType: "condition" | "derivation");
326
+ }
327
+
328
+ /**
329
+ * A serializable map of permissions keyed by "Type:id".
330
+ * Values are arrays of permitted action strings.
331
+ * Suitable for JSON transport to client-side TorideClient.
332
+ *
333
+ * Generic over TorideSchema so that the type parameter flows through
334
+ * to TorideClient<S> on deserialization. The runtime structure is unchanged.
335
+ */
336
+ type PermissionSnapshot<S extends TorideSchema = DefaultSchema> = Record<string, string[]> & {
337
+ readonly __schema?: S | undefined;
338
+ };
339
+
340
+ declare const CLIENT_VERSION = "0.0.1";
341
+
342
+ /** Minimal resource reference for client-side lookups. Generic over S and R for type narrowing. */
343
+ interface ClientResourceRef<S extends TorideSchema = DefaultSchema, R extends S["resources"] = S["resources"]> {
344
+ readonly type: R;
345
+ readonly id: string;
346
+ }
347
+ /**
348
+ * Client-side permission checker that provides instant synchronous checks
349
+ * against a PermissionSnapshot received from the server.
350
+ *
351
+ * Generic over TorideSchema so that action names and resource types
352
+ * are validated at compile time when a concrete schema is provided.
353
+ *
354
+ * Default-deny: unknown resources or actions return false.
355
+ * The snapshot is defensively copied to prevent external mutation.
356
+ */
357
+ declare class TorideClient<S extends TorideSchema = DefaultSchema> {
358
+ private readonly permissions;
359
+ constructor(snapshot: PermissionSnapshot<S>);
360
+ /**
361
+ * Synchronous permission check.
362
+ * Returns true if the action is permitted for the resource, false otherwise.
363
+ * Unknown resources return false (default-deny).
364
+ */
365
+ can<R extends S["resources"]>(action: S["permissionMap"][R], resource: ClientResourceRef<S, R>): boolean;
366
+ /**
367
+ * Return the list of permitted actions for a resource.
368
+ * Returns empty array for unknown resources.
369
+ */
370
+ permittedActions<R extends S["resources"]>(resource: ClientResourceRef<S, R>): S["permissionMap"][R][];
371
+ }
372
+
373
+ export { type ActorRef as A, type BatchCheckItem as B, type CheckOptions as C, type DefaultSchema as D, type ExplainResult as E, type FieldAccessDef as F, type GlobalRole as G, type MatchedRule as M, type Policy as P, type QueryEvent as Q, type ResourceRef as R, type SimpleConditions as S, type TorideSchema as T, ValidationError as V, type TorideOptions as a, type PermissionSnapshot as b, type TestCase as c, type Resolvers as d, type ActorDeclaration as e, type AttributeType as f, type ClientResourceRef as g, type ConditionExpression as h, type ConditionOperator as i, type ConditionValue as j, CycleError as k, type DecisionEvent as l, DepthLimitError as m, type DerivedRoleEntry as n, type DerivedRoleTrace as o, type EvaluatorFn as p, type ResolvedRolesDetail as q, type ResourceBlock as r, type ResourceResolver as s, type Rule as t, TorideClient as u, CLIENT_VERSION as v };
package/dist/client.d.ts CHANGED
@@ -1,33 +1 @@
1
- import { P as PermissionSnapshot } from './snapshot-CcCYeU0Q.js';
2
-
3
- declare const CLIENT_VERSION = "0.0.1";
4
-
5
- /** Minimal resource reference for client-side lookups. */
6
- interface ClientResourceRef {
7
- readonly type: string;
8
- readonly id: string;
9
- }
10
- /**
11
- * Client-side permission checker that provides instant synchronous checks
12
- * against a PermissionSnapshot received from the server.
13
- *
14
- * Default-deny: unknown resources or actions return false.
15
- * The snapshot is defensively copied to prevent external mutation.
16
- */
17
- declare class TorideClient {
18
- private readonly permissions;
19
- constructor(snapshot: PermissionSnapshot);
20
- /**
21
- * Synchronous permission check.
22
- * Returns true if the action is permitted for the resource, false otherwise.
23
- * Unknown resources return false (default-deny).
24
- */
25
- can(action: string, resource: ClientResourceRef): boolean;
26
- /**
27
- * Return the list of permitted actions for a resource.
28
- * Returns empty array for unknown resources.
29
- */
30
- permittedActions(resource: ClientResourceRef): string[];
31
- }
32
-
33
- export { CLIENT_VERSION, type ClientResourceRef, PermissionSnapshot, TorideClient };
1
+ export { v as CLIENT_VERSION, g as ClientResourceRef, b as PermissionSnapshot, u as TorideClient } from './client-aExMng5T.js';
package/dist/client.js CHANGED
@@ -1,35 +1,7 @@
1
- // src/client.ts
2
- var CLIENT_VERSION = "0.0.1";
3
- var TorideClient = class {
4
- permissions;
5
- constructor(snapshot) {
6
- this.permissions = /* @__PURE__ */ new Map();
7
- for (const [key, actions] of Object.entries(snapshot)) {
8
- this.permissions.set(key, new Set(actions));
9
- }
10
- }
11
- /**
12
- * Synchronous permission check.
13
- * Returns true if the action is permitted for the resource, false otherwise.
14
- * Unknown resources return false (default-deny).
15
- */
16
- can(action, resource) {
17
- const key = `${resource.type}:${resource.id}`;
18
- const actions = this.permissions.get(key);
19
- if (!actions) return false;
20
- return actions.has(action);
21
- }
22
- /**
23
- * Return the list of permitted actions for a resource.
24
- * Returns empty array for unknown resources.
25
- */
26
- permittedActions(resource) {
27
- const key = `${resource.type}:${resource.id}`;
28
- const actions = this.permissions.get(key);
29
- if (!actions) return [];
30
- return [...actions];
31
- }
32
- };
1
+ import {
2
+ CLIENT_VERSION,
3
+ TorideClient
4
+ } from "./chunk-S6DKDABO.js";
33
5
  export {
34
6
  CLIENT_VERSION,
35
7
  TorideClient
package/dist/index.d.ts CHANGED
@@ -1,230 +1,5 @@
1
- import { P as PermissionSnapshot } from './snapshot-CcCYeU0Q.js';
2
-
3
- /** Represents an entity performing actions. */
4
- interface ActorRef {
5
- readonly type: string;
6
- readonly id: string;
7
- readonly attributes: Record<string, unknown>;
8
- }
9
- /** Represents a protected entity being accessed. */
10
- interface ResourceRef {
11
- readonly type: string;
12
- readonly id: string;
13
- /** Pre-fetched attributes. Inline values take precedence over resolver results. */
14
- readonly attributes?: Record<string, unknown>;
15
- }
16
- /** Optional per-check configuration. */
17
- interface CheckOptions {
18
- readonly env?: Record<string, unknown>;
19
- }
20
- /** A single item in a canBatch() call. */
21
- interface BatchCheckItem {
22
- readonly action: string;
23
- readonly resource: ResourceRef;
24
- }
25
- /**
26
- * Per-type resolver function.
27
- * Called when the engine needs attributes not available inline.
28
- * Called at most once per unique resource per evaluation (cached).
29
- */
30
- type ResourceResolver = (ref: ResourceRef) => Promise<Record<string, unknown>>;
31
- /**
32
- * Map of resource type names to their resolver functions.
33
- * Not all types need resolvers — types without resolvers use trivial resolution
34
- * (fields are undefined unless provided inline).
35
- */
36
- type Resolvers = Record<string, ResourceResolver>;
37
- /** Custom evaluator function signature. */
38
- type EvaluatorFn = (actor: ActorRef, resource: ResourceRef, env: Record<string, unknown>) => Promise<boolean>;
39
- /** Engine construction options. */
40
- interface TorideOptions {
41
- readonly policy: Policy;
42
- /** Per-type resolver map. Optional — engine works without resolvers if all data is inline. */
43
- readonly resolvers?: Resolvers;
44
- readonly maxConditionDepth?: number;
45
- readonly maxDerivedRoleDepth?: number;
46
- readonly customEvaluators?: Record<string, EvaluatorFn>;
47
- readonly onDecision?: (event: DecisionEvent) => void;
48
- readonly onQuery?: (event: QueryEvent) => void;
49
- }
50
- /** Attribute type for actor declarations. */
51
- type AttributeType = "string" | "number" | "boolean";
52
- /** Actor type declaration with attribute schema. */
53
- interface ActorDeclaration {
54
- readonly attributes: Record<string, AttributeType>;
55
- }
56
- /** Global role definition derived from actor attributes. */
57
- interface GlobalRole {
58
- readonly actor_type: string;
59
- readonly when: ConditionExpression;
60
- }
61
- /**
62
- * Derived role entry. Exactly one derivation pattern per entry.
63
- * Patterns:
64
- * 1. from_global_role
65
- * 2. from_role + on_relation
66
- * 3. from_relation
67
- * 4. actor_type + when (conditional)
68
- * 5. when only
69
- */
70
- interface DerivedRoleEntry {
71
- readonly role: string;
72
- readonly from_global_role?: string;
73
- readonly from_role?: string;
74
- readonly on_relation?: string;
75
- readonly from_relation?: string;
76
- readonly actor_type?: string;
77
- readonly when?: ConditionExpression;
78
- }
79
- /** Conditional rule (permit or forbid). */
80
- interface Rule {
81
- readonly effect: "permit" | "forbid";
82
- readonly roles?: string[];
83
- readonly permissions: string[];
84
- readonly when: ConditionExpression;
85
- }
86
- /** Field-level access control definition. */
87
- interface FieldAccessDef {
88
- readonly read?: string[];
89
- readonly update?: string[];
90
- }
91
- /** Resource block definition. */
92
- interface ResourceBlock {
93
- readonly roles: string[];
94
- readonly permissions: string[];
95
- /** Relations map field names to target resource type names (simplified). */
96
- readonly relations?: Record<string, string>;
97
- readonly grants?: Record<string, string[]>;
98
- readonly derived_roles?: DerivedRoleEntry[];
99
- readonly rules?: Rule[];
100
- readonly field_access?: Record<string, FieldAccessDef>;
101
- }
102
- /** Operator-based condition value. */
103
- type ConditionOperator = {
104
- readonly eq: unknown;
105
- } | {
106
- readonly neq: unknown;
107
- } | {
108
- readonly gt: unknown;
109
- } | {
110
- readonly gte: unknown;
111
- } | {
112
- readonly lt: unknown;
113
- } | {
114
- readonly lte: unknown;
115
- } | {
116
- readonly in: unknown[] | string;
117
- } | {
118
- readonly includes: unknown;
119
- } | {
120
- readonly exists: boolean;
121
- } | {
122
- readonly startsWith: string;
123
- } | {
124
- readonly endsWith: string;
125
- } | {
126
- readonly contains: string;
127
- } | {
128
- readonly custom: string;
129
- };
130
- /**
131
- * Condition value: either a primitive (equality shorthand),
132
- * a cross-reference string ($actor.x, $resource.x, $env.x),
133
- * or an operator object.
134
- */
135
- type ConditionValue = string | number | boolean | ConditionOperator;
136
- /** Simple conditions: all key-value pairs ANDed together. */
137
- type SimpleConditions = Record<string, ConditionValue>;
138
- /**
139
- * Recursive condition expression.
140
- * Either simple conditions (Record<string, ConditionValue>),
141
- * or a logical combinator ({ any: ... } or { all: ... }).
142
- */
143
- type ConditionExpression = SimpleConditions | {
144
- readonly any: ConditionExpression[];
145
- } | {
146
- readonly all: ConditionExpression[];
147
- };
148
- /** Test case for declarative YAML tests. */
149
- interface TestCase {
150
- readonly name: string;
151
- readonly actor: ActorRef;
152
- /** Mock resolver data: keyed by "Type:id", values are attribute objects. */
153
- readonly resolvers?: Record<string, Record<string, unknown>>;
154
- readonly action: string;
155
- readonly resource: ResourceRef;
156
- readonly expected: "allow" | "deny";
157
- }
158
- /** Top-level policy object. */
159
- interface Policy {
160
- readonly version: "1";
161
- readonly actors: Record<string, ActorDeclaration>;
162
- readonly global_roles?: Record<string, GlobalRole>;
163
- readonly resources: Record<string, ResourceBlock>;
164
- readonly tests?: TestCase[];
165
- }
166
- /** Trace for a derived role showing derivation path. */
167
- interface DerivedRoleTrace {
168
- readonly role: string;
169
- readonly via: string;
170
- }
171
- /** Resolved roles detail with direct and derived breakdown. */
172
- interface ResolvedRolesDetail {
173
- readonly direct: string[];
174
- readonly derived: DerivedRoleTrace[];
175
- }
176
- /** A matched rule with evaluation context. */
177
- interface MatchedRule {
178
- readonly effect: "permit" | "forbid";
179
- readonly matched: boolean;
180
- readonly rule: Rule;
181
- readonly resolvedValues: Record<string, unknown>;
182
- }
183
- /** Full decision trace from explain(). */
184
- interface ExplainResult {
185
- readonly allowed: boolean;
186
- readonly resolvedRoles: ResolvedRolesDetail;
187
- readonly grantedPermissions: string[];
188
- readonly matchedRules: MatchedRule[];
189
- readonly finalDecision: string;
190
- }
191
- /** Audit event for authorization checks. */
192
- interface DecisionEvent {
193
- readonly actor: ActorRef;
194
- readonly action: string;
195
- readonly resource: ResourceRef;
196
- readonly allowed: boolean;
197
- readonly resolvedRoles: string[];
198
- readonly matchedRules: {
199
- effect: string;
200
- matched: boolean;
201
- }[];
202
- readonly timestamp: Date;
203
- }
204
- /** Audit event for constraint queries. */
205
- interface QueryEvent {
206
- readonly actor: ActorRef;
207
- readonly action: string;
208
- readonly resourceType: string;
209
- readonly resultType: "unrestricted" | "forbidden" | "constrained";
210
- readonly timestamp: Date;
211
- }
212
- /** Thrown when policy validation fails. */
213
- declare class ValidationError extends Error {
214
- readonly path: string;
215
- constructor(message: string, path: string);
216
- }
217
- /** Thrown when a cycle is detected in relation traversal. */
218
- declare class CycleError extends Error {
219
- readonly path: string[];
220
- constructor(message: string, path: string[]);
221
- }
222
- /** Thrown when depth limit is exceeded. */
223
- declare class DepthLimitError extends Error {
224
- readonly limit: number;
225
- readonly limitType: "condition" | "derivation";
226
- constructor(message: string, limit: number, limitType: "condition" | "derivation");
227
- }
1
+ import { P as Policy, T as TorideSchema, D as DefaultSchema, a as TorideOptions, A as ActorRef, R as ResourceRef, C as CheckOptions, E as ExplainResult, b as PermissionSnapshot, B as BatchCheckItem, c as TestCase, d as Resolvers } from './client-aExMng5T.js';
2
+ export { e as ActorDeclaration, f as AttributeType, g as ClientResourceRef, h as ConditionExpression, i as ConditionOperator, j as ConditionValue, k as CycleError, l as DecisionEvent, m as DepthLimitError, n as DerivedRoleEntry, o as DerivedRoleTrace, p as EvaluatorFn, F as FieldAccessDef, G as GlobalRole, M as MatchedRule, Q as QueryEvent, q as ResolvedRolesDetail, r as ResourceBlock, s as ResourceResolver, t as Rule, S as SimpleConditions, u as TorideClient, V as ValidationError } from './client-aExMng5T.js';
228
3
 
229
4
  /**
230
5
  * Parse and validate a YAML string into a typed Policy object.
@@ -374,23 +149,32 @@ interface NeverConstraint {
374
149
  type Constraint = FieldEqConstraint | FieldNeqConstraint | FieldGtConstraint | FieldGteConstraint | FieldLtConstraint | FieldLteConstraint | FieldInConstraint | FieldNinConstraint | FieldExistsConstraint | FieldIncludesConstraint | FieldContainsConstraint | RelationConstraint | HasRoleConstraint | UnknownConstraint | AndConstraint | OrConstraint | NotConstraint | AlwaysConstraint | NeverConstraint;
375
150
  /** Leaf constraint subset for ConstraintAdapter.translate(). */
376
151
  type LeafConstraint = FieldEqConstraint | FieldNeqConstraint | FieldGtConstraint | FieldGteConstraint | FieldLtConstraint | FieldLteConstraint | FieldInConstraint | FieldNinConstraint | FieldExistsConstraint | FieldIncludesConstraint | FieldContainsConstraint;
377
- /** Result of partial evaluation. */
378
- type ConstraintResult = {
152
+ /** Result of partial evaluation, tagged with resource type R (phantom). */
153
+ type ConstraintResult<R extends string = string> = {
379
154
  readonly unrestricted: true;
155
+ readonly __resource?: R;
380
156
  } | {
381
157
  readonly forbidden: true;
158
+ readonly __resource?: R;
382
159
  } | {
383
160
  readonly constraints: Constraint;
161
+ readonly __resource?: R;
384
162
  };
385
- /** User-provided adapter for translating constraint ASTs to queries. */
386
- interface ConstraintAdapter<TQuery> {
387
- translate(constraint: LeafConstraint): TQuery;
388
- relation(field: string, resourceType: string, childQuery: TQuery): TQuery;
389
- hasRole(actorId: string, actorType: string, role: string): TQuery;
390
- unknown(name: string): TQuery;
391
- and(queries: TQuery[]): TQuery;
392
- or(queries: TQuery[]): TQuery;
393
- not(query: TQuery): TQuery;
163
+ /**
164
+ * User-provided adapter for translating constraint ASTs to queries.
165
+ * TQueryMap maps resource type names to their query output types.
166
+ *
167
+ * BREAKING CHANGE: Previously ConstraintAdapter<TQuery> with a single query type.
168
+ * Now uses a resource-to-query-type map for per-resource output typing.
169
+ */
170
+ interface ConstraintAdapter<TQueryMap extends Record<string, unknown> = Record<string, unknown>> {
171
+ translate(constraint: LeafConstraint): TQueryMap[string];
172
+ relation(field: string, resourceType: string, childQuery: TQueryMap[string]): TQueryMap[string];
173
+ hasRole(actorId: string, actorType: string, role: string): TQueryMap[string];
174
+ unknown(name: string): TQueryMap[string];
175
+ and(queries: TQueryMap[string][]): TQueryMap[string];
176
+ or(queries: TQueryMap[string][]): TQueryMap[string];
177
+ not(query: TQueryMap[string]): TQueryMap[string];
394
178
  }
395
179
 
396
180
  /**
@@ -398,16 +182,16 @@ interface ConstraintAdapter<TQuery> {
398
182
  * Creates a per-check cache, resolves direct roles, checks grants,
399
183
  * and returns boolean with default-deny semantics.
400
184
  */
401
- declare class Toride {
185
+ declare class Toride<S extends TorideSchema = DefaultSchema> {
402
186
  private policy;
403
187
  private readonly resolvers;
404
188
  private readonly options;
405
- constructor(options: TorideOptions);
189
+ constructor(options: TorideOptions<S>);
406
190
  /**
407
191
  * Check if an actor can perform an action on a resource.
408
192
  * Default-deny: returns false if resource type is unknown or no grants match.
409
193
  */
410
- can(actor: ActorRef, action: string, resource: ResourceRef, options?: CheckOptions): Promise<boolean>;
194
+ can<R extends S["resources"]>(actor: ActorRef<S>, action: S["permissionMap"][R], resource: ResourceRef<S, R>, options?: CheckOptions): Promise<boolean>;
411
195
  /**
412
196
  * T097: Atomic policy swap. In-flight checks capture the resource block
413
197
  * at the start of evaluateInternal, so they complete with the old policy.
@@ -418,49 +202,53 @@ declare class Toride {
418
202
  * T068: Return full ExplainResult with role derivation traces,
419
203
  * granted permissions, matched rules, and human-readable final decision.
420
204
  */
421
- explain(actor: ActorRef, action: string, resource: ResourceRef, options?: CheckOptions): Promise<ExplainResult>;
205
+ explain<R extends S["resources"]>(actor: ActorRef<S>, action: S["permissionMap"][R], resource: ResourceRef<S, R>, options?: CheckOptions): Promise<ExplainResult<S, R>>;
422
206
  /**
423
207
  * T069: Check all declared permissions for a resource and return permitted ones.
424
208
  * Uses a shared cache across all per-action evaluations.
425
209
  */
426
- permittedActions(actor: ActorRef, resource: ResourceRef, options?: CheckOptions): Promise<string[]>;
210
+ permittedActions<R extends S["resources"]>(actor: ActorRef<S>, resource: ResourceRef<S, R>, options?: CheckOptions): Promise<S["permissionMap"][R][]>;
427
211
  /**
428
212
  * T083: Generate a PermissionSnapshot for a list of resources.
429
213
  * Calls permittedActions() for each resource and returns a map
430
214
  * keyed by "Type:id" with arrays of permitted action strings.
431
215
  * Suitable for serializing to the client via TorideClient.
432
216
  */
433
- snapshot(actor: ActorRef, resources: ResourceRef[], options?: CheckOptions): Promise<PermissionSnapshot>;
217
+ snapshot(actor: ActorRef<S>, resources: ResourceRef<S>[], options?: CheckOptions): Promise<PermissionSnapshot<S>>;
434
218
  /**
435
219
  * T095: Check if an actor can perform a field-level operation on a specific field.
436
220
  * Restricted fields require the actor to have a role listed in field_access.
437
221
  * Unlisted fields are unrestricted: any actor with the resource-level permission can access them.
438
222
  */
439
- canField(actor: ActorRef, operation: "read" | "update", resource: ResourceRef, field: string, options?: CheckOptions): Promise<boolean>;
223
+ canField<R extends S["resources"]>(actor: ActorRef<S>, operation: "read" | "update", resource: ResourceRef<S, R>, field: keyof S["resourceAttributeMap"][R] & string, options?: CheckOptions): Promise<boolean>;
440
224
  /**
441
225
  * T095: Return the list of declared field_access field names the actor can access
442
226
  * for the given operation. Only returns explicitly declared fields.
443
227
  */
444
- permittedFields(actor: ActorRef, operation: "read" | "update", resource: ResourceRef, options?: CheckOptions): Promise<string[]>;
228
+ permittedFields<R extends S["resources"]>(actor: ActorRef<S>, operation: "read" | "update", resource: ResourceRef<S, R>, options?: CheckOptions): Promise<(keyof S["resourceAttributeMap"][R] & string)[]>;
445
229
  /**
446
230
  * T070: Return flat deduplicated list of all resolved roles (direct + derived).
447
231
  */
448
- resolvedRoles(actor: ActorRef, resource: ResourceRef, options?: CheckOptions): Promise<string[]>;
232
+ resolvedRoles<R extends S["resources"]>(actor: ActorRef<S>, resource: ResourceRef<S, R>, options?: CheckOptions): Promise<S["roleMap"][R][]>;
449
233
  /**
450
234
  * T071: Evaluate multiple checks for the same actor with a shared resolver cache.
451
235
  * Returns boolean[] in the same order as the input checks.
452
236
  */
453
- canBatch(actor: ActorRef, checks: BatchCheckItem[], options?: CheckOptions): Promise<boolean[]>;
237
+ canBatch(actor: ActorRef<S>, checks: BatchCheckItem<S>[], options?: CheckOptions): Promise<boolean[]>;
454
238
  /**
455
239
  * T064: Build constraint AST for partial evaluation / data filtering.
456
240
  * Returns ConstraintResult with unrestricted/forbidden sentinels or constraint AST.
457
241
  */
458
- buildConstraints(actor: ActorRef, action: string, resourceType: string, options?: CheckOptions): Promise<ConstraintResult>;
242
+ buildConstraints<R extends S["resources"]>(actor: ActorRef<S>, action: S["permissionMap"][R], resourceType: R, options?: CheckOptions): Promise<ConstraintResult<R>>;
459
243
  /**
460
244
  * T064: Translate constraint AST using an adapter.
461
245
  * Dispatches each constraint node to the adapter's methods.
246
+ *
247
+ * Accepts ConstraintResult<R> from buildConstraints() and returns
248
+ * TQueryMap[R] — the adapter's mapped output type for resource R.
249
+ * The resource type R is inferred from the ConstraintResult phantom type.
462
250
  */
463
- translateConstraints<TQuery>(constraints: Constraint, adapter: ConstraintAdapter<TQuery>): TQuery;
251
+ translateConstraints<R extends string, TQueryMap extends Record<string, unknown>>(constraints: ConstraintResult<R>, adapter: ConstraintAdapter<TQueryMap>): TQueryMap[R];
464
252
  /**
465
253
  * T072: Fire onDecision audit callback via microtask (non-blocking).
466
254
  * Errors are silently swallowed to prevent audit failures from affecting authorization.
@@ -480,7 +268,7 @@ declare class Toride {
480
268
  /**
481
269
  * Typed factory function for creating a Toride instance.
482
270
  */
483
- declare function createToride(options: TorideOptions): Toride;
271
+ declare function createToride<S extends TorideSchema = DefaultSchema>(options: TorideOptions<S>): Toride<S>;
484
272
 
485
273
  /**
486
274
  * Constructs a Resolvers map from test case mock data.
@@ -531,4 +319,4 @@ declare function runTestCases(policy: Policy, tests: TestCase[]): Promise<TestRe
531
319
 
532
320
  declare const VERSION = "0.0.1";
533
321
 
534
- export { type ActorDeclaration, type ActorRef, type AttributeType, type BatchCheckItem, type CheckOptions, type ConditionExpression, type ConditionOperator, type ConditionValue, type Constraint, type ConstraintAdapter, type ConstraintResult, CycleError, type DecisionEvent, DepthLimitError, type DerivedRoleEntry, type DerivedRoleTrace, type EvaluatorFn, type ExplainResult, type FieldAccessDef, type GlobalRole, type LeafConstraint, type MatchedRule, PermissionSnapshot, type Policy, type QueryEvent, type ResolvedRolesDetail, type Resolvers, type ResourceBlock, type ResourceRef, type ResourceResolver, type Rule, type SimpleConditions, type StrictValidationResult, type TestCase, type TestFileResult, type TestResult, Toride, type TorideOptions, VERSION, type ValidationDiagnostic, ValidationError, type ValidationResult, createMockResolver, createToride, loadJson, loadYaml, mergePolicies, parseInlineTests, parseTestFile, runTestCases, validatePolicy, validatePolicyResult, validatePolicyStrict };
322
+ export { ActorRef, BatchCheckItem, CheckOptions, type Constraint, type ConstraintAdapter, type ConstraintResult, DefaultSchema, ExplainResult, type LeafConstraint, PermissionSnapshot, Policy, Resolvers, ResourceRef, type StrictValidationResult, TestCase, type TestFileResult, type TestResult, Toride, TorideOptions, TorideSchema, VERSION, type ValidationDiagnostic, type ValidationResult, createMockResolver, createToride, loadJson, loadYaml, mergePolicies, parseInlineTests, parseTestFile, runTestCases, validatePolicy, validatePolicyResult, validatePolicyStrict };
package/dist/index.js CHANGED
@@ -12,7 +12,10 @@ import {
12
12
  validatePolicy,
13
13
  validatePolicyResult,
14
14
  validatePolicyStrict
15
- } from "./chunk-QE4FJZEG.js";
15
+ } from "./chunk-2AWXNP37.js";
16
+ import {
17
+ TorideClient
18
+ } from "./chunk-S6DKDABO.js";
16
19
 
17
20
  // src/policy/parser.ts
18
21
  import * as YAML from "yaml";
@@ -226,6 +229,7 @@ export {
226
229
  CycleError,
227
230
  DepthLimitError,
228
231
  Toride,
232
+ TorideClient,
229
233
  VERSION,
230
234
  ValidationError,
231
235
  createMockResolver,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toride",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Relation-aware authorization engine for TypeScript",
5
5
  "type": "module",
6
6
  "exports": {
@@ -27,8 +27,8 @@
27
27
  ]
28
28
  },
29
29
  "dependencies": {
30
- "yaml": "^2.3.0",
31
- "valibot": "^1.2.0"
30
+ "valibot": "^1.2.0",
31
+ "yaml": "^2.3.0"
32
32
  },
33
33
  "keywords": [
34
34
  "authorization",
@@ -42,9 +42,13 @@
42
42
  "type": "git",
43
43
  "url": "https://github.com/toride-auth/toride"
44
44
  },
45
+ "devDependencies": {
46
+ "tsd": "^0.33.0"
47
+ },
45
48
  "scripts": {
46
49
  "build": "tsup",
47
50
  "test": "vitest run",
51
+ "typetest": "tsd --files 'src/__typetests__/*.test-d.ts'",
48
52
  "lint": "tsc --noEmit",
49
53
  "bench": "vitest bench"
50
54
  }
@@ -1,8 +0,0 @@
1
- /**
2
- * A serializable map of permissions keyed by "Type:id".
3
- * Values are arrays of permitted action strings.
4
- * Suitable for JSON transport to client-side TorideClient.
5
- */
6
- type PermissionSnapshot = Record<string, string[]>;
7
-
8
- export type { PermissionSnapshot as P };