@everystack/server 0.2.9 → 0.2.11

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@everystack/server",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "description": "Server runtime primitives for Lambda — event adapters, routing, SSR, image processing",
5
5
  "license": "AGPL-3.0-only",
6
6
  "publishConfig": {
package/src/db.ts CHANGED
@@ -1,21 +1,57 @@
1
1
  /// <reference path="./sst-env.d.ts" />
2
2
  /**
3
- * @everystack/server/db — SST Resource-linked database helpers.
3
+ * @everystack/server/db — Database and secret helpers.
4
4
  *
5
- * Provides a lazy singleton Drizzle connection using SST Resource linking
6
- * for database credentials and secrets.
5
+ * Provides a lazy singleton Drizzle connection. Supports both SST Resource
6
+ * linking (PascalCase names) and process.env (SCREAMING_SNAKE_CASE names)
7
+ * transparently. Works with createSecrets() or manual sst.Secret declarations.
7
8
  */
8
9
 
9
10
  import { Resource } from 'sst';
10
11
  import { drizzle } from 'drizzle-orm/postgres-js';
11
12
  import postgres from 'postgres';
12
13
 
14
+ /**
15
+ * Safely read a Resource property. Returns undefined if the Resource
16
+ * or property doesn't exist (avoids runtime throw from SST's proxy).
17
+ */
18
+ function tryResource<T>(fn: () => T): T | undefined {
19
+ try {
20
+ const val = fn();
21
+ // SST's Resource proxy may return undefined for missing keys
22
+ return val === undefined ? undefined : val;
23
+ } catch {
24
+ return undefined;
25
+ }
26
+ }
27
+
13
28
  export function getDatabaseUrl(): string {
14
- return `postgresql://${Resource.Database.username}:${Resource.Database.password}@${Resource.Database.host}:${Resource.Database.port}/${Resource.Database.database}`;
29
+ // SST Postgres component (Resource.Database)
30
+ const db = tryResource(() => Resource.Database);
31
+ if (db?.host) {
32
+ return `postgresql://${db.username}:${db.password}@${db.host}:${db.port}/${db.database}`;
33
+ }
34
+ // SST Secret — PascalCase (Resource.DatabaseUrl.value)
35
+ const pascal = tryResource(() => (Resource as any).DatabaseUrl?.value as string);
36
+ if (pascal) return pascal;
37
+ // SST Secret — raw env name (Resource.DATABASE_URL.value)
38
+ const raw = tryResource(() => (Resource as any).DATABASE_URL?.value as string);
39
+ if (raw) return raw;
40
+ // process.env fallback
41
+ if (process.env.DATABASE_URL) return process.env.DATABASE_URL;
42
+ throw new Error('DATABASE_URL not set — link an sst.aws.Postgres or sst.Secret, or set process.env.DATABASE_URL');
15
43
  }
16
44
 
17
45
  export function getJwtSecret(): Uint8Array {
18
- return new TextEncoder().encode(Resource.JwtSecret.value);
46
+ // PascalCase Resource (Resource.JwtSecret.value)
47
+ const pascal = tryResource(() => (Resource as any).JwtSecret?.value as string);
48
+ if (pascal) return new TextEncoder().encode(pascal);
49
+ // Raw env name Resource (Resource.JWT_SECRET.value)
50
+ const raw = tryResource(() => (Resource as any).JWT_SECRET?.value as string);
51
+ if (raw) return new TextEncoder().encode(raw);
52
+ // process.env fallback
53
+ if (process.env.JWT_SECRET) return new TextEncoder().encode(process.env.JWT_SECRET);
54
+ throw new Error('JWT_SECRET not set — link an sst.Secret or set process.env.JWT_SECRET');
19
55
  }
20
56
 
21
57
  // Lazy singleton DB connection
package/src/plugin.ts CHANGED
@@ -466,9 +466,13 @@ export function dbPlugin(options: DbPluginOptions): Plugin {
466
466
  const { eq, and, or, gt, lt, gte, lte, ne, count, sum, avg, sql, desc, asc } =
467
467
  await import('drizzle-orm');
468
468
  const evalContext: Record<string, unknown> = {
469
+ ctx,
469
470
  db: ctx.db, schema: ctx.schema,
470
471
  eq, and, or, gt, lt, gte, lte, ne, count, sum, avg, sql, desc, asc,
471
472
  };
473
+ // Expose plugin-contributed context (notifications, rpc, etc.)
474
+ if (ctx.notifications) evalContext.notifications = ctx.notifications;
475
+ if (ctx.rpc) evalContext.rpc = ctx.rpc;
472
476
  const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
473
477
  const fn = new AsyncFunction(
474
478
  ...Object.keys(evalContext),
package/src/sst-env.d.ts CHANGED
@@ -4,9 +4,14 @@
4
4
  /**
5
5
  * SST Resource type declarations for @everystack/server/db.
6
6
  *
7
- * Augments the `sst` module's Resource interface with the shapes
8
- * expected by db.ts. Consumers must link matching SST resources
9
- * (e.g., sst.aws.Postgres named "Database", sst.Secret named "JwtSecret").
7
+ * Augments the `sst` module's Resource interface with shapes expected
8
+ * by db.ts. Both PascalCase (JwtSecret) and SCREAMING_SNAKE_CASE
9
+ * (JWT_SECRET) naming conventions are supported at runtime — db.ts
10
+ * tries each in order and falls back to process.env.
11
+ *
12
+ * Consumers link matching SST resources in sst.config.ts:
13
+ * - sst.aws.Postgres named "Database"
14
+ * - sst.Secret named "JwtSecret" or "JWT_SECRET"
10
15
  */
11
16
  declare module 'sst' {
12
17
  export interface Resource {
@@ -20,6 +25,7 @@ declare module 'sst' {
20
25
  JwtSecret: {
21
26
  value: string;
22
27
  };
28
+ [key: string]: any;
23
29
  }
24
30
  }
25
31