wrangler 0.0.0-e7cf0caa → 0.0.0-e87ad068

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 (85) hide show
  1. package/miniflare-dist/index.mjs +167 -93
  2. package/package.json +6 -6
  3. package/src/__tests__/configuration.test.ts +218 -0
  4. package/src/__tests__/constellation.test.ts +371 -0
  5. package/src/__tests__/d1/migrate.test.ts +100 -0
  6. package/src/__tests__/index.test.ts +4 -2
  7. package/src/__tests__/init.test.ts +109 -3
  8. package/src/__tests__/mtls-certificates.test.ts +2 -1
  9. package/src/__tests__/pages/functions-build.test.ts +77 -0
  10. package/src/__tests__/pages-deployment-tail.test.ts +2 -0
  11. package/src/__tests__/publish.test.ts +646 -187
  12. package/src/__tests__/tail.test.ts +49 -7
  13. package/src/__tests__/tsconfig.tsbuildinfo +1 -1
  14. package/src/__tests__/unstableDev.test.ts +61 -0
  15. package/src/__tests__/user.test.ts +1 -1
  16. package/src/api/dev.ts +12 -0
  17. package/src/api/pages/create-worker-bundle-contents.ts +4 -0
  18. package/src/api/pages/publish.tsx +28 -12
  19. package/src/bundle.ts +58 -14
  20. package/src/cfetch/internal.ts +19 -14
  21. package/src/config/environment.ts +41 -0
  22. package/src/config/index.ts +21 -0
  23. package/src/config/validation.ts +182 -0
  24. package/src/constellation/createProject.tsx +51 -0
  25. package/src/constellation/deleteProject.ts +51 -0
  26. package/src/constellation/deleteProjectModel.ts +76 -0
  27. package/src/constellation/index.ts +75 -0
  28. package/src/constellation/listCatalog.tsx +35 -0
  29. package/src/constellation/listModel.tsx +49 -0
  30. package/src/constellation/listProject.tsx +28 -0
  31. package/src/constellation/listRuntime.tsx +28 -0
  32. package/src/constellation/options.ts +17 -0
  33. package/src/constellation/types.ts +17 -0
  34. package/src/constellation/uploadModel.tsx +84 -0
  35. package/src/constellation/utils.ts +90 -0
  36. package/src/create-worker-upload-form.ts +25 -0
  37. package/src/d1/backups.tsx +4 -4
  38. package/src/d1/constants.ts +3 -0
  39. package/src/d1/create.tsx +77 -43
  40. package/src/d1/delete.ts +1 -1
  41. package/src/d1/execute.tsx +27 -13
  42. package/src/d1/list.tsx +13 -11
  43. package/src/d1/migrations/apply.tsx +29 -6
  44. package/src/d1/migrations/helpers.ts +8 -9
  45. package/src/d1/types.ts +7 -0
  46. package/src/dev/dev.tsx +9 -2
  47. package/src/dev/local.tsx +52 -20
  48. package/src/dev/remote.tsx +4 -0
  49. package/src/dev/start-server.ts +64 -40
  50. package/src/dev/use-esbuild.ts +70 -43
  51. package/src/dev-registry.ts +11 -0
  52. package/src/dev.tsx +23 -6
  53. package/src/docs/helpers.ts +50 -0
  54. package/src/docs/index.ts +25 -68
  55. package/src/entry.ts +8 -1
  56. package/src/environment-variables/factory.ts +2 -1
  57. package/src/generate/index.ts +5 -0
  58. package/src/index.ts +12 -1
  59. package/src/init.ts +52 -10
  60. package/src/inspect.ts +107 -6
  61. package/src/kv/helpers.ts +7 -4
  62. package/src/logger.ts +5 -1
  63. package/src/miniflare-cli/index.ts +2 -2
  64. package/src/miniflare-cli/tsconfig.tsbuildinfo +1 -1
  65. package/src/module-collection.ts +1 -1
  66. package/src/pages/build.ts +30 -17
  67. package/src/pages/dev.ts +51 -8
  68. package/src/pages/functions/buildPlugin.ts +1 -0
  69. package/src/pages/functions/buildWorker.ts +92 -2
  70. package/src/pages/functions/tsconfig.tsbuildinfo +1 -1
  71. package/src/publish/publish.ts +9 -1
  72. package/src/secret/index.ts +4 -0
  73. package/src/sites.ts +164 -52
  74. package/src/tail/createTail.ts +9 -0
  75. package/src/tail/printing.ts +12 -0
  76. package/src/traverse-module-graph.ts +2 -1
  77. package/src/type-generation.ts +6 -0
  78. package/src/user/user.ts +1 -0
  79. package/src/utils/render.ts +1 -1
  80. package/src/worker.ts +26 -0
  81. package/templates/__tests__/tsconfig.tsbuildinfo +1 -1
  82. package/templates/pages-dev-util.ts +4 -1
  83. package/templates/tsconfig.tsbuildinfo +1 -1
  84. package/wrangler-dist/cli.d.ts +78 -2
  85. package/wrangler-dist/cli.js +65057 -63883
@@ -1,8 +1,13 @@
1
1
  import { cwd } from "process";
2
+ import { rest } from "msw";
2
3
  import { reinitialiseAuthTokens } from "../../user";
3
4
  import { mockAccountId, mockApiToken } from "../helpers/mock-account-id";
4
5
  import { mockConsoleMethods } from "../helpers/mock-console";
6
+ import { mockConfirm } from "../helpers/mock-dialogs";
5
7
  import { useMockIsTTY } from "../helpers/mock-istty";
8
+ import { mockGetMemberships, mockOAuthFlow } from "../helpers/mock-oauth-flow";
9
+ import { mockSetTimeout } from "../helpers/mock-set-timeout";
10
+ import { msw } from "../helpers/msw";
6
11
  import { runInTempDir } from "../helpers/run-in-tmp";
7
12
  import { runWrangler } from "../helpers/run-wrangler";
8
13
  import writeWranglerToml from "../helpers/write-wrangler-toml";
@@ -10,6 +15,8 @@ import writeWranglerToml from "../helpers/write-wrangler-toml";
10
15
  describe("migrate", () => {
11
16
  runInTempDir();
12
17
  mockConsoleMethods();
18
+ mockSetTimeout();
19
+
13
20
  const { setIsTTY } = useMockIsTTY();
14
21
 
15
22
  describe("create", () => {
@@ -28,6 +35,9 @@ describe("migrate", () => {
28
35
  });
29
36
 
30
37
  describe("apply", () => {
38
+ mockAccountId({ accountId: null });
39
+ mockApiToken();
40
+ const { mockOAuthServerCallback } = mockOAuthFlow();
31
41
  it("should not attempt to login in local mode", async () => {
32
42
  setIsTTY(false);
33
43
  writeWranglerToml({
@@ -77,6 +87,96 @@ describe("migrate", () => {
77
87
  runWrangler("d1 migrations apply --local db --preview")
78
88
  ).rejects.toThrowError(`Error: can't use --preview with --local`);
79
89
  });
90
+
91
+ it("multiple accounts: should throw when trying to apply migrations without an account_id in config", async () => {
92
+ setIsTTY(false);
93
+
94
+ writeWranglerToml({
95
+ d1_databases: [
96
+ {
97
+ binding: "DATABASE",
98
+ database_name: "db",
99
+ database_id: "xxxx",
100
+ migrations_dir: "/tmp/my-migrations-go-here",
101
+ },
102
+ ],
103
+ });
104
+ mockOAuthServerCallback();
105
+ mockGetMemberships([
106
+ { id: "IG-88", account: { id: "1701", name: "enterprise" } },
107
+ { id: "R2-D2", account: { id: "nx01", name: "enterprise-nx" } },
108
+ ]);
109
+ mockConfirm({
110
+ text: `No migrations folder found.
111
+ Ok to create /tmp/my-migrations-go-here?`,
112
+ result: true,
113
+ });
114
+ await runWrangler("d1 migrations create db test");
115
+ mockConfirm({
116
+ text: `About to apply 1 migration(s)
117
+ Your database may not be available to serve requests during the migration, continue?`,
118
+ result: true,
119
+ });
120
+ await expect(runWrangler("d1 migrations apply db")).rejects.toThrowError(
121
+ `More than one account available but unable to select one in non-interactive mode.`
122
+ );
123
+ });
124
+ it("multiple accounts: should let the user apply migrations with an account_id in config", async () => {
125
+ setIsTTY(false);
126
+ msw.use(
127
+ rest.post(
128
+ "*/accounts/:accountId/d1/database/:databaseId/query",
129
+ async (req, res, ctx) => {
130
+ return res(
131
+ ctx.status(200),
132
+ ctx.json({
133
+ result: [
134
+ {
135
+ results: [],
136
+ success: true,
137
+ meta: {},
138
+ },
139
+ ],
140
+ success: true,
141
+ errors: [],
142
+ messages: [],
143
+ })
144
+ );
145
+ }
146
+ )
147
+ );
148
+ writeWranglerToml({
149
+ d1_databases: [
150
+ {
151
+ binding: "DATABASE",
152
+ database_name: "db",
153
+ database_id: "xxxx",
154
+ migrations_dir: "/tmp/my-migrations-go-here",
155
+ },
156
+ ],
157
+ account_id: "nx01",
158
+ });
159
+ mockOAuthServerCallback();
160
+ mockGetMemberships([
161
+ { id: "IG-88", account: { id: "1701", name: "enterprise" } },
162
+ { id: "R2-D2", account: { id: "nx01", name: "enterprise-nx" } },
163
+ ]);
164
+ mockConfirm({
165
+ text: `No migrations folder found.
166
+ Ok to create /tmp/my-migrations-go-here?`,
167
+ result: true,
168
+ });
169
+ await runWrangler("d1 migrations create db test");
170
+ mockConfirm({
171
+ text: `About to apply 1 migration(s)
172
+ Your database may not be available to serve requests during the migration, continue?`,
173
+ result: true,
174
+ });
175
+ //if we get to this point, wrangler knows the account_id
176
+ await expect(runWrangler("d1 migrations apply db")).rejects.toThrowError(
177
+ `request to https://api.cloudflare.com/client/v4/accounts/nx01/d1/database/xxxx/backup failed`
178
+ );
179
+ });
80
180
  });
81
181
 
82
182
  describe("list", () => {
@@ -32,7 +32,7 @@ describe("wrangler", () => {
32
32
  "wrangler
33
33
 
34
34
  Commands:
35
- wrangler docs [command] 📚 Open wrangler's docs in your browser
35
+ wrangler docs [command..] 📚 Open wrangler's docs in your browser
36
36
  wrangler init [name] 📥 Initialize a basic Worker project, including a wrangler.toml file
37
37
  wrangler generate [name] [template] ✨ Generate a new Worker project from an existing Worker template. See https://github.com/cloudflare/templates
38
38
  wrangler dev [script] 👂 Start a local server for developing your worker
@@ -49,6 +49,7 @@ describe("wrangler", () => {
49
49
  wrangler r2 📦 Interact with an R2 store
50
50
  wrangler dispatch-namespace 📦 Interact with a dispatch namespace
51
51
  wrangler d1 🗄 Interact with a D1 database
52
+ wrangler constellation 🤖 Interact with Constellation models
52
53
  wrangler pubsub 📮 Interact and manage Pub/Sub Brokers
53
54
  wrangler mtls-certificate 🪪 Manage certificates used for mTLS connections
54
55
  wrangler login 🔓 Login to Cloudflare
@@ -85,7 +86,7 @@ describe("wrangler", () => {
85
86
  wrangler
86
87
 
87
88
  Commands:
88
- wrangler docs [command] 📚 Open wrangler's docs in your browser
89
+ wrangler docs [command..] 📚 Open wrangler's docs in your browser
89
90
  wrangler init [name] 📥 Initialize a basic Worker project, including a wrangler.toml file
90
91
  wrangler generate [name] [template] ✨ Generate a new Worker project from an existing Worker template. See https://github.com/cloudflare/templates
91
92
  wrangler dev [script] 👂 Start a local server for developing your worker
@@ -102,6 +103,7 @@ describe("wrangler", () => {
102
103
  wrangler r2 📦 Interact with an R2 store
103
104
  wrangler dispatch-namespace 📦 Interact with a dispatch namespace
104
105
  wrangler d1 🗄 Interact with a D1 database
106
+ wrangler constellation 🤖 Interact with Constellation models
105
107
  wrangler pubsub 📮 Interact and manage Pub/Sub Brokers
106
108
  wrangler mtls-certificate 🪪 Manage certificates used for mTLS connections
107
109
  wrangler login 🔓 Login to Cloudflare
@@ -4,6 +4,7 @@ import * as TOML from "@iarna/toml";
4
4
  import { execa, execaSync } from "execa";
5
5
  import { rest } from "msw";
6
6
  import { parseConfigFileTextToJson } from "typescript";
7
+ import { FormData } from "undici";
7
8
  import { version as wranglerVersion } from "../../package.json";
8
9
  import { getPackageManager } from "../package-manager";
9
10
  import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
@@ -2235,6 +2236,12 @@ describe("init", () => {
2235
2236
  },
2236
2237
  };
2237
2238
  `;
2239
+ afterEach(() => {
2240
+ // some test has a side-effect which is overwriting the compatibility_date
2241
+ mockServiceMetadata.default_environment.script.compatibility_date =
2242
+ "1987-9-27";
2243
+ mockConfigExpected.compatibility_date = "1987-9-27";
2244
+ });
2238
2245
  const mockServiceMetadata = {
2239
2246
  id: "memory-crystal",
2240
2247
  default_environment: {
@@ -2251,6 +2258,7 @@ describe("init", () => {
2251
2258
  migration_tag: "some-migration-tag",
2252
2259
  usage_model: "bundled",
2253
2260
  compatibility_date: "1987-9-27",
2261
+ tail_consumers: [{ service: "listener" }],
2254
2262
  },
2255
2263
  },
2256
2264
  created_on: "1987-9-27",
@@ -2452,6 +2460,7 @@ describe("init", () => {
2452
2460
  },
2453
2461
  ],
2454
2462
  },
2463
+ tail_consumers: [{ service: "listener" }],
2455
2464
  };
2456
2465
 
2457
2466
  function mockSupportingDashRequests({
@@ -2701,6 +2710,9 @@ describe("init", () => {
2701
2710
  test = { }
2702
2711
  staging = { }
2703
2712
 
2713
+ [[tail_consumers]]
2714
+ service = \\"listener\\"
2715
+
2704
2716
  [vars]
2705
2717
  ANOTHER-NAME = \\"thing-TEXT\\"
2706
2718
 
@@ -2945,6 +2957,7 @@ describe("init", () => {
2945
2957
  created_on: "1988-08-07",
2946
2958
  usage_model: "bundled",
2947
2959
  compatibility_date: "1988-08-07",
2960
+ tail_consumers: [{ service: "listener" }],
2948
2961
  },
2949
2962
  },
2950
2963
  environments: [],
@@ -3001,7 +3014,7 @@ describe("init", () => {
3001
3014
  success: true,
3002
3015
  errors: [],
3003
3016
  messages: [],
3004
- result: mockServiceMetadata.default_environment,
3017
+ result: mockData.default_environment,
3005
3018
  })
3006
3019
  );
3007
3020
  }
@@ -3053,6 +3066,7 @@ describe("init", () => {
3053
3066
  },
3054
3067
  usage_model: "bundled",
3055
3068
  name: "isolinear-optical-chip",
3069
+ tail_consumers: [{ service: "listener" }],
3056
3070
  }),
3057
3071
  },
3058
3072
  });
@@ -3074,6 +3088,72 @@ describe("init", () => {
3074
3088
  },
3075
3089
  });
3076
3090
  });
3091
+
3092
+ it("should download multi-module source scripts from dashboard", async () => {
3093
+ mockSupportingDashRequests({
3094
+ expectedAccountId: "LCARS",
3095
+ expectedScriptName: "isolinear-optical-chip",
3096
+ expectedEnvironment: "test",
3097
+ expectedCompatDate: "1987-9-27",
3098
+ });
3099
+ const indexjs = `
3100
+ import handleRequest from './handleRequest.js';
3101
+
3102
+ export default {
3103
+ async fetch(request, env, ctx) {
3104
+ return handleRequest(request, env, ctx);
3105
+ },
3106
+ };
3107
+ `;
3108
+ const otherjs = `
3109
+ export default function (request, env, ctx) {
3110
+ return new Response("Hello World!");
3111
+ }
3112
+ `;
3113
+ setMockFetchDashScript([
3114
+ { name: "index.js", contents: indexjs },
3115
+ { name: "nested/other.js", contents: otherjs },
3116
+ ]);
3117
+ mockConfirm(
3118
+ {
3119
+ text: "Would you like to use git to manage this Worker?",
3120
+ result: false,
3121
+ },
3122
+ {
3123
+ text: "No package.json found. Would you like to create one?",
3124
+ result: true,
3125
+ },
3126
+ {
3127
+ text: "Would you like to use TypeScript?",
3128
+ result: false,
3129
+ }
3130
+ );
3131
+
3132
+ await runWrangler("init --from-dash isolinear-optical-chip");
3133
+
3134
+ checkFiles({
3135
+ items: {
3136
+ "isolinear-optical-chip/src/index.js": {
3137
+ contents: indexjs,
3138
+ },
3139
+ "isolinear-optical-chip/src/nested/other.js": {
3140
+ contents: otherjs,
3141
+ },
3142
+ "isolinear-optical-chip/src/index.ts": false,
3143
+ "isolinear-optical-chip/package.json": {
3144
+ contents: expect.objectContaining({
3145
+ name: "isolinear-optical-chip",
3146
+ }),
3147
+ },
3148
+ "isolinear-optical-chip/tsconfig.json": false,
3149
+ "isolinear-optical-chip/wrangler.toml": wranglerToml({
3150
+ ...mockConfigExpected,
3151
+ name: "isolinear-optical-chip",
3152
+ main: "src/index.js",
3153
+ }),
3154
+ },
3155
+ });
3156
+ });
3077
3157
  });
3078
3158
  });
3079
3159
 
@@ -3095,12 +3175,38 @@ function getDefaultBranchName() {
3095
3175
  /**
3096
3176
  * Mock setter for usage within test blocks for dashboard script
3097
3177
  */
3098
- export function setMockFetchDashScript(mockResponse: string) {
3178
+ export function setMockFetchDashScript(
3179
+ mockResponse: string | { name: string; contents: string }[]
3180
+ ) {
3099
3181
  msw.use(
3100
3182
  rest.get(
3101
3183
  `*/accounts/:accountId/workers/services/:fromDashScriptName/environments/:environment/content`,
3102
3184
  (_, res, ctx) => {
3103
- return res(ctx.text(mockResponse));
3185
+ if (typeof mockResponse === "string") {
3186
+ return res(ctx.text(mockResponse));
3187
+ }
3188
+
3189
+ const fd = new FormData();
3190
+
3191
+ for (const { name, contents } of mockResponse) {
3192
+ fd.set(name, contents);
3193
+ }
3194
+
3195
+ const boundary = "--------boundary-12761293712";
3196
+ const responseText =
3197
+ `--${boundary}\r\n` +
3198
+ mockResponse
3199
+ .map(
3200
+ ({ name, contents }) =>
3201
+ `Content-Disposition: form-data; name="${name}"\r\n\r\n${contents}`
3202
+ )
3203
+ .join(`\r\n--${boundary}\r\n`) +
3204
+ `\r\n--${boundary}--`;
3205
+
3206
+ return res(
3207
+ ctx.set("Content-Type", `multipart/form-data; boundary=${boundary}`),
3208
+ ctx.body(responseText)
3209
+ );
3104
3210
  }
3105
3211
  )
3106
3212
  );
@@ -362,7 +362,7 @@ describe("wrangler", () => {
362
362
  "wrangler
363
363
 
364
364
  Commands:
365
- wrangler docs [command] 📚 Open wrangler's docs in your browser
365
+ wrangler docs [command..] 📚 Open wrangler's docs in your browser
366
366
  wrangler init [name] 📥 Initialize a basic Worker project, including a wrangler.toml file
367
367
  wrangler generate [name] [template] ✨ Generate a new Worker project from an existing Worker template. See https://github.com/cloudflare/templates
368
368
  wrangler dev [script] 👂 Start a local server for developing your worker
@@ -379,6 +379,7 @@ describe("wrangler", () => {
379
379
  wrangler r2 📦 Interact with an R2 store
380
380
  wrangler dispatch-namespace 📦 Interact with a dispatch namespace
381
381
  wrangler d1 🗄 Interact with a D1 database
382
+ wrangler constellation 🤖 Interact with Constellation models
382
383
  wrangler pubsub 📮 Interact and manage Pub/Sub Brokers
383
384
  wrangler mtls-certificate 🪪 Manage certificates used for mTLS connections
384
385
  wrangler login 🔓 Login to Cloudflare
@@ -448,5 +448,82 @@ export default {
448
448
  "Build failed with 1 error:
449
449
  hello.js:2:36: ERROR: Could not resolve \\"node:async_hooks\\""
450
450
  `);
451
+ expect(std.err).toContain(
452
+ 'The package "node:async_hooks" wasn\'t found on the file system but is built into node.'
453
+ );
454
+ expect(std.err).toContain(
455
+ 'Add the "nodejs_compat" compatibility flag to your Pages project to enable Node.js compatibility.'
456
+ );
457
+ });
458
+
459
+ it("should compile a _worker.js/ directory", async () => {
460
+ mkdirSync("public");
461
+ mkdirSync("public/_worker.js");
462
+ writeFileSync(
463
+ "public/_worker.js/index.js",
464
+ `
465
+ import { cat } from "./cat.js";
466
+
467
+ export default {
468
+ async fetch(request, env) {
469
+ return new Response("Hello from _worker.js/index.js" + cat);
470
+ },
471
+ };`
472
+ );
473
+ writeFileSync(
474
+ "public/_worker.js/cat.js",
475
+ `
476
+ export const cat = "cat";`
477
+ );
478
+
479
+ await runWrangler(`pages functions build --outfile=public/_worker.bundle`);
480
+
481
+ expect(existsSync("public/_worker.bundle")).toBe(true);
482
+ expect(std.out).toMatchInlineSnapshot(`
483
+ "🚧 'wrangler pages <command>' is a beta command. Please report any issues to https://github.com/cloudflare/workers-sdk/issues/new/choose
484
+ ✨ Compiled Worker successfully"
485
+ `);
486
+
487
+ const workerBundleContents = readFileSync("public/_worker.bundle", "utf-8");
488
+ const workerBundleWithConstantData = replaceRandomWithConstantData(
489
+ workerBundleContents,
490
+ [
491
+ [/------formdata-undici-0.[0-9]*/g, "------formdata-undici-0.test"],
492
+ [/bundledWorker-0.[0-9]*.mjs/g, "bundledWorker-0.test.mjs"],
493
+ [/bundledWorker-0.[0-9]*.map/g, "bundledWorker-0.test.map"],
494
+ ]
495
+ );
496
+
497
+ expect(workerBundleWithConstantData).toMatchInlineSnapshot(`
498
+ "------formdata-undici-0.test
499
+ Content-Disposition: form-data; name=\\"metadata\\"
500
+
501
+ {\\"main_module\\":\\"bundledWorker-0.test.mjs\\"}
502
+ ------formdata-undici-0.test
503
+ Content-Disposition: form-data; name=\\"bundledWorker-0.test.mjs\\"; filename=\\"bundledWorker-0.test.mjs\\"
504
+ Content-Type: application/javascript+module
505
+
506
+ // _worker.js/index.js
507
+ import { cat } from \\"./cat.js\\";
508
+ var worker_default = {
509
+ async fetch(request, env) {
510
+ return new Response(\\"Hello from _worker.js/index.js\\" + cat);
511
+ }
512
+ };
513
+ export {
514
+ worker_default as default
515
+ };
516
+ //# sourceMappingURL=bundledWorker-0.test.mjs.map
517
+
518
+ ------formdata-undici-0.test
519
+ Content-Disposition: form-data; name=\\"cat.js\\"; filename=\\"cat.js\\"
520
+ Content-Type: application/javascript+module
521
+
522
+
523
+ export const cat = \\"cat\\";
524
+ ------formdata-undici-0.test--"
525
+ `);
526
+
527
+ expect(std.err).toMatchInlineSnapshot(`""`);
451
528
  });
452
529
  });
@@ -13,6 +13,7 @@ import type {
13
13
  ScheduledEvent,
14
14
  AlarmEvent,
15
15
  EmailEvent,
16
+ TailInfo,
16
17
  } from "../tail/createTail";
17
18
  import type { RequestInit } from "undici";
18
19
  import type WebSocket from "ws";
@@ -655,6 +656,7 @@ function isRequest(
655
656
  | RequestEvent
656
657
  | AlarmEvent
657
658
  | EmailEvent
659
+ | TailInfo
658
660
  | undefined
659
661
  | null
660
662
  ): event is RequestEvent {