posthog-node 3.4.0 → 3.5.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.
@@ -51,7 +51,9 @@ describe('PostHog Node.js', () => {
51
51
  expect(mockedFetch).toHaveBeenCalledTimes(0)
52
52
  posthog.capture({ distinctId: '123', event: 'test-event', properties: { foo: 'bar' }, groups: { org: 123 } })
53
53
 
54
+ await waitForPromises()
54
55
  jest.runOnlyPendingTimers()
56
+
55
57
  const batchEvents = getLastBatchEvents()
56
58
  expect(batchEvents).toEqual([
57
59
  {
@@ -76,6 +78,7 @@ describe('PostHog Node.js', () => {
76
78
  expect(mockedFetch).toHaveBeenCalledTimes(0)
77
79
  posthog.capture({ distinctId: '123', event: 'test-event', properties: { foo: 'bar' }, groups: { org: 123 } })
78
80
 
81
+ await waitForPromises()
79
82
  jest.runOnlyPendingTimers()
80
83
  expect(getLastBatchEvents()?.[0]).toEqual(
81
84
  expect.objectContaining({
@@ -98,6 +101,7 @@ describe('PostHog Node.js', () => {
98
101
  groups: { other_group: 'x' },
99
102
  })
100
103
 
104
+ await waitForPromises()
101
105
  jest.runOnlyPendingTimers()
102
106
  expect(getLastBatchEvents()?.[0]).toEqual(
103
107
  expect.objectContaining({
@@ -173,6 +177,7 @@ describe('PostHog Node.js', () => {
173
177
  it('should allow overriding timestamp', async () => {
174
178
  expect(mockedFetch).toHaveBeenCalledTimes(0)
175
179
  posthog.capture({ event: 'custom-time', distinctId: '123', timestamp: new Date('2021-02-03') })
180
+ await waitForPromises()
176
181
  jest.runOnlyPendingTimers()
177
182
  const batchEvents = getLastBatchEvents()
178
183
  expect(batchEvents).toMatchObject([
@@ -194,6 +199,7 @@ describe('PostHog Node.js', () => {
194
199
  disableGeoip: false,
195
200
  })
196
201
 
202
+ await waitForPromises()
197
203
  jest.runOnlyPendingTimers()
198
204
  const batchEvents = getLastBatchEvents()
199
205
  expect(batchEvents?.[0].properties).toEqual({
@@ -212,7 +218,9 @@ describe('PostHog Node.js', () => {
212
218
  })
213
219
  client.capture({ distinctId: '123', event: 'test-event', properties: { foo: 'bar' }, groups: { org: 123 } })
214
220
 
221
+ await waitForPromises()
215
222
  jest.runOnlyPendingTimers()
223
+
216
224
  let batchEvents = getLastBatchEvents()
217
225
  expect(batchEvents?.[0].properties).toEqual({
218
226
  $groups: { org: 123 },
@@ -229,6 +237,7 @@ describe('PostHog Node.js', () => {
229
237
  disableGeoip: true,
230
238
  })
231
239
 
240
+ await waitForPromises()
232
241
  jest.runOnlyPendingTimers()
233
242
  batchEvents = getLastBatchEvents()
234
243
  console.warn(batchEvents)
@@ -248,6 +257,7 @@ describe('PostHog Node.js', () => {
248
257
  disableGeoip: false,
249
258
  })
250
259
 
260
+ await waitForPromises()
251
261
  jest.runOnlyPendingTimers()
252
262
  batchEvents = getLastBatchEvents()
253
263
  expect(batchEvents?.[0].properties).toEqual({
@@ -286,6 +296,7 @@ describe('PostHog Node.js', () => {
286
296
 
287
297
  afterEach(() => {
288
298
  posthog.debug(false)
299
+ jest.useFakeTimers()
289
300
  })
290
301
 
291
302
  it('should shutdown cleanly', async () => {
@@ -321,6 +332,45 @@ describe('PostHog Node.js', () => {
321
332
  jest.useFakeTimers()
322
333
  logSpy.mockRestore()
323
334
  })
335
+
336
+ it('should shutdown cleanly with pending capture flag promises', async () => {
337
+ posthog = new PostHog('TEST_API_KEY', {
338
+ host: 'http://example.com',
339
+ fetchRetryCount: 0,
340
+ flushAt: 4,
341
+ })
342
+
343
+ const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {})
344
+ jest.useRealTimers()
345
+ posthog.debug(true)
346
+
347
+ for (let i = 0; i < 10; i++) {
348
+ posthog.capture({ event: 'test-event', distinctId: `${i}`, sendFeatureFlags: true })
349
+ }
350
+
351
+ await posthog.shutdownAsync()
352
+ // all capture calls happen during shutdown
353
+ const batchEvents = getLastBatchEvents()
354
+ expect(batchEvents?.length).toEqual(2)
355
+ expect(batchEvents?.[batchEvents?.length - 1]).toMatchObject({
356
+ // last event in batch
357
+ distinct_id: '9',
358
+ event: 'test-event',
359
+ library: 'posthog-node',
360
+ library_version: '1.2.3',
361
+ properties: {
362
+ $lib: 'posthog-node',
363
+ $lib_version: '1.2.3',
364
+ $geoip_disable: true,
365
+ },
366
+ timestamp: expect.any(String),
367
+ type: 'capture',
368
+ })
369
+ expect(10).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('capture')).length)
370
+ expect(3).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('flush')).length)
371
+ jest.useFakeTimers()
372
+ logSpy.mockRestore()
373
+ })
324
374
  })
325
375
 
326
376
  describe('groupIdentify', () => {
@@ -384,8 +434,79 @@ describe('PostHog Node.js', () => {
384
434
  'feature-variant': 2,
385
435
  }
386
436
 
437
+ const multivariateFlag = {
438
+ id: 1,
439
+ name: 'Beta Feature',
440
+ key: 'beta-feature-local',
441
+ is_simple_flag: false,
442
+ active: true,
443
+ rollout_percentage: 100,
444
+ filters: {
445
+ groups: [
446
+ {
447
+ properties: [{ key: 'email', type: 'person', value: 'test@posthog.com', operator: 'exact' }],
448
+ rollout_percentage: 100,
449
+ },
450
+ {
451
+ rollout_percentage: 50,
452
+ },
453
+ ],
454
+ multivariate: {
455
+ variants: [
456
+ { key: 'first-variant', name: 'First Variant', rollout_percentage: 50 },
457
+ { key: 'second-variant', name: 'Second Variant', rollout_percentage: 25 },
458
+ { key: 'third-variant', name: 'Third Variant', rollout_percentage: 25 },
459
+ ],
460
+ },
461
+ payloads: { 'first-variant': 'some-payload', 'third-variant': { a: 'json' } },
462
+ },
463
+ }
464
+ const basicFlag = {
465
+ id: 1,
466
+ name: 'Beta Feature',
467
+ key: 'person-flag',
468
+ is_simple_flag: true,
469
+ active: true,
470
+ filters: {
471
+ groups: [
472
+ {
473
+ properties: [
474
+ {
475
+ key: 'region',
476
+ operator: 'exact',
477
+ value: ['USA'],
478
+ type: 'person',
479
+ },
480
+ ],
481
+ rollout_percentage: 100,
482
+ },
483
+ ],
484
+ payloads: { true: 300 },
485
+ },
486
+ }
487
+ const falseFlag = {
488
+ id: 1,
489
+ name: 'Beta Feature',
490
+ key: 'false-flag',
491
+ is_simple_flag: true,
492
+ active: true,
493
+ filters: {
494
+ groups: [
495
+ {
496
+ properties: [],
497
+ rollout_percentage: 0,
498
+ },
499
+ ],
500
+ payloads: { true: 300 },
501
+ },
502
+ }
503
+
387
504
  mockedFetch.mockImplementation(
388
- apiImplementation({ decideFlags: mockFeatureFlags, decideFlagPayloads: mockFeatureFlagPayloads })
505
+ apiImplementation({
506
+ decideFlags: mockFeatureFlags,
507
+ decideFlagPayloads: mockFeatureFlagPayloads,
508
+ localFlags: { flags: [multivariateFlag, basicFlag, falseFlag] },
509
+ })
389
510
  )
390
511
 
391
512
  posthog = new PostHog('TEST_API_KEY', {
@@ -430,15 +551,14 @@ describe('PostHog Node.js', () => {
430
551
  sendFeatureFlags: true,
431
552
  })
432
553
 
554
+ jest.runOnlyPendingTimers()
555
+ await waitForPromises()
556
+
433
557
  expect(mockedFetch).toHaveBeenCalledWith(
434
558
  'http://example.com/decide/?v=3',
435
559
  expect.objectContaining({ method: 'POST' })
436
560
  )
437
561
 
438
- jest.runOnlyPendingTimers()
439
-
440
- await waitForPromises()
441
-
442
562
  expect(getLastBatchEvents()?.[0]).toEqual(
443
563
  expect.objectContaining({
444
564
  distinct_id: 'distinct_id',
@@ -464,6 +584,113 @@ describe('PostHog Node.js', () => {
464
584
  )
465
585
  })
466
586
 
587
+ it('captures feature flags with locally evaluated flags', async () => {
588
+ mockedFetch.mockClear()
589
+ mockedFetch.mockClear()
590
+ expect(mockedFetch).toHaveBeenCalledTimes(0)
591
+
592
+ posthog = new PostHog('TEST_API_KEY', {
593
+ host: 'http://example.com',
594
+ flushAt: 1,
595
+ fetchRetryCount: 0,
596
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
597
+ })
598
+
599
+ jest.runOnlyPendingTimers()
600
+ await waitForPromises()
601
+
602
+ posthog.capture({
603
+ distinctId: 'distinct_id',
604
+ event: 'node test event',
605
+ })
606
+
607
+ expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
608
+ // no decide call
609
+ expect(mockedFetch).not.toHaveBeenCalledWith(
610
+ 'http://example.com/decide/?v=3',
611
+ expect.objectContaining({ method: 'POST' })
612
+ )
613
+
614
+ jest.runOnlyPendingTimers()
615
+
616
+ await waitForPromises()
617
+
618
+ expect(getLastBatchEvents()?.[0]).toEqual(
619
+ expect.objectContaining({
620
+ distinct_id: 'distinct_id',
621
+ event: 'node test event',
622
+ properties: expect.objectContaining({
623
+ $active_feature_flags: ['beta-feature-local'],
624
+ '$feature/beta-feature-local': 'third-variant',
625
+ '$feature/false-flag': false,
626
+ $lib: 'posthog-node',
627
+ $lib_version: '1.2.3',
628
+ $geoip_disable: true,
629
+ }),
630
+ })
631
+ )
632
+ expect(
633
+ Object.prototype.hasOwnProperty.call(getLastBatchEvents()?.[0].properties, '$feature/beta-feature-local')
634
+ ).toBe(true)
635
+ expect(Object.prototype.hasOwnProperty.call(getLastBatchEvents()?.[0].properties, '$feature/beta-feature')).toBe(
636
+ false
637
+ )
638
+
639
+ await posthog.shutdownAsync()
640
+ })
641
+
642
+ it('doesnt add flag properties when locally evaluated flags are empty', async () => {
643
+ mockedFetch.mockClear()
644
+ expect(mockedFetch).toHaveBeenCalledTimes(0)
645
+ mockedFetch.mockImplementation(
646
+ apiImplementation({ decideFlags: { a: false, b: 'true' }, decideFlagPayloads: {}, localFlags: { flags: [] } })
647
+ )
648
+
649
+ posthog = new PostHog('TEST_API_KEY', {
650
+ host: 'http://example.com',
651
+ flushAt: 1,
652
+ fetchRetryCount: 0,
653
+ personalApiKey: 'TEST_PERSONAL_API_KEY',
654
+ })
655
+
656
+ posthog.capture({
657
+ distinctId: 'distinct_id',
658
+ event: 'node test event',
659
+ })
660
+
661
+ jest.runOnlyPendingTimers()
662
+ await waitForPromises()
663
+
664
+ expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
665
+ // no decide call
666
+ expect(mockedFetch).not.toHaveBeenCalledWith(
667
+ 'http://example.com/decide/?v=3',
668
+ expect.objectContaining({ method: 'POST' })
669
+ )
670
+
671
+ jest.runOnlyPendingTimers()
672
+
673
+ await waitForPromises()
674
+
675
+ expect(getLastBatchEvents()?.[0]).toEqual(
676
+ expect.objectContaining({
677
+ distinct_id: 'distinct_id',
678
+ event: 'node test event',
679
+ properties: expect.objectContaining({
680
+ $lib: 'posthog-node',
681
+ $lib_version: '1.2.3',
682
+ $geoip_disable: true,
683
+ }),
684
+ })
685
+ )
686
+ expect(
687
+ Object.prototype.hasOwnProperty.call(getLastBatchEvents()?.[0].properties, '$feature/beta-feature-local')
688
+ ).toBe(false)
689
+ expect(Object.prototype.hasOwnProperty.call(getLastBatchEvents()?.[0].properties, '$feature/beta-feature')).toBe(
690
+ false
691
+ )
692
+ })
693
+
467
694
  it('captures feature flags with same geoip setting as capture', async () => {
468
695
  mockedFetch.mockClear()
469
696
  mockedFetch.mockClear()
@@ -482,19 +709,19 @@ describe('PostHog Node.js', () => {
482
709
  disableGeoip: false,
483
710
  })
484
711
 
712
+ await waitForPromises()
713
+ jest.runOnlyPendingTimers()
714
+
485
715
  expect(mockedFetch).toHaveBeenCalledWith(
486
716
  'http://example.com/decide/?v=3',
487
717
  expect.objectContaining({ method: 'POST', body: expect.not.stringContaining('geoip_disable') })
488
718
  )
489
719
 
490
- jest.runOnlyPendingTimers()
491
-
492
- await waitForPromises()
493
-
494
720
  expect(getLastBatchEvents()?.[0].properties).toEqual({
495
721
  $active_feature_flags: ['feature-1', 'feature-2', 'feature-variant'],
496
722
  '$feature/feature-1': true,
497
723
  '$feature/feature-2': true,
724
+ '$feature/disabled-flag': false,
498
725
  '$feature/feature-variant': 'variant',
499
726
  $lib: 'posthog-node',
500
727
  $lib_version: '1.2.3',
@@ -534,14 +761,16 @@ describe('PostHog Node.js', () => {
534
761
  personalApiKey: 'TEST_PERSONAL_API_KEY',
535
762
  maxCacheSize: 10,
536
763
  fetchRetryCount: 0,
764
+ flushAt: 1,
537
765
  })
538
766
 
539
767
  expect(Object.keys(posthog.distinctIdHasSentFlagCalls).length).toEqual(0)
540
768
 
541
- for (let i = 0; i < 1000; i++) {
769
+ for (let i = 0; i < 100; i++) {
542
770
  const distinctId = `some-distinct-id${i}`
543
771
  await posthog.getFeatureFlag('beta-feature', distinctId)
544
772
 
773
+ await waitForPromises()
545
774
  jest.runOnlyPendingTimers()
546
775
 
547
776
  const batchEvents = getLastBatchEvents()
@@ -566,6 +795,8 @@ describe('PostHog Node.js', () => {
566
795
  })
567
796
 
568
797
  it('$feature_flag_called is called appropriately when querying flags', async () => {
798
+ mockedFetch.mockClear()
799
+
569
800
  const flags = {
570
801
  flags: [
571
802
  {
@@ -596,12 +827,19 @@ describe('PostHog Node.js', () => {
596
827
  fetchRetryCount: 0,
597
828
  })
598
829
 
830
+ jest.runOnlyPendingTimers()
831
+
599
832
  expect(
600
833
  await posthog.getFeatureFlag('beta-feature', 'some-distinct-id', {
601
834
  personProperties: { region: 'USA', name: 'Aloha' },
602
835
  })
603
836
  ).toEqual(true)
837
+
838
+ // TRICKY: There's now an extra step before events are queued, so need to wait for that to resolve
604
839
  jest.runOnlyPendingTimers()
840
+ await waitForPromises()
841
+ await posthog.flushAsync()
842
+
605
843
  expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
606
844
 
607
845
  expect(getLastBatchEvents()?.[0]).toEqual(
@@ -628,6 +866,8 @@ describe('PostHog Node.js', () => {
628
866
  })
629
867
  ).toEqual(true)
630
868
  jest.runOnlyPendingTimers()
869
+ await waitForPromises()
870
+ await posthog.flushAsync()
631
871
 
632
872
  expect(mockedFetch).not.toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
633
873
 
@@ -640,6 +880,8 @@ describe('PostHog Node.js', () => {
640
880
  })
641
881
  ).toEqual(true)
642
882
  jest.runOnlyPendingTimers()
883
+ await waitForPromises()
884
+ await posthog.flushAsync()
643
885
  expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
644
886
 
645
887
  expect(getLastBatchEvents()?.[0]).toEqual(
@@ -667,6 +909,8 @@ describe('PostHog Node.js', () => {
667
909
  })
668
910
  ).toEqual(true)
669
911
  jest.runOnlyPendingTimers()
912
+ await waitForPromises()
913
+ await posthog.flushAsync()
670
914
  expect(mockedFetch).not.toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
671
915
 
672
916
  // # called for different flag, falls back to decide, should call capture again
@@ -677,6 +921,8 @@ describe('PostHog Node.js', () => {
677
921
  })
678
922
  ).toEqual('decide-value')
679
923
  jest.runOnlyPendingTimers()
924
+ await waitForPromises()
925
+ await posthog.flushAsync()
680
926
  // one to decide, one to batch
681
927
  expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
682
928
  expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
@@ -705,6 +951,8 @@ describe('PostHog Node.js', () => {
705
951
  })
706
952
  ).toEqual(true)
707
953
  jest.runOnlyPendingTimers()
954
+ await waitForPromises()
955
+ await posthog.flushAsync()
708
956
  // call decide, but not batch
709
957
  expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
710
958
  expect(mockedFetch).not.toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
@@ -750,5 +998,154 @@ describe('PostHog Node.js', () => {
750
998
  expect.objectContaining({ method: 'POST', body: expect.not.stringContaining('geoip_disable') })
751
999
  )
752
1000
  })
1001
+
1002
+ it('should add default person & group properties for feature flags', async () => {
1003
+ await posthog.getFeatureFlag('random_key', 'some_id', {
1004
+ groups: { company: 'id:5', instance: 'app.posthog.com' },
1005
+ personProperties: { x1: 'y1' },
1006
+ groupProperties: { company: { x: 'y' } },
1007
+ })
1008
+ jest.runOnlyPendingTimers()
1009
+
1010
+ expect(mockedFetch).toHaveBeenCalledWith(
1011
+ 'http://example.com/decide/?v=3',
1012
+ expect.objectContaining({
1013
+ body: JSON.stringify({
1014
+ token: 'TEST_API_KEY',
1015
+ distinct_id: 'some_id',
1016
+ groups: { company: 'id:5', instance: 'app.posthog.com' },
1017
+ person_properties: {
1018
+ $current_distinct_id: 'some_id',
1019
+ x1: 'y1',
1020
+ },
1021
+ group_properties: {
1022
+ company: { $group_key: 'id:5', x: 'y' },
1023
+ instance: { $group_key: 'app.posthog.com' },
1024
+ },
1025
+ geoip_disable: true,
1026
+ }),
1027
+ })
1028
+ )
1029
+
1030
+ mockedFetch.mockClear()
1031
+
1032
+ await posthog.getFeatureFlag('random_key', 'some_id', {
1033
+ groups: { company: 'id:5', instance: 'app.posthog.com' },
1034
+ personProperties: { $current_distinct_id: 'override' },
1035
+ groupProperties: { company: { $group_key: 'group_override' } },
1036
+ })
1037
+ jest.runOnlyPendingTimers()
1038
+
1039
+ expect(mockedFetch).toHaveBeenCalledWith(
1040
+ 'http://example.com/decide/?v=3',
1041
+ expect.objectContaining({
1042
+ body: JSON.stringify({
1043
+ token: 'TEST_API_KEY',
1044
+ distinct_id: 'some_id',
1045
+ groups: { company: 'id:5', instance: 'app.posthog.com' },
1046
+ person_properties: {
1047
+ $current_distinct_id: 'override',
1048
+ },
1049
+ group_properties: {
1050
+ company: { $group_key: 'group_override' },
1051
+ instance: { $group_key: 'app.posthog.com' },
1052
+ },
1053
+ geoip_disable: true,
1054
+ }),
1055
+ })
1056
+ )
1057
+
1058
+ mockedFetch.mockClear()
1059
+
1060
+ // test nones
1061
+ await posthog.getAllFlagsAndPayloads('some_id', {
1062
+ groups: undefined,
1063
+ personProperties: undefined,
1064
+ groupProperties: undefined,
1065
+ })
1066
+
1067
+ jest.runOnlyPendingTimers()
1068
+
1069
+ expect(mockedFetch).toHaveBeenCalledWith(
1070
+ 'http://example.com/decide/?v=3',
1071
+ expect.objectContaining({
1072
+ body: JSON.stringify({
1073
+ token: 'TEST_API_KEY',
1074
+ distinct_id: 'some_id',
1075
+ groups: {},
1076
+ person_properties: {
1077
+ $current_distinct_id: 'some_id',
1078
+ },
1079
+ group_properties: {},
1080
+ geoip_disable: true,
1081
+ }),
1082
+ })
1083
+ )
1084
+
1085
+ mockedFetch.mockClear()
1086
+ await posthog.getAllFlags('some_id', {
1087
+ groups: { company: 'id:5' },
1088
+ personProperties: undefined,
1089
+ groupProperties: undefined,
1090
+ })
1091
+ jest.runOnlyPendingTimers()
1092
+
1093
+ expect(mockedFetch).toHaveBeenCalledWith(
1094
+ 'http://example.com/decide/?v=3',
1095
+ expect.objectContaining({
1096
+ body: JSON.stringify({
1097
+ token: 'TEST_API_KEY',
1098
+ distinct_id: 'some_id',
1099
+ groups: { company: 'id:5' },
1100
+ person_properties: {
1101
+ $current_distinct_id: 'some_id',
1102
+ },
1103
+ group_properties: { company: { $group_key: 'id:5' } },
1104
+ geoip_disable: true,
1105
+ }),
1106
+ })
1107
+ )
1108
+
1109
+ mockedFetch.mockClear()
1110
+ await posthog.getFeatureFlagPayload('random_key', 'some_id', undefined)
1111
+ jest.runOnlyPendingTimers()
1112
+
1113
+ expect(mockedFetch).toHaveBeenCalledWith(
1114
+ 'http://example.com/decide/?v=3',
1115
+ expect.objectContaining({
1116
+ body: JSON.stringify({
1117
+ token: 'TEST_API_KEY',
1118
+ distinct_id: 'some_id',
1119
+ groups: {},
1120
+ person_properties: {
1121
+ $current_distinct_id: 'some_id',
1122
+ },
1123
+ group_properties: {},
1124
+ geoip_disable: true,
1125
+ }),
1126
+ })
1127
+ )
1128
+
1129
+ mockedFetch.mockClear()
1130
+
1131
+ await posthog.isFeatureEnabled('random_key', 'some_id')
1132
+ jest.runOnlyPendingTimers()
1133
+
1134
+ expect(mockedFetch).toHaveBeenCalledWith(
1135
+ 'http://example.com/decide/?v=3',
1136
+ expect.objectContaining({
1137
+ body: JSON.stringify({
1138
+ token: 'TEST_API_KEY',
1139
+ distinct_id: 'some_id',
1140
+ groups: {},
1141
+ person_properties: {
1142
+ $current_distinct_id: 'some_id',
1143
+ },
1144
+ group_properties: {},
1145
+ geoip_disable: true,
1146
+ }),
1147
+ })
1148
+ )
1149
+ })
753
1150
  })
754
1151
  })