convex 1.42.3 → 1.43.0-alpha.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 (243) hide show
  1. package/bin/main-dev +6 -1
  2. package/dist/browser.bundle.js +49 -10
  3. package/dist/browser.bundle.js.map +2 -2
  4. package/dist/cjs/browser/simple_client-node.js +35 -35
  5. package/dist/cjs/browser/simple_client-node.js.map +2 -2
  6. package/dist/cjs/browser/sync/paginated_query_client.js +20 -9
  7. package/dist/cjs/browser/sync/paginated_query_client.js.map +2 -2
  8. package/dist/cjs/cli/codegen_templates/validator_helpers.js +2 -0
  9. package/dist/cjs/cli/codegen_templates/validator_helpers.js.map +2 -2
  10. package/dist/cjs/cli/deployment.js +2 -1
  11. package/dist/cjs/cli/deployment.js.map +2 -2
  12. package/dist/cjs/cli/deploymentCreate.js.map +2 -2
  13. package/dist/cjs/cli/generatedApi.js.map +1 -1
  14. package/dist/cjs/cli/lib/deployApi/validator.js +1 -0
  15. package/dist/cjs/cli/lib/deployApi/validator.js.map +2 -2
  16. package/dist/cjs/cli/lib/deploymentSelection.js.map +2 -2
  17. package/dist/cjs/cli/lib/generateDocs.js +1 -1
  18. package/dist/cjs/cli/lib/generateDocs.js.map +2 -2
  19. package/dist/cjs/cli/lib/generatedFunctionLogsApi.js.map +1 -1
  20. package/dist/cjs/cli/lib/localDeployment/projectMismatch.js.map +2 -2
  21. package/dist/cjs/cli/lib/login.js +23 -23
  22. package/dist/cjs/cli/lib/login.js.map +2 -2
  23. package/dist/cjs/cli/lib/typecheck.js +28 -25
  24. package/dist/cjs/cli/lib/typecheck.js.map +2 -2
  25. package/dist/cjs/cli/lib/usageLimits.js +164 -0
  26. package/dist/cjs/cli/lib/usageLimits.js.map +7 -0
  27. package/dist/cjs/cli/usageLimits.js +326 -0
  28. package/dist/cjs/cli/usageLimits.js.map +7 -0
  29. package/dist/cjs/index.js +1 -1
  30. package/dist/cjs/index.js.map +1 -1
  31. package/dist/cjs/server/components/index.js.map +2 -2
  32. package/dist/cjs/server/cron.js +33 -29
  33. package/dist/cjs/server/cron.js.map +2 -2
  34. package/dist/cjs/server/database.js.map +1 -1
  35. package/dist/cjs/server/impl/database_impl.js +3 -0
  36. package/dist/cjs/server/impl/database_impl.js.map +2 -2
  37. package/dist/cjs/server/log.js +2 -1
  38. package/dist/cjs/server/log.js.map +2 -2
  39. package/dist/cjs/server/logVars.js +12 -1
  40. package/dist/cjs/server/logVars.js.map +2 -2
  41. package/dist/cjs/values/compare.js +5 -1
  42. package/dist/cjs/values/compare.js.map +2 -2
  43. package/dist/cjs/values/index.js +1 -0
  44. package/dist/cjs/values/index.js.map +2 -2
  45. package/dist/cjs/values/size.js +4 -0
  46. package/dist/cjs/values/size.js.map +2 -2
  47. package/dist/cjs/values/validator.js +11 -0
  48. package/dist/cjs/values/validator.js.map +2 -2
  49. package/dist/cjs/values/validators.js +20 -0
  50. package/dist/cjs/values/validators.js.map +2 -2
  51. package/dist/cjs/values/value.js +31 -0
  52. package/dist/cjs/values/value.js.map +2 -2
  53. package/dist/cjs-types/cli/codegen_templates/validator_helpers.d.ts.map +1 -1
  54. package/dist/cjs-types/cli/codegen_templates/validator_helpers.test.d.ts +2 -0
  55. package/dist/cjs-types/cli/codegen_templates/validator_helpers.test.d.ts.map +1 -0
  56. package/dist/cjs-types/cli/deployment.d.ts.map +1 -1
  57. package/dist/cjs-types/cli/deploymentCreate.d.ts +2 -2
  58. package/dist/cjs-types/cli/generatedApi.d.ts +43 -43
  59. package/dist/cjs-types/cli/generatedApi.d.ts.map +1 -1
  60. package/dist/cjs-types/cli/lib/deployApi/validator.d.ts +6 -0
  61. package/dist/cjs-types/cli/lib/deployApi/validator.d.ts.map +1 -1
  62. package/dist/cjs-types/cli/lib/deploymentSelection.d.ts +2 -2
  63. package/dist/cjs-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  64. package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts +45 -45
  65. package/dist/cjs-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
  66. package/dist/cjs-types/cli/lib/localDeployment/projectMismatch.d.ts +2 -2
  67. package/dist/cjs-types/cli/lib/localDeployment/projectMismatch.d.ts.map +1 -1
  68. package/dist/cjs-types/cli/lib/login.d.ts.map +1 -1
  69. package/dist/cjs-types/cli/lib/mcp/tools/insights.d.ts +24 -24
  70. package/dist/cjs-types/cli/lib/typecheck.d.ts +2 -0
  71. package/dist/cjs-types/cli/lib/typecheck.d.ts.map +1 -1
  72. package/dist/cjs-types/cli/lib/typecheck.test.d.ts +2 -0
  73. package/dist/cjs-types/cli/lib/typecheck.test.d.ts.map +1 -0
  74. package/dist/cjs-types/cli/lib/usageLimits.d.ts +46 -0
  75. package/dist/cjs-types/cli/lib/usageLimits.d.ts.map +1 -0
  76. package/dist/cjs-types/cli/usageLimits.d.ts +22 -0
  77. package/dist/cjs-types/cli/usageLimits.d.ts.map +1 -0
  78. package/dist/cjs-types/index.d.ts +1 -1
  79. package/dist/cjs-types/index.d.ts.map +1 -1
  80. package/dist/cjs-types/server/components/index.d.ts +26 -8
  81. package/dist/cjs-types/server/components/index.d.ts.map +1 -1
  82. package/dist/cjs-types/server/cron.d.ts +20 -11
  83. package/dist/cjs-types/server/cron.d.ts.map +1 -1
  84. package/dist/cjs-types/server/cron.test.d.ts +2 -0
  85. package/dist/cjs-types/server/cron.test.d.ts.map +1 -0
  86. package/dist/cjs-types/server/database.d.ts +14 -0
  87. package/dist/cjs-types/server/database.d.ts.map +1 -1
  88. package/dist/cjs-types/server/impl/database_impl.d.ts.map +1 -1
  89. package/dist/cjs-types/server/log.d.ts.map +1 -1
  90. package/dist/cjs-types/server/logVars.d.ts +14 -1
  91. package/dist/cjs-types/server/logVars.d.ts.map +1 -1
  92. package/dist/cjs-types/values/compare.d.ts.map +1 -1
  93. package/dist/cjs-types/values/index.d.ts +2 -2
  94. package/dist/cjs-types/values/index.d.ts.map +1 -1
  95. package/dist/cjs-types/values/size.d.ts +1 -1
  96. package/dist/cjs-types/values/size.d.ts.map +1 -1
  97. package/dist/cjs-types/values/validator.d.ts +10 -1
  98. package/dist/cjs-types/values/validator.d.ts.map +1 -1
  99. package/dist/cjs-types/values/validators.d.ts +14 -3
  100. package/dist/cjs-types/values/validators.d.ts.map +1 -1
  101. package/dist/cjs-types/values/value.d.ts +18 -1
  102. package/dist/cjs-types/values/value.d.ts.map +1 -1
  103. package/dist/cli.bundle.cjs +2534 -2009
  104. package/dist/cli.bundle.cjs.map +4 -4
  105. package/dist/esm/browser/simple_client-node.js +35 -35
  106. package/dist/esm/browser/simple_client-node.js.map +1 -1
  107. package/dist/esm/browser/sync/paginated_query_client.js +20 -9
  108. package/dist/esm/browser/sync/paginated_query_client.js.map +2 -2
  109. package/dist/esm/cli/codegen_templates/validator_helpers.js +2 -0
  110. package/dist/esm/cli/codegen_templates/validator_helpers.js.map +2 -2
  111. package/dist/esm/cli/deployment.js +2 -1
  112. package/dist/esm/cli/deployment.js.map +2 -2
  113. package/dist/esm/cli/deploymentCreate.js.map +2 -2
  114. package/dist/esm/cli/lib/deployApi/validator.js +1 -0
  115. package/dist/esm/cli/lib/deployApi/validator.js.map +2 -2
  116. package/dist/esm/cli/lib/deploymentSelection.js.map +2 -2
  117. package/dist/esm/cli/lib/generateDocs.js +1 -1
  118. package/dist/esm/cli/lib/generateDocs.js.map +2 -2
  119. package/dist/esm/cli/lib/localDeployment/projectMismatch.js.map +2 -2
  120. package/dist/esm/cli/lib/login.js +23 -23
  121. package/dist/esm/cli/lib/login.js.map +2 -2
  122. package/dist/esm/cli/lib/typecheck.js +27 -25
  123. package/dist/esm/cli/lib/typecheck.js.map +2 -2
  124. package/dist/esm/cli/lib/usageLimits.js +129 -0
  125. package/dist/esm/cli/lib/usageLimits.js.map +7 -0
  126. package/dist/esm/cli/usageLimits.js +316 -0
  127. package/dist/esm/cli/usageLimits.js.map +7 -0
  128. package/dist/esm/index.js +1 -1
  129. package/dist/esm/index.js.map +1 -1
  130. package/dist/esm/server/components/index.js.map +2 -2
  131. package/dist/esm/server/cron.js +33 -29
  132. package/dist/esm/server/cron.js.map +2 -2
  133. package/dist/esm/server/impl/database_impl.js +4 -1
  134. package/dist/esm/server/impl/database_impl.js.map +2 -2
  135. package/dist/esm/server/log.js +3 -2
  136. package/dist/esm/server/log.js.map +2 -2
  137. package/dist/esm/server/logVars.js +11 -1
  138. package/dist/esm/server/logVars.js.map +2 -2
  139. package/dist/esm/values/compare.js +5 -1
  140. package/dist/esm/values/compare.js.map +2 -2
  141. package/dist/esm/values/index.js +1 -1
  142. package/dist/esm/values/index.js.map +2 -2
  143. package/dist/esm/values/size.js +4 -0
  144. package/dist/esm/values/size.js.map +2 -2
  145. package/dist/esm/values/validator.js +12 -0
  146. package/dist/esm/values/validator.js.map +2 -2
  147. package/dist/esm/values/validators.js +19 -0
  148. package/dist/esm/values/validators.js.map +2 -2
  149. package/dist/esm/values/value.js +29 -1
  150. package/dist/esm/values/value.js.map +2 -2
  151. package/dist/esm-types/cli/codegen_templates/validator_helpers.d.ts.map +1 -1
  152. package/dist/esm-types/cli/codegen_templates/validator_helpers.test.d.ts +2 -0
  153. package/dist/esm-types/cli/codegen_templates/validator_helpers.test.d.ts.map +1 -0
  154. package/dist/esm-types/cli/deployment.d.ts.map +1 -1
  155. package/dist/esm-types/cli/deploymentCreate.d.ts +2 -2
  156. package/dist/esm-types/cli/generatedApi.d.ts +43 -43
  157. package/dist/esm-types/cli/generatedApi.d.ts.map +1 -1
  158. package/dist/esm-types/cli/lib/deployApi/validator.d.ts +6 -0
  159. package/dist/esm-types/cli/lib/deployApi/validator.d.ts.map +1 -1
  160. package/dist/esm-types/cli/lib/deploymentSelection.d.ts +2 -2
  161. package/dist/esm-types/cli/lib/deploymentSelection.d.ts.map +1 -1
  162. package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts +45 -45
  163. package/dist/esm-types/cli/lib/generatedFunctionLogsApi.d.ts.map +1 -1
  164. package/dist/esm-types/cli/lib/localDeployment/projectMismatch.d.ts +2 -2
  165. package/dist/esm-types/cli/lib/localDeployment/projectMismatch.d.ts.map +1 -1
  166. package/dist/esm-types/cli/lib/login.d.ts.map +1 -1
  167. package/dist/esm-types/cli/lib/mcp/tools/insights.d.ts +24 -24
  168. package/dist/esm-types/cli/lib/typecheck.d.ts +2 -0
  169. package/dist/esm-types/cli/lib/typecheck.d.ts.map +1 -1
  170. package/dist/esm-types/cli/lib/typecheck.test.d.ts +2 -0
  171. package/dist/esm-types/cli/lib/typecheck.test.d.ts.map +1 -0
  172. package/dist/esm-types/cli/lib/usageLimits.d.ts +46 -0
  173. package/dist/esm-types/cli/lib/usageLimits.d.ts.map +1 -0
  174. package/dist/esm-types/cli/usageLimits.d.ts +22 -0
  175. package/dist/esm-types/cli/usageLimits.d.ts.map +1 -0
  176. package/dist/esm-types/index.d.ts +1 -1
  177. package/dist/esm-types/index.d.ts.map +1 -1
  178. package/dist/esm-types/server/components/index.d.ts +26 -8
  179. package/dist/esm-types/server/components/index.d.ts.map +1 -1
  180. package/dist/esm-types/server/cron.d.ts +20 -11
  181. package/dist/esm-types/server/cron.d.ts.map +1 -1
  182. package/dist/esm-types/server/cron.test.d.ts +2 -0
  183. package/dist/esm-types/server/cron.test.d.ts.map +1 -0
  184. package/dist/esm-types/server/database.d.ts +14 -0
  185. package/dist/esm-types/server/database.d.ts.map +1 -1
  186. package/dist/esm-types/server/impl/database_impl.d.ts.map +1 -1
  187. package/dist/esm-types/server/log.d.ts.map +1 -1
  188. package/dist/esm-types/server/logVars.d.ts +14 -1
  189. package/dist/esm-types/server/logVars.d.ts.map +1 -1
  190. package/dist/esm-types/values/compare.d.ts.map +1 -1
  191. package/dist/esm-types/values/index.d.ts +2 -2
  192. package/dist/esm-types/values/index.d.ts.map +1 -1
  193. package/dist/esm-types/values/size.d.ts +1 -1
  194. package/dist/esm-types/values/size.d.ts.map +1 -1
  195. package/dist/esm-types/values/validator.d.ts +10 -1
  196. package/dist/esm-types/values/validator.d.ts.map +1 -1
  197. package/dist/esm-types/values/validators.d.ts +14 -3
  198. package/dist/esm-types/values/validators.d.ts.map +1 -1
  199. package/dist/esm-types/values/value.d.ts +18 -1
  200. package/dist/esm-types/values/value.d.ts.map +1 -1
  201. package/dist/react.bundle.js +53 -11
  202. package/dist/react.bundle.js.map +2 -2
  203. package/package.json +9 -10
  204. package/schemas/convex.schema.json +2 -1
  205. package/src/browser/sync/paginated_query_client.test.ts +209 -1
  206. package/src/browser/sync/paginated_query_client.ts +26 -9
  207. package/src/cli/codegen_templates/validator_helpers.test.ts +31 -0
  208. package/src/cli/codegen_templates/validator_helpers.ts +6 -0
  209. package/src/cli/deployment.ts +4 -1
  210. package/src/cli/deploymentCreate.test.ts +2 -2
  211. package/src/cli/deploymentCreate.ts +2 -2
  212. package/src/cli/generatedApi.ts +43 -43
  213. package/src/cli/lib/deployApi/validator.ts +1 -0
  214. package/src/cli/lib/deploymentSelection.ts +2 -2
  215. package/src/cli/lib/generateDocs.test.ts +1 -1
  216. package/src/cli/lib/generateDocs.ts +3 -3
  217. package/src/cli/lib/generatedFunctionLogsApi.ts +45 -45
  218. package/src/cli/lib/localDeployment/projectMismatch.ts +2 -2
  219. package/src/cli/lib/login.ts +33 -30
  220. package/src/cli/lib/typecheck.test.ts +71 -0
  221. package/src/cli/lib/typecheck.ts +33 -27
  222. package/src/cli/lib/usageLimits.ts +242 -0
  223. package/src/cli/usageLimits.ts +420 -0
  224. package/src/index.ts +1 -1
  225. package/src/server/audit_logging.test.ts +2 -0
  226. package/src/server/components/index.test.ts +33 -1
  227. package/src/server/components/index.ts +41 -14
  228. package/src/server/cron.test.ts +58 -0
  229. package/src/server/cron.ts +82 -21
  230. package/src/server/database.ts +15 -0
  231. package/src/server/impl/database_impl.ts +4 -1
  232. package/src/server/log.ts +4 -1
  233. package/src/server/logVars.ts +17 -1
  234. package/src/server/schema.test.ts +6 -1
  235. package/src/values/compare.ts +5 -2
  236. package/src/values/index.ts +2 -1
  237. package/src/values/size.test.ts +5 -0
  238. package/src/values/size.ts +5 -1
  239. package/src/values/validator.test.ts +16 -1
  240. package/src/values/validator.ts +13 -0
  241. package/src/values/validators.ts +29 -1
  242. package/src/values/value.test.ts +35 -0
  243. package/src/values/value.ts +56 -1
@@ -1,6 +1,7 @@
1
1
  import { getFunctionName, OptionalRestArgs } from "../server/api.js";
2
2
  import { parseArgs } from "../common/index.js";
3
3
  import { convexToJson, JSONValue, Value } from "../values/index.js";
4
+ import { getFunctionAddress } from "./components/paths.js";
4
5
  import { SchedulableFunctionReference } from "./scheduler.js";
5
6
 
6
7
  type CronSchedule = {
@@ -15,13 +16,13 @@ export type IntervalSchedule =
15
16
  /** @public */
16
17
  export type HourlySchedule = {
17
18
  type: "hourly";
18
- minuteUTC: number;
19
+ minuteUTC?: number;
19
20
  };
20
21
  /** @public */
21
22
  export type DailySchedule = {
22
23
  type: "daily";
23
24
  hourUTC: number;
24
- minuteUTC: number;
25
+ minuteUTC?: number;
25
26
  };
26
27
  const DAYS_OF_WEEK = [
27
28
  "sunday",
@@ -38,14 +39,14 @@ export type WeeklySchedule = {
38
39
  type: "weekly";
39
40
  dayOfWeek: DayOfWeek;
40
41
  hourUTC: number;
41
- minuteUTC: number;
42
+ minuteUTC?: number;
42
43
  };
43
44
  /** @public */
44
45
  export type MonthlySchedule = {
45
46
  type: "monthly";
46
47
  day: number;
47
48
  hourUTC: number;
48
- minuteUTC: number;
49
+ minuteUTC?: number;
49
50
  };
50
51
 
51
52
  // Duplicating types so docstrings are visible in signatures:
@@ -85,9 +86,11 @@ export type Interval =
85
86
  /** @public */
86
87
  export type Hourly = {
87
88
  /**
88
- * Minutes past the hour, 0-59.
89
+ * Minutes past the hour, 0-59. If omitted, Convex picks a minute for you
90
+ * and spreads runs across the hour so your job isn't tied to the busy top
91
+ * of the hour.
89
92
  */
90
- minuteUTC: number;
93
+ minuteUTC?: number;
91
94
  };
92
95
 
93
96
  /** @public */
@@ -97,9 +100,10 @@ export type Daily = {
97
100
  */
98
101
  hourUTC: number;
99
102
  /**
100
- * 0-59, minute of hour. Remember, this is UTC.
103
+ * 0-59, minute of hour. Remember, this is UTC. If omitted, Convex picks a
104
+ * minute for you and spreads runs across the hour.
101
105
  */
102
- minuteUTC: number;
106
+ minuteUTC?: number;
103
107
  };
104
108
 
105
109
  /** @public */
@@ -114,8 +118,10 @@ export type Monthly = {
114
118
  hourUTC: number;
115
119
  /**
116
120
  * 0-59, minute of hour. Remember to convert from your own time zone to UTC.
121
+ * If omitted, Convex picks a minute for you and spreads runs across the
122
+ * hour.
117
123
  */
118
- minuteUTC: number;
124
+ minuteUTC?: number;
119
125
  };
120
126
  /** @public */
121
127
  export type Weekly = {
@@ -129,8 +135,10 @@ export type Weekly = {
129
135
  hourUTC: number;
130
136
  /**
131
137
  * 0-59, minute of hour. Remember to convert from your own time zone to UTC.
138
+ * If omitted, Convex picks a minute for you and spreads runs across the
139
+ * hour.
132
140
  */
133
- minuteUTC: number;
141
+ minuteUTC?: number;
134
142
  };
135
143
 
136
144
  /** @public */
@@ -220,6 +228,26 @@ function validatedMinuteOfHour(n: number) {
220
228
  return n;
221
229
  }
222
230
 
231
+ // `minuteUTC` is optional: when omitted the backend picks a minute per job and
232
+ // spreads runs across the hour. Only validate when a minute was provided.
233
+ function validatedOptionalMinuteOfHour(n: number | undefined) {
234
+ return n === undefined ? undefined : validatedMinuteOfHour(n);
235
+ }
236
+
237
+ // `crons.hourly()` lets the schedule object be omitted entirely. Tell the two
238
+ // call shapes apart by whether the second argument resolves to a function
239
+ // reference (the way the scheduler syscalls do) rather than a schedule object.
240
+ function isSchedulableFunctionReference(
241
+ arg: Hourly | SchedulableFunctionReference,
242
+ ): arg is SchedulableFunctionReference {
243
+ try {
244
+ getFunctionAddress(arg);
245
+ return true;
246
+ } catch {
247
+ return false;
248
+ }
249
+ }
250
+
223
251
  function validatedCronString(s: string) {
224
252
  return s;
225
253
  }
@@ -323,22 +351,49 @@ export class Crons {
323
351
  * ```
324
352
  *
325
353
  * @param cronIdentifier - A unique name for this scheduled job.
326
- * @param schedule - What time (UTC) each day to run this function.
354
+ * @param schedule - What minute (UTC) of each hour to run this function. May
355
+ * be omitted to let Convex pick a minute and spread runs across the hour.
327
356
  * @param functionReference - A {@link FunctionReference} for the function
328
357
  * to schedule.
329
358
  * @param args - The arguments to the function.
330
359
  */
360
+ hourly<FuncRef extends SchedulableFunctionReference>(
361
+ cronIdentifier: string,
362
+ functionReference: FuncRef,
363
+ ...args: OptionalRestArgs<FuncRef>
364
+ ): void;
331
365
  hourly<FuncRef extends SchedulableFunctionReference>(
332
366
  cronIdentifier: string,
333
367
  schedule: Hourly,
334
368
  functionReference: FuncRef,
335
369
  ...args: OptionalRestArgs<FuncRef>
336
- ) {
337
- const minuteUTC = validatedMinuteOfHour(schedule.minuteUTC);
370
+ ): void;
371
+ hourly<FuncRef extends SchedulableFunctionReference>(
372
+ cronIdentifier: string,
373
+ scheduleOrFunctionReference: Hourly | FuncRef,
374
+ functionReferenceOrArg?: FuncRef | Record<string, Value>,
375
+ ...args: any[]
376
+ ): void {
377
+ // The schedule object may be omitted, so the second argument is either the
378
+ // function reference (no schedule) or the schedule.
379
+ if (isSchedulableFunctionReference(scheduleOrFunctionReference)) {
380
+ this.schedule(
381
+ cronIdentifier,
382
+ { type: "hourly" },
383
+ scheduleOrFunctionReference,
384
+ functionReferenceOrArg as Record<string, Value> | undefined,
385
+ );
386
+ return;
387
+ }
388
+ const minuteUTC = validatedOptionalMinuteOfHour(
389
+ scheduleOrFunctionReference.minuteUTC,
390
+ );
338
391
  this.schedule(
339
392
  cronIdentifier,
340
- { minuteUTC, type: "hourly" },
341
- functionReference,
393
+ minuteUTC === undefined
394
+ ? { type: "hourly" }
395
+ : { minuteUTC, type: "hourly" },
396
+ functionReferenceOrArg as FuncRef,
342
397
  ...args,
343
398
  );
344
399
  }
@@ -370,10 +425,12 @@ export class Crons {
370
425
  ...args: OptionalRestArgs<FuncRef>
371
426
  ) {
372
427
  const hourUTC = validatedHourOfDay(schedule.hourUTC);
373
- const minuteUTC = validatedMinuteOfHour(schedule.minuteUTC);
428
+ const minuteUTC = validatedOptionalMinuteOfHour(schedule.minuteUTC);
374
429
  this.schedule(
375
430
  cronIdentifier,
376
- { hourUTC, minuteUTC, type: "daily" },
431
+ minuteUTC === undefined
432
+ ? { hourUTC, type: "daily" }
433
+ : { hourUTC, minuteUTC, type: "daily" },
377
434
  functionReference,
378
435
  ...args,
379
436
  );
@@ -407,10 +464,12 @@ export class Crons {
407
464
  ) {
408
465
  const dayOfWeek = validatedDayOfWeek(schedule.dayOfWeek);
409
466
  const hourUTC = validatedHourOfDay(schedule.hourUTC);
410
- const minuteUTC = validatedMinuteOfHour(schedule.minuteUTC);
467
+ const minuteUTC = validatedOptionalMinuteOfHour(schedule.minuteUTC);
411
468
  this.schedule(
412
469
  cronIdentifier,
413
- { dayOfWeek, hourUTC, minuteUTC, type: "weekly" },
470
+ minuteUTC === undefined
471
+ ? { dayOfWeek, hourUTC, type: "weekly" }
472
+ : { dayOfWeek, hourUTC, minuteUTC, type: "weekly" },
414
473
  functionReference,
415
474
  ...args,
416
475
  );
@@ -448,10 +507,12 @@ export class Crons {
448
507
  ) {
449
508
  const day = validatedDayOfMonth(schedule.day);
450
509
  const hourUTC = validatedHourOfDay(schedule.hourUTC);
451
- const minuteUTC = validatedMinuteOfHour(schedule.minuteUTC);
510
+ const minuteUTC = validatedOptionalMinuteOfHour(schedule.minuteUTC);
452
511
  this.schedule(
453
512
  cronIdentifier,
454
- { day, hourUTC, minuteUTC, type: "monthly" },
513
+ minuteUTC === undefined
514
+ ? { day, hourUTC, type: "monthly" }
515
+ : { day, hourUTC, minuteUTC, type: "monthly" },
455
516
  functionReference,
456
517
  ...args,
457
518
  );
@@ -1,4 +1,5 @@
1
1
  import { GenericId } from "../values/index.js";
2
+ import { CommitTsPlaceholder } from "../values/value.js";
2
3
  import {
3
4
  DocumentByName,
4
5
  GenericDataModel,
@@ -370,6 +371,20 @@ export interface GenericDatabaseWriter<
370
371
  * @param id - The {@link values.GenericId} of the document to remove.
371
372
  */
372
373
  delete(id: GenericId<TableNamesInDataModel<DataModel>>): Promise<void>;
374
+
375
+ /**
376
+ * Values that are not known until the mutation commits.
377
+ */
378
+ vars: {
379
+ /**
380
+ * The placeholder for the transaction's commit timestamp. Written into a
381
+ * document field via `db.insert`, it resolves at commit to an int64
382
+ * (`bigint`) ordered by commit order. Within the writing mutation,
383
+ * reading the field back yields the placeholder, which cannot be used as
384
+ * a number.
385
+ */
386
+ commitTs: CommitTsPlaceholder;
387
+ };
373
388
  }
374
389
 
375
390
  /**
@@ -15,7 +15,7 @@ import { QueryInitializerImpl } from "./query_impl.js";
15
15
  import { GenericDataModel, GenericDocument } from "../data_model.js";
16
16
  import { validateArg } from "./validate.js";
17
17
  import { version } from "../../index.js";
18
- import { patchValueToJson } from "../../values/value.js";
18
+ import { commitTsPlaceholder, patchValueToJson } from "../../values/value.js";
19
19
 
20
20
  async function get(
21
21
  table: string | undefined,
@@ -164,6 +164,9 @@ export function setupWriter(): GenericDatabaseWriter<GenericDataModel> &
164
164
  table: (tableName) => {
165
165
  return new TableWriter(tableName, false);
166
166
  },
167
+ vars: {
168
+ commitTs: commitTsPlaceholder,
169
+ },
167
170
  };
168
171
  }
169
172
 
package/src/server/log.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { audit } from "./audit_logging.js";
2
- import { vars } from "./logVars.js";
2
+ import { internalVars, vars } from "./logVars.js";
3
3
 
4
4
  // Type annotations are needed for the `unique symbol` types in `vars` to typecheck correctly
5
5
  interface Log {
@@ -26,9 +26,12 @@ interface Log {
26
26
  */
27
27
  audit: typeof audit;
28
28
  vars: typeof vars;
29
+ /** @internal */
30
+ internalVars: typeof internalVars;
29
31
  }
30
32
 
31
33
  export const log: Log = {
32
34
  audit,
33
35
  vars,
36
+ internalVars,
34
37
  };
@@ -2,18 +2,21 @@ const REQUEST_ID = Symbol("var.requestId");
2
2
  const IP = Symbol("var.ip");
3
3
  const USER_AGENT = Symbol("var.userAgent");
4
4
  const NOW = Symbol("var.now");
5
+ const CONVEX_ACTOR = Symbol("var.convexActor");
5
6
 
6
7
  export type LogVar =
7
8
  | typeof REQUEST_ID
8
9
  | typeof IP
9
10
  | typeof USER_AGENT
10
- | typeof NOW;
11
+ | typeof NOW
12
+ | typeof CONVEX_ACTOR;
11
13
 
12
14
  export const varNames: Record<symbol, string> = {
13
15
  [REQUEST_ID]: "requestId",
14
16
  [IP]: "ip",
15
17
  [USER_AGENT]: "userAgent",
16
18
  [NOW]: "now",
19
+ [CONVEX_ACTOR]: "convexActor",
17
20
  };
18
21
 
19
22
  export const vars = {
@@ -29,3 +32,16 @@ export const vars = {
29
32
  */
30
33
  now: NOW,
31
34
  } as const;
35
+
36
+ /**
37
+ * Internal-only log vars, surfaced via `log.internalVars`. These are kept out
38
+ * of the public `log.vars` surface.
39
+ */
40
+ export const internalVars = {
41
+ /**
42
+ * If the function was invoked using admin auth (either directly or while
43
+ * acting as an end user, e.g. from the dashboard), resolved to information
44
+ * about the admin. Otherwise, resolved to `null`.
45
+ */
46
+ convexActor: CONVEX_ACTOR,
47
+ } as const;
@@ -1,5 +1,5 @@
1
1
  /* eslint-disable @typescript-eslint/no-unused-vars */
2
- import { GenericId } from "../values/index.js";
2
+ import { CommitTsPlaceholder, GenericId } from "../values/index.js";
3
3
  import { describe, expect, test } from "vitest";
4
4
  import { assert, Equals } from "../test/type_testing.js";
5
5
  import { SystemIndexes } from "./system_fields.js";
@@ -27,6 +27,7 @@ describe("DataModelFromSchemaDefinition", () => {
27
27
  number: v.number(),
28
28
  float64: v.float64(),
29
29
  int64: v.int64(),
30
+ commitTs: v.commitTs(),
30
31
  boolean: v.boolean(),
31
32
  string: v.string(),
32
33
  bytes: v.bytes(),
@@ -43,6 +44,7 @@ describe("DataModelFromSchemaDefinition", () => {
43
44
  number: number;
44
45
  float64: number;
45
46
  int64: bigint;
47
+ commitTs: bigint | CommitTsPlaceholder;
46
48
  boolean: boolean;
47
49
  string: string;
48
50
  array: boolean[];
@@ -57,6 +59,7 @@ describe("DataModelFromSchemaDefinition", () => {
57
59
  | "number"
58
60
  | "float64"
59
61
  | "int64"
62
+ | "commitTs"
60
63
  | "boolean"
61
64
  | "string"
62
65
  | "bytes"
@@ -743,6 +746,7 @@ describe("JsonTypesFromSchema", () => {
743
746
  numberField: v.number(),
744
747
  float64Field: v.float64(),
745
748
  int64Field: v.int64(),
749
+ commitTsField: v.commitTs(),
746
750
  booleanField: v.boolean(),
747
751
  stringField: v.string(),
748
752
  bytesField: v.bytes(),
@@ -766,6 +770,7 @@ describe("JsonTypesFromSchema", () => {
766
770
  numberField: { fieldType: { type: "number" }, optional: false },
767
771
  float64Field: { fieldType: { type: "number" }, optional: false },
768
772
  int64Field: { fieldType: { type: "bigint" }, optional: false },
773
+ commitTsField: { fieldType: { type: "commitTs" }, optional: false },
769
774
  booleanField: { fieldType: { type: "boolean" }, optional: false },
770
775
  stringField: { fieldType: { type: "string" }, optional: false },
771
776
  bytesField: { fieldType: { type: "bytes" }, optional: false },
@@ -1,4 +1,4 @@
1
- import { Value } from "./value.js";
1
+ import { CommitTsPlaceholder, MAX_INT64, Value } from "./value.js";
2
2
  import { compareUTF8 } from "./compare_utf8.js";
3
3
 
4
4
  export function compareValues(k1: Value | undefined, k2: Value | undefined) {
@@ -122,6 +122,9 @@ function makeComparable(v: Value | undefined): [number, any] {
122
122
  if (typeof v === "bigint") {
123
123
  return [2, v];
124
124
  }
125
+ if (v instanceof CommitTsPlaceholder) {
126
+ return [2, MAX_INT64];
127
+ }
125
128
  if (typeof v === "number") {
126
129
  return [3, v];
127
130
  }
@@ -140,5 +143,5 @@ function makeComparable(v: Value | undefined): [number, any] {
140
143
  // Otherwise, it's an POJO.
141
144
  const keys = Object.keys(v).sort();
142
145
  const pojo: Value[] = keys.map((k) => [k, v[k]!]);
143
- return [8, pojo.map(makeComparable)];
146
+ return [9, pojo.map(makeComparable)];
144
147
  }
@@ -6,7 +6,7 @@
6
6
  * @module
7
7
  */
8
8
 
9
- export { convexToJson, jsonToConvex } from "./value.js";
9
+ export { convexToJson, jsonToConvex, CommitTsPlaceholder } from "./value.js";
10
10
  export type {
11
11
  Id as GenericId,
12
12
  JSONValue,
@@ -30,6 +30,7 @@ export type {
30
30
  VId,
31
31
  VFloat64,
32
32
  VInt64,
33
+ VCommitTs,
33
34
  VBoolean,
34
35
  VBytes,
35
36
  VString,
@@ -5,6 +5,7 @@ import {
5
5
  SYSTEM_FIELD_CREATION_TIME_SIZE,
6
6
  SYSTEM_FIELD_ID_ESTIMATE,
7
7
  } from "./size.js";
8
+ import { commitTsPlaceholder } from "./value.js";
8
9
 
9
10
  /**
10
11
  * Tests for getConvexSize JS-specific behavior.
@@ -16,6 +17,10 @@ describe("getConvexSize", () => {
16
17
  expect(getConvexSize(undefined)).toBe(0);
17
18
  });
18
19
 
20
+ test("commit timestamp placeholder sized as resolved Int64", () => {
21
+ expect(getConvexSize(commitTsPlaceholder)).toBe(9);
22
+ });
23
+
19
24
  test("long strings use TextEncoder path (>500 chars)", () => {
20
25
  // This tests the JS-specific optimization for long strings
21
26
  const longAscii = "a".repeat(1000);
@@ -23,7 +23,7 @@
23
23
  * @module
24
24
  */
25
25
 
26
- import type { Value } from "./value.js";
26
+ import { CommitTsPlaceholder, type Value } from "./value.js";
27
27
  import { isSimpleObject } from "../common/index.js";
28
28
 
29
29
  /**
@@ -51,6 +51,10 @@ export function getConvexSize(value: Value | undefined): number {
51
51
  // Int64: 1 byte type marker + 8 bytes value
52
52
  return 9;
53
53
  }
54
+ if (value instanceof CommitTsPlaceholder) {
55
+ // Same as bigint
56
+ return 9;
57
+ }
54
58
  if (typeof value === "number") {
55
59
  // Float64: 1 byte type marker + 8 bytes value
56
60
  return 9;
@@ -1,4 +1,4 @@
1
- import { GenericId } from "../values/index.js";
1
+ import { CommitTsPlaceholder, GenericId } from "../values/index.js";
2
2
  import { describe, test, expect } from "vitest";
3
3
  import { assert, Equals } from "../test/type_testing.js";
4
4
  import { v, Infer } from "../values/validator.js";
@@ -56,6 +56,21 @@ describe("Validators", () => {
56
56
  >();
57
57
  });
58
58
 
59
+ test("commitTs validator", () => {
60
+ const commitTs = v.commitTs();
61
+ expect(commitTs.kind).toEqual("commitTs");
62
+ expect(commitTs.json).toEqual({ type: "commitTs" });
63
+ assert<Equals<Infer<typeof commitTs>, bigint | CommitTsPlaceholder>>();
64
+
65
+ const _optionalCommitTs = v.optional(v.commitTs());
66
+ assert<
67
+ Equals<
68
+ Infer<typeof _optionalCommitTs>,
69
+ bigint | CommitTsPlaceholder | undefined
70
+ >
71
+ >();
72
+ });
73
+
59
74
  test("Most validators don't accept optional validators as children", () => {
60
75
  const optional = v.optional(v.string());
61
76
  const required = v.string();
@@ -8,6 +8,7 @@ import {
8
8
  VBytes,
9
9
  VFloat64,
10
10
  VId,
11
+ VCommitTs,
11
12
  VInt64,
12
13
  VLiteral,
13
14
  VNull,
@@ -206,6 +207,18 @@ export const v = {
206
207
  return new VInt64({ isOptional: "required" });
207
208
  },
208
209
 
210
+ /**
211
+ * Validates that the value is a commit timestamp: an int64 (`bigint`)
212
+ * assigned from the transaction's commit timestamp.
213
+ *
214
+ * An index on a field with this validator orders documents by commit order.
215
+ * Accepts any int64 value; write `db.vars.commitTs` to have the field
216
+ * resolve to the commit timestamp when the mutation commits.
217
+ */
218
+ commitTs: () => {
219
+ return new VCommitTs({ isOptional: "required" });
220
+ },
221
+
209
222
  /**
210
223
  * Validates that the value is a `boolean`.
211
224
  */
@@ -1,7 +1,7 @@
1
1
  import { Expand } from "../type_utils.js";
2
2
  import { GenericId } from "./index.js";
3
3
  import { GenericValidator, ObjectType } from "./validator.js";
4
- import { JSONValue, convexToJson } from "./value.js";
4
+ import { CommitTsPlaceholder, JSONValue, convexToJson } from "./value.js";
5
5
 
6
6
  type TableNameFromType<T> =
7
7
  T extends GenericId<infer TableName> ? TableName : string;
@@ -155,6 +155,30 @@ export class VInt64<
155
155
  }
156
156
  }
157
157
 
158
+ /**
159
+ * The type of the `v.commitTs()` validator.
160
+ */
161
+ export class VCommitTs<
162
+ Type = bigint | CommitTsPlaceholder,
163
+ IsOptional extends OptionalProperty = "required",
164
+ > extends BaseValidator<Type, IsOptional> {
165
+ /**
166
+ * The kind of validator, `"commitTs"`.
167
+ */
168
+ readonly kind = "commitTs" as const;
169
+
170
+ /** @internal */
171
+ get json(): ValidatorJSON {
172
+ return { type: this.kind };
173
+ }
174
+ /** @internal */
175
+ asOptional() {
176
+ return new VCommitTs<Type | undefined, "optional">({
177
+ isOptional: "optional",
178
+ });
179
+ }
180
+ }
181
+
158
182
  /**
159
183
  * The type of the `v.boolean()` validator.
160
184
  */
@@ -653,6 +677,8 @@ export type VOptional<T extends Validator<any, OptionalProperty, any>> =
653
677
  ? VFloat64<Type | undefined, "optional">
654
678
  : T extends VInt64<infer Type, OptionalProperty>
655
679
  ? VInt64<Type | undefined, "optional">
680
+ : T extends VCommitTs<infer Type, OptionalProperty>
681
+ ? VCommitTs<Type | undefined, "optional">
656
682
  : T extends VBoolean<infer Type, OptionalProperty>
657
683
  ? VBoolean<Type | undefined, "optional">
658
684
  : T extends VNull<infer Type, OptionalProperty>
@@ -712,6 +738,7 @@ export type Validator<
712
738
  | VString<Type, IsOptional>
713
739
  | VFloat64<Type, IsOptional>
714
740
  | VInt64<Type, IsOptional>
741
+ | VCommitTs<Type, IsOptional>
715
742
  | VBoolean<Type, IsOptional>
716
743
  | VNull<Type, IsOptional>
717
744
  | VAny<Type, IsOptional>
@@ -750,6 +777,7 @@ export type ValidatorJSON =
750
777
  | { type: "null" }
751
778
  | { type: "number" }
752
779
  | { type: "bigint" }
780
+ | { type: "commitTs" }
753
781
  | { type: "boolean" }
754
782
  | { type: "string" }
755
783
  | { type: "bytes" }
@@ -8,6 +8,7 @@ import {
8
8
  modernBigIntToBase64,
9
9
  convexToJson,
10
10
  jsonToConvex,
11
+ commitTsPlaceholder,
11
12
  } from "./value.js";
12
13
  import { compareValues } from "./compare.js";
13
14
 
@@ -88,6 +89,36 @@ describe("jsonToConvex", () => {
88
89
  });
89
90
  });
90
91
 
92
+ describe("commit timestamp sentinel", () => {
93
+ test("round trips through JSON as the singleton", () => {
94
+ const decoded = jsonToConvex({ property: { $commitTs: null } }) as any;
95
+ expect(decoded.property).toBe(commitTsPlaceholder);
96
+ expect(convexToJson({ property: commitTsPlaceholder as any })).toEqual({
97
+ property: { $commitTs: null },
98
+ });
99
+ });
100
+
101
+ test("rejects malformed tokens", () => {
102
+ expect(() => jsonToConvex({ $commitTs: 1 } as any)).toThrow(
103
+ /Malformed \$commitTs/,
104
+ );
105
+ // A `$commitTs` key alongside other fields is not the token; it's an
106
+ // invalid field name.
107
+ expect(() => jsonToConvex({ $commitTs: null, x: 1 })).toThrow(/reserved/);
108
+ });
109
+
110
+ test("throws on coercion", () => {
111
+ expect(() => Number(commitTsPlaceholder)).toThrow(/unresolved/);
112
+ expect(() => (commitTsPlaceholder as any) + 1n).toThrow(/unresolved/);
113
+ expect(() => JSON.stringify({ commitTs: commitTsPlaceholder })).toThrow(
114
+ /unresolved/,
115
+ );
116
+ expect(`${commitTsPlaceholder as any}`).toEqual(
117
+ "[unresolved commit timestamp]",
118
+ );
119
+ });
120
+ });
121
+
91
122
  describe("bigints in Safari 14", () => {
92
123
  test("roundtrips BigInt even in Safari 14", () => {
93
124
  // https://caniuse.com/mdn-javascript_builtins_dataview_setbigint64
@@ -194,4 +225,8 @@ describe("compare", () => {
194
225
  }
195
226
  }
196
227
  });
228
+
229
+ test("commit timestamp placeholders compare equal", () => {
230
+ expect(compareValues(commitTsPlaceholder, commitTsPlaceholder)).toEqual(0);
231
+ });
197
232
  });