cvx-kit 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +200 -0
  3. package/dist/_virtual/_rolldown/runtime.mjs +1 -0
  4. package/dist/auth.d.mts +353 -0
  5. package/dist/auth.mjs +2 -0
  6. package/dist/auth.mjs.map +1 -0
  7. package/dist/components/approvals/_generated/api.d.mts +47 -0
  8. package/dist/components/approvals/_generated/api.mjs +2 -0
  9. package/dist/components/approvals/_generated/api.mjs.map +1 -0
  10. package/dist/components/approvals/_generated/component.d.mts +297 -0
  11. package/dist/components/approvals/_generated/component.mjs +1 -0
  12. package/dist/components/approvals/_generated/dataModel.d.mts +41 -0
  13. package/dist/components/approvals/_generated/dataModel.mjs +1 -0
  14. package/dist/components/approvals/_generated/server.d.mts +116 -0
  15. package/dist/components/approvals/_generated/server.mjs +2 -0
  16. package/dist/components/approvals/_generated/server.mjs.map +1 -0
  17. package/dist/components/approvals/audit.d.mts +40 -0
  18. package/dist/components/approvals/audit.mjs +2 -0
  19. package/dist/components/approvals/audit.mjs.map +1 -0
  20. package/dist/components/approvals/client.d.mts +98 -0
  21. package/dist/components/approvals/client.mjs +2 -0
  22. package/dist/components/approvals/client.mjs.map +1 -0
  23. package/dist/components/approvals/constants.d.mts +31 -0
  24. package/dist/components/approvals/constants.mjs +2 -0
  25. package/dist/components/approvals/constants.mjs.map +1 -0
  26. package/dist/components/approvals/convex.config.d.mts +5 -0
  27. package/dist/components/approvals/convex.config.mjs +2 -0
  28. package/dist/components/approvals/convex.config.mjs.map +1 -0
  29. package/dist/components/approvals/decisions.d.mts +61 -0
  30. package/dist/components/approvals/decisions.mjs +2 -0
  31. package/dist/components/approvals/decisions.mjs.map +1 -0
  32. package/dist/components/approvals/functions.d.mts +9 -0
  33. package/dist/components/approvals/functions.mjs +2 -0
  34. package/dist/components/approvals/functions.mjs.map +1 -0
  35. package/dist/components/approvals/requests.d.mts +323 -0
  36. package/dist/components/approvals/requests.mjs +2 -0
  37. package/dist/components/approvals/requests.mjs.map +1 -0
  38. package/dist/components/approvals/schema.d.mts +376 -0
  39. package/dist/components/approvals/schema.mjs +2 -0
  40. package/dist/components/approvals/schema.mjs.map +1 -0
  41. package/dist/components/approvals/validators.d.mts +302 -0
  42. package/dist/components/approvals/validators.mjs +2 -0
  43. package/dist/components/approvals/validators.mjs.map +1 -0
  44. package/dist/components/approvals/workflow.d.mts +11 -0
  45. package/dist/components/approvals/workflow.mjs +2 -0
  46. package/dist/components/approvals/workflow.mjs.map +1 -0
  47. package/dist/components/approvals/workflow_steps.d.mts +83 -0
  48. package/dist/components/approvals/workflow_steps.mjs +2 -0
  49. package/dist/components/approvals/workflow_steps.mjs.map +1 -0
  50. package/dist/components/foundation/_generated/api.d.mts +40 -0
  51. package/dist/components/foundation/_generated/api.mjs +2 -0
  52. package/dist/components/foundation/_generated/api.mjs.map +1 -0
  53. package/dist/components/foundation/_generated/component.d.mts +21 -0
  54. package/dist/components/foundation/_generated/component.mjs +1 -0
  55. package/dist/components/foundation/_generated/dataModel.d.mts +41 -0
  56. package/dist/components/foundation/_generated/dataModel.mjs +1 -0
  57. package/dist/components/foundation/_generated/server.d.mts +116 -0
  58. package/dist/components/foundation/_generated/server.mjs +2 -0
  59. package/dist/components/foundation/_generated/server.mjs.map +1 -0
  60. package/dist/components/foundation/client.d.mts +93 -0
  61. package/dist/components/foundation/client.mjs +2 -0
  62. package/dist/components/foundation/client.mjs.map +1 -0
  63. package/dist/components/foundation/convex.config.d.mts +5 -0
  64. package/dist/components/foundation/convex.config.mjs +2 -0
  65. package/dist/components/foundation/convex.config.mjs.map +1 -0
  66. package/dist/components/foundation/functions.d.mts +8 -0
  67. package/dist/components/foundation/functions.mjs +2 -0
  68. package/dist/components/foundation/functions.mjs.map +1 -0
  69. package/dist/components/foundation/modules/command/command.d.mts +47 -0
  70. package/dist/components/foundation/modules/command/command.mjs +2 -0
  71. package/dist/components/foundation/modules/command/command.mjs.map +1 -0
  72. package/dist/components/foundation/modules/observability/observability.d.mts +31 -0
  73. package/dist/components/foundation/modules/observability/observability.mjs +2 -0
  74. package/dist/components/foundation/modules/observability/observability.mjs.map +1 -0
  75. package/dist/components/foundation/modules/query/query.d.mts +24 -0
  76. package/dist/components/foundation/modules/query/query.mjs +2 -0
  77. package/dist/components/foundation/modules/query/query.mjs.map +1 -0
  78. package/dist/components/foundation/result.d.mts +27 -0
  79. package/dist/components/foundation/result.mjs +2 -0
  80. package/dist/components/foundation/result.mjs.map +1 -0
  81. package/dist/components/foundation/schema.d.mts +6 -0
  82. package/dist/components/foundation/schema.mjs +2 -0
  83. package/dist/components/foundation/schema.mjs.map +1 -0
  84. package/dist/components/foundation/telemetry.d.mts +9 -0
  85. package/dist/components/foundation/telemetry.mjs +2 -0
  86. package/dist/components/foundation/telemetry.mjs.map +1 -0
  87. package/dist/errors.d.mts +24 -0
  88. package/dist/errors.mjs +2 -0
  89. package/dist/errors.mjs.map +1 -0
  90. package/dist/index.d.mts +6 -0
  91. package/dist/index.mjs +1 -0
  92. package/dist/triggers.d.mts +27 -0
  93. package/dist/triggers.mjs +2 -0
  94. package/dist/triggers.mjs.map +1 -0
  95. package/dist/zod-table.d.mts +124 -0
  96. package/dist/zod-table.mjs +2 -0
  97. package/dist/zod-table.mjs.map +1 -0
  98. package/package.json +108 -0
  99. package/src/auth.ts +360 -0
  100. package/src/components/approvals/_generated/api.ts +69 -0
  101. package/src/components/approvals/_generated/component.ts +335 -0
  102. package/src/components/approvals/_generated/dataModel.ts +60 -0
  103. package/src/components/approvals/_generated/server.ts +156 -0
  104. package/src/components/approvals/audit.ts +148 -0
  105. package/src/components/approvals/client.ts +321 -0
  106. package/src/components/approvals/constants.ts +51 -0
  107. package/src/components/approvals/convex.config.ts +10 -0
  108. package/src/components/approvals/decisions.ts +376 -0
  109. package/src/components/approvals/functions.ts +27 -0
  110. package/src/components/approvals/requests.ts +240 -0
  111. package/src/components/approvals/schema.ts +23 -0
  112. package/src/components/approvals/validators.ts +272 -0
  113. package/src/components/approvals/workflow.ts +204 -0
  114. package/src/components/approvals/workflow_steps.ts +189 -0
  115. package/src/components/foundation/_generated/api.ts +62 -0
  116. package/src/components/foundation/_generated/component.ts +29 -0
  117. package/src/components/foundation/_generated/dataModel.ts +60 -0
  118. package/src/components/foundation/_generated/server.ts +156 -0
  119. package/src/components/foundation/client.ts +190 -0
  120. package/src/components/foundation/convex.config.ts +3 -0
  121. package/src/components/foundation/functions.ts +10 -0
  122. package/src/components/foundation/modules/command/__tests__/command.test.ts +106 -0
  123. package/src/components/foundation/modules/command/command.ts +154 -0
  124. package/src/components/foundation/modules/observability/__tests__/observability.test.ts +79 -0
  125. package/src/components/foundation/modules/observability/observability.ts +95 -0
  126. package/src/components/foundation/modules/query/__tests__/query.test.ts +25 -0
  127. package/src/components/foundation/modules/query/query.ts +47 -0
  128. package/src/components/foundation/result.ts +77 -0
  129. package/src/components/foundation/schema.ts +4 -0
  130. package/src/components/foundation/telemetry.ts +23 -0
  131. package/src/errors.ts +25 -0
  132. package/src/index.ts +41 -0
  133. package/src/test.ts +36 -0
  134. package/src/triggers.ts +90 -0
  135. package/src/zod-table.ts +197 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Angel Lopez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,200 @@
1
+ # cvx-kit
2
+
3
+ [![CI](https://github.com/ImRLopezAG/cvx-kit/actions/workflows/ci.yml/badge.svg)](https://github.com/ImRLopezAG/cvx-kit/actions/workflows/ci.yml)
4
+ [![npm](https://img.shields.io/npm/v/cvx-kit)](https://www.npmjs.com/package/cvx-kit)
5
+
6
+ Reusable Convex application kit: foundation kernels, a generic approvals
7
+ component, zod table boundaries, auth-aware function constructors with
8
+ per-app role vocabularies, an application trigger registry, and an audited
9
+ command protocol.
10
+
11
+ ```sh
12
+ bun add cvx-kit
13
+ # or: npm install cvx-kit
14
+ ```
15
+
16
+ ## Toolchain
17
+
18
+ - `bun install` — dependencies
19
+ - `bun run test` — vite-plus test suite (`vp test run`, convex-test on edge-runtime)
20
+ - `bun run typecheck` — `tsc --noEmit`
21
+ - `bun run build` — `vp pack` (tsdown on Rolldown); all build options live in
22
+ the `pack` key of `vite.config.ts`: per-module ESM + `.d.mts` to `dist/`,
23
+ target ES2025, minified, deps external, publint-verified
24
+ - `bun run build:codegen` — re-run `convex codegen` for both components, then build
25
+
26
+ ## Modules
27
+
28
+ ### `cvx-kit/zod-table`
29
+
30
+ One zod shape per entity, masked into every boundary it crosses:
31
+
32
+ ```ts
33
+ import { zodTable } from 'cvx-kit/zod-table'
34
+
35
+ export const documents = zodTable(
36
+ 'documents',
37
+ (id) => ({
38
+ title: z.string(),
39
+ ownerId: id('users'),
40
+ secretNote: z.string(),
41
+ }),
42
+ {
43
+ commandFields: ['title'], // what a command may say
44
+ publicFields: ['title', 'ownerId'], // the DTO allowlist
45
+ },
46
+ )
47
+ // documents.table → defineTable(...) for schema.ts
48
+ // documents.toPublicDto(row) → projects AND re-parses (runtime redaction)
49
+ ```
50
+
51
+ Every table gets `createdAt`, `updatedAt`, and `archivedAt` baked in —
52
+ opinionated, server-owned, always excluded from inserts and commands
53
+ (expose them via `publicFields` when a DTO needs them). Register the
54
+ `timestamps` trigger once and they maintain themselves; `archivedAt` stays
55
+ under application control.
56
+
57
+ `zodVariantTable` covers discriminated-union tables; `jsonSafeZid` keeps
58
+ LLM-tool JSON schemas primitive while preserving the `Id<...>` type.
59
+
60
+ ### `cvx-kit/auth`
61
+
62
+ `createAuthFunctions` builds the six auth-aware constructors from injected
63
+ policy — nothing in the kit imports your app's singletons:
64
+
65
+ ```ts
66
+ import { createAuthFunctions } from 'cvx-kit/auth'
67
+ import { action, internalAction, internalMutation, mutation, query } from './_generated/server'
68
+
69
+ export const { authQuery, authMutation, authAction, adminQuery, adminMutation,
70
+ adminAction, systemMutation, systemAction, include } =
71
+ createAuthFunctions<DataModel>({
72
+ query, mutation, action, internalMutation, internalAction,
73
+ getAuthUser: (ctx) => authKit.getAuthUser(ctx),
74
+ verifyMembership: async ({ userId, organizationId }) => {
75
+ const memberships = await authKit.workos.userManagement
76
+ .listOrganizationMemberships({ organizationId, userId, statuses: ['active'] })
77
+ const m = memberships.data.find((c) => c.userId === userId)
78
+ return m ? { organizationId: m.organizationId, roleSlug: m.role.slug } : null
79
+ },
80
+ wrapDB: (ctx) => triggers.wrapDB(ctx),
81
+ })
82
+ ```
83
+
84
+ Actions re-verify membership live and fail closed; mutations get the trigger
85
+ registry structurally; `include()` bounds every read (default 100 rows).
86
+
87
+ ### `cvx-kit/components/foundation`
88
+
89
+ Declared once; everything destructures from it. `Command` comes out of the
90
+ Foundation already bound to observability and the audit writer — there is no
91
+ separate command import.
92
+
93
+ ```ts
94
+ // convex/convex.config.ts
95
+ import foundation from 'cvx-kit/components/foundation'
96
+ app.use(foundation)
97
+
98
+ // convex/foundation.ts — declared once, the single kernel source
99
+ import { Foundation } from 'cvx-kit/components/foundation'
100
+ export const { Command, Query, observability } = new Foundation(
101
+ components.foundation,
102
+ {
103
+ observability: {
104
+ enabled: () => env.OBS === 'true',
105
+ classifyError,
106
+ writeAudit: (ctx, entry) => writeAuditEntry(ctx, entry),
107
+ },
108
+ },
109
+ )
110
+
111
+ // convex/domain/<owner>/commands.ts
112
+ import { Command } from '../foundation'
113
+
114
+ const commands = new Command({
115
+ 'documents.rename': Command.operation({
116
+ command: renameInput,
117
+ result: renameResult,
118
+ classification: 'business',
119
+ audit: ({ command }, ctx) => ({ ... }),
120
+ }),
121
+ })
122
+ export const executeRename = commands.exec({ operation: 'documents.rename', handler })
123
+ ```
124
+
125
+ Every command is validated (input and output zod-parsed), observed, and
126
+ audited in-transaction. `classification` and `audit()` are mandatory per
127
+ operation: auditing is type-enforced, not opt-in. The foundation component
128
+ itself owns zero tables and ships `executeResultBoundary` (typed failures
129
+ only while the transaction has no effects; otherwise rethrow so Convex
130
+ rolls back).
131
+
132
+ ### `cvx-kit/components/approvals` (component)
133
+
134
+ Declarative approval workflows (decision / branch / mutation / action /
135
+ notify) with quorum, maker-checker, expiry, compatibility keys, durable
136
+ execution via a nested `@convex-dev/workflow`, and its own audit trail via a
137
+ nested `convex-audit-log`. Tenancy is an opaque `scopeRef`; callbacks are
138
+ function handles — the component never imports host code.
139
+
140
+ ```ts
141
+ // convex/convex.config.ts
142
+ import approvals from 'cvx-kit/components/approvals'
143
+ app.use(approvals)
144
+ ```
145
+
146
+ ## Component authoring conventions
147
+
148
+ This package follows the official Convex component template
149
+ (`get-convex/templates/template-component`; see
150
+ https://docs.convex.dev/components/authoring):
151
+
152
+ - Components live in `src/components/<name>` with their own
153
+ `convex.config.ts`, `schema.ts`, functions, and **checked-in
154
+ `_generated/`** (required for npm-distributed components).
155
+ - Hosts mount via the `./components/<name>/convex.config` subpath export and call the
156
+ component only through its client class — never its tables directly.
157
+ - `bun run build:codegen` re-runs `convex codegen --component-dir` for both
158
+ components before building.
159
+ - Consumers test in-memory via the `cvx-kit/test` export:
160
+
161
+ ```ts
162
+ import { convexTest } from 'convex-test'
163
+ import { registerApprovals, registerFoundation } from 'cvx-kit/test'
164
+
165
+ const t = convexTest(appSchema, modules)
166
+ registerFoundation(t)
167
+ registerApprovals(t)
168
+ ```
169
+
170
+ ## Releasing
171
+
172
+ Publishing uses [npm Trusted Publishing](https://docs.npmjs.com/trusted-publishers/)
173
+ (OIDC) — no tokens stored anywhere. The release workflow authenticates via
174
+ GitHub's OIDC and npm generates provenance attestations automatically.
175
+
176
+ - `bun run release` — patch version bump, tag, and push; the `v*` tag
177
+ triggers `.github/workflows/release.yml`, which verifies (typecheck, test,
178
+ build + publint) and publishes.
179
+ - `bun run release:minor` — same, minor bump.
180
+
181
+ One-time setup (already-published package required first):
182
+
183
+ 1. First publish is manual: `npm publish --access public` locally.
184
+ 2. On npmjs.com → package → Settings → Trusted Publisher: GitHub Actions,
185
+ owner `ImRLopezAG`, repository `cvx-kit`, workflow `release.yml`,
186
+ environment `npm`.
187
+ 3. Create the `npm` environment in the GitHub repo settings.
188
+
189
+ ## Roadmap
190
+
191
+ - [ ] Port the approvals `copyability` test (cp -r + tsc + `convex codegen
192
+ --dry-run`) into this repo's suite as the portability guarantee.
193
+ - [ ] `createArchitectureTest(rules)` helper so consuming projects get the
194
+ boundary guardrails (no inline tables/enums, single facade per
195
+ component) on day one.
196
+ - [ ] WorkOS scaffold generator for `auth.ts` / `auth.config.ts` / `http.ts`.
197
+ - [ ] Audit facade helpers (fingerprinted cursors, bounded windows) over any
198
+ `convex-audit-log` instance.
199
+ - [ ] Decide the published name/scope before `npm publish` (`cvx-kit` is a
200
+ working title).
@@ -0,0 +1 @@
1
+ var e=Object.defineProperty,t=(t,n)=>{let r={};for(var i in t)e(r,i,{get:t[i],enumerable:!0});return n||e(r,Symbol.toStringTag,{value:`Module`}),r};export{t as __exportAll};
@@ -0,0 +1,353 @@
1
+ import { ErrorFactory } from "./errors.mjs";
2
+ import { ActionBuilder, DocumentByInfo, GenericActionCtx, GenericDataModel, GenericMutationCtx, GenericQueryCtx, GenericTableInfo, IndexNames, IndexRange, IndexRangeBuilder, MutationBuilder, NamedIndex, Query, QueryBuilder, QueryInitializer, UserIdentity } from "convex/server";
3
+ import { Triggers } from "convex-helpers/server/triggers";
4
+ //#region src/auth.d.ts
5
+ /** The default vocabulary; every app can supply its own via the Role generic. */
6
+ type DefaultRole = 'reader' | 'writer' | 'admin';
7
+ type Actor<Role extends string> = Readonly<{
8
+ userId: string;
9
+ organizationId: string;
10
+ role: Role;
11
+ }>;
12
+ type AuthBundle<Role extends string> = Readonly<{
13
+ identity: UserIdentity;
14
+ user: Readonly<{
15
+ id: string;
16
+ }>;
17
+ org: Readonly<{
18
+ organizationId: string;
19
+ role: Role;
20
+ }>;
21
+ role: Role;
22
+ actor: Actor<Role>;
23
+ }>;
24
+ type AnyAuthContext<DataModel extends GenericDataModel> = GenericQueryCtx<DataModel> | GenericMutationCtx<DataModel> | GenericActionCtx<DataModel>;
25
+ type AuthFunctionsConfig<DataModel extends GenericDataModel, Role extends string> = {
26
+ /** The consumer's generated function builders (from ./_generated/server). */
27
+ query: QueryBuilder<DataModel, 'public'>;
28
+ mutation: MutationBuilder<DataModel, 'public'>;
29
+ action: ActionBuilder<DataModel, 'public'>;
30
+ internalMutation: MutationBuilder<DataModel, 'internal'>;
31
+ internalAction: ActionBuilder<DataModel, 'internal'>;
32
+ /** Resolves the synchronized principal, e.g. (ctx) => authKit.getAuthUser(ctx). */
33
+ getAuthUser: (ctx: AnyAuthContext<DataModel>) => Promise<{
34
+ id: string;
35
+ } | null>;
36
+ /**
37
+ * Maps an identity role slug onto the app's role vocabulary. Returning
38
+ * null rejects the caller with FORBIDDEN — unknown roles never pass.
39
+ */
40
+ mapRole: (roleSlug?: string) => Role | null;
41
+ /** Roles allowed through the admin* constructors. */
42
+ adminRoles: readonly NoInfer<Role>[];
43
+ /**
44
+ * Live membership check for actions (queries/mutations trust the JWT).
45
+ * Authentication fails closed with FORBIDDEN when verification errors.
46
+ * Omit to let actions trust the JWT like queries do.
47
+ */
48
+ verifyMembership?: (input: {
49
+ userId: string;
50
+ organizationId: string;
51
+ }) => Promise<{
52
+ organizationId: string;
53
+ roleSlug: string;
54
+ } | null>;
55
+ /** Host error policy; defaults to throwing KitError. */
56
+ errors?: ErrorFactory;
57
+ /**
58
+ * Application trigger registry. When set, every authMutation,
59
+ * roleMutation, and systemMutation write runs through triggers.wrapDB —
60
+ * evaluations registered on tables fire structurally.
61
+ */
62
+ triggers?: Triggers<DataModel>;
63
+ /** Lower-level alternative to `triggers`: a custom ctx wrapper. */
64
+ wrapDB?: (ctx: GenericMutationCtx<DataModel>) => GenericMutationCtx<DataModel>;
65
+ /** Upper bound for include() reads. */
66
+ maxIncludedQueryRows?: number;
67
+ };
68
+ /** WorkOS-style default: `member` writes; reader/writer/admin pass through. */
69
+ declare function defaultRoleMap(roleSlug?: string): DefaultRole | null;
70
+ type IncludedQuery<Table extends GenericTableInfo> = {
71
+ when<Value>(value: Value | null | undefined, select: (query: QueryInitializer<Table>, value: Value) => Query<Table>): IncludedQuery<Table>;
72
+ matching<IndexName extends IndexNames<Table>>(indexName: IndexName, indexRange?: (query: IndexRangeBuilder<DocumentByInfo<Table>, NamedIndex<Table, IndexName>>) => IndexRange, shouldMatch?: boolean): IncludedQuery<Table>;
73
+ otherwise(select: (query: QueryInitializer<Table>) => Query<Table>): Query<Table>;
74
+ resolve(): Query<Table>;
75
+ execute<Result = Awaited<ReturnType<Query<Table>['take']>>>(limit: number, transform?: (data: Awaited<ReturnType<Query<Table>['take']>>) => Result | Promise<Result>): Promise<Result>;
76
+ };
77
+ type Include = <Table extends GenericTableInfo>(query: QueryInitializer<Table>) => IncludedQuery<Table>;
78
+ /** Builds an include() that selects the first matching indexed query and bounds the read. */
79
+ declare function createInclude(options?: {
80
+ errors?: ErrorFactory;
81
+ maxRows?: number;
82
+ }): Include;
83
+ /**
84
+ * Builds the auth-aware function constructors from the host's injected
85
+ * policy: identity resolution, the app's own role vocabulary, live
86
+ * membership verification for actions, trigger wrapping for mutations,
87
+ * and errors. Role-based access is first-class — every constructor family
88
+ * has a role-gated variant (roleQuery/roleMutation/roleAction), and the
89
+ * admin* constructors are just that gate applied to config.adminRoles.
90
+ */
91
+ declare function createAuthFunctions<DataModel extends GenericDataModel, const Role extends string = DefaultRole>(config: AuthFunctionsConfig<DataModel, Role>): {
92
+ include: Include;
93
+ authenticatedUser: (ctx: AnyAuthContext<DataModel>) => Promise<AuthBundle<Role>>;
94
+ roleQuery: (...allowed: readonly Role[]) => import("convex-helpers/server/zod4").CustomBuilder<"query", {}, {
95
+ db: import("convex/server").GenericDatabaseReader<DataModel>;
96
+ auth: import("convex/server").Auth;
97
+ storage: import("convex/server").StorageReader;
98
+ runQuery: <Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").ArgsAndOptions<Query, {
99
+ transactionLimits?: import("convex/server").TransactionLimits;
100
+ }>) => Promise<import("convex/server").FunctionReturnType<Query>>;
101
+ meta: import("convex/server").QueryMeta;
102
+ identity: UserIdentity;
103
+ user: Readonly<{
104
+ id: string;
105
+ }>;
106
+ org: Readonly<{
107
+ organizationId: string;
108
+ role: Role;
109
+ }>;
110
+ role: Role;
111
+ actor: Readonly<{
112
+ userId: string;
113
+ organizationId: string;
114
+ role: Role;
115
+ }>;
116
+ include: Include;
117
+ }, {}, GenericQueryCtx<DataModel>, "public", object>;
118
+ roleMutation: (...allowed: readonly Role[]) => import("convex-helpers/server/zod4").CustomBuilder<"mutation", {}, {
119
+ db: import("convex/server").GenericDatabaseWriter<DataModel>;
120
+ auth: import("convex/server").Auth;
121
+ storage: import("convex/server").StorageWriter;
122
+ scheduler: import("convex/server").Scheduler;
123
+ runQuery: <Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").ArgsAndOptions<Query, import("convex/server").AdvancedRunQueryOptions>) => Promise<import("convex/server").FunctionReturnType<Query>>;
124
+ runMutation: <Mutation extends import("convex/server").FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: import("convex/server").ArgsAndOptions<Mutation, {
125
+ transactionLimits?: import("convex/server").TransactionLimits;
126
+ }>) => Promise<import("convex/server").FunctionReturnType<Mutation>>;
127
+ meta: import("convex/server").MutationMeta;
128
+ identity: UserIdentity;
129
+ user: Readonly<{
130
+ id: string;
131
+ }>;
132
+ org: Readonly<{
133
+ organizationId: string;
134
+ role: Role;
135
+ }>;
136
+ role: Role;
137
+ actor: Readonly<{
138
+ userId: string;
139
+ organizationId: string;
140
+ role: Role;
141
+ }>;
142
+ include: Include;
143
+ }, {}, GenericMutationCtx<DataModel>, "public", object>;
144
+ roleAction: (...allowed: readonly Role[]) => import("convex-helpers/server/zod4").CustomBuilder<"action", {}, {
145
+ runQuery<Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").OptionalRestArgs<Query>): Promise<import("convex/server").FunctionReturnType<Query>>;
146
+ runMutation<Mutation extends import("convex/server").FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: import("convex/server").OptionalRestArgs<Mutation>): Promise<import("convex/server").FunctionReturnType<Mutation>>;
147
+ runAction<Action extends import("convex/server").FunctionReference<"action", "public" | "internal">>(action: Action, ...args: import("convex/server").OptionalRestArgs<Action>): Promise<import("convex/server").FunctionReturnType<Action>>;
148
+ scheduler: import("convex/server").Scheduler;
149
+ auth: import("convex/server").Auth;
150
+ storage: import("convex/server").StorageActionWriter;
151
+ vectorSearch<TableName extends import("convex/server").TableNamesInDataModel<DataModel>, IndexName extends import("convex/server").VectorIndexNames<import("convex/server").NamedTableInfo<DataModel, TableName>>>(tableName: TableName, indexName: IndexName, query: {
152
+ vector: number[];
153
+ limit?: number;
154
+ filter?: ((q: import("convex/server").VectorFilterBuilder<DocumentByInfo<import("convex/server").NamedTableInfo<DataModel, TableName>>, import("convex/server").NamedVectorIndex<import("convex/server").NamedTableInfo<DataModel, TableName>, IndexName>>) => import("convex/server").FilterExpression<boolean>) | undefined;
155
+ }): Promise<{
156
+ _id: import("convex/values").GenericId<TableName>;
157
+ _score: number;
158
+ }[]>;
159
+ meta: import("convex/server").ActionMeta;
160
+ identity: UserIdentity;
161
+ user: Readonly<{
162
+ id: string;
163
+ }>;
164
+ org: Readonly<{
165
+ organizationId: string;
166
+ role: Role;
167
+ }>;
168
+ role: Role;
169
+ actor: Readonly<{
170
+ userId: string;
171
+ organizationId: string;
172
+ role: Role;
173
+ }>;
174
+ }, {}, GenericActionCtx<DataModel>, "public", object>;
175
+ authQuery: import("convex-helpers/server/zod4").CustomBuilder<"query", {}, {
176
+ db: import("convex/server").GenericDatabaseReader<DataModel>;
177
+ auth: import("convex/server").Auth;
178
+ storage: import("convex/server").StorageReader;
179
+ runQuery: <Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").ArgsAndOptions<Query, {
180
+ transactionLimits?: import("convex/server").TransactionLimits;
181
+ }>) => Promise<import("convex/server").FunctionReturnType<Query>>;
182
+ meta: import("convex/server").QueryMeta;
183
+ identity: UserIdentity;
184
+ user: Readonly<{
185
+ id: string;
186
+ }>;
187
+ org: Readonly<{
188
+ organizationId: string;
189
+ role: Role;
190
+ }>;
191
+ role: Role;
192
+ actor: Readonly<{
193
+ userId: string;
194
+ organizationId: string;
195
+ role: Role;
196
+ }>;
197
+ include: Include;
198
+ }, {}, GenericQueryCtx<DataModel>, "public", object>;
199
+ authMutation: import("convex-helpers/server/zod4").CustomBuilder<"mutation", {}, {
200
+ db: import("convex/server").GenericDatabaseWriter<DataModel>;
201
+ auth: import("convex/server").Auth;
202
+ storage: import("convex/server").StorageWriter;
203
+ scheduler: import("convex/server").Scheduler;
204
+ runQuery: <Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").ArgsAndOptions<Query, import("convex/server").AdvancedRunQueryOptions>) => Promise<import("convex/server").FunctionReturnType<Query>>;
205
+ runMutation: <Mutation extends import("convex/server").FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: import("convex/server").ArgsAndOptions<Mutation, {
206
+ transactionLimits?: import("convex/server").TransactionLimits;
207
+ }>) => Promise<import("convex/server").FunctionReturnType<Mutation>>;
208
+ meta: import("convex/server").MutationMeta;
209
+ identity: UserIdentity;
210
+ user: Readonly<{
211
+ id: string;
212
+ }>;
213
+ org: Readonly<{
214
+ organizationId: string;
215
+ role: Role;
216
+ }>;
217
+ role: Role;
218
+ actor: Readonly<{
219
+ userId: string;
220
+ organizationId: string;
221
+ role: Role;
222
+ }>;
223
+ include: Include;
224
+ }, {}, GenericMutationCtx<DataModel>, "public", object>;
225
+ authAction: import("convex-helpers/server/zod4").CustomBuilder<"action", {}, {
226
+ runQuery<Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").OptionalRestArgs<Query>): Promise<import("convex/server").FunctionReturnType<Query>>;
227
+ runMutation<Mutation extends import("convex/server").FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: import("convex/server").OptionalRestArgs<Mutation>): Promise<import("convex/server").FunctionReturnType<Mutation>>;
228
+ runAction<Action extends import("convex/server").FunctionReference<"action", "public" | "internal">>(action: Action, ...args: import("convex/server").OptionalRestArgs<Action>): Promise<import("convex/server").FunctionReturnType<Action>>;
229
+ scheduler: import("convex/server").Scheduler;
230
+ auth: import("convex/server").Auth;
231
+ storage: import("convex/server").StorageActionWriter;
232
+ vectorSearch<TableName extends import("convex/server").TableNamesInDataModel<DataModel>, IndexName extends import("convex/server").VectorIndexNames<import("convex/server").NamedTableInfo<DataModel, TableName>>>(tableName: TableName, indexName: IndexName, query: {
233
+ vector: number[];
234
+ limit?: number;
235
+ filter?: ((q: import("convex/server").VectorFilterBuilder<DocumentByInfo<import("convex/server").NamedTableInfo<DataModel, TableName>>, import("convex/server").NamedVectorIndex<import("convex/server").NamedTableInfo<DataModel, TableName>, IndexName>>) => import("convex/server").FilterExpression<boolean>) | undefined;
236
+ }): Promise<{
237
+ _id: import("convex/values").GenericId<TableName>;
238
+ _score: number;
239
+ }[]>;
240
+ meta: import("convex/server").ActionMeta;
241
+ identity: UserIdentity;
242
+ user: Readonly<{
243
+ id: string;
244
+ }>;
245
+ org: Readonly<{
246
+ organizationId: string;
247
+ role: Role;
248
+ }>;
249
+ role: Role;
250
+ actor: Readonly<{
251
+ userId: string;
252
+ organizationId: string;
253
+ role: Role;
254
+ }>;
255
+ }, {}, GenericActionCtx<DataModel>, "public", object>;
256
+ adminQuery: import("convex-helpers/server/zod4").CustomBuilder<"query", {}, {
257
+ db: import("convex/server").GenericDatabaseReader<DataModel>;
258
+ auth: import("convex/server").Auth;
259
+ storage: import("convex/server").StorageReader;
260
+ runQuery: <Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").ArgsAndOptions<Query, {
261
+ transactionLimits?: import("convex/server").TransactionLimits;
262
+ }>) => Promise<import("convex/server").FunctionReturnType<Query>>;
263
+ meta: import("convex/server").QueryMeta;
264
+ identity: UserIdentity;
265
+ user: Readonly<{
266
+ id: string;
267
+ }>;
268
+ org: Readonly<{
269
+ organizationId: string;
270
+ role: Role;
271
+ }>;
272
+ role: Role;
273
+ actor: Readonly<{
274
+ userId: string;
275
+ organizationId: string;
276
+ role: Role;
277
+ }>;
278
+ include: Include;
279
+ }, {}, GenericQueryCtx<DataModel>, "public", object>;
280
+ adminMutation: import("convex-helpers/server/zod4").CustomBuilder<"mutation", {}, {
281
+ db: import("convex/server").GenericDatabaseWriter<DataModel>;
282
+ auth: import("convex/server").Auth;
283
+ storage: import("convex/server").StorageWriter;
284
+ scheduler: import("convex/server").Scheduler;
285
+ runQuery: <Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").ArgsAndOptions<Query, import("convex/server").AdvancedRunQueryOptions>) => Promise<import("convex/server").FunctionReturnType<Query>>;
286
+ runMutation: <Mutation extends import("convex/server").FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: import("convex/server").ArgsAndOptions<Mutation, {
287
+ transactionLimits?: import("convex/server").TransactionLimits;
288
+ }>) => Promise<import("convex/server").FunctionReturnType<Mutation>>;
289
+ meta: import("convex/server").MutationMeta;
290
+ identity: UserIdentity;
291
+ user: Readonly<{
292
+ id: string;
293
+ }>;
294
+ org: Readonly<{
295
+ organizationId: string;
296
+ role: Role;
297
+ }>;
298
+ role: Role;
299
+ actor: Readonly<{
300
+ userId: string;
301
+ organizationId: string;
302
+ role: Role;
303
+ }>;
304
+ include: Include;
305
+ }, {}, GenericMutationCtx<DataModel>, "public", object>;
306
+ adminAction: import("convex-helpers/server/zod4").CustomBuilder<"action", {}, {
307
+ runQuery<Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").OptionalRestArgs<Query>): Promise<import("convex/server").FunctionReturnType<Query>>;
308
+ runMutation<Mutation extends import("convex/server").FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: import("convex/server").OptionalRestArgs<Mutation>): Promise<import("convex/server").FunctionReturnType<Mutation>>;
309
+ runAction<Action extends import("convex/server").FunctionReference<"action", "public" | "internal">>(action: Action, ...args: import("convex/server").OptionalRestArgs<Action>): Promise<import("convex/server").FunctionReturnType<Action>>;
310
+ scheduler: import("convex/server").Scheduler;
311
+ auth: import("convex/server").Auth;
312
+ storage: import("convex/server").StorageActionWriter;
313
+ vectorSearch<TableName extends import("convex/server").TableNamesInDataModel<DataModel>, IndexName extends import("convex/server").VectorIndexNames<import("convex/server").NamedTableInfo<DataModel, TableName>>>(tableName: TableName, indexName: IndexName, query: {
314
+ vector: number[];
315
+ limit?: number;
316
+ filter?: ((q: import("convex/server").VectorFilterBuilder<DocumentByInfo<import("convex/server").NamedTableInfo<DataModel, TableName>>, import("convex/server").NamedVectorIndex<import("convex/server").NamedTableInfo<DataModel, TableName>, IndexName>>) => import("convex/server").FilterExpression<boolean>) | undefined;
317
+ }): Promise<{
318
+ _id: import("convex/values").GenericId<TableName>;
319
+ _score: number;
320
+ }[]>;
321
+ meta: import("convex/server").ActionMeta;
322
+ identity: UserIdentity;
323
+ user: Readonly<{
324
+ id: string;
325
+ }>;
326
+ org: Readonly<{
327
+ organizationId: string;
328
+ role: Role;
329
+ }>;
330
+ role: Role;
331
+ actor: Readonly<{
332
+ userId: string;
333
+ organizationId: string;
334
+ role: Role;
335
+ }>;
336
+ }, {}, GenericActionCtx<DataModel>, "public", object>;
337
+ systemMutation: import("convex-helpers/server/zod4").CustomBuilder<"mutation", {}, {
338
+ db: import("convex/server").GenericDatabaseWriter<DataModel>;
339
+ auth: import("convex/server").Auth;
340
+ storage: import("convex/server").StorageWriter;
341
+ scheduler: import("convex/server").Scheduler;
342
+ runQuery: <Query extends import("convex/server").FunctionReference<"query", "public" | "internal">>(query: Query, ...args: import("convex/server").ArgsAndOptions<Query, import("convex/server").AdvancedRunQueryOptions>) => Promise<import("convex/server").FunctionReturnType<Query>>;
343
+ runMutation: <Mutation extends import("convex/server").FunctionReference<"mutation", "public" | "internal">>(mutation: Mutation, ...args: import("convex/server").ArgsAndOptions<Mutation, {
344
+ transactionLimits?: import("convex/server").TransactionLimits;
345
+ }>) => Promise<import("convex/server").FunctionReturnType<Mutation>>;
346
+ meta: import("convex/server").MutationMeta;
347
+ include: Include;
348
+ }, {}, GenericMutationCtx<DataModel>, "internal", object>;
349
+ systemAction: import("convex-helpers/server/zod4").CustomBuilder<"action", {}, GenericActionCtx<DataModel>, {}, GenericActionCtx<DataModel>, "internal", object>;
350
+ };
351
+ //#endregion
352
+ export { Actor, AuthBundle, AuthFunctionsConfig, DefaultRole, Include, IncludedQuery, createAuthFunctions, createInclude, defaultRoleMap };
353
+ //# sourceMappingURL=auth.d.mts.map
package/dist/auth.mjs ADDED
@@ -0,0 +1,2 @@
1
+ import{defaultErrors as e}from"./errors.mjs";import{zCustomAction as t,zCustomMutation as n,zCustomQuery as r}from"convex-helpers/server/zod4";function i(e){let t=e?.trim().toLowerCase();return t===`member`?`writer`:t===`reader`||t===`writer`||t===`admin`?t:null}function a(t){let n=t?.errors??e,r=t?.maxRows??100;function i(e,t){return{when:(n,r)=>t||n==null?i(e,t):i(e,r(e,n)),matching:(n,r,a=!0)=>t||!a?i(e,t):i(e,e.withIndex(n,r)),otherwise:n=>t??n(e),resolve:()=>t??e.fullTableScan(),execute:async(i,a)=>{if(!Number.isSafeInteger(i)||i<1||i>r)return n.throw({code:`INVALID_REQUEST_BOUNDARY`,message:`A bounded query limit must be from 1 to ${r}`});let o=await(t??e.fullTableScan()).take(i);return a?a(o):o}}}return e=>i(e)}function o(i){let o=i.errors??e,s=i.triggers?e=>i.triggers.wrapDB(e):i.wrapDB??(e=>e),c=a({errors:o,maxRows:i.maxIncludedQueryRows});async function l(e){let t=await e.auth.getUserIdentity(),n=t?await i.getAuthUser(e):null;if(!t||!n)return o.throw({code:`UNAUTHENTICATED`});let r=t.organization,a=r&&typeof r==`object`&&`organizationId`in r&&typeof r.organizationId==`string`?r.organizationId:typeof t.org_id==`string`?t.org_id:null;if(!a)return o.throw({code:`UNAUTHENTICATED`});let s=r&&typeof r==`object`&&`role`in r&&typeof r.role==`string`?r.role:typeof t.role==`string`?t.role:void 0,c=i.mapRole(s);return c?Object.freeze({identity:t,user:Object.freeze({id:n.id}),org:Object.freeze({organizationId:a,role:c}),role:c,actor:Object.freeze({userId:n.id,organizationId:a,role:c})}):o.throw({code:`FORBIDDEN`})}async function u(e,t){if(!i.verifyMembership)return null;try{let n=await i.verifyMembership({userId:e,organizationId:t});if(n&&n.organizationId===t)return n}catch{}return o.throw({code:`FORBIDDEN`})}function d(e,t){if(!t.includes(e))return o.throw({code:`FORBIDDEN`})}async function f(e){return{ctx:{...e,include:c,...await l(e)},args:{}}}async function p(e){return{ctx:{...s(e),include:c,...await l(e)},args:{}}}async function m(e){let t=await l(e),n=await u(t.user.id,t.org.organizationId);if(!n)return{ctx:{...e,...t},args:{}};let r=i.mapRole(n.roleSlug);return r?{ctx:{...e,...t,org:Object.freeze({organizationId:n.organizationId,role:r}),role:r,actor:Object.freeze({userId:t.user.id,organizationId:n.organizationId,role:r})},args:{}}:o.throw({code:`FORBIDDEN`})}function h(...e){return r(i.query,{args:{},input:async t=>{let n=await f(t);return d(n.ctx.actor.role,e),n}})}function g(...e){return n(i.mutation,{args:{},input:async t=>{let n=await p(t);return d(n.ctx.actor.role,e),n}})}function _(...e){return t(i.action,{args:{},input:async t=>{let n=await m(t);return d(n.ctx.actor.role,e),n}})}return{include:c,authenticatedUser:l,roleQuery:h,roleMutation:g,roleAction:_,authQuery:r(i.query,{args:{},input:f}),authMutation:n(i.mutation,{args:{},input:p}),authAction:t(i.action,{args:{},input:m}),adminQuery:h(...i.adminRoles),adminMutation:g(...i.adminRoles),adminAction:_(...i.adminRoles),systemMutation:n(i.internalMutation,{args:{},input:async e=>({ctx:{...s(e),include:c},args:{}})}),systemAction:t(i.internalAction,{args:{},input:async e=>({ctx:e,args:{}})})}}export{o as createAuthFunctions,a as createInclude,i as defaultRoleMap};
2
+ //# sourceMappingURL=auth.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.mjs","names":[],"sources":["../src/auth.ts"],"sourcesContent":["import {\n\tzCustomAction,\n\tzCustomMutation,\n\tzCustomQuery,\n} from 'convex-helpers/server/zod4'\nimport type {\n\tActionBuilder,\n\tDocumentByInfo,\n\tGenericActionCtx,\n\tGenericDataModel,\n\tGenericMutationCtx,\n\tGenericQueryCtx,\n\tGenericTableInfo,\n\tIndexNames,\n\tIndexRange,\n\tIndexRangeBuilder,\n\tMutationBuilder,\n\tNamedIndex,\n\tQuery,\n\tQueryBuilder,\n\tQueryInitializer,\n\tUserIdentity,\n} from 'convex/server'\nimport type { Triggers } from 'convex-helpers/server/triggers'\nimport { defaultErrors, type ErrorFactory } from './errors'\n\n/** The default vocabulary; every app can supply its own via the Role generic. */\nexport type DefaultRole = 'reader' | 'writer' | 'admin'\n\nexport type Actor<Role extends string> = Readonly<{\n\tuserId: string\n\torganizationId: string\n\trole: Role\n}>\n\nexport type AuthBundle<Role extends string> = Readonly<{\n\tidentity: UserIdentity\n\tuser: Readonly<{ id: string }>\n\torg: Readonly<{ organizationId: string; role: Role }>\n\trole: Role\n\tactor: Actor<Role>\n}>\n\ntype AnyAuthContext<DataModel extends GenericDataModel> =\n\t| GenericQueryCtx<DataModel>\n\t| GenericMutationCtx<DataModel>\n\t| GenericActionCtx<DataModel>\n\nexport type AuthFunctionsConfig<\n\tDataModel extends GenericDataModel,\n\tRole extends string,\n> = {\n\t/** The consumer's generated function builders (from ./_generated/server). */\n\tquery: QueryBuilder<DataModel, 'public'>\n\tmutation: MutationBuilder<DataModel, 'public'>\n\taction: ActionBuilder<DataModel, 'public'>\n\tinternalMutation: MutationBuilder<DataModel, 'internal'>\n\tinternalAction: ActionBuilder<DataModel, 'internal'>\n\t/** Resolves the synchronized principal, e.g. (ctx) => authKit.getAuthUser(ctx). */\n\tgetAuthUser: (\n\t\tctx: AnyAuthContext<DataModel>,\n\t) => Promise<{ id: string } | null>\n\t/**\n\t * Maps an identity role slug onto the app's role vocabulary. Returning\n\t * null rejects the caller with FORBIDDEN — unknown roles never pass.\n\t */\n\tmapRole: (roleSlug?: string) => Role | null\n\t/** Roles allowed through the admin* constructors. */\n\tadminRoles: readonly NoInfer<Role>[]\n\t/**\n\t * Live membership check for actions (queries/mutations trust the JWT).\n\t * Authentication fails closed with FORBIDDEN when verification errors.\n\t * Omit to let actions trust the JWT like queries do.\n\t */\n\tverifyMembership?: (input: {\n\t\tuserId: string\n\t\torganizationId: string\n\t}) => Promise<{ organizationId: string; roleSlug: string } | null>\n\t/** Host error policy; defaults to throwing KitError. */\n\terrors?: ErrorFactory\n\t/**\n\t * Application trigger registry. When set, every authMutation,\n\t * roleMutation, and systemMutation write runs through triggers.wrapDB —\n\t * evaluations registered on tables fire structurally.\n\t */\n\ttriggers?: Triggers<DataModel>\n\t/** Lower-level alternative to `triggers`: a custom ctx wrapper. */\n\twrapDB?: (\n\t\tctx: GenericMutationCtx<DataModel>,\n\t) => GenericMutationCtx<DataModel>\n\t/** Upper bound for include() reads. */\n\tmaxIncludedQueryRows?: number\n}\n\n/** WorkOS-style default: `member` writes; reader/writer/admin pass through. */\nexport function defaultRoleMap(roleSlug?: string): DefaultRole | null {\n\tconst normalized = roleSlug?.trim().toLowerCase()\n\tif (normalized === 'member') return 'writer'\n\treturn normalized === 'reader' ||\n\t\tnormalized === 'writer' ||\n\t\tnormalized === 'admin'\n\t\t? normalized\n\t\t: null\n}\n\nexport type IncludedQuery<Table extends GenericTableInfo> = {\n\twhen<Value>(\n\t\tvalue: Value | null | undefined,\n\t\tselect: (query: QueryInitializer<Table>, value: Value) => Query<Table>,\n\t): IncludedQuery<Table>\n\tmatching<IndexName extends IndexNames<Table>>(\n\t\tindexName: IndexName,\n\t\tindexRange?: (\n\t\t\tquery: IndexRangeBuilder<\n\t\t\t\tDocumentByInfo<Table>,\n\t\t\t\tNamedIndex<Table, IndexName>\n\t\t\t>,\n\t\t) => IndexRange,\n\t\tshouldMatch?: boolean,\n\t): IncludedQuery<Table>\n\totherwise(\n\t\tselect: (query: QueryInitializer<Table>) => Query<Table>,\n\t): Query<Table>\n\tresolve(): Query<Table>\n\texecute<Result = Awaited<ReturnType<Query<Table>['take']>>>(\n\t\tlimit: number,\n\t\ttransform?: (\n\t\t\tdata: Awaited<ReturnType<Query<Table>['take']>>,\n\t\t) => Result | Promise<Result>,\n\t): Promise<Result>\n}\n\nexport type Include = <Table extends GenericTableInfo>(\n\tquery: QueryInitializer<Table>,\n) => IncludedQuery<Table>\n\n/** Builds an include() that selects the first matching indexed query and bounds the read. */\nexport function createInclude(options?: {\n\terrors?: ErrorFactory\n\tmaxRows?: number\n}): Include {\n\tconst errors = options?.errors ?? defaultErrors\n\tconst maxRows = options?.maxRows ?? 100\n\n\tfunction attach<Table extends GenericTableInfo>(\n\t\tquery: QueryInitializer<Table>,\n\t\tselected?: Query<Table>,\n\t): IncludedQuery<Table> {\n\t\treturn {\n\t\t\twhen: (value, select) =>\n\t\t\t\tselected || value === undefined || value === null\n\t\t\t\t\t? attach(query, selected)\n\t\t\t\t\t: attach(query, select(query, value)),\n\t\t\tmatching: (indexName, indexRange, shouldMatch = true) =>\n\t\t\t\tselected || !shouldMatch\n\t\t\t\t\t? attach(query, selected)\n\t\t\t\t\t: attach(query, query.withIndex(indexName, indexRange)),\n\t\t\totherwise: (select) => selected ?? select(query),\n\t\t\tresolve: () => selected ?? query.fullTableScan(),\n\t\t\texecute: async (limit, transform) => {\n\t\t\t\tif (!Number.isSafeInteger(limit) || limit < 1 || limit > maxRows) {\n\t\t\t\t\treturn errors.throw({\n\t\t\t\t\t\tcode: 'INVALID_REQUEST_BOUNDARY',\n\t\t\t\t\t\tmessage: `A bounded query limit must be from 1 to ${maxRows}`,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tconst data = await (selected ?? query.fullTableScan()).take(limit)\n\t\t\t\treturn transform ? transform(data) : (data as never)\n\t\t\t},\n\t\t}\n\t}\n\n\treturn (query) => attach(query)\n}\n\n/**\n * Builds the auth-aware function constructors from the host's injected\n * policy: identity resolution, the app's own role vocabulary, live\n * membership verification for actions, trigger wrapping for mutations,\n * and errors. Role-based access is first-class — every constructor family\n * has a role-gated variant (roleQuery/roleMutation/roleAction), and the\n * admin* constructors are just that gate applied to config.adminRoles.\n */\nexport function createAuthFunctions<\n\tDataModel extends GenericDataModel,\n\tconst Role extends string = DefaultRole,\n>(config: AuthFunctionsConfig<DataModel, Role>) {\n\tconst errors = config.errors ?? defaultErrors\n\tconst wrapDB = config.triggers\n\t\t? (ctx: GenericMutationCtx<DataModel>) =>\n\t\t\t\t(config.triggers as Triggers<DataModel>).wrapDB(ctx)\n\t\t: (config.wrapDB ?? ((ctx) => ctx))\n\tconst include = createInclude({\n\t\terrors,\n\t\tmaxRows: config.maxIncludedQueryRows,\n\t})\n\n\tasync function authenticatedUser(\n\t\tctx: AnyAuthContext<DataModel>,\n\t): Promise<AuthBundle<Role>> {\n\t\tconst identity = await ctx.auth.getUserIdentity()\n\t\tconst user = identity ? await config.getAuthUser(ctx) : null\n\t\tif (!identity || !user) return errors.throw({ code: 'UNAUTHENTICATED' })\n\t\tconst organization = identity.organization\n\t\tconst organizationId =\n\t\t\torganization &&\n\t\t\ttypeof organization === 'object' &&\n\t\t\t'organizationId' in organization &&\n\t\t\ttypeof organization.organizationId === 'string'\n\t\t\t\t? organization.organizationId\n\t\t\t\t: typeof identity.org_id === 'string'\n\t\t\t\t\t? identity.org_id\n\t\t\t\t\t: null\n\t\tif (!organizationId) return errors.throw({ code: 'UNAUTHENTICATED' })\n\t\tconst organizationRole =\n\t\t\torganization &&\n\t\t\ttypeof organization === 'object' &&\n\t\t\t'role' in organization &&\n\t\t\ttypeof organization.role === 'string'\n\t\t\t\t? organization.role\n\t\t\t\t: typeof identity.role === 'string'\n\t\t\t\t\t? identity.role\n\t\t\t\t\t: undefined\n\t\tconst role = config.mapRole(organizationRole)\n\t\tif (!role) return errors.throw({ code: 'FORBIDDEN' })\n\t\treturn Object.freeze({\n\t\t\tidentity,\n\t\t\tuser: Object.freeze({ id: user.id }),\n\t\t\torg: Object.freeze({ organizationId, role }),\n\t\t\trole,\n\t\t\tactor: Object.freeze({ userId: user.id, organizationId, role }),\n\t\t})\n\t}\n\n\tasync function verifiedMembership(userId: string, organizationId: string) {\n\t\tif (!config.verifyMembership) return null\n\t\ttry {\n\t\t\tconst membership = await config.verifyMembership({\n\t\t\t\tuserId,\n\t\t\t\torganizationId,\n\t\t\t})\n\t\t\tif (membership && membership.organizationId === organizationId) {\n\t\t\t\treturn membership\n\t\t\t}\n\t\t} catch {\n\t\t\t// Authentication fails closed when membership cannot be verified.\n\t\t}\n\t\treturn errors.throw({ code: 'FORBIDDEN' })\n\t}\n\n\tfunction requireRole(role: Role, allowed: readonly Role[]) {\n\t\tif (!allowed.includes(role)) return errors.throw({ code: 'FORBIDDEN' })\n\t}\n\n\tasync function queryInput(ctx: GenericQueryCtx<DataModel>) {\n\t\treturn {\n\t\t\tctx: { ...ctx, include, ...(await authenticatedUser(ctx)) },\n\t\t\targs: {},\n\t\t}\n\t}\n\n\tasync function mutationInput(ctx: GenericMutationCtx<DataModel>) {\n\t\treturn {\n\t\t\tctx: { ...wrapDB(ctx), include, ...(await authenticatedUser(ctx)) },\n\t\t\targs: {},\n\t\t}\n\t}\n\n\tasync function actionInput(ctx: GenericActionCtx<DataModel>) {\n\t\tconst authenticated = await authenticatedUser(ctx)\n\t\tconst membership = await verifiedMembership(\n\t\t\tauthenticated.user.id,\n\t\t\tauthenticated.org.organizationId,\n\t\t)\n\t\tif (!membership) return { ctx: { ...ctx, ...authenticated }, args: {} }\n\t\tconst role = config.mapRole(membership.roleSlug)\n\t\tif (!role) return errors.throw({ code: 'FORBIDDEN' })\n\t\treturn {\n\t\t\tctx: {\n\t\t\t\t...ctx,\n\t\t\t\t...authenticated,\n\t\t\t\torg: Object.freeze({\n\t\t\t\t\torganizationId: membership.organizationId,\n\t\t\t\t\trole,\n\t\t\t\t}),\n\t\t\t\trole,\n\t\t\t\tactor: Object.freeze({\n\t\t\t\t\tuserId: authenticated.user.id,\n\t\t\t\t\torganizationId: membership.organizationId,\n\t\t\t\t\trole,\n\t\t\t\t}),\n\t\t\t},\n\t\t\targs: {},\n\t\t}\n\t}\n\n\t/** Query constructor allowing only the given roles. */\n\tfunction roleQuery(...allowed: readonly Role[]) {\n\t\treturn zCustomQuery(config.query, {\n\t\t\targs: {},\n\t\t\tinput: async (ctx) => {\n\t\t\t\tconst authenticated = await queryInput(ctx)\n\t\t\t\trequireRole(authenticated.ctx.actor.role, allowed)\n\t\t\t\treturn authenticated\n\t\t\t},\n\t\t})\n\t}\n\n\t/** Mutation constructor allowing only the given roles. */\n\tfunction roleMutation(...allowed: readonly Role[]) {\n\t\treturn zCustomMutation(config.mutation, {\n\t\t\targs: {},\n\t\t\tinput: async (ctx) => {\n\t\t\t\tconst authenticated = await mutationInput(ctx)\n\t\t\t\trequireRole(authenticated.ctx.actor.role, allowed)\n\t\t\t\treturn authenticated\n\t\t\t},\n\t\t})\n\t}\n\n\t/** Action constructor allowing only the given roles (post live verification). */\n\tfunction roleAction(...allowed: readonly Role[]) {\n\t\treturn zCustomAction(config.action, {\n\t\t\targs: {},\n\t\t\tinput: async (ctx) => {\n\t\t\t\tconst authenticated = await actionInput(ctx)\n\t\t\t\trequireRole(authenticated.ctx.actor.role, allowed)\n\t\t\t\treturn authenticated\n\t\t\t},\n\t\t})\n\t}\n\n\treturn {\n\t\tinclude,\n\t\tauthenticatedUser,\n\t\troleQuery,\n\t\troleMutation,\n\t\troleAction,\n\t\tauthQuery: zCustomQuery(config.query, { args: {}, input: queryInput }),\n\t\tauthMutation: zCustomMutation(config.mutation, {\n\t\t\targs: {},\n\t\t\tinput: mutationInput,\n\t\t}),\n\t\tauthAction: zCustomAction(config.action, {\n\t\t\targs: {},\n\t\t\tinput: actionInput,\n\t\t}),\n\t\tadminQuery: roleQuery(...config.adminRoles),\n\t\tadminMutation: roleMutation(...config.adminRoles),\n\t\tadminAction: roleAction(...config.adminRoles),\n\t\tsystemMutation: zCustomMutation(config.internalMutation, {\n\t\t\targs: {},\n\t\t\tinput: async (ctx) => ({ ctx: { ...wrapDB(ctx), include }, args: {} }),\n\t\t}),\n\t\tsystemAction: zCustomAction(config.internalAction, {\n\t\t\targs: {},\n\t\t\tinput: async (ctx) => ({ ctx, args: {} }),\n\t\t}),\n\t}\n}\n"],"mappings":"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"}