{"version":3,"sources":["../src/context.tsx"],"names":["createContext","useMemo","useContext","useCallback"],"mappings":";;;;;AAwEA,IAAM,mBAAA,GAAyC;AAAA,EAC7C,KAAK,MAAM,IAAA;AAAA,EACX,IAAA,EAAM;AACR,CAAA;AAEA,IAAM,sBAAA,GAAyBA,oBAAiC,mBAAmB,CAAA;AAa5E,SAAS,kBAAA,CAAmB;AAAA,EACjC,KAAA;AAAA,EACA;AACF,CAAA,EAA4B;AAC1B,EAAA,MAAM,MAAA,GAASC,aAAA;AAAA,IACb,OAAO,EAAE,GAAG,mBAAA,EAAqB,GAAG,KAAA,EAAM,CAAA;AAAA,IAC1C,CAAC,KAAK;AAAA,GACR;AACA,EAAA,sCACG,sBAAA,CAAuB,QAAA,EAAvB,EAAgC,KAAA,EAAO,QACrC,QAAA,EACH,CAAA;AAEJ;AAOO,SAAS,cAAA,GAAoC;AAClD,EAAA,OAAOC,iBAAW,sBAAsB,CAAA;AAC1C;AAQO,SAAS,kBAAA,CACd,UACA,OAAA,EACmB;AACnB,EAAA,IAAI,CAAC,UAAU,OAAO,OAAA;AACtB,EAAA,OAAO,EAAE,GAAG,OAAA,EAAS,GAAG,QAAA,EAAS;AACnC;AAOO,SAAS,kBAAA,CACd,GAAA,EACA,MAAA,EACA,QAAA,EACA,KAAA,EACiD;AACjD,EAAA,MAAM,OAAA,GAAU,GAAA,CAAI,GAAA,CAAI,MAAA,EAAQ,UAAU,KAAK,CAAA;AAC/C,EAAA,MAAM,aAAa,OAAA,GAAU,IAAA,GAAQ,IAAI,UAAA,GAAa,MAAA,EAAQ,QAAQ,CAAA,IAAK,IAAA;AAC3E,EAAA,OAAO,EAAE,SAAS,UAAA,EAAW;AAC/B;AAwDA,IAAM,kBAA6B,MAAM;AAAC,CAAA;AAE1C,IAAM,iBAAA,GAAoBF,oBAAyB,eAAe,CAAA;AAY3D,SAAS,aAAA,CAAc,EAAE,IAAA,EAAM,QAAA,EAAS,EAAuB;AACpE,EAAA,sCACG,iBAAA,CAAkB,QAAA,EAAlB,EAA2B,KAAA,EAAO,MAChC,QAAA,EACH,CAAA;AAEJ;AAcO,SAAS,QAAA,GAA6C;AAC3D,EAAA,MAAM,IAAA,GAAOE,iBAAW,iBAAiB,CAAA;AACzC,EAAA,OAAOC,iBAAA;AAAA,IACL,CAAC,KAAA,KAAU;AACT,MAAA,IAAA,CAAK,EAAE,GAAG,KAAA,EAAO,SAAA,EAAA,qBAAe,IAAA,EAAK,EAAE,WAAA,EAAY,EAAG,CAAA;AAAA,IACxD,CAAA;AAAA,IACA,CAAC,IAAI;AAAA,GACP;AACF","file":"context.cjs","sourcesContent":["/**\n * @oshon-ai/primitives — shared RBAC + audit plumbing.\n *\n * Every interactive primitive reads permissions from `PermissionContext`\n * and fires `AuditEvent` through `useAudit()`. This is principle #3\n * (\"RBAC + audit by default\") from the Oshon engineering principles.\n *\n * Shapes match ARCHITECTURE.md §10 verbatim — an action/resource\n * permission model (`can(action, resource, attrs)`) plus a structured\n * `AuditEvent` (ISO-8601 timestamp, regime, entity, details, actor,\n * permissionResult, denyReason). Data components in Phase 4+ inherit\n * these same contracts without reshaping.\n *\n * Phase 3a ships the contract + a no-op default provider. Phase 5+ wires\n * `PermissionProvider` up to a real auth source and `AuditSink` to a real\n * telemetry surface.\n *\n * Canonical action names live in `./actions.ts` — see ADR-004.\n */\n\n'use client';\n\nimport { createContext, useCallback, useContext, useMemo } from 'react';\nimport type { ReactNode } from 'react';\n\n// ────────────────────────────────────────────────────────────────────────────\n// PermissionContext (ARCHITECTURE.md §10)\n\n/**\n * Three-way fallback for how a primitive presents a denied action:\n *   - `'disabled'` — the affordance is visible, greyed out, `aria-disabled`.\n *     The default. Keeps the UI discoverable: users see what they can't do.\n *   - `'hidden'`   — the affordance returns `null`. Use when the action's\n *     mere presence would leak information (e.g. an \"Approve\" button that\n *     reveals a role to unprivileged users).\n *   - `'readOnly'` — the affordance is visible and enabled but non-mutating.\n *     Use for view-only data displays that keep their navigation intact.\n */\nexport type PermissionMode = 'disabled' | 'hidden' | 'readOnly';\n\n/**\n * A permission predicate. Consumers implement the policy; primitives only\n * ask questions. `action` + `resource` name what's being attempted;\n * `attrs` is an open bag for attribute-based checks (row ID, tenant,\n * owner, etc.). Return `true` to allow, `false` to deny.\n */\nexport type PermissionChecker = (\n  action: string,\n  resource: string,\n  attrs?: Record<string, unknown>,\n) => boolean;\n\n/**\n * Reason factory invoked when a check returned false. Returns a\n * human-readable string (localized by the consumer) or `null` to\n * suppress the tooltip / aria-description. Primitives surface this via\n * the `denyReason` prop on the underlying DOM element.\n */\nexport type DenyReasonFactory = (\n  action: string,\n  resource: string,\n) => string | null;\n\nexport interface PermissionContext {\n  /** Policy predicate. Default (no provider) allows everything. */\n  can: PermissionChecker;\n  /** How denied actions present. Defaults to `'disabled'`. */\n  mode?: PermissionMode;\n  /** Optional deny-reason factory surfaced to the user. */\n  denyReason?: DenyReasonFactory;\n}\n\nconst DEFAULT_PERMISSIONS: PermissionContext = {\n  can: () => true,\n  mode: 'disabled',\n};\n\nconst PermissionReactContext = createContext<PermissionContext>(DEFAULT_PERMISSIONS);\n\nexport interface PermissionProviderProps {\n  /** Partial value — merges over the allow-everything default. */\n  value: Partial<PermissionContext>;\n  children: ReactNode;\n}\n\n/**\n * Scope a permission policy to a subtree. Partial values merge over the\n * default `{ can: () => true, mode: 'disabled' }` so a provider declaring\n * only `{ can: appCan }` inherits the default mode.\n */\nexport function PermissionProvider({\n  value,\n  children,\n}: PermissionProviderProps) {\n  const merged = useMemo<PermissionContext>(\n    () => ({ ...DEFAULT_PERMISSIONS, ...value }),\n    [value],\n  );\n  return (\n    <PermissionReactContext.Provider value={merged}>\n      {children}\n    </PermissionReactContext.Provider>\n  );\n}\n\n/**\n * Read the effective permission policy for the current subtree. Primitives\n * use this to ask `can('view', 'dialog')`, `can('edit', 'row', { id })`,\n * etc. and branch on the result.\n */\nexport function usePermissions(): PermissionContext {\n  return useContext(PermissionReactContext);\n}\n\n/**\n * Resolve a primitive's effective policy given both a prop-level\n * `permissions` override and the context. Prop wins — consumers pinning\n * `permissions={{ mode: 'hidden' }}` on a single instance override the\n * ambient policy for that subtree only.\n */\nexport function resolvePermissions(\n  override: Partial<PermissionContext> | undefined,\n  context: PermissionContext,\n): PermissionContext {\n  if (!override) return context;\n  return { ...context, ...override };\n}\n\n/**\n * Evaluate a single check against the resolved policy. Returns a tuple\n * `[granted, denyReason]` so primitives can emit audit events with the\n * full deny rationale without duplicating the factory call.\n */\nexport function evaluatePermission(\n  ctx: PermissionContext,\n  action: string,\n  resource: string,\n  attrs?: Record<string, unknown>,\n): { granted: boolean; denyReason: string | null } {\n  const granted = ctx.can(action, resource, attrs);\n  const denyReason = granted ? null : (ctx.denyReason?.(action, resource) ?? null);\n  return { granted, denyReason };\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// AuditContext (ARCHITECTURE.md §10)\n\n/**\n * The six-regime data-density enum used by `AuditEvent.regime`. Defined\n * here (not imported from `@oshon-ai/data`) so `@oshon-ai/primitives` has\n * no upward dependency — data components consume primitives, not the\n * other way around.\n */\nexport type DensityRegime =\n  | 'card'\n  | 'standard'\n  | 'paginated'\n  | 'virtualized'\n  | 'server'\n  | 'infinite';\n\n/**\n * Structured audit event emitted by every interactive primitive and\n * (in Phase 4+) by every data component. Matches ARCHITECTURE.md §10\n * verbatim. Transport is the consumer's responsibility — primitives only\n * emit; the `AuditSink` hands events off to Segment / Datadog / internal\n * bus wiring.\n *\n * PII rule: `details` may reference entity IDs, not full payloads\n * (principle #11).\n */\nexport interface AuditEvent {\n  /** ISO-8601 timestamp, assigned at emission by `useAudit()`. */\n  timestamp: string;\n  /** Stable primitive / component name, e.g. `'button'`, `'DataTable'`. */\n  component: string;\n  /**\n   * Namespaced verb. Primitives draw from `CANONICAL_ACTIONS` (see\n   * `./actions.ts` + ADR-004). Data components extend with `data:*` and\n   * `row:*` verbs per §10.\n   */\n  action: string;\n  /** Current density regime (data components only — undefined for primitives). */\n  regime?: DensityRegime;\n  /** Entity acted on. `{ type: 'order', id: '123' }` shape. No PII. */\n  entity?: { type: string; id: string };\n  /** Action-specific context. Shallow, JSON-serializable, no PII. */\n  details?: Record<string, unknown>;\n  /** Who did it. Consumer supplies; primitives never forge an actor. */\n  actor?: { id: string; role?: string };\n  /** Whether the permission gate allowed the action. */\n  permissionResult?: 'granted' | 'denied';\n  /** Populated when `permissionResult === 'denied'`. */\n  denyReason?: string;\n}\n\nexport type AuditSink = (event: AuditEvent) => void;\n\nconst NOOP_AUDIT_SINK: AuditSink = () => {};\n\nconst AuditReactContext = createContext<AuditSink>(NOOP_AUDIT_SINK);\n\nexport interface AuditProviderProps {\n  sink: AuditSink;\n  children: ReactNode;\n}\n\n/**\n * Route audit events from every descendant primitive into a single sink.\n * In tests pass a `vi.fn()`; in production route to your telemetry /\n * audit-log pipeline.\n */\nexport function AuditProvider({ sink, children }: AuditProviderProps) {\n  return (\n    <AuditReactContext.Provider value={sink}>\n      {children}\n    </AuditReactContext.Provider>\n  );\n}\n\n/**\n * Input to `useAudit()`'s returned emitter. Caller supplies the payload;\n * the hook stamps `timestamp` automatically so event ordering is reliable\n * regardless of sink implementation.\n */\nexport type AuditEventInput = Omit<AuditEvent, 'timestamp'>;\n\n/**\n * Return a stable emitter bound to the nearest `AuditProvider`. If no\n * provider is mounted, the emitter is a no-op — primitives can emit\n * events unconditionally and unwired apps simply discard them.\n */\nexport function useAudit(): (event: AuditEventInput) => void {\n  const sink = useContext(AuditReactContext);\n  return useCallback(\n    (event) => {\n      sink({ ...event, timestamp: new Date().toISOString() });\n    },\n    [sink],\n  );\n}\n"]}