@automate.ax/catalog 0.75.0 → 0.77.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 (63) hide show
  1. package/dist/authoring.d.ts +1 -0
  2. package/dist/authoring.js +1 -0
  3. package/dist/catalog.d.ts +142 -74
  4. package/dist/catalog.js +74 -13
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.js +1 -1
  7. package/dist/triggers/airtable.js +1 -2
  8. package/dist/triggers/asana.js +1 -2
  9. package/dist/triggers/brevo.js +1 -2
  10. package/dist/triggers/core-dashboard.d.ts +374 -0
  11. package/dist/triggers/core-dashboard.js +202 -0
  12. package/dist/triggers/core-http.d.ts +30 -0
  13. package/dist/triggers/core-http.js +43 -0
  14. package/dist/triggers/core-invocation.d.ts +9 -0
  15. package/dist/triggers/core-invocation.js +15 -0
  16. package/dist/triggers/core-mailhook.d.ts +35 -0
  17. package/dist/triggers/core-mailhook.js +63 -0
  18. package/dist/triggers/core-schedule.d.ts +9 -0
  19. package/dist/triggers/core-schedule.js +17 -0
  20. package/dist/triggers/core.d.ts +15 -426
  21. package/dist/triggers/core.js +16 -346
  22. package/dist/triggers/github.js +4 -5
  23. package/dist/triggers/google.d.ts +143 -0
  24. package/dist/triggers/google.js +92 -5
  25. package/dist/triggers/index.d.ts +347 -74
  26. package/dist/triggers/index.js +2 -1
  27. package/dist/triggers/linear.js +1 -2
  28. package/dist/triggers/microsoft.js +3 -4
  29. package/dist/triggers/name.d.ts +13 -0
  30. package/dist/triggers/name.js +31 -0
  31. package/dist/triggers/resend.js +1 -2
  32. package/dist/triggers/slack.js +3 -4
  33. package/dist/triggers/trello.js +1 -2
  34. package/dist/triggers/vercel.d.ts +27 -27
  35. package/dist/triggers/vercel.js +10 -11
  36. package/dist/triggers/whatsapp.js +1 -2
  37. package/dist/types.d.ts +1 -1
  38. package/dist/types.js +3 -3
  39. package/package.json +118 -5
  40. package/src/authoring.ts +8 -0
  41. package/src/catalog.ts +76 -13
  42. package/src/index.ts +2 -0
  43. package/src/triggers/airtable.ts +1 -2
  44. package/src/triggers/asana.ts +1 -2
  45. package/src/triggers/brevo.ts +1 -2
  46. package/src/triggers/core-dashboard.ts +361 -0
  47. package/src/triggers/core-http.ts +69 -0
  48. package/src/triggers/core-invocation.ts +18 -0
  49. package/src/triggers/core-mailhook.ts +83 -0
  50. package/src/triggers/core-schedule.ts +21 -0
  51. package/src/triggers/core.ts +38 -544
  52. package/src/triggers/github.ts +4 -5
  53. package/src/triggers/google.ts +93 -5
  54. package/src/triggers/index.ts +2 -1
  55. package/src/triggers/linear.ts +1 -2
  56. package/src/triggers/microsoft.ts +3 -4
  57. package/src/triggers/name.ts +37 -0
  58. package/src/triggers/resend.ts +1 -2
  59. package/src/triggers/slack.ts +3 -4
  60. package/src/triggers/trello.ts +1 -2
  61. package/src/triggers/vercel.ts +10 -11
  62. package/src/triggers/whatsapp.ts +1 -2
  63. package/src/types.ts +4 -4
@@ -0,0 +1,202 @@
1
+ import { sentenceCase } from "./name.js";
2
+ import * as z from "zod/mini";
3
+ const EMAIL_SCHEMA = z.email();
4
+ const URL_SCHEMA = z.url();
5
+ const FIELD_NAME_SCHEMA = z
6
+ .string()
7
+ .check(z.trim(), z.minLength(1), z.maxLength(100));
8
+ const FIELD_LABEL_SCHEMA = z
9
+ .string()
10
+ .check(z.trim(), z.minLength(1), z.maxLength(255));
11
+ const FIELD_DESCRIPTION_SCHEMA = z.string().check(z.maxLength(2_000));
12
+ const FIELD_LENGTH_SCHEMA = z.int().check(z.nonnegative());
13
+ const DASHBOARD_RUN_FIELD_BASE_SCHEMA = {
14
+ description: z.optional(FIELD_DESCRIPTION_SCHEMA),
15
+ label: z.optional(FIELD_LABEL_SCHEMA),
16
+ name: FIELD_NAME_SCHEMA,
17
+ };
18
+ const DASHBOARD_RUN_TEXT_FIELD_SCHEMA = z.object({
19
+ ...DASHBOARD_RUN_FIELD_BASE_SCHEMA,
20
+ defaultValue: z.optional(z.string()),
21
+ maxLength: z.optional(FIELD_LENGTH_SCHEMA),
22
+ minLength: z.optional(FIELD_LENGTH_SCHEMA),
23
+ placeholder: z.optional(z.string()),
24
+ required: z.prefault(z.boolean(), true),
25
+ type: z.enum(["email", "phone", "text", "textarea", "url"]),
26
+ });
27
+ const DASHBOARD_RUN_NUMBER_FIELD_SCHEMA = z.object({
28
+ ...DASHBOARD_RUN_FIELD_BASE_SCHEMA,
29
+ defaultValue: z.optional(z.number()),
30
+ max: z.optional(z.number()),
31
+ min: z.optional(z.number()),
32
+ placeholder: z.optional(z.string()),
33
+ required: z.prefault(z.boolean(), true),
34
+ step: z.prefault(z.number().check(z.positive()), 1),
35
+ type: z.literal("number"),
36
+ });
37
+ const DASHBOARD_RUN_DATE_FIELD_SCHEMA = z.object({
38
+ ...DASHBOARD_RUN_FIELD_BASE_SCHEMA,
39
+ defaultValue: z.optional(z.iso.date()),
40
+ max: z.optional(z.iso.date()),
41
+ min: z.optional(z.iso.date()),
42
+ required: z.prefault(z.boolean(), true),
43
+ step: z.prefault(z.int().check(z.positive()), 1),
44
+ type: z.literal("date"),
45
+ });
46
+ const DASHBOARD_RUN_DATETIME_FIELD_SCHEMA = z.object({
47
+ ...DASHBOARD_RUN_FIELD_BASE_SCHEMA,
48
+ defaultValue: z.optional(z.iso.datetime({ local: true })),
49
+ max: z.optional(z.iso.datetime({ local: true })),
50
+ min: z.optional(z.iso.datetime({ local: true })),
51
+ required: z.prefault(z.boolean(), true),
52
+ step: z.prefault(z.number().check(z.positive()), 60),
53
+ type: z.literal("datetime"),
54
+ });
55
+ const DASHBOARD_RUN_CHOICE_FIELD_SCHEMA = {
56
+ ...DASHBOARD_RUN_FIELD_BASE_SCHEMA,
57
+ defaultValue: z.optional(FIELD_NAME_SCHEMA),
58
+ options: z
59
+ .array(z.object({
60
+ label: FIELD_LABEL_SCHEMA,
61
+ value: FIELD_NAME_SCHEMA,
62
+ }))
63
+ .check(z.minLength(1)),
64
+ placeholder: z.optional(z.string()),
65
+ required: z.prefault(z.boolean(), true),
66
+ };
67
+ const DASHBOARD_RUN_SELECT_FIELD_SCHEMA = z.object({
68
+ ...DASHBOARD_RUN_CHOICE_FIELD_SCHEMA,
69
+ type: z.literal("select"),
70
+ });
71
+ const DASHBOARD_RUN_RADIO_FIELD_SCHEMA = z.object({
72
+ ...DASHBOARD_RUN_CHOICE_FIELD_SCHEMA,
73
+ type: z.literal("radio"),
74
+ });
75
+ const DASHBOARD_RUN_MULTI_SELECT_FIELD_SCHEMA = z.object({
76
+ ...DASHBOARD_RUN_FIELD_BASE_SCHEMA,
77
+ defaultValue: z.optional(z.array(FIELD_NAME_SCHEMA)),
78
+ options: z
79
+ .array(z.object({
80
+ label: FIELD_LABEL_SCHEMA,
81
+ value: FIELD_NAME_SCHEMA,
82
+ }))
83
+ .check(z.minLength(1)),
84
+ placeholder: z.optional(z.string()),
85
+ required: z.prefault(z.boolean(), true),
86
+ type: z.literal("multi-select"),
87
+ });
88
+ const DASHBOARD_RUN_CHECKBOX_FIELD_SCHEMA = z.object({
89
+ ...DASHBOARD_RUN_FIELD_BASE_SCHEMA,
90
+ defaultValue: z.prefault(z.boolean(), false),
91
+ required: z.prefault(z.boolean(), false),
92
+ type: z.literal("checkbox"),
93
+ });
94
+ const DASHBOARD_RUN_FIELD_SCHEMA = z.pipe(z.discriminatedUnion("type", [
95
+ DASHBOARD_RUN_TEXT_FIELD_SCHEMA,
96
+ DASHBOARD_RUN_NUMBER_FIELD_SCHEMA,
97
+ DASHBOARD_RUN_DATE_FIELD_SCHEMA,
98
+ DASHBOARD_RUN_DATETIME_FIELD_SCHEMA,
99
+ DASHBOARD_RUN_SELECT_FIELD_SCHEMA,
100
+ DASHBOARD_RUN_RADIO_FIELD_SCHEMA,
101
+ DASHBOARD_RUN_MULTI_SELECT_FIELD_SCHEMA,
102
+ DASHBOARD_RUN_CHECKBOX_FIELD_SCHEMA,
103
+ ]), z.transform((field) => ({
104
+ ...field,
105
+ label: field.label ??
106
+ `${sentenceCase(field.name)}${field.type === "checkbox" ? "?" : ""}`,
107
+ })));
108
+ const DASHBOARD_RUN_FIELDS_SCHEMA = z.array(DASHBOARD_RUN_FIELD_SCHEMA).check(z.refine((fields) => new Set(fields.map((field) => field.name)).size === fields.length, "Dashboard run field names must be unique."), z.refine((fields) => fields.every(isDashboardRunFieldDefaultValid), "Dashboard run field defaults must satisfy their field constraints."));
109
+ export const DASHBOARD_RUN_CONFIG_SCHEMA = z.object({
110
+ description: z.optional(FIELD_DESCRIPTION_SCHEMA),
111
+ fields: z.prefault(DASHBOARD_RUN_FIELDS_SCHEMA, []),
112
+ submitLabel: z.prefault(z.string().check(z.trim(), z.minLength(1), z.maxLength(100)), "Run"),
113
+ title: z.string().check(z.trim(), z.minLength(1), z.maxLength(255)),
114
+ });
115
+ export const dashboardRunTriggerDefinition = {
116
+ getDetails: ({ config }) => [
117
+ {
118
+ label: "Run form",
119
+ value: DASHBOARD_RUN_CONFIG_SCHEMA.parse(config).title,
120
+ },
121
+ ],
122
+ icon: "automation",
123
+ name: "Dashboard run",
124
+ type: "dashboard.run",
125
+ };
126
+ /**
127
+ * Returns the organization-authenticated submission endpoint for one dashboard
128
+ * run.
129
+ *
130
+ * @param options - Public origin and trigger identity.
131
+ */
132
+ export function getDashboardRunEndpoint(options) {
133
+ return new URL(`/api/dashboard-runs/${getDashboardRunEndpointKey(options)}`, options.appOrigin).toString();
134
+ }
135
+ /**
136
+ * Returns the durable endpoint identity for one dashboard run trigger.
137
+ *
138
+ * @param options - Trigger and owning automation identity.
139
+ */
140
+ export function getDashboardRunEndpointKey(options) {
141
+ return `${options.automationId}:${[
142
+ ...(options.scopePath ?? []),
143
+ options.hookSlot,
144
+ ].join(".")}`;
145
+ }
146
+ /**
147
+ * Checks a configured default against the same constraints as submitted data.
148
+ *
149
+ * @param field - Field definition to validate.
150
+ */
151
+ function isDashboardRunFieldDefaultValid(field) {
152
+ if (field.type === "checkbox" || field.defaultValue === undefined)
153
+ return true;
154
+ if (field.type === "multi-select") {
155
+ if (field.required !== false && field.defaultValue.length === 0)
156
+ return false;
157
+ return field.defaultValue.every((value) => field.options.some((option) => option.value === value));
158
+ }
159
+ if (field.type === "radio" || field.type === "select") {
160
+ return field.options.some((option) => option.value === field.defaultValue);
161
+ }
162
+ if (field.type === "number") {
163
+ return ((field.min === undefined || field.defaultValue >= field.min) &&
164
+ (field.max === undefined || field.defaultValue <= field.max) &&
165
+ !isDashboardRunStepMismatch(field.defaultValue, field.min ?? field.defaultValue, field.step ?? 1));
166
+ }
167
+ if (field.type === "date" || field.type === "datetime") {
168
+ const defaultValue = parseDashboardRunDateValue(field.type, field.defaultValue);
169
+ return ((field.min === undefined ||
170
+ defaultValue >= parseDashboardRunDateValue(field.type, field.min)) &&
171
+ (field.max === undefined ||
172
+ defaultValue <= parseDashboardRunDateValue(field.type, field.max)) &&
173
+ !isDashboardRunStepMismatch(defaultValue, parseDashboardRunDateValue(field.type, field.min ?? field.defaultValue), (field.step ?? (field.type === "date" ? 1 : 60)) *
174
+ (field.type === "date" ? 86_400_000 : 1_000)));
175
+ }
176
+ const defaultValue = field.defaultValue.trim();
177
+ return ((defaultValue.length > 0 || field.required === false) &&
178
+ (field.minLength === undefined || defaultValue.length >= field.minLength) &&
179
+ (field.maxLength === undefined || defaultValue.length <= field.maxLength) &&
180
+ (field.type !== "email" || EMAIL_SCHEMA.safeParse(defaultValue).success) &&
181
+ (field.type !== "url" || URL_SCHEMA.safeParse(defaultValue).success));
182
+ }
183
+ /**
184
+ * Checks whether a value falls outside an allowed step interval.
185
+ *
186
+ * @param value - Configured default value.
187
+ * @param base - Value from which step intervals begin.
188
+ * @param step - Allowed interval size.
189
+ */
190
+ function isDashboardRunStepMismatch(value, base, step) {
191
+ const intervals = (value - base) / step;
192
+ return Math.abs(intervals - Math.round(intervals)) > 1e-9;
193
+ }
194
+ /**
195
+ * Converts a validated local date or date-time string to a UTC number.
196
+ *
197
+ * @param type - Temporal field type.
198
+ * @param value - Local ISO value to convert.
199
+ */
200
+ function parseDashboardRunDateValue(type, value) {
201
+ return Date.parse(type === "date" ? `${value}T00:00:00Z` : `${value}Z`);
202
+ }
@@ -0,0 +1,30 @@
1
+ export declare const httpRequestTriggerDefinition: {
2
+ readonly getPrimary: ({ appOrigin, automationId, config, hookSlot, projectId, scopePath, }: import("..").TriggerPresentationContext) => {
3
+ copyable: true;
4
+ value: string;
5
+ };
6
+ readonly icon: "webhook";
7
+ readonly name: "HTTP request";
8
+ readonly type: "http.request";
9
+ };
10
+ export type HttpTriggerScope = "trigger" | "automation" | "project";
11
+ export interface HttpTriggerEndpointOptions {
12
+ appOrigin: string;
13
+ automationId: string;
14
+ hookSlot: number;
15
+ projectId: string;
16
+ scope: HttpTriggerScope;
17
+ scopePath?: readonly number[];
18
+ }
19
+ /**
20
+ * Returns the public URL for one deployed HTTP trigger.
21
+ *
22
+ * @param options - Public origin and trigger identity.
23
+ */
24
+ export declare function getHttpTriggerEndpoint(options: HttpTriggerEndpointOptions): string;
25
+ /**
26
+ * Returns the durable endpoint identity for one configured HTTP scope.
27
+ *
28
+ * @param options - Trigger and owning resource identity.
29
+ */
30
+ export declare function getHttpTriggerEndpointKey(options: Omit<HttpTriggerEndpointOptions, "appOrigin">): string;
@@ -0,0 +1,43 @@
1
+ import * as z from "zod/mini";
2
+ const HTTP_PRESENTATION_CONFIG_SCHEMA = z.object({
3
+ scope: z.prefault(z.enum(["trigger", "automation", "project"]), "trigger"),
4
+ });
5
+ export const httpRequestTriggerDefinition = {
6
+ getPrimary: ({ appOrigin, automationId, config, hookSlot, projectId, scopePath, }) => ({
7
+ copyable: true,
8
+ value: getHttpTriggerEndpoint({
9
+ appOrigin,
10
+ automationId,
11
+ hookSlot,
12
+ projectId,
13
+ scope: HTTP_PRESENTATION_CONFIG_SCHEMA.parse(config).scope,
14
+ scopePath,
15
+ }),
16
+ }),
17
+ icon: "webhook",
18
+ name: "HTTP request",
19
+ type: "http.request",
20
+ };
21
+ /**
22
+ * Returns the public URL for one deployed HTTP trigger.
23
+ *
24
+ * @param options - Public origin and trigger identity.
25
+ */
26
+ export function getHttpTriggerEndpoint(options) {
27
+ return new URL(`/x/${getHttpTriggerEndpointKey(options)}/`, options.appOrigin).toString();
28
+ }
29
+ /**
30
+ * Returns the durable endpoint identity for one configured HTTP scope.
31
+ *
32
+ * @param options - Trigger and owning resource identity.
33
+ */
34
+ export function getHttpTriggerEndpointKey(options) {
35
+ if (options.scope === "project")
36
+ return options.projectId;
37
+ if (options.scope === "automation")
38
+ return options.automationId;
39
+ return `${options.automationId}:${[
40
+ ...(options.scopePath ?? []),
41
+ options.hookSlot,
42
+ ].join(".")}`;
43
+ }
@@ -0,0 +1,9 @@
1
+ export declare const automationInvokedTriggerDefinition: {
2
+ readonly getDetails: ({ config }: import("..").TriggerPresentationContext) => {
3
+ label: string;
4
+ value: string;
5
+ }[];
6
+ readonly icon: "automation";
7
+ readonly name: "Invocation";
8
+ readonly type: "automation.invoked";
9
+ };
@@ -0,0 +1,15 @@
1
+ import * as z from "zod/mini";
2
+ const INVOCATION_PRESENTATION_CONFIG_SCHEMA = z.object({
3
+ entrypoint: z.string(),
4
+ });
5
+ export const automationInvokedTriggerDefinition = {
6
+ getDetails: ({ config }) => [
7
+ {
8
+ label: "Entrypoint",
9
+ value: INVOCATION_PRESENTATION_CONFIG_SCHEMA.parse(config).entrypoint,
10
+ },
11
+ ],
12
+ icon: "automation",
13
+ name: "Invocation",
14
+ type: "automation.invoked",
15
+ };
@@ -0,0 +1,35 @@
1
+ import type { HttpTriggerScope } from "./core-http.js";
2
+ export declare const PLATFORM_EMAIL_DOMAIN = "automations.automate.ax";
3
+ export declare const mailhookEmailReceivedTriggerDefinition: {
4
+ readonly getPrimary: ({ automationId, config, hookSlot, projectId, scopePath }: import("..").TriggerPresentationContext) => {
5
+ copyable: true;
6
+ value: string;
7
+ };
8
+ readonly icon: "webhook";
9
+ readonly name: "Mailhook";
10
+ readonly type: "mailhook.email.received";
11
+ };
12
+ export type MailhookScope = HttpTriggerScope;
13
+ export interface MailhookAddressOptions {
14
+ automationId: string;
15
+ hookSlot: number;
16
+ plusPath?: string;
17
+ projectId: string;
18
+ scope: MailhookScope;
19
+ scopePath?: readonly number[];
20
+ }
21
+ /**
22
+ * Returns the unique inbound address for one deployed mailhook.
23
+ *
24
+ * Add `+suffix` before the `@` to carry a routing value into the event.
25
+ *
26
+ * @param options - Mailhook identity and sharing scope.
27
+ * @throws When the generated local part exceeds the email size limit.
28
+ */
29
+ export declare function getMailhookAddress(options: MailhookAddressOptions): string;
30
+ /**
31
+ * Returns the local-part router key for one configured mailhook scope.
32
+ *
33
+ * @param options - Mailhook identity and sharing scope.
34
+ */
35
+ export declare function getMailhookAddressKey(options: MailhookAddressOptions): string;
@@ -0,0 +1,63 @@
1
+ import * as z from "zod/mini";
2
+ const MAX_EMAIL_LOCAL_PART_BYTES = 64;
3
+ const EMAIL_SCHEMA = z.email();
4
+ const MAILHOOK_PRESENTATION_CONFIG_SCHEMA = z.object({
5
+ scope: z.prefault(z.enum(["trigger", "automation", "project"]), "trigger"),
6
+ });
7
+ export const PLATFORM_EMAIL_DOMAIN = "automations.automate.ax";
8
+ export const mailhookEmailReceivedTriggerDefinition = {
9
+ getPrimary: ({ automationId, config, hookSlot, projectId, scopePath }) => ({
10
+ copyable: true,
11
+ value: getMailhookAddress({
12
+ automationId,
13
+ hookSlot,
14
+ projectId,
15
+ scope: MAILHOOK_PRESENTATION_CONFIG_SCHEMA.parse(config).scope,
16
+ scopePath,
17
+ }),
18
+ }),
19
+ icon: "webhook",
20
+ name: "Mailhook",
21
+ type: "mailhook.email.received",
22
+ };
23
+ /**
24
+ * Returns the unique inbound address for one deployed mailhook.
25
+ *
26
+ * Add `+suffix` before the `@` to carry a routing value into the event.
27
+ *
28
+ * @param options - Mailhook identity and sharing scope.
29
+ * @throws When the generated local part exceeds the email size limit.
30
+ */
31
+ export function getMailhookAddress(options) {
32
+ const localPart = `${getMailhookAddressKey(options)}${options.plusPath ? `+${options.plusPath}` : ""}`;
33
+ if (new TextEncoder().encode(localPart).length > MAX_EMAIL_LOCAL_PART_BYTES) {
34
+ throw new RangeError("Mailhook address local part exceeds 64 bytes.");
35
+ }
36
+ return EMAIL_SCHEMA.parse(`${localPart}@${PLATFORM_EMAIL_DOMAIN}`);
37
+ }
38
+ /**
39
+ * Returns the local-part router key for one configured mailhook scope.
40
+ *
41
+ * @param options - Mailhook identity and sharing scope.
42
+ */
43
+ export function getMailhookAddressKey(options) {
44
+ if (options.scope === "project")
45
+ return `p${typeIdSuffix(options.projectId)}`;
46
+ if (options.scope === "automation") {
47
+ return `a${typeIdSuffix(options.automationId)}`;
48
+ }
49
+ return `a${typeIdSuffix(options.automationId)}.${[
50
+ ...(options.scopePath ?? []),
51
+ options.hookSlot,
52
+ ]
53
+ .map((segment) => segment.toString(36))
54
+ .join(".")}`;
55
+ }
56
+ /**
57
+ * Removes the redundant TypeID prefix from an address router.
58
+ *
59
+ * @param id - Project or automation TypeID.
60
+ */
61
+ function typeIdSuffix(id) {
62
+ return id.includes("_") ? id.slice(id.indexOf("_") + 1) : id;
63
+ }
@@ -0,0 +1,9 @@
1
+ export declare const cronTickTriggerDefinition: {
2
+ readonly getDetails: ({ config }: import("..").TriggerPresentationContext) => {
3
+ label: string;
4
+ value: string;
5
+ }[];
6
+ readonly icon: "schedule";
7
+ readonly name: "Cron schedule";
8
+ readonly type: "cron.tick";
9
+ };
@@ -0,0 +1,17 @@
1
+ import * as z from "zod/mini";
2
+ const CRON_PRESENTATION_CONFIG_SCHEMA = z.object({
3
+ schedule: z.string(),
4
+ timeZone: z.prefault(z.string(), "UTC"),
5
+ });
6
+ export const cronTickTriggerDefinition = {
7
+ getDetails: ({ config }) => {
8
+ const parsedConfig = CRON_PRESENTATION_CONFIG_SCHEMA.parse(config);
9
+ return [
10
+ { label: "Schedule", value: parsedConfig.schedule },
11
+ { label: "Time zone", value: parsedConfig.timeZone },
12
+ ];
13
+ },
14
+ icon: "schedule",
15
+ name: "Cron schedule",
16
+ type: "cron.tick",
17
+ };