@noego/proper 0.0.3 → 0.0.5
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/bin/cli.d.mts +1 -0
- package/bin/cli.d.ts +1 -0
- package/bin/cli.js +426 -39
- package/bin/cli.js.map +1 -1
- package/bin/cli.mjs +433 -39
- package/bin/cli.mjs.map +1 -1
- package/bin/index.d.mts +179 -0
- package/bin/index.d.ts +179 -0
- package/bin/index.js +430 -37
- package/bin/index.js.map +1 -1
- package/bin/index.mjs +428 -36
- package/bin/index.mjs.map +1 -1
- package/lib/runner.d.mts +51 -0
- package/lib/runner.d.ts +51 -0
- package/lib/runner.js +257 -0
- package/lib/runner.js.map +1 -0
- package/lib/runner.mjs +230 -0
- package/lib/runner.mjs.map +1 -0
- package/package.json +12 -1
- package/readme.md +295 -39
package/readme.md
CHANGED
|
@@ -1,29 +1,36 @@
|
|
|
1
1
|
## SQL Proper
|
|
2
2
|
|
|
3
|
-
**SQL Proper** is a lightweight and straightforward SQL migration tool for both **MySQL/MariaDB** (via `mysql2`) and **SQLite
|
|
3
|
+
**SQL Proper** is a lightweight and straightforward SQL migration tool for both **MySQL/MariaDB** (via `mysql2`) and **SQLite**, with:
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
- A simple **CLI** for application developers
|
|
6
|
+
- A focused **programmatic API** for Node.js
|
|
7
|
+
- A small, testable **framework core** for contributors and advanced users
|
|
8
|
+
|
|
9
|
+
This README is written primarily for **developers integrating or extending** SQL Proper. If you only need usage details, you can skim **Quick Start (CLI)** and **Usage in Code (Import)**.
|
|
10
10
|
|
|
11
11
|
---
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Installation
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Add SQL Proper to your project:
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
npm install -g @noego/proper
|
|
17
|
+
```bash
|
|
18
|
+
# Globally (optional, for CLI-only usage)
|
|
19
|
+
npm install -g @noego/proper
|
|
21
20
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
# Or locally to your project (recommended)
|
|
22
|
+
npm install --save @noego/proper
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
For TypeScript projects, the emitted `.d.ts` files from the build are included in the published package; you don’t need to install separate type packages.
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Quick Start (CLI)
|
|
30
|
+
|
|
31
|
+
Below is a fast-track on **how to work** with SQL Proper from the command line. Skip to [Configuration](#configuration) to learn how to connect to your database or to [Programmatic API](#programmatic-api) for using it from code.
|
|
32
|
+
|
|
33
|
+
1. **Initialize a config file** (creates `proper.json` by default):
|
|
27
34
|
```bash
|
|
28
35
|
proper init
|
|
29
36
|
```
|
|
@@ -44,33 +51,33 @@ Below is a fast-track on **how to work** with SQL Proper. Skip to [Configuration
|
|
|
44
51
|
|
|
45
52
|
Also create a `migrations/` directory in your project root to hold `.up.sql` and `.down.sql` files. File names follow `<timestamp>_<name>.up.sql` and `<timestamp>_<name>.down.sql`.
|
|
46
53
|
|
|
47
|
-
|
|
54
|
+
2. **Create a new migration** (generates timestamped `.up.sql` & `.down.sql` files):
|
|
48
55
|
```bash
|
|
49
56
|
proper create --name "create_users_table"
|
|
50
57
|
```
|
|
51
58
|
Edit those `.sql` files and add your migration scripts.
|
|
52
59
|
|
|
53
|
-
|
|
60
|
+
3. **Apply migrations**:
|
|
54
61
|
```bash
|
|
55
62
|
proper up
|
|
56
63
|
```
|
|
57
64
|
- Applies all **pending** migrations.
|
|
58
65
|
- Use `--increment <number>` to limit the count, e.g. `proper up --increment 1`.
|
|
59
66
|
|
|
60
|
-
|
|
67
|
+
4. **Roll back migrations**:
|
|
61
68
|
```bash
|
|
62
69
|
proper down
|
|
63
70
|
```
|
|
64
71
|
- Rolls back the **latest** applied migrations.
|
|
65
72
|
- Use `--increment <number>` to limit the count, e.g. `proper down --increment 1`.
|
|
66
73
|
|
|
67
|
-
|
|
74
|
+
5. **Reset your database**:
|
|
68
75
|
```bash
|
|
69
76
|
proper reset
|
|
70
77
|
```
|
|
71
78
|
- Rolls back **all** migrations, then re-applies them.
|
|
72
79
|
|
|
73
|
-
|
|
80
|
+
6. **Check status**:
|
|
74
81
|
```bash
|
|
75
82
|
proper status
|
|
76
83
|
```
|
|
@@ -117,9 +124,9 @@ proper up --config path/to/my-custom.json
|
|
|
117
124
|
|
|
118
125
|
---
|
|
119
126
|
|
|
120
|
-
##
|
|
127
|
+
## Programmatic API
|
|
121
128
|
|
|
122
|
-
If you prefer integrating migrations in your Node.js
|
|
129
|
+
If you prefer integrating migrations directly in your Node.js services or test harnesses, import SQL Proper:
|
|
123
130
|
|
|
124
131
|
```ts
|
|
125
132
|
import { MigrationRunnerFactory } from "@noego/proper";
|
|
@@ -127,6 +134,10 @@ import { MigrationRunnerFactory } from "@noego/proper";
|
|
|
127
134
|
(async () => {
|
|
128
135
|
// Suppose "myconfig.json" is your config file
|
|
129
136
|
const runner = await MigrationRunnerFactory.create("myconfig.json");
|
|
137
|
+
|
|
138
|
+
// Or pass an existing connection (useful for testing or connection pooling):
|
|
139
|
+
// const runner = await MigrationRunnerFactory.create("myconfig.json", existingConnection);
|
|
140
|
+
|
|
130
141
|
await runner.setup();
|
|
131
142
|
|
|
132
143
|
// Apply pending migrations
|
|
@@ -144,15 +155,213 @@ import { MigrationRunnerFactory } from "@noego/proper";
|
|
|
144
155
|
})();
|
|
145
156
|
```
|
|
146
157
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
158
|
+
### Public exports
|
|
159
|
+
|
|
160
|
+
From `@noego/proper`:
|
|
161
|
+
|
|
162
|
+
- `MigrationRunnerFactory`
|
|
163
|
+
- `create(configPath: string, connection?: any)`: create a runner backed by config, optionally reusing an existing DB connection (see examples below).
|
|
164
|
+
- `createEmpty(configPath: string)`: create a runner without connecting to a database (used by `init`, `create`).
|
|
165
|
+
- `MigrationConfig`: config model used by the framework.
|
|
166
|
+
- `MigrationRunner` (alias of `MySQLMigrationRunner`): convenience export for MySQL-specific runner.
|
|
167
|
+
|
|
168
|
+
From `@noego/proper/runners`:
|
|
169
|
+
|
|
170
|
+
- `BaseSQLRunner`, `SQLRunner`, `SQLiteRunner`: low-level database runners for advanced integrations and testing.
|
|
171
|
+
|
|
172
|
+
These are implemented in the `framework/` directory and re-exported via `framework/lib/runner.ts`. Check the generated `.d.ts` files in `lib/` and `bin/` for the exact TypeScript shapes in a given release.
|
|
173
|
+
|
|
174
|
+
### Core runner API
|
|
175
|
+
|
|
176
|
+
The `MigrationRunner` instances returned from `MigrationRunnerFactory.create` expose:
|
|
177
|
+
|
|
178
|
+
- `setup()`: Prepare DB environment (creates migration table if missing).
|
|
179
|
+
- `getMigrations()`: Load all migrations from the configured folder.
|
|
150
180
|
- `getPendingMigrations()` / `getCompletedMigrations()`: Filtered migration sets.
|
|
151
|
-
- `migrate(nodes, forward)`:
|
|
152
|
-
- `reset()`:
|
|
153
|
-
- `createMigration(name)`:
|
|
154
|
-
- `init(configPath)`:
|
|
155
|
-
- `
|
|
181
|
+
- `migrate(nodes, forward)`: Execute migrations (`forward = true` for `up`, `false` for `down`).
|
|
182
|
+
- `reset()`: Roll back everything, then reapply them (used by CLI `reset`).
|
|
183
|
+
- `createMigration(name)`: Scaffold `<timestamp>_<name>.up.sql` and `.down.sql` files.
|
|
184
|
+
- `init(configPath)`: Create a default `proper.json` if missing.
|
|
185
|
+
- `query(sql: string, params?)`: Execute read-only queries (used by CLI `query`).
|
|
186
|
+
- `close()`: Close the DB connection (no-op if you provided the connection).
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Testing with In-Memory Databases
|
|
191
|
+
|
|
192
|
+
For test isolation and faster test execution, you can pass an existing database connection to Proper. This is particularly useful when working with SQLite `:memory:` databases where you want multiple tests to share the same in-memory instance.
|
|
193
|
+
|
|
194
|
+
**Why This Matters:** When using SQLite `:memory:` databases for testing, the database only exists as long as the connection is open. By creating the connection yourself and passing it to Proper, you can:
|
|
195
|
+
|
|
196
|
+
1. **Create** the `:memory:` connection once
|
|
197
|
+
2. **Run migrations** using Proper with that connection
|
|
198
|
+
3. **Run your tests** using the same connection with a fully migrated schema
|
|
199
|
+
|
|
200
|
+
Without this capability, you'd have to let Proper create the connection, but then you couldn't access that same in-memory database from your tests (it would be lost when Proper closes). Connection injection solves this by letting you control the lifecycle.
|
|
201
|
+
|
|
202
|
+
### Example: Using a Shared `:memory:` Connection
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
import { MigrationRunnerFactory } from "@noego/proper";
|
|
206
|
+
import * as sqlite from "sqlite";
|
|
207
|
+
import * as sqlite3 from "sqlite3";
|
|
208
|
+
|
|
209
|
+
// Create a single :memory: connection that all tests will share
|
|
210
|
+
const memoryDb = await sqlite.open({
|
|
211
|
+
filename: ':memory:',
|
|
212
|
+
driver: sqlite3.Database
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
// Pass the connection to Proper
|
|
216
|
+
const runner = await MigrationRunnerFactory.create("test-config.json", memoryDb);
|
|
217
|
+
|
|
218
|
+
// Setup and run migrations on the shared connection
|
|
219
|
+
await runner.setup();
|
|
220
|
+
const pending = await runner.getPendingMigrations();
|
|
221
|
+
await runner.migrate(pending, true);
|
|
222
|
+
|
|
223
|
+
// Now your tests can use the same memoryDb instance
|
|
224
|
+
// All queries will run against the same in-memory database
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Example: Test Setup with Connection Injection
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
import { describe, beforeAll, afterAll, test } from '@jest/globals';
|
|
231
|
+
|
|
232
|
+
describe('Database Tests', () => {
|
|
233
|
+
let db;
|
|
234
|
+
let runner;
|
|
235
|
+
|
|
236
|
+
beforeAll(async () => {
|
|
237
|
+
// Step 1: Create the :memory: database connection
|
|
238
|
+
// This connection will persist for all tests in this suite
|
|
239
|
+
db = await sqlite.open({
|
|
240
|
+
filename: ':memory:',
|
|
241
|
+
driver: sqlite3.Database
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
// Step 2: Pass the connection to Proper and run migrations
|
|
245
|
+
// Proper will use YOUR connection instead of creating its own
|
|
246
|
+
runner = await MigrationRunnerFactory.create("proper.json", db);
|
|
247
|
+
await runner.setup();
|
|
248
|
+
await runner.migrate(await runner.getPendingMigrations(), true);
|
|
249
|
+
|
|
250
|
+
// Step 3: Now your tests can use the same 'db' connection
|
|
251
|
+
// The schema is fully migrated and ready for testing
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
afterAll(async () => {
|
|
255
|
+
await runner.close();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test('should query migrated database', async () => {
|
|
259
|
+
// Use the same db connection that was migrated by Proper
|
|
260
|
+
// This works because we created the connection ourselves
|
|
261
|
+
const result = await db.get('SELECT * FROM users WHERE id = ?', [1]);
|
|
262
|
+
expect(result).toBeDefined();
|
|
263
|
+
});
|
|
264
|
+
});
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## Seeding
|
|
270
|
+
|
|
271
|
+
SQL Proper supports a powerful seeding feature that allows you to populate your database with initial data (reference data, test users, etc.). Seeds can be SQL files or JavaScript/TypeScript modules.
|
|
272
|
+
|
|
273
|
+
### Configuration
|
|
274
|
+
|
|
275
|
+
Configure seeds in your `proper.json`:
|
|
276
|
+
|
|
277
|
+
```json
|
|
278
|
+
{
|
|
279
|
+
"seeds": {
|
|
280
|
+
"migrationsDir": "database/seed/migrations",
|
|
281
|
+
"dataDir": "database/seed/data",
|
|
282
|
+
"list": ["muscles", "exercises", "equipments", "user"]
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
- **migrationsDir**: Directory containing seed implementation files (`.sql`, `.ts`, or `.js`).
|
|
288
|
+
- **dataDir**: (Optional) Directory containing JSON data files for seeds.
|
|
289
|
+
- **list**: Default list of seed names to run when no names are provided to the CLI.
|
|
290
|
+
|
|
291
|
+
### CLI Usage
|
|
292
|
+
|
|
293
|
+
Run seeds via the CLI:
|
|
294
|
+
|
|
295
|
+
```bash
|
|
296
|
+
# Run default seeds defined in config
|
|
297
|
+
proper seed
|
|
298
|
+
|
|
299
|
+
# Run specific seeds
|
|
300
|
+
proper seed muscles exercises
|
|
301
|
+
|
|
302
|
+
# Seed rollback (runs down migration for seeds)
|
|
303
|
+
proper seed --action down muscles
|
|
304
|
+
|
|
305
|
+
# Transactional mode
|
|
306
|
+
proper seed --transactional seed # Wrap each seed in a transaction
|
|
307
|
+
proper seed --transactional runner # Wrap all seeds in one transaction
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Seed Implementations
|
|
311
|
+
|
|
312
|
+
Seeds can be implemented in two ways:
|
|
313
|
+
|
|
314
|
+
**1. SQL Pair** (`<name>.up.sql` and `<name>.down.sql`) in `migrationsDir`.
|
|
315
|
+
- Simple raw SQL execution.
|
|
316
|
+
- Good for static reference data.
|
|
317
|
+
|
|
318
|
+
**2. TypeScript/JavaScript Module** (`<name>.ts` or `<name>.js`) in `migrationsDir`.
|
|
319
|
+
- Exports `up` and `down` functions.
|
|
320
|
+
- Receives a `runner` and a `context` object.
|
|
321
|
+
- `context` contains:
|
|
322
|
+
- `data`: Loaded JSON data matching the seed name (from `dataDir`).
|
|
323
|
+
- `dialect`: The current database dialect (`sql`, `sqlite`, etc.).
|
|
324
|
+
- `log`: A logger function.
|
|
325
|
+
|
|
326
|
+
**Example TS Seed (`database/seed/migrations/user.ts`):**
|
|
327
|
+
|
|
328
|
+
```ts
|
|
329
|
+
import type { IMigrationRunner, SeedContext } from "@noego/proper";
|
|
330
|
+
|
|
331
|
+
export async function up(runner: IMigrationRunner, ctx: SeedContext) {
|
|
332
|
+
const users = ctx.data as any[]; // Data loaded from database/seed/data/user.json
|
|
333
|
+
|
|
334
|
+
for (const user of users) {
|
|
335
|
+
await runner.query("INSERT INTO users (email, name) VALUES (?, ?)", [user.email, user.name]);
|
|
336
|
+
ctx.log?.(`Created user ${user.email}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export async function down(runner: IMigrationRunner, ctx: SeedContext) {
|
|
341
|
+
const users = ctx.data as any[];
|
|
342
|
+
for (const user of users) {
|
|
343
|
+
await runner.query("DELETE FROM users WHERE email = ?", [user.email]);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Programmatic API
|
|
349
|
+
|
|
350
|
+
Run seeds from your code (great for tests):
|
|
351
|
+
|
|
352
|
+
```ts
|
|
353
|
+
import { MigrationRunnerFactory, runSeedsWithRunner, loadMigrationConfig } from "@noego/proper";
|
|
354
|
+
|
|
355
|
+
// Load config
|
|
356
|
+
const config = loadMigrationConfig("proper.json");
|
|
357
|
+
const runner = await MigrationRunnerFactory.create("proper.json");
|
|
358
|
+
|
|
359
|
+
// Run seeds
|
|
360
|
+
await runSeedsWithRunner(runner, config, 'up', {
|
|
361
|
+
names: ['muscles', 'exercises'], // Optional: override list
|
|
362
|
+
validate: true, // Optional: validate JSON data against schema
|
|
363
|
+
});
|
|
364
|
+
```
|
|
156
365
|
|
|
157
366
|
---
|
|
158
367
|
|
|
@@ -199,19 +408,66 @@ SQL_PASSWORD=supersecret proper up --config proper.json
|
|
|
199
408
|
|
|
200
409
|
---
|
|
201
410
|
|
|
202
|
-
##
|
|
411
|
+
## Project Layout (for contributors)
|
|
412
|
+
|
|
413
|
+
This is a small, framework-style project. The main pieces are:
|
|
414
|
+
|
|
415
|
+
- `cli.ts`: CLI entrypoint (compiled to `bin/cli.js` and published as the `proper` binary).
|
|
416
|
+
- `index.ts`: Library entrypoint, exporting `MigrationRunnerFactory`, `MigrationConfig`, and `MigrationRunner`.
|
|
417
|
+
- `framework/`: Core framework classes:
|
|
418
|
+
- `MigrationCLI.ts`: CLI parser and command wiring.
|
|
419
|
+
- `MigrationRunner.ts`: main migration runner + factory.
|
|
420
|
+
- `MigrationConfig.ts`: config reading/validation.
|
|
421
|
+
- `MigrationDirectoryReader.ts`: migration discovery from the filesystem.
|
|
422
|
+
- `MigrationStatus.ts`, `MigrationNode.ts`, `MigrationFilter.ts`: migration model, status helpers, and filtering logic.
|
|
423
|
+
- `SQLRunner.ts`: MySQL and SQLite runner implementations.
|
|
424
|
+
- `errors.ts`: domain-specific error types (used heavily by CLI and tests).
|
|
425
|
+
- `framework/lib/runner.ts`: re-exports low-level SQL runners for consumers (`@noego/proper/runners`).
|
|
426
|
+
- `helper/`: small utilities (e.g. string helpers).
|
|
427
|
+
- `tests/`: Jest-based unit tests for the framework and CLI.
|
|
428
|
+
- `example/`: runnable MySQL + migrations example (with `docker-compose` and `Makefile`).
|
|
429
|
+
|
|
430
|
+
Build artifacts are written to:
|
|
431
|
+
|
|
432
|
+
- `bin/`: compiled CLI and public library entrypoints.
|
|
433
|
+
- `lib/`: compiled runner exports (`runners` subpath).
|
|
434
|
+
|
|
435
|
+
The package’s `"files"` array in `package.json` ensures only `bin/`, `lib/`, and `readme.md` are published.
|
|
436
|
+
|
|
437
|
+
---
|
|
438
|
+
|
|
439
|
+
## Development & Contributing
|
|
440
|
+
|
|
441
|
+
Requirements:
|
|
442
|
+
|
|
443
|
+
- Node.js 16+ (LTS recommended)
|
|
444
|
+
- npm (or a compatible package manager)
|
|
203
445
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
-
|
|
207
|
-
-
|
|
446
|
+
Local workflow:
|
|
447
|
+
|
|
448
|
+
- Install dependencies: `npm install`
|
|
449
|
+
- Run tests: `npm test`
|
|
450
|
+
- Build the package: `npm run build` (emits to `bin/` and `lib/` via `tsup`)
|
|
208
451
|
- Run CLI from source (TypeScript): `npx tsx cli.ts --config sqlite/proper.json up`
|
|
209
452
|
|
|
453
|
+
Test tooling:
|
|
454
|
+
|
|
455
|
+
- Jest with `ts-jest` and `jest-extended` (see `jest.config.ts`).
|
|
456
|
+
- Core framework tests in `tests/unit/framework`.
|
|
457
|
+
- CLI tests in `tests/unit/cli.test.ts`.
|
|
458
|
+
|
|
210
459
|
Debugging in VS Code:
|
|
460
|
+
|
|
211
461
|
- Use the provided `.vscode/launch.json`:
|
|
212
|
-
- “Jest: Current File” runs the open test file
|
|
213
|
-
- “Jest: All Tests” runs the full suite
|
|
214
|
-
- “Jest: CLI Test File” runs only `tests/unit/cli.test.ts
|
|
462
|
+
- “Jest: Current File” runs the open test file.
|
|
463
|
+
- “Jest: All Tests” runs the full suite.
|
|
464
|
+
- “Jest: CLI Test File” runs only `tests/unit/cli.test.ts`.
|
|
465
|
+
|
|
466
|
+
Contribution guidelines (informal for now):
|
|
467
|
+
|
|
468
|
+
- Keep the public API surface (`index.ts`, `framework/lib/runner.ts`, CLI flags) backward compatible where possible.
|
|
469
|
+
- Add or update tests in `tests/unit` when changing framework or CLI behavior.
|
|
470
|
+
- Prefer small, focused abstractions in `framework/` over adding logic in the CLI.
|
|
215
471
|
|
|
216
472
|
---
|
|
217
473
|
|