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
@@ -0,0 +1,90 @@
1
+ import {
2
+ Triggers,
3
+ type Change,
4
+ type Trigger,
5
+ } from 'convex-helpers/server/triggers'
6
+ import type {
7
+ GenericDataModel,
8
+ TableNamesInDataModel,
9
+ } from 'convex/server'
10
+
11
+ export { Triggers }
12
+ export type { Change, Trigger }
13
+
14
+ /**
15
+ * One application-wide mutation interceptor registry. Pass it to
16
+ * createAuthFunctions({ triggers }) so every authMutation/roleMutation/
17
+ * systemMutation runs writes through it — trigger enforcement becomes
18
+ * structural, not a convention.
19
+ */
20
+ export function createTriggers<
21
+ DataModel extends GenericDataModel,
22
+ >(): Triggers<DataModel> {
23
+ return new Triggers<DataModel>()
24
+ }
25
+
26
+ /**
27
+ * Marks tables as append-only evidence tables: inserts pass, every update or
28
+ * delete throws. The ontology pattern for votes/history/audit-style tables.
29
+ */
30
+ export function appendOnly<DataModel extends GenericDataModel>(
31
+ triggers: Triggers<DataModel>,
32
+ ...tables: readonly TableNamesInDataModel<DataModel>[]
33
+ ) {
34
+ for (const table of tables) {
35
+ triggers.register(table, async (_ctx, change) => {
36
+ if (change.operation !== 'insert') {
37
+ throw new Error(`${table} is append-only`)
38
+ }
39
+ })
40
+ }
41
+ }
42
+
43
+ /** Forbids hard deletes on the given tables (soft-delete discipline). */
44
+ export function noDelete<DataModel extends GenericDataModel>(
45
+ triggers: Triggers<DataModel>,
46
+ ...tables: readonly TableNamesInDataModel<DataModel>[]
47
+ ) {
48
+ for (const table of tables) {
49
+ triggers.register(table, async (_ctx, change) => {
50
+ if (change.operation === 'delete') {
51
+ throw new Error(`${table} rows cannot be deleted`)
52
+ }
53
+ })
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Maintains zod-table's opinionated lifecycle timestamps: createdAt and
59
+ * updatedAt on insert, updatedAt on every update. archivedAt stays under
60
+ * application control. Register once per table; writes made through the
61
+ * auth constructors keep these fields correct automatically.
62
+ */
63
+ export function timestamps<DataModel extends GenericDataModel>(
64
+ triggers: Triggers<DataModel>,
65
+ ...tables: readonly TableNamesInDataModel<DataModel>[]
66
+ ) {
67
+ for (const table of tables) {
68
+ triggers.register(table, async (ctx, change) => {
69
+ if (change.operation === 'delete') return
70
+ const now = Date.now()
71
+ if (change.operation === 'insert') {
72
+ const document = change.newDoc as { createdAt?: number }
73
+ if (document.createdAt === undefined) {
74
+ await ctx.innerDb.patch(change.id, {
75
+ createdAt: now,
76
+ updatedAt: now,
77
+ } as never)
78
+ }
79
+ return
80
+ }
81
+ const previous = change.oldDoc as { updatedAt?: number }
82
+ const current = change.newDoc as { updatedAt?: number }
83
+ // A write that already moved updatedAt (including our own patch)
84
+ // is left alone — this is the recursion guard.
85
+ if (current.updatedAt === previous.updatedAt) {
86
+ await ctx.innerDb.patch(change.id, { updatedAt: now } as never)
87
+ }
88
+ })
89
+ }
90
+ }
@@ -0,0 +1,197 @@
1
+ import {
2
+ type ConvexValidatorFromZod,
3
+ zid,
4
+ zodToConvex,
5
+ } from 'convex-helpers/server/zod4'
6
+ import { defineTable, type TableDefinition } from 'convex/server'
7
+ import type { GenericId } from 'convex/values'
8
+ import { z } from 'zod'
9
+
10
+ type Shape = Record<string, z.ZodType>
11
+ type ShapeKey<Fields extends Shape> = Extract<keyof Fields, string>
12
+
13
+ /**
14
+ * Opinionated lifecycle timestamps — every table has them, server-owned,
15
+ * always excluded from insert/command boundaries. Maintained by the
16
+ * `timestamps` trigger helper (see cvx-kit/triggers).
17
+ */
18
+ const timestampShape = {
19
+ createdAt: z.number().optional(),
20
+ updatedAt: z.number().optional(),
21
+ archivedAt: z.number().optional(),
22
+ } satisfies Shape
23
+
24
+ type TimestampShape = typeof timestampShape
25
+
26
+ export const TIMESTAMP_FIELDS = [
27
+ 'createdAt',
28
+ 'updatedAt',
29
+ 'archivedAt',
30
+ ] as const
31
+
32
+ export type TableBoundaryOptions<
33
+ Fields extends Shape,
34
+ ServerFields extends readonly ShapeKey<Fields>[],
35
+ CommandFields extends readonly ShapeKey<Fields>[],
36
+ PublicFields extends readonly ShapeKey<Fields & TimestampShape>[],
37
+ > = {
38
+ serverFields?: ServerFields
39
+ commandFields?: CommandFields
40
+ publicFields?: PublicFields
41
+ }
42
+
43
+ /** Defines one table and derives its storage, document, write, and DTO boundaries. */
44
+ export function zodTable<
45
+ Table extends string,
46
+ Fields extends Shape,
47
+ const ServerFields extends readonly ShapeKey<Fields>[] = readonly [],
48
+ const CommandFields extends readonly ShapeKey<Fields>[] = readonly [],
49
+ const PublicFields extends readonly ShapeKey<
50
+ Fields & TimestampShape
51
+ >[] = readonly [],
52
+ >(
53
+ tableName: Table,
54
+ fields: (id: typeof zid) => Fields,
55
+ options: TableBoundaryOptions<
56
+ Fields,
57
+ ServerFields,
58
+ CommandFields,
59
+ PublicFields
60
+ > = {},
61
+ ) {
62
+ const shape = { ...fields(zid), ...timestampShape } as Fields &
63
+ TimestampShape
64
+ const storage = z.object(shape).strict()
65
+ const schema = storage.extend({
66
+ _id: zid(tableName),
67
+ _creationTime: z.number(),
68
+ })
69
+ const insertSchema = z
70
+ .object(
71
+ omitShape(shape, [
72
+ ...TIMESTAMP_FIELDS,
73
+ ...(options.serverFields ?? ([] as never)),
74
+ ] as never),
75
+ )
76
+ .strict() as z.ZodObject<
77
+ Omit<Fields, ServerFields[number] | (typeof TIMESTAMP_FIELDS)[number]>
78
+ >
79
+ const updateSchema = insertSchema.partial()
80
+ const commandInput = z
81
+ .object(pickShape(shape, options.commandFields ?? ([] as never)))
82
+ .strict()
83
+ const publicDto = z
84
+ .object(pickShape(shape, options.publicFields ?? ([] as never)))
85
+ .strict()
86
+ const toPublicDto = (row: z.input<typeof storage>) =>
87
+ publicDto.parse(
88
+ Object.fromEntries(
89
+ Object.keys(publicDto.shape).map((key) => [
90
+ key,
91
+ (row as Record<string, unknown>)[key],
92
+ ]),
93
+ ),
94
+ )
95
+
96
+ type InsertSchema = typeof insertSchema
97
+ type UpdateSchema = typeof updateSchema
98
+ type InsertMask = z.util.Mask<keyof InsertSchema['shape']>
99
+ type UpdateMask = z.util.Mask<keyof UpdateSchema['shape']>
100
+
101
+ function insert(): InsertSchema
102
+ function insert<OmitMask extends InsertMask>(
103
+ omit: OmitMask,
104
+ ): ZodObjectOmit<InsertSchema, OmitMask>
105
+ function insert(omit?: InsertMask) {
106
+ return omit ? insertSchema.omit(omit as never) : insertSchema
107
+ }
108
+
109
+ function update(): UpdateSchema
110
+ function update<OmitMask extends UpdateMask>(
111
+ omit: OmitMask,
112
+ ): ZodObjectOmit<UpdateSchema, OmitMask>
113
+ function update(omit?: UpdateMask) {
114
+ return omit ? updateSchema.omit(omit as never) : updateSchema
115
+ }
116
+
117
+ return {
118
+ tableName,
119
+ schema,
120
+ storage,
121
+ insertSchema,
122
+ updateSchema,
123
+ commandInput,
124
+ publicDto,
125
+ toPublicDto,
126
+ table: defineTable(zodToConvex(storage)) as TableDefinition<
127
+ ConvexValidatorFromZod<typeof storage, 'required'>
128
+ >,
129
+ insert,
130
+ update,
131
+ tools: {
132
+ insert: commandInput,
133
+ update: z
134
+ .object({ data: commandInput.partial(), id: zid(tableName) })
135
+ .strict(),
136
+ id: z.object({ id: zid(tableName) }).strict(),
137
+ },
138
+ }
139
+ }
140
+
141
+ /** Preserves discriminated-union storage while retaining the same table owner. */
142
+ export function zodVariantTable<Table extends string, Schema extends z.ZodType>(
143
+ tableName: Table,
144
+ storage: Schema,
145
+ ) {
146
+ return {
147
+ tableName,
148
+ storage,
149
+ insertSchema: storage,
150
+ table: defineTable(zodToConvex(storage)),
151
+ }
152
+ }
153
+
154
+ /**
155
+ * A zid that presents as a plain string in generated JSON schemas so
156
+ * LLM-tool inputs stay primitive, while keeping the Id type at compile time.
157
+ */
158
+ export function jsonSafeZid<Table extends string>(tableName: Table) {
159
+ return z
160
+ .string()
161
+ .describe(
162
+ `Convex document id for table "${tableName}"`,
163
+ ) as unknown as z.ZodType<GenericId<Table>, string>
164
+ }
165
+
166
+ type ZodObjectOmit<
167
+ Schema extends z.ZodObject<any, any>,
168
+ OmitMask extends z.util.Mask<keyof Schema['shape']>,
169
+ > =
170
+ Schema extends z.ZodObject<infer ObjectShape, infer Config>
171
+ ? z.ZodObject<
172
+ z.util.Flatten<
173
+ Omit<ObjectShape, Extract<keyof ObjectShape, keyof OmitMask>>
174
+ >,
175
+ Config
176
+ >
177
+ : never
178
+
179
+ function pickShape<
180
+ Fields extends Shape,
181
+ const Keys extends readonly ShapeKey<Fields>[],
182
+ >(fields: Fields, keys: Keys) {
183
+ return Object.fromEntries(keys.map((key) => [key, fields[key]])) as Pick<
184
+ Fields,
185
+ Keys[number]
186
+ >
187
+ }
188
+
189
+ function omitShape<
190
+ Fields extends Shape,
191
+ const Keys extends readonly ShapeKey<Fields>[],
192
+ >(fields: Fields, keys: Keys) {
193
+ const omitted = new Set<string>(keys)
194
+ return Object.fromEntries(
195
+ Object.entries(fields).filter(([key]) => !omitted.has(key)),
196
+ ) as Omit<Fields, Keys[number]>
197
+ }