@toride/codegen
Advanced tools
| // src/generator.ts | ||
| var SAFE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/; | ||
| var ATTRIBUTE_TYPE_MAP = { | ||
| string: "string", | ||
| number: "number", | ||
| boolean: "boolean" | ||
| }; | ||
| function escapeStringLiteral(s) { | ||
| return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); | ||
| } | ||
| function assertSafeIdentifier(s, context) { | ||
| if (!SAFE_IDENTIFIER.test(s)) { | ||
| throw new Error( | ||
| `Unsafe identifier in ${context}: "${escapeStringLiteral(s)}". Identifiers must match ${SAFE_IDENTIFIER.source}` | ||
| ); | ||
| } | ||
| } | ||
| function mapAttributeType(attrType, context) { | ||
| const mapped = ATTRIBUTE_TYPE_MAP[attrType]; | ||
| if (!mapped) { | ||
| throw new Error( | ||
| `Unknown attribute type "${escapeStringLiteral(attrType)}" in ${context}. Supported types: ${Object.keys(ATTRIBUTE_TYPE_MAP).join(", ")}` | ||
| ); | ||
| } | ||
| return mapped; | ||
| } | ||
| function generateAttributeFields(attrs, entityType, entityName) { | ||
| const fields = Object.entries(attrs).map(([k, v]) => `${k}: ${mapAttributeType(v, `attribute "${k}" in ${entityType} "${entityName}"`)}`).join("; "); | ||
| return `{ ${fields}; }`; | ||
| } | ||
| function generateTypes(policy) { | ||
| const resourceEntries = Object.entries(policy.resources); | ||
| const actorEntries = Object.entries(policy.actors); | ||
| const resourceNames = resourceEntries.map(([name]) => name); | ||
| const actorNames = actorEntries.map(([name]) => name); | ||
| const lines = []; | ||
| lines.push("// Auto-generated by @toride/codegen \u2014 do not edit"); | ||
| lines.push(""); | ||
| lines.push('import type { TorideSchema } from "toride";'); | ||
| lines.push(""); | ||
| for (const name of resourceNames) { | ||
| assertSafeIdentifier(name, "resource name"); | ||
| } | ||
| for (const [rName, block] of resourceEntries) { | ||
| for (const role of block.roles) { | ||
| assertSafeIdentifier(role, `role in resource "${rName}"`); | ||
| } | ||
| for (const perm of block.permissions) { | ||
| assertSafeIdentifier(perm, `permission in resource "${rName}"`); | ||
| } | ||
| if (block.relations) { | ||
| for (const [relName, relDef] of Object.entries(block.relations)) { | ||
| assertSafeIdentifier(relName, `relation name in resource "${rName}"`); | ||
| assertSafeIdentifier(relDef, `relation target in resource "${rName}"`); | ||
| } | ||
| } | ||
| if (block.attributes) { | ||
| for (const [attrName, attrType] of Object.entries(block.attributes)) { | ||
| assertSafeIdentifier(attrName, `attribute name in resource "${rName}"`); | ||
| mapAttributeType(attrType, `attribute "${attrName}" in resource "${rName}"`); | ||
| } | ||
| } | ||
| } | ||
| for (const [actorName, actorDecl] of actorEntries) { | ||
| assertSafeIdentifier(actorName, "actor type name"); | ||
| for (const [attrName, attrType] of Object.entries(actorDecl.attributes)) { | ||
| assertSafeIdentifier(attrName, `attribute name in actor "${actorName}"`); | ||
| mapAttributeType(attrType, `attribute "${attrName}" in actor "${actorName}"`); | ||
| } | ||
| } | ||
| const allActions = /* @__PURE__ */ new Set(); | ||
| for (const [, block] of resourceEntries) { | ||
| for (const perm of block.permissions) { | ||
| allActions.add(perm); | ||
| } | ||
| } | ||
| lines.push(`/** All action strings declared across all resources */`); | ||
| if (allActions.size === 0) { | ||
| lines.push("export type Actions = never;"); | ||
| } else { | ||
| const actionUnion = [...allActions].map((a) => `"${a}"`).join(" | "); | ||
| lines.push(`export type Actions = ${actionUnion};`); | ||
| } | ||
| lines.push(""); | ||
| lines.push(`/** All resource type names */`); | ||
| if (resourceNames.length === 0) { | ||
| lines.push("export type Resources = never;"); | ||
| } else { | ||
| const resourceUnion = resourceNames.map((r) => `"${r}"`).join(" | "); | ||
| lines.push(`export type Resources = ${resourceUnion};`); | ||
| } | ||
| lines.push(""); | ||
| lines.push(`/** All actor type names */`); | ||
| if (actorNames.length === 0) { | ||
| lines.push("export type ActorTypes = never;"); | ||
| } else { | ||
| const actorUnion = actorNames.map((a) => `"${a}"`).join(" | "); | ||
| lines.push(`export type ActorTypes = ${actorUnion};`); | ||
| } | ||
| lines.push(""); | ||
| lines.push(`/** Per-resource role types */`); | ||
| lines.push(`export interface RoleMap {`); | ||
| for (const [name, block] of resourceEntries) { | ||
| if (block.roles.length > 0) { | ||
| const roleUnion = block.roles.map((r) => `"${r}"`).join(" | "); | ||
| lines.push(` ${name}: ${roleUnion};`); | ||
| } else { | ||
| lines.push(` ${name}: never;`); | ||
| } | ||
| } | ||
| lines.push(`}`); | ||
| lines.push(""); | ||
| lines.push(`/** Per-resource permission types */`); | ||
| lines.push(`export interface PermissionMap {`); | ||
| for (const [name, block] of resourceEntries) { | ||
| if (block.permissions.length > 0) { | ||
| const permUnion = block.permissions.map((p) => `"${p}"`).join(" | "); | ||
| lines.push(` ${name}: ${permUnion};`); | ||
| } else { | ||
| lines.push(` ${name}: never;`); | ||
| } | ||
| } | ||
| lines.push(`}`); | ||
| lines.push(""); | ||
| lines.push(`/** Per-resource attribute types */`); | ||
| lines.push(`export interface ResourceAttributeMap {`); | ||
| for (const [name, block] of resourceEntries) { | ||
| if (block.attributes && Object.keys(block.attributes).length > 0) { | ||
| lines.push(` ${name}: ${generateAttributeFields(block.attributes, "resource", name)};`); | ||
| } else { | ||
| lines.push(` ${name}: Record<string, unknown>;`); | ||
| } | ||
| } | ||
| lines.push(`}`); | ||
| lines.push(""); | ||
| lines.push(`/** Per-actor attribute types */`); | ||
| lines.push(`export interface ActorAttributeMap {`); | ||
| for (const [name, actorDecl] of actorEntries) { | ||
| const attrKeys = Object.keys(actorDecl.attributes); | ||
| if (attrKeys.length > 0) { | ||
| lines.push(` ${name}: ${generateAttributeFields(actorDecl.attributes, "actor", name)};`); | ||
| } else { | ||
| lines.push(` ${name}: Record<string, unknown>;`); | ||
| } | ||
| } | ||
| lines.push(`}`); | ||
| lines.push(""); | ||
| lines.push(`/** Relation map \u2014 resource type -> relation name -> target resource type */`); | ||
| lines.push(`export interface RelationMap {`); | ||
| for (const [name, block] of resourceEntries) { | ||
| const relations = block.relations ?? {}; | ||
| const relEntries = Object.entries(relations); | ||
| if (relEntries.length === 0) { | ||
| lines.push(` ${name}: Record<string, never>;`); | ||
| } else { | ||
| lines.push(` ${name}: {`); | ||
| for (const [relName, relDef] of relEntries) { | ||
| lines.push(` ${relName}: "${relDef}";`); | ||
| } | ||
| lines.push(` };`); | ||
| } | ||
| } | ||
| lines.push(`}`); | ||
| lines.push(""); | ||
| lines.push(`/** Per-type resolver map \u2014 typed return values match resource attributes */`); | ||
| lines.push(`export type ResolverMap = {`); | ||
| lines.push(` [R in Resources]?: (`); | ||
| lines.push(` ref: { type: R; id: string; attributes?: ResourceAttributeMap[R] },`); | ||
| lines.push(` ) => Promise<ResourceAttributeMap[R]>;`); | ||
| lines.push(`};`); | ||
| lines.push(""); | ||
| lines.push(`/**`); | ||
| lines.push(` * Unified schema interface \u2014 pass this as Toride<GeneratedSchema>.`); | ||
| lines.push(` * Aggregates all type maps into the TorideSchema shape.`); | ||
| lines.push(` */`); | ||
| lines.push(`export interface GeneratedSchema extends TorideSchema {`); | ||
| lines.push(` resources: Resources;`); | ||
| lines.push(` actions: Actions;`); | ||
| lines.push(` actorTypes: ActorTypes;`); | ||
| lines.push(` permissionMap: PermissionMap;`); | ||
| lines.push(` roleMap: RoleMap;`); | ||
| lines.push(` resourceAttributeMap: ResourceAttributeMap;`); | ||
| lines.push(` actorAttributeMap: ActorAttributeMap;`); | ||
| lines.push(` relationMap: RelationMap;`); | ||
| lines.push(`}`); | ||
| lines.push(""); | ||
| return lines.join("\n"); | ||
| } | ||
| export { | ||
| generateTypes | ||
| }; |
+1
-1
| #!/usr/bin/env node | ||
| import { | ||
| generateTypes | ||
| } from "./chunk-ON3XL4K7.js"; | ||
| } from "./chunk-XMLNKEBL.js"; | ||
@@ -6,0 +6,0 @@ // src/cli.ts |
+1
-1
| import { | ||
| generateTypes | ||
| } from "./chunk-ON3XL4K7.js"; | ||
| } from "./chunk-XMLNKEBL.js"; | ||
@@ -5,0 +5,0 @@ // src/index.ts |
+2
-2
| { | ||
| "name": "@toride/codegen", | ||
| "version": "0.1.0", | ||
| "version": "0.2.0", | ||
| "description": "Code generation tools for Toride authorization engine", | ||
@@ -26,3 +26,3 @@ "type": "module", | ||
| "dependencies": { | ||
| "toride": "0.1.0" | ||
| "toride": "0.2.0" | ||
| }, | ||
@@ -29,0 +29,0 @@ "publishConfig": { |
| // src/generator.ts | ||
| var SAFE_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/; | ||
| function escapeStringLiteral(s) { | ||
| return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); | ||
| } | ||
| function assertSafeIdentifier(s, context) { | ||
| if (!SAFE_IDENTIFIER.test(s)) { | ||
| throw new Error( | ||
| `Unsafe identifier in ${context}: "${escapeStringLiteral(s)}". Identifiers must match ${SAFE_IDENTIFIER.source}` | ||
| ); | ||
| } | ||
| } | ||
| function generateTypes(policy) { | ||
| const resourceNames = Object.keys(policy.resources); | ||
| const lines = []; | ||
| lines.push("// Auto-generated by @toride/codegen \u2014 do not edit"); | ||
| lines.push(""); | ||
| for (const name of resourceNames) { | ||
| assertSafeIdentifier(name, "resource name"); | ||
| } | ||
| for (const [rName, block] of Object.entries(policy.resources)) { | ||
| for (const role of block.roles) { | ||
| assertSafeIdentifier(role, `role in resource "${rName}"`); | ||
| } | ||
| for (const perm of block.permissions) { | ||
| assertSafeIdentifier(perm, `permission in resource "${rName}"`); | ||
| } | ||
| if (block.relations) { | ||
| for (const [relName, relDef] of Object.entries(block.relations)) { | ||
| assertSafeIdentifier(relName, `relation name in resource "${rName}"`); | ||
| assertSafeIdentifier(relDef, `relation target in resource "${rName}"`); | ||
| } | ||
| } | ||
| } | ||
| const allActions = /* @__PURE__ */ new Set(); | ||
| for (const block of Object.values(policy.resources)) { | ||
| for (const perm of block.permissions) { | ||
| allActions.add(perm); | ||
| } | ||
| } | ||
| lines.push(`/** All action strings declared across all resources */`); | ||
| if (allActions.size === 0) { | ||
| lines.push("export type Actions = never;"); | ||
| } else { | ||
| const actionUnion = [...allActions].map((a) => `"${a}"`).join(" | "); | ||
| lines.push(`export type Actions = ${actionUnion};`); | ||
| } | ||
| lines.push(""); | ||
| lines.push(`/** All resource type names */`); | ||
| if (resourceNames.length === 0) { | ||
| lines.push("export type Resources = never;"); | ||
| } else { | ||
| const resourceUnion = resourceNames.map((r) => `"${r}"`).join(" | "); | ||
| lines.push(`export type Resources = ${resourceUnion};`); | ||
| } | ||
| lines.push(""); | ||
| lines.push(`/** Per-resource role types */`); | ||
| lines.push(`export interface RoleMap {`); | ||
| for (const [name, block] of Object.entries(policy.resources)) { | ||
| if (block.roles.length > 0) { | ||
| const roleUnion = block.roles.map((r) => `"${r}"`).join(" | "); | ||
| lines.push(` ${name}: ${roleUnion};`); | ||
| } else { | ||
| lines.push(` ${name}: never;`); | ||
| } | ||
| } | ||
| lines.push(`}`); | ||
| lines.push(""); | ||
| lines.push(`/** Per-resource permission types */`); | ||
| lines.push(`export interface PermissionMap {`); | ||
| for (const [name, block] of Object.entries(policy.resources)) { | ||
| if (block.permissions.length > 0) { | ||
| const permUnion = block.permissions.map((p) => `"${p}"`).join(" | "); | ||
| lines.push(` ${name}: ${permUnion};`); | ||
| } else { | ||
| lines.push(` ${name}: never;`); | ||
| } | ||
| } | ||
| lines.push(`}`); | ||
| lines.push(""); | ||
| lines.push(`/** Relation map \u2014 resource type -> relation name -> target resource type */`); | ||
| lines.push(`export interface RelationMap {`); | ||
| for (const [name, block] of Object.entries(policy.resources)) { | ||
| const relations = block.relations ?? {}; | ||
| const relEntries = Object.entries(relations); | ||
| if (relEntries.length === 0) { | ||
| lines.push(` ${name}: Record<string, never>;`); | ||
| } else { | ||
| lines.push(` ${name}: {`); | ||
| for (const [relName, relDef] of relEntries) { | ||
| lines.push(` ${relName}: "${relDef}";`); | ||
| } | ||
| lines.push(` };`); | ||
| } | ||
| } | ||
| lines.push(`}`); | ||
| lines.push(""); | ||
| lines.push(`/** Per-type resolver map \u2014 TypeScript ensures all resource types have resolvers */`); | ||
| lines.push(`export type ResolverMap = {`); | ||
| lines.push(` [R in Resources]?: (`); | ||
| lines.push(` ref: { type: R; id: string; attributes?: Record<string, unknown> },`); | ||
| lines.push(` ) => Promise<Record<string, unknown>>;`); | ||
| lines.push(`};`); | ||
| lines.push(""); | ||
| return lines.join("\n"); | ||
| } | ||
| export { | ||
| generateTypes | ||
| }; |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
10807
44.77%285
40.39%1
Infinity%+ Added
- Removed
Updated