wrangler 2.5.0 → 2.6.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.
@@ -1,6 +1,7 @@
1
+ import { rest } from "msw";
1
2
  import { mockAccountId, mockApiToken } from "./helpers/mock-account-id";
2
- import { setMockResponse, unsetAllMocks } from "./helpers/mock-cfetch";
3
3
  import { mockConsoleMethods } from "./helpers/mock-console";
4
+ import { msw } from "./helpers/msw";
4
5
  import { runInTempDir } from "./helpers/run-in-tmp";
5
6
  import { runWrangler } from "./helpers/run-wrangler";
6
7
  import type {
@@ -16,10 +17,6 @@ describe("wrangler", () => {
16
17
  runInTempDir();
17
18
  const std = mockConsoleMethods();
18
19
 
19
- afterEach(() => {
20
- unsetAllMocks();
21
- });
22
-
23
20
  describe("pubsub", () => {
24
21
  describe("help menu", () => {
25
22
  it("shows usage details", async () => {
@@ -83,15 +80,30 @@ describe("wrangler", () => {
83
80
  describe("create", () => {
84
81
  function mockCreateRequest(expectedNamespaceName: string) {
85
82
  const requests = { count: 0 };
86
- setMockResponse(
87
- "/accounts/:accountId/pubsub/namespaces",
88
- "POST",
89
- ([_url, accountId], { body }) => {
90
- expect(accountId).toEqual("some-account-id");
91
- const namespaceName = JSON.parse(body as string).name;
92
- expect(namespaceName).toEqual(expectedNamespaceName);
93
- requests.count += 1;
94
- }
83
+ msw.use(
84
+ rest.post(
85
+ "*/accounts/:accountId/pubsub/namespaces",
86
+ async (req, res, ctx) => {
87
+ expect(req.params.accountId).toEqual("some-account-id");
88
+ const namespace = await req.json();
89
+ expect(namespace.name).toEqual(expectedNamespaceName);
90
+ requests.count++;
91
+ return res.once(
92
+ ctx.status(200),
93
+ ctx.json({
94
+ success: true,
95
+ errors: [],
96
+ messages: [],
97
+ result: {
98
+ id: "some-namespace-id",
99
+ name: namespace.name,
100
+ created_at: "3005-01-01T00:00:00.000000Z",
101
+ updated_at: "3005-01-01T00:00:00.000000Z",
102
+ },
103
+ })
104
+ );
105
+ }
106
+ )
95
107
  );
96
108
  return requests;
97
109
  }
@@ -107,14 +119,24 @@ describe("wrangler", () => {
107
119
  describe("list", () => {
108
120
  function mockListRequest(namespaces: PubSubNamespace[]) {
109
121
  const requests = { count: 0 };
110
- setMockResponse(
111
- "/accounts/:accountId/pubsub/namespaces",
112
- ([_url, accountId], init) => {
113
- requests.count++;
114
- expect(accountId).toEqual("some-account-id");
115
- expect(init).toEqual({});
116
- return { namespaces };
117
- }
122
+ msw.use(
123
+ rest.get(
124
+ "*/accounts/:accountId/pubsub/namespaces",
125
+ async (req, res, ctx) => {
126
+ requests.count++;
127
+ expect(req.params.accountId).toEqual("some-account-id");
128
+
129
+ return res.once(
130
+ ctx.status(200),
131
+ ctx.json({
132
+ success: true,
133
+ errors: [],
134
+ messages: [],
135
+ result: namespaces,
136
+ })
137
+ );
138
+ }
139
+ )
118
140
  );
119
141
  return requests;
120
142
  }
@@ -128,7 +150,12 @@ describe("wrangler", () => {
128
150
  await runWrangler("pubsub namespace list");
129
151
 
130
152
  expect(std.err).toMatchInlineSnapshot(`""`);
131
- // TODO(elithrar): check returned object
153
+ expect(std.out).toMatchInlineSnapshot(`
154
+ "[
155
+ { name: 'namespace-1', created_on: '01-01-2001' },
156
+ { name: 'namespace-2', created_on: '01-01-2001' }
157
+ ]"
158
+ `);
132
159
  expect(requests.count).toEqual(1);
133
160
  });
134
161
  });
@@ -174,16 +201,29 @@ describe("wrangler", () => {
174
201
  describe("create", () => {
175
202
  function mockCreateRequest(expectedBrokerName: string) {
176
203
  const requests = { count: 0 };
177
- setMockResponse(
178
- "/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers",
179
- "POST",
180
- ([_url, accountId, namespaceName], { body }) => {
181
- expect(accountId).toEqual("some-account-id");
182
- expect(namespaceName).toEqual("some-namespace");
183
- const brokerName = JSON.parse(body as string).name;
184
- expect(brokerName).toEqual(expectedBrokerName);
185
- requests.count += 1;
186
- }
204
+ msw.use(
205
+ rest.post(
206
+ "*/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers",
207
+ async (req, res, ctx) => {
208
+ expect(req.params.accountId).toEqual("some-account-id");
209
+ expect(req.params.namespaceName).toEqual("some-namespace");
210
+ const broker = await req.json();
211
+ expect(broker.name).toEqual(expectedBrokerName);
212
+ requests.count++;
213
+ return res.once(
214
+ ctx.status(200),
215
+ ctx.json({
216
+ success: true,
217
+ errors: [],
218
+ messages: [],
219
+ result: {
220
+ name: expectedBrokerName,
221
+ created_on: "01-01-2001",
222
+ },
223
+ })
224
+ );
225
+ }
226
+ )
187
227
  );
188
228
  return requests;
189
229
  }
@@ -194,7 +234,10 @@ describe("wrangler", () => {
194
234
  "pubsub broker create my-broker --namespace=some-namespace"
195
235
  );
196
236
 
197
- // TODO: check returned object
237
+ expect(std.err).toMatchInlineSnapshot(`""`);
238
+ expect(std.out).toMatchInlineSnapshot(
239
+ `"{ name: 'my-broker', created_on: '01-01-2001' }"`
240
+ );
198
241
  expect(requests.count).toEqual(1);
199
242
  });
200
243
 
@@ -215,21 +258,34 @@ describe("wrangler", () => {
215
258
  expectedOnPublishConfig: PubSubBrokerOnPublish
216
259
  ) {
217
260
  const requests = { count: 0 };
218
- setMockResponse(
219
- "/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers/:brokerName",
220
- "PATCH",
221
- ([_url, accountId, namespaceName, brokerName], { body }) => {
222
- expect(accountId).toEqual("some-account-id");
223
- expect(namespaceName).toEqual("some-namespace");
224
- expect(brokerName).toEqual(expectedBrokerName);
225
-
226
- const patchBody: PubSubBrokerUpdate = JSON.parse(body as string);
227
- expect(patchBody.expiration).toEqual(expectedExpiration);
228
- expect(patchBody.description).toEqual(expectedDescription);
229
- expect(patchBody.on_publish).toEqual(expectedOnPublishConfig);
230
-
231
- requests.count += 1;
232
- }
261
+ msw.use(
262
+ rest.patch(
263
+ "*/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers/:brokerName",
264
+ async (req, res, cxt) => {
265
+ requests.count += 1;
266
+ expect(req.params.accountId).toEqual("some-account-id");
267
+ expect(req.params.namespaceName).toEqual("some-namespace");
268
+ expect(req.params.brokerName).toEqual(expectedBrokerName);
269
+
270
+ const patchBody: PubSubBrokerUpdate = await req.json();
271
+
272
+ expect(patchBody.expiration).toEqual(expectedExpiration);
273
+ expect(patchBody.description).toEqual(expectedDescription);
274
+ expect(patchBody.on_publish).toEqual(expectedOnPublishConfig);
275
+ return res.once(
276
+ cxt.status(200),
277
+ cxt.json({
278
+ success: true,
279
+ errors: [],
280
+ messages: [],
281
+ result: {
282
+ name: expectedBrokerName,
283
+ created_on: "01-01-2001",
284
+ },
285
+ })
286
+ );
287
+ }
288
+ )
233
289
  );
234
290
  return requests;
235
291
  }
@@ -249,7 +305,10 @@ describe("wrangler", () => {
249
305
  );
250
306
 
251
307
  expect(std.err).toMatchInlineSnapshot(`""`);
252
- // TODO(elithrar): check returned object
308
+ expect(std.out).toMatchInlineSnapshot(`
309
+ "{ name: 'my-broker', created_on: '01-01-2001' }
310
+ Successfully updated Pub/Sub Broker my-broker"
311
+ `);
253
312
  expect(requests.count).toEqual(1);
254
313
  });
255
314
  });
@@ -257,15 +316,24 @@ describe("wrangler", () => {
257
316
  describe("list", () => {
258
317
  function mockListRequest(brokers: PubSubBroker[]) {
259
318
  const requests = { count: 0 };
260
- setMockResponse(
261
- "/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers",
262
- ([_url, accountId, namespaceName], init) => {
263
- requests.count++;
264
- expect(accountId).toEqual("some-account-id");
265
- expect(namespaceName).toEqual("some-namespace");
266
- expect(init).toEqual({});
267
- return { brokers };
268
- }
319
+ msw.use(
320
+ rest.get(
321
+ "*/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers",
322
+ async (req, res, cxt) => {
323
+ requests.count++;
324
+ expect(req.params.accountId).toEqual("some-account-id");
325
+ expect(req.params.namespaceName).toEqual("some-namespace");
326
+ return res.once(
327
+ cxt.status(200),
328
+ cxt.json({
329
+ success: true,
330
+ errors: [],
331
+ messages: [],
332
+ result: brokers,
333
+ })
334
+ );
335
+ }
336
+ )
269
337
  );
270
338
  return requests;
271
339
  }
@@ -279,7 +347,12 @@ describe("wrangler", () => {
279
347
  await runWrangler("pubsub broker list --namespace=some-namespace");
280
348
 
281
349
  expect(std.err).toMatchInlineSnapshot(`""`);
282
- // TODO(elithrar): check returned object
350
+ expect(std.out).toMatchInlineSnapshot(`
351
+ "[
352
+ { name: 'broker-1', created_on: '01-01-2001' },
353
+ { name: 'broker-2', created_on: '01-01-2001' }
354
+ ]"
355
+ `);
283
356
  expect(requests.count).toEqual(1);
284
357
  });
285
358
  });
@@ -287,15 +360,25 @@ describe("wrangler", () => {
287
360
  describe("describe", () => {
288
361
  function mockGetRequest(broker: PubSubBroker) {
289
362
  const requests = { count: 0 };
290
- setMockResponse(
291
- "/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers/:brokerName",
292
- ([_url, accountId, namespaceName, brokerName]) => {
293
- requests.count++;
294
- expect(accountId).toEqual("some-account-id");
295
- expect(namespaceName).toEqual("some-namespace");
296
- expect(brokerName).toEqual(broker.name);
297
- return { result: broker };
298
- }
363
+ msw.use(
364
+ rest.get(
365
+ "*/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers/:brokerName",
366
+ (req, res, cxt) => {
367
+ requests.count++;
368
+ expect(req.params.accountId).toEqual("some-account-id");
369
+ expect(req.params.namespaceName).toEqual("some-namespace");
370
+ expect(req.params.brokerName).toEqual(broker.name);
371
+ return res.once(
372
+ cxt.status(200),
373
+ cxt.json({
374
+ success: true,
375
+ errors: [],
376
+ messages: [],
377
+ result: broker,
378
+ })
379
+ );
380
+ }
381
+ )
299
382
  );
300
383
  return requests;
301
384
  }
@@ -307,7 +390,9 @@ describe("wrangler", () => {
307
390
  );
308
391
 
309
392
  expect(std.err).toMatchInlineSnapshot(`""`);
310
- // TODO(elithrar): check returned object
393
+ expect(std.out).toMatchInlineSnapshot(
394
+ `"{ id: '1234', name: 'my-broker' }"`
395
+ );
311
396
  expect(requests.count).toEqual(1);
312
397
  });
313
398
  });
@@ -315,14 +400,28 @@ describe("wrangler", () => {
315
400
  describe("issue", () => {
316
401
  function mockIssueRequest(expectedBrokerName: string) {
317
402
  const requests = { count: 0 };
318
- setMockResponse(
319
- "/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers/:brokerName/credentials",
320
- ([_url, accountId, namespaceName, brokerName]) => {
321
- expect(accountId).toEqual("some-account-id");
322
- expect(namespaceName).toEqual("some-namespace");
323
- expect(brokerName).toEqual(expectedBrokerName);
324
- requests.count += 1;
325
- }
403
+ msw.use(
404
+ rest.get(
405
+ "*/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers/:brokerName/credentials",
406
+ (req, res, cxt) => {
407
+ requests.count++;
408
+ expect(req.params.accountId).toEqual("some-account-id");
409
+ expect(req.params.namespaceName).toEqual("some-namespace");
410
+ expect(req.params.brokerName).toEqual(expectedBrokerName);
411
+ return res.once(
412
+ cxt.status(200),
413
+ cxt.json({
414
+ success: true,
415
+ errors: [],
416
+ messages: [],
417
+ result: {
418
+ "MOCK-89T6DXG3SVG1WQRA": `<base-64-encoded-JWT>`,
419
+ "MOCK-393REE4WRRE4NHAV96": `<base-64-encoded-JWT>`,
420
+ },
421
+ })
422
+ );
423
+ }
424
+ )
326
425
  );
327
426
  return requests;
328
427
  }
@@ -334,7 +433,13 @@ describe("wrangler", () => {
334
433
  );
335
434
 
336
435
  expect(std.err).toMatchInlineSnapshot(`""`);
337
- // TODO(elithrar): check returned object
436
+ expect(std.out).toMatchInlineSnapshot(`
437
+ "🔑 Issuing credential(s) for my-broker.some-namespace...
438
+ {
439
+ 'MOCK-89T6DXG3SVG1WQRA': '<base-64-encoded-JWT>',
440
+ 'MOCK-393REE4WRRE4NHAV96': '<base-64-encoded-JWT>'
441
+ }"
442
+ `);
338
443
  expect(requests.count).toEqual(1);
339
444
  });
340
445
  });
@@ -342,14 +447,27 @@ describe("wrangler", () => {
342
447
  describe("public-keys", () => {
343
448
  function mockIssueRequest(expectedBrokerName: string) {
344
449
  const requests = { count: 0 };
345
- setMockResponse(
346
- "/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers/:brokerName/publickeys",
347
- ([_url, accountId, namespaceName, brokerName]) => {
348
- expect(accountId).toEqual("some-account-id");
349
- expect(namespaceName).toEqual("some-namespace");
350
- expect(brokerName).toEqual(expectedBrokerName);
351
- requests.count += 1;
352
- }
450
+ msw.use(
451
+ rest.get(
452
+ "*/accounts/:accountId/pubsub/namespaces/:namespaceName/brokers/:brokerName/publickeys",
453
+ (req, res, cxt) => {
454
+ requests.count++;
455
+ expect(req.params.accountId).toEqual("some-account-id");
456
+ expect(req.params.namespaceName).toEqual("some-namespace");
457
+ expect(req.params.brokerName).toEqual(expectedBrokerName);
458
+ return res.once(
459
+ cxt.status(200),
460
+ cxt.json({
461
+ success: true,
462
+ errors: [],
463
+ messages: [],
464
+ result: {
465
+ public_keys: "Mock-Public-Key",
466
+ },
467
+ })
468
+ );
469
+ }
470
+ )
353
471
  );
354
472
  return requests;
355
473
  }
@@ -361,7 +479,9 @@ describe("wrangler", () => {
361
479
  );
362
480
 
363
481
  expect(std.err).toMatchInlineSnapshot(`""`);
364
- // TODO(elithrar): check returned object
482
+ expect(std.out).toMatchInlineSnapshot(
483
+ `"{ public_keys: 'Mock-Public-Key' }"`
484
+ );
365
485
  expect(requests.count).toEqual(1);
366
486
  });
367
487
  });
@@ -694,7 +694,7 @@ function mockCreateTailRequest(
694
694
  if (!legacyEnv) {
695
695
  expect(req.params.envName).toEqual(env);
696
696
  }
697
- return res(
697
+ return res.once(
698
698
  ctx.status(200),
699
699
  ctx.json({
700
700
  success: true,
@@ -52,6 +52,12 @@ const bindingsConfigMock: Partial<Config> = {
52
52
  },
53
53
  ],
54
54
  services: [{ binding: "SERVICE_BINDING", service: "SERVICE_NAME" }],
55
+ analytics_engine_datasets: [
56
+ {
57
+ binding: "AE_DATASET_BINDING",
58
+ dataset: "AE_DATASET_TEST",
59
+ },
60
+ ],
55
61
  dispatch_namespaces: [
56
62
  { binding: "NAMESPACE_BINDING", namespace: "NAMESPACE_ID" },
57
63
  ],
@@ -116,6 +122,7 @@ describe("generateTypes()", () => {
116
122
  R2_BUCKET_BINDING: R2Bucket;
117
123
  D1_TESTING_SOMETHING: D1Database;
118
124
  SERVICE_BINDING: Fetcher;
125
+ AE_DATASET_BINDING: AnalyticsEngineDataset;
119
126
  NAMESPACE_BINDING: any;
120
127
  LOGFWDR_SCHEMA: any;
121
128
  SOME_DATA_BLOB1: ArrayBuffer;
@@ -439,6 +439,22 @@ interface EnvironmentNonInheritable {
439
439
  }[]
440
440
  | undefined;
441
441
 
442
+ /**
443
+ * Specifies analytics engine datasets that are bound to this Worker environment.
444
+ *
445
+ * NOTE: This field is not automatically inherited from the top level environment,
446
+ * and so must be specified in every named environment.
447
+ *
448
+ * @default `[]`
449
+ * @nonInheritable
450
+ */
451
+ analytics_engine_datasets: {
452
+ /** The binding name used to refer to the dataset in the worker. */
453
+ binding: string;
454
+ /** The name of this dataset to write to. */
455
+ dataset?: string;
456
+ }[];
457
+
442
458
  /**
443
459
  * "Unsafe" tables for features that aren't directly supported by wrangler.
444
460
  *
@@ -93,6 +93,7 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) {
93
93
  r2_buckets,
94
94
  logfwdr,
95
95
  services,
96
+ analytics_engine_datasets,
96
97
  text_blobs,
97
98
  unsafe,
98
99
  vars,
@@ -159,14 +160,21 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) {
159
160
  if (d1_databases !== undefined && d1_databases.length > 0) {
160
161
  output.push({
161
162
  type: "D1 Databases",
162
- entries: d1_databases.map(({ binding, database_name, database_id }) => {
163
- return {
164
- key: removeD1BetaPrefix(binding),
165
- value: database_name
166
- ? `${database_name} (${database_id})`
167
- : database_id,
168
- };
169
- }),
163
+ entries: d1_databases.map(
164
+ ({ binding, database_name, database_id, preview_database_id }) => {
165
+ let databaseValue = `${database_id}`;
166
+ if (database_name) {
167
+ databaseValue = `${database_name} (${database_id})`;
168
+ }
169
+ if (preview_database_id) {
170
+ databaseValue += `, Preview: (${preview_database_id})`;
171
+ }
172
+ return {
173
+ key: removeD1BetaPrefix(binding),
174
+ value: databaseValue,
175
+ };
176
+ }
177
+ ),
170
178
  });
171
179
  }
172
180
 
@@ -211,6 +219,21 @@ export function printBindings(bindings: CfWorkerInit["bindings"]) {
211
219
  });
212
220
  }
213
221
 
222
+ if (
223
+ analytics_engine_datasets !== undefined &&
224
+ analytics_engine_datasets.length > 0
225
+ ) {
226
+ output.push({
227
+ type: "Analytics Engine Datasets",
228
+ entries: analytics_engine_datasets.map(({ binding, dataset }) => {
229
+ return {
230
+ key: binding,
231
+ value: dataset ?? binding,
232
+ };
233
+ }),
234
+ });
235
+ }
236
+
214
237
  if (text_blobs !== undefined && Object.keys(text_blobs).length > 0) {
215
238
  output.push({
216
239
  type: "Text Blobs",
@@ -1115,6 +1115,16 @@ function normalizeAndValidateEnvironment(
1115
1115
  validateBindingArray(envName, validateServiceBinding),
1116
1116
  []
1117
1117
  ),
1118
+ analytics_engine_datasets: notInheritable(
1119
+ diagnostics,
1120
+ topLevelEnv,
1121
+ rawConfig,
1122
+ rawEnv,
1123
+ envName,
1124
+ "analytics_engine_datasets",
1125
+ validateBindingArray(envName, validateAnalyticsEngineBinding),
1126
+ []
1127
+ ),
1118
1128
  dispatch_namespaces: notInheritable(
1119
1129
  diagnostics,
1120
1130
  topLevelEnv,
@@ -1834,6 +1844,7 @@ const validateBindingsHaveUniqueNames = (
1834
1844
  durable_objects,
1835
1845
  kv_namespaces,
1836
1846
  r2_buckets,
1847
+ analytics_engine_datasets,
1837
1848
  text_blobs,
1838
1849
  unsafe,
1839
1850
  vars,
@@ -1848,6 +1859,7 @@ const validateBindingsHaveUniqueNames = (
1848
1859
  "Durable Object": getBindingNames(durable_objects),
1849
1860
  "KV Namespace": getBindingNames(kv_namespaces),
1850
1861
  "R2 Bucket": getBindingNames(r2_buckets),
1862
+ "Analytics Engine Dataset": getBindingNames(analytics_engine_datasets),
1851
1863
  "Text Blob": getBindingNames(text_blobs),
1852
1864
  Unsafe: getBindingNames(unsafe),
1853
1865
  "Environment Variable": getBindingNames(vars),
@@ -1956,6 +1968,43 @@ const validateServiceBinding: ValidatorFn = (diagnostics, field, value) => {
1956
1968
  return isValid;
1957
1969
  };
1958
1970
 
1971
+ const validateAnalyticsEngineBinding: ValidatorFn = (
1972
+ diagnostics,
1973
+ field,
1974
+ value
1975
+ ) => {
1976
+ if (typeof value !== "object" || value === null) {
1977
+ diagnostics.errors.push(
1978
+ `"analytics_engine" bindings should be objects, but got ${JSON.stringify(
1979
+ value
1980
+ )}`
1981
+ );
1982
+ return false;
1983
+ }
1984
+ let isValid = true;
1985
+ // Service bindings must have a binding and optional dataset.
1986
+ if (!isRequiredProperty(value, "binding", "string")) {
1987
+ diagnostics.errors.push(
1988
+ `"${field}" bindings should have a string "binding" field but got ${JSON.stringify(
1989
+ value
1990
+ )}.`
1991
+ );
1992
+ isValid = false;
1993
+ }
1994
+ if (
1995
+ !isOptionalProperty(value, "dataset", "string") ||
1996
+ (value as { dataset: string }).dataset?.length === 0
1997
+ ) {
1998
+ diagnostics.errors.push(
1999
+ `"${field}" bindings should, optionally, have a string "dataset" field but got ${JSON.stringify(
2000
+ value
2001
+ )}.`
2002
+ );
2003
+ isValid = false;
2004
+ }
2005
+ return isValid;
2006
+ };
2007
+
1959
2008
  const validateWorkerNamespaceBinding: ValidatorFn = (
1960
2009
  diagnostics,
1961
2010
  field,
@@ -43,6 +43,7 @@ type WorkerMetadataBinding =
43
43
  | { type: "r2_bucket"; name: string; bucket_name: string }
44
44
  | { type: "d1"; name: string; id: string; internalEnv?: string }
45
45
  | { type: "service"; name: string; service: string; environment?: string }
46
+ | { type: "analytics_engine"; name: string; dataset?: string }
46
47
  | { type: "namespace"; name: string; namespace: string }
47
48
  | {
48
49
  type: "logfwdr";
@@ -149,6 +150,14 @@ export function createWorkerUploadForm(worker: CfWorkerInit): FormData {
149
150
  });
150
151
  });
151
152
 
153
+ bindings.analytics_engine_datasets?.forEach(({ binding, dataset }) => {
154
+ metadataBindings.push({
155
+ name: binding,
156
+ type: "analytics_engine",
157
+ dataset,
158
+ });
159
+ });
160
+
152
161
  bindings.dispatch_namespaces?.forEach(({ binding, namespace }) => {
153
162
  metadataBindings.push({
154
163
  name: binding,