Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(signals): add a way to set symbol as key #4665

Open
wants to merge 6 commits into
base: master
Choose a base branch
from

Conversation

rax-it
Copy link
Contributor

@rax-it rax-it commented Oct 18, 2024

Details

Continuing this as work that entailed from feedback from #4650
lwc-engine

  • exposes a setSignalIdentity API, the symbol is used to recognize a signal known to engine at runtime

lwc-signals

  • exposes a way to set a symbol that will be used to create signals recognized by lwc-engine

Any runtime bootstrap service should be able to configure these unique symbols.

Does this pull request introduce a breaking change?

  • 😮‍💨 No, it does not introduce a breaking change.

Does this pull request introduce an observable change?

  • 🔬 Yes, it does include an observable change.

GUS work item

W-16994654

Copy link
Contributor

@wjhsf wjhsf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking a bit more about this, if we want to prevent "fake" signals, we can't attach anything to the signal class, because end users can sniff anything we'd want to check:

// symbol as value
class Signal {
  __id = Symbol('private-lwc-signal');
}
new Signal().__id // 💥

class Signal {
  // symbol as prop
  [Symbol('private-lwc-signal')]: true;
}
Object.getOwnPropertySymbols(Signal.prototype)[0]; // 💥

class Signal {
  // symbol not directly accessible
  #identity = Symbol('private-lwc-signal');
  __isLwcSignal() { /* do something with #identity */ }
}
class FakeSignal extends Signal {
  __isLwcSignal() { return true } // 💥
}

Rather than exposing the symbol, we need to expose a checker function:

// @lwc/engine-core
import { setSignalValidator, Signal } from "@lwc/signals"
const trustedSignals = new WeakSet()
const trustThisSignal = (signal: Signal) => trustedSignals.has(signal);
setSignalValidator(trustThisSignal);
// ...
if (trustedSignals.has(someSignal)) { ... }

// @lwc/signal
let trustThisSignal: undefined | (() => void)
const setSignalValidator = once(fn => trustThisSignal = fn)
class Signal {
  constructor () {
    trustThisSignal?.(this)
  }
}

By doing this, we're not altering the Signal interface, and the setSignalValidator function is a no-op after we use it, so there's no way for end users to hijack the system.

@nolanlawson
Copy link
Collaborator

if we want to prevent "fake" signals, we can't attach anything to the signal class

This is a really good point. Maybe at this point we should be using a WeakSet instead.

import { setSignalValidator } from '@lwc/engine-dom'
import { setSignalValidator as setSignalValidator2 } from '@lwc/signals'

const weakSet = new WeakSet()
setSignalValidator(weakSet) // throws if called twice
setSignalValidator2(weakSet) // throws if called twice

Then the constructor for SignalBaseClass could add itself to the weakSet. This seems simpler than using Symbols.

@rax-it
Copy link
Contributor Author

rax-it commented Oct 21, 2024

Added a shared weakset that's used to allow only trusted signals to be tracked by engine.

Copy link
Collaborator

@nolanlawson nolanlawson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!


### setSignalValidator()

This experimental API enables the addition of a signal as a trusted signal. If the [`ENABLE_EXPERIMENTAL_SIGNALS`](https://github.com/salesforce/lwc/blob/master/packages/%40lwc/features/README.md#lwcfeatures) feature is enabled, any signal value change will trigger a re-render.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This experimental API enables the addition of a signal as a trusted signal. If the [`ENABLE_EXPERIMENTAL_SIGNALS`](https://github.com/salesforce/lwc/blob/master/packages/%40lwc/features/README.md#lwcfeatures) feature is enabled, any signal value change will trigger a re-render.
This experimental API enables the addition of a signal as a trusted signal. If the [`ENABLE_EXPERIMENTAL_SIGNALS`](https://github.com/salesforce/lwc/blob/master/packages/%40lwc/features/README.md#lwcfeatures) feature is enabled, any signal value change will trigger a re-render.
If `setSignalValidator` is called more than once, it will throw an error. If it is never called, then no trusted signal validation will be performed. The same `setSignalValidator` API must be called on both `@lwc/engine-dom` and `@lwc/signals`.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants