Skip to content

[RFC] Primitives API & Feature Access#307

Merged
mihar-22 merged 16 commits intomainfrom
rfc/slice-accessor-design
Jan 21, 2026
Merged

[RFC] Primitives API & Feature Access#307
mihar-22 merged 16 commits intomainfrom
rfc/slice-accessor-design

Conversation

@mihar-22
Copy link
Copy Markdown
Member

@mihar-22 mihar-22 commented Jan 15, 2026

Closes #306

Summary

Folded feature accessor design into the player-api RFC as primitives.md.

@vercel
Copy link
Copy Markdown

vercel bot commented Jan 15, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

4 Skipped Deployments
Project Deployment Review Updated (UTC)
vjs-10-demo-html Ignored Ignored Preview Jan 21, 2026 11:16am
vjs-10-demo-next Ignored Ignored Preview Jan 21, 2026 11:16am
vjs-10-demo-react Ignored Ignored Preview Jan 21, 2026 11:16am
vjs-10-website Ignored Ignored Preview Jan 21, 2026 11:16am

Request Review

@mihar-22 mihar-22 added this to the Alpha milestone Jan 15, 2026
@mihar-22 mihar-22 self-assigned this Jan 15, 2026
@mihar-22 mihar-22 removed the planning label Jan 16, 2026
@decepulis
Copy link
Copy Markdown
Collaborator

Amateur-hour question. Not asked skeptically.
What's the difference between the state returned from usePlayer() and useSlice()? Which will high-level authors have to access if they're, say, just building their own play button?

Comment thread rfc/feature-availability-design.md
Comment thread .claude/plans/slice-accessor-design.md Outdated
Comment thread .claude/plans/slice-accessor-design.md Outdated
@mihar-22
Copy link
Copy Markdown
Member Author

mihar-22 commented Jan 18, 2026

@decepulis useSlice is a both an internal and advanced API for anyone creating their own slices or primitives. The specific problems it's solving are outlined at the top of feature-accessor-design doc. Probably 99% of users will never see it. For everyone else, usePlayer is all you need.

Related-ish comment

@mihar-22 mihar-22 mentioned this pull request Jan 19, 2026
@mihar-22 mihar-22 force-pushed the rfc/slice-accessor-design branch from 95ba375 to a434413 Compare January 20, 2026 13:14
@mihar-22 mihar-22 changed the title [RFC] Slice Accessor Design [RFC] Feature Accessor Design Jan 20, 2026
@mihar-22
Copy link
Copy Markdown
Member Author

mihar-22 commented Jan 20, 2026

@decepulis
Copy link
Copy Markdown
Collaborator

decepulis commented Jan 20, 2026

@mihar-22 I had trouble grokking the top of feature-accessor-design.md in the context of my question (maybe that section could be fleshed out a bit for newbies like me). Regardless, I think I got my answer in feature-availability-design.md.

Seems to me like a high-level dev building their own volume slider would be accessing const { volume, volumeAvailability } = usePlayer() instead of useFeature(volumeFeature), right? Or... wait... this comment makes it seem like we can't use volumeAvailability internally. I wonder why. You say distributing UI components, but, I don't completely follow.

Meanwhile, feature developers will use useFeature for cross-feature access?

- Merge feature-accessor-design.md content into player-api/
- Add primitives.md for library author API (hasFeature, types)
- Replace STORE_SYMBOL with getStore() function
- Update package export locations (store exports utils, core/dom exports types)
- Update all cross-references
@mihar-22 mihar-22 force-pushed the rfc/slice-accessor-design branch from a434413 to 81aee0b Compare January 21, 2026 10:04
- Update architecture.md examples to use getFeature(target.media, f) pattern
- Add StoreHost contract documentation to decisions.md
- Document overloaded hasFeature/getFeature for stores vs proxies
- Update primitives.md with StoreHost contract and implementation details
…tore

Key changes:
- StoreProxy<T> interface replaces StoreHost (generic preserves store type)
- UnknownPlayer/UnknownMedia are interfaces extending StoreProxy
- PlayerTarget.media is now UnknownMedia (flat proxy, not store)
- Remove hasFeature/getFeature store overloads (only work with proxies)
- Remove getStore() from public API (internal only)
- Add createProxy(store) factory for store → proxy conversion
- Feature authors use same flat API as component authors
- Remove verbose type signatures from primitives.md Implementation section
- Consolidate 4 examples into 1 focused example
- Convert Package Exports to table
- Condense Reactive System to bullet points
- Convert File Structure and Player Features to tables
- Remove duplicate Fullscreen feature example (already shown in Cross-Store Access)
- Trim hasFeature/getFeature rationale to essentials
- Remove verbose code examples from decisions.md

Net: -306 lines
- Restore StoreProxy<T> interface definition in decisions.md
- Add hasFeature/getFeature usage examples
- Add both React and Lit examples to primitives.md for cross-framework clarity
- Add throwMissingFeature(feature, componentName) to surface misconfiguration
- Update examples to show throw pattern for critical features (PlayButton)
- Update examples to show getFeature for optional features
- Add to package exports table
Change from throwMissingFeature(feature, 'Name') to
throwMissingFeature(feature, { displayName: 'Name' }) for extensibility
Critical components like PlayButton should throw on missing feature,
not silently return null which hides misconfiguration
- Update 'Two functions' → 'Three utilities' in primitives.md
- Add throwMissingFeature to solution table and cross-framework table
- Rename 'Optional Feature' section to 'Mixing Required and Optional'
- Add throwMissingFeature to architecture.md constraints
- Fix stale 'navigate two stores' trade-off in decisions.md
- Add store type definitions to StoreProxy example in decisions.md
- Remove duplicate PlayButton examples from primitives.md
- Replace verbose Primitives API section in decisions.md with rationale-only + link
- Replace Two Stores rationale in decisions.md with link to architecture.md
- Condense Feature Registry in architecture.md to one line with link

Canonical locations:
- Types & examples → primitives.md
- Rationale & decisions → decisions.md
- How it works → architecture.md

Net: -120 lines
@mihar-22 mihar-22 changed the title [RFC] Feature Accessor Design [RFC] Primitives API & Feature Access Jan 21, 2026
@mihar-22
Copy link
Copy Markdown
Member Author

mihar-22 commented Jan 21, 2026

Thanks @luwes and @decepulis! Both of you calling it out made me go back to the drawing board and simplify the design.

I hope both of you are happy with this design! I'm merging to keep us moving but please feel free to leave more comments for us to discuss. Happy to update any time.

API surface

Primitives (component authors):

function PlayButton() {
  const player = usePlayer(); // UnknownPlayer
  
  // Type guard
  if (!hasFeature(player, features.playback)) {
    throwMissingFeature(features.playback, { displayName: 'PlayButton' });
  }
  
  // Typed here now
  return <button onClick={player.play}>{player.paused ? '▶' : '⏸'}</button>;
}

// Optional features
const volume = getFeature(player, features.volume);
volume.setVolume?.(0.5); // safe if missing

Feature authors — same flat API:

const fullscreen = createPlayerFeature({
  request: {
    enterFullscreen: (_, { target }) => {
      const mediaFS = getFeature(target.media, media.fullscreen);
      mediaFS.enterFullscreen?.(); // flat, not mediaFS.request.enterFullscreen()
    },
  },
});

Key changes

  • target.media is now a flat proxy (UnknownMedia) — feature authors use same API as components
  • Three utilities: hasFeature (guard), getFeature (optional), throwMissingFeature (critical)
  • StoreProxy<T> interface — generic base for all proxies
  • Folded into rfc/player-api/primitives.md

@mihar-22 mihar-22 merged commit 8c2407e into main Jan 21, 2026
3 checks passed
@mihar-22 mihar-22 deleted the rfc/slice-accessor-design branch January 21, 2026 11:29
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.

Discovery: Feature Accessor Design

3 participants