@convex-dev/crons 0.0.1 → 0.1.0

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 (35) hide show
  1. package/README.md +233 -20
  2. package/dist/commonjs/client/index.d.ts +1 -1
  3. package/dist/commonjs/client/index.d.ts.map +1 -1
  4. package/dist/commonjs/client/index.js +1 -1
  5. package/dist/commonjs/client/index.js.map +1 -1
  6. package/dist/esm/client/index.d.ts +1 -1
  7. package/dist/esm/client/index.d.ts.map +1 -1
  8. package/dist/esm/client/index.js +1 -1
  9. package/dist/esm/client/index.js.map +1 -1
  10. package/package.json +16 -3
  11. package/src/client/index.ts +1 -1
  12. package/dist/commonjs/component/index.d.ts +0 -135
  13. package/dist/commonjs/component/index.d.ts.map +0 -1
  14. package/dist/commonjs/component/index.js +0 -289
  15. package/dist/commonjs/component/index.js.map +0 -1
  16. package/dist/commonjs/component/parseArgs.d.ts +0 -11
  17. package/dist/commonjs/component/parseArgs.d.ts.map +0 -1
  18. package/dist/commonjs/component/parseArgs.js +0 -28
  19. package/dist/commonjs/component/parseArgs.js.map +0 -1
  20. package/dist/commonjs/frontend/index.d.ts +0 -2
  21. package/dist/commonjs/frontend/index.d.ts.map +0 -1
  22. package/dist/commonjs/frontend/index.js +0 -8
  23. package/dist/commonjs/frontend/index.js.map +0 -1
  24. package/dist/esm/component/index.d.ts +0 -135
  25. package/dist/esm/component/index.d.ts.map +0 -1
  26. package/dist/esm/component/index.js +0 -289
  27. package/dist/esm/component/index.js.map +0 -1
  28. package/dist/esm/component/parseArgs.d.ts +0 -11
  29. package/dist/esm/component/parseArgs.d.ts.map +0 -1
  30. package/dist/esm/component/parseArgs.js +0 -28
  31. package/dist/esm/component/parseArgs.js.map +0 -1
  32. package/dist/esm/frontend/index.d.ts +0 -2
  33. package/dist/esm/frontend/index.d.ts.map +0 -1
  34. package/dist/esm/frontend/index.js +0 -8
  35. package/dist/esm/frontend/index.js.map +0 -1
package/README.md CHANGED
@@ -1,12 +1,14 @@
1
1
  # Crons Convex Component
2
2
 
3
+ [![npm version](https://badge.fury.io/js/@convex-dev%2Fcrons.svg)](https://badge.fury.io/js/@convex-dev%2Fcrons)
4
+
3
5
  This Convex component provides functionality for registering and managing cron
4
6
  jobs at runtime. Convex comes with built-in support for cron jobs but they must
5
7
  be statically defined at deployment time. This library allows for dynamic
6
8
  registration of cron jobs at runtime.
7
9
 
8
- It supports intervals in ms as well as cron schedules with the same format as
9
- the unix `cron` command:
10
+ It supports intervals in milliseconds as well as cron schedules with the same
11
+ format as the unix `cron` command:
10
12
 
11
13
  ```
12
14
  * * * * * *
@@ -20,31 +22,119 @@ the unix `cron` command:
20
22
  └───────────────── second (0 - 59, optional)
21
23
  ```
22
24
 
23
- See `example/convex/example.ts` for a simple example of how to use this library.
25
+ ### Design
26
+
27
+ The design of this component is based on the Cronvex demo app that's described in
28
+ [this Stack post](https://stack.convex.dev/cron-jobs).
29
+
30
+ ### Convex App
31
+
32
+ You'll need a Convex App to use the component. Run `npm create convex` or
33
+ follow any of the [Convex quickstarts](https://docs.convex.dev/home) to set one up.
34
+
35
+ ## Installation
36
+
37
+ Install the component package:
38
+
39
+ ```ts
40
+ npm install @convex-dev/crons
41
+ ```
42
+
43
+ Create a `convex.config.ts` file in your app's `convex/` folder and install the component by calling `use`:
24
44
 
25
- The component must first be defined in convex.config.ts:
45
+ ```ts
46
+ // convex/convex.config.js
47
+ import { defineApp } from "convex/server";
48
+ import crons from "@convex-dev/crons/convex.config";
26
49
 
27
- ```typescript
28
50
  const app = defineApp();
29
- app.use(component, { name: "crons" });
51
+ app.use(crons);
52
+
53
+ export default app;
30
54
  ```
31
55
 
32
- and can then be used from Convex functions via the `Crons` wrapper class:
56
+ ## Usage
57
+
58
+ A `Crons` wrapper can be instantiated within your Convex code as:
59
+
60
+ ```ts
61
+ import { components } from "./_generated/api";
62
+ import { Crons } from "@convex-dev/crons";
33
63
 
34
- ```typescript
35
64
  const crons = new Crons(components.crons);
65
+ ```
66
+
67
+ The `Crons` wrapper class provides the following methods:
68
+
69
+ - `register(ctx, schedule, fn, args, name?)`: Registers a new cron job.
70
+ - `get(ctx, { name | id })`: Gets a cron job by name or ID.
71
+ - `list(ctx)`: Lists all cron jobs.
72
+ - `delete(ctx, { name | id })`: Deletes a cron job by name or ID.
73
+
74
+ Example usage:
75
+
76
+ ```ts
77
+ import { v } from "convex/values";
78
+ import { internalMutation } from "./_generated/server";
79
+ import { internal } from "./_generated/api";
80
+
81
+ // Dummy function that we're going to schedule.
82
+ export const logStuff = internalMutation({
83
+ args: {
84
+ message: v.string(),
85
+ },
86
+ handler: async (_ctx, args) => {
87
+ console.log(args.message);
88
+ },
89
+ });
90
+
91
+ // Run a bunch of cron operations as a test. Note that this function runs as a
92
+ // transaction and cleans up after itself so you won't actually see these crons
93
+ // showing up in the database while it's in progress.
94
+ export const doSomeStuff = internalMutation({
95
+ handler: async (ctx) => {
96
+ // Register some crons.
97
+ const namedCronId = await crons.register(
98
+ ctx,
99
+ { kind: "interval", ms: 3600000 },
100
+ internal.example.logStuff,
101
+ { message: "Hourly cron test" },
102
+ "hourly-test"
103
+ );
104
+ console.log("Registered new cron job with ID:", namedCronId);
105
+ const unnamedCronId = await crons.register(
106
+ ctx,
107
+ { kind: "cron", cronspec: "0 * * * *" },
108
+ internal.example.logStuff,
109
+ { message: "Minutely cron test" }
110
+ );
111
+ console.log("Registered new cron job with ID:", unnamedCronId);
36
112
 
37
- // ...
113
+ // Get the cron job by name.
114
+ const cronByName = await crons.get(ctx, { name: "hourly-test" });
115
+ console.log("Retrieved cron job by name:", cronByName);
38
116
 
39
- if ((await crons.get(ctx, { name: "daily" })) === null) {
40
- await crons.register(
41
- ctx,
42
- { kind: "cron", cronspec: "0 0 * * *" },
43
- internal.example.logStuff,
44
- { message: "daily cron" },
45
- "daily"
46
- );
47
- }
117
+ // Get the cron job by ID.
118
+ const cronById = await crons.get(ctx, { id: unnamedCronId });
119
+ console.log("Retrieved cron job by ID:", cronById);
120
+
121
+ // List all cron jobs.
122
+ const allCrons = await crons.list(ctx);
123
+ console.log("All cron jobs:", allCrons);
124
+
125
+ // Delete the cron jobs.
126
+ await crons.delete(ctx, { name: "hourly-test" });
127
+ console.log("Deleted cron job by name:", "hourly-test");
128
+ await crons.delete(ctx, { id: unnamedCronId });
129
+ console.log("Deleted cron job by ID:", unnamedCronId);
130
+
131
+ // Verify deletion.
132
+ const deletedCronByName = await crons.get(ctx, { name: "hourly-test" });
133
+ console.log("Deleted cron job (should be null):", deletedCronByName);
134
+ const deletedCronById = await crons.get(ctx, { id: unnamedCronId });
135
+ console.log("Deleted cron job (should be null):", deletedCronById);
136
+ },
137
+ });
48
138
  ```
49
139
 
50
140
  If you'd like to statically define cronjobs like in the built-in `crons.ts`
@@ -52,5 +142,128 @@ Convex feature you can do so via an init script that idempotently registers a
52
142
  cron with a given name. e.g., in an `init.ts` file that gets run on every
53
143
  deploy via `convex dev --run init`.
54
144
 
55
- The design of this component is based on the Cronvex demo app that's described in
56
- [this Stack post](https://stack.convex.dev/cron-jobs).
145
+ ```ts
146
+ // Register a daily cron job. This could be called from an init script to make
147
+ // sure it's always registered, like the built-in crons in Convex.
148
+ export const registerDailyCron = internalMutation({
149
+ handler: async (ctx) => {
150
+ if ((await crons.get(ctx, { name: "daily" })) === null) {
151
+ await crons.register(
152
+ ctx,
153
+ { kind: "cron", cronspec: "0 0 * * *" },
154
+ internal.example.logStuff,
155
+ {
156
+ message: "daily cron",
157
+ },
158
+ "daily"
159
+ );
160
+ }
161
+ },
162
+ });
163
+ ```
164
+
165
+ Crons are created transactionally and will be guaranteed to exist after the
166
+ mutation that creates them has run. It's thus possible to write workflows like
167
+ the following that schedules a cron and then deletes itself as soon as it runs,
168
+ without any additional error handling about the cron not existing.
169
+
170
+ ```ts
171
+ // This will schedule a cron job to run every 10 seconds but then delete itself
172
+ // the first time it runs.
173
+ export const selfDeletingCron = internalMutation({
174
+ handler: async (ctx) => {
175
+ const cronId = await crons.register(
176
+ ctx,
177
+ { kind: "interval", ms: 10000 },
178
+ internal.example.deleteSelf,
179
+ { name: "self-deleting-cron" },
180
+ "self-deleting-cron"
181
+ );
182
+
183
+ console.log("Registered self-deleting cron job with ID:", cronId);
184
+ },
185
+ });
186
+
187
+ // Worker function that deletes a cron job.
188
+ export const deleteSelf = internalMutation({
189
+ args: { name: v.string() },
190
+ handler: async (ctx, { name }) => {
191
+ console.log("Self-deleting cron job running. Name:", name);
192
+ await crons.delete(ctx, { name });
193
+ console.log("Self-deleting cron job has been deleted. Name:", name);
194
+ },
195
+ });
196
+ ```
197
+
198
+ # 🧑‍🏫 What is Convex?
199
+
200
+ [Convex](https://convex.dev) is a hosted backend platform with a
201
+ built-in database that lets you write your
202
+ [database schema](https://docs.convex.dev/database/schemas) and
203
+ [server functions](https://docs.convex.dev/functions) in
204
+ [TypeScript](https://docs.convex.dev/typescript). Server-side database
205
+ [queries](https://docs.convex.dev/functions/query-functions) automatically
206
+ [cache](https://docs.convex.dev/functions/query-functions#caching--reactivity) and
207
+ [subscribe](https://docs.convex.dev/client/react#reactivity) to data, powering a
208
+ [realtime `useQuery` hook](https://docs.convex.dev/client/react#fetching-data) in our
209
+ [React client](https://docs.convex.dev/client/react). There are also clients for
210
+ [Python](https://docs.convex.dev/client/python),
211
+ [Rust](https://docs.convex.dev/client/rust),
212
+ [ReactNative](https://docs.convex.dev/client/react-native), and
213
+ [Node](https://docs.convex.dev/client/javascript), as well as a straightforward
214
+ [HTTP API](https://docs.convex.dev/http-api/).
215
+
216
+ The database supports
217
+ [NoSQL-style documents](https://docs.convex.dev/database/document-storage) with
218
+ [opt-in schema validation](https://docs.convex.dev/database/schemas),
219
+ [relationships](https://docs.convex.dev/database/document-ids) and
220
+ [custom indexes](https://docs.convex.dev/database/indexes/)
221
+ (including on fields in nested objects).
222
+
223
+ The
224
+ [`query`](https://docs.convex.dev/functions/query-functions) and
225
+ [`mutation`](https://docs.convex.dev/functions/mutation-functions) server functions have transactional,
226
+ low latency access to the database and leverage our
227
+ [`v8` runtime](https://docs.convex.dev/functions/runtimes) with
228
+ [determinism guardrails](https://docs.convex.dev/functions/runtimes#using-randomness-and-time-in-queries-and-mutations)
229
+ to provide the strongest ACID guarantees on the market:
230
+ immediate consistency,
231
+ serializable isolation, and
232
+ automatic conflict resolution via
233
+ [optimistic multi-version concurrency control](https://docs.convex.dev/database/advanced/occ) (OCC / MVCC).
234
+
235
+ The [`action` server functions](https://docs.convex.dev/functions/actions) have
236
+ access to external APIs and enable other side-effects and non-determinism in
237
+ either our
238
+ [optimized `v8` runtime](https://docs.convex.dev/functions/runtimes) or a more
239
+ [flexible `node` runtime](https://docs.convex.dev/functions/runtimes#nodejs-runtime).
240
+
241
+ Functions can run in the background via
242
+ [scheduling](https://docs.convex.dev/scheduling/scheduled-functions) and
243
+ [cron jobs](https://docs.convex.dev/scheduling/cron-jobs).
244
+
245
+ Development is cloud-first, with
246
+ [hot reloads for server function](https://docs.convex.dev/cli#run-the-convex-dev-server) editing via the
247
+ [CLI](https://docs.convex.dev/cli),
248
+ [preview deployments](https://docs.convex.dev/production/hosting/preview-deployments),
249
+ [logging and exception reporting integrations](https://docs.convex.dev/production/integrations/),
250
+ There is a
251
+ [dashboard UI](https://docs.convex.dev/dashboard) to
252
+ [browse and edit data](https://docs.convex.dev/dashboard/deployments/data),
253
+ [edit environment variables](https://docs.convex.dev/production/environment-variables),
254
+ [view logs](https://docs.convex.dev/dashboard/deployments/logs),
255
+ [run server functions](https://docs.convex.dev/dashboard/deployments/functions), and more.
256
+
257
+ There are built-in features for
258
+ [reactive pagination](https://docs.convex.dev/database/pagination),
259
+ [file storage](https://docs.convex.dev/file-storage),
260
+ [reactive text search](https://docs.convex.dev/text-search),
261
+ [vector search](https://docs.convex.dev/vector-search),
262
+ [https endpoints](https://docs.convex.dev/functions/http-actions) (for webhooks),
263
+ [snapshot import/export](https://docs.convex.dev/database/import-export/),
264
+ [streaming import/export](https://docs.convex.dev/production/integrations/streaming-import-export), and
265
+ [runtime validation](https://docs.convex.dev/database/schemas#validators) for
266
+ [function arguments](https://docs.convex.dev/functions/args-validation) and
267
+ [database data](https://docs.convex.dev/database/schemas#schema-validation).
268
+
269
+ Everything scales automatically, and it’s [free to start](https://www.convex.dev/plans).
@@ -41,7 +41,7 @@ export declare class Crons {
41
41
  *
42
42
  * @param identifier - Either the ID or name of the cron job.
43
43
  */
44
- del(ctx: RunMutationCtx, identifier: {
44
+ delete(ctx: RunMutationCtx, identifier: {
45
45
  id: string;
46
46
  } | {
47
47
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,YAAY,EAEZ,4BAA4B,EAC7B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEjE,YAAY,EAAE,QAAQ,EAAE,CAAC;AAqCzB,qBAAa,KAAK;IACJ,OAAO,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC;IAEjD;;;;;;;;;;;OAWG;IACG,QAAQ,CAAC,CAAC,SAAS,4BAA4B,EACnD,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,EACrB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC;IASlB;;;;OAIG;IACG,IAAI,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAUjD;;;;;OAKG;IACG,GAAG,CACP,GAAG,EAAE,WAAW,EAChB,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAC5C,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAa3B;;;;OAIG;IACG,GAAG,CACP,GAAG,EAAE,cAAc,EACnB,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAC5C,OAAO,CAAC,IAAI,CAAC;CAGjB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,YAAY,EAEZ,4BAA4B,EAC7B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEjE,YAAY,EAAE,QAAQ,EAAE,CAAC;AAqCzB,qBAAa,KAAK;IACJ,OAAO,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC;IAEjD;;;;;;;;;;;OAWG;IACG,QAAQ,CAAC,CAAC,SAAS,4BAA4B,EACnD,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,EACrB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC;IASlB;;;;OAIG;IACG,IAAI,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAUjD;;;;;OAKG;IACG,GAAG,CACP,GAAG,EAAE,WAAW,EAChB,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAC5C,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAa3B;;;;OAIG;IACG,MAAM,CACV,GAAG,EAAE,cAAc,EACnB,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAC5C,OAAO,CAAC,IAAI,CAAC;CAGjB"}
@@ -92,7 +92,7 @@ export class Crons {
92
92
  *
93
93
  * @param identifier - Either the ID or name of the cron job.
94
94
  */
95
- async del(ctx, identifier) {
95
+ async delete(ctx, identifier) {
96
96
  return ctx.runMutation(this.component.public.del, { identifier });
97
97
  }
98
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,qDAAqD;AAErD,OAAO,EACL,oBAAoB,GAIrB,MAAM,eAAe,CAAC;AAOvB,2EAA2E;AAE3E,yCAAyC;AACzC,EAAE;AACF,6EAA6E;AAC7E,2BAA2B;AAC3B,EAAE;AACF,oBAAoB;AACpB,oBAAoB;AACpB,oBAAoB;AACpB,mEAAmE;AACnE,qCAAqC;AACrC,+CAA+C;AAC/C,oCAAoC;AACpC,sCAAsC;AACtC,gDAAgD;AAChD,EAAE;AACF,kEAAkE;AAClE,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,4EAA4E;AAC5E,sCAAsC;AACtC,EAAE;AACF,6CAA6C;AAC7C,MAAM;AACN,4DAA4D;AAC5D,0BAA0B;AAC1B,WAAW;AACX,+CAA+C;AAC/C,iCAAiC;AACjC,iCAAiC;AACjC,cAAc;AACd,OAAO;AACP,IAAI;AACJ,MAAM,OAAO,KAAK;IACI;IAApB,YAAoB,SAA6B;QAA7B,cAAS,GAAT,SAAS,CAAoB;IAAG,CAAC;IAErD;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,QAAQ,CACZ,GAAmB,EACnB,QAAkB,EAClB,IAAO,EACP,IAAqB,EACrB,IAAa;QAEb,OAAO,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE;YACrD,IAAI;YACJ,QAAQ;YACR,cAAc,EAAE,MAAM,oBAAoB,CAAC,IAAI,CAAC;YAChD,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,IAAI,CAAC,GAAgB;QACzB,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACjE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC1B,GAAG,IAAI;YACP,cAAc,EAAE,IAAI,CAAC,cAEpB;SACF,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,GAAG,CACP,GAAgB,EAChB,UAA6C;QAE7C,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QAC3E,IAAI,IAAI,KAAK,IAAI,EAAE;YACjB,OAAO,IAAI,CAAC;SACb;QACD,OAAO;YACL,GAAG,IAAI;YACP,cAAc,EAAE,IAAI,CAAC,cAEpB;SACF,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,GAAG,CACP,GAAmB,EACnB,UAA6C;QAE7C,OAAO,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IACpE,CAAC;CACF"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,qDAAqD;AAErD,OAAO,EACL,oBAAoB,GAIrB,MAAM,eAAe,CAAC;AAOvB,2EAA2E;AAE3E,yCAAyC;AACzC,EAAE;AACF,6EAA6E;AAC7E,2BAA2B;AAC3B,EAAE;AACF,oBAAoB;AACpB,oBAAoB;AACpB,oBAAoB;AACpB,mEAAmE;AACnE,qCAAqC;AACrC,+CAA+C;AAC/C,oCAAoC;AACpC,sCAAsC;AACtC,gDAAgD;AAChD,EAAE;AACF,kEAAkE;AAClE,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,4EAA4E;AAC5E,sCAAsC;AACtC,EAAE;AACF,6CAA6C;AAC7C,MAAM;AACN,4DAA4D;AAC5D,0BAA0B;AAC1B,WAAW;AACX,+CAA+C;AAC/C,iCAAiC;AACjC,iCAAiC;AACjC,cAAc;AACd,OAAO;AACP,IAAI;AACJ,MAAM,OAAO,KAAK;IACI;IAApB,YAAoB,SAA6B;QAA7B,cAAS,GAAT,SAAS,CAAoB;IAAG,CAAC;IAErD;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,QAAQ,CACZ,GAAmB,EACnB,QAAkB,EAClB,IAAO,EACP,IAAqB,EACrB,IAAa;QAEb,OAAO,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE;YACrD,IAAI;YACJ,QAAQ;YACR,cAAc,EAAE,MAAM,oBAAoB,CAAC,IAAI,CAAC;YAChD,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,IAAI,CAAC,GAAgB;QACzB,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACjE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC1B,GAAG,IAAI;YACP,cAAc,EAAE,IAAI,CAAC,cAEpB;SACF,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,GAAG,CACP,GAAgB,EAChB,UAA6C;QAE7C,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QAC3E,IAAI,IAAI,KAAK,IAAI,EAAE;YACjB,OAAO,IAAI,CAAC;SACb;QACD,OAAO;YACL,GAAG,IAAI;YACP,cAAc,EAAE,IAAI,CAAC,cAEpB;SACF,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,MAAM,CACV,GAAmB,EACnB,UAA6C;QAE7C,OAAO,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IACpE,CAAC;CACF"}
@@ -41,7 +41,7 @@ export declare class Crons {
41
41
  *
42
42
  * @param identifier - Either the ID or name of the cron job.
43
43
  */
44
- del(ctx: RunMutationCtx, identifier: {
44
+ delete(ctx: RunMutationCtx, identifier: {
45
45
  id: string;
46
46
  } | {
47
47
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,YAAY,EAEZ,4BAA4B,EAC7B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEjE,YAAY,EAAE,QAAQ,EAAE,CAAC;AAqCzB,qBAAa,KAAK;IACJ,OAAO,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC;IAEjD;;;;;;;;;;;OAWG;IACG,QAAQ,CAAC,CAAC,SAAS,4BAA4B,EACnD,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,EACrB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC;IASlB;;;;OAIG;IACG,IAAI,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAUjD;;;;;OAKG;IACG,GAAG,CACP,GAAG,EAAE,WAAW,EAChB,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAC5C,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAa3B;;;;OAIG;IACG,GAAG,CACP,GAAG,EAAE,cAAc,EACnB,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAC5C,OAAO,CAAC,IAAI,CAAC;CAGjB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,YAAY,EAEZ,4BAA4B,EAC7B,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,GAAG,EAAE,MAAM,gCAAgC,CAAC;AACrD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEjE,YAAY,EAAE,QAAQ,EAAE,CAAC;AAqCzB,qBAAa,KAAK;IACJ,OAAO,CAAC,SAAS;gBAAT,SAAS,EAAE,MAAM,CAAC,OAAO,GAAG,CAAC;IAEjD;;;;;;;;;;;OAWG;IACG,QAAQ,CAAC,CAAC,SAAS,4BAA4B,EACnD,GAAG,EAAE,cAAc,EACnB,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,CAAC,EACP,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC,EACrB,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,MAAM,CAAC;IASlB;;;;OAIG;IACG,IAAI,CAAC,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;IAUjD;;;;;OAKG;IACG,GAAG,CACP,GAAG,EAAE,WAAW,EAChB,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAC5C,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;IAa3B;;;;OAIG;IACG,MAAM,CACV,GAAG,EAAE,cAAc,EACnB,UAAU,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,GAC5C,OAAO,CAAC,IAAI,CAAC;CAGjB"}
@@ -92,7 +92,7 @@ export class Crons {
92
92
  *
93
93
  * @param identifier - Either the ID or name of the cron job.
94
94
  */
95
- async del(ctx, identifier) {
95
+ async delete(ctx, identifier) {
96
96
  return ctx.runMutation(this.component.public.del, { identifier });
97
97
  }
98
98
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,qDAAqD;AAErD,OAAO,EACL,oBAAoB,GAIrB,MAAM,eAAe,CAAC;AAOvB,2EAA2E;AAE3E,yCAAyC;AACzC,EAAE;AACF,6EAA6E;AAC7E,2BAA2B;AAC3B,EAAE;AACF,oBAAoB;AACpB,oBAAoB;AACpB,oBAAoB;AACpB,mEAAmE;AACnE,qCAAqC;AACrC,+CAA+C;AAC/C,oCAAoC;AACpC,sCAAsC;AACtC,gDAAgD;AAChD,EAAE;AACF,kEAAkE;AAClE,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,4EAA4E;AAC5E,sCAAsC;AACtC,EAAE;AACF,6CAA6C;AAC7C,MAAM;AACN,4DAA4D;AAC5D,0BAA0B;AAC1B,WAAW;AACX,+CAA+C;AAC/C,iCAAiC;AACjC,iCAAiC;AACjC,cAAc;AACd,OAAO;AACP,IAAI;AACJ,MAAM,OAAO,KAAK;IACI;IAApB,YAAoB,SAA6B;QAA7B,cAAS,GAAT,SAAS,CAAoB;IAAG,CAAC;IAErD;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,QAAQ,CACZ,GAAmB,EACnB,QAAkB,EAClB,IAAO,EACP,IAAqB,EACrB,IAAa;QAEb,OAAO,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE;YACrD,IAAI;YACJ,QAAQ;YACR,cAAc,EAAE,MAAM,oBAAoB,CAAC,IAAI,CAAC;YAChD,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,IAAI,CAAC,GAAgB;QACzB,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACjE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC1B,GAAG,IAAI;YACP,cAAc,EAAE,IAAI,CAAC,cAEpB;SACF,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,GAAG,CACP,GAAgB,EAChB,UAA6C;QAE7C,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QAC3E,IAAI,IAAI,KAAK,IAAI,EAAE;YACjB,OAAO,IAAI,CAAC;SACb;QACD,OAAO;YACL,GAAG,IAAI;YACP,cAAc,EAAE,IAAI,CAAC,cAEpB;SACF,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,GAAG,CACP,GAAmB,EACnB,UAA6C;QAE7C,OAAO,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IACpE,CAAC;CACF"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,qDAAqD;AAErD,OAAO,EACL,oBAAoB,GAIrB,MAAM,eAAe,CAAC;AAOvB,2EAA2E;AAE3E,yCAAyC;AACzC,EAAE;AACF,6EAA6E;AAC7E,2BAA2B;AAC3B,EAAE;AACF,oBAAoB;AACpB,oBAAoB;AACpB,oBAAoB;AACpB,mEAAmE;AACnE,qCAAqC;AACrC,+CAA+C;AAC/C,oCAAoC;AACpC,sCAAsC;AACtC,gDAAgD;AAChD,EAAE;AACF,kEAAkE;AAClE,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,4EAA4E;AAC5E,sCAAsC;AACtC,EAAE;AACF,6CAA6C;AAC7C,MAAM;AACN,4DAA4D;AAC5D,0BAA0B;AAC1B,WAAW;AACX,+CAA+C;AAC/C,iCAAiC;AACjC,iCAAiC;AACjC,cAAc;AACd,OAAO;AACP,IAAI;AACJ,MAAM,OAAO,KAAK;IACI;IAApB,YAAoB,SAA6B;QAA7B,cAAS,GAAT,SAAS,CAAoB;IAAG,CAAC;IAErD;;;;;;;;;;;OAWG;IACH,KAAK,CAAC,QAAQ,CACZ,GAAmB,EACnB,QAAkB,EAClB,IAAO,EACP,IAAqB,EACrB,IAAa;QAEb,OAAO,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,EAAE;YACrD,IAAI;YACJ,QAAQ;YACR,cAAc,EAAE,MAAM,oBAAoB,CAAC,IAAI,CAAC;YAChD,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,IAAI,CAAC,GAAgB;QACzB,MAAM,KAAK,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACjE,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAC1B,GAAG,IAAI;YACP,cAAc,EAAE,IAAI,CAAC,cAEpB;SACF,CAAC,CAAC,CAAC;IACN,CAAC;IAED;;;;;OAKG;IACH,KAAK,CAAC,GAAG,CACP,GAAgB,EAChB,UAA6C;QAE7C,MAAM,IAAI,GAAG,MAAM,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QAC3E,IAAI,IAAI,KAAK,IAAI,EAAE;YACjB,OAAO,IAAI,CAAC;SACb;QACD,OAAO;YACL,GAAG,IAAI;YACP,cAAc,EAAE,IAAI,CAAC,cAEpB;SACF,CAAC;IACJ,CAAC;IAED;;;;OAIG;IACH,KAAK,CAAC,MAAM,CACV,GAAmB,EACnB,UAA6C;QAE7C,OAAO,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;IACpE,CAAC;CACF"}
package/package.json CHANGED
@@ -1,8 +1,21 @@
1
1
  {
2
2
  "name": "@convex-dev/crons",
3
- "version": "0.0.1",
4
3
  "description": "Convex component for scheduling periodic jobs.",
4
+ "repository": "github:get-convex/crons",
5
+ "homepage": "https://github.com/get-convex/crons#readme",
6
+ "bugs": {
7
+ "email": "support@convex.dev",
8
+ "url": "https://github.com/get-convex/crons/issues"
9
+ },
10
+ "version": "0.1.0",
5
11
  "license": "Apache-2.0",
12
+ "keywords": [
13
+ "convex",
14
+ "component",
15
+ "crons",
16
+ "cron",
17
+ "scheduler"
18
+ ],
6
19
  "type": "module",
7
20
  "scripts": {
8
21
  "build": "npm run build:esm && npm run build:cjs",
@@ -29,7 +42,7 @@
29
42
  "default": "./dist/commonjs/client/index.js"
30
43
  }
31
44
  },
32
- "./convex.config.js": {
45
+ "./convex.config": {
33
46
  "import": {
34
47
  "@convex-dev/component-source": "./src/component/convex.config.ts",
35
48
  "types": "./dist/esm/component/convex.config.d.ts",
@@ -38,7 +51,7 @@
38
51
  }
39
52
  },
40
53
  "dependencies": {
41
- "convex": "^1.16.1",
54
+ "convex": "1.16.2",
42
55
  "cron-parser": "^4.9.0"
43
56
  },
44
57
  "devDependencies": {
@@ -119,7 +119,7 @@ export class Crons {
119
119
  *
120
120
  * @param identifier - Either the ID or name of the cron job.
121
121
  */
122
- async del(
122
+ async delete(
123
123
  ctx: RunMutationCtx,
124
124
  identifier: { id: string } | { name: string }
125
125
  ): Promise<null> {
@@ -1,135 +0,0 @@
1
- /**
2
- * Schedule a mutation or action to run on a recurring basis.
3
- *
4
- * Like the unix command `cron`, Sunday is 0, Monday is 1, etc.
5
- *
6
- * ```
7
- * * * * * * *
8
- * ┬ ┬ ┬ ┬ ┬ ┬
9
- * │ │ │ │ │ |
10
- * │ │ │ │ │ └── day of week (0 - 7, 1L - 7L) (0 or 7 is Sun)
11
- * │ │ │ │ └───── month (1 - 12)
12
- * │ │ │ └──────── day of month (1 - 31, L)
13
- * │ │ └─────────── hour (0 - 23)
14
- * │ └────────────── minute (0 - 59)
15
- * └───────────────── second (0 - 59, optional)
16
- * ```
17
- *
18
- * @param name - Optional unique name for the cron job. Will throw if a name is
19
- * provided and a cron with the same name already exists.
20
- * @param cronspec - Cron string like `"15 7 * * *"` (Every day at 7:15 UTC)
21
- * @param functionHandle - A {@link FunctionHandle} string for the function to schedule.
22
- * @param args - The arguments to the function.
23
- * @returns The ID of the cron job.
24
- */
25
- export declare const registerCron: import("convex/server").RegisteredMutation<"public", {
26
- name?: string | undefined;
27
- functionHandle: string;
28
- args: any;
29
- cronspec: string;
30
- }, Promise<import("convex/values").GenericId<"crons">>>;
31
- /**
32
- * Schedule a mutation or action to run on the given interval.
33
- *
34
- * @param name - Optional unique name for the cron job. Will throw if a name is
35
- * provided and a cron with the same name already exists.
36
- * @param ms - The time in ms between runs for this scheduled job, >= 1000.
37
- * @param functionHandle - A {@link FunctionHandle} string for the function to schedule.
38
- * @param args - Any arguments to the function.
39
- * @returns The ID of the cron job.
40
- */
41
- export declare const registerInterval: import("convex/server").RegisteredMutation<"public", {
42
- name?: string | undefined;
43
- functionHandle: string;
44
- args: any;
45
- ms: number;
46
- }, Promise<import("convex/values").GenericId<"crons">>>;
47
- /**
48
- * List all user space cron jobs.
49
- *
50
- * @returns List of `cron` table rows.
51
- */
52
- export declare const list: import("convex/server").RegisteredQuery<"public", {}, Promise<{
53
- _id: import("convex/values").GenericId<"crons">;
54
- _creationTime: number;
55
- name?: string | undefined;
56
- schedulerJobId?: import("convex/values").GenericId<"_scheduled_functions"> | undefined;
57
- executionJobId?: import("convex/values").GenericId<"_scheduled_functions"> | undefined;
58
- functionHandle: string;
59
- args: any;
60
- schedule: {
61
- kind: "interval";
62
- ms: number;
63
- } | {
64
- kind: "cron";
65
- cronspec: string;
66
- };
67
- }[]>>;
68
- /**
69
- * Get an existing cron job by id.
70
- *
71
- * @param id - ID of the cron job.
72
- * @returns Cron job document.
73
- */
74
- export declare const get: import("convex/server").RegisteredQuery<"public", {
75
- id: import("convex/values").GenericId<"crons">;
76
- }, Promise<{
77
- _id: import("convex/values").GenericId<"crons">;
78
- _creationTime: number;
79
- name?: string | undefined;
80
- schedulerJobId?: import("convex/values").GenericId<"_scheduled_functions"> | undefined;
81
- executionJobId?: import("convex/values").GenericId<"_scheduled_functions"> | undefined;
82
- functionHandle: string;
83
- args: any;
84
- schedule: {
85
- kind: "interval";
86
- ms: number;
87
- } | {
88
- kind: "cron";
89
- cronspec: string;
90
- };
91
- } | null>>;
92
- /**
93
- * Get an existing cron job by name.
94
- *
95
- * @param name - Name of the cron job.
96
- * @returns Cron job document.
97
- */
98
- export declare const getByName: import("convex/server").RegisteredQuery<"public", {
99
- name: string;
100
- }, Promise<{
101
- _id: import("convex/values").GenericId<"crons">;
102
- _creationTime: number;
103
- name?: string | undefined;
104
- schedulerJobId?: import("convex/values").GenericId<"_scheduled_functions"> | undefined;
105
- executionJobId?: import("convex/values").GenericId<"_scheduled_functions"> | undefined;
106
- functionHandle: string;
107
- args: any;
108
- schedule: {
109
- kind: "interval";
110
- ms: number;
111
- } | {
112
- kind: "cron";
113
- cronspec: string;
114
- };
115
- } | null>>;
116
- /**
117
- * Delete and deschedule a cron job by id.
118
- *
119
- * @param id - ID of the cron job.
120
- */
121
- export declare const del: import("convex/server").RegisteredMutation<"public", {
122
- id: import("convex/values").GenericId<"crons">;
123
- }, Promise<void>>;
124
- /**
125
- * Delete and deschedule a cron job by name.
126
- *
127
- * @param name - Name of the cron job.
128
- */
129
- export declare const delByName: import("convex/server").RegisteredMutation<"public", {
130
- name: string;
131
- }, Promise<void>>;
132
- export declare const rescheduler: import("convex/server").RegisteredMutation<"internal", {
133
- id: import("convex/values").GenericId<"crons">;
134
- }, Promise<void>>;
135
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/component/index.ts"],"names":[],"mappings":"AAgCA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,eAAO,MAAM,YAAY;;;;;uDAgBvB,CAAC;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB;;;;;uDAgB3B,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;KAKf,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,GAAG;;;;;;;;;;;;;;;;;UAOd,CAAC;AAEH;;;;;GAKG;AACH,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;UAUpB,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,GAAG;;iBAWd,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,SAAS;;iBAcpB,CAAC;AAiGH,eAAO,MAAM,WAAW;;iBA6EtB,CAAC"}