cvx-kit 0.1.10 → 0.1.11

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/CHANGELOG.md CHANGED
@@ -12,6 +12,23 @@ are this file's matching section.
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ## [0.1.11] - 2026-10-09
16
+
17
+ ### Added
18
+
19
+ - Configure shared post-auth Effect services and command/query handles once across
20
+ all auth, role, admin, and system constructors with `createEffectAuthFunctions` (#24).
21
+ - Keep query, mutation, action, and internal system capabilities separately typed
22
+ and configured; preserve secured database wrapping and live action membership.
23
+ - Document the shared application factory with packed-compiled examples and
24
+ constructor capability guidance for coding agents.
25
+
26
+ ### Verified
27
+
28
+ - Cover denied authentication, roles and membership, request-local scopes, context
29
+ collisions, nested command auditing, and registered mutation rollback.
30
+ - Preserve packed native auth context compatibility on Convex 1.45 and 1.46.
31
+
15
32
  ## [0.1.10] - 2026-10-09
16
33
 
17
34
  ### Fixed
package/dist/effect.d.mts CHANGED
@@ -1,12 +1,13 @@
1
1
  import { OperationExecutor, OperationPublicResult, OperationTool, OperationToolConverter, OperationToolDefinition, OperationToolJsonSchema, OperationToolOutcome, SelectedOperation, bindOperationExecutor, createOperationTools, operationToolDialect, selectOperation } from "./modules/contracts/exposure.mjs";
2
2
  import { ContractDecoderError, ContractDecoderRequirements, ContractValidationError, EffectDecoder, decodeEffectContract, effectContract, effectSchema, effectStandardSchema } from "./modules/effect/schema.mjs";
3
3
  import { CallbackError, CallbackRequirements, EffectMiddlewareInput, EffectOperationArgument, EffectOperationError, EffectOperationHandlerResult, EffectOperationInput, EffectOperationRequirements, EffectOperationResult, EffectPreparation, EffectValue, effectOperationFactory } from "./modules/effect/operation.mjs";
4
+ import { EffectZodApiBuilder, effectZodApiBuilder } from "./modules/effect/api-zod.mjs";
5
+ import { EffectAuthFunctions, createEffectAuthFunctions } from "./modules/effect/auth.mjs";
4
6
  import { EffectFoundationOptions, createEffectFoundation } from "./modules/effect/foundation.mjs";
5
7
  import { EffectIdempotencyPreparation, effectTransactionalIdempotency } from "./modules/effect/idempotency.mjs";
6
8
  import { EffectCrudConfig, EffectCrudContext, EffectCrudPage, EffectCrudPagination, EffectCrudRepository, EffectCrudTable, createEffectCrud } from "./modules/effect/crud.mjs";
7
9
  import { EffectApiBuilder, effectApiBuilder } from "./modules/effect/api.mjs";
8
- import { EffectZodApiBuilder, effectZodApiBuilder } from "./modules/effect/api-zod.mjs";
9
10
  import { projectErrorCause } from "./modules/effect/errors.mjs";
10
11
  import { EffectReceiptProtectedWorkflowMutation, effectReceiptWorkflowMutation, effectTerminalWorkflowAttempt, effectWorkflowAttempt, effectWorkflowStatusBindings } from "./modules/effect/workflow.mjs";
11
12
  import "./modules/effect/client.mjs";
12
- export { type CallbackError, type CallbackRequirements, type ContractDecoderError, type ContractDecoderRequirements, ContractValidationError, type EffectApiBuilder, type EffectCrudConfig, type EffectCrudContext, type EffectCrudPage, type EffectCrudPagination, type EffectCrudRepository, type EffectCrudTable, type EffectDecoder, type EffectFoundationOptions, type EffectIdempotencyPreparation, type EffectMiddlewareInput, type EffectOperationArgument, type EffectOperationError, type EffectOperationHandlerResult, type EffectOperationInput, type EffectOperationRequirements, type EffectOperationResult, type EffectPreparation, type EffectReceiptProtectedWorkflowMutation, type EffectValue, type EffectZodApiBuilder, type OperationExecutor, type OperationPublicResult, type OperationTool, type OperationToolConverter, type OperationToolDefinition, type OperationToolJsonSchema, type OperationToolOutcome, type SelectedOperation, bindOperationExecutor, createEffectCrud, createEffectFoundation, createOperationTools, decodeEffectContract, effectApiBuilder, effectContract, effectOperationFactory, effectReceiptWorkflowMutation, effectSchema, effectStandardSchema, effectTerminalWorkflowAttempt, effectTransactionalIdempotency, effectWorkflowAttempt, effectWorkflowStatusBindings, effectZodApiBuilder, operationToolDialect, projectErrorCause, selectOperation };
13
+ export { type CallbackError, type CallbackRequirements, type ContractDecoderError, type ContractDecoderRequirements, ContractValidationError, type EffectApiBuilder, type EffectAuthFunctions, type EffectCrudConfig, type EffectCrudContext, type EffectCrudPage, type EffectCrudPagination, type EffectCrudRepository, type EffectCrudTable, type EffectDecoder, type EffectFoundationOptions, type EffectIdempotencyPreparation, type EffectMiddlewareInput, type EffectOperationArgument, type EffectOperationError, type EffectOperationHandlerResult, type EffectOperationInput, type EffectOperationRequirements, type EffectOperationResult, type EffectPreparation, type EffectReceiptProtectedWorkflowMutation, type EffectValue, type EffectZodApiBuilder, type OperationExecutor, type OperationPublicResult, type OperationTool, type OperationToolConverter, type OperationToolDefinition, type OperationToolJsonSchema, type OperationToolOutcome, type SelectedOperation, bindOperationExecutor, createEffectAuthFunctions, createEffectCrud, createEffectFoundation, createOperationTools, decodeEffectContract, effectApiBuilder, effectContract, effectOperationFactory, effectReceiptWorkflowMutation, effectSchema, effectStandardSchema, effectTerminalWorkflowAttempt, effectTransactionalIdempotency, effectWorkflowAttempt, effectWorkflowStatusBindings, effectZodApiBuilder, operationToolDialect, projectErrorCause, selectOperation };
package/dist/effect.mjs CHANGED
@@ -1 +1 @@
1
- import{bindOperationExecutor as e,createOperationTools as t,operationToolDialect as n,selectOperation as r}from"./modules/contracts/exposure.mjs";import{effectOperationFactory as i}from"./modules/effect/operation.mjs";import{ContractValidationError as a,decodeEffectContract as o,effectContract as s,effectSchema as c,effectStandardSchema as l}from"./modules/effect/schema.mjs";import{createEffectFoundation as u}from"./modules/effect/foundation.mjs";import{effectTransactionalIdempotency as d}from"./modules/effect/idempotency.mjs";import{createEffectCrud as f}from"./modules/effect/crud.mjs";import{projectErrorCause as p}from"./modules/effect/errors.mjs";import{effectApiBuilder as m}from"./modules/effect/api.mjs";import{effectZodApiBuilder as h}from"./modules/effect/api-zod.mjs";import{effectReceiptWorkflowMutation as g,effectTerminalWorkflowAttempt as _,effectWorkflowAttempt as v,effectWorkflowStatusBindings as y}from"./modules/effect/workflow.mjs";export{a as ContractValidationError,e as bindOperationExecutor,f as createEffectCrud,u as createEffectFoundation,t as createOperationTools,o as decodeEffectContract,m as effectApiBuilder,s as effectContract,i as effectOperationFactory,g as effectReceiptWorkflowMutation,c as effectSchema,l as effectStandardSchema,_ as effectTerminalWorkflowAttempt,d as effectTransactionalIdempotency,v as effectWorkflowAttempt,y as effectWorkflowStatusBindings,h as effectZodApiBuilder,n as operationToolDialect,p as projectErrorCause,r as selectOperation};
1
+ import{bindOperationExecutor as e,createOperationTools as t,operationToolDialect as n,selectOperation as r}from"./modules/contracts/exposure.mjs";import{projectErrorCause as i}from"./modules/effect/errors.mjs";import{effectZodApiBuilder as a}from"./modules/effect/api-zod.mjs";import{createEffectAuthFunctions as o}from"./modules/effect/auth.mjs";import{effectOperationFactory as s}from"./modules/effect/operation.mjs";import{ContractValidationError as c,decodeEffectContract as l,effectContract as u,effectSchema as d,effectStandardSchema as f}from"./modules/effect/schema.mjs";import{createEffectFoundation as p}from"./modules/effect/foundation.mjs";import{effectTransactionalIdempotency as m}from"./modules/effect/idempotency.mjs";import{createEffectCrud as h}from"./modules/effect/crud.mjs";import{effectApiBuilder as g}from"./modules/effect/api.mjs";import{effectReceiptWorkflowMutation as _,effectTerminalWorkflowAttempt as v,effectWorkflowAttempt as y,effectWorkflowStatusBindings as b}from"./modules/effect/workflow.mjs";export{c as ContractValidationError,e as bindOperationExecutor,o as createEffectAuthFunctions,h as createEffectCrud,p as createEffectFoundation,t as createOperationTools,l as decodeEffectContract,g as effectApiBuilder,u as effectContract,s as effectOperationFactory,_ as effectReceiptWorkflowMutation,d as effectSchema,f as effectStandardSchema,v as effectTerminalWorkflowAttempt,m as effectTransactionalIdempotency,y as effectWorkflowAttempt,b as effectWorkflowStatusBindings,a as effectZodApiBuilder,n as operationToolDialect,i as projectErrorCause,r as selectOperation};
@@ -1,2 +1,2 @@
1
- import{normalizeEffect as e}from"./normalize.mjs";import{projectErrorCause as t}from"./errors.mjs";import{ConvexError as n}from"convex/values";import{Cause as r,Context as i,Effect as a,Exit as o}from"effect";function s(s,c){return l=>{let u=typeof l==`function`?l:l.handler,d=async(s,...l)=>{let d=e(()=>c.services?c.services(s):i.empty()).pipe(a.flatMap(t=>e(()=>c.context?c.context(s):{}).pipe(a.flatMap(e=>a.sync(()=>{if(typeof e!=`object`||!e)throw Error(`Effect API context must be an object`);let t=Object.getPrototypeOf(e);if(t!==Object.prototype&&t!==null)throw Error(`Effect API context additions must be a plain record`);let n=s;for(let t of Reflect.ownKeys(e))if(t in n)throw Error(`Effect API context cannot replace ${String(t)}`);return c.context?Object.create(Object.getPrototypeOf(n),{...Object.getOwnPropertyDescriptors(n),...Object.getOwnPropertyDescriptors(e)}):s})),a.flatMap(t=>e(()=>u(t,...l))),a.provideContext(t)))),f=await a.runPromiseExit(a.scoped(d));if(o.isSuccess(f))return f.value;if(c.errors)throw new n(t(c.errors,f.cause));let p=f.cause.reasons.length===1?f.cause.reasons[0]:void 0;throw p&&r.isFailReason(p)?c.mapError?c.mapError(p.error):p.error:p&&r.isDieReason(p)?p.defect:Error(`Effect API execution failed`,{cause:f.cause})};return s(typeof l==`function`?d:{...l,handler:d})}}export{s as wrapEffectBuilder};
1
+ import{projectErrorCause as e}from"./errors.mjs";import{normalizeEffect as t}from"./normalize.mjs";import{ConvexError as n}from"convex/values";import{Cause as r,Context as i,Effect as a,Exit as o}from"effect";function s(s,c){return l=>{let u=typeof l==`function`?l:l.handler,d=async(s,...l)=>{let d=t(()=>c.services?c.services(s):i.empty()).pipe(a.flatMap(e=>t(()=>c.context?c.context(s):{}).pipe(a.flatMap(e=>a.sync(()=>{if(typeof e!=`object`||!e)throw Error(`Effect API context must be an object`);let t=Object.getPrototypeOf(e);if(t!==Object.prototype&&t!==null)throw Error(`Effect API context additions must be a plain record`);let n=s;for(let t of Reflect.ownKeys(e))if(t in n)throw Error(`Effect API context cannot replace ${String(t)}`);return c.context?Object.create(Object.getPrototypeOf(n),{...Object.getOwnPropertyDescriptors(n),...Object.getOwnPropertyDescriptors(e)}):s})),a.flatMap(e=>t(()=>u(e,...l))),a.provideContext(e)))),f=await a.runPromiseExit(a.scoped(d));if(o.isSuccess(f))return f.value;if(c.errors)throw new n(e(c.errors,f.cause));let p=f.cause.reasons.length===1?f.cause.reasons[0]:void 0;throw p&&r.isFailReason(p)?c.mapError?c.mapError(p.error):p.error:p&&r.isDieReason(p)?p.defect:Error(`Effect API execution failed`,{cause:f.cause})};return s(typeof l==`function`?d:{...l,handler:d})}}export{s as wrapEffectBuilder};
2
2
  //# sourceMappingURL=api-runtime.mjs.map
@@ -0,0 +1,43 @@
1
+ import { AuthBundle, AuthFunctions, AuthFunctionsConfig, DefaultRole, Include } from "../../auth.mjs";
2
+ import { EffectValue } from "./operation.mjs";
3
+ import { EffectApiContext, EffectApiOptions, EffectApiServices, ProvidedServices } from "./api-runtime.mjs";
4
+ import { EffectZodApiBuilder } from "./api-zod.mjs";
5
+ import { CustomBuilder } from "convex-helpers/server/zod4";
6
+ import { GenericActionCtx, GenericDataModel, GenericMutationCtx, GenericQueryCtx } from "convex/server";
7
+ import { Context } from "effect";
8
+ //#region src/modules/effect/auth.d.ts
9
+ type Adapted<Builder, Services, Added> = Builder extends CustomBuilder<infer Kind, infer Args, infer Ctx, infer Made, infer Input, infer Visibility, infer Extra> ? EffectZodApiBuilder<Kind, Args, Ctx, Made, Input, Visibility, Extra, ProvidedServices<Services>, EffectValue<Added>, Added> : never;
10
+ /** Shared post-auth composition; internal/system policies are configured separately. */
11
+ export type EffectAuthFunctions<DataModel extends GenericDataModel, Role extends string = DefaultRole, QueryServices extends EffectApiServices = Context.Context<never>, QueryAdded extends EffectApiContext = {}, MutationServices extends EffectApiServices = Context.Context<never>, MutationAdded extends EffectApiContext = {}, ActionServices extends EffectApiServices = Context.Context<never>, ActionAdded extends EffectApiContext = {}, SystemQueryServices extends EffectApiServices = Context.Context<never>, SystemQueryAdded extends EffectApiContext = {}, SystemMutationServices extends EffectApiServices = Context.Context<never>, SystemMutationAdded extends EffectApiContext = {}, SystemActionServices extends EffectApiServices = Context.Context<never>, SystemActionAdded extends EffectApiContext = {}> = Pick<AuthFunctions<DataModel, Role>, 'include' | 'authenticatedUser'> & {
12
+ authQuery: Adapted<AuthFunctions<DataModel, Role>['authQuery'], QueryServices, QueryAdded>;
13
+ roleQuery: (...allowed: readonly Role[]) => Adapted<AuthFunctions<DataModel, Role>['authQuery'], QueryServices, QueryAdded>;
14
+ adminQuery: Adapted<AuthFunctions<DataModel, Role>['authQuery'], QueryServices, QueryAdded>;
15
+ authMutation: Adapted<AuthFunctions<DataModel, Role>['authMutation'], MutationServices, MutationAdded>;
16
+ roleMutation: (...allowed: readonly Role[]) => Adapted<AuthFunctions<DataModel, Role>['authMutation'], MutationServices, MutationAdded>;
17
+ adminMutation: Adapted<AuthFunctions<DataModel, Role>['authMutation'], MutationServices, MutationAdded>;
18
+ authAction: Adapted<AuthFunctions<DataModel, Role>['authAction'], ActionServices, ActionAdded>;
19
+ roleAction: (...allowed: readonly Role[]) => Adapted<AuthFunctions<DataModel, Role>['authAction'], ActionServices, ActionAdded>;
20
+ adminAction: Adapted<AuthFunctions<DataModel, Role>['authAction'], ActionServices, ActionAdded>;
21
+ systemQuery: Adapted<AuthFunctions<DataModel, Role>['systemQuery'], SystemQueryServices, SystemQueryAdded>;
22
+ systemMutation: Adapted<AuthFunctions<DataModel, Role>['systemMutation'], SystemMutationServices, SystemMutationAdded>;
23
+ systemAction: Adapted<AuthFunctions<DataModel, Role>['systemAction'], SystemActionServices, SystemActionAdded>;
24
+ };
25
+ /** Providers and handles run once per invocation after security and live role checks. */
26
+ export declare function createEffectAuthFunctions<DataModel extends GenericDataModel, const Role extends string = DefaultRole, QueryServices extends EffectApiServices = Context.Context<never>, QueryAdded extends EffectApiContext = {}, MutationServices extends EffectApiServices = Context.Context<never>, MutationAdded extends EffectApiContext = {}, ActionServices extends EffectApiServices = Context.Context<never>, ActionAdded extends EffectApiContext = {}, SystemQueryServices extends EffectApiServices = Context.Context<never>, SystemQueryAdded extends EffectApiContext = {}, SystemMutationServices extends EffectApiServices = Context.Context<never>, SystemMutationAdded extends EffectApiContext = {}, SystemActionServices extends EffectApiServices = Context.Context<never>, SystemActionAdded extends EffectApiContext = {}>(config: AuthFunctionsConfig<DataModel, Role>, options: {
27
+ query: EffectApiOptions<GenericQueryCtx<DataModel> & AuthBundle<Role> & {
28
+ include: Include;
29
+ }, QueryServices, QueryAdded>;
30
+ mutation: EffectApiOptions<GenericMutationCtx<DataModel> & AuthBundle<Role> & {
31
+ include: Include;
32
+ }, MutationServices, MutationAdded>;
33
+ action: EffectApiOptions<GenericActionCtx<DataModel> & AuthBundle<Role>, ActionServices, ActionAdded>;
34
+ systemQuery: EffectApiOptions<GenericQueryCtx<DataModel> & {
35
+ include: Include;
36
+ }, SystemQueryServices, SystemQueryAdded>;
37
+ systemMutation: EffectApiOptions<GenericMutationCtx<DataModel> & {
38
+ include: Include;
39
+ }, SystemMutationServices, SystemMutationAdded>;
40
+ systemAction: EffectApiOptions<GenericActionCtx<DataModel>, SystemActionServices, SystemActionAdded>;
41
+ }): EffectAuthFunctions<DataModel, Role, QueryServices, QueryAdded, MutationServices, MutationAdded, ActionServices, ActionAdded, SystemQueryServices, SystemQueryAdded, SystemMutationServices, SystemMutationAdded, SystemActionServices, SystemActionAdded>;
42
+ //#endregion
43
+ //# sourceMappingURL=auth.d.mts.map
@@ -0,0 +1,2 @@
1
+ import{createAuthFunctions as e}from"../../auth.mjs";import{effectZodApiBuilder as t}from"./api-zod.mjs";function n(n,r){let i=e(n);return{include:i.include,authenticatedUser:i.authenticatedUser,authQuery:t(i.authQuery,r.query),roleQuery:(...e)=>t(i.roleQuery(...e),r.query),adminQuery:t(i.adminQuery,r.query),authMutation:t(i.authMutation,r.mutation),roleMutation:(...e)=>t(i.roleMutation(...e),r.mutation),adminMutation:t(i.adminMutation,r.mutation),authAction:t(i.authAction,r.action),roleAction:(...e)=>t(i.roleAction(...e),r.action),adminAction:t(i.adminAction,r.action),systemQuery:t(i.systemQuery,r.systemQuery),systemMutation:t(i.systemMutation,r.systemMutation),systemAction:t(i.systemAction,r.systemAction)}}export{n as createEffectAuthFunctions};
2
+ //# sourceMappingURL=auth.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.mjs","names":["createEffectAuthFunctions","auth","createAuthFunctions","config","effectZodApiBuilder","options","allowed"],"sources":["../../../src/modules/effect/auth.ts"],"sourcesContent":["import type { Context } from 'effect'\nimport type {\n\tGenericDataModel,\n\tGenericQueryCtx,\n\tGenericMutationCtx,\n\tGenericActionCtx,\n} from 'convex/server'\nimport type { CustomBuilder } from 'convex-helpers/server/zod4'\nimport {\n\tcreateAuthFunctions,\n\ttype AuthFunctions,\n\ttype AuthFunctionsConfig,\n\ttype AuthBundle,\n\ttype DefaultRole,\n\ttype Include,\n} from '../../auth'\nimport { effectZodApiBuilder, type EffectZodApiBuilder } from './api-zod'\nimport type {\n\tEffectApiOptions,\n\tEffectApiServices,\n\tEffectApiContext,\n\tProvidedServices,\n} from './api-runtime'\nimport type { EffectValue } from './operation'\n\n// Derive the adapter from the symbolic auth contract, never from inferred native spreads.\ntype Adapted<Builder, Services, Added> =\n\tBuilder extends CustomBuilder<\n\t\tinfer Kind,\n\t\tinfer Args,\n\t\tinfer Ctx,\n\t\tinfer Made,\n\t\tinfer Input,\n\t\tinfer Visibility,\n\t\tinfer Extra\n\t>\n\t\t? EffectZodApiBuilder<\n\t\t\t\tKind,\n\t\t\t\tArgs,\n\t\t\t\tCtx,\n\t\t\t\tMade,\n\t\t\t\tInput,\n\t\t\t\tVisibility,\n\t\t\t\tExtra,\n\t\t\t\tProvidedServices<Services>,\n\t\t\t\tEffectValue<Added>,\n\t\t\t\tAdded\n\t\t\t>\n\t\t: never\n\n/** Shared post-auth composition; internal/system policies are configured separately. */\nexport type EffectAuthFunctions<\n\tDataModel extends GenericDataModel,\n\tRole extends string = DefaultRole,\n\tQueryServices extends EffectApiServices = Context.Context<never>,\n\tQueryAdded extends EffectApiContext = {},\n\tMutationServices extends EffectApiServices = Context.Context<never>,\n\tMutationAdded extends EffectApiContext = {},\n\tActionServices extends EffectApiServices = Context.Context<never>,\n\tActionAdded extends EffectApiContext = {},\n\tSystemQueryServices extends EffectApiServices = Context.Context<never>,\n\tSystemQueryAdded extends EffectApiContext = {},\n\tSystemMutationServices extends EffectApiServices = Context.Context<never>,\n\tSystemMutationAdded extends EffectApiContext = {},\n\tSystemActionServices extends EffectApiServices = Context.Context<never>,\n\tSystemActionAdded extends EffectApiContext = {},\n> = Pick<AuthFunctions<DataModel, Role>, 'include' | 'authenticatedUser'> & {\n\tauthQuery: Adapted<AuthFunctions<DataModel, Role>['authQuery'], QueryServices, QueryAdded>\n\troleQuery: (\n\t\t...allowed: readonly Role[]\n\t) => Adapted<AuthFunctions<DataModel, Role>['authQuery'], QueryServices, QueryAdded>\n\tadminQuery: Adapted<AuthFunctions<DataModel, Role>['authQuery'], QueryServices, QueryAdded>\n\tauthMutation: Adapted<\n\t\tAuthFunctions<DataModel, Role>['authMutation'],\n\t\tMutationServices,\n\t\tMutationAdded\n\t>\n\troleMutation: (\n\t\t...allowed: readonly Role[]\n\t) => Adapted<AuthFunctions<DataModel, Role>['authMutation'], MutationServices, MutationAdded>\n\tadminMutation: Adapted<\n\t\tAuthFunctions<DataModel, Role>['authMutation'],\n\t\tMutationServices,\n\t\tMutationAdded\n\t>\n\tauthAction: Adapted<AuthFunctions<DataModel, Role>['authAction'], ActionServices, ActionAdded>\n\troleAction: (\n\t\t...allowed: readonly Role[]\n\t) => Adapted<AuthFunctions<DataModel, Role>['authAction'], ActionServices, ActionAdded>\n\tadminAction: Adapted<AuthFunctions<DataModel, Role>['authAction'], ActionServices, ActionAdded>\n\tsystemQuery: Adapted<\n\t\tAuthFunctions<DataModel, Role>['systemQuery'],\n\t\tSystemQueryServices,\n\t\tSystemQueryAdded\n\t>\n\tsystemMutation: Adapted<\n\t\tAuthFunctions<DataModel, Role>['systemMutation'],\n\t\tSystemMutationServices,\n\t\tSystemMutationAdded\n\t>\n\tsystemAction: Adapted<\n\t\tAuthFunctions<DataModel, Role>['systemAction'],\n\t\tSystemActionServices,\n\t\tSystemActionAdded\n\t>\n}\n\n/** Providers and handles run once per invocation after security and live role checks. */\nexport function createEffectAuthFunctions<\n\tDataModel extends GenericDataModel,\n\tconst Role extends string = DefaultRole,\n\tQueryServices extends EffectApiServices = Context.Context<never>,\n\tQueryAdded extends EffectApiContext = {},\n\tMutationServices extends EffectApiServices = Context.Context<never>,\n\tMutationAdded extends EffectApiContext = {},\n\tActionServices extends EffectApiServices = Context.Context<never>,\n\tActionAdded extends EffectApiContext = {},\n\tSystemQueryServices extends EffectApiServices = Context.Context<never>,\n\tSystemQueryAdded extends EffectApiContext = {},\n\tSystemMutationServices extends EffectApiServices = Context.Context<never>,\n\tSystemMutationAdded extends EffectApiContext = {},\n\tSystemActionServices extends EffectApiServices = Context.Context<never>,\n\tSystemActionAdded extends EffectApiContext = {},\n>(\n\tconfig: AuthFunctionsConfig<DataModel, Role>,\n\toptions: {\n\t\tquery: EffectApiOptions<\n\t\t\tGenericQueryCtx<DataModel> & AuthBundle<Role> & { include: Include },\n\t\t\tQueryServices,\n\t\t\tQueryAdded\n\t\t>\n\t\tmutation: EffectApiOptions<\n\t\t\tGenericMutationCtx<DataModel> & AuthBundle<Role> & { include: Include },\n\t\t\tMutationServices,\n\t\t\tMutationAdded\n\t\t>\n\t\taction: EffectApiOptions<\n\t\t\tGenericActionCtx<DataModel> & AuthBundle<Role>,\n\t\t\tActionServices,\n\t\t\tActionAdded\n\t\t>\n\t\tsystemQuery: EffectApiOptions<\n\t\t\tGenericQueryCtx<DataModel> & { include: Include },\n\t\t\tSystemQueryServices,\n\t\t\tSystemQueryAdded\n\t\t>\n\t\tsystemMutation: EffectApiOptions<\n\t\t\tGenericMutationCtx<DataModel> & { include: Include },\n\t\t\tSystemMutationServices,\n\t\t\tSystemMutationAdded\n\t\t>\n\t\tsystemAction: EffectApiOptions<\n\t\t\tGenericActionCtx<DataModel>,\n\t\t\tSystemActionServices,\n\t\t\tSystemActionAdded\n\t\t>\n\t},\n): EffectAuthFunctions<\n\tDataModel,\n\tRole,\n\tQueryServices,\n\tQueryAdded,\n\tMutationServices,\n\tMutationAdded,\n\tActionServices,\n\tActionAdded,\n\tSystemQueryServices,\n\tSystemQueryAdded,\n\tSystemMutationServices,\n\tSystemMutationAdded,\n\tSystemActionServices,\n\tSystemActionAdded\n> {\n\tconst auth = createAuthFunctions(config)\n\treturn {\n\t\tinclude: auth.include,\n\t\tauthenticatedUser: auth.authenticatedUser,\n\t\tauthQuery: effectZodApiBuilder(auth.authQuery, options.query),\n\t\troleQuery: (...allowed) => effectZodApiBuilder(auth.roleQuery(...allowed), options.query),\n\t\tadminQuery: effectZodApiBuilder(auth.adminQuery, options.query),\n\t\tauthMutation: effectZodApiBuilder(auth.authMutation, options.mutation),\n\t\troleMutation: (...allowed) =>\n\t\t\teffectZodApiBuilder(auth.roleMutation(...allowed), options.mutation),\n\t\tadminMutation: effectZodApiBuilder(auth.adminMutation, options.mutation),\n\t\tauthAction: effectZodApiBuilder(auth.authAction, options.action),\n\t\troleAction: (...allowed) => effectZodApiBuilder(auth.roleAction(...allowed), options.action),\n\t\tadminAction: effectZodApiBuilder(auth.adminAction, options.action),\n\t\tsystemQuery: effectZodApiBuilder(auth.systemQuery, options.systemQuery),\n\t\tsystemMutation: effectZodApiBuilder(auth.systemMutation, options.systemMutation),\n\t\tsystemAction: effectZodApiBuilder(auth.systemAction, options.systemAction),\n\t}\n}\n"],"mappings":"yGA4GA,SAAgBA,EAgBf,EACA,EA+CC,CACD,IAAMC,EAAOC,EAAoBC,CAAM,EACvC,MAAO,CACN,QAASF,EAAK,QACd,kBAAmBA,EAAK,kBACxB,UAAWG,EAAoBH,EAAK,UAAWI,EAAQ,KAAK,EAC5D,WAAY,GAAGC,IAAYF,EAAoBH,EAAK,UAAU,GAAGK,CAAO,EAAGD,EAAQ,KAAK,EACxF,WAAYD,EAAoBH,EAAK,WAAYI,EAAQ,KAAK,EAC9D,aAAcD,EAAoBH,EAAK,aAAcI,EAAQ,QAAQ,EACrE,cAAe,GAAGC,IACjBF,EAAoBH,EAAK,aAAa,GAAGK,CAAO,EAAGD,EAAQ,QAAQ,EACpE,cAAeD,EAAoBH,EAAK,cAAeI,EAAQ,QAAQ,EACvE,WAAYD,EAAoBH,EAAK,WAAYI,EAAQ,MAAM,EAC/D,YAAa,GAAGC,IAAYF,EAAoBH,EAAK,WAAW,GAAGK,CAAO,EAAGD,EAAQ,MAAM,EAC3F,YAAaD,EAAoBH,EAAK,YAAaI,EAAQ,MAAM,EACjE,YAAaD,EAAoBH,EAAK,YAAaI,EAAQ,WAAW,EACtE,eAAgBD,EAAoBH,EAAK,eAAgBI,EAAQ,cAAc,EAC/E,aAAcD,EAAoBH,EAAK,aAAcI,EAAQ,YAAY,CAC1E,CACD"}
@@ -1,11 +1,11 @@
1
1
  import { OperationExecutor, OperationPublicResult, OperationTool, OperationToolConverter, OperationToolDefinition, OperationToolJsonSchema, OperationToolOutcome, SelectedOperation, bindOperationExecutor, createOperationTools, operationToolDialect, selectOperation } from "../contracts/exposure.mjs";
2
2
  import { ContractDecoderError, ContractDecoderRequirements, ContractValidationError, EffectDecoder, decodeEffectContract, effectContract, effectSchema, effectStandardSchema } from "./schema.mjs";
3
3
  import { CallbackError, CallbackRequirements, EffectMiddlewareInput, EffectOperationArgument, EffectOperationError, EffectOperationHandlerResult, EffectOperationInput, EffectOperationRequirements, EffectOperationResult, EffectPreparation, EffectValue, effectOperationFactory } from "./operation.mjs";
4
+ import { EffectZodApiBuilder, effectZodApiBuilder } from "./api-zod.mjs";
4
5
  import { EffectFoundationOptions, createEffectFoundation } from "./foundation.mjs";
5
6
  import { EffectIdempotencyPreparation, effectTransactionalIdempotency } from "./idempotency.mjs";
6
7
  import { EffectCrudConfig, EffectCrudContext, EffectCrudPage, EffectCrudPagination, EffectCrudRepository, EffectCrudTable, createEffectCrud } from "./crud.mjs";
7
8
  import { EffectApiBuilder, effectApiBuilder } from "./api.mjs";
8
- import { EffectZodApiBuilder, effectZodApiBuilder } from "./api-zod.mjs";
9
9
  import { projectErrorCause } from "./errors.mjs";
10
10
  import { EffectReceiptProtectedWorkflowMutation, effectReceiptWorkflowMutation, effectTerminalWorkflowAttempt, effectWorkflowAttempt, effectWorkflowStatusBindings } from "./workflow.mjs";
11
11
  export { type CallbackError, type CallbackRequirements, type ContractDecoderError, type ContractDecoderRequirements, ContractValidationError, type EffectApiBuilder, type EffectCrudConfig, type EffectCrudContext, type EffectCrudPage, type EffectCrudPagination, type EffectCrudRepository, type EffectCrudTable, type EffectDecoder, type EffectFoundationOptions, type EffectIdempotencyPreparation, type EffectMiddlewareInput, type EffectOperationArgument, type EffectOperationError, type EffectOperationHandlerResult, type EffectOperationInput, type EffectOperationRequirements, type EffectOperationResult, type EffectPreparation, type EffectReceiptProtectedWorkflowMutation, type EffectValue, type EffectZodApiBuilder, type OperationExecutor, type OperationPublicResult, type OperationTool, type OperationToolConverter, type OperationToolDefinition, type OperationToolJsonSchema, type OperationToolOutcome, type SelectedOperation, bindOperationExecutor, createEffectCrud, createEffectFoundation, createOperationTools, decodeEffectContract, effectApiBuilder, effectContract, effectOperationFactory, effectReceiptWorkflowMutation, effectSchema, effectStandardSchema, effectTerminalWorkflowAttempt, effectTransactionalIdempotency, effectWorkflowAttempt, effectWorkflowStatusBindings, effectZodApiBuilder, operationToolDialect, projectErrorCause, selectOperation };
@@ -1 +1 @@
1
- import{bindOperationExecutor as e,createOperationTools as t,operationToolDialect as n,selectOperation as r}from"../contracts/exposure.mjs";import{effectOperationFactory as i}from"./operation.mjs";import{ContractValidationError as a,decodeEffectContract as o,effectContract as s,effectSchema as c,effectStandardSchema as l}from"./schema.mjs";import{createEffectFoundation as u}from"./foundation.mjs";import{effectTransactionalIdempotency as d}from"./idempotency.mjs";import{createEffectCrud as f}from"./crud.mjs";import{projectErrorCause as p}from"./errors.mjs";import{effectApiBuilder as m}from"./api.mjs";import{effectZodApiBuilder as h}from"./api-zod.mjs";import{effectReceiptWorkflowMutation as g,effectTerminalWorkflowAttempt as _,effectWorkflowAttempt as v,effectWorkflowStatusBindings as y}from"./workflow.mjs";export{a as ContractValidationError,e as bindOperationExecutor,f as createEffectCrud,u as createEffectFoundation,t as createOperationTools,o as decodeEffectContract,m as effectApiBuilder,s as effectContract,i as effectOperationFactory,g as effectReceiptWorkflowMutation,c as effectSchema,l as effectStandardSchema,_ as effectTerminalWorkflowAttempt,d as effectTransactionalIdempotency,v as effectWorkflowAttempt,y as effectWorkflowStatusBindings,h as effectZodApiBuilder,n as operationToolDialect,p as projectErrorCause,r as selectOperation};
1
+ import{bindOperationExecutor as e,createOperationTools as t,operationToolDialect as n,selectOperation as r}from"../contracts/exposure.mjs";import{projectErrorCause as i}from"./errors.mjs";import{effectZodApiBuilder as a}from"./api-zod.mjs";import{effectOperationFactory as o}from"./operation.mjs";import{ContractValidationError as s,decodeEffectContract as c,effectContract as l,effectSchema as u,effectStandardSchema as d}from"./schema.mjs";import{createEffectFoundation as f}from"./foundation.mjs";import{effectTransactionalIdempotency as p}from"./idempotency.mjs";import{createEffectCrud as m}from"./crud.mjs";import{effectApiBuilder as h}from"./api.mjs";import{effectReceiptWorkflowMutation as g,effectTerminalWorkflowAttempt as _,effectWorkflowAttempt as v,effectWorkflowStatusBindings as y}from"./workflow.mjs";export{s as ContractValidationError,e as bindOperationExecutor,m as createEffectCrud,f as createEffectFoundation,t as createOperationTools,c as decodeEffectContract,h as effectApiBuilder,l as effectContract,o as effectOperationFactory,g as effectReceiptWorkflowMutation,u as effectSchema,d as effectStandardSchema,_ as effectTerminalWorkflowAttempt,p as effectTransactionalIdempotency,v as effectWorkflowAttempt,y as effectWorkflowStatusBindings,a as effectZodApiBuilder,n as operationToolDialect,i as projectErrorCause,r as selectOperation};
@@ -1,2 +1,2 @@
1
- import{selectOperation as e}from"../contracts/exposure.mjs";import{effectOperationFactory as t}from"./operation.mjs";import{decodeEffectContract as n}from"./schema.mjs";import{CommandPermissionError as r,executeCommandLifecycle as i}from"../../components/foundation/modules/command/lifecycle.mjs";import{normalizeEffect as a,observationFailure as o}from"./normalize.mjs";import{Effect as s,Exit as c}from"effect";const l={succeed:s.succeed,suspend:s.suspend,flatMap:s.flatMap};function u(u){let d=u.operations(t()),p=(e,t,p)=>s.suspend(()=>{if(!Object.prototype.hasOwnProperty.call(d,e))throw Error(`The selected command operation is not configured`);let m=d[e];return s.flatMap(n(m.input,t),t=>{let d=t,h=u.observability.start({operation:e,classification:m.classification}),g=u.context;return a(()=>g(p)).pipe(s.flatMap(t=>i(l,{operation:e,classification:m.classification,context:t,command:d,permission:m.permission===void 0?void 0:()=>a(()=>{if(!u.checkPermission)throw new r(e);return u.checkPermission(p,{permission:m.permission,operation:e})}).pipe(s.asVoid),prepare:()=>a(()=>m.prepare?.(t,d)).pipe(s.map(e=>e?.kind===`execute`?{kind:`execute`,complete:e.complete?t=>a(()=>e.complete(t)).pipe(s.asVoid):void 0}:e)),decodeReplay:e=>n(m.replayResult??m.result,e),middleware:m.middleware?[e=>f(()=>m.middleware({...e,input:d}))]:[],defaultGuard:e=>a(()=>u.defaults?.guard?.(e)).pipe(s.asVoid),guard:e=>a(()=>m.guard?.(e,d)).pipe(s.asVoid),run:e=>f(()=>m.handler(d,e)),decodeResult:e=>n(m.result,e),audit:e=>a(()=>m.audit({command:d,result:e},t)),writeAudit:e=>a(()=>u.writeAudit(p,e)),aggregates:m.aggregates})),s.onExit(e=>s.sync(()=>{c.isSuccess(e)?h.completed():h.failed(o(e.cause))})))})});return{withContext(e){return{exec:(t,n)=>p(t,n,e)}},expose(t,n){if(!Object.hasOwn(d,n))throw Error(`The selected command operation is not configured`);return e(t,`mutation`,n,d[n])},exec:p}}function d(e){return function(t){return u({...t,...e})}}function f(e){return a(e)}export{d as bindEffectCommand,u as createEffectCommand};
1
+ import{selectOperation as e}from"../contracts/exposure.mjs";import{normalizeEffect as t,observationFailure as n}from"./normalize.mjs";import{effectOperationFactory as r}from"./operation.mjs";import{decodeEffectContract as i}from"./schema.mjs";import{CommandPermissionError as a,executeCommandLifecycle as o}from"../../components/foundation/modules/command/lifecycle.mjs";import{Effect as s,Exit as c}from"effect";const l={succeed:s.succeed,suspend:s.suspend,flatMap:s.flatMap};function u(u){let d=u.operations(r()),p=(e,r,p)=>s.suspend(()=>{if(!Object.prototype.hasOwnProperty.call(d,e))throw Error(`The selected command operation is not configured`);let m=d[e];return s.flatMap(i(m.input,r),r=>{let d=r,h=u.observability.start({operation:e,classification:m.classification}),g=u.context;return t(()=>g(p)).pipe(s.flatMap(n=>o(l,{operation:e,classification:m.classification,context:n,command:d,permission:m.permission===void 0?void 0:()=>t(()=>{if(!u.checkPermission)throw new a(e);return u.checkPermission(p,{permission:m.permission,operation:e})}).pipe(s.asVoid),prepare:()=>t(()=>m.prepare?.(n,d)).pipe(s.map(e=>e?.kind===`execute`?{kind:`execute`,complete:e.complete?n=>t(()=>e.complete(n)).pipe(s.asVoid):void 0}:e)),decodeReplay:e=>i(m.replayResult??m.result,e),middleware:m.middleware?[e=>f(()=>m.middleware({...e,input:d}))]:[],defaultGuard:e=>t(()=>u.defaults?.guard?.(e)).pipe(s.asVoid),guard:e=>t(()=>m.guard?.(e,d)).pipe(s.asVoid),run:e=>f(()=>m.handler(d,e)),decodeResult:e=>i(m.result,e),audit:e=>t(()=>m.audit({command:d,result:e},n)),writeAudit:e=>t(()=>u.writeAudit(p,e)),aggregates:m.aggregates})),s.onExit(e=>s.sync(()=>{c.isSuccess(e)?h.completed():h.failed(n(e.cause))})))})});return{withContext(e){return{exec:(t,n)=>p(t,n,e)}},expose(t,n){if(!Object.hasOwn(d,n))throw Error(`The selected command operation is not configured`);return e(t,`mutation`,n,d[n])},exec:p}}function d(e){return function(t){return u({...t,...e})}}function f(e){return t(e)}export{d as bindEffectCommand,u as createEffectCommand};
2
2
  //# sourceMappingURL=command.mjs.map
@@ -1,2 +1,2 @@
1
- import{defaultErrors as e}from"../../errors.mjs";import{effectOperationFactory as t}from"./operation.mjs";import{normalizeEffect as n}from"./normalize.mjs";import{paginated as r,zid as i}from"../../zod-table.mjs";import{z as a}from"zod";import{Effect as o}from"effect";function s(s){let{table:p}=s;if(`tenant`in p.storage.shape&&!s.enrich)return e.throw({code:`CRUD_ENRICH_REQUIRED`,message:`Table "${p.tableName}" requires an enrich callback`});if(!Number.isSafeInteger(s.read.maxPageSize)||s.read.maxPageSize<1)throw Error(`CRUD maxPageSize must be a positive safe integer`);let m=s.read.maximumRowsRead??s.read.maxPageSize;if(!Number.isSafeInteger(m)||m<1)throw Error(`CRUD maximumRowsRead must be a positive safe integer`);let h=p.tableName,g=i(h),_=d(p.commandInput),v=d(p.tools.update),y=d(p.tools.id),b=p.publicDto,x=d(a.object({id:g}).strict()),S=a.object({ok:a.literal(!0)}).strict(),C=a.object({numItems:a.number().int().min(1).max(s.read.maxPageSize),cursor:a.string().nullable(),endCursor:a.string().nullable().optional(),id:a.number().optional(),maximumRowsRead:a.number().int().positive().max(2**53-1).optional(),maximumBytesRead:a.number().int().positive().max(2**53-1).optional()}).strict(),w=r(b).result,T=s.classification??`business`,E=e=>({type:s.aggregateType,id:e}),D=`${h}.create`,O=`${h}.update`,k=`${h}.archive`,A=`${h}.get`,j=`${h}.list`;function M(t,n){return u(()=>s.checkId?s.checkId(t,n):t.db.normalizeId(p.tableName,n)!==null).pipe(o.map(t=>t===!0?n:e.throw({code:`CRUD_INVALID_ID`,message:`Invalid ID for table "${p.tableName}"`})))}function N(e,t){if(s.repository)return n(()=>s.repository.insert(e,t));let r=e;return n(()=>r.db.insert(p.tableName,t))}function P(e,t,r){if(s.repository)return n(()=>s.repository.patch(e,t,r));let i=e;return n(()=>i.db.patch(t,r))}let F=l(s.overrides?.create,(e,t)=>o.gen(function*(){let n=yield*u(()=>s.enrich?.(t,e));return{id:yield*N(t,Object.fromEntries(Object.entries({...e,...n}).filter(([,e])=>e!==void 0)))}})),I=l(s.overrides?.update,(e,t)=>P(t,e.id,e.data).pipe(o.as({ok:!0}))),L=(e,t)=>M(t,e.id).pipe(o.flatMap(()=>n(()=>I(e,t)))),R=l(s.overrides?.archive,(e,t)=>P(t,e.id,f(Date.now())).pipe(o.as({ok:!0}))),z=(e,t)=>M(t,e.id).pipe(o.flatMap(()=>n(()=>R(e,t)))),B=t(),V=Object.assign({},c(D,B.command({input:_,result:x,classification:T,aggregates:[s.aggregateType],permission:s.policy?.permissions?.create,guard:s.guards?.create,handler:F,audit:({result:e},t)=>u(()=>s.actor(t)).pipe(o.map(t=>({operation:D,actorId:t,aggregate:E(e.id)})))})),c(O,B.command({input:v,result:S,classification:T,aggregates:[s.aggregateType],permission:s.policy?.permissions?.update,guard:s.guards?.update,handler:L,audit:({command:e},t)=>u(()=>s.actor(t)).pipe(o.map(t=>({operation:O,actorId:t,aggregate:E(e.id)})))})),c(k,B.command({input:y,result:S,classification:T,aggregates:[s.aggregateType],permission:s.policy?.permissions?.archive,guard:s.guards?.archive,handler:z,audit:({command:e},t)=>u(()=>s.actor(t)).pipe(o.map(t=>({operation:k,actorId:t,aggregate:E(e.id)})))}))),H={context:s.context,defaults:{guard:s.policy?.guard},operations:()=>V},U=s.foundation.Command(H),W=d(b.nullable()),G=Object.assign({},c(A,B.query({input:y,result:W,permission:s.policy?.permissions?.get,guard:s.guards?.get,handler:(e,t)=>M(t,e.id).pipe(o.flatMap(e=>u(()=>s.read.get(t,e))),o.flatMap(e=>e===null?o.succeed(null):u(()=>s.read.project(e))))})),c(j,B.query({input:C,result:w,permission:s.policy?.permissions?.list,guard:s.guards?.list,handler:(e,t)=>u(()=>s.read.list(t,{...e,maximumRowsRead:Math.min(e.maximumRowsRead??m,m)})).pipe(o.flatMap(e=>o.forEach(e.page,e=>u(()=>s.read.project(e))).pipe(o.map(t=>({...e,page:t})))))}))),K={context:s.context,defaults:{guard:s.policy?.guard},operations:()=>G};return{commands:U,queries:s.foundation.Query(K),operations:V,queryOperations:G}}function c(e,t){return{[e]:t}}function l(e,t){return e??t}function u(e){return n(e)}function d(e){return{"~standard":e[`~standard`],parse:t=>e.parse(t),parseAsync:t=>e.parseAsync(t)}}function f(e){return Object.fromEntries([[`archivedAt`,e]])}export{s as createEffectCrud};
1
+ import{defaultErrors as e}from"../../errors.mjs";import{normalizeEffect as t}from"./normalize.mjs";import{effectOperationFactory as n}from"./operation.mjs";import{paginated as r,zid as i}from"../../zod-table.mjs";import{z as a}from"zod";import{Effect as o}from"effect";function s(s){let{table:p}=s;if(`tenant`in p.storage.shape&&!s.enrich)return e.throw({code:`CRUD_ENRICH_REQUIRED`,message:`Table "${p.tableName}" requires an enrich callback`});if(!Number.isSafeInteger(s.read.maxPageSize)||s.read.maxPageSize<1)throw Error(`CRUD maxPageSize must be a positive safe integer`);let m=s.read.maximumRowsRead??s.read.maxPageSize;if(!Number.isSafeInteger(m)||m<1)throw Error(`CRUD maximumRowsRead must be a positive safe integer`);let h=p.tableName,g=i(h),_=d(p.commandInput),v=d(p.tools.update),y=d(p.tools.id),b=p.publicDto,x=d(a.object({id:g}).strict()),S=a.object({ok:a.literal(!0)}).strict(),C=a.object({numItems:a.number().int().min(1).max(s.read.maxPageSize),cursor:a.string().nullable(),endCursor:a.string().nullable().optional(),id:a.number().optional(),maximumRowsRead:a.number().int().positive().max(2**53-1).optional(),maximumBytesRead:a.number().int().positive().max(2**53-1).optional()}).strict(),w=r(b).result,T=s.classification??`business`,E=e=>({type:s.aggregateType,id:e}),D=`${h}.create`,O=`${h}.update`,k=`${h}.archive`,A=`${h}.get`,j=`${h}.list`;function M(t,n){return u(()=>s.checkId?s.checkId(t,n):t.db.normalizeId(p.tableName,n)!==null).pipe(o.map(t=>t===!0?n:e.throw({code:`CRUD_INVALID_ID`,message:`Invalid ID for table "${p.tableName}"`})))}function N(e,n){if(s.repository)return t(()=>s.repository.insert(e,n));let r=e;return t(()=>r.db.insert(p.tableName,n))}function P(e,n,r){if(s.repository)return t(()=>s.repository.patch(e,n,r));let i=e;return t(()=>i.db.patch(n,r))}let F=l(s.overrides?.create,(e,t)=>o.gen(function*(){let n=yield*u(()=>s.enrich?.(t,e));return{id:yield*N(t,Object.fromEntries(Object.entries({...e,...n}).filter(([,e])=>e!==void 0)))}})),I=l(s.overrides?.update,(e,t)=>P(t,e.id,e.data).pipe(o.as({ok:!0}))),L=(e,n)=>M(n,e.id).pipe(o.flatMap(()=>t(()=>I(e,n)))),R=l(s.overrides?.archive,(e,t)=>P(t,e.id,f(Date.now())).pipe(o.as({ok:!0}))),z=(e,n)=>M(n,e.id).pipe(o.flatMap(()=>t(()=>R(e,n)))),B=n(),V=Object.assign({},c(D,B.command({input:_,result:x,classification:T,aggregates:[s.aggregateType],permission:s.policy?.permissions?.create,guard:s.guards?.create,handler:F,audit:({result:e},t)=>u(()=>s.actor(t)).pipe(o.map(t=>({operation:D,actorId:t,aggregate:E(e.id)})))})),c(O,B.command({input:v,result:S,classification:T,aggregates:[s.aggregateType],permission:s.policy?.permissions?.update,guard:s.guards?.update,handler:L,audit:({command:e},t)=>u(()=>s.actor(t)).pipe(o.map(t=>({operation:O,actorId:t,aggregate:E(e.id)})))})),c(k,B.command({input:y,result:S,classification:T,aggregates:[s.aggregateType],permission:s.policy?.permissions?.archive,guard:s.guards?.archive,handler:z,audit:({command:e},t)=>u(()=>s.actor(t)).pipe(o.map(t=>({operation:k,actorId:t,aggregate:E(e.id)})))}))),H={context:s.context,defaults:{guard:s.policy?.guard},operations:()=>V},U=s.foundation.Command(H),W=d(b.nullable()),G=Object.assign({},c(A,B.query({input:y,result:W,permission:s.policy?.permissions?.get,guard:s.guards?.get,handler:(e,t)=>M(t,e.id).pipe(o.flatMap(e=>u(()=>s.read.get(t,e))),o.flatMap(e=>e===null?o.succeed(null):u(()=>s.read.project(e))))})),c(j,B.query({input:C,result:w,permission:s.policy?.permissions?.list,guard:s.guards?.list,handler:(e,t)=>u(()=>s.read.list(t,{...e,maximumRowsRead:Math.min(e.maximumRowsRead??m,m)})).pipe(o.flatMap(e=>o.forEach(e.page,e=>u(()=>s.read.project(e))).pipe(o.map(t=>({...e,page:t})))))}))),K={context:s.context,defaults:{guard:s.policy?.guard},operations:()=>G};return{commands:U,queries:s.foundation.Query(K),operations:V,queryOperations:G}}function c(e,t){return{[e]:t}}function l(e,t){return e??t}function u(e){return t(e)}function d(e){return{"~standard":e[`~standard`],parse:t=>e.parse(t),parseAsync:t=>e.parseAsync(t)}}function f(e){return Object.fromEntries([[`archivedAt`,e]])}export{s as createEffectCrud};
2
2
  //# sourceMappingURL=crud.mjs.map
@@ -1,2 +1,2 @@
1
- import{selectOperation as e}from"../contracts/exposure.mjs";import{effectOperationFactory as t}from"./operation.mjs";import{Observability as n}from"../../components/foundation/modules/observability/observability.mjs";import{decodeEffectContract as r}from"./schema.mjs";import{normalizeEffect as i,observationFailure as a}from"./normalize.mjs";import{Effect as o,Exit as s}from"effect";var c=class extends Error{code=`QUERY_MIDDLEWARE_NEXT_REUSED`;name=`QueryMiddlewareError`;constructor(){super(`A query middleware called next() more than once`)}},l=class extends Error{code=`QUERY_OPERATION_NOT_CONFIGURED`;name=`QueryConfigurationError`;constructor(){super(`The selected query operation is not configured`)}},u=class extends Error{code=`QUERY_PERMISSION_CHECKER_MISSING`;name=`QueryPermissionError`;constructor(){super(`Query permission requires an injected permission checker`)}};function d(d){let f=t(),p=d.operations(f),m=d.observability instanceof n?d.observability:new n(d.observability??{enabled:!1,classifyError:()=>({outcome:`failed`,errorCode:`UNEXPECTED`})});function h(e,t,n){return o.suspend(()=>{if(!Object.prototype.hasOwnProperty.call(p,e))return o.die(new l);let f=p[e];return o.flatMap(r(f.input,t),t=>{let l=m.start({operation:e,classification:f.classification??`query`});return o.gen(function*(){if(f.permission!==void 0){let t=d.checkPermission;if(!t)return yield*o.die(new u);yield*i(()=>t(n,{permission:f.permission,operation:e}))}let a=yield*i(()=>d.context(n)),s={...d.defaults?.metadata,...f.metadata},l=e=>o.gen(function*(){let n=d.defaults?.guard;return n&&(yield*i(()=>n(e))),f.guard&&(yield*i(()=>f.guard(e,t))),yield*i(()=>f.handler(t,e))}),p=!1,m=f.middleware,h=m?yield*i(()=>m({operation:e,input:t,command:t,context:a,metadata:s,next:e=>o.suspend(()=>p?o.die(new c):(p=!0,l(e?.context?Object.assign({},a,e.context):a)))})):yield*l(a);return yield*r(f.result,h)}).pipe(o.onExit(e=>o.sync(()=>{s.isSuccess(e)?l.completed():l.failed(a(e.cause))})))})})}function g(t,n){if(!Object.hasOwn(p,n))throw new l;return e(t,`query`,n,p[n])}return{exec:h,expose:g,withContext(e){return{exec:(t,n)=>h(t,n,e)}}}}function f(e){return function(t){let n=e.checkPermission;return d({...t,observability:e.observability,checkPermission:e.checkPermission?n:void 0})}}export{f as bindEffectQuery,d as createEffectQuery};
1
+ import{selectOperation as e}from"../contracts/exposure.mjs";import{normalizeEffect as t,observationFailure as n}from"./normalize.mjs";import{effectOperationFactory as r}from"./operation.mjs";import{Observability as i}from"../../components/foundation/modules/observability/observability.mjs";import{decodeEffectContract as a}from"./schema.mjs";import{Effect as o,Exit as s}from"effect";var c=class extends Error{code=`QUERY_MIDDLEWARE_NEXT_REUSED`;name=`QueryMiddlewareError`;constructor(){super(`A query middleware called next() more than once`)}},l=class extends Error{code=`QUERY_OPERATION_NOT_CONFIGURED`;name=`QueryConfigurationError`;constructor(){super(`The selected query operation is not configured`)}},u=class extends Error{code=`QUERY_PERMISSION_CHECKER_MISSING`;name=`QueryPermissionError`;constructor(){super(`Query permission requires an injected permission checker`)}};function d(d){let f=r(),p=d.operations(f),m=d.observability instanceof i?d.observability:new i(d.observability??{enabled:!1,classifyError:()=>({outcome:`failed`,errorCode:`UNEXPECTED`})});function h(e,r,i){return o.suspend(()=>{if(!Object.prototype.hasOwnProperty.call(p,e))return o.die(new l);let f=p[e];return o.flatMap(a(f.input,r),r=>{let l=m.start({operation:e,classification:f.classification??`query`});return o.gen(function*(){if(f.permission!==void 0){let n=d.checkPermission;if(!n)return yield*o.die(new u);yield*t(()=>n(i,{permission:f.permission,operation:e}))}let n=yield*t(()=>d.context(i)),s={...d.defaults?.metadata,...f.metadata},l=e=>o.gen(function*(){let n=d.defaults?.guard;return n&&(yield*t(()=>n(e))),f.guard&&(yield*t(()=>f.guard(e,r))),yield*t(()=>f.handler(r,e))}),p=!1,m=f.middleware,h=m?yield*t(()=>m({operation:e,input:r,command:r,context:n,metadata:s,next:e=>o.suspend(()=>p?o.die(new c):(p=!0,l(e?.context?Object.assign({},n,e.context):n)))})):yield*l(n);return yield*a(f.result,h)}).pipe(o.onExit(e=>o.sync(()=>{s.isSuccess(e)?l.completed():l.failed(n(e.cause))})))})})}function g(t,n){if(!Object.hasOwn(p,n))throw new l;return e(t,`query`,n,p[n])}return{exec:h,expose:g,withContext(e){return{exec:(t,n)=>h(t,n,e)}}}}function f(e){return function(t){let n=e.checkPermission;return d({...t,observability:e.observability,checkPermission:e.checkPermission?n:void 0})}}export{f as bindEffectQuery,d as createEffectQuery};
2
2
  //# sourceMappingURL=query.mjs.map
@@ -1,2 +1,2 @@
1
- import{decodeEffectContract as e}from"./schema.mjs";import{normalizeEffect as t}from"./normalize.mjs";import{WorkflowBindingError as n,workflowCancel as r,workflowExecution as i,workflowRestart as a,workflowTransition as o}from"../contracts/workflow.mjs";import{Effect as s}from"effect";function c(e){return n=>s.gen(function*(){let r=yield*f(n),i=yield*t(()=>e.authorize(r));return yield*t(()=>e.execute(r,i))})}function l(n){return r=>s.gen(function*(){let i=yield*f(r),a=yield*t(()=>n.authorize(i)),o=yield*s.tryPromise({try:()=>n.invokeMutation(i,a),catch:e=>e}).pipe(s.map(e=>({kind:`success`,value:e})),s.catch(e=>s.succeed({kind:`rejected`,error:e})));if(o.kind===`rejected`){let r=yield*t(()=>n.terminal(o.error));if(r===void 0)return yield*s.die(o.error);let a=yield*e(n.failure,r);return yield*f({...i,args:a}),{version:1,kind:`failed`,error:a}}return yield*e(n.result,o.value),yield*f({...i,args:o.value}),{version:1,kind:`succeeded`,value:o.value}})}function u(n){return{handler:r=>s.gen(function*(){let i=yield*f(r),a=yield*t(()=>n.authorize(i)),o=n.receipt(a),s=yield*t(()=>o.prepare(n.invocation(i,a)));if(s.kind===`replay`)return yield*e(o.replayResult,s.result);let c=yield*t(()=>n.execute(i,a)),l=s.complete;return yield*t(()=>l(c)),c})}}function d(e){function n(){return t(()=>e.read()).pipe(s.map(e=>e))}function i(r,i,a){return s.gen(function*(){let s=yield*n(),c={generation:r,nativeStatus:i},l=yield*p(()=>o(s,a===void 0?c:{...c,outcome:a}));return l.kind===`applied`&&(yield*t(()=>e.write(r,l.state))),l})}return{callback(n,r,a){return t(()=>e.authorize()).pipe(s.flatMap(()=>i(n,r,a)))},reconcile(n){return s.gen(function*(){return yield*t(()=>e.authorize()),yield*i(n,yield*t(()=>e.nativeStatus()),yield*t(()=>e.receiptOutcome()))})},cancel(i){return s.gen(function*(){yield*t(()=>e.authorize());let a=yield*n(),o=yield*p(()=>r(a,i));return o.kind===`applied`&&a.status===`running`&&(yield*t(()=>e.cancelNative()),yield*t(()=>e.write(i,o.state))),o})},restart(r){return s.gen(function*(){yield*t(()=>e.authorize());let i=yield*n();if(i.generation!==r)return{kind:`ignored`,state:i};let o=yield*p(()=>a(i));return yield*t(()=>e.restartNative()),yield*t(()=>e.write(r,o)),{kind:`applied`,state:o}})}}}function f(e){return p(()=>i(e))}function p(e){return s.try({try:e,catch:e=>{if(e instanceof n)return e;throw e}})}export{u as effectReceiptWorkflowMutation,l as effectTerminalWorkflowAttempt,c as effectWorkflowAttempt,d as effectWorkflowStatusBindings};
1
+ import{normalizeEffect as e}from"./normalize.mjs";import{decodeEffectContract as t}from"./schema.mjs";import{WorkflowBindingError as n,workflowCancel as r,workflowExecution as i,workflowRestart as a,workflowTransition as o}from"../contracts/workflow.mjs";import{Effect as s}from"effect";function c(t){return n=>s.gen(function*(){let r=yield*f(n),i=yield*e(()=>t.authorize(r));return yield*e(()=>t.execute(r,i))})}function l(n){return r=>s.gen(function*(){let i=yield*f(r),a=yield*e(()=>n.authorize(i)),o=yield*s.tryPromise({try:()=>n.invokeMutation(i,a),catch:e=>e}).pipe(s.map(e=>({kind:`success`,value:e})),s.catch(e=>s.succeed({kind:`rejected`,error:e})));if(o.kind===`rejected`){let r=yield*e(()=>n.terminal(o.error));if(r===void 0)return yield*s.die(o.error);let a=yield*t(n.failure,r);return yield*f({...i,args:a}),{version:1,kind:`failed`,error:a}}return yield*t(n.result,o.value),yield*f({...i,args:o.value}),{version:1,kind:`succeeded`,value:o.value}})}function u(n){return{handler:r=>s.gen(function*(){let i=yield*f(r),a=yield*e(()=>n.authorize(i)),o=n.receipt(a),s=yield*e(()=>o.prepare(n.invocation(i,a)));if(s.kind===`replay`)return yield*t(o.replayResult,s.result);let c=yield*e(()=>n.execute(i,a)),l=s.complete;return yield*e(()=>l(c)),c})}}function d(t){function n(){return e(()=>t.read()).pipe(s.map(e=>e))}function i(r,i,a){return s.gen(function*(){let s=yield*n(),c={generation:r,nativeStatus:i},l=yield*p(()=>o(s,a===void 0?c:{...c,outcome:a}));return l.kind===`applied`&&(yield*e(()=>t.write(r,l.state))),l})}return{callback(n,r,a){return e(()=>t.authorize()).pipe(s.flatMap(()=>i(n,r,a)))},reconcile(n){return s.gen(function*(){return yield*e(()=>t.authorize()),yield*i(n,yield*e(()=>t.nativeStatus()),yield*e(()=>t.receiptOutcome()))})},cancel(i){return s.gen(function*(){yield*e(()=>t.authorize());let a=yield*n(),o=yield*p(()=>r(a,i));return o.kind===`applied`&&a.status===`running`&&(yield*e(()=>t.cancelNative()),yield*e(()=>t.write(i,o.state))),o})},restart(r){return s.gen(function*(){yield*e(()=>t.authorize());let i=yield*n();if(i.generation!==r)return{kind:`ignored`,state:i};let o=yield*p(()=>a(i));return yield*e(()=>t.restartNative()),yield*e(()=>t.write(r,o)),{kind:`applied`,state:o}})}}}function f(e){return p(()=>i(e))}function p(e){return s.try({try:e,catch:e=>{if(e instanceof n)return e;throw e}})}export{u as effectReceiptWorkflowMutation,l as effectTerminalWorkflowAttempt,c as effectWorkflowAttempt,d as effectWorkflowStatusBindings};
2
2
  //# sourceMappingURL=workflow.mjs.map
package/docs/auth.md CHANGED
@@ -64,9 +64,139 @@ export const {
64
64
  Every function in the app is then built from these — raw `query`/`mutation`
65
65
  imports from `_generated/server` appear **only** in this file.
66
66
 
67
+ ## One shared Effect function foundation
68
+
69
+ Use `createEffectAuthFunctions` from `cvx-kit/effect` in `convex/functions.ts`
70
+ to configure all twelve constructors once. Its first argument is the same
71
+ `AuthFunctionsConfig` used by `createAuthFunctions`; its second argument has
72
+ six explicit policies: `query`, `mutation`, `action`, `systemQuery`,
73
+ `systemMutation`, and `systemAction`. Each accepts `services`, `context`, and
74
+ `mapError`, like `effectZodApiBuilder`. Pass `{}` when no additions are needed.
75
+
76
+ Type the auth config with your generated `DataModel` and role vocabulary,
77
+ then let the factory infer its generics from both arguments. Do not supply
78
+ partial generic arguments, which would fix the remaining inference defaults.
79
+ The following public-package example defines a reusable app factory; the
80
+ host passes its auth, membership, trigger, and RLS policy as `config`.
81
+
82
+ <!-- packed-effect-example -->
83
+
84
+ ```ts
85
+ import {
86
+ defineSchema,
87
+ defineTable,
88
+ makeFunctionReference,
89
+ type DataModelFromSchemaDefinition,
90
+ type GenericQueryCtx,
91
+ type GenericMutationCtx,
92
+ } from 'convex/server'
93
+ import { v } from 'convex/values'
94
+ import { Effect } from 'effect'
95
+ import { z } from 'zod'
96
+ import type { AuthFunctionsConfig, AuthBundle } from 'cvx-kit/auth'
97
+ import { createEffectAuthFunctions, createEffectFoundation } from 'cvx-kit/effect'
98
+
99
+ const schema = defineSchema({ notes: defineTable({ tenant: v.string(), title: v.string() }) })
100
+ type Model = DataModelFromSchemaDefinition<typeof schema>
101
+ type Role = 'owner' | 'viewer'
102
+ const input = z.object({ title: z.string().trim().min(1) })
103
+ const saveRef = makeFunctionReference<'mutation', z.input<typeof input>, null>('notes:save')
104
+
105
+ export function createApplicationFunctions(config: AuthFunctionsConfig<Model, Role>) {
106
+ const foundation = createEffectFoundation({
107
+ observability: {
108
+ enabled: false,
109
+ classifyError: () => ({ outcome: 'failed', errorCode: 'FAILED' }),
110
+ },
111
+ writeAudit: () => undefined, // supply the app's transactional audit writer in production
112
+ })
113
+ const queries = foundation.Query({
114
+ context: (ctx: (GenericQueryCtx<Model> | GenericMutationCtx<Model>) & AuthBundle<Role>) => ctx,
115
+ operations: ({ query }) => ({
116
+ actor: query({
117
+ input: z.object({}),
118
+ result: z.string(),
119
+ handler: (_input, ctx) => ctx.actor.userId,
120
+ }),
121
+ }),
122
+ })
123
+ const commands = foundation.Command({
124
+ context: (ctx: GenericMutationCtx<Model> & AuthBundle<Role>) => ctx,
125
+ operations: ({ command }) => ({
126
+ save: command({
127
+ input,
128
+ result: z.null(),
129
+ classification: 'business',
130
+ audit: () => null,
131
+ handler: (args, ctx) =>
132
+ Effect.promise(async () => {
133
+ await ctx.db.insert('notes', { title: args.title, tenant: ctx.tenant })
134
+ return null
135
+ }),
136
+ }),
137
+ }),
138
+ })
139
+ const functions = createEffectAuthFunctions(config, {
140
+ query: { context: (ctx) => ({ queries: queries.withContext(ctx) }) },
141
+ mutation: {
142
+ context: (ctx) => ({
143
+ queries: queries.withContext(ctx),
144
+ commands: commands.withContext(ctx),
145
+ }),
146
+ },
147
+ action: {
148
+ context: (ctx) => ({
149
+ commands: {
150
+ save: (args: z.input<typeof input>) =>
151
+ Effect.promise(() => ctx.runMutation(saveRef, args)),
152
+ },
153
+ }),
154
+ },
155
+ systemQuery: {},
156
+ systemMutation: {},
157
+ systemAction: {},
158
+ })
159
+ const save = functions.roleMutation('owner')({
160
+ args: input.shape,
161
+ returns: z.null(),
162
+ handler: (ctx, args) => ctx.commands.exec('save', args),
163
+ })
164
+ return { ...functions, save }
165
+ }
166
+ ```
167
+
168
+ Export the returned constructors once from `convex/functions.ts`, and export
169
+ `save` at the registered `notes:save` mutation path. Endpoint modules can then
170
+ use `ctx.commands` or `ctx.queries` directly. The callbacks run per invocation
171
+ after auth, role checks, triggers/RLS wrapping, and live action membership
172
+ refresh. Binding is lazy; do not cache an authenticated `withContext` handle
173
+ at module scope. Denied requests never run the callbacks.
174
+
175
+ | Policy | Available authority and capabilities |
176
+ | ------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
177
+ | `query` | Trusted auth bundle, secured DB reader, `include`; bind read registries only. |
178
+ | `mutation` | Trusted auth bundle, secured triggered DB writer, `include`; bind queries and mutation commands. |
179
+ | `action` | Live-verified auth bundle, `runQuery`/`runMutation`; use action-compatible registries or forward to registered functions. No DB or `include`. |
180
+ | `systemQuery` / `systemMutation` / `systemAction` | Existing internal authority, configured separately. No fabricated actor or inherited public handles; system mutations retain triggers and bypass public RLS as before. |
181
+
182
+ Every auth/role/admin constructor of a kind shares that kind's policy. System
183
+ policies are deliberately explicit; choose their trusted capabilities rather
184
+ than synthesizing public credentials. An action forwarding to `runMutation`
185
+ creates a separate registered mutation transaction; it does not turn the
186
+ whole action into a transaction or carry its live role into that mutation's
187
+ JWT-based auth policy.
188
+
189
+ Additions cannot replace native or auth context keys: static checks reject
190
+ known collisions and runtime checks also reject inherited keys before the
191
+ handler executes. Put class-based handles under named properties in the
192
+ returned plain record. Scoped services, required Effect services, Zod raw and
193
+ parsed argument/result types, function references, and error projection retain
194
+ the existing adapter's behavior. Let failures escape registered mutations so
195
+ nested command writes, triggers, and audit writes roll back together.
196
+
67
197
  ## Shared Effect execution after authentication
68
198
 
69
- Wrap the constructors returned by `createAuthFunctions` with
199
+ For an individual constructor, wrap the builder returned by `createAuthFunctions` with
70
200
  `effectZodApiBuilder` from `cvx-kit/effect`. The original constructor resolves
71
201
  authentication, authorization, tenant policy, and the wrapped database before
72
202
  the service provider runs. Keep that ordering when adding Effect; use the Zod
package/docs/effect.md CHANGED
@@ -384,11 +384,17 @@ only when you want a typed error channel.
384
384
  The [command guide](./commands.md) documents the current lifecycle and migration
385
385
  steps; the [deprecated API reference](./commands-legacy.md) retains old examples.
386
386
  The [authentication guide](./auth.md)
387
- shows how to wrap trusted custom builders with `effectZodApiBuilder`, so auth,
387
+ shows the shared `createEffectAuthFunctions(config, policies)` factory and how
388
+ to wrap individual trusted builders with `effectZodApiBuilder`, so auth,
388
389
  tenancy, validators, row-level policies, and triggers run through their
389
390
  existing builder before Effect request services are provisioned.
390
391
 
391
- Optional request context can be added through the existing Effect API adapter's
392
+ For an app-wide foundation, configure `query`, `mutation`, and `action` policies
393
+ once with `createEffectAuthFunctions` and explicit separate system policies.
394
+ Query handles bind readers, mutation handles bind the secured triggered writer,
395
+ and action handles use `runMutation` rather than a mutation database.
396
+
397
+ Optional request context can also be added through the existing Effect API adapter's
392
398
  `context` callback. Bind selected registries with `commands.withContext(value)`
393
399
  and consume `ctx.commands.exec(name, input)` through the application's existing
394
400
  function helper names. Keep dependencies local to each feature; explicit execution
package/docs/llms.md CHANGED
@@ -22,7 +22,7 @@
22
22
  cvx-kit is a reusable Convex application kit: zod table boundaries, auth-aware
23
23
  function constructors, a trigger registry, an audited command protocol
24
24
  (Foundation component), and a declarative approvals component. Peer deps:
25
- `convex ^1.45.0`, `zod ^4.5.4`; command implementations also install
25
+ `convex ^1.46.0`, `zod ^4.5.4`; command implementations also install
26
26
  `effect >=4.0.1 <5` and import `cvx-kit/effect`.
27
27
 
28
28
  **Command API selection:** use `createEffectFoundation` and declare handlers
@@ -38,12 +38,13 @@ copy that API into new implementations.
38
38
  `convex/functions.ts`** (the `createAuthFunctions` call). All other
39
39
  functions use `authQuery/authMutation/authAction`, `roleQuery/...`,
40
40
  `adminQuery/...` (public) or `systemQuery/systemMutation/systemAction` (internal),
41
- wrapped once with `effectZodApiBuilder` when their handlers return Effects.
41
+ built once with `createEffectAuthFunctions(config, policies)` from `cvx-kit/effect`
42
+ for shared Effect handles, or individually wrapped with `effectZodApiBuilder`.
42
43
  This is what guarantees auth, triggers, and bounded reads.
43
44
  3. **Every public query returns DTOs** via `<table>.toPublicDto(row)` —
44
45
  runtime redaction, not just types.
45
46
  4. **Every read is bounded**: `ctx.include(ctx.db.query('t')).matching(...)
46
- .execute(limit)` with `1 ≤ limit ≤ 100`. `.resolve()` falls back to a full
47
+ .execute(limit)` with `1 ≤ limit ≤ 100`. `.resolve()` falls back to a full
47
48
  table scan — avoid it.
48
49
  5. **State changes are Effect commands**: declare
49
50
  `Command({ context, operations: ({ command }) => ({ ... }), defaults })`.
@@ -78,23 +79,23 @@ copy that API into new implementations.
78
79
 
79
80
  ## Exports map
80
81
 
81
- | Import | Provides |
82
- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
83
- | `cvx-kit` | Non-Effect helpers and component clients; import the optional Effect facade explicitly from `cvx-kit/effect`. |
84
- | `cvx-kit/zod-table` | `zodTable`, `tenantTable`, `createModule`, `paginated`, `zodVariantTable`, `jsonSafeZid`, `TIMESTAMP_FIELDS` |
85
- | `cvx-kit/effect` | Recommended command/query API: `createEffectFoundation`, `effectApiBuilder`, `effectZodApiBuilder`, `createEffectCrud`, idempotency/workflow adapters and operation tools. |
86
- | `cvx-kit/auth` | `createAuthFunctions` (incl. optional `security` RLS config), `createInclude`, `defaultRoleMap` |
87
- | `cvx-kit/tenancy` | `createTenantRules`, `composeRules`, `requireTenantReference`, `assertTenantOwned`, `TENANT_FIELD` |
88
- | `cvx-kit/crud` | Compatibility CRUD factory using the deprecated Promise command facade; use `createEffectCrud` from `cvx-kit/effect` for new domains. |
89
- | `cvx-kit/state-machine` | `createStateMachine` — typed transitions from constants tuples; `assert` drops into command guards |
90
- | `cvx-kit/middleware` | `rateLimit` — packaged middleware over an injected rate-limiter instance; keyed by `ctx.tenant`, missing key = config error |
91
- | `cvx-kit/webhooks` | `createWebhookBoundary`, `recordWebhookEvent`, `webhookEventsTable` — raw-body verify, natural-key dedup in the mutation |
92
- | `cvx-kit/agent-tools` | `createAgentTools` — tool records from table masks; mutation handlers route through command executors |
93
- | `cvx-kit/triggers` | `createTriggers`, `timestamps`, `appendOnly`, `noDelete`, `tenantOwnership`, `Triggers` |
94
- | `cvx-kit/errors` | `KitError`, `defaultErrors`, `ErrorFactory` |
95
- | `cvx-kit/components/foundation` | Component client and types; `Foundation.Command` is deprecated. The component and other capabilities remain available. New command registries use `cvx-kit/effect`. |
96
- | `cvx-kit/components/approvals` | `Approvals` client; default export = component config for `app.use` |
97
- | `cvx-kit/test` | `registerFoundation(t)`, `registerApprovals(t)` for convex-test |
82
+ | Import | Provides |
83
+ | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
84
+ | `cvx-kit` | Non-Effect helpers and component clients; import the optional Effect facade explicitly from `cvx-kit/effect`. |
85
+ | `cvx-kit/zod-table` | `zodTable`, `tenantTable`, `createModule`, `paginated`, `zodVariantTable`, `jsonSafeZid`, `TIMESTAMP_FIELDS` |
86
+ | `cvx-kit/effect` | Recommended command/query API: `createEffectFoundation`, `createEffectAuthFunctions`, `effectApiBuilder`, `effectZodApiBuilder`, `createEffectCrud`, idempotency/workflow adapters and operation tools. |
87
+ | `cvx-kit/auth` | `createAuthFunctions` (incl. optional `security` RLS config), `createInclude`, `defaultRoleMap` |
88
+ | `cvx-kit/tenancy` | `createTenantRules`, `composeRules`, `requireTenantReference`, `assertTenantOwned`, `TENANT_FIELD` |
89
+ | `cvx-kit/crud` | Compatibility CRUD factory using the deprecated Promise command facade; use `createEffectCrud` from `cvx-kit/effect` for new domains. |
90
+ | `cvx-kit/state-machine` | `createStateMachine` — typed transitions from constants tuples; `assert` drops into command guards |
91
+ | `cvx-kit/middleware` | `rateLimit` — packaged middleware over an injected rate-limiter instance; keyed by `ctx.tenant`, missing key = config error |
92
+ | `cvx-kit/webhooks` | `createWebhookBoundary`, `recordWebhookEvent`, `webhookEventsTable` — raw-body verify, natural-key dedup in the mutation |
93
+ | `cvx-kit/agent-tools` | `createAgentTools` — tool records from table masks; mutation handlers route through command executors |
94
+ | `cvx-kit/triggers` | `createTriggers`, `timestamps`, `appendOnly`, `noDelete`, `tenantOwnership`, `Triggers` |
95
+ | `cvx-kit/errors` | `KitError`, `defaultErrors`, `ErrorFactory` |
96
+ | `cvx-kit/components/foundation` | Component client and types; `Foundation.Command` is deprecated. The component and other capabilities remain available. New command registries use `cvx-kit/effect`. |
97
+ | `cvx-kit/components/approvals` | `Approvals` client; default export = component config for `app.use` |
98
+ | `cvx-kit/test` | `registerFoundation(t)`, `registerApprovals(t)` for convex-test |
98
99
 
99
100
  ## Minimal app wiring (the five root files)
100
101
 
@@ -291,3 +292,23 @@ Optional request context can be added through the existing Effect API adapter's
291
292
  and consume `ctx.commands.exec(name, input)` through the application's existing
292
293
  function helper names. Keep dependencies local to each feature; explicit execution
293
294
  and input-only command handlers remain supported. See [optional context injection](./command-context.md).
295
+
296
+ ## Shared auth and Effect context
297
+
298
+ Configure `createEffectAuthFunctions` once in `convex/functions.ts` using a typed
299
+ `AuthFunctionsConfig<DataModel, Role>` and inferred factory generics. Supply six
300
+ explicit policies: `query`, `mutation`, `action`, `systemQuery`, `systemMutation`,
301
+ and `systemAction`; use `{}` for no additions. Each supports `services`,
302
+ `context`, and `mapError`. All auth/role/admin constructors share the policy
303
+ for their kind. Services and context initialize only after successful auth and
304
+ role checks, using the secured DB and refreshed live action membership.
305
+
306
+ Bind query registries only in queries. Mutations can bind queries and commands
307
+ with their secured triggered writer. Actions have no DB; bind action-compatible
308
+ registries or delegate with `runMutation`/`runQuery`. Each registered mutation
309
+ has its own transaction and JWT auth; an action is not one transaction. Configure
310
+ internal system authority separately, without fabricated actors or inherited
311
+ public handles. Return plain records with handles under named keys; native/auth
312
+ key collisions are rejected, including inherited keys at runtime. Keep bound
313
+ handles request-local, return lazy Effects, and let failures escape mutations
314
+ for rollback. See `auth.md` for a packed-compiled shared factory example.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cvx-kit",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "Reusable Convex application kit: foundation kernels, approvals component, zod table boundaries, WorkOS-backed auth function constructors, and audited command protocol.",
5
5
  "keywords": [
6
6
  "component",
@@ -45,6 +45,10 @@ try {
45
45
  join(fixture, 'auth.ts'),
46
46
  readFileSync(join(root, 'test/auth-native-context-types.ts')),
47
47
  )
48
+ writeFileSync(
49
+ join(fixture, 'shared-auth.ts'),
50
+ readFileSync(join(root, 'test/effect-auth-types.ts')),
51
+ )
48
52
  writeFileSync(
49
53
  join(fixture, 'tsconfig.json'),
50
54
  JSON.stringify({
@@ -56,7 +60,7 @@ try {
56
60
  module: 'ESNext',
57
61
  moduleResolution: 'bundler',
58
62
  },
59
- include: ['auth.ts'],
63
+ include: ['auth.ts', 'shared-auth.ts'],
60
64
  }),
61
65
  )
62
66
  run('bun', ['install', '--ignore-scripts'], fixture)
@@ -396,6 +396,7 @@ try {
396
396
  'effect-foundation-types.ts',
397
397
  'effect-api-types.ts',
398
398
  'effect-api-zod-types.ts',
399
+ 'effect-auth-types.ts',
399
400
  'effect-registry-review-types.ts',
400
401
  'operation-contract-types.ts',
401
402
  'effect-schema-types.ts',