create-stitchkit 0.6.1 → 0.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -12,6 +12,31 @@ step is overwritten by the next release.
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ## [0.6.2] — 2026-09-21
16
+
17
+ ### Fixed
18
+
19
+ - **A scaffold installs the framework the manifest promises.** 0.6.1 rode in the
20
+ same release train as stitchkit 0.90.6 and therefore shipped a lockfile
21
+ resolving 0.90.5: a lockfile is written by an install, so it could not name a
22
+ version that train had not published yet. Every scaffold from 0.6.1 got a
23
+ framework one release behind the one its own range allowed. The template now
24
+ targets `^0.90.7` over a lockfile resolving 0.90.7, and the framework's release
25
+ machinery refuses that shape of train outright.
26
+
27
+ ### Changed
28
+
29
+ - **The generated frontend takes its QueryClient from the framework.**
30
+ `lib/query-client.ts` now calls `createQueryClientFactory` from
31
+ `stitchkit/react` instead of restating it locally. The local copy spelled the
32
+ query retry as a plain `1`, which retries an unauthorized, forbidden or invalid
33
+ request exactly as eagerly as a network blip; the framework predicate retries
34
+ server failures and network faults and leaves the rest alone. Request-local
35
+ server identity, the browser singleton, pending dehydration and the
36
+ no-retry-for-mutations rule are unchanged in behaviour. This is the cutover
37
+ `UPGRADING.md` recorded under *Released migration: 0.6.1* — adopting it in a
38
+ project you already own remains optional.
39
+
15
40
  ## [0.6.1] — 2026-09-20
16
41
 
17
42
  ### Changed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-stitchkit",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
4
4
  "description": "Create a production-shaped Stitchkit application",
5
5
  "license": "MIT",
6
6
  "author": "Max Listov <maxlistov@gmail.com>",
package/template/bun.lock CHANGED
@@ -145,7 +145,7 @@
145
145
  "zod": "4.6.5",
146
146
  },
147
147
  "catalog": {
148
- "stitchkit": "^0.90.5",
148
+ "stitchkit": "^0.90.7",
149
149
  },
150
150
  "packages": {
151
151
  "@ai-sdk/gateway": ["@ai-sdk/gateway@4.0.87", "", { "dependencies": { "@ai-sdk/provider": "4.0.17", "@ai-sdk/provider-utils": "5.0.45", "@vercel/oidc": "3.2.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-6wJmX/D5OO7tvDJbtFdXdBuRt8AkCnW+FLK+W35UHe/tHgolAa4hclXtJcMSYnlTDy/t5DNRgdesVaoMXtwzow=="],
@@ -906,7 +906,7 @@
906
906
 
907
907
  "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="],
908
908
 
909
- "ky": ["ky@2.0.2", "", {}, "sha512-/GmXpo9F9W+f8n4Ivr2iH+7h7wL7jLbLKWkMlpflcCRb6kGjBfTlASEXaZ9qUgNTn4VgS0P2pwxxzQ4EM6Ulgg=="],
909
+ "ky": ["ky@2.1.0", "", {}, "sha512-nIKwelw+7qVqKOlX4Eht6GXfEXlDHcdIjyLWV3s119kYt5s+70ayTnYS+0fRNmtgGRRAfqdeWQbh8YizqkS0ng=="],
910
910
 
911
911
  "lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
912
912
 
@@ -1128,7 +1128,7 @@
1128
1128
 
1129
1129
  "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="],
1130
1130
 
1131
- "stitchkit": ["stitchkit@0.90.5", "", { "dependencies": { "ky": "^2.0.2" }, "peerDependencies": { "@modelcontextprotocol/ext-apps": "^1.7.2", "@modelcontextprotocol/server": "^2.0.0", "@openrouter/ai-sdk-provider": "^3.0.0", "@opentelemetry/api": "^1.9.0", "@socket.io/bun-engine": "^0.1.1", "@socket.io/component-emitter": "^3.1.2", "@tanstack/react-query": ">=5", "@types/bun": "^1.3.14", "ai": "^7.0.0", "grammy": "^1.45.1", "maxmind": "^5.0.7", "react": ">=18", "react-query-kit": "^3.3.3", "socket.io": "^4.8.3", "socket.io-client": "^4.8.3", "srvx": "^0.12.5", "zod": "^4.4.3" }, "optionalPeers": ["@modelcontextprotocol/ext-apps", "@modelcontextprotocol/server", "@openrouter/ai-sdk-provider", "@opentelemetry/api", "@socket.io/bun-engine", "@socket.io/component-emitter", "@tanstack/react-query", "@types/bun", "ai", "grammy", "maxmind", "react", "react-query-kit", "socket.io", "socket.io-client", "srvx"], "bin": { "stitchkit": "dist/upgrade-cli.js" } }, "sha512-DCkHJYXbDB6VNUUgi0gnSsrB3ukrYBYKzyT072dkFLFrBUhg+lRmfgLFL+fAs+XKO7IB+xfCcpSw2lop/wxLBw=="],
1131
+ "stitchkit": ["stitchkit@0.90.7", "", { "dependencies": { "ky": "^2.1.0" }, "peerDependencies": { "@modelcontextprotocol/ext-apps": "^1.7.2 || ^2.0.0", "@modelcontextprotocol/server": "^2.0.0", "@openrouter/ai-sdk-provider": "^3.1.0", "@opentelemetry/api": "^1.9.0", "@socket.io/bun-engine": "^0.1.2", "@socket.io/component-emitter": "^3.1.2", "@tanstack/react-query": ">=5", "@types/bun": "^1.4.2", "ai": "^7.0.107", "google-auth-library": "^11.1.0", "grammy": "^1.46.0", "maxmind": "^5.0.7", "react": ">=18", "react-query-kit": "^3.3.3", "socket.io": "^4.8.3", "socket.io-client": "^4.8.3", "srvx": "^0.12.5 || ^1.0.5", "zod": "^4.6.5" }, "optionalPeers": ["@modelcontextprotocol/ext-apps", "@modelcontextprotocol/server", "@openrouter/ai-sdk-provider", "@opentelemetry/api", "@socket.io/bun-engine", "@socket.io/component-emitter", "@tanstack/react-query", "@types/bun", "ai", "google-auth-library", "grammy", "maxmind", "react", "react-query-kit", "socket.io", "socket.io-client", "srvx"], "bin": { "stitchkit": "dist/upgrade-cli.js" } }, "sha512-My36iuzUc4FULJ26jIQK/czwY+uBy8MRh6AgbnvQNBIvo4fghuiINmN501uoJzeN7JM8v4J1C6fqjwA/XNxf/A=="],
1132
1132
 
1133
1133
  "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="],
1134
1134
 
@@ -7,7 +7,7 @@
7
7
  "packages/*"
8
8
  ],
9
9
  "catalog": {
10
- "stitchkit": "^0.90.5"
10
+ "stitchkit": "^0.90.7"
11
11
  },
12
12
  "overrides": {
13
13
  "@playwright/test": "1.63.0",
@@ -1,4 +1,5 @@
1
1
  import { describe, expect, test } from 'bun:test';
2
+ import { ApiError } from 'stitchkit';
2
3
  import { getQueryClient } from './query-client';
3
4
 
4
5
  describe('query dehydration policy', () => {
@@ -20,9 +21,25 @@ describe('query dehydration policy', () => {
20
21
  expect(shouldDehydrate(pending)).toBe(true);
21
22
  });
22
23
 
23
- test('retries a query once and never retries a mutation', () => {
24
+ test('retries a server failure once and never retries a mutation', () => {
25
+ // Asserted through the decision, not through the literal: the policy is a
26
+ // predicate now, and `retry === 1` would pass for a client that retries an
27
+ // unauthorized request too.
24
28
  const defaults = getQueryClient().getDefaultOptions();
25
- expect(defaults.queries?.retry).toBe(1);
29
+ const retry = defaults.queries?.retry;
30
+ if (typeof retry !== 'function') throw new Error('query retry policy is required');
31
+ const serverFailure = new ApiError('INTERNAL_SERVER_ERROR', 500);
32
+ expect(retry(0, serverFailure)).toBe(true);
33
+ expect(retry(1, serverFailure)).toBe(false);
26
34
  expect(defaults.mutations?.retry).toBe(false);
27
35
  });
36
+
37
+ test('a request the user cannot repeat into success is not retried', () => {
38
+ const defaults = getQueryClient().getDefaultOptions();
39
+ const retry = defaults.queries?.retry;
40
+ if (typeof retry !== 'function') throw new Error('query retry policy is required');
41
+ for (const code of ['UNAUTHORIZED', 'FORBIDDEN', 'VALIDATION_ERROR']) {
42
+ expect(retry(0, new ApiError(code, 401))).toBe(false);
43
+ }
44
+ });
28
45
  });
@@ -1,36 +1,17 @@
1
- import {
2
- defaultShouldDehydrateQuery,
3
- environmentManager,
4
- QueryClient,
5
- type QueryClientConfig,
6
- } from '@tanstack/react-query';
7
1
  import { cache } from 'react';
2
+ import { createQueryClientFactory } from 'stitchkit/react';
8
3
 
9
- // The template targets the published catalog release, so it cannot import
10
- // `createQueryClientFactory` yet; the retry policy below is the part of that
11
- // factory a released core already lets it state. UPGRADING names the cutover.
12
- const config = {
13
- defaultOptions: {
14
- // One retry for queries, none for mutations — a mutation retried on a
15
- // timeout may run twice; a query only reads.
16
- queries: { staleTime: 30_000, retry: 1 },
17
- mutations: { retry: false },
18
- dehydrate: {
19
- shouldDehydrateQuery: (query) =>
20
- defaultShouldDehydrateQuery(query) || query.state.status === 'pending',
21
- },
4
+ // One request-local server client and one browser singleton, both from the
5
+ // framework. The local construction this replaced was a second implementation
6
+ // of the same thing: it restated the dehydration policy and the mutation rule,
7
+ // and it spelled the query retry as a plain `1`, which retries an unauthorized
8
+ // or invalid request exactly as eagerly as a network blip. `apiErrorRetry` —
9
+ // the factory's default — retries what is worth retrying and nothing else.
10
+ // Project-specific cache configuration and mutation toasts belong in these
11
+ // options, never in a copy of the framework's retry predicate.
12
+ export const getQueryClient = createQueryClientFactory({
13
+ serverCache: cache,
14
+ queryClient: {
15
+ defaultOptions: { queries: { staleTime: 30_000 } },
22
16
  },
23
- } satisfies QueryClientConfig;
24
-
25
- function createQueryClient(): QueryClient {
26
- return new QueryClient(config);
27
- }
28
-
29
- const getServerQueryClient = cache(createQueryClient);
30
- let browserQueryClient: QueryClient | undefined;
31
-
32
- export function getQueryClient(): QueryClient {
33
- if (environmentManager.isServer()) return getServerQueryClient();
34
- browserQueryClient ??= createQueryClient();
35
- return browserQueryClient;
36
- }
17
+ });