@spfn/core 0.3.0-beta.7 → 0.3.0-beta.9

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.
@@ -125,8 +125,17 @@ interface JobSendOptions {
125
125
  }
126
126
  /**
127
127
  * Job handler function type
128
+ *
129
+ * The check is wrapped in a tuple — `[TInput] extends [void]`, not
130
+ * `TInput extends void` — to stop the conditional from distributing. A bare
131
+ * type parameter in the check position makes the conditional distributive, so a
132
+ * union input `A | B | C` is evaluated per member and the results are unioned
133
+ * into three function types whose parameters intersect to `never`: no caller
134
+ * can then pass any member. The tuple makes the union one opaque type, which is
135
+ * what a handler taking `A | B | C` needs. The same wrap is on `send`, `run`
136
+ * and `sendBatch` below, for the same reason. Do not "simplify" it away.
128
137
  */
129
- type JobHandler<TInput, TOutput = void> = TInput extends void ? () => Promise<TOutput> : (input: TInput) => Promise<TOutput>;
138
+ type JobHandler<TInput, TOutput = void> = [TInput] extends [void] ? () => Promise<TOutput> : (input: TInput) => Promise<TOutput>;
130
139
  /**
131
140
  * Compensate handler function type (for rollback)
132
141
  */
@@ -178,17 +187,17 @@ interface JobDef<TInput = void, TOutput = void> {
178
187
  /**
179
188
  * Send job to queue (returns immediately, executes in background)
180
189
  */
181
- send: TInput extends void ? (options?: JobSendOptions) => Promise<string | null> : (input: TInput, options?: JobSendOptions) => Promise<string | null>;
190
+ send: [TInput] extends [void] ? (options?: JobSendOptions) => Promise<string | null> : (input: TInput, options?: JobSendOptions) => Promise<string | null>;
182
191
  /**
183
192
  * Run job synchronously (for testing/debugging)
184
193
  */
185
- run: TInput extends void ? () => Promise<TOutput> : (input: TInput) => Promise<TOutput>;
194
+ run: [TInput] extends [void] ? () => Promise<TOutput> : (input: TInput) => Promise<TOutput>;
186
195
  /**
187
196
  * Bulk insert jobs into the queue (pg-boss insert).
188
197
  * Much faster than calling send() in a loop.
189
198
  * Only available for jobs with input schema.
190
199
  */
191
- sendBatch: TInput extends void ? (options?: JobSendOptions) => Promise<void> : (inputs: TInput[], options?: JobSendOptions) => Promise<void>;
200
+ sendBatch: [TInput] extends [void] ? (options?: JobSendOptions) => Promise<void> : (inputs: TInput[], options?: JobSendOptions) => Promise<void>;
192
201
  /**
193
202
  * Type inference helpers
194
203
  */
@@ -162,7 +162,7 @@ var coreEnvSchema = defineEnvSchema({
162
162
  // Drizzle ORM
163
163
  // ========================================================================
164
164
  DRIZZLE_SCHEMA_PATH: envString({
165
- description: "Path to Drizzle schema configuration",
165
+ description: "Entity registry file spfn db push/generate/studio load when src/server/entities/ holds no entity file, or when the registry exports a table no file there defines",
166
166
  required: false,
167
167
  default: "./src/server/entities/config.ts",
168
168
  examples: ["./src/db/schema.ts", "./src/server/entities/config.ts"]
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/config/schema.ts","../../src/config/index.ts"],"names":[],"mappings":";;;AAsCO,IAAM,gBAAgB,eAAA,CAAgB;AAAA;AAAA;AAAA;AAAA,EAKzC,QAAA,EAAU,QAAQ,CAAC,OAAA,EAAS,eAAe,SAAA,EAAW,YAAA,EAAc,MAAM,CAAA,EAAY;AAAA,IAClF,WAAA,EAAa,6BAAA;AAAA,IACb,OAAA,EAAS,OAAA;AAAA,IACT,MAAA,EAAQ;AAAA,GACX,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,cAAc,SAAA,CAAU;AAAA,IACpB,WAAA,EAAa,iCAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,SAAA,EAAW,IAAA;AAAA,IACX,SAAA,EAAW,gBAAA;AAAA,IACX,QAAA,EAAU,CAAC,kDAAkD;AAAA,GAChE,CAAA;AAAA,EAED,oBAAoB,SAAA,CAAU;AAAA,IAC1B,WAAA,EAAa,6CAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,SAAA,EAAW,IAAA;AAAA,IACX,SAAA,EAAW,gBAAA;AAAA,IACX,QAAA,EAAU,CAAC,+CAA+C;AAAA,GAC7D,CAAA;AAAA,EAED,mBAAmB,SAAA,CAAU;AAAA,IACzB,WAAA,EAAa,4CAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,SAAA,EAAW,IAAA;AAAA,IACX,SAAA,EAAW,gBAAA;AAAA,IACX,QAAA,EAAU,CAAC,gDAAgD;AAAA,GAC9D,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,aAAa,SAAA,CAAU;AAAA,IACnB,WAAA,EAAa,gDAAA;AAAA,IACb,OAAA,EAAS,EAAA;AAAA,IACT,QAAA,EAAU,CAAC,EAAA,EAAI,EAAA,EAAI,EAAE;AAAA,GACxB,CAAA;AAAA,EAED,kBAAkB,SAAA,CAAU;AAAA,IACxB,WAAA,EAAa,wOAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,QAAA,EAAU,CAAC,CAAA,EAAG,EAAA,EAAI,EAAE;AAAA,GACvB,CAAA;AAAA,EAED,sBAAsB,SAAA,CAAU;AAAA,IAC5B,WAAA,EAAa,6CAAA;AAAA,IACb,OAAA,EAAS,EAAA;AAAA,IACT,QAAA,EAAU,CAAC,EAAA,EAAI,EAAA,EAAI,EAAE;AAAA,GACxB,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,cAAc,SAAA,CAAU;AAAA,IACpB,WAAA,EAAa,sDAAA;AAAA,IACb,OAAA,EAAS,CAAA;AAAA,IACT,QAAA,EAAU,CAAC,CAAA,EAAG,CAAA,EAAG,EAAE;AAAA,GACtB,CAAA;AAAA,EAED,wBAAwB,SAAA,CAAU;AAAA,IAC9B,WAAA,EAAa,8DAAA;AAAA,IACb,OAAA,EAAS,GAAA;AAAA,IACT,QAAA,EAAU,CAAC,EAAA,EAAI,GAAA,EAAK,GAAG;AAAA,GAC1B,CAAA;AAAA,EAED,oBAAoB,SAAA,CAAU;AAAA,IAC1B,WAAA,EAAa,8DAAA;AAAA,IACb,OAAA,EAAS,GAAA;AAAA,IACT,QAAA,EAAU,CAAC,GAAA,EAAM,GAAA,EAAO,GAAK;AAAA,GAChC,CAAA;AAAA,EAED,iBAAiB,SAAA,CAAU;AAAA,IACvB,WAAA,EAAa,sDAAA;AAAA,IACb,OAAA,EAAS,CAAA;AAAA,IACT,QAAA,EAAU,CAAC,CAAA,EAAG,GAAA,EAAK,CAAC;AAAA,GACvB,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,yBAAyB,UAAA,CAAW;AAAA,IAChC,WAAA,EAAa,wCAAA;AAAA,IACb,OAAA,EAAS,IAAA;AAAA,IACT,QAAA,EAAU,CAAC,IAAA,EAAM,KAAK;AAAA,GACzB,CAAA;AAAA,EAED,0BAA0B,SAAA,CAAU;AAAA,IAChC,WAAA,EAAa,+CAAA;AAAA,IACb,OAAA,EAAS,GAAA;AAAA,IACT,QAAA,EAAU,CAAC,GAAA,EAAO,GAAA,EAAO,IAAM;AAAA,GAClC,CAAA;AAAA,EAED,2BAA2B,UAAA,CAAW;AAAA,IAClC,WAAA,EAAa,+CAAA;AAAA,IACb,OAAA,EAAS,IAAA;AAAA,IACT,QAAA,EAAU,CAAC,IAAA,EAAM,KAAK;AAAA,GACzB,CAAA;AAAA,EAED,6BAA6B,SAAA,CAAU;AAAA,IACnC,WAAA,EAAa,8DAAA;AAAA,IACb,OAAA,EAAS,CAAA;AAAA,IACT,QAAA,EAAU,CAAC,CAAA,EAAG,CAAA,EAAG,EAAE;AAAA,GACtB,CAAA;AAAA,EAED,gCAAgC,SAAA,CAAU;AAAA,IACtC,WAAA,EAAa,6DAAA;AAAA,IACb,OAAA,EAAS,GAAA;AAAA,IACT,QAAA,EAAU,CAAC,GAAA,EAAM,GAAA,EAAO,IAAK;AAAA,GAChC,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,uBAAuB,UAAA,CAAW;AAAA,IAC9B,WAAA,EAAa,8CAAA;AAAA,IACb,OAAA,EAAS,KAAA;AAAA,IACT,QAAA,EAAU,CAAC,IAAA,EAAM,KAAK;AAAA,GACzB,CAAA;AAAA,EAED,8BAA8B,SAAA,CAAU;AAAA,IACpC,WAAA,EAAa,oDAAA;AAAA,IACb,OAAA,EAAS,GAAA;AAAA,IACT,QAAA,EAAU,CAAC,GAAA,EAAK,GAAA,EAAM,GAAI;AAAA,GAC7B,CAAA;AAAA,EAED,2BAA2B,UAAA,CAAW;AAAA,IAClC,WAAA,EAAa,kDAAA;AAAA,IACb,OAAA,EAAS,KAAA;AAAA,IACT,QAAA,EAAU,CAAC,IAAA,EAAM,KAAK;AAAA,GACzB,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,qBAAqB,SAAA,CAAU;AAAA,IAC3B,WAAA,EAAa,qCAAA;AAAA,IACb,OAAA,EAAS,GAAA;AAAA,IACT,QAAA,EAAU,CAAC,GAAA,EAAO,GAAA,EAAO,GAAK;AAAA,GACjC,CAAA;AAAA,EAED,0BAA0B,SAAA,CAAU;AAAA,IAChC,WAAA,EAAa,+NAAA;AAAA,IACb,OAAA,EAAS,GAAA;AAAA,IACT,QAAA,EAAU,CAAC,GAAA,EAAO,GAAA,EAAO,CAAC;AAAA,GAC7B,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,8BAA8B,SAAA,CAAU;AAAA,IACpC,WAAA,EAAa,6LAAA;AAAA,IACb,OAAA,EAAS,CAAA;AAAA,IACT,QAAA,EAAU,CAAC,CAAA,EAAG,CAAA,EAAG,EAAE;AAAA,GACtB,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,gBAAgB,UAAA,CAAW;AAAA,IACvB,WAAA,EAAa,uDAAA;AAAA,IACb,OAAA,EAAS,KAAA;AAAA,IACT,QAAA,EAAU,CAAC,IAAA,EAAM,KAAK;AAAA,GACzB,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,+BAA+B,UAAA,CAAW;AAAA,IACtC,WAAA,EAAa,+QAAA;AAAA,IACb,OAAA,EAAS,KAAA;AAAA,IACT,QAAA,EAAU,CAAC,IAAA,EAAM,KAAK;AAAA,GACzB,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,qBAAqB,SAAA,CAAU;AAAA,IAC3B,WAAA,EAAa,sCAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,OAAA,EAAS,iCAAA;AAAA,IACT,QAAA,EAAU,CAAC,oBAAA,EAAsB,iCAAiC;AAAA,GACrE,CAAA;AAAA,EAED,iBAAiB,SAAA,CAAU;AAAA,IACvB,WAAA,EAAa,yCAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,OAAA,EAAS,WAAA;AAAA,IACT,QAAA,EAAU,CAAC,WAAA,EAAa,cAAc;AAAA,GACzC,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,cAAA,EAAgB,QAAQ,CAAC,OAAA,EAAS,QAAQ,MAAA,EAAQ,OAAA,EAAS,OAAO,CAAA,EAAY;AAAA,IAC1E,WAAA,EAAa,6BAAA;AAAA,IACb,OAAA,EAAS;AAAA,GACZ,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,WAAW,SAAA,CAAU;AAAA,IACjB,WAAA,EAAa,kCAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,SAAA,EAAW,IAAA;AAAA,IACX,SAAA,EAAW,aAAA;AAAA,IACX,QAAA,EAAU,CAAC,wBAAA,EAA0B,gCAAgC;AAAA,GACxE,CAAA;AAAA,EAED,iBAAiB,SAAA,CAAU;AAAA,IACvB,WAAA,EAAa,6DAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,SAAA,EAAW,IAAA;AAAA,IACX,SAAA,EAAW,aAAA;AAAA,IACX,QAAA,EAAU,CAAC,qBAAqB;AAAA,GACnC,CAAA;AAAA,EAED,gBAAgB,SAAA,CAAU;AAAA,IACtB,WAAA,EAAa,6DAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,SAAA,EAAW,IAAA;AAAA,IACX,SAAA,EAAW,aAAA;AAAA,IACX,QAAA,EAAU,CAAC,sBAAsB;AAAA,GACpC,CAAA;AAAA,EAED,sBAAsB,SAAA,CAAU;AAAA,IAC5B,WAAA,EAAa,sCAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,QAAA,EAAU,CAAC,iCAAiC;AAAA,GAC/C,CAAA;AAAA,EAED,qBAAqB,SAAA,CAAU;AAAA,IAC3B,WAAA,EAAa,qCAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,QAAA,EAAU,CAAC,kCAAkC;AAAA,GAChD,CAAA;AAAA,EAED,mBAAmB,SAAA,CAAU;AAAA,IACzB,WAAA,EAAa,4BAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,QAAA,EAAU,CAAC,UAAU;AAAA,GACxB,CAAA;AAAA,EAED,gBAAgB,SAAA,CAAU;AAAA,IACtB,WAAA,EAAa,sCAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,SAAA,EAAW,IAAA;AAAA,IACX,QAAA,EAAU,CAAC,qBAAqB;AAAA,GACnC,CAAA;AAAA,EAED,+BAA+B,UAAA,CAAW;AAAA,IACtC,WAAA,EAAa,sDAAA;AAAA,IACb,OAAA,EAAS,IAAA;AAAA,IACT,QAAA,EAAU,CAAC,IAAA,EAAM,KAAK;AAAA,GACzB,CAAA;AAAA,EAED,+BAA+B,SAAA,CAAU;AAAA,IACrC,WAAA,EAAa,4HAAA;AAAA,IACb,OAAA,EAAS,CAAA;AAAA,IACT,QAAA,EAAU,CAAC,CAAA,EAAG,CAAA,EAAG,EAAE;AAAA,GACtB,CAAA;AAAA,EAED,4BAA4B,UAAA,CAAW;AAAA,IACnC,WAAA,EAAa,yJAAA;AAAA,IACb,OAAA,EAAS,IAAA;AAAA,IACT,QAAA,EAAU,CAAC,IAAA,EAAM,KAAK;AAAA,GACzB,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,aAAa,SAAA,CAAU;AAAA,IACnB,WAAA,EAAa,6MAAA;AAAA,IACb,OAAA,EAAS,CAAA;AAAA,IACT,QAAA,EAAU,CAAC,CAAA,EAAG,GAAA,EAAM,GAAK;AAAA,GAC5B,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAsBD,gBAAgB,SAAA,CAAU;AAAA,IACtB,WAAA,EAAa,iCAAA;AAAA,IACb,OAAA,EAAS,IAAA;AAAA,IACT,QAAA,EAAU,CAAC,GAAA,EAAO,IAAA,EAAQ,GAAM;AAAA,GACnC,CAAA;AAAA,EAED,0BAA0B,SAAA,CAAU;AAAA,IAChC,WAAA,EAAa,oCAAA;AAAA,IACb,OAAA,EAAS,IAAA;AAAA,IACT,QAAA,EAAU,CAAC,GAAA,EAAO,IAAA,EAAO,IAAM;AAAA,GAClC,CAAA;AAAA,EAED,wBAAwB,SAAA,CAAU;AAAA,IAC9B,WAAA,EAAa,iCAAA;AAAA,IACb,OAAA,EAAS,GAAA;AAAA,IACT,QAAA,EAAU,CAAC,GAAA,EAAO,GAAA,EAAO,IAAM;AAAA,GAClC,CAAA;AAAA,EAED,kBAAkB,SAAA,CAAU;AAAA,IACxB,WAAA,EAAa,yIAAA;AAAA,IACb,OAAA,EAAS,IAAA;AAAA,IACT,QAAA,EAAU,CAAC,GAAA,EAAO,IAAA,EAAQ,IAAM;AAAA,GACnC,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,uBAAuB,SAAA,CAAU;AAAA,IAC7B,WAAA,EAAa,uGAAA;AAAA,IACb,OAAA,EAAS,GAAA;AAAA,IACT,QAAA,EAAU,CAAC,GAAA,EAAM,GAAA,EAAO,GAAK;AAAA,GAChC,CAAA;AAAA,EAED,uBAAuB,SAAA,CAAU;AAAA,IAC7B,WAAA,EAAa,6FAAA;AAAA,IACb,OAAA,EAAS,GAAA;AAAA,IACT,QAAA,EAAU,CAAC,IAAA,EAAQ,GAAA,EAAQ,GAAM;AAAA,GACpC,CAAA;AAAA,EAED,oBAAoB,SAAA,CAAU;AAAA,IAC1B,WAAA,EAAa,yFAAA;AAAA,IACb,OAAA,EAAS,GAAA;AAAA,IACT,QAAA,EAAU,CAAC,IAAA,EAAQ,GAAA,EAAQ,GAAM;AAAA,GACpC,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,cAAc,MAAA,CAAO;AAAA,IACjB,WAAA,EAAa,gDAAA;AAAA,IACb,QAAA,EAAU,IAAA;AAAA,IACV,MAAA,EAAQ,IAAA;AAAA,IACR,QAAA,EAAU,CAAC,uBAAA,EAAyB,0BAA0B;AAAA,GACjE,CAAA;AAAA,EAED,0BAA0B,MAAA,CAAO;AAAA,IAC7B,WAAA,EAAa,gDAAA;AAAA,IACb,QAAA,EAAU,IAAA;AAAA,IACV,MAAA,EAAQ,IAAA;AAAA,IACR,QAAA,EAAU,CAAC,uBAAA,EAAyB,0BAA0B;AAAA,GACjE,CAAA;AAAA,EAED,cAAc,MAAA,CAAO;AAAA,IACjB,WAAA,EAAa,+CAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,MAAA,EAAQ,IAAA;AAAA,IACR,QAAA,EAAU,CAAC,uBAAA,EAAyB,sBAAsB;AAAA,GAC7D,CAAA;AAAA,EAED,mBAAmB,SAAA,CAAU;AAAA,IACzB,WAAA,EAAa,mJAAA;AAAA,IACb,OAAA,EAAS,IAAA;AAAA,IACT,MAAA,EAAQ,IAAA;AAAA,IACR,QAAA,EAAU,CAAC,GAAA,EAAO,IAAA,EAAQ,IAAM;AAAA,GACnC,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,mBAAmB,SAAA,CAAU;AAAA,IACzB,WAAA,EAAa,iYAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,SAAA,EAAW,IAAA;AAAA,IACX,MAAA,EAAQ,IAAA;AAAA,IACR,QAAA,EAAU,CAAC,uBAAA,EAAyB,0BAA0B;AAAA,GACjE,CAAA;AAAA,EAED,4BAA4B,SAAA,CAAU;AAAA,IAClC,WAAA,EAAa,4UAAA;AAAA,IACb,QAAA,EAAU,KAAA;AAAA,IACV,SAAA,EAAW,IAAA;AAAA,IACX,MAAA,EAAQ,KAAA;AAAA,IACR,QAAA,EAAU,CAAC,iBAAA,EAAmB,qBAAqB;AAAA,GACtD,CAAA;AAAA,EAED,oBAAoB,SAAA,CAAU;AAAA,IAC1B,WAAA,EAAa,+aAAA;AAAA,IACb,OAAA,EAAS,CAAA;AAAA,IACT,MAAA,EAAQ,IAAA;AAAA,IACR,QAAA,EAAU,CAAC,CAAA,EAAG,CAAA,EAAG,CAAC;AAAA,GACrB,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,eAAA,EAAiB,OAAA,CAAQ,CAAC,KAAA,EAAO,IAAI,CAAA,EAAY;AAAA,IAC7C,WAAA,EAAa,CAAA,iTAAA,CAAA;AAAA,IACb,OAAA,EAAS;AAAA,GACZ,CAAA;AAAA,EAED,0BAA0B,SAAA,CAAU;AAAA,IAChC,WAAA,EAAa,mHAAA;AAAA,IACb,OAAA,EAAS,GAAA;AAAA,IACT,QAAA,EAAU,CAAC,EAAA,EAAI,GAAA,EAAK,GAAG;AAAA,GAC1B,CAAA;AAAA,EAED,8BAA8B,SAAA,CAAU;AAAA,IACpC,WAAA,EAAa,+DAAA;AAAA,IACb,OAAA,EAAS,GAAA;AAAA,IACT,QAAA,EAAU,CAAC,GAAA,EAAM,GAAK;AAAA,GACzB,CAAA;AAAA,EAED,wBAAwB,UAAA,CAAW;AAAA,IAC/B,WAAA,EAAa,6YAAA;AAAA,IACb,OAAA,EAAS;AAAA,GACZ,CAAA;AAAA;AAAA;AAAA;AAAA,EAMD,8BAA8B,UAAA,CAAW;AAAA,IACrC,WAAA,EAAa,0TAAA;AAAA,IACb,OAAA,EAAS;AAAA,GACZ;AACL,CAAC;;;ACjdM,IAAM,QAAA,GAAW,kBAAkB,aAAa;AAKhD,IAAM,GAAA,GAAM,SAAS,QAAA","file":"index.js","sourcesContent":["/**\n * Core Package Environment Variable Schema\n *\n * Centralized schema definition for all environment variables used in @spfn/core.\n * This provides type safety, validation, and documentation for environment configuration.\n *\n * @module config/schema\n */\n\nimport {\n defineEnvSchema,\n envEnum,\n envNumber,\n envBoolean,\n envUrl,\n envString,\n parsePostgresUrl,\n parseRedisUrl,\n} from '@spfn/core/env';\n\n/**\n * Core package environment variable schema\n *\n * Defines all environment variables with:\n * - Type information\n * - Default values\n * - Validation rules\n * - Documentation\n *\n * @example\n * ```typescript\n * import { coreEnvSchema } from '@spfn/core/config';\n *\n * // Access schema information\n * console.log(coreEnvSchema.DB_POOL_MAX.description);\n * console.log(coreEnvSchema.DB_POOL_MAX.default);\n * ```\n */\nexport const coreEnvSchema = defineEnvSchema({\n // ========================================================================\n // Core Environment\n // ========================================================================\n\n NODE_ENV: envEnum(['local', 'development', 'staging', 'production', 'test'] as const, {\n description: 'Node.js runtime environment',\n default: 'local',\n nextjs: true,\n }),\n\n // ========================================================================\n // Database - Connection\n // ========================================================================\n\n DATABASE_URL: envString({\n description: 'Primary database connection URL',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@localhost:5432/dbname'],\n }),\n\n DATABASE_WRITE_URL: envString({\n description: 'Write database URL (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@master:5432/dbname'],\n }),\n\n DATABASE_READ_URL: envString({\n description: 'Read database URL (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@replica:5432/dbname'],\n }),\n\n // ========================================================================\n // Database - Connection Pool\n // ========================================================================\n\n DB_POOL_MAX: envNumber({\n description: 'Maximum number of database connections in pool',\n default: 10,\n examples: [10, 20, 50],\n }),\n\n DB_POOL_READ_MAX: envNumber({\n description: 'Maximum connections for the read-replica pool. Defaults to DB_POOL_MAX. Set lower so write.max + read.max stays under the server max_connections (each process otherwise opens up to 2 × DB_POOL_MAX when a replica is configured).',\n required: false,\n examples: [5, 10, 20],\n }),\n\n DB_POOL_IDLE_TIMEOUT: envNumber({\n description: 'Database connection idle timeout in seconds',\n default: 30,\n examples: [20, 30, 60],\n }),\n\n // ========================================================================\n // Database - Retry Configuration\n // ========================================================================\n\n DB_RETRY_MAX: envNumber({\n description: 'Maximum number of database connection retry attempts',\n default: 3,\n examples: [3, 5, 10],\n }),\n\n DB_RETRY_INITIAL_DELAY: envNumber({\n description: 'Initial delay between database retry attempts (milliseconds)',\n default: 100,\n examples: [50, 100, 200],\n }),\n\n DB_RETRY_MAX_DELAY: envNumber({\n description: 'Maximum delay cap for database retry attempts (milliseconds)',\n default: 10000,\n examples: [5000, 10000, 30000],\n }),\n\n DB_RETRY_FACTOR: envNumber({\n description: 'Exponential backoff factor for database retry delays',\n default: 2,\n examples: [2, 1.5, 3],\n }),\n\n // ========================================================================\n // Database - Health Check\n // ========================================================================\n\n DB_HEALTH_CHECK_ENABLED: envBoolean({\n description: 'Enable periodic database health checks',\n default: true,\n examples: [true, false],\n }),\n\n DB_HEALTH_CHECK_INTERVAL: envNumber({\n description: 'Database health check interval (milliseconds)',\n default: 60000,\n examples: [30000, 60000, 120000],\n }),\n\n DB_HEALTH_CHECK_RECONNECT: envBoolean({\n description: 'Reconnect to database on health check failure',\n default: true,\n examples: [true, false],\n }),\n\n DB_HEALTH_CHECK_MAX_RETRIES: envNumber({\n description: 'Maximum health check retry attempts before marking as failed',\n default: 3,\n examples: [3, 5, 10],\n }),\n\n DB_HEALTH_CHECK_RETRY_INTERVAL: envNumber({\n description: 'Interval between health check retry attempts (milliseconds)',\n default: 5000,\n examples: [5000, 10000, 15000],\n }),\n\n // ========================================================================\n // Database - Monitoring\n // ========================================================================\n\n DB_MONITORING_ENABLED: envBoolean({\n description: 'Enable database query performance monitoring',\n default: false,\n examples: [true, false],\n }),\n\n DB_MONITORING_SLOW_THRESHOLD: envNumber({\n description: 'Slow query threshold for monitoring (milliseconds)',\n default: 1000,\n examples: [500, 1000, 2000],\n }),\n\n DB_MONITORING_LOG_QUERIES: envBoolean({\n description: 'Log all database queries (not just slow queries)',\n default: false,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Database - Transaction\n // ========================================================================\n\n TRANSACTION_TIMEOUT: envNumber({\n description: 'Transaction timeout in milliseconds',\n default: 30000,\n examples: [10000, 30000, 60000],\n }),\n\n TRANSACTION_IDLE_TIMEOUT: envNumber({\n description: 'Max time (ms) a transaction may sit idle (no running query) before Postgres terminates it and reclaims the pooled connection. Guards against external I/O held inside a transaction starving the connection pool. 0 disables.',\n default: 30000,\n examples: [10000, 30000, 0],\n }),\n\n // ========================================================================\n // Jobs (pg-boss)\n // ========================================================================\n\n JOB_POLLING_INTERVAL_SECONDS: envNumber({\n description: 'How often each pg-boss worker polls the DB for new jobs (seconds). Lower = faster pickup, more idle SELECT load; higher = less DB chatter, slower pickup. Per-job override via job options.',\n default: 2,\n examples: [1, 2, 10],\n }),\n\n // ========================================================================\n // Database - Development\n // ========================================================================\n\n DB_DEBUG_TRACE: envBoolean({\n description: 'Enable detailed debug tracing for database operations',\n default: false,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Database - Migrations\n // ========================================================================\n\n SPFN_ALLOW_PENDING_MIGRATIONS: envBoolean({\n description: 'Start the server even when a function package or the project has migrations the database has not applied. Off by default: a server booted with pending migrations fails only at request time, as an opaque 500. The flag equivalent is `spfn dev --allow-pending-migrations`.',\n default: false,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Drizzle ORM\n // ========================================================================\n\n DRIZZLE_SCHEMA_PATH: envString({\n description: 'Path to Drizzle schema configuration',\n required: false,\n default: './src/server/entities/config.ts',\n examples: ['./src/db/schema.ts', './src/server/entities/config.ts'],\n }),\n\n DRIZZLE_OUT_DIR: envString({\n description: 'Output directory for Drizzle migrations',\n required: false,\n default: './drizzle',\n examples: ['./drizzle', './migrations'],\n }),\n\n // ========================================================================\n // Logger - Core\n // ========================================================================\n\n SPFN_LOG_LEVEL: envEnum(['debug', 'info', 'warn', 'error', 'fatal'] as const, {\n description: 'Minimum log level to output',\n default: 'info',\n }),\n\n // ========================================================================\n // Cache (Redis/Valkey)\n // ========================================================================\n\n CACHE_URL: envString({\n description: 'Single Redis/Valkey instance URL',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://localhost:6379', 'rediss://secure.cache.com:6380'],\n }),\n\n CACHE_WRITE_URL: envString({\n description: 'Master Redis/Valkey URL for writes (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://master:6379'],\n }),\n\n CACHE_READ_URL: envString({\n description: 'Replica Redis/Valkey URL for reads (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://replica:6379'],\n }),\n\n CACHE_SENTINEL_HOSTS: envString({\n description: 'Comma-separated Redis Sentinel hosts',\n required: false,\n examples: ['sentinel1:26379,sentinel2:26379'],\n }),\n\n CACHE_CLUSTER_NODES: envString({\n description: 'Comma-separated Redis Cluster nodes',\n required: false,\n examples: ['node1:6379,node2:6379,node3:6379'],\n }),\n\n CACHE_MASTER_NAME: envString({\n description: 'Redis Sentinel master name',\n required: false,\n examples: ['mymaster'],\n }),\n\n CACHE_PASSWORD: envString({\n description: 'Redis/Valkey authentication password',\n required: false,\n sensitive: true,\n examples: ['your-redis-password'],\n }),\n\n CACHE_TLS_REJECT_UNAUTHORIZED: envBoolean({\n description: 'Verify TLS certificates for secure Redis connections',\n default: true,\n examples: [true, false],\n }),\n\n CACHE_MAX_RETRIES_PER_REQUEST: envNumber({\n description: 'Max ioredis retries per command before it rejects (fail fast instead of hanging on a cache outage). ioredis default is 20.',\n default: 3,\n examples: [1, 3, 20],\n }),\n\n CACHE_ENABLE_OFFLINE_QUEUE: envBoolean({\n description: 'Queue commands while the cache is disconnected (true) vs reject immediately for strict fail-fast (false). Default true keeps resilience to brief blips.',\n default: true,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Database - Query limits\n // ========================================================================\n\n DB_MAX_ROWS: envNumber({\n description: 'Safety ceiling for rows returned by repository findMany (0 = unlimited). When >0, an unbounded query is capped and an explicit limit is clamped, guarding against accidentally loading a whole large table.',\n default: 0,\n examples: [0, 1000, 10000],\n }),\n\n // ========================================================================\n // Server - Core\n // ========================================================================\n\n // PORT and HOST are deliberately absent.\n //\n // They were declared here with defaults of 4000 and 'localhost', and a\n // schema default is indistinguishable from a value someone set — so\n // `env.PORT` was never undefined, and the resolution order had to consult\n // the environment last to avoid it winning every time. That is what made an\n // injected port unreachable. The server's address now resolves through\n // `@spfn/core/app-config`: SPFN_PORT, then spfn.config.js, then the\n // default, with the default existing in exactly one place.\n //\n // `PORT` is also Next.js's own variable, and two processes are started.\n\n // ========================================================================\n // Server - Timeout\n // ========================================================================\n\n SERVER_TIMEOUT: envNumber({\n description: 'Request timeout in milliseconds',\n default: 120000,\n examples: [60000, 120000, 300000],\n }),\n\n SERVER_KEEPALIVE_TIMEOUT: envNumber({\n description: 'Keep-alive timeout in milliseconds',\n default: 65000,\n examples: [30000, 65000, 120000],\n }),\n\n SERVER_HEADERS_TIMEOUT: envNumber({\n description: 'Headers timeout in milliseconds',\n default: 60000,\n examples: [30000, 60000, 120000],\n }),\n\n SHUTDOWN_TIMEOUT: envNumber({\n description: 'Graceful shutdown timeout in milliseconds (must be less than k8s terminationGracePeriodSeconds minus preStop sleep, with safety margin)',\n default: 280000,\n examples: [30000, 120000, 280000],\n }),\n\n // ========================================================================\n // Fetch (Node.js undici) - outbound HTTP request timeout\n // ========================================================================\n\n FETCH_CONNECT_TIMEOUT: envNumber({\n description: 'Fetch TCP connection timeout in milliseconds (time to establish socket connection to upstream server)',\n default: 10000,\n examples: [5000, 10000, 30000],\n }),\n\n FETCH_HEADERS_TIMEOUT: envNumber({\n description: 'Fetch headers timeout in milliseconds (time to receive response headers after request sent)',\n default: 300000,\n examples: [120000, 300000, 600000],\n }),\n\n FETCH_BODY_TIMEOUT: envNumber({\n description: 'Fetch body timeout in milliseconds (time between body data chunks from upstream server)',\n default: 300000,\n examples: [120000, 300000, 600000],\n }),\n\n // ========================================================================\n // Next.js Integration\n // ========================================================================\n\n SPFN_API_URL: envUrl({\n description: 'SPFN API URL (used by Next.js to call backend)',\n required: true,\n nextjs: true,\n examples: ['http://localhost:8790', 'https://api.your-app.com'],\n }),\n\n NEXT_PUBLIC_SPFN_API_URL: envUrl({\n description: 'SPFN API URL (used by Next.js to call backend)',\n required: true,\n nextjs: true,\n examples: ['http://localhost:8790', 'https://api.your-app.com'],\n }),\n\n SPFN_APP_URL: envUrl({\n description: 'Next.js application URL (used by SPFN server)',\n required: false,\n nextjs: true,\n examples: ['http://localhost:3790', 'https://your-app.com'],\n }),\n\n RPC_PROXY_TIMEOUT: envNumber({\n description: 'RPC proxy request timeout in milliseconds (AbortController timeout for proxied requests to backend, should be shorter than FETCH_HEADERS_TIMEOUT)',\n default: 120000,\n nextjs: true,\n examples: [60000, 120000, 280000],\n }),\n\n // ========================================================================\n // Proxy → Backend trust (HMAC signing)\n // ========================================================================\n\n SPFN_PROXY_SECRET: envString({\n description: 'Shared secret for signing proxy→backend requests (HMAC-SHA256). Read by BOTH processes — the Next.js proxy (to sign) and the SPFN backend (to verify) — so it belongs in .env.local (loaded by both; the backend reads it via loadEnv, Next.js reads it server-side without exposing it to the browser). Set the SAME value on both. Leave unset to disable proxy-guard signing.',\n required: false,\n sensitive: true,\n nextjs: true,\n examples: ['<32+ byte random hex>', 'v2:<32+ byte random hex>'],\n }),\n\n SPFN_PROXY_SECRET_PREVIOUS: envString({\n description: 'Previous (grace) proxy keys still accepted for verification during rotation — comma-separated <keyId>:<secret>. The proxy never signs with these; they only keep requests signed with the prior key verifying until a rollout settles. Backend-only (verification), so it belongs in .env.server, NOT exposed to the Next.js process.',\n required: false,\n sensitive: true,\n nextjs: false,\n examples: ['v1:<old secret>', 'v1:<old>,v0:<older>'],\n }),\n\n TRUSTED_PROXY_HOPS: envNumber({\n description: 'Number of trusted reverse proxies in front of the Next.js proxy (e.g. cloud LB + nginx = 2). Read by the proxy to extract the real client IP from the inbound X-Forwarded-For (counting from the right, which your own infra appends and a client cannot spoof) and forward it to the backend for rate limiting. Set it to your actual hop count; too low trusts a client-spoofable entry, too high collapses users behind a shared proxy IP.',\n default: 1,\n nextjs: true,\n examples: [1, 2, 3],\n }),\n\n // ========================================================================\n // Rate limiting (global default limiter)\n // ========================================================================\n\n RATE_LIMIT_MODE: envEnum(['off', 'on'] as const, {\n description: 'Global default rate limiter. \"off\": only routes tagged with rateLimitPolicy() are limited. \"on\": every named-middleware route gets the default limit too (opt out per route with .skip([\\'rateLimit\\'])). Health/SSE/WebSocket endpoints are always exempt. Overridden by defineServerConfig().rateLimit({ mode }).',\n default: 'off',\n }),\n\n RATE_LIMIT_DEFAULT_LIMIT: envNumber({\n description: 'Max requests per window for the global default limiter (RATE_LIMIT_MODE=on), counted per route and per client IP.',\n default: 100,\n examples: [60, 100, 300],\n }),\n\n RATE_LIMIT_DEFAULT_WINDOW_MS: envNumber({\n description: 'Window length in milliseconds for the global default limiter.',\n default: 60000,\n examples: [1000, 60000],\n }),\n\n RATE_LIMIT_FAIL_CLOSED: envBoolean({\n description: 'When the cache (Redis/Valkey) backing the limiter is unavailable, reject with 429 instead of counting in-process. Default false: the limiter falls back to per-process counters, so limits still apply — but the effective limit multiplies by the instance count, since each process counts alone. Set true only where a shared count is required and refusing traffic is preferable to a looser one.',\n default: false,\n }),\n\n // ========================================================================\n // Outbound request safety (SSRF)\n // ========================================================================\n\n SAFE_FETCH_BLOCK_PRIVATE_IPS: envBoolean({\n description: 'Default for safeFetch (@spfn/core/security): block outbound requests that resolve to private/reserved IP ranges, including the cloud metadata address. Keep true in production; set false only for trusted internal-network calls in development. Overridden by defineServerConfig().outboundFetch({ blockPrivateIps }).',\n default: true,\n }),\n});\n","/**\n * Core Package Configuration\n *\n * @example\n * ```typescript\n * import { registry } from '@spfn/core/config';\n *\n * const env = registry.validate();\n * console.log(env.DB_POOL_MAX);\n * ```\n *\n * @module config\n */\n\nimport { createEnvRegistry } from '@spfn/core/env';\nimport { coreEnvSchema } from './schema';\n\n/**\n * Core environment schema\n */\nexport { coreEnvSchema as envSchema } from './schema';\n\n/**\n * Environment registry\n *\n * @example\n * ```typescript\n * // Reset for testing\n * registry.reset();\n * ```\n */\nexport const registry = createEnvRegistry(coreEnvSchema);\n\n/**\n * Validated environment configuration\n */\nexport const env = registry.validate();\n"]}
1
+ {"version":3,"sources":["../../src/config/schema.ts","../../src/config/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Core Package Environment Variable Schema\n *\n * Centralized schema definition for all environment variables used in @spfn/core.\n * This provides type safety, validation, and documentation for environment configuration.\n *\n * @module config/schema\n */\n\nimport {\n defineEnvSchema,\n envEnum,\n envNumber,\n envBoolean,\n envUrl,\n envString,\n parsePostgresUrl,\n parseRedisUrl,\n} from '@spfn/core/env';\n\n/**\n * Core package environment variable schema\n *\n * Defines all environment variables with:\n * - Type information\n * - Default values\n * - Validation rules\n * - Documentation\n *\n * @example\n * ```typescript\n * import { coreEnvSchema } from '@spfn/core/config';\n *\n * // Access schema information\n * console.log(coreEnvSchema.DB_POOL_MAX.description);\n * console.log(coreEnvSchema.DB_POOL_MAX.default);\n * ```\n */\nexport const coreEnvSchema = defineEnvSchema({\n // ========================================================================\n // Core Environment\n // ========================================================================\n\n NODE_ENV: envEnum(['local', 'development', 'staging', 'production', 'test'] as const, {\n description: 'Node.js runtime environment',\n default: 'local',\n nextjs: true,\n }),\n\n // ========================================================================\n // Database - Connection\n // ========================================================================\n\n DATABASE_URL: envString({\n description: 'Primary database connection URL',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@localhost:5432/dbname'],\n }),\n\n DATABASE_WRITE_URL: envString({\n description: 'Write database URL (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@master:5432/dbname'],\n }),\n\n DATABASE_READ_URL: envString({\n description: 'Read database URL (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parsePostgresUrl,\n examples: ['postgresql://user:password@replica:5432/dbname'],\n }),\n\n // ========================================================================\n // Database - Connection Pool\n // ========================================================================\n\n DB_POOL_MAX: envNumber({\n description: 'Maximum number of database connections in pool',\n default: 10,\n examples: [10, 20, 50],\n }),\n\n DB_POOL_READ_MAX: envNumber({\n description: 'Maximum connections for the read-replica pool. Defaults to DB_POOL_MAX. Set lower so write.max + read.max stays under the server max_connections (each process otherwise opens up to 2 × DB_POOL_MAX when a replica is configured).',\n required: false,\n examples: [5, 10, 20],\n }),\n\n DB_POOL_IDLE_TIMEOUT: envNumber({\n description: 'Database connection idle timeout in seconds',\n default: 30,\n examples: [20, 30, 60],\n }),\n\n // ========================================================================\n // Database - Retry Configuration\n // ========================================================================\n\n DB_RETRY_MAX: envNumber({\n description: 'Maximum number of database connection retry attempts',\n default: 3,\n examples: [3, 5, 10],\n }),\n\n DB_RETRY_INITIAL_DELAY: envNumber({\n description: 'Initial delay between database retry attempts (milliseconds)',\n default: 100,\n examples: [50, 100, 200],\n }),\n\n DB_RETRY_MAX_DELAY: envNumber({\n description: 'Maximum delay cap for database retry attempts (milliseconds)',\n default: 10000,\n examples: [5000, 10000, 30000],\n }),\n\n DB_RETRY_FACTOR: envNumber({\n description: 'Exponential backoff factor for database retry delays',\n default: 2,\n examples: [2, 1.5, 3],\n }),\n\n // ========================================================================\n // Database - Health Check\n // ========================================================================\n\n DB_HEALTH_CHECK_ENABLED: envBoolean({\n description: 'Enable periodic database health checks',\n default: true,\n examples: [true, false],\n }),\n\n DB_HEALTH_CHECK_INTERVAL: envNumber({\n description: 'Database health check interval (milliseconds)',\n default: 60000,\n examples: [30000, 60000, 120000],\n }),\n\n DB_HEALTH_CHECK_RECONNECT: envBoolean({\n description: 'Reconnect to database on health check failure',\n default: true,\n examples: [true, false],\n }),\n\n DB_HEALTH_CHECK_MAX_RETRIES: envNumber({\n description: 'Maximum health check retry attempts before marking as failed',\n default: 3,\n examples: [3, 5, 10],\n }),\n\n DB_HEALTH_CHECK_RETRY_INTERVAL: envNumber({\n description: 'Interval between health check retry attempts (milliseconds)',\n default: 5000,\n examples: [5000, 10000, 15000],\n }),\n\n // ========================================================================\n // Database - Monitoring\n // ========================================================================\n\n DB_MONITORING_ENABLED: envBoolean({\n description: 'Enable database query performance monitoring',\n default: false,\n examples: [true, false],\n }),\n\n DB_MONITORING_SLOW_THRESHOLD: envNumber({\n description: 'Slow query threshold for monitoring (milliseconds)',\n default: 1000,\n examples: [500, 1000, 2000],\n }),\n\n DB_MONITORING_LOG_QUERIES: envBoolean({\n description: 'Log all database queries (not just slow queries)',\n default: false,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Database - Transaction\n // ========================================================================\n\n TRANSACTION_TIMEOUT: envNumber({\n description: 'Transaction timeout in milliseconds',\n default: 30000,\n examples: [10000, 30000, 60000],\n }),\n\n TRANSACTION_IDLE_TIMEOUT: envNumber({\n description: 'Max time (ms) a transaction may sit idle (no running query) before Postgres terminates it and reclaims the pooled connection. Guards against external I/O held inside a transaction starving the connection pool. 0 disables.',\n default: 30000,\n examples: [10000, 30000, 0],\n }),\n\n // ========================================================================\n // Jobs (pg-boss)\n // ========================================================================\n\n JOB_POLLING_INTERVAL_SECONDS: envNumber({\n description: 'How often each pg-boss worker polls the DB for new jobs (seconds). Lower = faster pickup, more idle SELECT load; higher = less DB chatter, slower pickup. Per-job override via job options.',\n default: 2,\n examples: [1, 2, 10],\n }),\n\n // ========================================================================\n // Database - Development\n // ========================================================================\n\n DB_DEBUG_TRACE: envBoolean({\n description: 'Enable detailed debug tracing for database operations',\n default: false,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Database - Migrations\n // ========================================================================\n\n SPFN_ALLOW_PENDING_MIGRATIONS: envBoolean({\n description: 'Start the server even when a function package or the project has migrations the database has not applied. Off by default: a server booted with pending migrations fails only at request time, as an opaque 500. The flag equivalent is `spfn dev --allow-pending-migrations`.',\n default: false,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Drizzle ORM\n // ========================================================================\n\n DRIZZLE_SCHEMA_PATH: envString({\n description: 'Entity registry file spfn db push/generate/studio load when src/server/entities/ holds no entity file, or when the registry exports a table no file there defines',\n required: false,\n default: './src/server/entities/config.ts',\n examples: ['./src/db/schema.ts', './src/server/entities/config.ts'],\n }),\n\n DRIZZLE_OUT_DIR: envString({\n description: 'Output directory for Drizzle migrations',\n required: false,\n default: './drizzle',\n examples: ['./drizzle', './migrations'],\n }),\n\n // ========================================================================\n // Logger - Core\n // ========================================================================\n\n SPFN_LOG_LEVEL: envEnum(['debug', 'info', 'warn', 'error', 'fatal'] as const, {\n description: 'Minimum log level to output',\n default: 'info',\n }),\n\n // ========================================================================\n // Cache (Redis/Valkey)\n // ========================================================================\n\n CACHE_URL: envString({\n description: 'Single Redis/Valkey instance URL',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://localhost:6379', 'rediss://secure.cache.com:6380'],\n }),\n\n CACHE_WRITE_URL: envString({\n description: 'Master Redis/Valkey URL for writes (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://master:6379'],\n }),\n\n CACHE_READ_URL: envString({\n description: 'Replica Redis/Valkey URL for reads (master-replica pattern)',\n required: false,\n sensitive: true,\n validator: parseRedisUrl,\n examples: ['redis://replica:6379'],\n }),\n\n CACHE_SENTINEL_HOSTS: envString({\n description: 'Comma-separated Redis Sentinel hosts',\n required: false,\n examples: ['sentinel1:26379,sentinel2:26379'],\n }),\n\n CACHE_CLUSTER_NODES: envString({\n description: 'Comma-separated Redis Cluster nodes',\n required: false,\n examples: ['node1:6379,node2:6379,node3:6379'],\n }),\n\n CACHE_MASTER_NAME: envString({\n description: 'Redis Sentinel master name',\n required: false,\n examples: ['mymaster'],\n }),\n\n CACHE_PASSWORD: envString({\n description: 'Redis/Valkey authentication password',\n required: false,\n sensitive: true,\n examples: ['your-redis-password'],\n }),\n\n CACHE_TLS_REJECT_UNAUTHORIZED: envBoolean({\n description: 'Verify TLS certificates for secure Redis connections',\n default: true,\n examples: [true, false],\n }),\n\n CACHE_MAX_RETRIES_PER_REQUEST: envNumber({\n description: 'Max ioredis retries per command before it rejects (fail fast instead of hanging on a cache outage). ioredis default is 20.',\n default: 3,\n examples: [1, 3, 20],\n }),\n\n CACHE_ENABLE_OFFLINE_QUEUE: envBoolean({\n description: 'Queue commands while the cache is disconnected (true) vs reject immediately for strict fail-fast (false). Default true keeps resilience to brief blips.',\n default: true,\n examples: [true, false],\n }),\n\n // ========================================================================\n // Database - Query limits\n // ========================================================================\n\n DB_MAX_ROWS: envNumber({\n description: 'Safety ceiling for rows returned by repository findMany (0 = unlimited). When >0, an unbounded query is capped and an explicit limit is clamped, guarding against accidentally loading a whole large table.',\n default: 0,\n examples: [0, 1000, 10000],\n }),\n\n // ========================================================================\n // Server - Core\n // ========================================================================\n\n // PORT and HOST are deliberately absent.\n //\n // They were declared here with defaults of 4000 and 'localhost', and a\n // schema default is indistinguishable from a value someone set — so\n // `env.PORT` was never undefined, and the resolution order had to consult\n // the environment last to avoid it winning every time. That is what made an\n // injected port unreachable. The server's address now resolves through\n // `@spfn/core/app-config`: SPFN_PORT, then spfn.config.js, then the\n // default, with the default existing in exactly one place.\n //\n // `PORT` is also Next.js's own variable, and two processes are started.\n\n // ========================================================================\n // Server - Timeout\n // ========================================================================\n\n SERVER_TIMEOUT: envNumber({\n description: 'Request timeout in milliseconds',\n default: 120000,\n examples: [60000, 120000, 300000],\n }),\n\n SERVER_KEEPALIVE_TIMEOUT: envNumber({\n description: 'Keep-alive timeout in milliseconds',\n default: 65000,\n examples: [30000, 65000, 120000],\n }),\n\n SERVER_HEADERS_TIMEOUT: envNumber({\n description: 'Headers timeout in milliseconds',\n default: 60000,\n examples: [30000, 60000, 120000],\n }),\n\n SHUTDOWN_TIMEOUT: envNumber({\n description: 'Graceful shutdown timeout in milliseconds (must be less than k8s terminationGracePeriodSeconds minus preStop sleep, with safety margin)',\n default: 280000,\n examples: [30000, 120000, 280000],\n }),\n\n // ========================================================================\n // Fetch (Node.js undici) - outbound HTTP request timeout\n // ========================================================================\n\n FETCH_CONNECT_TIMEOUT: envNumber({\n description: 'Fetch TCP connection timeout in milliseconds (time to establish socket connection to upstream server)',\n default: 10000,\n examples: [5000, 10000, 30000],\n }),\n\n FETCH_HEADERS_TIMEOUT: envNumber({\n description: 'Fetch headers timeout in milliseconds (time to receive response headers after request sent)',\n default: 300000,\n examples: [120000, 300000, 600000],\n }),\n\n FETCH_BODY_TIMEOUT: envNumber({\n description: 'Fetch body timeout in milliseconds (time between body data chunks from upstream server)',\n default: 300000,\n examples: [120000, 300000, 600000],\n }),\n\n // ========================================================================\n // Next.js Integration\n // ========================================================================\n\n SPFN_API_URL: envUrl({\n description: 'SPFN API URL (used by Next.js to call backend)',\n required: true,\n nextjs: true,\n examples: ['http://localhost:8790', 'https://api.your-app.com'],\n }),\n\n NEXT_PUBLIC_SPFN_API_URL: envUrl({\n description: 'SPFN API URL (used by Next.js to call backend)',\n required: true,\n nextjs: true,\n examples: ['http://localhost:8790', 'https://api.your-app.com'],\n }),\n\n SPFN_APP_URL: envUrl({\n description: 'Next.js application URL (used by SPFN server)',\n required: false,\n nextjs: true,\n examples: ['http://localhost:3790', 'https://your-app.com'],\n }),\n\n RPC_PROXY_TIMEOUT: envNumber({\n description: 'RPC proxy request timeout in milliseconds (AbortController timeout for proxied requests to backend, should be shorter than FETCH_HEADERS_TIMEOUT)',\n default: 120000,\n nextjs: true,\n examples: [60000, 120000, 280000],\n }),\n\n // ========================================================================\n // Proxy → Backend trust (HMAC signing)\n // ========================================================================\n\n SPFN_PROXY_SECRET: envString({\n description: 'Shared secret for signing proxy→backend requests (HMAC-SHA256). Read by BOTH processes — the Next.js proxy (to sign) and the SPFN backend (to verify) — so it belongs in .env.local (loaded by both; the backend reads it via loadEnv, Next.js reads it server-side without exposing it to the browser). Set the SAME value on both. Leave unset to disable proxy-guard signing.',\n required: false,\n sensitive: true,\n nextjs: true,\n examples: ['<32+ byte random hex>', 'v2:<32+ byte random hex>'],\n }),\n\n SPFN_PROXY_SECRET_PREVIOUS: envString({\n description: 'Previous (grace) proxy keys still accepted for verification during rotation — comma-separated <keyId>:<secret>. The proxy never signs with these; they only keep requests signed with the prior key verifying until a rollout settles. Backend-only (verification), so it belongs in .env.server, NOT exposed to the Next.js process.',\n required: false,\n sensitive: true,\n nextjs: false,\n examples: ['v1:<old secret>', 'v1:<old>,v0:<older>'],\n }),\n\n TRUSTED_PROXY_HOPS: envNumber({\n description: 'Number of trusted reverse proxies in front of the Next.js proxy (e.g. cloud LB + nginx = 2). Read by the proxy to extract the real client IP from the inbound X-Forwarded-For (counting from the right, which your own infra appends and a client cannot spoof) and forward it to the backend for rate limiting. Set it to your actual hop count; too low trusts a client-spoofable entry, too high collapses users behind a shared proxy IP.',\n default: 1,\n nextjs: true,\n examples: [1, 2, 3],\n }),\n\n // ========================================================================\n // Rate limiting (global default limiter)\n // ========================================================================\n\n RATE_LIMIT_MODE: envEnum(['off', 'on'] as const, {\n description: 'Global default rate limiter. \"off\": only routes tagged with rateLimitPolicy() are limited. \"on\": every named-middleware route gets the default limit too (opt out per route with .skip([\\'rateLimit\\'])). Health/SSE/WebSocket endpoints are always exempt. Overridden by defineServerConfig().rateLimit({ mode }).',\n default: 'off',\n }),\n\n RATE_LIMIT_DEFAULT_LIMIT: envNumber({\n description: 'Max requests per window for the global default limiter (RATE_LIMIT_MODE=on), counted per route and per client IP.',\n default: 100,\n examples: [60, 100, 300],\n }),\n\n RATE_LIMIT_DEFAULT_WINDOW_MS: envNumber({\n description: 'Window length in milliseconds for the global default limiter.',\n default: 60000,\n examples: [1000, 60000],\n }),\n\n RATE_LIMIT_FAIL_CLOSED: envBoolean({\n description: 'When the cache (Redis/Valkey) backing the limiter is unavailable, reject with 429 instead of counting in-process. Default false: the limiter falls back to per-process counters, so limits still apply — but the effective limit multiplies by the instance count, since each process counts alone. Set true only where a shared count is required and refusing traffic is preferable to a looser one.',\n default: false,\n }),\n\n // ========================================================================\n // Outbound request safety (SSRF)\n // ========================================================================\n\n SAFE_FETCH_BLOCK_PRIVATE_IPS: envBoolean({\n description: 'Default for safeFetch (@spfn/core/security): block outbound requests that resolve to private/reserved IP ranges, including the cloud metadata address. Keep true in production; set false only for trusted internal-network calls in development. Overridden by defineServerConfig().outboundFetch({ blockPrivateIps }).',\n default: true,\n }),\n});\n","/**\n * Core Package Configuration\n *\n * @example\n * ```typescript\n * import { registry } from '@spfn/core/config';\n *\n * const env = registry.validate();\n * console.log(env.DB_POOL_MAX);\n * ```\n *\n * @module config\n */\n\nimport { createEnvRegistry } from '@spfn/core/env';\nimport { coreEnvSchema } from './schema';\n\n/**\n * Core environment schema\n */\nexport { coreEnvSchema as envSchema } from './schema';\n\n/**\n * Environment registry\n *\n * @example\n * ```typescript\n * // Reset for testing\n * registry.reset();\n * ```\n */\nexport const registry = createEnvRegistry(coreEnvSchema);\n\n/**\n * Validated environment configuration\n */\nexport const env = registry.validate();\n"]}
@@ -484,7 +484,7 @@ declare function checkConnection(client: Sql): Promise<boolean>;
484
484
  interface DrizzleConfigOptions {
485
485
  /** Database connection URL (defaults to process.env.DATABASE_URL) */
486
486
  databaseUrl?: string;
487
- /** Schema files glob pattern or array of patterns (defaults to './src/server/entities/\*\*\/*.ts') */
487
+ /** Schema entry file(s), directories or glob pattern(s). Default: './src/server/entities/\*\*\/*.ts', or the registry `DRIZZLE_SCHEMA_PATH` (./src/server/entities/config.ts) when that scan finds no entity file; `schemaRegistry` reports the registry when both exist */
488
488
  schema?: string | string[];
489
489
  /** Migration output directory (defaults to './src/server/drizzle') */
490
490
  out?: string;
@@ -500,8 +500,6 @@ interface DrizzleConfigOptions {
500
500
  expandGlobs?: boolean;
501
501
  /** PostgreSQL schema filter for push/introspect commands */
502
502
  schemaFilter?: string[];
503
- /** Auto-detect PostgreSQL schemas from entity files (requires expandGlobs: true) */
504
- autoDetectSchemas?: boolean;
505
503
  /** Migration prefix strategy (default: 'timestamp') */
506
504
  migrationPrefix?: 'index' | 'timestamp' | 'unix' | 'none';
507
505
  }
@@ -530,6 +528,8 @@ declare function detectDialect(url: string): 'postgresql' | 'mysql' | 'sqlite';
530
528
  */
531
529
  declare function getDrizzleConfig(options?: DrizzleConfigOptions): {
532
530
  schema: string | string[];
531
+ schemaSource: string;
532
+ schemaRegistry: string | undefined;
533
533
  out: string;
534
534
  dialect: "postgresql" | "mysql" | "sqlite";
535
535
  dbCredentials: {
@@ -541,6 +541,8 @@ declare function getDrizzleConfig(options?: DrizzleConfigOptions): {
541
541
  schemaFilter?: undefined;
542
542
  } | {
543
543
  schema: string | string[];
544
+ schemaSource: string;
545
+ schemaRegistry: string | undefined;
544
546
  out: string;
545
547
  dialect: "postgresql" | "mysql" | "sqlite";
546
548
  dbCredentials: {
@@ -552,10 +554,29 @@ declare function getDrizzleConfig(options?: DrizzleConfigOptions): {
552
554
  };
553
555
  };
554
556
  /**
555
- * Generate drizzle.config.ts file content
557
+ * The schema fields a rendered drizzle.config.ts needs
558
+ */
559
+ interface RenderableDrizzleConfig {
560
+ schema: string | string[];
561
+ out: string;
562
+ dialect: string;
563
+ dbCredentials: Record<string, unknown>;
564
+ schemaFilter?: string[];
565
+ migrations?: Record<string, unknown>;
566
+ }
567
+ /**
568
+ * Render an already-resolved config as drizzle.config.ts source, schema
569
+ * paths made absolute for Drizzle Studio
556
570
  *
557
- * @param options - Configuration options
558
- * @returns File content as string
571
+ * @param config - Result of getDrizzleConfig, possibly with the schema replaced
572
+ * @param cwd - Base for relative schema paths (default: process.cwd())
573
+ */
574
+ declare function renderDrizzleConfig(config: RenderableDrizzleConfig, cwd?: string): string;
575
+ /**
576
+ * Generate drizzle.config.ts source from options: resolve the config, then render it
577
+ *
578
+ * @param options - Configuration options (see getDrizzleConfig)
579
+ * @returns drizzle.config.ts source
559
580
  */
560
581
  declare function generateDrizzleConfigFile(options?: DrizzleConfigOptions): string;
561
582
 
@@ -2152,4 +2173,4 @@ declare abstract class BaseRepository<TRelations extends AnyRelations = EmptyRel
2152
2173
  protected _count<T extends PgTable>(table: T, where?: Record<string, any> | SQL | undefined): Promise<number>;
2153
2174
  }
2154
2175
 
2155
- export { type AfterCommitCallback, type AfterRollbackCallback, BaseRepository, type BeforeCommitCallback, type DatabaseClients, type DatabaseInitOptions, type DatabaseOptions, type DatabaseProvider, type DatabaseTransaction, type DefaultDatabase, type DrizzleConfigOptions, type DrizzleDatabase, type FunctionMigrationEntry, type FunctionMigrationInfo, type MigrationStatus, type MigrationStatusDb, type MigrationTargetStatus, type NestedFrameGate, PROJECT_MIGRATIONS_TABLE, PROJECT_TARGET_NAME, type PoolConfig, RUN_MIGRATIONS_HINT, type RepositoryDatabase, RepositoryError, type RetryConfig, type RunInTransactionOptions, type TransactionContext, type TransactionDB, Transactional, type TransactionalOptions, auditFields, checkConnection, closeDatabase, collectMigrationStatus, count, countPendingMigrations, create, createDatabaseConnection, createDatabaseFromEnv, createMany, createSchema, deleteMany, deleteOne, detectDialect, discoverFunctionMigrations, enumText, filterPendingEntries, findMany, findOne, forceReconnectDatabase, foreignKey, formatPendingMigrations, fromPostgresError, functionMigrationsTable, generateDrizzleConfigFile, getDatabase, getDatabaseInfo, getDrizzleConfig, getSchemaInfo, getTransaction, getTransactionContext, hasMigrationTargets, id, initDatabase, isConnectionLevelError, migrationTargets, onAfterCommit, onAfterRollback, onBeforeCommit, optionalForeignKey, packageNameToSchema, pendingMigrationTargets, pendingMigrationsSummary, projectMigrationsDir, publishingFields, readMigrationEntries, reportDatabaseError, resetConnectionErrorCounter, runInTransaction, runWithTransaction, setDatabase, setDatabaseProvider, softDelete, timestamps, typedJsonb, updateMany, updateOne, upsert, utcTimestamp, uuid, verificationTimestamp };
2176
+ export { type AfterCommitCallback, type AfterRollbackCallback, BaseRepository, type BeforeCommitCallback, type DatabaseClients, type DatabaseInitOptions, type DatabaseOptions, type DatabaseProvider, type DatabaseTransaction, type DefaultDatabase, type DrizzleConfigOptions, type DrizzleDatabase, type FunctionMigrationEntry, type FunctionMigrationInfo, type MigrationStatus, type MigrationStatusDb, type MigrationTargetStatus, type NestedFrameGate, PROJECT_MIGRATIONS_TABLE, PROJECT_TARGET_NAME, type PoolConfig, RUN_MIGRATIONS_HINT, type RenderableDrizzleConfig, type RepositoryDatabase, RepositoryError, type RetryConfig, type RunInTransactionOptions, type TransactionContext, type TransactionDB, Transactional, type TransactionalOptions, auditFields, checkConnection, closeDatabase, collectMigrationStatus, count, countPendingMigrations, create, createDatabaseConnection, createDatabaseFromEnv, createMany, createSchema, deleteMany, deleteOne, detectDialect, discoverFunctionMigrations, enumText, filterPendingEntries, findMany, findOne, forceReconnectDatabase, foreignKey, formatPendingMigrations, fromPostgresError, functionMigrationsTable, generateDrizzleConfigFile, getDatabase, getDatabaseInfo, getDrizzleConfig, getSchemaInfo, getTransaction, getTransactionContext, hasMigrationTargets, id, initDatabase, isConnectionLevelError, migrationTargets, onAfterCommit, onAfterRollback, onBeforeCommit, optionalForeignKey, packageNameToSchema, pendingMigrationTargets, pendingMigrationsSummary, projectMigrationsDir, publishingFields, readMigrationEntries, renderDrizzleConfig, reportDatabaseError, resetConnectionErrorCounter, runInTransaction, runWithTransaction, setDatabase, setDatabaseProvider, softDelete, timestamps, typedJsonb, updateMany, updateOne, upsert, utcTimestamp, uuid, verificationTimestamp };
package/dist/db/index.js CHANGED
@@ -5,8 +5,9 @@ import net from 'net';
5
5
  import postgres from 'postgres';
6
6
  import { QueryError, ConnectionError, DeadlockError, TransactionError, ConstraintViolationError, DuplicateEntryError, SerializableError } from '@spfn/core/errors';
7
7
  import { parseNumber, parseBoolean } from '@spfn/core/env';
8
- import { existsSync, readdirSync, readFileSync, statSync } from 'fs';
9
- import { join, dirname, basename } from 'path';
8
+ import { existsSync, readdirSync, readFileSync, statSync, realpathSync, lstatSync } from 'fs';
9
+ import { join, relative } from 'path';
10
+ import mm from 'micromatch';
10
11
  import { sql, count as count$1, lt, gt, and, desc, asc, eq } from 'drizzle-orm';
11
12
  import { bigserial, timestamp, bigint, uuid as uuid$1, text, jsonb, pgSchema } from 'drizzle-orm/pg-core';
12
13
  import { createHash, randomUUID } from 'crypto';
@@ -1213,7 +1214,10 @@ var BARREL_FILE_PATTERNS = [
1213
1214
  "/config.js",
1214
1215
  "/config.mjs"
1215
1216
  ];
1216
- var SUPPORTED_EXTENSIONS = [".ts", ".js", ".mjs"];
1217
+ var DEFAULT_ENTITY_GLOB = "./src/server/entities/**/*.ts";
1218
+ var SUPPORTED_EXTENSIONS = [".ts", ".mts", ".cts", ".tsx", ".js", ".mjs", ".cjs", ".jsx"];
1219
+ var DECLARATION_PATTERN = /\.d\.[mc]?ts$/;
1220
+ var SKIPPED_DIRECTORIES = /^(\.|node_modules$)/;
1217
1221
  function isBarrelFile(filePath) {
1218
1222
  const posixPath = toPosixPath(filePath);
1219
1223
  return BARREL_FILE_PATTERNS.some((pattern) => posixPath.endsWith(pattern));
@@ -1223,90 +1227,100 @@ function isAbsolutePath(path) {
1223
1227
  return !!path.match(/^[A-Za-z]:[\/\\]/);
1224
1228
  }
1225
1229
  function hasSupportedExtension(filePath) {
1226
- if (filePath.endsWith(".d.ts")) return false;
1230
+ if (DECLARATION_PATTERN.test(filePath)) return false;
1227
1231
  return SUPPORTED_EXTENSIONS.some((ext) => filePath.endsWith(ext));
1228
1232
  }
1229
1233
  function filterBarrelFiles(files) {
1230
1234
  return files.filter((file) => !isBarrelFile(file));
1231
1235
  }
1232
- function scanDirectoryRecursive(dir, extension) {
1233
- const files = [];
1234
- if (!existsSync(dir)) return files;
1236
+ function safeStat(path) {
1235
1237
  try {
1236
- const entries = readdirSync(dir);
1237
- for (const entry of entries) {
1238
- const fullPath = join(dir, entry);
1239
- try {
1240
- const stat = statSync(fullPath);
1241
- if (stat.isDirectory()) {
1242
- files.push(...scanDirectoryRecursive(fullPath, extension));
1243
- } else if (stat.isFile()) {
1244
- if ((!extension || fullPath.endsWith(extension)) && hasSupportedExtension(fullPath)) {
1245
- files.push(fullPath);
1246
- }
1247
- }
1248
- } catch (error) {
1249
- }
1250
- }
1251
- } catch (error) {
1238
+ return statSync(path);
1239
+ } catch {
1240
+ return void 0;
1252
1241
  }
1253
- return files;
1254
1242
  }
1255
- function scanDirectorySingleLevel(dir, filePattern) {
1256
- const files = [];
1257
- if (!existsSync(dir)) return files;
1243
+ function safeReaddir(dir) {
1258
1244
  try {
1259
- const entries = readdirSync(dir);
1260
- for (const entry of entries) {
1261
- const fullPath = join(dir, entry);
1262
- try {
1263
- const stat = statSync(fullPath);
1264
- if (stat.isFile()) {
1265
- if ((filePattern === "*" || filePattern.startsWith("*.") && entry.endsWith(filePattern.slice(1))) && hasSupportedExtension(fullPath)) {
1266
- files.push(fullPath);
1267
- }
1268
- }
1269
- } catch (error) {
1270
- }
1271
- }
1272
- } catch (error) {
1245
+ return readdirSync(dir);
1246
+ } catch {
1247
+ return [];
1273
1248
  }
1274
- return files;
1275
1249
  }
1276
- function detectSchemasFromFiles(files) {
1277
- const schemas = /* @__PURE__ */ new Set(["public"]);
1278
- const pgSchemaPattern = /pgSchema\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
1279
- const createSchemaPattern = /createSchema\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
1280
- for (const filePath of files) {
1281
- try {
1282
- const content = readFileSync(filePath, "utf-8");
1283
- let match;
1284
- while ((match = pgSchemaPattern.exec(content)) !== null) {
1285
- schemas.add(match[1]);
1286
- }
1287
- while ((match = createSchemaPattern.exec(content)) !== null) {
1288
- const packageName = match[1];
1289
- const schemaName = packageName.replace(/@/g, "").replace(/\//g, "_").replace(/-/g, "_");
1290
- schemas.add(schemaName);
1291
- }
1292
- } catch {
1293
- }
1250
+ function walkDirectory(dir, maxDepth, ancestors = /* @__PURE__ */ new Set()) {
1251
+ const walk = { files: [], directories: [] };
1252
+ if (maxDepth < 1 || !safeStat(dir)?.isDirectory()) return walk;
1253
+ const realDir = realpathSafe(dir);
1254
+ if (ancestors.has(realDir)) return walk;
1255
+ const chain = new Set(ancestors).add(realDir);
1256
+ for (const entry of safeReaddir(dir)) {
1257
+ const path = join(dir, entry);
1258
+ const stat = safeStat(path);
1259
+ if (stat?.isFile() && hasSupportedExtension(path)) {
1260
+ walk.files.push(path);
1261
+ } else if (stat?.isDirectory() && !SKIPPED_DIRECTORIES.test(entry)) {
1262
+ walk.directories.push(path);
1263
+ const isLink = isSymlink(path);
1264
+ const child = walkDirectory(path, isLink ? Math.min(maxDepth - 1, 1) : maxDepth - 1, chain);
1265
+ for (const file of child.files) walk.files.push(file);
1266
+ for (const sub of child.directories) walk.directories.push(sub);
1267
+ }
1268
+ }
1269
+ return walk;
1270
+ }
1271
+ function isSymlink(path) {
1272
+ try {
1273
+ return lstatSync(path).isSymbolicLink();
1274
+ } catch {
1275
+ return false;
1294
1276
  }
1295
- return Array.from(schemas);
1296
1277
  }
1297
- function expandGlobPattern(pattern) {
1298
- if (!pattern.includes("*")) {
1299
- return existsSync(pattern) ? [pattern] : [];
1300
- }
1301
- if (pattern.includes("**")) {
1302
- const [baseDir, ...rest] = pattern.split("**");
1303
- const extension = rest.join("").replace(/[\/\\]\*\./g, "").trim();
1304
- const dir2 = baseDir.trim() || ".";
1305
- return scanDirectoryRecursive(dir2, extension || void 0);
1278
+ function realpathSafe(path) {
1279
+ try {
1280
+ return realpathSync(path);
1281
+ } catch {
1282
+ return path;
1283
+ }
1284
+ }
1285
+ function scanDirectory(dir, maxDepth) {
1286
+ return walkDirectory(dir, maxDepth).files;
1287
+ }
1288
+ function toRelativePattern(entry, cwd) {
1289
+ return toPosixPath(isAbsolutePath(entry) ? relative(cwd, entry) : entry).replace(/^\.\//, "").replace(/[()]/g, "\\$&");
1290
+ }
1291
+ function expandGlob(pattern, cwd) {
1292
+ const scan = mm.scan(pattern);
1293
+ const depth = scan.isGlobstar || scan.glob.includes("**") ? Infinity : scan.glob.split("/").length;
1294
+ const base = join(cwd, scan.base.replace(/\\([()])/g, "$1"));
1295
+ const matches = mm.matcher(pattern);
1296
+ const matched = (path) => matches(toPosixPath(relative(cwd, path)));
1297
+ const walk = walkDirectory(base, depth);
1298
+ return Array.from(/* @__PURE__ */ new Set([...walk.files.filter(matched), ...walk.directories.filter(matched).flatMap((dir) => scanDirectory(dir, 1))]));
1299
+ }
1300
+ function expandSchemaEntry(entry, cwd) {
1301
+ const pattern = toRelativePattern(entry, cwd);
1302
+ if (mm.scan(pattern).isGlob) {
1303
+ return expandGlob(pattern, cwd);
1304
+ }
1305
+ const path = isAbsolutePath(entry) ? entry : join(cwd, entry);
1306
+ const stat = safeStat(path);
1307
+ if (!stat) return [];
1308
+ return stat.isDirectory() ? scanDirectory(path, 1) : [path];
1309
+ }
1310
+ function selectDefaultSchema(cwd) {
1311
+ const registry = env.DRIZZLE_SCHEMA_PATH;
1312
+ const registryFile = isAbsolutePath(registry) ? registry : join(cwd, registry);
1313
+ const registryExists = safeStat(registryFile)?.isFile() === true;
1314
+ const scanned = filterBarrelFiles(expandSchemaEntry(DEFAULT_ENTITY_GLOB, cwd));
1315
+ if (scanned.length === 0 && registryExists) {
1316
+ return { schemas: [registry], source: `entity registry ${registry}` };
1306
1317
  }
1307
- const dir = dirname(pattern);
1308
- const filePattern = basename(pattern);
1309
- return scanDirectorySingleLevel(dir, filePattern);
1318
+ return {
1319
+ schemas: [DEFAULT_ENTITY_GLOB],
1320
+ source: `${DEFAULT_ENTITY_GLOB} scan`,
1321
+ files: scanned,
1322
+ registry: registryExists ? registry : void 0
1323
+ };
1310
1324
  }
1311
1325
  function discoverPackageSchemas(cwd) {
1312
1326
  const schemas = [];
@@ -1335,9 +1349,7 @@ function discoverPackageSchemas(cwd) {
1335
1349
  const packageSchemas = Array.isArray(pkgJson.spfn.schemas) ? pkgJson.spfn.schemas : [pkgJson.spfn.schemas];
1336
1350
  for (const schema of packageSchemas) {
1337
1351
  const absolutePath = join(pkgPath, schema);
1338
- const expandedFiles = expandGlobPattern(absolutePath);
1339
- const schemaFiles = filterBarrelFiles(expandedFiles);
1340
- schemas.push(...schemaFiles);
1352
+ schemas.push(...filterBarrelFiles(expandSchemaEntry(absolutePath, pkgPath)));
1341
1353
  }
1342
1354
  }
1343
1355
  } catch (error) {
@@ -1396,6 +1408,8 @@ function getDrizzleConfig(options = {}) {
1396
1408
  const schema2 = filteredSchemas.length === 1 ? filteredSchemas[0] : filteredSchemas;
1397
1409
  return {
1398
1410
  schema: schema2,
1411
+ schemaSource: `package ${options.packageFilter}`,
1412
+ schemaRegistry: void 0,
1399
1413
  out,
1400
1414
  dialect,
1401
1415
  dbCredentials: getDbCredentials(dialect, databaseUrl),
@@ -1404,32 +1418,18 @@ function getDrizzleConfig(options = {}) {
1404
1418
  }
1405
1419
  };
1406
1420
  }
1407
- const userSchema = options.schema ?? "./src/server/entities/**/*.ts";
1408
- const userSchemas = Array.isArray(userSchema) ? userSchema : [userSchema];
1409
- const packageSchemas = options.disablePackageDiscovery ? [] : discoverPackageSchemas(options.cwd ?? process.cwd());
1410
- let allSchemas = [...userSchemas, ...packageSchemas];
1411
1421
  const cwd = options.cwd ?? process.cwd();
1412
- let expandedFiles = [];
1413
- if (options.expandGlobs) {
1414
- for (const schema2 of allSchemas) {
1415
- const absoluteSchema = isAbsolutePath(schema2) ? schema2 : join(cwd, schema2);
1416
- const expanded = expandGlobPattern(absoluteSchema);
1417
- const filtered = filterBarrelFiles(expanded);
1418
- expandedFiles.push(...filtered);
1419
- }
1420
- allSchemas = expandedFiles;
1421
- }
1422
+ const selection = options.schema ? { schemas: Array.isArray(options.schema) ? options.schema : [options.schema], source: "schema option" } : selectDefaultSchema(cwd);
1423
+ const schemaSource = selection.source;
1424
+ const schemaRegistry = selection.registry;
1425
+ const packageSchemas = options.disablePackageDiscovery ? [] : discoverPackageSchemas(cwd);
1426
+ const allSchemas = options.expandGlobs ? [...selection.files ?? selection.schemas.flatMap((entry) => expandSchemaEntry(entry, cwd)), ...packageSchemas] : [...selection.schemas, ...packageSchemas];
1422
1427
  const schema = allSchemas.length === 1 ? allSchemas[0] : allSchemas;
1423
- let schemaFilter;
1424
- if (dialect === "postgresql") {
1425
- if (options.schemaFilter) {
1426
- schemaFilter = options.schemaFilter;
1427
- } else if (options.autoDetectSchemas && expandedFiles.length > 0) {
1428
- schemaFilter = detectSchemasFromFiles(expandedFiles);
1429
- }
1430
- }
1428
+ const schemaFilter = dialect === "postgresql" ? options.schemaFilter : void 0;
1431
1429
  return {
1432
1430
  schema,
1431
+ schemaSource,
1432
+ schemaRegistry,
1433
1433
  out,
1434
1434
  dialect,
1435
1435
  dbCredentials: getDbCredentials(dialect, databaseUrl),
@@ -1451,9 +1451,7 @@ function getDbCredentials(dialect, url) {
1451
1451
  throw new Error(`Unsupported dialect: ${dialect}`);
1452
1452
  }
1453
1453
  }
1454
- function generateDrizzleConfigFile(options = {}) {
1455
- const config = getDrizzleConfig(options);
1456
- const cwd = options.cwd ?? process.cwd();
1454
+ function renderDrizzleConfig(config, cwd = process.cwd()) {
1457
1455
  const normalizeSchemaPath = (schemaPath) => {
1458
1456
  if (isAbsolutePath(schemaPath)) {
1459
1457
  return schemaPath;
@@ -1477,6 +1475,9 @@ export default defineConfig({
1477
1475
  });
1478
1476
  `;
1479
1477
  }
1478
+ function generateDrizzleConfigFile(options = {}) {
1479
+ return renderDrizzleConfig(getDrizzleConfig(options), options.cwd ?? process.cwd());
1480
+ }
1480
1481
  function id() {
1481
1482
  return bigserial("id", { mode: "number" }).primaryKey();
1482
1483
  }
@@ -2666,6 +2667,6 @@ var BaseRepository = class {
2666
2667
  }
2667
2668
  };
2668
2669
 
2669
- export { BaseRepository, PROJECT_MIGRATIONS_TABLE, PROJECT_TARGET_NAME, RUN_MIGRATIONS_HINT, RepositoryError, Transactional, auditFields, checkConnection, closeDatabase, collectMigrationStatus, count, countPendingMigrations, create, createDatabaseConnection, createDatabaseFromEnv, createMany, createSchema, deleteMany, deleteOne, detectDialect, discoverFunctionMigrations, enumText, filterPendingEntries, findMany, findOne, forceReconnectDatabase, foreignKey, formatPendingMigrations, fromPostgresError, functionMigrationsTable, generateDrizzleConfigFile, getDatabase, getDatabaseInfo, getDrizzleConfig, getSchemaInfo, getTransaction, getTransactionContext, hasMigrationTargets, id, initDatabase, isConnectionLevelError, migrationTargets, onAfterCommit, onAfterRollback, onBeforeCommit, optionalForeignKey, packageNameToSchema, pendingMigrationTargets, pendingMigrationsSummary, projectMigrationsDir, publishingFields, readMigrationEntries, reportDatabaseError, resetConnectionErrorCounter, runInTransaction, runWithTransaction, setDatabase, setDatabaseProvider, softDelete, timestamps, typedJsonb, updateMany, updateOne, upsert, utcTimestamp, uuid, verificationTimestamp };
2670
+ export { BaseRepository, PROJECT_MIGRATIONS_TABLE, PROJECT_TARGET_NAME, RUN_MIGRATIONS_HINT, RepositoryError, Transactional, auditFields, checkConnection, closeDatabase, collectMigrationStatus, count, countPendingMigrations, create, createDatabaseConnection, createDatabaseFromEnv, createMany, createSchema, deleteMany, deleteOne, detectDialect, discoverFunctionMigrations, enumText, filterPendingEntries, findMany, findOne, forceReconnectDatabase, foreignKey, formatPendingMigrations, fromPostgresError, functionMigrationsTable, generateDrizzleConfigFile, getDatabase, getDatabaseInfo, getDrizzleConfig, getSchemaInfo, getTransaction, getTransactionContext, hasMigrationTargets, id, initDatabase, isConnectionLevelError, migrationTargets, onAfterCommit, onAfterRollback, onBeforeCommit, optionalForeignKey, packageNameToSchema, pendingMigrationTargets, pendingMigrationsSummary, projectMigrationsDir, publishingFields, readMigrationEntries, renderDrizzleConfig, reportDatabaseError, resetConnectionErrorCounter, runInTransaction, runWithTransaction, setDatabase, setDatabaseProvider, softDelete, timestamps, typedJsonb, updateMany, updateOne, upsert, utcTimestamp, uuid, verificationTimestamp };
2670
2671
  //# sourceMappingURL=index.js.map
2671
2672
  //# sourceMappingURL=index.js.map