@player-ui/react-subscribe 0.11.3--canary.670.24048 → 0.11.3--canary.666.24070

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.
@@ -35,7 +35,6 @@ __export(src_exports, {
35
35
  });
36
36
  module.exports = __toCommonJS(src_exports);
37
37
  var import_react = __toESM(require("react"));
38
- var import_shim = require("use-sync-external-store/shim");
39
38
  function deferred() {
40
39
  let resolve = () => void 0;
41
40
  let reject = () => void 0;
@@ -132,36 +131,20 @@ var Subscribe = class {
132
131
  }
133
132
  };
134
133
  function useSubscribedState(subscriber) {
135
- const subscription = import_react.default.useMemo(() => {
136
- function subscribe(callback) {
137
- const id = subscriber.add(
138
- (resp) => {
139
- callback(resp);
140
- },
141
- {
142
- initializeWithPreviousValue: true
143
- }
144
- );
145
- return () => {
146
- if (subscriber) {
147
- subscriber.remove(id);
148
- }
149
- };
150
- }
151
- return subscribe;
134
+ const [state, setState] = import_react.default.useState(subscriber.get());
135
+ import_react.default.useEffect(() => {
136
+ const id = subscriber.add(
137
+ (resp) => {
138
+ setState(resp);
139
+ },
140
+ {
141
+ initializeWithPreviousValue: true
142
+ }
143
+ );
144
+ return () => {
145
+ subscriber.remove(id);
146
+ };
152
147
  }, [subscriber]);
153
- function getSnapshot() {
154
- try {
155
- return subscriber.get();
156
- } catch (err) {
157
- return void 0;
158
- }
159
- }
160
- const state = (0, import_shim.useSyncExternalStore)(
161
- subscription,
162
- getSnapshot,
163
- () => void 0
164
- );
165
148
  return state;
166
149
  }
167
150
  // Annotate the CommonJS export names for ESM import in node:
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/react/subscribe/src/index.tsx"],"sourcesContent":["import React from \"react\";\nimport { useSyncExternalStore } from \"use-sync-external-store/shim\";\n\nexport type SubscribeID = number;\n\ntype ResolveType<T> = (arg?: T) => void;\ntype RejectType = (error?: Error) => void;\ntype StatusType = \"success\" | \"failure\" | \"pending\";\ntype DefferedReturnType<T> = {\n /** a function to resolve the promise */\n resolve: ResolveType<T>;\n\n /** a function to reject the promise */\n reject: RejectType;\n\n /** the status of the promise */\n status: StatusType;\n\n /** a promise to express the above */\n promise: Promise<T>;\n};\n\n/** create a deferred promise */\nfunction deferred<T>(): DefferedReturnType<T> {\n /** the default resolve handler is a noop */\n let resolve: ResolveType<T> = () => undefined;\n\n /** the default reject handler is a noop */\n let reject: RejectType = () => undefined;\n\n let status: StatusType = \"pending\";\n\n const promise = new Promise<T>((res, rej) => {\n resolve = (a?: T) => {\n status = \"success\";\n const resolveFunc = res as ResolveType<T>;\n resolveFunc(a);\n };\n\n reject = (error?: Error) => {\n status = \"failure\";\n rej(error);\n };\n });\n\n return {\n resolve,\n status,\n reject,\n promise,\n };\n}\n\nconst NOT_CALLED = Symbol(\"Subscribe -- Empty Value\");\n/**\n * A pub-sub module that works across the React bridge\n */\nexport class Subscribe<T> {\n private callbacks: Map<SubscribeID, (val: T | undefined) => void> = new Map();\n private deferredResult = deferred<T>();\n private lastValue: T | typeof NOT_CALLED = NOT_CALLED;\n private resetDeferred: DefferedReturnType<void> | null = null;\n constructor() {\n this.publish = this.publish.bind(this);\n this.add = this.add.bind(this);\n this.remove = this.remove.bind(this);\n }\n\n /**\n * Trigger the subscriptions using the provided value\n * if there is a reset in progress, wait for it before publishing a new value.\n */\n async publish(val: T): Promise<void> {\n await this.resetDeferred?.promise;\n this.lastValue = val;\n this.deferredResult.resolve(val);\n this.callbacks.forEach((c) => c(val));\n }\n\n /**\n * Subscribe to updates\n */\n add(\n callback: (arg: T | undefined) => void,\n options?: {\n /** Use the last updated value for this subscription to immediately trigger the onSet callback */\n initializeWithPreviousValue?: boolean;\n },\n ): SubscribeID {\n const id = this.callbacks.size;\n this.callbacks.set(id, callback);\n\n if (\n this.lastValue !== NOT_CALLED &&\n options?.initializeWithPreviousValue === true\n ) {\n callback(this.lastValue);\n }\n\n return id;\n }\n\n /**\n * Remove any updates from the given listener\n */\n remove(id: SubscribeID): void {\n this.callbacks.delete(id);\n }\n\n /**\n * Reset the state of the listener\n * Passing in a promise will defer resetting the view until the promise is resolved\n */\n async reset(promise?: Promise<void>): Promise<void> {\n if (promise) {\n this.resetDeferred = deferred<void>();\n await promise;\n }\n\n if (this.lastValue !== NOT_CALLED) {\n this.deferredResult = deferred();\n }\n\n this.lastValue = NOT_CALLED;\n this.callbacks.forEach((c) => c(undefined));\n\n this.resetDeferred?.resolve();\n this.resetDeferred = null;\n }\n\n /**\n * _Throws_ a promise if the value is still pending\n * Otherwise returns it\n */\n suspend(): T {\n if (this.lastValue === NOT_CALLED) {\n throw this.deferredResult.promise;\n }\n\n return this.lastValue;\n }\n\n /** Get the current value of the subscription */\n get(): T | undefined {\n if (this.lastValue === NOT_CALLED) {\n return undefined;\n }\n\n return this.lastValue;\n }\n}\n\nexport interface SubscribedStateHookOptions {\n /** if the state should trigger suspense when waiting to resolve */\n suspend?: boolean;\n}\n\n/** Subscribe to a state change event in a react component */\nexport function useSubscribedState<T>(subscriber: Subscribe<T>): T | undefined {\n const subscription = React.useMemo(() => {\n function subscribe(callback: (val?: T) => void) {\n const id = subscriber.add(\n (resp) => {\n callback(resp);\n },\n {\n initializeWithPreviousValue: true,\n },\n );\n\n return () => {\n if (subscriber) {\n subscriber.remove(id);\n }\n };\n }\n\n return subscribe;\n }, [subscriber]);\n\n function getSnapshot() {\n try {\n return subscriber.get();\n } catch (err) {\n return undefined;\n }\n }\n\n const state = useSyncExternalStore(\n subscription,\n getSnapshot,\n () => undefined,\n );\n\n return state;\n}\n"],"mappings":"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","names":["React"]}
1
+ {"version":3,"sources":["../../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/react/subscribe/src/index.tsx"],"sourcesContent":["import React from \"react\";\n\nexport type SubscribeID = number;\n\ntype ResolveType<T> = (arg?: T) => void;\ntype RejectType = (error?: Error) => void;\ntype StatusType = \"success\" | \"failure\" | \"pending\";\ntype DefferedReturnType<T> = {\n /** a function to resolve the promise */\n resolve: ResolveType<T>;\n\n /** a function to reject the promise */\n reject: RejectType;\n\n /** the status of the promise */\n status: StatusType;\n\n /** a promise to express the above */\n promise: Promise<T>;\n};\n\n/** create a deferred promise */\nfunction deferred<T>(): DefferedReturnType<T> {\n /** the default resolve handler is a noop */\n let resolve: ResolveType<T> = () => undefined;\n\n /** the default reject handler is a noop */\n let reject: RejectType = () => undefined;\n\n let status: StatusType = \"pending\";\n\n const promise = new Promise<T>((res, rej) => {\n resolve = (a?: T) => {\n status = \"success\";\n const resolveFunc = res as ResolveType<T>;\n resolveFunc(a);\n };\n\n reject = (error?: Error) => {\n status = \"failure\";\n rej(error);\n };\n });\n\n return {\n resolve,\n status,\n reject,\n promise,\n };\n}\n\nconst NOT_CALLED = Symbol(\"Subscribe -- Empty Value\");\n/**\n * A pub-sub module that works across the React bridge\n */\nexport class Subscribe<T> {\n private callbacks: Map<SubscribeID, (val: T | undefined) => void> = new Map();\n private deferredResult = deferred<T>();\n private lastValue: T | typeof NOT_CALLED = NOT_CALLED;\n private resetDeferred: DefferedReturnType<void> | null = null;\n constructor() {\n this.publish = this.publish.bind(this);\n this.add = this.add.bind(this);\n this.remove = this.remove.bind(this);\n }\n\n /**\n * Trigger the subscriptions using the provided value\n * if there is a reset in progress, wait for it before publishing a new value.\n */\n async publish(val: T): Promise<void> {\n await this.resetDeferred?.promise;\n this.lastValue = val;\n this.deferredResult.resolve(val);\n this.callbacks.forEach((c) => c(val));\n }\n\n /**\n * Subscribe to updates\n */\n add(\n callback: (arg: T | undefined) => void,\n options?: {\n /** Use the last updated value for this subscription to immediately trigger the onSet callback */\n initializeWithPreviousValue?: boolean;\n },\n ): SubscribeID {\n const id = this.callbacks.size;\n this.callbacks.set(id, callback);\n\n if (\n this.lastValue !== NOT_CALLED &&\n options?.initializeWithPreviousValue === true\n ) {\n callback(this.lastValue);\n }\n\n return id;\n }\n\n /**\n * Remove any updates from the given listener\n */\n remove(id: SubscribeID) {\n this.callbacks.delete(id);\n }\n\n /**\n * Reset the state of the listener\n * Passing in a promise will defer resetting the view until the promise is resolved\n */\n async reset(promise?: Promise<void>) {\n if (promise) {\n this.resetDeferred = deferred<void>();\n await promise;\n }\n\n if (this.lastValue !== NOT_CALLED) {\n this.deferredResult = deferred();\n }\n\n this.lastValue = NOT_CALLED;\n this.callbacks.forEach((c) => c(undefined));\n\n this.resetDeferred?.resolve();\n this.resetDeferred = null;\n }\n\n /**\n * _Throws_ a promise if the value is still pending\n * Otherwise returns it\n */\n suspend(): T {\n if (this.lastValue === NOT_CALLED) {\n throw this.deferredResult.promise;\n }\n\n return this.lastValue;\n }\n\n /** Get the current value of the subscription */\n get(): T | undefined {\n if (this.lastValue === NOT_CALLED) {\n return undefined;\n }\n\n return this.lastValue;\n }\n}\n\nexport interface SubscribedStateHookOptions {\n /** if the state should trigger suspense when waiting to resolve */\n suspend?: boolean;\n}\n\n/** Subscribe to a state change event in a react component */\nexport function useSubscribedState<T>(subscriber: Subscribe<T>): T | undefined {\n const [state, setState] = React.useState<T | undefined>(subscriber.get());\n\n React.useEffect(() => {\n const id = subscriber.add(\n (resp) => {\n setState(resp);\n },\n {\n initializeWithPreviousValue: true,\n },\n );\n\n return () => {\n subscriber.remove(id);\n };\n }, [subscriber]);\n\n return state;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAkB;AAsBlB,SAAS,WAAqC;AAE5C,MAAI,UAA0B,MAAM;AAGpC,MAAI,SAAqB,MAAM;AAE/B,MAAI,SAAqB;AAEzB,QAAM,UAAU,IAAI,QAAW,CAAC,KAAK,QAAQ;AAC3C,cAAU,CAAC,MAAU;AACnB,eAAS;AACT,YAAM,cAAc;AACpB,kBAAY,CAAC;AAAA,IACf;AAEA,aAAS,CAAC,UAAkB;AAC1B,eAAS;AACT,UAAI,KAAK;AAAA,IACX;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,IAAM,aAAa,OAAO,0BAA0B;AAI7C,IAAM,YAAN,MAAmB;AAAA,EAKxB,cAAc;AAJd,SAAQ,YAA4D,oBAAI,IAAI;AAC5E,SAAQ,iBAAiB,SAAY;AACrC,SAAQ,YAAmC;AAC3C,SAAQ,gBAAiD;AAEvD,SAAK,UAAU,KAAK,QAAQ,KAAK,IAAI;AACrC,SAAK,MAAM,KAAK,IAAI,KAAK,IAAI;AAC7B,SAAK,SAAS,KAAK,OAAO,KAAK,IAAI;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAQ,KAAuB;AACnC,UAAM,KAAK,eAAe;AAC1B,SAAK,YAAY;AACjB,SAAK,eAAe,QAAQ,GAAG;AAC/B,SAAK,UAAU,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,IACE,UACA,SAIa;AACb,UAAM,KAAK,KAAK,UAAU;AAC1B,SAAK,UAAU,IAAI,IAAI,QAAQ;AAE/B,QACE,KAAK,cAAc,cACnB,SAAS,gCAAgC,MACzC;AACA,eAAS,KAAK,SAAS;AAAA,IACzB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,IAAiB;AACtB,SAAK,UAAU,OAAO,EAAE;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAM,SAAyB;AACnC,QAAI,SAAS;AACX,WAAK,gBAAgB,SAAe;AACpC,YAAM;AAAA,IACR;AAEA,QAAI,KAAK,cAAc,YAAY;AACjC,WAAK,iBAAiB,SAAS;AAAA,IACjC;AAEA,SAAK,YAAY;AACjB,SAAK,UAAU,QAAQ,CAAC,MAAM,EAAE,MAAS,CAAC;AAE1C,SAAK,eAAe,QAAQ;AAC5B,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAa;AACX,QAAI,KAAK,cAAc,YAAY;AACjC,YAAM,KAAK,eAAe;AAAA,IAC5B;AAEA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,MAAqB;AACnB,QAAI,KAAK,cAAc,YAAY;AACjC,aAAO;AAAA,IACT;AAEA,WAAO,KAAK;AAAA,EACd;AACF;AAQO,SAAS,mBAAsB,YAAyC;AAC7E,QAAM,CAAC,OAAO,QAAQ,IAAI,aAAAA,QAAM,SAAwB,WAAW,IAAI,CAAC;AAExE,eAAAA,QAAM,UAAU,MAAM;AACpB,UAAM,KAAK,WAAW;AAAA,MACpB,CAAC,SAAS;AACR,iBAAS,IAAI;AAAA,MACf;AAAA,MACA;AAAA,QACE,6BAA6B;AAAA,MAC/B;AAAA,IACF;AAEA,WAAO,MAAM;AACX,iBAAW,OAAO,EAAE;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,UAAU,CAAC;AAEf,SAAO;AACT;","names":["React"]}
@@ -1,6 +1,5 @@
1
1
  // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/react/subscribe/src/index.tsx
2
2
  import React from "react";
3
- import { useSyncExternalStore } from "use-sync-external-store/shim";
4
3
  function deferred() {
5
4
  let resolve = () => void 0;
6
5
  let reject = () => void 0;
@@ -97,36 +96,20 @@ var Subscribe = class {
97
96
  }
98
97
  };
99
98
  function useSubscribedState(subscriber) {
100
- const subscription = React.useMemo(() => {
101
- function subscribe(callback) {
102
- const id = subscriber.add(
103
- (resp) => {
104
- callback(resp);
105
- },
106
- {
107
- initializeWithPreviousValue: true
108
- }
109
- );
110
- return () => {
111
- if (subscriber) {
112
- subscriber.remove(id);
113
- }
114
- };
115
- }
116
- return subscribe;
99
+ const [state, setState] = React.useState(subscriber.get());
100
+ React.useEffect(() => {
101
+ const id = subscriber.add(
102
+ (resp) => {
103
+ setState(resp);
104
+ },
105
+ {
106
+ initializeWithPreviousValue: true
107
+ }
108
+ );
109
+ return () => {
110
+ subscriber.remove(id);
111
+ };
117
112
  }, [subscriber]);
118
- function getSnapshot() {
119
- try {
120
- return subscriber.get();
121
- } catch (err) {
122
- return void 0;
123
- }
124
- }
125
- const state = useSyncExternalStore(
126
- subscription,
127
- getSnapshot,
128
- () => void 0
129
- );
130
113
  return state;
131
114
  }
132
115
  export {
package/dist/index.mjs CHANGED
@@ -1,6 +1,5 @@
1
1
  // ../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/react/subscribe/src/index.tsx
2
2
  import React from "react";
3
- import { useSyncExternalStore } from "use-sync-external-store/shim";
4
3
  function deferred() {
5
4
  let resolve = () => void 0;
6
5
  let reject = () => void 0;
@@ -97,36 +96,20 @@ var Subscribe = class {
97
96
  }
98
97
  };
99
98
  function useSubscribedState(subscriber) {
100
- const subscription = React.useMemo(() => {
101
- function subscribe(callback) {
102
- const id = subscriber.add(
103
- (resp) => {
104
- callback(resp);
105
- },
106
- {
107
- initializeWithPreviousValue: true
108
- }
109
- );
110
- return () => {
111
- if (subscriber) {
112
- subscriber.remove(id);
113
- }
114
- };
115
- }
116
- return subscribe;
99
+ const [state, setState] = React.useState(subscriber.get());
100
+ React.useEffect(() => {
101
+ const id = subscriber.add(
102
+ (resp) => {
103
+ setState(resp);
104
+ },
105
+ {
106
+ initializeWithPreviousValue: true
107
+ }
108
+ );
109
+ return () => {
110
+ subscriber.remove(id);
111
+ };
117
112
  }, [subscriber]);
118
- function getSnapshot() {
119
- try {
120
- return subscriber.get();
121
- } catch (err) {
122
- return void 0;
123
- }
124
- }
125
- const state = useSyncExternalStore(
126
- subscription,
127
- getSnapshot,
128
- () => void 0
129
- );
130
113
  return state;
131
114
  }
132
115
  export {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/react/subscribe/src/index.tsx"],"sourcesContent":["import React from \"react\";\nimport { useSyncExternalStore } from \"use-sync-external-store/shim\";\n\nexport type SubscribeID = number;\n\ntype ResolveType<T> = (arg?: T) => void;\ntype RejectType = (error?: Error) => void;\ntype StatusType = \"success\" | \"failure\" | \"pending\";\ntype DefferedReturnType<T> = {\n /** a function to resolve the promise */\n resolve: ResolveType<T>;\n\n /** a function to reject the promise */\n reject: RejectType;\n\n /** the status of the promise */\n status: StatusType;\n\n /** a promise to express the above */\n promise: Promise<T>;\n};\n\n/** create a deferred promise */\nfunction deferred<T>(): DefferedReturnType<T> {\n /** the default resolve handler is a noop */\n let resolve: ResolveType<T> = () => undefined;\n\n /** the default reject handler is a noop */\n let reject: RejectType = () => undefined;\n\n let status: StatusType = \"pending\";\n\n const promise = new Promise<T>((res, rej) => {\n resolve = (a?: T) => {\n status = \"success\";\n const resolveFunc = res as ResolveType<T>;\n resolveFunc(a);\n };\n\n reject = (error?: Error) => {\n status = \"failure\";\n rej(error);\n };\n });\n\n return {\n resolve,\n status,\n reject,\n promise,\n };\n}\n\nconst NOT_CALLED = Symbol(\"Subscribe -- Empty Value\");\n/**\n * A pub-sub module that works across the React bridge\n */\nexport class Subscribe<T> {\n private callbacks: Map<SubscribeID, (val: T | undefined) => void> = new Map();\n private deferredResult = deferred<T>();\n private lastValue: T | typeof NOT_CALLED = NOT_CALLED;\n private resetDeferred: DefferedReturnType<void> | null = null;\n constructor() {\n this.publish = this.publish.bind(this);\n this.add = this.add.bind(this);\n this.remove = this.remove.bind(this);\n }\n\n /**\n * Trigger the subscriptions using the provided value\n * if there is a reset in progress, wait for it before publishing a new value.\n */\n async publish(val: T): Promise<void> {\n await this.resetDeferred?.promise;\n this.lastValue = val;\n this.deferredResult.resolve(val);\n this.callbacks.forEach((c) => c(val));\n }\n\n /**\n * Subscribe to updates\n */\n add(\n callback: (arg: T | undefined) => void,\n options?: {\n /** Use the last updated value for this subscription to immediately trigger the onSet callback */\n initializeWithPreviousValue?: boolean;\n },\n ): SubscribeID {\n const id = this.callbacks.size;\n this.callbacks.set(id, callback);\n\n if (\n this.lastValue !== NOT_CALLED &&\n options?.initializeWithPreviousValue === true\n ) {\n callback(this.lastValue);\n }\n\n return id;\n }\n\n /**\n * Remove any updates from the given listener\n */\n remove(id: SubscribeID): void {\n this.callbacks.delete(id);\n }\n\n /**\n * Reset the state of the listener\n * Passing in a promise will defer resetting the view until the promise is resolved\n */\n async reset(promise?: Promise<void>): Promise<void> {\n if (promise) {\n this.resetDeferred = deferred<void>();\n await promise;\n }\n\n if (this.lastValue !== NOT_CALLED) {\n this.deferredResult = deferred();\n }\n\n this.lastValue = NOT_CALLED;\n this.callbacks.forEach((c) => c(undefined));\n\n this.resetDeferred?.resolve();\n this.resetDeferred = null;\n }\n\n /**\n * _Throws_ a promise if the value is still pending\n * Otherwise returns it\n */\n suspend(): T {\n if (this.lastValue === NOT_CALLED) {\n throw this.deferredResult.promise;\n }\n\n return this.lastValue;\n }\n\n /** Get the current value of the subscription */\n get(): T | undefined {\n if (this.lastValue === NOT_CALLED) {\n return undefined;\n }\n\n return this.lastValue;\n }\n}\n\nexport interface SubscribedStateHookOptions {\n /** if the state should trigger suspense when waiting to resolve */\n suspend?: boolean;\n}\n\n/** Subscribe to a state change event in a react component */\nexport function useSubscribedState<T>(subscriber: Subscribe<T>): T | undefined {\n const subscription = React.useMemo(() => {\n function subscribe(callback: (val?: T) => void) {\n const id = subscriber.add(\n (resp) => {\n callback(resp);\n },\n {\n initializeWithPreviousValue: true,\n },\n );\n\n return () => {\n if (subscriber) {\n subscriber.remove(id);\n }\n };\n }\n\n return subscribe;\n }, [subscriber]);\n\n function getSnapshot() {\n try {\n return subscriber.get();\n } catch (err) {\n return undefined;\n }\n }\n\n const state = useSyncExternalStore(\n subscription,\n getSnapshot,\n () => undefined,\n );\n\n return state;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../../../../../../../../execroot/_main/bazel-out/k8-fastbuild/bin/react/subscribe/src/index.tsx"],"sourcesContent":["import React from \"react\";\n\nexport type SubscribeID = number;\n\ntype ResolveType<T> = (arg?: T) => void;\ntype RejectType = (error?: Error) => void;\ntype StatusType = \"success\" | \"failure\" | \"pending\";\ntype DefferedReturnType<T> = {\n /** a function to resolve the promise */\n resolve: ResolveType<T>;\n\n /** a function to reject the promise */\n reject: RejectType;\n\n /** the status of the promise */\n status: StatusType;\n\n /** a promise to express the above */\n promise: Promise<T>;\n};\n\n/** create a deferred promise */\nfunction deferred<T>(): DefferedReturnType<T> {\n /** the default resolve handler is a noop */\n let resolve: ResolveType<T> = () => undefined;\n\n /** the default reject handler is a noop */\n let reject: RejectType = () => undefined;\n\n let status: StatusType = \"pending\";\n\n const promise = new Promise<T>((res, rej) => {\n resolve = (a?: T) => {\n status = \"success\";\n const resolveFunc = res as ResolveType<T>;\n resolveFunc(a);\n };\n\n reject = (error?: Error) => {\n status = \"failure\";\n rej(error);\n };\n });\n\n return {\n resolve,\n status,\n reject,\n promise,\n };\n}\n\nconst NOT_CALLED = Symbol(\"Subscribe -- Empty Value\");\n/**\n * A pub-sub module that works across the React bridge\n */\nexport class Subscribe<T> {\n private callbacks: Map<SubscribeID, (val: T | undefined) => void> = new Map();\n private deferredResult = deferred<T>();\n private lastValue: T | typeof NOT_CALLED = NOT_CALLED;\n private resetDeferred: DefferedReturnType<void> | null = null;\n constructor() {\n this.publish = this.publish.bind(this);\n this.add = this.add.bind(this);\n this.remove = this.remove.bind(this);\n }\n\n /**\n * Trigger the subscriptions using the provided value\n * if there is a reset in progress, wait for it before publishing a new value.\n */\n async publish(val: T): Promise<void> {\n await this.resetDeferred?.promise;\n this.lastValue = val;\n this.deferredResult.resolve(val);\n this.callbacks.forEach((c) => c(val));\n }\n\n /**\n * Subscribe to updates\n */\n add(\n callback: (arg: T | undefined) => void,\n options?: {\n /** Use the last updated value for this subscription to immediately trigger the onSet callback */\n initializeWithPreviousValue?: boolean;\n },\n ): SubscribeID {\n const id = this.callbacks.size;\n this.callbacks.set(id, callback);\n\n if (\n this.lastValue !== NOT_CALLED &&\n options?.initializeWithPreviousValue === true\n ) {\n callback(this.lastValue);\n }\n\n return id;\n }\n\n /**\n * Remove any updates from the given listener\n */\n remove(id: SubscribeID) {\n this.callbacks.delete(id);\n }\n\n /**\n * Reset the state of the listener\n * Passing in a promise will defer resetting the view until the promise is resolved\n */\n async reset(promise?: Promise<void>) {\n if (promise) {\n this.resetDeferred = deferred<void>();\n await promise;\n }\n\n if (this.lastValue !== NOT_CALLED) {\n this.deferredResult = deferred();\n }\n\n this.lastValue = NOT_CALLED;\n this.callbacks.forEach((c) => c(undefined));\n\n this.resetDeferred?.resolve();\n this.resetDeferred = null;\n }\n\n /**\n * _Throws_ a promise if the value is still pending\n * Otherwise returns it\n */\n suspend(): T {\n if (this.lastValue === NOT_CALLED) {\n throw this.deferredResult.promise;\n }\n\n return this.lastValue;\n }\n\n /** Get the current value of the subscription */\n get(): T | undefined {\n if (this.lastValue === NOT_CALLED) {\n return undefined;\n }\n\n return this.lastValue;\n }\n}\n\nexport interface SubscribedStateHookOptions {\n /** if the state should trigger suspense when waiting to resolve */\n suspend?: boolean;\n}\n\n/** Subscribe to a state change event in a react component */\nexport function useSubscribedState<T>(subscriber: Subscribe<T>): T | undefined {\n const [state, setState] = React.useState<T | undefined>(subscriber.get());\n\n React.useEffect(() => {\n const id = subscriber.add(\n (resp) => {\n setState(resp);\n },\n {\n initializeWithPreviousValue: true,\n },\n );\n\n return () => {\n subscriber.remove(id);\n };\n }, [subscriber]);\n\n return state;\n}\n"],"mappings":";AAAA,OAAO,WAAW;AAsBlB,SAAS,WAAqC;AAE5C,MAAI,UAA0B,MAAM;AAGpC,MAAI,SAAqB,MAAM;AAE/B,MAAI,SAAqB;AAEzB,QAAM,UAAU,IAAI,QAAW,CAAC,KAAK,QAAQ;AAC3C,cAAU,CAAC,MAAU;AACnB,eAAS;AACT,YAAM,cAAc;AACpB,kBAAY,CAAC;AAAA,IACf;AAEA,aAAS,CAAC,UAAkB;AAC1B,eAAS;AACT,UAAI,KAAK;AAAA,IACX;AAAA,EACF,CAAC;AAED,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;AAEA,IAAM,aAAa,OAAO,0BAA0B;AAI7C,IAAM,YAAN,MAAmB;AAAA,EAKxB,cAAc;AAJd,SAAQ,YAA4D,oBAAI,IAAI;AAC5E,SAAQ,iBAAiB,SAAY;AACrC,SAAQ,YAAmC;AAC3C,SAAQ,gBAAiD;AAEvD,SAAK,UAAU,KAAK,QAAQ,KAAK,IAAI;AACrC,SAAK,MAAM,KAAK,IAAI,KAAK,IAAI;AAC7B,SAAK,SAAS,KAAK,OAAO,KAAK,IAAI;AAAA,EACrC;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,QAAQ,KAAuB;AACnC,UAAM,KAAK,eAAe;AAC1B,SAAK,YAAY;AACjB,SAAK,eAAe,QAAQ,GAAG;AAC/B,SAAK,UAAU,QAAQ,CAAC,MAAM,EAAE,GAAG,CAAC;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA,EAKA,IACE,UACA,SAIa;AACb,UAAM,KAAK,KAAK,UAAU;AAC1B,SAAK,UAAU,IAAI,IAAI,QAAQ;AAE/B,QACE,KAAK,cAAc,cACnB,SAAS,gCAAgC,MACzC;AACA,eAAS,KAAK,SAAS;AAAA,IACzB;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,IAAiB;AACtB,SAAK,UAAU,OAAO,EAAE;AAAA,EAC1B;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,MAAM,MAAM,SAAyB;AACnC,QAAI,SAAS;AACX,WAAK,gBAAgB,SAAe;AACpC,YAAM;AAAA,IACR;AAEA,QAAI,KAAK,cAAc,YAAY;AACjC,WAAK,iBAAiB,SAAS;AAAA,IACjC;AAEA,SAAK,YAAY;AACjB,SAAK,UAAU,QAAQ,CAAC,MAAM,EAAE,MAAS,CAAC;AAE1C,SAAK,eAAe,QAAQ;AAC5B,SAAK,gBAAgB;AAAA,EACvB;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,UAAa;AACX,QAAI,KAAK,cAAc,YAAY;AACjC,YAAM,KAAK,eAAe;AAAA,IAC5B;AAEA,WAAO,KAAK;AAAA,EACd;AAAA;AAAA,EAGA,MAAqB;AACnB,QAAI,KAAK,cAAc,YAAY;AACjC,aAAO;AAAA,IACT;AAEA,WAAO,KAAK;AAAA,EACd;AACF;AAQO,SAAS,mBAAsB,YAAyC;AAC7E,QAAM,CAAC,OAAO,QAAQ,IAAI,MAAM,SAAwB,WAAW,IAAI,CAAC;AAExE,QAAM,UAAU,MAAM;AACpB,UAAM,KAAK,WAAW;AAAA,MACpB,CAAC,SAAS;AACR,iBAAS,IAAI;AAAA,MACf;AAAA,MACA;AAAA,QACE,6BAA6B;AAAA,MAC/B;AAAA,IACF;AAEA,WAAO,MAAM;AACX,iBAAW,OAAO,EAAE;AAAA,IACtB;AAAA,EACF,GAAG,CAAC,UAAU,CAAC;AAEf,SAAO;AACT;","names":[]}
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "types"
7
7
  ],
8
8
  "name": "@player-ui/react-subscribe",
9
- "version": "0.11.3--canary.670.24048",
9
+ "version": "0.11.3--canary.666.24070",
10
10
  "main": "dist/cjs/index.cjs",
11
11
  "module": "dist/index.legacy-esm.js",
12
12
  "types": "types/index.d.ts",
@@ -21,12 +21,10 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "p-defer": "^3.0.0",
24
- "use-sync-external-store": "^1.5.0",
25
24
  "tslib": "^2.6.2"
26
25
  },
27
26
  "peerDependencies": {
28
27
  "@types/react": "^18.2.39",
29
- "@types/use-sync-external-store": "^1.5.0",
30
28
  "react": "^18.2.0"
31
29
  }
32
30
  }
@@ -0,0 +1,43 @@
1
+ import { test, vitest, expect } from "vitest";
2
+ import { Subscribe } from ".";
3
+
4
+ test("Passes events to subscriptions", async () => {
5
+ const stateSub = new Subscribe<{
6
+ value: boolean;
7
+ }>();
8
+ const { add, publish } = stateSub;
9
+ const f = vitest.fn();
10
+ add(f);
11
+ publish({ value: true });
12
+
13
+ await vitest.waitFor(() => expect(f).toBeCalledTimes(1));
14
+ expect(f.mock.calls[0][0].value).toBe(true);
15
+ });
16
+
17
+ test("Removes subscriptions", async () => {
18
+ const stateSub = new Subscribe<{
19
+ value: boolean;
20
+ }>();
21
+ const { add, remove, publish } = stateSub;
22
+ const f = vitest.fn();
23
+ const id = add(f);
24
+ remove(id);
25
+ publish({ value: true });
26
+
27
+ await vitest.waitFor(() => expect(f).not.toHaveBeenCalled());
28
+ });
29
+
30
+ test("Calls multiple", async () => {
31
+ const stateSub = new Subscribe<{
32
+ value: boolean;
33
+ }>();
34
+ const { add, publish } = stateSub;
35
+ const f = vitest.fn();
36
+ const g = vitest.fn();
37
+ add(f);
38
+ add(g);
39
+ publish({ value: true });
40
+
41
+ await vitest.waitFor(() => expect(f).toBeCalledTimes(1));
42
+ expect(g).toBeCalledTimes(1);
43
+ });
package/src/index.tsx CHANGED
@@ -1,5 +1,4 @@
1
1
  import React from "react";
2
- import { useSyncExternalStore } from "use-sync-external-store/shim";
3
2
 
4
3
  export type SubscribeID = number;
5
4
 
@@ -103,7 +102,7 @@ export class Subscribe<T> {
103
102
  /**
104
103
  * Remove any updates from the given listener
105
104
  */
106
- remove(id: SubscribeID): void {
105
+ remove(id: SubscribeID) {
107
106
  this.callbacks.delete(id);
108
107
  }
109
108
 
@@ -111,7 +110,7 @@ export class Subscribe<T> {
111
110
  * Reset the state of the listener
112
111
  * Passing in a promise will defer resetting the view until the promise is resolved
113
112
  */
114
- async reset(promise?: Promise<void>): Promise<void> {
113
+ async reset(promise?: Promise<void>) {
115
114
  if (promise) {
116
115
  this.resetDeferred = deferred<void>();
117
116
  await promise;
@@ -157,40 +156,22 @@ export interface SubscribedStateHookOptions {
157
156
 
158
157
  /** Subscribe to a state change event in a react component */
159
158
  export function useSubscribedState<T>(subscriber: Subscribe<T>): T | undefined {
160
- const subscription = React.useMemo(() => {
161
- function subscribe(callback: (val?: T) => void) {
162
- const id = subscriber.add(
163
- (resp) => {
164
- callback(resp);
165
- },
166
- {
167
- initializeWithPreviousValue: true,
168
- },
169
- );
170
-
171
- return () => {
172
- if (subscriber) {
173
- subscriber.remove(id);
174
- }
175
- };
176
- }
177
-
178
- return subscribe;
159
+ const [state, setState] = React.useState<T | undefined>(subscriber.get());
160
+
161
+ React.useEffect(() => {
162
+ const id = subscriber.add(
163
+ (resp) => {
164
+ setState(resp);
165
+ },
166
+ {
167
+ initializeWithPreviousValue: true,
168
+ },
169
+ );
170
+
171
+ return () => {
172
+ subscriber.remove(id);
173
+ };
179
174
  }, [subscriber]);
180
175
 
181
- function getSnapshot() {
182
- try {
183
- return subscriber.get();
184
- } catch (err) {
185
- return undefined;
186
- }
187
- }
188
-
189
- const state = useSyncExternalStore(
190
- subscription,
191
- getSnapshot,
192
- () => undefined,
193
- );
194
-
195
176
  return state;
196
177
  }
@@ -1,83 +0,0 @@
1
- import { test, vitest, expect, describe, vi } from "vitest";
2
- import { renderHook } from "@testing-library/react";
3
- import { Subscribe, useSubscribedState } from ".";
4
-
5
- test("Passes events to subscriptions", async () => {
6
- const stateSub = new Subscribe<{
7
- value: boolean;
8
- }>();
9
- const { add, publish } = stateSub;
10
- const f = vitest.fn();
11
- add(f);
12
- publish({ value: true });
13
-
14
- await vitest.waitFor(() => expect(f).toBeCalledTimes(1));
15
- expect(f.mock.calls[0][0].value).toBe(true);
16
- });
17
-
18
- test("Removes subscriptions", async () => {
19
- const stateSub = new Subscribe<{
20
- value: boolean;
21
- }>();
22
- const { add, remove, publish } = stateSub;
23
- const f = vitest.fn();
24
- const id = add(f);
25
- remove(id);
26
- publish({ value: true });
27
-
28
- await vitest.waitFor(() => expect(f).not.toHaveBeenCalled());
29
- });
30
-
31
- test("Calls multiple", async () => {
32
- const stateSub = new Subscribe<{
33
- value: boolean;
34
- }>();
35
- const { add, publish } = stateSub;
36
- const f = vitest.fn();
37
- const g = vitest.fn();
38
- add(f);
39
- add(g);
40
- publish({ value: true });
41
-
42
- await vitest.waitFor(() => expect(f).toBeCalledTimes(1));
43
- expect(g).toBeCalledTimes(1);
44
- });
45
-
46
- describe("useSubscribedState", () => {
47
- test("updates value based on Subscriber changes", async () => {
48
- const stateSub = new Subscribe<boolean>();
49
- const addSpy = vi.spyOn(stateSub, "add");
50
- const removeSpy = vi.spyOn(stateSub, "remove");
51
-
52
- const { result, rerender, unmount } = renderHook(() =>
53
- useSubscribedState(stateSub),
54
- );
55
- expect(result.current).toBeUndefined();
56
- expect(addSpy).toHaveBeenCalledTimes(1);
57
- expect(removeSpy).toHaveBeenCalledTimes(0);
58
-
59
- await stateSub.publish(true);
60
- expect(result.current).toBe(true);
61
- expect(addSpy).toHaveBeenCalledTimes(1);
62
- expect(removeSpy).toHaveBeenCalledTimes(0);
63
-
64
- await stateSub.publish(false);
65
- expect(result.current).toBe(false);
66
- expect(addSpy).toHaveBeenCalledTimes(1);
67
- expect(removeSpy).toHaveBeenCalledTimes(0);
68
-
69
- rerender();
70
- expect(result.current).toBe(false);
71
- expect(addSpy).toHaveBeenCalledTimes(1);
72
- expect(removeSpy).toHaveBeenCalledTimes(0);
73
-
74
- await stateSub.reset();
75
- expect(result.current).toBeUndefined();
76
- expect(addSpy).toHaveBeenCalledTimes(1);
77
- expect(removeSpy).toHaveBeenCalledTimes(0);
78
-
79
- unmount();
80
- expect(addSpy).toHaveBeenCalledTimes(1);
81
- expect(removeSpy).toHaveBeenCalledTimes(1);
82
- });
83
- });