@nubase/backend 0.1.35 → 0.1.37

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/dist/index.d.mts CHANGED
@@ -1,7 +1,8 @@
1
1
  import * as hono from 'hono';
2
2
  import { Context, Hono } from 'hono';
3
- import { RequestSchema, InferRequestParams, InferRequestBody, InferResponseBody } from '@nubase/core';
3
+ import { RequestSchema, InferRequestParams, InferRequestBody, InferResponseBody, BaseSchema, ObjectSchema } from '@nubase/core';
4
4
  import { ContentfulStatusCode } from 'hono/utils/http-status';
5
+ import { StringReference, ExpressionBuilder, ExpressionWrapper, SqlBool } from 'kysely';
5
6
 
6
7
  /**
7
8
  * Represents an authenticated user on the backend.
@@ -318,6 +319,47 @@ declare function getUser<TUser extends BackendUser = BackendUser>(c: Context): T
318
319
  */
319
320
  declare function getAuthController<TUser extends BackendUser = BackendUser>(c: Context): BackendAuthController<TUser>;
320
321
 
322
+ /**
323
+ * Configuration for the auth section of {@link NubaseBackendConfig}.
324
+ * Consumed by `BackendAuthController` implementations to derive JWT and
325
+ * cookie behaviour.
326
+ */
327
+ interface NubaseBackendAuthConfig {
328
+ /** Secret used to sign and verify session JWTs. */
329
+ jwtSecret: string;
330
+ /**
331
+ * Lifetime of the session, in seconds. Applied to both the JWT `exp`
332
+ * claim and the session cookie's `Max-Age`.
333
+ */
334
+ sessionMaxAgeSeconds: number;
335
+ /** Name of the HttpOnly cookie that carries the session JWT. */
336
+ cookieName: string;
337
+ }
338
+ /**
339
+ * Backend application configuration for a Nubase app.
340
+ *
341
+ * An app declares this in `src/backend/backend-config.ts` and the wiring
342
+ * code (controller singletons, middleware factories) reads it. The type
343
+ * lives in the framework so all Nubase apps share one shape; apps may
344
+ * extend it with their own sections by intersecting with this type.
345
+ *
346
+ * @example
347
+ * ```ts
348
+ * import type { NubaseBackendConfig } from "@nubase/backend";
349
+ *
350
+ * export const config: NubaseBackendConfig = {
351
+ * auth: {
352
+ * jwtSecret: process.env.JWT_SECRET ?? "dev-secret",
353
+ * sessionMaxAgeSeconds: 60 * 60 * 24 * 30,
354
+ * cookieName: "nubase_auth",
355
+ * },
356
+ * };
357
+ * ```
358
+ */
359
+ interface NubaseBackendConfig {
360
+ auth: NubaseBackendAuthConfig;
361
+ }
362
+
321
363
  /**
322
364
  * Custom HTTP error class that allows handlers to throw errors with specific status codes.
323
365
  * Use this to return proper HTTP error responses instead of generic 500 errors.
@@ -329,7 +371,19 @@ declare function getAuthController<TUser extends BackendUser = BackendUser>(c: C
329
371
  */
330
372
  declare class HttpError extends Error {
331
373
  statusCode: ContentfulStatusCode;
332
- constructor(statusCode: ContentfulStatusCode, message: string);
374
+ /**
375
+ * Optional structured payload merged into the JSON response body under
376
+ * the `error` key. Use when the caller needs machine-readable details
377
+ * (error code, field, line/column, etc.) beyond the human message.
378
+ */
379
+ payload?: Record<string, unknown> | undefined;
380
+ constructor(statusCode: ContentfulStatusCode, message: string,
381
+ /**
382
+ * Optional structured payload merged into the JSON response body under
383
+ * the `error` key. Use when the caller needs machine-readable details
384
+ * (error code, field, line/column, etc.) beyond the human message.
385
+ */
386
+ payload?: Record<string, unknown> | undefined);
333
387
  }
334
388
  /**
335
389
  * URL Parameter Coercion System (Backend)
@@ -565,6 +619,194 @@ type EndpointSelector<TEndpoints extends Record<string, RequestSchema>, T extend
565
619
  */
566
620
  declare function createHandlerFactory<TEndpoints extends Record<string, RequestSchema>, TUser extends BackendUser = BackendUser>(config: HandlerFactoryConfig<TEndpoints>): <TSelector extends (endpoints: TEndpoints) => RequestSchema, TAuth extends AuthLevel = "none">(selector: TSelector, options: FactoryHandlerOptions<ReturnType<TSelector>, TAuth, TUser>) => HttpHandler;
567
621
 
622
+ type ShapeOf = Record<string, BaseSchema<any>>;
623
+ /**
624
+ * Runtime shape of an NQL field-to-column map. Keys are schema field names;
625
+ * values are Kysely column references (e.g. `"tickets.title"`).
626
+ *
627
+ * This type is intentionally loose at runtime. The strong typing is applied
628
+ * at binding-creation time via {@link createNqlBindings}, which constrains
629
+ * keys to the schema's shape and values to the Kysely `StringReference`
630
+ * union. Once built, the bindings object is carried through the NQL
631
+ * pipeline as a plain `Record<string, string>`.
632
+ */
633
+ type FieldBindings = Record<string, string>;
634
+ /**
635
+ * Curry a bindings factory for a specific Kysely DB type.
636
+ *
637
+ * The outer call pins `DB` (the generated Kysely database interface). The
638
+ * inner call takes the NQL schema and a map whose keys are checked against
639
+ * the schema's shape and whose values are checked against the Kysely
640
+ * `StringReference<DB, TB>` union — so a typo in either half is a compile
641
+ * error, and referencing a table you haven't joined in the current query
642
+ * is caught when the resulting bindings are passed to `compileNql`.
643
+ *
644
+ * @example
645
+ * ```ts
646
+ * import type { DB } from "./db-types";
647
+ * const ticketBindings = createNqlBindings<DB>()(ticketListSchema, {
648
+ * id: "tickets.id",
649
+ * title: "tickets.title",
650
+ * assigneeName: "users.displayName", // cross-table, only valid if joined
651
+ * });
652
+ * ```
653
+ */
654
+ declare function createNqlBindings<DB>(): <TShape extends ShapeOf, TB extends keyof DB>(schema: ObjectSchema<TShape>, mapping: Partial<Record<keyof TShape & string, StringReference<DB, TB>>>) => FieldBindings;
655
+
656
+ /**
657
+ * Source position tracking. `line` and `column` are 1-based to match
658
+ * how most editors report positions. `offset` is a 0-based character
659
+ * offset into the original source string.
660
+ */
661
+ interface SourceSpan {
662
+ line: number;
663
+ column: number;
664
+ offset: number;
665
+ length: number;
666
+ }
667
+ type NqlErrorCode = "TOKENIZE" | "PARSE" | "VALIDATE";
668
+ interface NqlError {
669
+ code: NqlErrorCode;
670
+ message: string;
671
+ line: number;
672
+ column: number;
673
+ length?: number;
674
+ }
675
+ type Result<T, E> = {
676
+ ok: true;
677
+ value: T;
678
+ } | {
679
+ ok: false;
680
+ error: E;
681
+ };
682
+ type ComparisonOp = "=" | "!=" | "<" | "<=" | ">" | ">=" | "CONTAINS" | "STARTS_WITH" | "ENDS_WITH";
683
+ interface IdentifierRef {
684
+ raw: string;
685
+ span: SourceSpan;
686
+ }
687
+ interface StringLit {
688
+ kind: "string";
689
+ value: string;
690
+ span: SourceSpan;
691
+ }
692
+ interface NumberLit {
693
+ kind: "number";
694
+ value: number;
695
+ span: SourceSpan;
696
+ }
697
+ interface BoolLit {
698
+ kind: "boolean";
699
+ value: boolean;
700
+ span: SourceSpan;
701
+ }
702
+ interface NullLit {
703
+ kind: "null";
704
+ span: SourceSpan;
705
+ }
706
+ type Literal = StringLit | NumberLit | BoolLit | NullLit;
707
+ interface ComparisonNode {
708
+ type: "comparison";
709
+ field: IdentifierRef;
710
+ op: ComparisonOp;
711
+ value: Literal;
712
+ span: SourceSpan;
713
+ }
714
+ interface NullCheckNode {
715
+ type: "nullCheck";
716
+ field: IdentifierRef;
717
+ negated: boolean;
718
+ span: SourceSpan;
719
+ }
720
+ interface InNode {
721
+ type: "in";
722
+ field: IdentifierRef;
723
+ negated: boolean;
724
+ values: Literal[];
725
+ span: SourceSpan;
726
+ }
727
+ interface LogicalNode {
728
+ type: "logical";
729
+ op: "AND" | "OR" | "NOT";
730
+ children: ParsedNode[];
731
+ span: SourceSpan;
732
+ }
733
+ type ParsedNode = ComparisonNode | NullCheckNode | InNode | LogicalNode;
734
+ type ValidFieldType = "string" | "number" | "boolean";
735
+ interface ValidFieldRef {
736
+ /** Canonical name from the schema (not the raw source text). */
737
+ name: string;
738
+ baseType: ValidFieldType;
739
+ /** True if the schema wraps this field in OptionalSchema. */
740
+ optional: boolean;
741
+ span: SourceSpan;
742
+ }
743
+ interface ValidComparisonNode {
744
+ type: "comparison";
745
+ field: ValidFieldRef;
746
+ op: ComparisonOp;
747
+ value: Literal;
748
+ span: SourceSpan;
749
+ }
750
+ interface ValidNullCheckNode {
751
+ type: "nullCheck";
752
+ field: ValidFieldRef;
753
+ negated: boolean;
754
+ span: SourceSpan;
755
+ }
756
+ interface ValidInNode {
757
+ type: "in";
758
+ field: ValidFieldRef;
759
+ negated: boolean;
760
+ values: Literal[];
761
+ span: SourceSpan;
762
+ }
763
+ interface ValidLogicalNode {
764
+ type: "logical";
765
+ op: "AND" | "OR" | "NOT";
766
+ children: ValidNode[];
767
+ span: SourceSpan;
768
+ }
769
+ type ValidNode = ValidComparisonNode | ValidNullCheckNode | ValidInNode | ValidLogicalNode;
770
+
771
+ /**
772
+ * A compiled NQL expression, ready to be passed to Kysely's `.where(...)`.
773
+ * Calling it with an `ExpressionBuilder` yields a boolean `ExpressionWrapper`
774
+ * usable directly as a WHERE clause.
775
+ */
776
+ type CompiledNql = (eb: ExpressionBuilder<any, any>) => ExpressionWrapper<any, any, SqlBool>;
777
+ interface CompileNqlOptions {
778
+ /**
779
+ * Bindings map declaring which schema fields are queryable via NQL, and
780
+ * the Kysely column reference each one resolves to. Only fields whose
781
+ * keys appear here are queryable; anything else in the schema is
782
+ * rejected as an unknown field at validate time.
783
+ *
784
+ * Build this with {@link createNqlBindings} to get end-to-end type
785
+ * safety (schema keys on the key side, Kysely DB columns on the value
786
+ * side).
787
+ */
788
+ fields: FieldBindings;
789
+ }
790
+ /**
791
+ * End-to-end pipeline: tokenize → parse → validate → compile.
792
+ *
793
+ * Returns a discriminated-union Result. On success the `value` is a function
794
+ * that can be handed directly to `query.where(...)`. On failure the `error`
795
+ * carries a `code` identifying the stage (`TOKENIZE`, `PARSE`, `VALIDATE`)
796
+ * and a 1-based `line`/`column` for surfacing back to the user.
797
+ */
798
+ declare function compileNql(source: string, schema: ObjectSchema<any>, options: CompileNqlOptions): Result<CompiledNql, NqlError>;
799
+
800
+ interface CompileOptions {
801
+ /**
802
+ * Map of schema field name to Kysely column reference (e.g. `"tickets.title"`
803
+ * or `"users.displayName"`). Values here are trusted to be valid Kysely
804
+ * refs — type safety is enforced at binding-creation time, usually via
805
+ * {@link createNqlBindings}.
806
+ */
807
+ fields: FieldBindings;
808
+ }
809
+
568
810
  /**
569
811
  * Parse a cookie header string into a key-value object.
570
812
  *
@@ -608,4 +850,4 @@ interface ValidateEnvironmentOptions {
608
850
  */
609
851
  declare function validateEnvironment(options?: ValidateEnvironmentOptions): Promise<void>;
610
852
 
611
- export { AUTH_CONTROLLER_KEY, AUTH_USER_KEY, type AuthHandlers, type AuthLevel, type AuthMiddlewareOptions, type AuthVariables, type BackendAuthController, type BackendUser, type CreateAuthHandlersOptions, type CreateHttpHandlerOptions, type EndpointSelector, type FactoryHandlerOptions, type HandlerFactoryConfig, HttpError, type HttpHandler, type HttpHandlers, type TokenPayload, type TypedHandler, type TypedHandlerContext, type TypedRouteDefinition, type TypedRoutes, type ValidateEnvironmentOptions, type VerifyTokenResult, createAuthHandlers, createAuthMiddleware, createHandlerFactory, createHttpHandler, createTypedHandler, createTypedRoutes, getAuthController, getCookie, getUser, parseCookies, registerHandlers, requireAuth, validateEnvironment };
853
+ export { AUTH_CONTROLLER_KEY, AUTH_USER_KEY, type AuthHandlers, type AuthLevel, type AuthMiddlewareOptions, type AuthVariables, type BackendAuthController, type BackendUser, type CompileNqlOptions, type CompileOptions, type CompiledNql, type CreateAuthHandlersOptions, type CreateHttpHandlerOptions, type EndpointSelector, type FactoryHandlerOptions, type FieldBindings, type HandlerFactoryConfig, HttpError, type HttpHandler, type HttpHandlers, type NqlError, type NqlErrorCode, type NubaseBackendAuthConfig, type NubaseBackendConfig, type ParsedNode, type Result, type SourceSpan, type TokenPayload, type TypedHandler, type TypedHandlerContext, type TypedRouteDefinition, type TypedRoutes, type ValidFieldRef, type ValidFieldType, type ValidNode, type ValidateEnvironmentOptions, type VerifyTokenResult, compileNql, createAuthHandlers, createAuthMiddleware, createHandlerFactory, createHttpHandler, createNqlBindings, createTypedHandler, createTypedRoutes, getAuthController, getCookie, getUser, parseCookies, registerHandlers, requireAuth, validateEnvironment };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import * as hono from 'hono';
2
2
  import { Context, Hono } from 'hono';
3
- import { RequestSchema, InferRequestParams, InferRequestBody, InferResponseBody } from '@nubase/core';
3
+ import { RequestSchema, InferRequestParams, InferRequestBody, InferResponseBody, BaseSchema, ObjectSchema } from '@nubase/core';
4
4
  import { ContentfulStatusCode } from 'hono/utils/http-status';
5
+ import { StringReference, ExpressionBuilder, ExpressionWrapper, SqlBool } from 'kysely';
5
6
 
6
7
  /**
7
8
  * Represents an authenticated user on the backend.
@@ -318,6 +319,47 @@ declare function getUser<TUser extends BackendUser = BackendUser>(c: Context): T
318
319
  */
319
320
  declare function getAuthController<TUser extends BackendUser = BackendUser>(c: Context): BackendAuthController<TUser>;
320
321
 
322
+ /**
323
+ * Configuration for the auth section of {@link NubaseBackendConfig}.
324
+ * Consumed by `BackendAuthController` implementations to derive JWT and
325
+ * cookie behaviour.
326
+ */
327
+ interface NubaseBackendAuthConfig {
328
+ /** Secret used to sign and verify session JWTs. */
329
+ jwtSecret: string;
330
+ /**
331
+ * Lifetime of the session, in seconds. Applied to both the JWT `exp`
332
+ * claim and the session cookie's `Max-Age`.
333
+ */
334
+ sessionMaxAgeSeconds: number;
335
+ /** Name of the HttpOnly cookie that carries the session JWT. */
336
+ cookieName: string;
337
+ }
338
+ /**
339
+ * Backend application configuration for a Nubase app.
340
+ *
341
+ * An app declares this in `src/backend/backend-config.ts` and the wiring
342
+ * code (controller singletons, middleware factories) reads it. The type
343
+ * lives in the framework so all Nubase apps share one shape; apps may
344
+ * extend it with their own sections by intersecting with this type.
345
+ *
346
+ * @example
347
+ * ```ts
348
+ * import type { NubaseBackendConfig } from "@nubase/backend";
349
+ *
350
+ * export const config: NubaseBackendConfig = {
351
+ * auth: {
352
+ * jwtSecret: process.env.JWT_SECRET ?? "dev-secret",
353
+ * sessionMaxAgeSeconds: 60 * 60 * 24 * 30,
354
+ * cookieName: "nubase_auth",
355
+ * },
356
+ * };
357
+ * ```
358
+ */
359
+ interface NubaseBackendConfig {
360
+ auth: NubaseBackendAuthConfig;
361
+ }
362
+
321
363
  /**
322
364
  * Custom HTTP error class that allows handlers to throw errors with specific status codes.
323
365
  * Use this to return proper HTTP error responses instead of generic 500 errors.
@@ -329,7 +371,19 @@ declare function getAuthController<TUser extends BackendUser = BackendUser>(c: C
329
371
  */
330
372
  declare class HttpError extends Error {
331
373
  statusCode: ContentfulStatusCode;
332
- constructor(statusCode: ContentfulStatusCode, message: string);
374
+ /**
375
+ * Optional structured payload merged into the JSON response body under
376
+ * the `error` key. Use when the caller needs machine-readable details
377
+ * (error code, field, line/column, etc.) beyond the human message.
378
+ */
379
+ payload?: Record<string, unknown> | undefined;
380
+ constructor(statusCode: ContentfulStatusCode, message: string,
381
+ /**
382
+ * Optional structured payload merged into the JSON response body under
383
+ * the `error` key. Use when the caller needs machine-readable details
384
+ * (error code, field, line/column, etc.) beyond the human message.
385
+ */
386
+ payload?: Record<string, unknown> | undefined);
333
387
  }
334
388
  /**
335
389
  * URL Parameter Coercion System (Backend)
@@ -565,6 +619,194 @@ type EndpointSelector<TEndpoints extends Record<string, RequestSchema>, T extend
565
619
  */
566
620
  declare function createHandlerFactory<TEndpoints extends Record<string, RequestSchema>, TUser extends BackendUser = BackendUser>(config: HandlerFactoryConfig<TEndpoints>): <TSelector extends (endpoints: TEndpoints) => RequestSchema, TAuth extends AuthLevel = "none">(selector: TSelector, options: FactoryHandlerOptions<ReturnType<TSelector>, TAuth, TUser>) => HttpHandler;
567
621
 
622
+ type ShapeOf = Record<string, BaseSchema<any>>;
623
+ /**
624
+ * Runtime shape of an NQL field-to-column map. Keys are schema field names;
625
+ * values are Kysely column references (e.g. `"tickets.title"`).
626
+ *
627
+ * This type is intentionally loose at runtime. The strong typing is applied
628
+ * at binding-creation time via {@link createNqlBindings}, which constrains
629
+ * keys to the schema's shape and values to the Kysely `StringReference`
630
+ * union. Once built, the bindings object is carried through the NQL
631
+ * pipeline as a plain `Record<string, string>`.
632
+ */
633
+ type FieldBindings = Record<string, string>;
634
+ /**
635
+ * Curry a bindings factory for a specific Kysely DB type.
636
+ *
637
+ * The outer call pins `DB` (the generated Kysely database interface). The
638
+ * inner call takes the NQL schema and a map whose keys are checked against
639
+ * the schema's shape and whose values are checked against the Kysely
640
+ * `StringReference<DB, TB>` union — so a typo in either half is a compile
641
+ * error, and referencing a table you haven't joined in the current query
642
+ * is caught when the resulting bindings are passed to `compileNql`.
643
+ *
644
+ * @example
645
+ * ```ts
646
+ * import type { DB } from "./db-types";
647
+ * const ticketBindings = createNqlBindings<DB>()(ticketListSchema, {
648
+ * id: "tickets.id",
649
+ * title: "tickets.title",
650
+ * assigneeName: "users.displayName", // cross-table, only valid if joined
651
+ * });
652
+ * ```
653
+ */
654
+ declare function createNqlBindings<DB>(): <TShape extends ShapeOf, TB extends keyof DB>(schema: ObjectSchema<TShape>, mapping: Partial<Record<keyof TShape & string, StringReference<DB, TB>>>) => FieldBindings;
655
+
656
+ /**
657
+ * Source position tracking. `line` and `column` are 1-based to match
658
+ * how most editors report positions. `offset` is a 0-based character
659
+ * offset into the original source string.
660
+ */
661
+ interface SourceSpan {
662
+ line: number;
663
+ column: number;
664
+ offset: number;
665
+ length: number;
666
+ }
667
+ type NqlErrorCode = "TOKENIZE" | "PARSE" | "VALIDATE";
668
+ interface NqlError {
669
+ code: NqlErrorCode;
670
+ message: string;
671
+ line: number;
672
+ column: number;
673
+ length?: number;
674
+ }
675
+ type Result<T, E> = {
676
+ ok: true;
677
+ value: T;
678
+ } | {
679
+ ok: false;
680
+ error: E;
681
+ };
682
+ type ComparisonOp = "=" | "!=" | "<" | "<=" | ">" | ">=" | "CONTAINS" | "STARTS_WITH" | "ENDS_WITH";
683
+ interface IdentifierRef {
684
+ raw: string;
685
+ span: SourceSpan;
686
+ }
687
+ interface StringLit {
688
+ kind: "string";
689
+ value: string;
690
+ span: SourceSpan;
691
+ }
692
+ interface NumberLit {
693
+ kind: "number";
694
+ value: number;
695
+ span: SourceSpan;
696
+ }
697
+ interface BoolLit {
698
+ kind: "boolean";
699
+ value: boolean;
700
+ span: SourceSpan;
701
+ }
702
+ interface NullLit {
703
+ kind: "null";
704
+ span: SourceSpan;
705
+ }
706
+ type Literal = StringLit | NumberLit | BoolLit | NullLit;
707
+ interface ComparisonNode {
708
+ type: "comparison";
709
+ field: IdentifierRef;
710
+ op: ComparisonOp;
711
+ value: Literal;
712
+ span: SourceSpan;
713
+ }
714
+ interface NullCheckNode {
715
+ type: "nullCheck";
716
+ field: IdentifierRef;
717
+ negated: boolean;
718
+ span: SourceSpan;
719
+ }
720
+ interface InNode {
721
+ type: "in";
722
+ field: IdentifierRef;
723
+ negated: boolean;
724
+ values: Literal[];
725
+ span: SourceSpan;
726
+ }
727
+ interface LogicalNode {
728
+ type: "logical";
729
+ op: "AND" | "OR" | "NOT";
730
+ children: ParsedNode[];
731
+ span: SourceSpan;
732
+ }
733
+ type ParsedNode = ComparisonNode | NullCheckNode | InNode | LogicalNode;
734
+ type ValidFieldType = "string" | "number" | "boolean";
735
+ interface ValidFieldRef {
736
+ /** Canonical name from the schema (not the raw source text). */
737
+ name: string;
738
+ baseType: ValidFieldType;
739
+ /** True if the schema wraps this field in OptionalSchema. */
740
+ optional: boolean;
741
+ span: SourceSpan;
742
+ }
743
+ interface ValidComparisonNode {
744
+ type: "comparison";
745
+ field: ValidFieldRef;
746
+ op: ComparisonOp;
747
+ value: Literal;
748
+ span: SourceSpan;
749
+ }
750
+ interface ValidNullCheckNode {
751
+ type: "nullCheck";
752
+ field: ValidFieldRef;
753
+ negated: boolean;
754
+ span: SourceSpan;
755
+ }
756
+ interface ValidInNode {
757
+ type: "in";
758
+ field: ValidFieldRef;
759
+ negated: boolean;
760
+ values: Literal[];
761
+ span: SourceSpan;
762
+ }
763
+ interface ValidLogicalNode {
764
+ type: "logical";
765
+ op: "AND" | "OR" | "NOT";
766
+ children: ValidNode[];
767
+ span: SourceSpan;
768
+ }
769
+ type ValidNode = ValidComparisonNode | ValidNullCheckNode | ValidInNode | ValidLogicalNode;
770
+
771
+ /**
772
+ * A compiled NQL expression, ready to be passed to Kysely's `.where(...)`.
773
+ * Calling it with an `ExpressionBuilder` yields a boolean `ExpressionWrapper`
774
+ * usable directly as a WHERE clause.
775
+ */
776
+ type CompiledNql = (eb: ExpressionBuilder<any, any>) => ExpressionWrapper<any, any, SqlBool>;
777
+ interface CompileNqlOptions {
778
+ /**
779
+ * Bindings map declaring which schema fields are queryable via NQL, and
780
+ * the Kysely column reference each one resolves to. Only fields whose
781
+ * keys appear here are queryable; anything else in the schema is
782
+ * rejected as an unknown field at validate time.
783
+ *
784
+ * Build this with {@link createNqlBindings} to get end-to-end type
785
+ * safety (schema keys on the key side, Kysely DB columns on the value
786
+ * side).
787
+ */
788
+ fields: FieldBindings;
789
+ }
790
+ /**
791
+ * End-to-end pipeline: tokenize → parse → validate → compile.
792
+ *
793
+ * Returns a discriminated-union Result. On success the `value` is a function
794
+ * that can be handed directly to `query.where(...)`. On failure the `error`
795
+ * carries a `code` identifying the stage (`TOKENIZE`, `PARSE`, `VALIDATE`)
796
+ * and a 1-based `line`/`column` for surfacing back to the user.
797
+ */
798
+ declare function compileNql(source: string, schema: ObjectSchema<any>, options: CompileNqlOptions): Result<CompiledNql, NqlError>;
799
+
800
+ interface CompileOptions {
801
+ /**
802
+ * Map of schema field name to Kysely column reference (e.g. `"tickets.title"`
803
+ * or `"users.displayName"`). Values here are trusted to be valid Kysely
804
+ * refs — type safety is enforced at binding-creation time, usually via
805
+ * {@link createNqlBindings}.
806
+ */
807
+ fields: FieldBindings;
808
+ }
809
+
568
810
  /**
569
811
  * Parse a cookie header string into a key-value object.
570
812
  *
@@ -608,4 +850,4 @@ interface ValidateEnvironmentOptions {
608
850
  */
609
851
  declare function validateEnvironment(options?: ValidateEnvironmentOptions): Promise<void>;
610
852
 
611
- export { AUTH_CONTROLLER_KEY, AUTH_USER_KEY, type AuthHandlers, type AuthLevel, type AuthMiddlewareOptions, type AuthVariables, type BackendAuthController, type BackendUser, type CreateAuthHandlersOptions, type CreateHttpHandlerOptions, type EndpointSelector, type FactoryHandlerOptions, type HandlerFactoryConfig, HttpError, type HttpHandler, type HttpHandlers, type TokenPayload, type TypedHandler, type TypedHandlerContext, type TypedRouteDefinition, type TypedRoutes, type ValidateEnvironmentOptions, type VerifyTokenResult, createAuthHandlers, createAuthMiddleware, createHandlerFactory, createHttpHandler, createTypedHandler, createTypedRoutes, getAuthController, getCookie, getUser, parseCookies, registerHandlers, requireAuth, validateEnvironment };
853
+ export { AUTH_CONTROLLER_KEY, AUTH_USER_KEY, type AuthHandlers, type AuthLevel, type AuthMiddlewareOptions, type AuthVariables, type BackendAuthController, type BackendUser, type CompileNqlOptions, type CompileOptions, type CompiledNql, type CreateAuthHandlersOptions, type CreateHttpHandlerOptions, type EndpointSelector, type FactoryHandlerOptions, type FieldBindings, type HandlerFactoryConfig, HttpError, type HttpHandler, type HttpHandlers, type NqlError, type NqlErrorCode, type NubaseBackendAuthConfig, type NubaseBackendConfig, type ParsedNode, type Result, type SourceSpan, type TokenPayload, type TypedHandler, type TypedHandlerContext, type TypedRouteDefinition, type TypedRoutes, type ValidFieldRef, type ValidFieldType, type ValidNode, type ValidateEnvironmentOptions, type VerifyTokenResult, compileNql, createAuthHandlers, createAuthMiddleware, createHandlerFactory, createHttpHandler, createNqlBindings, createTypedHandler, createTypedRoutes, getAuthController, getCookie, getUser, parseCookies, registerHandlers, requireAuth, validateEnvironment };