@3lineas/d1-orm 1.0.7 → 1.0.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.
package/README.md CHANGED
@@ -20,196 +20,92 @@ pnpm add @3lineas/d1-orm
20
20
 
21
21
  ## Initial Setup
22
22
 
23
- To get started, configure the database connection in your Worker. This is typically done in your application's entry point (e.g., `index.ts` or `server.ts`).
23
+ By default, the ORM attempts to **auto-initialize** itself if your D1 binding is named `DB`.
24
24
 
25
- ```typescript
26
- import { Database } from "@3lineas/d1-orm";
27
-
28
- export default {
29
- async fetch(request, env, ctx) {
30
- // Initialize the D1 connection (assuming it's named DB in wrangler.toml)
31
- Database.setup(env.DB);
32
-
33
- // ... your routing logic
34
- },
35
- };
36
- ```
37
-
38
- ## Defining Models
39
-
40
- Define your models by extending the `Model` class. By default, the ORM assumes the table name is the lowercase plural form of the class name (e.g., `User` -> `users`).
25
+ For zero-configuration setup in Cloudflare Workers or Next.js:
41
26
 
42
27
  ```typescript
43
- import { Model } from "@3lineas/d1-orm";
44
-
45
- export class User extends Model {
46
- // Optional: Custom table name
47
- // protected static table = 'my_users';
48
-
49
- // Optional: Define attributes for typing (recommended)
50
- declare id: number;
51
- declare name: string;
52
- declare email: string;
53
- declare created_at: string;
54
- }
55
- ```
56
-
57
- ## CRUD Operations
58
-
59
- ### Create
60
-
61
- ```typescript
62
- const user = await User.create({
63
- name: "John Doe",
64
- email: "john@example.com",
65
- });
66
- ```
67
-
68
- ### Read
69
-
70
- ```typescript
71
- // Get all records
28
+ // No manual setup required if binding name is 'DB'!
72
29
  const users = await User.all();
73
-
74
- // Find by ID
75
- const user = await User.find(1);
76
-
77
- // Custom queries
78
- const activeUsers = await User.where("status", "=", "active")
79
- .orderBy("created_at", "desc")
80
- .get();
81
-
82
- // Get the first result
83
- const firstUser = await User.where("email", "john@example.com").first();
84
30
  ```
85
31
 
86
- ### Update
32
+ If you use a custom binding name or want manual control:
87
33
 
88
34
  ```typescript
89
- const user = await User.find(1);
90
- if (user) {
91
- user.fill({ name: "Updated Name" });
92
- await user.save();
93
- }
35
+ import { Database } from "@3lineas/d1-orm";
94
36
 
95
- // Or update directly via query
96
- await User.where("status", "inactive").update({ status: "active" });
37
+ export default {
38
+ async fetch(request, env, ctx) {
39
+ Database.setup(env.MY_CUSTOM_DB);
40
+ // ...
41
+ },
42
+ };
97
43
  ```
98
44
 
99
- ### Delete
100
-
101
- ```typescript
102
- const user = await User.find(1);
103
- if (user) {
104
- await user.delete();
105
- }
106
-
107
- // Or delete directly via query
108
- await User.where("status", "banned").delete();
109
- ```
45
+ ## Directory Structure
110
46
 
111
- ## Relationships
47
+ When you run `init`, the ORM creates a unified structure in `src/database` (if `src` exists) or `database/`:
112
48
 
113
- Support for basic relationships to structure your data.
49
+ - `database/models/`: Your Eloquent-style models.
50
+ - `database/migrations/`: SQL migration files.
51
+ - `database/seeders/`: Data seeders.
52
+ - `database/config.ts`: Central configuration.
114
53
 
115
- ### One to One (HasOne)
54
+ ### Configuration (`database/config.ts`)
116
55
 
117
56
  ```typescript
118
- // In User Model
119
- hasOneProfile() {
120
- return this.hasOne(Profile);
121
- }
122
-
123
- // Usage
124
- const profile = await user.hasOneProfile().get();
57
+ export default {
58
+ binding: "DB", // The name of your D1 binding
59
+ };
125
60
  ```
126
61
 
127
- ### One to Many (HasMany)
128
-
129
- ```typescript
130
- // In User Model
131
- posts() {
132
- return this.hasMany(Post);
133
- }
134
-
135
- // Usage
136
- const posts = await user.posts().get();
137
- ```
62
+ ## Defining Models
138
63
 
139
- ### Belongs To (BelongsTo)
64
+ Define your models by extending the `Model` class. Models are typically placed in `database/models/`.
140
65
 
141
66
  ```typescript
142
- // In Post Model
143
- user() {
144
- return this.belongsTo(User);
145
- }
67
+ import { Model } from "@3lineas/d1-orm";
146
68
 
147
- // Usage
148
- const author = await post.user().get();
69
+ export class User extends Model {
70
+ declare id: number;
71
+ declare name: string;
72
+ }
149
73
  ```
150
74
 
151
75
  ## CLI & Migrations
152
76
 
153
- The ORM includes a CLI to simplify database management.
154
-
155
- ### Automatic Configuration
77
+ The ORM includes a modern, Astro-style CLI for database management.
156
78
 
157
- The `init` command will automatically configure the `orm` script in your `package.json`.
158
-
159
- To run the initial command (before the script is added):
79
+ ### Initialization
160
80
 
161
81
  ```bash
162
- npx d1-orm init
163
- ```
164
-
165
- Once executed, the `orm` script will be added to your `package.json`:
166
-
167
- ```json
168
- "scripts": {
169
- "orm": "d1-orm"
170
- }
82
+ pnpm d1-orm init
171
83
  ```
172
84
 
173
- From then on, you can use:
174
-
175
- ```bash
176
- pnpm orm ...
177
- ```
85
+ This command is non-interactive and automatically detects your project structure.
178
86
 
179
87
  ### Available Commands
180
88
 
181
- #### Initialize Project
182
-
183
- An interactive command that sets up your project. It will ask where you want to keep your models and will automatically generate the directory structure, an example `User` model, its migration, and a seeder.
184
-
185
- #### Create a Model
186
-
187
- Generate a model file interactively. It allows you to optionally create a linked migration and seeder.
89
+ #### Create Files
188
90
 
189
91
  ```bash
190
- pnpm orm make:model
191
- ```
192
-
193
- #### Create a Migration
92
+ # Create a model (and optionally migration/seeder)
93
+ pnpm orm make:model User
194
94
 
195
- Generate a migration file interactively in `database/migrations`.
196
-
197
- ```bash
198
- pnpm orm make:migration
95
+ # Create a standalone migration
96
+ pnpm orm make:migration create_posts_table
199
97
  ```
200
98
 
201
99
  #### Run Migrations
202
100
 
203
- Run pending migrations.
204
-
205
101
  ```bash
206
- # Local (default)
102
+ # Local
207
103
  pnpm orm migrate
208
104
 
209
- # Remote (Production)
105
+ # Remote
210
106
  pnpm orm migrate --remote
211
107
 
212
- # Run migrations and then seed
108
+ # Migrate and then seed
213
109
  pnpm orm migrate --seed
214
110
  ```
215
111
 
@@ -230,12 +126,9 @@ Run seeders defined in `database/seeders`.
230
126
 
231
127
  ```bash
232
128
  pnpm orm db:seed
233
-
234
- # Remote
235
- pnpm orm db:seed --remote
236
129
  ```
237
130
 
238
- > **Note:** Local migrations require `wrangler` to be configured and running in your environment.
131
+ > **Note:** The CLI automatically detects your `wrangler.jsonc`, `wrangler.json` or `wrangler.toml` to find your D1 binding.
239
132
 
240
133
  ---
241
134
 
@@ -0,0 +1,127 @@
1
+ // src/core/connection.ts
2
+ var Connection = class {
3
+ /**
4
+ * The underlying D1Database instance.
5
+ */
6
+ db;
7
+ /**
8
+ * Create a new Connection instance.
9
+ *
10
+ * @param database - The D1Database instance.
11
+ */
12
+ constructor(database) {
13
+ this.db = database;
14
+ }
15
+ /**
16
+ * Execute a SELECT statement.
17
+ *
18
+ * @param query - The SQL query string.
19
+ * @param bindings - The parameter bindings.
20
+ * @returns A promise that resolves to an array of results.
21
+ */
22
+ async select(query, bindings = []) {
23
+ const stmt = this.db.prepare(query).bind(...bindings);
24
+ const result = await stmt.all();
25
+ return result.results || [];
26
+ }
27
+ /**
28
+ * Execute an INSERT statement.
29
+ *
30
+ * @param query - The SQL query string.
31
+ * @param bindings - The parameter bindings.
32
+ * @returns A promise that resolves to true on success.
33
+ */
34
+ async insert(query, bindings = []) {
35
+ const stmt = this.db.prepare(query).bind(...bindings);
36
+ const result = await stmt.run();
37
+ return result.success;
38
+ }
39
+ /**
40
+ * Execute an UPDATE statement.
41
+ *
42
+ * @param query - The SQL query string.
43
+ * @param bindings - The parameter bindings.
44
+ * @returns A promise that resolves to true on success.
45
+ */
46
+ async update(query, bindings = []) {
47
+ const stmt = this.db.prepare(query).bind(...bindings);
48
+ const result = await stmt.run();
49
+ return result.success;
50
+ }
51
+ /**
52
+ * Execute a DELETE statement.
53
+ *
54
+ * @param query - The SQL query string.
55
+ * @param bindings - The parameter bindings.
56
+ * @returns A promise that resolves to true on success.
57
+ */
58
+ async delete(query, bindings = []) {
59
+ const stmt = this.db.prepare(query).bind(...bindings);
60
+ const result = await stmt.run();
61
+ return result.success;
62
+ }
63
+ /**
64
+ * Execute an arbitrary SQL statement.
65
+ *
66
+ * @param query - The SQL query string.
67
+ * @param bindings - The parameter bindings.
68
+ * @returns A promise that resolves to true on success.
69
+ */
70
+ async statement(query, bindings = []) {
71
+ const stmt = this.db.prepare(query).bind(...bindings);
72
+ const result = await stmt.run();
73
+ return result.success;
74
+ }
75
+ };
76
+
77
+ // src/core/database.ts
78
+ var Database = class _Database {
79
+ /**
80
+ * Symbol for global singleton access.
81
+ */
82
+ static INSTANCE_KEY = /* @__PURE__ */ Symbol.for("d1-orm-instance");
83
+ /**
84
+ * The active database connection.
85
+ */
86
+ connection;
87
+ /**
88
+ * Private constructor to enforce singleton pattern.
89
+ *
90
+ * @param d1 - The D1Database instance from Cloudflare.
91
+ */
92
+ constructor(d1) {
93
+ this.connection = new Connection(d1);
94
+ }
95
+ /**
96
+ * Setup the database connection.
97
+ *
98
+ * @param d1 - The D1Database instance from Cloudflare environment (env.DB).
99
+ */
100
+ static setup(d1) {
101
+ globalThis[_Database.INSTANCE_KEY] = new _Database(d1);
102
+ }
103
+ /**
104
+ * Get the singleton Database instance.
105
+ *
106
+ * @throws Error if setup() has not been called and auto-init fails.
107
+ * @returns The Database instance.
108
+ */
109
+ static getInstance() {
110
+ const instance = globalThis[_Database.INSTANCE_KEY];
111
+ if (!instance) {
112
+ if (globalThis["DB"] && typeof globalThis["DB"].prepare === "function") {
113
+ _Database.setup(globalThis["DB"]);
114
+ return globalThis[_Database.INSTANCE_KEY];
115
+ }
116
+ throw new Error(
117
+ "Database not initialized. Call Database.setup(env.DB) first or ensure your D1 binding is named 'DB'."
118
+ );
119
+ }
120
+ return instance;
121
+ }
122
+ };
123
+
124
+ export {
125
+ Connection,
126
+ Database
127
+ };