@trylight/app 0.1.2 → 0.1.3

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/README.md CHANGED
@@ -46,6 +46,11 @@ sign-in; a stranger to a public app only reads what members kept.
46
46
 
47
47
  ## Schema sync
48
48
 
49
+ `light-db sync` and `migrate` connect with `DATABASE_URL_UNPOOLED`, the
50
+ database's direct address, and refuse to run without it: they serialise on
51
+ Postgres advisory locks, which a transaction pooler (the app's `DATABASE_URL`
52
+ on Neon) keeps on a pooled connection after the process exits.
53
+
49
54
  `plan()` diffs `db/schema` against the newest migration snapshot with
50
55
  drizzle-kit's API, removals first and additions second, so drizzle-kit never
51
56
  has to ask whether a column was renamed (it can't without a terminal).
package/dist/db/cli.js CHANGED
@@ -24,7 +24,9 @@ const SYNC_LOCK_KEY = 4_815_162_343;
24
24
  /** Exit code of a migrate whose migrations committed but whose change triggers didn't go on. */
25
25
  const APPLIED_UNTRACKED = 2;
26
26
  // pg_advisory_lock waits for the one before it; a process that dies releases it.
27
- const connect = (onnotice = () => undefined) => postgres(databaseUrl(), { max: 1, onnotice });
27
+ // Only on a direct connection: behind a transaction pooler the lock stays on a
28
+ // pooled server connection after the process exits, and every later sync waits.
29
+ const connect = (onnotice = () => undefined) => postgres(unpooledDatabaseUrl(), { max: 1, onnotice });
28
30
  const root = process.cwd();
29
31
  const envFile = join(root, ".env.local");
30
32
  if (existsSync(envFile))
@@ -33,11 +35,12 @@ const [command] = process.argv.slice(2);
33
35
  async function loadSchema() {
34
36
  return import(pathToFileURL(join(root, SCHEMA_ENTRY)).href);
35
37
  }
36
- function databaseUrl() {
37
- const { databaseUrl } = env();
38
- if (databaseUrl === undefined)
39
- throw new Error("DATABASE_URL is not set: there is no database to sync db/schema into.");
40
- return databaseUrl;
38
+ function unpooledDatabaseUrl() {
39
+ const { unpooledDatabaseUrl } = env();
40
+ if (unpooledDatabaseUrl === undefined) {
41
+ throw new Error("DATABASE_URL_UNPOOLED is not set: light-db sync and migrate need the database's direct (unpooled) connection.");
42
+ }
43
+ return unpooledDatabaseUrl;
41
44
  }
42
45
  /**
43
46
  * Makes the database match `db/schema`, whatever the change does to the rows
@@ -1,6 +1,7 @@
1
1
  /** Read when asked, not at import: a build imports this without a runtime env. */
2
2
  export declare function env(): {
3
3
  databaseUrl: string | undefined;
4
+ unpooledDatabaseUrl: string | undefined;
4
5
  /** Sign-in needs Plaza, this app's id in it, and a database for sessions. */
5
6
  configured: boolean;
6
7
  /**
@@ -4,12 +4,15 @@ import { z } from "zod/v4";
4
4
  const serverEnv = z
5
5
  .object({
6
6
  DATABASE_URL: z.string().min(1).optional(),
7
+ /** The same database without a pooler: schema changes and their locks need a session. */
8
+ DATABASE_URL_UNPOOLED: z.string().min(1).optional(),
7
9
  MII_PLAZA_URL: z.url().optional(),
8
10
  MII_APP_ID: z.string().min(1).optional(),
9
11
  NODE_ENV: z.string().optional()
10
12
  })
11
13
  .transform((env) => ({
12
14
  databaseUrl: env.DATABASE_URL,
15
+ unpooledDatabaseUrl: env.DATABASE_URL_UNPOOLED,
13
16
  /** Sign-in needs Plaza, this app's id in it, and a database for sessions. */
14
17
  configured: Boolean(env.MII_PLAZA_URL && env.MII_APP_ID && env.DATABASE_URL),
15
18
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trylight/app",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "The Light SDK every v2 org app depends on: entities (Drizzle tables with a typed client), sign-in, contracts and in-app chat.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",