Skip to content

Roadmap

Each item below is scoped so it can be built and tested against the bundled mock API, which already exposes the server side of phases 2 and 3. The detailed, numbered backlog (including DX and performance findings from the example app) is TODO.md at the repository root; this page is the summary.

  1. Normalized cache — done

    Objects with __typename + id are stored once, referenced from the tree, and scopes are notified per entity field. launch(id:) is served from the entity the list wrote. See Caching. Unreachable entities are swept automatically (SlingClient(gcAfterWrites:)). Type policies (SlingClient(typePolicies:)): key arguments and a custom merge per field, and RelayStylePagination keeping a cursor connection in one growing list — see Pagination.

  2. Mutations — done

    client.mutate((m) => m.toggleFavorite(launchId: id)?.favorite) and MutationBuilder<Mutation>, with journaled optimistic writes rolled back on failure. See Mutations. Also done: refetchQueries sugar, typed cache access (client.cacheScope), and list membership after a mutation (cacheScope.list((q) => q.me?.favorites).append/remove, evict) — see Caching. Partial errors fail the call but keep what the server resolved, over the rolled-back optimistic writes. List rules keep filtered lists and merged connections in sync with their entities.

  3. Subscriptions — done

    client.subscribe((s) => s.launchStatusChanged?..status) and SubscriptionBuilder<Subscription> record a selection once, open a GraphQL-over-SSE stream (SlingClient(subscriptionTransport:) to swap the protocol), and write each event through the same writeResponse as a query — see Subscriptions. MockGraphQLServer(subscription:) takes Streams. Reconnection (subscriptionRetryAfter, reconnect()) is done. SSE is the only built-in transport; a graphql-ws adapter may come later as its own package.

  4. Unions and interfaces — done

    The generator emits one accessor per union/interface with an asType getter per member (hit.asLaunch?.name) and when(launch: …, rocket: …, orElse: …); the printer emits inline fragments ... on Launch { … }, every branch recorded on the skeleton so one request covers them all. Members normalize like any entity. The mock API has search(text:): [SearchResult!]! and node(id:): Node; the example’s Search tab uses them — see Querying. Same-named fields across fragments are printed type-qualified and mapped back before caching.

  5. Fine-grained rebuilds — done

    Scopes rebuild on entity.field intersection instead of root aliases; inline objects and lists are compared structurally, so identical responses touch nothing; SlingRow gives a list row its own dependency set, so a write to one entity rebuilds that row, not the list (see Caching). Inline lists notify per element (entity.field[i]) and per length (entity.field[length]), so a change at one index rebuilds only its readers.

  6. Cache expiry and SWR — done

    FetchPolicy (cacheFirst / cacheAndNetwork / networkOnly) per widget or client-wide, maxAge stale-while-revalidate on per-field fetch stamps, state.isStale, soft state.revalidate() — see Fetch policies & freshness. Left: WeakReference-held stale entries as an eviction mechanism.

  7. Developer experience — in progress

    Done: real Dart enums with an unknown value (see Code generation), no setters on key fields or connection metadata, and PaginatedQueryBuilder (see Pagination), and a dev-mode waterfall warning (SlingClient.onWaterfall, QueryBuilder.debugLabel) naming the scope and fields that caused a second round trip; Accessor.isFetched + QueryState.isSkeleton for the three-meanings-of-null story; refetchQueries on mutations; --scalar mapping; MutationBuilder resolving its root from SlingScope(schema:); the sling_gql_test package (MockGraphQLServer, pumpUntilSettled — see Testing); example dev tools (latency picker, live cache stats, resolve() prefetch — see Example app); the in-app request overlay (SlingRequestOverlay) and one log line per operation (logRequests, client.requests), each request attributed to the widgets that recorded it (see Batching and waterfalls); the flutter_hooks adapter sling_gql_hooks (useSlingQuery, useSlingMutation, useSlingSubscription — see Hooks).

  8. Transport options — done

    SlingClient(transport:) is the single interception point (see Transport). sling_gql_link turns a gql_link chain (auth headers, retries, persisted queries) into that transport and the subscription transport, so the runtime stays http-only (see Transport).

  9. Live docs — in progress

    Done: the example app has a web build that runs on this site — Try it live — with the mock API executing in your browser (the same resolvers as the Node server, behind the app’s http.Client), so every visitor gets their own data and the site stays static. The batching, fetch-policies and mutations guides each run a small demo next to its code; the code shown is the demo’s source. Next: demos for the other guides.

  10. Persistence — done (SQLite)

    sling_gql_sqflite keeps a durable copy of the normalized cache: deltas saved in the background (changesSince, debounced, flushed when the app pauses), restored before the client starts, bounded by age and entity count, migrated along the generated slingSchema.fields when the schema’s hash changes (what the new code can still read is kept), recreated when the file is corrupt, one writer per file, with an optional row codec for encryption — see Persistence. Hive and file adapters are not planned until someone needs them.

  11. Production readiness — done, released as 1.0.0

    Done: type policies and connection merging (one growing cache list for cursor pages, RelayStylePagination); persistence migration across schema changes; a typed, sealed error model with errorPolicy (Loading states & errors), timeouts and cancellation, query retry with backoff and a single-flight token-refresh hook (Transport); Android, a second GraphQL server (graphql-http) and a soak test in CI; an offline mutation queue (Mutations, persisted by sling_gql_sqflite); an API audit with a versioning policy and an upgrade guide. All six packages are 1.0.0 on pub.dev.

  • SSR hydration. Not applicable.
  • graphql-ws in the core. Subscriptions stay on SSE; a WebSocket transport would be a separate package plugged into SlingClient(subscriptionTransport:).
  • Lazy / transaction / paginated query variants. resolve() plus widget state cover the same ground.