@rotorsoft/act 0.32.1 → 0.32.2

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.
package/dist/index.js CHANGED
@@ -672,6 +672,136 @@ process.once("unhandledRejection", async (arg) => {
672
672
  import { randomUUID as randomUUID2 } from "crypto";
673
673
  import EventEmitter from "events";
674
674
 
675
+ // src/internal/merge.ts
676
+ import { ZodObject } from "zod";
677
+ function baseTypeName(zodType) {
678
+ let t = zodType;
679
+ while (typeof t.unwrap === "function") {
680
+ t = t.unwrap();
681
+ }
682
+ return t.constructor.name;
683
+ }
684
+ function mergeSchemas(existing, incoming, stateName) {
685
+ if (existing instanceof ZodObject && incoming instanceof ZodObject) {
686
+ const existingShape = existing.shape;
687
+ const incomingShape = incoming.shape;
688
+ for (const key of Object.keys(incomingShape)) {
689
+ if (key in existingShape) {
690
+ const existingBase = baseTypeName(existingShape[key]);
691
+ const incomingBase = baseTypeName(incomingShape[key]);
692
+ if (existingBase !== incomingBase) {
693
+ throw new Error(
694
+ `Schema conflict in "${stateName}": key "${key}" has type "${existingBase}" but incoming partial declares "${incomingBase}"`
695
+ );
696
+ }
697
+ }
698
+ }
699
+ return existing.extend(incomingShape);
700
+ }
701
+ return existing;
702
+ }
703
+ function mergeInits(existing, incoming) {
704
+ return () => ({ ...existing(), ...incoming() });
705
+ }
706
+ function registerState(state2, states, actions, events) {
707
+ const existing = states.get(state2.name);
708
+ if (existing) {
709
+ mergeIntoExisting(state2, existing, states, actions, events);
710
+ } else {
711
+ registerNewState(state2, states, actions, events);
712
+ }
713
+ }
714
+ function registerNewState(state2, states, actions, events) {
715
+ states.set(state2.name, state2);
716
+ for (const name of Object.keys(state2.actions)) {
717
+ if (actions[name]) throw new Error(`Duplicate action "${name}"`);
718
+ actions[name] = state2;
719
+ }
720
+ for (const name of Object.keys(state2.events)) {
721
+ if (events[name]) throw new Error(`Duplicate event "${name}"`);
722
+ events[name] = { schema: state2.events[name], reactions: /* @__PURE__ */ new Map() };
723
+ }
724
+ }
725
+ function mergeIntoExisting(state2, existing, states, actions, events) {
726
+ for (const name of Object.keys(state2.actions)) {
727
+ if (existing.actions[name] === state2.actions[name]) continue;
728
+ if (actions[name]) throw new Error(`Duplicate action "${name}"`);
729
+ }
730
+ for (const name of Object.keys(state2.events)) {
731
+ if (existing.events[name] === state2.events[name]) continue;
732
+ if (existing.events[name]) continue;
733
+ if (events[name]) throw new Error(`Duplicate event "${name}"`);
734
+ }
735
+ const mergedPatch = mergePatches(existing.patch, state2.patch, state2.name);
736
+ const merged = {
737
+ ...existing,
738
+ state: mergeSchemas(existing.state, state2.state, state2.name),
739
+ init: mergeInits(existing.init, state2.init),
740
+ events: { ...existing.events, ...state2.events },
741
+ actions: { ...existing.actions, ...state2.actions },
742
+ patch: mergedPatch,
743
+ on: { ...existing.on, ...state2.on },
744
+ given: { ...existing.given, ...state2.given },
745
+ snap: state2.snap && existing.snap && state2.snap !== existing.snap ? (() => {
746
+ throw new Error(
747
+ `Duplicate snap strategy for state "${state2.name}"`
748
+ );
749
+ })() : state2.snap || existing.snap
750
+ };
751
+ states.set(state2.name, merged);
752
+ for (const name of Object.keys(merged.actions)) {
753
+ actions[name] = merged;
754
+ }
755
+ for (const name of Object.keys(state2.events)) {
756
+ if (events[name]) continue;
757
+ events[name] = { schema: state2.events[name], reactions: /* @__PURE__ */ new Map() };
758
+ }
759
+ }
760
+ function mergePatches(existing, incoming, stateName) {
761
+ const merged = { ...existing };
762
+ for (const name of Object.keys(incoming)) {
763
+ const existingP = existing[name];
764
+ const incomingP = incoming[name];
765
+ if (!existingP) {
766
+ merged[name] = incomingP;
767
+ continue;
768
+ }
769
+ const existingIsDefault = existingP._passthrough;
770
+ const incomingIsDefault = incomingP._passthrough;
771
+ if (!existingIsDefault && !incomingIsDefault && existingP !== incomingP) {
772
+ throw new Error(
773
+ `Duplicate custom patch for event "${name}" in state "${stateName}"`
774
+ );
775
+ }
776
+ if (existingIsDefault && !incomingIsDefault) {
777
+ merged[name] = incomingP;
778
+ }
779
+ }
780
+ return merged;
781
+ }
782
+ function mergeProjection(proj, events) {
783
+ for (const eventName of Object.keys(proj.events)) {
784
+ const projRegister = proj.events[eventName];
785
+ const existing = events[eventName];
786
+ if (!existing) {
787
+ events[eventName] = {
788
+ schema: projRegister.schema,
789
+ reactions: new Map(projRegister.reactions)
790
+ };
791
+ } else {
792
+ for (const [name, reaction] of projRegister.reactions) {
793
+ let key = name;
794
+ while (existing.reactions.has(key)) key = `${key}_p`;
795
+ existing.reactions.set(key, reaction);
796
+ }
797
+ }
798
+ }
799
+ }
800
+ var _this_ = ({ stream }) => ({
801
+ source: stream,
802
+ target: stream
803
+ });
804
+
675
805
  // src/internal/drain.ts
676
806
  var claim = (lagging, leading, by, millis) => store().claim(lagging, leading, by, millis);
677
807
  async function fetch(leased, eventLimit) {
@@ -694,7 +824,6 @@ var subscribe = (streams) => store().subscribe(streams);
694
824
  // src/internal/event-sourcing.ts
695
825
  import { patch } from "@rotorsoft/act-patch";
696
826
  import { randomUUID } from "crypto";
697
- var logger2 = log();
698
827
  async function snap(snapshot) {
699
828
  try {
700
829
  const { id, stream, name, meta, version } = snapshot.event;
@@ -709,11 +838,11 @@ async function snap(snapshot) {
709
838
  // IMPORTANT! - state events are committed right after the snapshot event
710
839
  );
711
840
  } catch (error) {
712
- logger2.error(error);
841
+ log().error(error);
713
842
  }
714
843
  }
715
844
  async function load(me, stream, callback, asOf) {
716
- const timeTravel = asOf && (asOf.before !== void 0 || asOf.created_before !== void 0 || asOf.created_after !== void 0 || asOf.limit !== void 0);
845
+ const timeTravel = !!asOf && Object.values(asOf).some((v) => v !== void 0);
717
846
  const cached = timeTravel ? void 0 : await cache().get(stream);
718
847
  let state2 = cached?.state ?? (me.init ? me.init() : {});
719
848
  let patches = cached?.patches ?? 0;
@@ -797,7 +926,7 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
797
926
  reactingTo ? void 0 : expected
798
927
  );
799
928
  } catch (error) {
800
- if (error.name === "ERR_CONCURRENCY") {
929
+ if (error instanceof ConcurrencyError) {
801
930
  await cache().invalidate(stream);
802
931
  }
803
932
  throw error;
@@ -811,241 +940,56 @@ async function action(me, action2, target, payload, reactingTo, skipValidation =
811
940
  });
812
941
  const last = snapshots.at(-1);
813
942
  const snapped = me.snap && me.snap(last);
814
- void cache().set(stream, {
943
+ cache().set(stream, {
815
944
  state: last.state,
816
945
  version: last.event.version,
817
946
  event_id: last.event.id,
818
947
  patches: snapped ? 0 : last.patches,
819
948
  snaps: snapped ? last.snaps + 1 : last.snaps
820
- });
949
+ }).catch((err) => log().error(err));
821
950
  if (snapped) void snap(last);
822
951
  return snapshots;
823
952
  }
824
953
 
825
- // src/internal/merge.ts
826
- import { ZodObject } from "zod";
827
- function baseTypeName(zodType) {
828
- let t = zodType;
829
- while (typeof t.unwrap === "function") {
830
- t = t.unwrap();
831
- }
832
- return t.constructor.name;
833
- }
834
- function mergeSchemas(existing, incoming, stateName) {
835
- if (existing instanceof ZodObject && incoming instanceof ZodObject) {
836
- const existingShape = existing.shape;
837
- const incomingShape = incoming.shape;
838
- for (const key of Object.keys(incomingShape)) {
839
- if (key in existingShape) {
840
- const existingBase = baseTypeName(existingShape[key]);
841
- const incomingBase = baseTypeName(incomingShape[key]);
842
- if (existingBase !== incomingBase) {
843
- throw new Error(
844
- `Schema conflict in "${stateName}": key "${key}" has type "${existingBase}" but incoming partial declares "${incomingBase}"`
845
- );
846
- }
847
- }
848
- }
849
- return existing.extend(incomingShape);
850
- }
851
- return existing;
852
- }
853
- function mergeInits(existing, incoming) {
854
- return () => ({ ...existing(), ...incoming() });
855
- }
856
- function registerState(state2, states, actions, events) {
857
- if (states.has(state2.name)) {
858
- const existing = states.get(state2.name);
859
- for (const name of Object.keys(state2.actions)) {
860
- if (existing.actions[name] === state2.actions[name]) continue;
861
- if (actions[name]) throw new Error(`Duplicate action "${name}"`);
862
- }
863
- for (const name of Object.keys(state2.events)) {
864
- if (existing.events[name] === state2.events[name]) continue;
865
- if (existing.events[name]) continue;
866
- if (events[name]) throw new Error(`Duplicate event "${name}"`);
867
- }
868
- const mergedPatch = { ...existing.patch };
869
- for (const name of Object.keys(state2.patch)) {
870
- const existingP = existing.patch[name];
871
- const incomingP = state2.patch[name];
872
- if (!existingP) {
873
- mergedPatch[name] = incomingP;
874
- } else {
875
- const existingIsDefault = existingP._passthrough;
876
- const incomingIsDefault = incomingP._passthrough;
877
- if (!existingIsDefault && !incomingIsDefault && existingP !== incomingP) {
878
- throw new Error(
879
- `Duplicate custom patch for event "${name}" in state "${state2.name}"`
880
- );
881
- }
882
- if (existingIsDefault && !incomingIsDefault) {
883
- mergedPatch[name] = incomingP;
884
- }
885
- }
886
- }
887
- const merged = {
888
- ...existing,
889
- state: mergeSchemas(existing.state, state2.state, state2.name),
890
- init: mergeInits(existing.init, state2.init),
891
- events: { ...existing.events, ...state2.events },
892
- actions: { ...existing.actions, ...state2.actions },
893
- patch: mergedPatch,
894
- on: { ...existing.on, ...state2.on },
895
- given: { ...existing.given, ...state2.given },
896
- snap: state2.snap && existing.snap && state2.snap !== existing.snap ? (() => {
897
- throw new Error(
898
- `Duplicate snap strategy for state "${state2.name}"`
899
- );
900
- })() : state2.snap || existing.snap
901
- };
902
- states.set(state2.name, merged);
903
- for (const name of Object.keys(merged.actions)) {
904
- actions[name] = merged;
905
- }
906
- for (const name of Object.keys(state2.events)) {
907
- if (events[name]) continue;
908
- events[name] = {
909
- schema: state2.events[name],
910
- reactions: /* @__PURE__ */ new Map()
911
- };
912
- }
913
- } else {
914
- states.set(state2.name, state2);
915
- for (const name of Object.keys(state2.actions)) {
916
- if (actions[name]) throw new Error(`Duplicate action "${name}"`);
917
- actions[name] = state2;
918
- }
919
- for (const name of Object.keys(state2.events)) {
920
- if (events[name]) throw new Error(`Duplicate event "${name}"`);
921
- events[name] = {
922
- schema: state2.events[name],
923
- reactions: /* @__PURE__ */ new Map()
924
- };
925
- }
926
- }
927
- }
928
- function mergeProjection(proj, events) {
929
- for (const eventName of Object.keys(proj.events)) {
930
- const projRegister = proj.events[eventName];
931
- const existing = events[eventName];
932
- if (!existing) {
933
- events[eventName] = {
934
- schema: projRegister.schema,
935
- reactions: new Map(projRegister.reactions)
936
- };
937
- } else {
938
- for (const [name, reaction] of projRegister.reactions) {
939
- let key = name;
940
- while (existing.reactions.has(key)) key = `${key}_p`;
941
- existing.reactions.set(key, reaction);
942
- }
943
- }
944
- }
945
- }
946
- var _this_ = ({ stream }) => ({
947
- source: stream,
948
- target: stream
949
- });
950
-
951
954
  // src/internal/tracing.ts
952
- var logger3 = log();
953
- var withSnapTrace = (inner) => async (snapshot) => {
954
- logger3.trace(
955
- `\u{1F7E0} snap ${snapshot.event.stream}@${snapshot.event.version}`
956
- );
957
- return inner(snapshot);
958
- };
959
- var withLoadTrace = (inner) => async (me, stream, callback, asOf) => {
960
- logger3.trace(`\u{1F7E2} load ${stream}${asOf ? " (as-of)" : ""}`);
961
- return inner(me, stream, callback, asOf);
962
- };
963
- var withActionTrace = (inner) => async (me, action2, target, payload, reactingTo, skipValidation) => {
964
- logger3.trace(payload, `\u{1F535} ${target.stream}.${action2}`);
965
- const snapshots = await inner(
966
- me,
967
- action2,
968
- target,
969
- payload,
970
- reactingTo,
971
- skipValidation
972
- );
973
- const committed = snapshots.filter((s) => s.event);
974
- if (committed.length) {
975
- logger3.trace(
976
- committed.map((s) => s.event.data),
977
- `\u{1F534} commit ${target.stream}.${committed.map((s) => s.event.name).join(", ")}`
978
- );
979
- }
980
- return snapshots;
981
- };
982
- function buildEs(level) {
983
- if (level !== "trace") {
955
+ var traced = (inner, exit, entry) => (async (...args) => {
956
+ entry?.(...args);
957
+ const result = await inner(...args);
958
+ exit?.(result, ...args);
959
+ return result;
960
+ });
961
+ function buildEs(logger2) {
962
+ if (logger2.level !== "trace") {
984
963
  return { snap, load, action };
985
964
  }
986
965
  return {
987
- snap: withSnapTrace(snap),
988
- load: withLoadTrace(load),
989
- action: withActionTrace(action)
966
+ snap: traced(snap, void 0, (snapshot) => {
967
+ logger2.trace(
968
+ `\u{1F7E0} snap ${snapshot.event.stream}@${snapshot.event.version}`
969
+ );
970
+ }),
971
+ load: traced(load, void 0, (_me, stream, _cb, asOf) => {
972
+ logger2.trace(`\u{1F7E2} load ${stream}${asOf ? " (as-of)" : ""}`);
973
+ }),
974
+ action: traced(
975
+ action,
976
+ (snapshots, _me, _action, target) => {
977
+ const committed = snapshots.filter((s) => s.event);
978
+ if (committed.length) {
979
+ logger2.trace(
980
+ committed.map((s) => s.event.data),
981
+ `\u{1F534} commit ${target.stream}.${committed.map((s) => s.event.name).join(", ")}`
982
+ );
983
+ }
984
+ },
985
+ (_me, action2, target, payload) => {
986
+ logger2.trace(payload, `\u{1F535} ${target.stream}.${action2}`);
987
+ }
988
+ )
990
989
  };
991
990
  }
992
- var withClaimTrace = (inner) => async (lagging, leading, by, millis) => {
993
- const leased = await inner(lagging, leading, by, millis);
994
- if (leased.length) {
995
- const data = Object.fromEntries(
996
- leased.map(({ stream, at, retry }) => [stream, { at, retry }])
997
- );
998
- logger3.trace(data, ">> lease");
999
- }
1000
- return leased;
1001
- };
1002
- var withFetchTrace = (inner) => async (leased, eventLimit) => {
1003
- const fetched = await inner(leased, eventLimit);
1004
- const data = Object.fromEntries(
1005
- fetched.map(({ stream, source, events }) => {
1006
- const key = source ? `${stream}<-${source}` : stream;
1007
- const value = Object.fromEntries(
1008
- events.map(({ id, stream: stream2, name }) => [id, { [stream2]: name }])
1009
- );
1010
- return [key, value];
1011
- })
1012
- );
1013
- logger3.trace(data, ">> fetch");
1014
- return fetched;
1015
- };
1016
- var withAckTrace = (inner) => async (leases) => {
1017
- const acked = await inner(leases);
1018
- if (acked.length) {
1019
- const data = Object.fromEntries(
1020
- acked.map(({ stream, at, retry }) => [stream, { at, retry }])
1021
- );
1022
- logger3.trace(data, ">> ack");
1023
- }
1024
- return acked;
1025
- };
1026
- var withBlockTrace = (inner) => async (leases) => {
1027
- const blocked = await inner(leases);
1028
- if (blocked.length) {
1029
- const data = Object.fromEntries(
1030
- blocked.map(({ stream, at, retry, error }) => [
1031
- stream,
1032
- { at, retry, error }
1033
- ])
1034
- );
1035
- logger3.trace(data, ">> block");
1036
- }
1037
- return blocked;
1038
- };
1039
- var withSubscribeTrace = (inner) => async (streams) => {
1040
- const result = await inner(streams);
1041
- if (result.subscribed) {
1042
- const data = streams.map(({ stream }) => stream).join(" ");
1043
- logger3.trace(`>> correlate ${data}`);
1044
- }
1045
- return result;
1046
- };
1047
- function buildDrain(level) {
1048
- if (level !== "trace") {
991
+ function buildDrain(logger2) {
992
+ if (logger2.level !== "trace") {
1049
993
  return {
1050
994
  claim,
1051
995
  fetch,
@@ -1055,24 +999,62 @@ function buildDrain(level) {
1055
999
  };
1056
1000
  }
1057
1001
  return {
1058
- claim: withClaimTrace(claim),
1059
- fetch: withFetchTrace(fetch),
1060
- ack: withAckTrace(ack),
1061
- block: withBlockTrace(block),
1062
- subscribe: withSubscribeTrace(subscribe)
1002
+ claim: traced(claim, (leased) => {
1003
+ if (leased.length) {
1004
+ const data = Object.fromEntries(
1005
+ leased.map(({ stream, at, retry }) => [stream, { at, retry }])
1006
+ );
1007
+ logger2.trace(data, ">> lease");
1008
+ }
1009
+ }),
1010
+ fetch: traced(fetch, (fetched) => {
1011
+ const data = Object.fromEntries(
1012
+ fetched.map(({ stream, source, events }) => {
1013
+ const key = source ? `${stream}<-${source}` : stream;
1014
+ const value = Object.fromEntries(
1015
+ events.map(({ id, stream: stream2, name }) => [id, { [stream2]: name }])
1016
+ );
1017
+ return [key, value];
1018
+ })
1019
+ );
1020
+ logger2.trace(data, ">> fetch");
1021
+ }),
1022
+ ack: traced(ack, (acked) => {
1023
+ if (acked.length) {
1024
+ const data = Object.fromEntries(
1025
+ acked.map(({ stream, at, retry }) => [stream, { at, retry }])
1026
+ );
1027
+ logger2.trace(data, ">> ack");
1028
+ }
1029
+ }),
1030
+ block: traced(block, (blocked) => {
1031
+ if (blocked.length) {
1032
+ const data = Object.fromEntries(
1033
+ blocked.map(({ stream, at, retry, error }) => [
1034
+ stream,
1035
+ { at, retry, error }
1036
+ ])
1037
+ );
1038
+ logger2.trace(data, ">> block");
1039
+ }
1040
+ }),
1041
+ subscribe: traced(subscribe, (result, streams) => {
1042
+ if (result.subscribed) {
1043
+ const data = streams.map(({ stream }) => stream).join(" ");
1044
+ logger2.trace(`>> correlate ${data}`);
1045
+ }
1046
+ })
1063
1047
  };
1064
1048
  }
1065
1049
 
1066
1050
  // src/act.ts
1067
- var logger4 = log();
1068
1051
  var Act = class {
1069
1052
  constructor(registry, _states = /* @__PURE__ */ new Map(), batchHandlers = /* @__PURE__ */ new Map()) {
1070
1053
  this.registry = registry;
1071
1054
  this._states = _states;
1072
1055
  this._batch_handlers = batchHandlers;
1073
- const level = log().level;
1074
- this._es = buildEs(level);
1075
- this._cd = buildDrain(level);
1056
+ this._es = buildEs(this._logger);
1057
+ this._cd = buildDrain(this._logger);
1076
1058
  const statics = [];
1077
1059
  for (const [name, register] of Object.entries(this.registry.events)) {
1078
1060
  if (register.reactions.size > 0) {
@@ -1136,6 +1118,8 @@ var Act = class {
1136
1118
  _es;
1137
1119
  /** Correlate/drain pipeline ops, optionally wrapped with trace decorators */
1138
1120
  _cd;
1121
+ /** Logger resolved at construction time (after user port configuration) */
1122
+ _logger = log();
1139
1123
  /**
1140
1124
  * Executes an action on a state instance, committing resulting events.
1141
1125
  *
@@ -1357,7 +1341,7 @@ var Act = class {
1357
1341
  if (payloads.length === 0) return { lease, handled: 0, at: lease.at };
1358
1342
  const stream = lease.stream;
1359
1343
  let at = payloads.at(0).event.id, handled = 0;
1360
- lease.retry > 0 && logger4.warn(`Retrying ${stream}@${at} (${lease.retry}).`);
1344
+ lease.retry > 0 && this._logger.warn(`Retrying ${stream}@${at} (${lease.retry}).`);
1361
1345
  const doAction = this.do.bind(this);
1362
1346
  const scopedApp = {
1363
1347
  do: doAction,
@@ -1379,9 +1363,11 @@ var Act = class {
1379
1363
  at = event.id;
1380
1364
  handled++;
1381
1365
  } catch (error) {
1382
- logger4.error(error);
1366
+ this._logger.error(error);
1383
1367
  const block2 = lease.retry >= options.maxRetries && options.blockOnError;
1384
- block2 && logger4.error(`Blocking ${stream} after ${lease.retry} retries.`);
1368
+ block2 && this._logger.error(
1369
+ `Blocking ${stream} after ${lease.retry} retries.`
1370
+ );
1385
1371
  return {
1386
1372
  lease,
1387
1373
  handled,
@@ -1411,15 +1397,17 @@ var Act = class {
1411
1397
  const stream = lease.stream;
1412
1398
  const events = payloads.map((p) => p.event);
1413
1399
  const at = events.at(-1).id;
1414
- lease.retry > 0 && logger4.warn(`Retrying batch ${stream}@${events[0].id} (${lease.retry}).`);
1400
+ lease.retry > 0 && this._logger.warn(
1401
+ `Retrying batch ${stream}@${events[0].id} (${lease.retry}).`
1402
+ );
1415
1403
  try {
1416
1404
  await batchHandler(events, stream);
1417
1405
  return { lease, handled: events.length, at };
1418
1406
  } catch (error) {
1419
- logger4.error(error);
1407
+ this._logger.error(error);
1420
1408
  const { options } = payloads[0];
1421
1409
  const block2 = lease.retry >= options.maxRetries && options.blockOnError;
1422
- block2 && logger4.error(`Blocking ${stream} after ${lease.retry} retries.`);
1410
+ block2 && this._logger.error(`Blocking ${stream} after ${lease.retry} retries.`);
1423
1411
  return {
1424
1412
  lease,
1425
1413
  handled: 0,
@@ -1545,7 +1533,7 @@ var Act = class {
1545
1533
  this._needs_drain = false;
1546
1534
  return result;
1547
1535
  } catch (error) {
1548
- logger4.error(error);
1536
+ this._logger.error(error);
1549
1537
  } finally {
1550
1538
  this._drain_locked = false;
1551
1539
  }
@@ -2025,7 +2013,7 @@ var Act = class {
2025
2013
  if (!made_progress) break;
2026
2014
  }
2027
2015
  if (lastDrain) this.emit("settled", lastDrain);
2028
- })().catch((err) => logger4.error(err)).finally(() => {
2016
+ })().catch((err) => this._logger.error(err)).finally(() => {
2029
2017
  this._settling = false;
2030
2018
  });
2031
2019
  }, debounceMs);
@@ -2272,8 +2260,9 @@ function state(entry) {
2272
2260
  emits(events) {
2273
2261
  const defaultPatch = Object.fromEntries(
2274
2262
  Object.keys(events).map((k) => {
2275
- const fn = ({ data }) => data;
2276
- fn._passthrough = true;
2263
+ const fn = Object.assign(({ data }) => data, {
2264
+ _passthrough: true
2265
+ });
2277
2266
  return [k, fn];
2278
2267
  })
2279
2268
  );