bun-query-builder 0.1.10 → 0.1.12

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.
@@ -0,0 +1,27 @@
1
+ export type { BrowserModelDefinition as ModelDefinition };
2
+ /**
3
+ * Define an isomorphic model that works in both server and browser.
4
+ *
5
+ * In the browser, this creates a model that uses fetch() to call your API.
6
+ * On the server, this returns the definition for ORM generation AND provides
7
+ * query methods that work directly with the database.
8
+ *
9
+ * @param definition - The model definition
10
+ * @returns An isomorphic model with query methods
11
+ */
12
+ export declare function defineModel<const TDef extends BrowserModelDefinition>(definition: TDef): void;
13
+ /**
14
+ * Register models on the global window.StacksBrowser object.
15
+ * Call this in your app's entry point to make models available for STX auto-imports.
16
+ *
17
+ * @param models - Object mapping model names to model instances
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import Trail from './Models/Trail'
22
+ * import Activity from './Models/Activity'
23
+ *
24
+ * registerBrowserModels({ Trail, Activity })
25
+ * ```
26
+ */
27
+ export declare function registerBrowserModels(models: Record<string, unknown>): void;
package/dist/orm.d.ts ADDED
@@ -0,0 +1,244 @@
1
+ import { Database } from 'bun:sqlite';
2
+ import type { Faker } from 'ts-mocker';
3
+ export type {
4
+ ModelInstance,
5
+ ModelQueryBuilder,
6
+ ModelAttributes,
7
+ InferModelAttributes,
8
+ InferAttributeType,
9
+ SystemFields,
10
+ ColumnName,
11
+ AttributeKeys,
12
+ FillableKeys,
13
+ HiddenKeys,
14
+ };
15
+ export declare function configureOrm(options: { database?: string | Database; verbose?: boolean }): void;
16
+ export declare function getDatabase(): Database;
17
+ /**
18
+ * Create a model class from a definition with full type inference
19
+ */
20
+ export declare function createModel<const TDef extends ModelDefinition>(definition: TDef): void;
21
+ export declare function createTableFromModel(definition: ModelDefinition): void;
22
+ export declare function seedModel(definition: ModelDefinition, count?: number, faker?: any): Promise<void>;
23
+ // Attribute definition with explicit type
24
+ export declare interface TypedAttribute<T = unknown> {
25
+ type?: T
26
+ order?: number
27
+ fillable?: boolean
28
+ unique?: boolean
29
+ hidden?: boolean
30
+ guarded?: boolean
31
+ nullable?: boolean
32
+ default?: InferType<T>
33
+ validation?: {
34
+ rule: unknown
35
+ message?: Record<string, string>
36
+ }
37
+ factory?: (faker: Faker) => InferType<T>
38
+ }
39
+ // Base model definition
40
+ export declare interface ModelDefinition {
41
+ name: string
42
+ table: string
43
+ primaryKey?: string
44
+ autoIncrement?: boolean
45
+ connection?: string
46
+ traits?: {
47
+ readonly useUuid?: boolean
48
+ readonly useTimestamps?: boolean | object
49
+ readonly timestampable?: boolean | object
50
+ readonly useSoftDeletes?: boolean | object
51
+ readonly softDeletable?: boolean | object
52
+ readonly useSearch?: boolean | {
53
+ readonly displayable?: readonly string[]
54
+ readonly searchable?: readonly string[]
55
+ readonly sortable?: readonly string[]
56
+ readonly filterable?: readonly string[]
57
+ }
58
+ readonly useSeeder?: boolean | {
59
+ readonly count: number
60
+ }
61
+ readonly seedable?: boolean | {
62
+ readonly count: number
63
+ }
64
+ readonly useApi?: boolean | {
65
+ readonly uri?: string
66
+ readonly routes?: readonly string[]
67
+ readonly middleware?: readonly string[]
68
+ }
69
+ readonly useAuth?: boolean | {
70
+ readonly usePasskey?: boolean
71
+ readonly useTwoFactor?: boolean
72
+ }
73
+ readonly authenticatable?: boolean | object
74
+ readonly observe?: boolean | readonly string[]
75
+ readonly billable?: boolean
76
+ readonly likeable?: boolean | object
77
+ readonly taggable?: boolean
78
+ readonly categorizable?: boolean
79
+ readonly commentables?: boolean
80
+ readonly useActivityLog?: boolean | object
81
+ readonly useSocials?: readonly string[]
82
+ }
83
+ belongsTo?: readonly string[]
84
+ hasMany?: readonly string[]
85
+ hasOne?: readonly string[]
86
+ belongsToMany?: readonly (string | object)[]
87
+ hasOneThrough?: readonly (string | object)[]
88
+ hasManyThrough?: readonly (string | object)[]
89
+ morphOne?: string | object
90
+ morphMany?: readonly (string | object)[]
91
+ morphTo?: object
92
+ morphToMany?: readonly string[]
93
+ morphedByMany?: readonly string[]
94
+ attributes: {
95
+ readonly [key: string]: TypedAttribute<unknown>
96
+ }
97
+ get?: Record<string, (attributes: Record<string, unknown>) => unknown>
98
+ set?: Record<string, (attributes: Record<string, unknown>) => unknown>
99
+ scopes?: Record<string, (value: unknown) => unknown>
100
+ indexes?: readonly object[]
101
+ dashboard?: { readonly highlight?: boolean | number }
102
+ hooks?: {
103
+ readonly beforeCreate?: (data: any) => void | Promise<void>
104
+ readonly afterCreate?: (model: any) => void | Promise<void>
105
+ readonly beforeUpdate?: (model: any, data: any) => void | Promise<void>
106
+ readonly afterUpdate?: (model: any) => void | Promise<void>
107
+ readonly beforeDelete?: (model: any) => void | Promise<void>
108
+ readonly afterDelete?: (model: any) => void | Promise<void>
109
+ }
110
+ }
111
+ // Binding helper type for SQL queries
112
+ declare type Bindings = SQLQueryBindings[]
113
+ // Primitive type mappings
114
+ declare type PrimitiveTypeMap = {
115
+ string: string
116
+ number: number
117
+ boolean: boolean
118
+ date: Date
119
+ json: Record<string, unknown>
120
+ }
121
+ // Infer the actual TS type from attribute type definition
122
+ declare type InferType<T> = T extends keyof PrimitiveTypeMap ? PrimitiveTypeMap[T] :
123
+ T extends readonly (infer U)[] ? U :
124
+ T extends (infer U)[] ? U :
125
+ unknown
126
+ // Extract attribute keys from definition
127
+ declare type AttributeKeys<TDef extends ModelDefinition> = keyof TDef['attributes'] & string
128
+ // Infer single attribute type
129
+ declare type InferAttributeType<TAttr> = TAttr extends { type: infer T } ? InferType<T> :
130
+ TAttr extends { factory: (faker: Faker) => infer R } ? R :
131
+ unknown
132
+ // Build the full attributes type from definition
133
+ declare type InferModelAttributes<TDef extends ModelDefinition> = {
134
+ [K in AttributeKeys<TDef>]: InferAttributeType<TDef['attributes'][K]>
135
+ }
136
+ // System fields added by traits
137
+ declare type SystemFields<TDef extends ModelDefinition> = { id: number } &
138
+ (TDef['traits'] extends { useUuid: true } ? { uuid: string } : {}) &
139
+ (TDef['traits'] extends { useTimestamps: true } ? { created_at: string; updated_at: string } : {}) &
140
+ (TDef['traits'] extends { useSoftDeletes: true } ? { deleted_at: string | null } : {})
141
+ // Complete model type
142
+ declare type ModelAttributes<TDef extends ModelDefinition> = InferModelAttributes<TDef> & SystemFields<TDef>
143
+ // All valid column names
144
+ declare type ColumnName<TDef extends ModelDefinition> = | AttributeKeys<TDef>
145
+ | 'id'
146
+ | (TDef['traits'] extends { useUuid: true } ? 'uuid' : never)
147
+ | (TDef['traits'] extends { useTimestamps: true } ? 'created_at' | 'updated_at' : never)
148
+ | (TDef['traits'] extends { useSoftDeletes: true } ? 'deleted_at' : never)
149
+ // Hidden fields
150
+ declare type HiddenKeys<TDef extends ModelDefinition> = {
151
+ [K in AttributeKeys<TDef>]: TDef['attributes'][K] extends { hidden: true } ? K : never
152
+ }[AttributeKeys<TDef>]
153
+ // Fillable fields
154
+ declare type FillableKeys<TDef extends ModelDefinition> = {
155
+ [K in AttributeKeys<TDef>]: TDef['attributes'][K] extends { fillable: true } ? K : never
156
+ }[AttributeKeys<TDef>]
157
+ // Infer relation names from model definition
158
+ declare type InferBelongsToNames<TDef> = TDef extends { belongsTo: readonly (infer R)[] }
159
+ ? R extends string ? Lowercase<R> : never : never
160
+ declare type InferHasManyNames<TDef> = TDef extends { hasMany: readonly (infer R)[] }
161
+ ? R extends string ? Lowercase<R> : never : never
162
+ declare type InferHasOneNames<TDef> = TDef extends { hasOne: readonly (infer R)[] }
163
+ ? R extends string ? Lowercase<R> : never : never
164
+ declare type InferBelongsToManyNames<TDef> = TDef extends { belongsToMany: readonly (infer R)[] }
165
+ ? R extends string ? Lowercase<R> : R extends { model: infer M extends string } ? Lowercase<M> : never : never
166
+ declare type InferHasOneThroughNames<TDef> = TDef extends { hasOneThrough: readonly (infer R)[] }
167
+ ? R extends string ? Lowercase<R> : R extends { model: infer M extends string } ? Lowercase<M> : never : never
168
+ declare type InferHasManyThroughNames<TDef> = TDef extends { hasManyThrough: readonly (infer R)[] }
169
+ ? R extends string ? Lowercase<R> : R extends { model: infer M extends string } ? Lowercase<M> : never : never
170
+ export type InferRelationNames<TDef> = | InferBelongsToNames<TDef>
171
+ | InferHasManyNames<TDef>
172
+ | InferHasOneNames<TDef>
173
+ | InferBelongsToManyNames<TDef>
174
+ | InferHasOneThroughNames<TDef>
175
+ | InferHasManyThroughNames<TDef>
176
+ declare type WhereOperator = '=' | '!=' | '<' | '>' | '<=' | '>=' | 'like' | 'in' | 'not in'
177
+ /**
178
+ * Model instance - represents a single database record
179
+ */
180
+ declare class ModelInstance<TDef extends ModelDefinition, TSelected extends ColumnName<TDef> = ColumnName<TDef>> {
181
+ private _attributes: Record<string, unknown>;
182
+ private _original: Record<string, unknown>;
183
+ private _definition: TDef;
184
+ private _hasSaved: any;
185
+ constructor(definition: TDef, attributes?: Partial<ModelAttributes<TDef>>);
186
+ get<K extends TSelected>(key: K): K extends keyof ModelAttributes<TDef> ? ModelAttributes<TDef>[K] : never;
187
+ set<K extends AttributeKeys<TDef>>(key: K, value: ModelAttributes<TDef>[K]): void;
188
+ isDirty<K extends AttributeKeys<TDef>>(column?: K): boolean;
189
+ isClean<K extends AttributeKeys<TDef>>(column?: K): boolean;
190
+ getOriginal<K extends AttributeKeys<TDef>>(column: K): ModelAttributes<TDef>[K];
191
+ getChanges(): Partial<InferModelAttributes<TDef>>;
192
+ fill(data: Partial<Pick<InferModelAttributes<TDef>, FillableKeys<TDef>>>): this;
193
+ forceFill(data: Partial<InferModelAttributes<TDef>>): this;
194
+ save(): this;
195
+ update(data: Partial<Pick<InferModelAttributes<TDef>, FillableKeys<TDef>>>): this;
196
+ delete(): boolean;
197
+ refresh(): this;
198
+ toJSON(): Omit<Pick<ModelAttributes<TDef>, TSelected & keyof ModelAttributes<TDef>>, HiddenKeys<TDef>>;
199
+ }
200
+ /**
201
+ * Query builder with precise type narrowing
202
+ */
203
+ declare class ModelQueryBuilder<TDef extends ModelDefinition, TSelected extends ColumnName<TDef> = ColumnName<TDef>> {
204
+ private _definition: TDef;
205
+ private _wheres: { column: string; operator: WhereOperator; value: unknown; boolean: 'and' | 'or' }[];
206
+ private _orderBy: { column: string; direction: 'asc' | 'desc' }[];
207
+ private _limit?: number;
208
+ private _offset?: number;
209
+ private _select: string[];
210
+ private _withRelations: string[];
211
+ constructor(definition: TDef);
212
+ where<K extends ColumnName<TDef>>(column: K, operatorOrValue: WhereOperator | (K extends keyof ModelAttributes<TDef> ? ModelAttributes<TDef>[K] : unknown), value?: K extends keyof ModelAttributes<TDef> ? ModelAttributes<TDef>[K] : unknown): ModelQueryBuilder<TDef, TSelected>;
213
+ orWhere<K extends ColumnName<TDef>>(column: K, operatorOrValue: WhereOperator | (K extends keyof ModelAttributes<TDef> ? ModelAttributes<TDef>[K] : unknown), value?: K extends keyof ModelAttributes<TDef> ? ModelAttributes<TDef>[K] : unknown): ModelQueryBuilder<TDef, TSelected>;
214
+ whereIn<K extends ColumnName<TDef>>(column: K, values: (K extends keyof ModelAttributes<TDef> ? ModelAttributes<TDef>[K] : unknown)[]): ModelQueryBuilder<TDef, TSelected>;
215
+ whereNotIn<K extends ColumnName<TDef>>(column: K, values: (K extends keyof ModelAttributes<TDef> ? ModelAttributes<TDef>[K] : unknown)[]): ModelQueryBuilder<TDef, TSelected>;
216
+ whereNull<K extends ColumnName<TDef>>(column: K): ModelQueryBuilder<TDef, TSelected>;
217
+ whereNotNull<K extends ColumnName<TDef>>(column: K): ModelQueryBuilder<TDef, TSelected>;
218
+ whereLike<K extends ColumnName<TDef>>(column: K, pattern: string): ModelQueryBuilder<TDef, TSelected>;
219
+ orderBy<K extends ColumnName<TDef>>(column: K, direction?: 'asc' | 'desc'): ModelQueryBuilder<TDef, TSelected>;
220
+ orderByDesc<K extends ColumnName<TDef>>(column: K): ModelQueryBuilder<TDef, TSelected>;
221
+ orderByAsc<K extends ColumnName<TDef>>(column: K): ModelQueryBuilder<TDef, TSelected>;
222
+ limit(count: number): ModelQueryBuilder<TDef, TSelected>;
223
+ take(count: number): ModelQueryBuilder<TDef, TSelected>;
224
+ offset(count: number): ModelQueryBuilder<TDef, TSelected>;
225
+ skip(count: number): ModelQueryBuilder<TDef, TSelected>;
226
+ select<K extends ColumnName<TDef>>(columns: K[]): ModelQueryBuilder<TDef, K>;
227
+ with<R extends InferRelationNames<TDef>>(relations: R[]): ModelQueryBuilder<TDef, TSelected>;
228
+ getWithRelations(): string[];
229
+ private buildQuery(): { sql: string; params: unknown[] };
230
+ get(): ModelInstance<TDef, TSelected>[];
231
+ first(): ModelInstance<TDef, TSelected> | undefined;
232
+ firstOrFail(): ModelInstance<TDef, TSelected>;
233
+ last(): ModelInstance<TDef, TSelected> | undefined;
234
+ count(): number;
235
+ exists(): boolean;
236
+ paginate(page?: any, perPage?: any): void;
237
+ pluck<K extends ColumnName<TDef>>(column: K): (K extends keyof ModelAttributes<TDef> ? ModelAttributes<TDef>[K] : unknown)[];
238
+ max<K extends AttributeKeys<TDef>>(column: K): number;
239
+ min<K extends AttributeKeys<TDef>>(column: K): number;
240
+ avg<K extends AttributeKeys<TDef>>(column: K): number;
241
+ sum<K extends AttributeKeys<TDef>>(column: K): number;
242
+ delete(): number;
243
+ update(data: Partial<Pick<InferModelAttributes<TDef>, FillableKeys<TDef>>>): number;
244
+ }
package/dist/schema.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { Faker } from 'ts-mocker';
1
2
  /**
2
3
  * # `defineModel(model)`
3
4
  *
@@ -50,7 +51,7 @@ export declare interface Attribute {
50
51
  hidden?: boolean
51
52
  fillable?: boolean
52
53
  guarded?: boolean
53
- factory?: (faker: unknown) => any
54
+ factory?: (faker: Faker) => any
54
55
  validation: {
55
56
  rule: ValidationType
56
57
  message?: ValidatorMessage
package/dist/types.d.ts CHANGED
@@ -151,6 +151,21 @@ export declare interface DatabaseConfig {
151
151
  url?: string
152
152
  port: number
153
153
  }
154
+ /**
155
+ * # `BrowserConfig`
156
+ *
157
+ * Configuration for browser mode that uses fetch() API instead of direct database connections.
158
+ * This enables the query builder to work in browser environments by translating queries to REST API calls.
159
+ */
160
+ export declare interface BrowserConfig {
161
+ baseUrl: string
162
+ getToken?: () => string | null | Promise<string | null>
163
+ onUnauthorized?: () => void
164
+ headers?: Record<string, string>
165
+ timeout?: number
166
+ transformResponse?: <T>(response: any) => T
167
+ transformRequest?: <T>(data: T) => any
168
+ }
154
169
  /**
155
170
  * # `QueryBuilderConfig`
156
171
  *
@@ -171,6 +186,7 @@ export declare interface QueryBuilderConfig {
171
186
  verbose: boolean
172
187
  dialect: SupportedDialect
173
188
  database: DatabaseConfig
189
+ browser?: BrowserConfig
174
190
  timestamps: TimestampConfig
175
191
  pagination: PaginationConfig
176
192
  aliasing: AliasingConfig
@@ -230,5 +246,6 @@ export declare interface UnsafeOptions {
230
246
  * - 'postgres': Uses `RANDOM()`, supports JSON operators (e.g. `@>`), `FOR SHARE`, `FOR UPDATE`, CTEs
231
247
  * - 'mysql': Uses `RAND()`, shared locks via `LOCK IN SHARE MODE`
232
248
  * - 'sqlite': Lightweight engine; some features are limited or emulated
249
+ * - 'browser': Browser-compatible mode that uses fetch() API calls instead of direct database connections
233
250
  */
234
- export type SupportedDialect = 'postgres' | 'mysql' | 'sqlite'
251
+ export type SupportedDialect = 'postgres' | 'mysql' | 'sqlite' | 'browser'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bun-query-builder",
3
3
  "type": "module",
4
- "version": "0.1.10",
4
+ "version": "0.1.12",
5
5
  "description": "A simple yet performant query builder for TypeScript. Built with Bun.",
6
6
  "author": "Chris Breuer <chris@stacksjs.org>",
7
7
  "license": "MIT",
@@ -21,10 +21,22 @@
21
21
  ],
22
22
  "exports": {
23
23
  ".": {
24
+ "bun": "./src/index.ts",
24
25
  "types": "./dist/index.d.ts",
25
26
  "import": "./dist/index.js"
26
27
  },
28
+ "./browser": {
29
+ "bun": "./src/browser.ts",
30
+ "types": "./dist/browser.d.ts",
31
+ "import": "./dist/browser.js"
32
+ },
33
+ "./dynamodb": {
34
+ "bun": "./src/dynamodb/index.ts",
35
+ "types": "./dist/dynamodb/index.d.ts",
36
+ "import": "./dist/dynamodb/index.js"
37
+ },
27
38
  "./*": {
39
+ "bun": "./src/*",
28
40
  "import": "./dist/*"
29
41
  }
30
42
  },
@@ -58,8 +70,8 @@
58
70
  "fresh": "bunx rimraf node_modules/ bun.lock && bun i",
59
71
  "prepublishOnly": "bun --bun run build && bun run compile:all && bun run zip",
60
72
  "test": "bun test",
61
- "lint": "bunx --bun eslint .",
62
- "lint:fix": "bunx --bun eslint . --fix",
73
+ "lint": "bunx --bun pickier lint .",
74
+ "lint:fix": "bunx --bun pickier lint . --fix",
63
75
  "changelog": "bunx logsmith --verbose",
64
76
  "changelog:generate": "bunx logsmith --output CHANGELOG.md",
65
77
  "release": "bun --bun run changelog:generate && bunx --bun bumpx prompt --recursive",
@@ -70,7 +82,8 @@
70
82
  },
71
83
  "dependencies": {
72
84
  "@stacksjs/clapp": "^0.2.0",
73
- "@stacksjs/ts-validation": "^0.4.7",
85
+ "@stacksjs/ts-validation": "^0.4.9",
86
+ "dynamodb-tooling": "^0.3.2",
74
87
  "ts-mocker": "^0.1.5"
75
88
  },
76
89
  "devDependencies": {
@@ -82,7 +95,7 @@
82
95
  "git-hooks": {
83
96
  "pre-commit": {
84
97
  "staged-lint": {
85
- "*.{js,ts,json,yaml,yml,md}": "bunx --bun eslint --fix"
98
+ "*.{js,ts,json,yaml,yml,md}": "bunx --bun pickier lint --fix"
86
99
  }
87
100
  },
88
101
  "commit-msg": "bunx gitlint --edit .git/COMMIT_EDITMSG"