Skip to content

feat: add @tanstack/marko-store - a Marko v6 adapter for TanStack Store#340

Open
defunkt-dev wants to merge 26 commits into
TanStack:mainfrom
defunkt-dev:main
Open

feat: add @tanstack/marko-store - a Marko v6 adapter for TanStack Store#340
defunkt-dev wants to merge 26 commits into
TanStack:mainfrom
defunkt-dev:main

Conversation

@defunkt-dev

@defunkt-dev defunkt-dev commented Jun 30, 2026

Copy link
Copy Markdown

Summary

This PR adds @tanstack/marko-store, a Marko v6 adapter for @tanstack/store, alongside the existing react / solid / svelte / vue / angular / preact / lit adapters in the monorepo. It exposes the store API as idiomatic Marko tags, re-exports the framework-agnostic @tanstack/store core, and supports Marko's full rendering model: client rendering, SSR with resume, per-request isolation, and progressive (out-of-order) streaming.

What's added

Package: packages/marko-store

Five tags plus a small internal helper:

  • <store-provider> — provides a per-request bundle of stores to descendants by a context key.
  • <store-context> — reads the provided bundle (for dispatching actions, etc.).
  • <store-selector> — subscribes to a store or atom and projects a slice, in from mode or context mode.
  • <store-atom> — two-way binding to a single writable atom from createAtom().
  • <stream-store-provider> — a provider for progressive streaming that builds a per-request bundle from data awaited inside a late <await> and keeps it live across the server-to-client boundary.
  • store-bus.ts — an internal publish bus (publishStore / subscribeStorePublish) used to reattach selectors when a provider parks its bundle during resume/streaming.
    The package re-exports the full @tanstack/store core (createStore, createAtom, Store, Derived, shallow, and so on) so Marko users have a single import.

Distribution mirrors the Marko-library convention: the tags ship as raw .marko source (files includes src/tags), so the consumer's Marko tooling reads their types directly. Each tag also ships a hand-written .d.marko declaration for robust type resolution in consumer projects under noUnusedLocals and referenced-file checking; only dist/index.d.ts is tsc-emitted (for the core re-export).

Examples: examples/marko/*

Five client-only parity examples mirroring the sibling adapters: simple, atoms, stores, store-actions, and store-context.

Three Marko-specific SSR examples demonstrating server behavior: ssr-resume (store stays live after resume), ssr-per-request-multi (per-request store isolation), and streaming (progressive streaming with a live store). These run on the standalone @marko/vite dev server; each README notes that a production deployment uses @marko/run (see Known limitations).

Docs: docs/framework/marko/*

A hand-written quick-start.md and a hand-authored tag reference (reference/index.md plus one page per tag under reference/tags/). The reference is hand-authored because the public API is .marko tags, which TypeDoc does not read; see the matching docs/config.json nav entries (Getting Started, Examples, API Reference, Store API Reference).

Design notes

  • Born-with-data (BWD) transport for streamed state. For data that resolves inside a late <await>, the store is created from the awaited value inside the await body; the value crosses to the client through the await subtree's resume scope. Marko's serializedGlobals is unsuitable here because it is written once at the first flush (the shell), before late awaits resolve, so globals set inside a late await read back as null on the client.
  • $global + publish-bus model for context. Context delivery (the bundle) plus a live store reference is modeled on the marko-query adapter's $global + bus pattern, not a tree-scoped context primitive. The bus lets a context-mode <store-selector> reattach the moment its provider parks the bundle during streaming or resume.
  • One provider per key. <store-provider> and <stream-store-provider> share an owner marker on the context key and throw on a duplicate, rather than silently clobbering the same box.
  • Production path is @marko/run. A standalone @marko/vite production build does not coordinate resume for the adapter; @marko/run does. This was verified by building a minimal @marko/run app and confirming store liveness and BWD streaming in a production build under real Chromium.

Testing

  • Unit and characterization tests under jsdom for the tag behaviors.
  • Eight Phase 3 streaming e2e tests in real Chromium across four specs: streaming-liveness and streaming-out-of-order (behavioral), and streaming-fill-render and streaming-fill-onmount (characterization).
  • e2e coverage for SSR resume and per-request isolation.
    All suites pass under the monorepo's test:ci pipeline (package build, marko-type-check, knip, eslint, jsdom tests, real-Chromium e2e).

Commands to use to test locally:

pnpm --filter @tanstack/marko-store build
pnpm --filter @tanstack/store build && pnpm --filter @tanstack/marko-store test:lib
pnpm --filter @tanstack/marko-store test:eslint 
pnpm --filter @tanstack/marko-store test:types
pnpm run test:ci

## to run e2e tests
cd packages/marko-store
pnpm test:e2e

## use the following to run examples
pnpm --filter @tanstack/store-example-marko-stores dev
pnpm --filter @tanstack/store-example-marko-simple dev
pnpm --filter @tanstack/store-example-marko-atoms dev
pnpm --filter @tanstack/store-example-marko-store-actions dev
pnpm --filter @tanstack/store-example-marko-store-context dev
pnpm --filter @tanstack/store-example-marko-ssr-resume dev
pnpm --filter @tanstack/store-example-marko-ssr-per-request-multi dev
pnpm --filter @tanstack/store-example-marko-streaming dev

Known limitations

  • SSR examples are dev-only. They run under the standalone @marko/vite dev server; a production Marko v6 app uses @marko/run, and the example READMEs say so. (@marko/run is currently beta at 0.10.0.)
  • No tree-scoped context primitive. Marko v6 has no context primitive that serializes arbitrary values across resume; the adapter uses the $global + bus model in the interim. This tracks Marko core discussion (issue #3155); let-global is the community interim pattern. The adapter solves the constrained case (a serializable bundle plus a live store reference), not general tree-scoped context.
  • Out-of-order streaming uses <try> + <@placeholder>. Marko v6 removed the client-reorder attribute on <await>, so out-of-order streaming is expressed with <try> and a placeholder.

Checklist

  • pnpm test:ci green in the monorepo
  • Changeset added (@tanstack/marko-store)
  • Docs nav entries merged into docs/config.json

Note

  • The changeset may look massive, but all of this are under packages/marko-store, examples/marko and docs/framework/marko.
  • A good amount of the files are tests and examples and e2e tests.
  • The actual code is under packages/marko-store/src
  • The additional files are package.json, pnpm-lock.yaml, pnpm-workspace.yaml, docs/config.json , .gitignore, .changeset/, .prettierrcandknip.json`

@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Too many files!

This PR contains 184 files, which is 34 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: df065621-28a0-4533-b341-aecba21b3d44

📥 Commits

Reviewing files that changed from the base of the PR and between 78e3f86 and c880a77.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (184)
  • .changeset/add-marko-store.md
  • .gitignore
  • .prettierrc
  • docs/config.json
  • docs/framework/marko/quick-start.md
  • docs/framework/marko/reference/index.md
  • docs/framework/marko/reference/tags/store-atom.md
  • docs/framework/marko/reference/tags/store-context.md
  • docs/framework/marko/reference/tags/store-provider.md
  • docs/framework/marko/reference/tags/store-selector.md
  • docs/framework/marko/reference/tags/stream-store-provider.md
  • examples/marko/README.md
  • examples/marko/atoms/README.md
  • examples/marko/atoms/index.html
  • examples/marko/atoms/package.json
  • examples/marko/atoms/src/App.marko
  • examples/marko/atoms/src/atom.ts
  • examples/marko/atoms/src/index.ts
  • examples/marko/atoms/tsconfig.json
  • examples/marko/atoms/vite.config.ts
  • examples/marko/simple/README.md
  • examples/marko/simple/index.html
  • examples/marko/simple/package.json
  • examples/marko/simple/src/App.marko
  • examples/marko/simple/src/Display.marko
  • examples/marko/simple/src/Increment.marko
  • examples/marko/simple/src/index.ts
  • examples/marko/simple/src/store.ts
  • examples/marko/simple/tsconfig.json
  • examples/marko/simple/vite.config.ts
  • examples/marko/ssr-per-request-multi/README.md
  • examples/marko/ssr-per-request-multi/package.json
  • examples/marko/ssr-per-request-multi/server.mjs
  • examples/marko/ssr-per-request-multi/src/index.ts
  • examples/marko/ssr-per-request-multi/src/multi-page.marko
  • examples/marko/ssr-per-request-multi/src/perreq-page.marko
  • examples/marko/ssr-per-request-multi/src/stores.ts
  • examples/marko/ssr-per-request-multi/tsconfig.json
  • examples/marko/ssr-resume/README.md
  • examples/marko/ssr-resume/package.json
  • examples/marko/ssr-resume/server.mjs
  • examples/marko/ssr-resume/src/index.ts
  • examples/marko/ssr-resume/src/page.marko
  • examples/marko/ssr-resume/src/store.ts
  • examples/marko/ssr-resume/tsconfig.json
  • examples/marko/store-actions/README.md
  • examples/marko/store-actions/index.html
  • examples/marko/store-actions/package.json
  • examples/marko/store-actions/src/App.marko
  • examples/marko/store-actions/src/index.ts
  • examples/marko/store-actions/src/store.ts
  • examples/marko/store-actions/tsconfig.json
  • examples/marko/store-actions/vite.config.ts
  • examples/marko/store-context/README.md
  • examples/marko/store-context/index.html
  • examples/marko/store-context/package.json
  • examples/marko/store-context/src/App.marko
  • examples/marko/store-context/src/AtomSummary.marko
  • examples/marko/store-context/src/CatCard.marko
  • examples/marko/store-context/src/DeepAtomEditor.marko
  • examples/marko/store-context/src/DogCard.marko
  • examples/marko/store-context/src/NestedAtomControls.marko
  • examples/marko/store-context/src/StoreButtons.marko
  • examples/marko/store-context/src/TotalCard.marko
  • examples/marko/store-context/src/index.ts
  • examples/marko/store-context/tsconfig.json
  • examples/marko/store-context/vite.config.ts
  • examples/marko/stores/README.md
  • examples/marko/stores/index.html
  • examples/marko/stores/package.json
  • examples/marko/stores/src/App.marko
  • examples/marko/stores/src/index.ts
  • examples/marko/stores/src/store.ts
  • examples/marko/stores/tsconfig.json
  • examples/marko/stores/vite.config.ts
  • examples/marko/streaming/README.md
  • examples/marko/streaming/package.json
  • examples/marko/streaming/server.mjs
  • examples/marko/streaming/src/index.ts
  • examples/marko/streaming/src/inorder-page.marko
  • examples/marko/streaming/src/outoforder-page.marko
  • examples/marko/streaming/tsconfig.json
  • knip.json
  • package.json
  • packages/marko-store/README.md
  • packages/marko-store/e2e/README.md
  • packages/marko-store/e2e/playwright.config.ts
  • packages/marko-store/e2e/server.mjs
  • packages/marko-store/e2e/src/context-page-multi.marko
  • packages/marko-store/e2e/src/context-page-perreq.marko
  • packages/marko-store/e2e/src/context-page.marko
  • packages/marko-store/e2e/src/context-store.ts
  • packages/marko-store/e2e/src/helper-eo.ts
  • packages/marko-store/e2e/src/index.ts
  • packages/marko-store/e2e/src/page.marko
  • packages/marko-store/e2e/src/store.ts
  • packages/marko-store/e2e/src/streaming-fill-onmount.marko
  • packages/marko-store/e2e/src/streaming-fill-render.marko
  • packages/marko-store/e2e/src/streaming-helper-context-above.marko
  • packages/marko-store/e2e/src/streaming-helper-dup-try.marko
  • packages/marko-store/e2e/src/streaming-helper-effect-order.marko
  • packages/marko-store/e2e/src/streaming-helper-liveness.marko
  • packages/marko-store/e2e/src/streaming-helper-multi.marko
  • packages/marko-store/e2e/src/streaming-helper-ooo.marko
  • packages/marko-store/e2e/src/streaming-helper-outside.marko
  • packages/marko-store/e2e/src/streaming-helper-provider-collision.marko
  • packages/marko-store/e2e/src/streaming-liveness.marko
  • packages/marko-store/e2e/src/streaming-out-of-order.marko
  • packages/marko-store/e2e/src/streaming-store.ts
  • packages/marko-store/e2e/store-context-resume.spec.ts
  • packages/marko-store/e2e/store-resume-liveness.spec.ts
  • packages/marko-store/e2e/streaming-fill.spec.ts
  • packages/marko-store/e2e/streaming-helper-context-above.spec.ts
  • packages/marko-store/e2e/streaming-helper-dup-try.spec.ts
  • packages/marko-store/e2e/streaming-helper-effect-order.spec.ts
  • packages/marko-store/e2e/streaming-helper-liveness.spec.ts
  • packages/marko-store/e2e/streaming-helper-multi.spec.ts
  • packages/marko-store/e2e/streaming-helper-no-js.spec.ts
  • packages/marko-store/e2e/streaming-helper-ooo.spec.ts
  • packages/marko-store/e2e/streaming-helper-outside.spec.ts
  • packages/marko-store/e2e/streaming-helper-provider-collision.spec.ts
  • packages/marko-store/e2e/streaming-liveness.spec.ts
  • packages/marko-store/e2e/streaming-out-of-order.spec.ts
  • packages/marko-store/eslint.config.js
  • packages/marko-store/marko.json
  • packages/marko-store/package.json
  • packages/marko-store/src/index.ts
  • packages/marko-store/src/tags/store-atom.d.marko
  • packages/marko-store/src/tags/store-atom.marko
  • packages/marko-store/src/tags/store-bus.ts
  • packages/marko-store/src/tags/store-context.d.marko
  • packages/marko-store/src/tags/store-context.marko
  • packages/marko-store/src/tags/store-provider.d.marko
  • packages/marko-store/src/tags/store-provider.marko
  • packages/marko-store/src/tags/store-selector.d.marko
  • packages/marko-store/src/tags/store-selector.marko
  • packages/marko-store/src/tags/stream-store-provider.d.marko
  • packages/marko-store/src/tags/stream-store-provider.marko
  • packages/marko-store/tests/fixtures/atom-host.marko
  • packages/marko-store/tests/fixtures/csr-stream-build-host.marko
  • packages/marko-store/tests/fixtures/csr-stream-multi-host.marko
  • packages/marko-store/tests/fixtures/ctx-customkey-host.marko
  • packages/marko-store/tests/fixtures/ctx-dup-provider-host.marko
  • packages/marko-store/tests/fixtures/ctx-getter-fed-host.marko
  • packages/marko-store/tests/fixtures/ctx-missing-provider-host.marko
  • packages/marko-store/tests/fixtures/ctx-multi-host.marko
  • packages/marko-store/tests/fixtures/ctx-nested-host.marko
  • packages/marko-store/tests/fixtures/ctx-no-provider-host.marko
  • packages/marko-store/tests/fixtures/ctx-selector-host.marko
  • packages/marko-store/tests/fixtures/ctx-ssr-host.marko
  • packages/marko-store/tests/fixtures/ctx-store.ts
  • packages/marko-store/tests/fixtures/ctx-write-host.marko
  • packages/marko-store/tests/fixtures/selector-both-host.marko
  • packages/marko-store/tests/fixtures/selector-custom-compare-host.marko
  • packages/marko-store/tests/fixtures/selector-host.marko
  • packages/marko-store/tests/fixtures/selector-no-selector-host.marko
  • packages/marko-store/tests/fixtures/selector-no-selector-swap-host.marko
  • packages/marko-store/tests/fixtures/selector-selector-swap-host.marko
  • packages/marko-store/tests/fixtures/selector-source-swap-host.marko
  • packages/marko-store/tests/fixtures/selector-swap-compare-host.marko
  • packages/marko-store/tests/fixtures/ssr-atom.marko
  • packages/marko-store/tests/fixtures/ssr-object-antipattern.marko
  • packages/marko-store/tests/fixtures/ssr-selector.marko
  • packages/marko-store/tests/fixtures/ssr-store.ts
  • packages/marko-store/tests/fixtures/ssr-stream-const-antipattern.marko
  • packages/marko-store/tests/fixtures/ssr-stream-ctx-above.marko
  • packages/marko-store/tests/fixtures/ssr-stream-dup.marko
  • packages/marko-store/tests/fixtures/ssr-stream-helper.marko
  • packages/marko-store/tests/index.test.ts
  • packages/marko-store/tests/marko.d.ts
  • packages/marko-store/tests/setup.ts
  • packages/marko-store/tests/ssr.test.ts
  • packages/marko-store/tests/store-bus.test.ts
  • packages/marko-store/tests/store-context-ssr.test.ts
  • packages/marko-store/tests/store-context-tags.test.ts
  • packages/marko-store/tests/stream-store-provider-client.test.ts
  • packages/marko-store/tests/stream-store-provider.test.ts
  • packages/marko-store/tests/tags.test.ts
  • packages/marko-store/tsconfig.build.json
  • packages/marko-store/tsconfig.json
  • packages/marko-store/tsconfig.marko.json
  • packages/marko-store/tsdown.config.ts
  • packages/marko-store/vitest.config.ts
  • pnpm-workspace.yaml

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@socket-security

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​marko/​vite@​6.1.07810010098100
Added@​playwright/​test@​1.61.110010010099100

View full report

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.

1 participant