Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Duo Laravel Package

joshcirre/duo

Duo brings local-first IndexedDB syncing to Laravel and Livewire. Add a single trait and your Livewire components become Alpine-powered, caching Eloquent models in the browser with offline support, optimistic updates, and automatic schema extraction.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. Duo adds a client-side persistence tier (IndexedDB via Dexie) to a standard Laravel + Livewire 3/Volt stack, plus a service worker and a Vite plugin. That is a materially larger architectural commitment than the one-line composer require suggests: you are introducing a second database, a second schema lifecycle, a build-time code generator, and a browser-side sync client into an app whose server-side persistence is currently the single source of truth. It fits applications where the primary product requirement is offline-first field/workflow capture on repeat-visit surfaces (inspections, checklists, POS, note-taking). It does not fit apps where offline is a nice-to-have or where the Livewire components are highly stateful.

Integration feasibility. The integration surface is unusually wide for a package of this size:

  • PHP: Syncable trait on models (schema extraction from DB columns), WithDuo trait on Livewire components.
  • Blade: a required @duoMeta directive in <head>, optional <x-duo::sync-status> and debug panel.
  • JS/build: @joshcirre/vite-plugin-duo in vite.config.js with file-watching over app/Models/** and a manifest emitted to resources/js/duo/manifest.json.
  • Runtime: a service worker copied to public/duo-sw.js and registered client-side.
  • Implicit conventions: a user() relationship must exist on user-scoped models, and user_id must not be in $fillable or the package's security model degrades.

That last point is the tell. Security behavior is inferred from relationship naming and $fillable contents rather than configured explicitly — powerful when conventions hold, brittle when they don't, and hard to audit.

Technical risk. High, concentrated in three places:

  1. The Livewire → Alpine transform. The WithDuo trait rewrites wire:click to @click, @forelse to x-for, {{ $x }} to x-text, and injects x-cloak. This is a source-to-source UI rewrite performed by the package. Any Livewire feature outside the supported grammar — validation error bags, computed properties, wire:model modifiers, file uploads, polling, dispatch/listeners, nested components, third-party Livewire packages — is a candidate for silent breakage. This is the single largest unknown and must be scoped by feature-by-feature testing, not by reading the README.
  2. Sync semantics are undocumented. "Optimistic updates with background sync" and "sync queue that resumes when online" describe transport, not conflict resolution. There is no visible spec for merge strategy (last-write-wins? per-field? server-authoritative?), idempotency of queued writes on retry, or multi-device/multi-tab reconciliation. For a TPM, this is a data-integrity question, not an implementation detail.
  3. Dual schema lifecycle. SQL migrations and IndexedDB version bumps are separate processes; the manifest is generated at build time. Model changes that don't trigger a clean regenerate leave the client schema stale, and a bad client-side migration risks losing queued or cached user data with no server record.

Key questions for the maintainer / spike.

  • What is the conflict-resolution algorithm, and is it documented? What happens when two devices edit the same record offline?
  • Are queued sync requests idempotent? What prevents duplicate creates on network retry or tab duplication?
  • How is authorization enforced on the sync endpoint per record — policies, global scopes, tenant scoping, soft deletes?
  • What is the IndexedDB schema-version and migration strategy when a model changes? Is there a data-loss path?
  • Which Livewire features are explicitly unsupported by the transform? Is there a compatibility matrix?
  • What is the supported Laravel/Livewire/PHP version matrix, and what is the breaking-change policy given the WIP status?
  • How does the queue behave when the session expires or the CSRF token rotates while offline (419s on reconnect)?
  • The release metadata shows a date of 2026-09-01 on a repo self-described as "VERY MUCH A WIP" — verify actual release cadence and commit activity before treating this as maintained.

Integration Approach

Stack fit. Qualified yes for: Laravel 11/12, Livewire 3+, Volt, Vite, Postgres/MySQL, SPA-ish or PWA-oriented front ends, single-tenant or cleanly tenant-scoped Eloquent models. Explicit no for: Inertia/React/Vue front ends (the transform is Livewire-specific), API-first architectures serving mobile clients, apps that rely on Livewire file uploads or heavy client-side validation on the components in scope, and anything where the affected models are large or highly relational (no evidence relationships are synced — the README's examples are all flat records).

Migration path. Do not treat this as an incremental upgrade to existing components. The realistic path is a carve-out: pick one new or low-traffic surface (e.g., a "my tasks" list, an offline inspection form), model it with Syncable + WithDuo from day one, and leave all existing Livewire components untouched. The shipped debug panel's toggle between Duo mode and standard Livewire mode is the rollback lever — validate that it works, and treat it as the kill switch.

Compatibility work to budget for.

  • Pin the Composer package and the NPM plugin to exact versions; they ship as a coordinated pair and the plugin generates artifacts the PHP side consumes.
  • Lock Dexie's version explicitly (it installs transitively) so a transitive bump doesn't change the IndexedDB layer under you.
  • Verify the manifest output in CI — assert resources/js/duo/manifest.json exists and matches expected columns for each Syncable model. Model changes that silently don't regenerate are the most likely production surprise.
  • Audit every Syncable model for user() relationship presence and user_id fillable status as a review checklist item, not as tribal knowledge.

Sequencing.

  1. Spike (1–2 weeks): one model, one component, one browser. Reproduce the README quick start verbatim. Test offline edit → reconnect → sync, plus a second tab, plus a session-expiry-while-offline case.
  2. Feature-parity audit: enumerate the Livewire features on the target components and mark each supported / unsupported / untested against the transform. Gate go/no-go on this.
  3. Data-integrity design: write down your own conflict-resolution and idempotency expectations; if the package's behavior differs, that is a blocker, not a config tweak.
  4. Pilot behind a flag for a bounded user cohort, with server-side logging of every sync request payload so you can reconstruct client state during incidents.
  5. Decide build-vs-buy before broad rollout. At 36 stars, zero dependents, and a self-declared WIP, the fallback is to fork the package in-house or to write the IndexedDB layer directly against Dexie — the latter is more work but removes the transform risk entirely.

Operational Impact

Maintenance. This is the dominant cost. One maintainer, no dependents, no visible ecosystem, and a README that opens with "VERY MUCH A WIP." Treat the package as a codebase you are adopting, not a dependency you are consuming. Budget for a named internal owner, a fork in your VCS, and the ability to patch both the PHP and TypeScript sides. Every Laravel or Livewire minor upgrade becomes a compatibility question for a package with no stated version support matrix.

Support. GitHub issues only, no commercial backing, no SLA. Internal escalation path must exist before pilot. The debug panel is a developer aid, not an operational tool — it is gated to the local environment.

Scaling. Read path scales well: repeat renders hit IndexedDB instead of the server, which is the actual value proposition and will show up as a real reduction in Livewire round-trips. Write path scales worse than before in one specific dimension — reconnect bursts. After an outage or a deploy, every offline client flushes its queue at once, producing a synchronized write spike and a correlated read-your-writes problem. Plan for sync-endpoint rate limiting and for server-side headroom sized to the burst, not the average. Also model storage: IndexedDB quotas are per-origin and evictable, especially on iOS Safari, so "the data is on the device" is not a durability guarantee.

Failure modes to instrument.

  • Silent data loss on storage eviction — browser reclaims IndexedDB, queued writes vanish with no server record. Needs a client-side "unflushed changes" signal and a user-visible warning.
  • Duplicate writes on retry — non-idempotent creates replayed after a network failure.
  • Split-brain across devices — two offline clients editing the same record; outcome depends on undocumented merge logic.
  • Auth-boundary failures — 419/401 on flush after session expiry, leaving a permanently stuck queue; and per-record authorization gaps if global scopes or policies aren't applied on the sync endpoint.
  • Stale service worker — cached HTML/JS outliving a deploy, serving an old client against a new manifest and corrupting the schema handshake.
  • Transform regressions — a Livewire component change that the Alpine rewrite silently mishandles, producing wrong UI without a PHP exception.

Ramp-up. Steeper than the "one trait" pitch implies. Engineers need working fluency in Livewire 3 internals, Alpine, IndexedDB/Dexie, and service worker lifecycle — four layers, and the abstraction actively hides three of them. Debugging moves from "read the stack trace" to "inspect the generated Alpine markup and the IndexedDB contents," which most Laravel teams are not set up for. Expect a 2–4 week ramp per engineer and a hard dependency on whoever ran the spike. Front-load the runbook: how to inspect IndexedDB in production, how to force a schema regeneration, how to drain a stuck queue, how to disable Duo per-user.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
ably/pubsub-server
mstfkhazaal/defender
mfonte/base62x
karnoweb/metapilot
tobya/bcsapiwrapper
swalker2/cpanel
vish4395/laravel-translation-manager
morningtrain/economic
teksite/systeminfo-laravel
atomcms/voting
run6/weather
siokas/laravelembeddirectives
adamlien/pkuuids
gilsonsouza/lara-doc-generator
manelgavalda/enrollment_mobile
unabomber/timezone
delatbabel/contacts
astritzeqiri/g-recaptcha
kevinongko/laravel-genode
developeruz/laravel-analytics