@stjoseph/prumo 0.4.1 → 0.4.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.
@@ -55,11 +55,13 @@ been published since January 2024. Every other rule here (placement, no database
55
55
  behaviour) holds unchanged. **What it costs:** two runners in one workspace with near-identical APIs, so `vi.fn`
56
56
  written in a mobile test is a mistake an assistant will make.
57
57
 
58
- **Three things a mobile test meets, verified with Expo SDK 57:** `render` from `@testing-library/react-native` 14
58
+ **Four things a mobile test meets, verified with Expo SDK 57:** `render` from `@testing-library/react-native` 14
59
59
  is **asynchronous** and its matchers need no setup. `expo-secure-store` needs the native Keychain or Keystore,
60
60
  which Jest does not have, so a test builds the auth client with an in-memory token store, which is why a form
61
61
  asks only for the auth methods it calls. And `jest-expo` leaves the app manifest empty, so `expo-linking` cannot
62
- know the app's scheme: a test that reaches `Linking.createURL` mocks it with the scheme it expects.
62
+ know the app's scheme: a test that reaches `Linking.createURL` mocks it with the scheme it expects. A test rendering a `useMutation` builds
63
+ its `QueryClient` with `mutations: { gcTime: Infinity }`: once unmounted, the mutation otherwise starts TanStack
64
+ Query's five-minute garbage-collection timer, and Jest force-exits the worker still waiting on it.
63
65
 
64
66
  A coverage threshold turns a proxy into a target. Whoever is below writes tests to raise the number rather
65
67
  than to check anything; whoever is above stops thinking. With no database mocks a test costs more to write,
@@ -11,6 +11,8 @@ boot, and never by hand.
11
11
  Make every migration compatible with the **previous** release. Removing a column is two deployments:
12
12
  one that stops using it, one that removes it.
13
13
 
14
+ Keep the `package.json` in the migrations directory. It holds only `"type": "module"`.
15
+
14
16
  Delete the generated `down`. Undoing a schema change is a new migration going forward.
15
17
 
16
18
  Keep DDL and data apart. A migration performs DDL only. Filling existing rows is a separate task, run
@@ -31,6 +33,12 @@ migration fails. It would also mean **the process serving requests holds DDL per
31
33
  injection or compromise could drop a table. A separate step lets the deploy use a privileged user for
32
34
  thirty seconds while the application runs as one that only reads and writes rows.
33
35
 
36
+ The migrations are TypeScript that Node loads directly, by type stripping; the build never compiles
37
+ them. They use `import`, and without a declared module type Node guesses: it fails to parse them as
38
+ CommonJS, reparses them as ES modules, and warns `MODULE_TYPELESS_PACKAGE_JSON` in every process that
39
+ runs them. The nearest `package.json` decides a file's type, so the one in the migrations directory
40
+ settles it for those files alone and leaves the CommonJS build untouched.
41
+
34
42
  During a rolling deploy the previous version is still running against the new schema, so a migration
35
43
  that removes something the old code reads breaks production before the new version finishes arriving.
36
44
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stjoseph/prumo",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Scaffolds a TypeScript project and ships it the conventions it follows, in a .prumo/ folder any AI assistant reads.",
5
5
  "keywords": [
6
6
  "scaffolding",
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -1,7 +1,7 @@
1
1
  import { PostgreSqlContainer, type StartedPostgreSqlContainer } from '@testcontainers/postgresql'
2
2
  import type { TestProject } from 'vitest/node'
3
3
 
4
- let container: StartedPostgreSqlContainer
4
+ let container: StartedPostgreSqlContainer | undefined
5
5
 
6
6
  export async function setup(project: TestProject): Promise<void> {
7
7
  container = await new PostgreSqlContainer('postgres:18-alpine').start()
@@ -9,8 +9,10 @@ export async function setup(project: TestProject): Promise<void> {
9
9
  project.provide('postgresUrl', container.getConnectionUri())
10
10
  }
11
11
 
12
+ // Vitest still tears down when setup threw, as when the image could not be pulled; there is no container then, and
13
+ // failing here would print a second error over the one that matters.
12
14
  export async function teardown(): Promise<void> {
13
- await container.stop()
15
+ await container?.stop()
14
16
  }
15
17
 
16
18
  declare module 'vitest' {
@@ -36,8 +36,12 @@ describe('ProfileForm', () => {
36
36
  }),
37
37
  })
38
38
 
39
+ // A mutation left without an observer starts a five-minute garbage-collection timer, and Jest's worker would wait
40
+ // it out. Infinity schedules none, as TanStack Query's testing guide advises.
41
+ const queryClient = new QueryClient({ defaultOptions: { mutations: { gcTime: Infinity } } })
42
+
39
43
  await render(
40
- <QueryClientProvider client={new QueryClient()}>
44
+ <QueryClientProvider client={queryClient}>
41
45
  <ProfileForm api={api} profile={profile} />
42
46
  </QueryClientProvider>,
43
47
  )