postgresdk 0.1.2-alpha.4 → 0.2.0
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 +143 -42
- package/dist/cli-init.d.ts +2 -0
- package/dist/cli-pull.d.ts +1 -0
- package/dist/cli.js +355 -19
- package/dist/emit-sdk-bundle.d.ts +8 -0
- package/dist/index.js +66 -0
- package/dist/types.d.ts +6 -0
- package/package.json +5 -2
package/README.md
CHANGED
@@ -35,7 +35,7 @@ CREATE TABLE book_tags (
|
|
35
35
|
Run one command:
|
36
36
|
|
37
37
|
```bash
|
38
|
-
npx postgresdk
|
38
|
+
npx postgresdk generate
|
39
39
|
```
|
40
40
|
|
41
41
|
Get a complete, type-safe SDK with:
|
@@ -140,36 +140,43 @@ All from your existing database schema. No manual coding required.
|
|
140
140
|
- 🔐 **Built-in Auth** - API key and JWT authentication with zero configuration
|
141
141
|
- 🎯 **Zero Config** - Works out of the box with sensible defaults
|
142
142
|
- 🏗️ **Framework Ready** - Server routes built for Hono, client SDK works anywhere
|
143
|
-
- 📦 **Lightweight** - Minimal dependencies, optimized
|
143
|
+
- 📦 **Lightweight** - Minimal dependencies, optimized bundle size with shared BaseClient
|
144
|
+
- 🔄 **SDK Distribution** - Built-in SDK bundling and pull mechanism for easy client distribution
|
144
145
|
|
145
146
|
## Installation
|
146
147
|
|
147
148
|
```bash
|
148
149
|
npm install -g postgresdk
|
149
150
|
# or
|
150
|
-
npx postgresdk
|
151
|
+
npx postgresdk generate
|
151
152
|
```
|
152
153
|
|
153
154
|
## Quick Start
|
154
155
|
|
155
|
-
1.
|
156
|
+
1. Initialize your project:
|
157
|
+
|
158
|
+
```bash
|
159
|
+
npx postgresdk init
|
160
|
+
```
|
161
|
+
|
162
|
+
This creates a `postgresdk.config.ts` file with all available options documented.
|
163
|
+
|
164
|
+
2. Edit the configuration file with your database connection:
|
156
165
|
|
157
166
|
```typescript
|
158
167
|
export default {
|
159
|
-
connectionString: "postgres://user:pass@localhost:5432/mydb",
|
160
|
-
|
161
|
-
outServer: "./generated/server",
|
162
|
-
outClient: "./generated/client",
|
168
|
+
connectionString: process.env.DATABASE_URL || "postgres://user:pass@localhost:5432/mydb",
|
169
|
+
// Uncomment and customize other options as needed
|
163
170
|
};
|
164
171
|
```
|
165
172
|
|
166
|
-
|
173
|
+
3. Run the generator:
|
167
174
|
|
168
175
|
```bash
|
169
|
-
postgresdk
|
176
|
+
postgresdk generate
|
170
177
|
```
|
171
178
|
|
172
|
-
|
179
|
+
4. Use the generated SDK:
|
173
180
|
|
174
181
|
```typescript
|
175
182
|
// Server (Hono)
|
@@ -212,19 +219,19 @@ export default {
|
|
212
219
|
includeDepthLimit: 3, // Max depth for nested includes
|
213
220
|
dateType: "date", // "date" | "string" - How to handle timestamps
|
214
221
|
|
215
|
-
// Authentication (optional)
|
222
|
+
// Authentication (optional) - simplified syntax
|
216
223
|
auth: {
|
224
|
+
apiKey: process.env.API_KEY, // Simple API key auth
|
225
|
+
// OR
|
226
|
+
jwt: process.env.JWT_SECRET, // Simple JWT auth
|
227
|
+
// OR full syntax for advanced options:
|
217
228
|
strategy: "none" | "api-key" | "jwt-hs256", // Default: "none"
|
218
|
-
|
219
|
-
//
|
220
|
-
apiKeyHeader: "x-api-key", // Header name for API key
|
221
|
-
apiKeys: ["key1", "key2"], // Array of valid keys
|
222
|
-
|
223
|
-
// For JWT auth (HS256)
|
229
|
+
apiKeyHeader: "x-api-key", // Custom header name
|
230
|
+
apiKeys: ["key1", "key2"], // Multiple valid keys
|
224
231
|
jwt: {
|
225
|
-
sharedSecret: "your-secret",
|
226
|
-
issuer: "your-app",
|
227
|
-
audience: "your-audience"
|
232
|
+
sharedSecret: "your-secret", // Shared secret for HS256
|
233
|
+
issuer: "your-app", // Optional: validate issuer claim
|
234
|
+
audience: "your-audience" // Optional: validate audience claim
|
228
235
|
}
|
229
236
|
}
|
230
237
|
};
|
@@ -348,11 +355,11 @@ export default {
|
|
348
355
|
### API Key Authentication
|
349
356
|
|
350
357
|
```typescript
|
351
|
-
// postgresdk.config.ts -
|
358
|
+
// postgresdk.config.ts - Simple syntax (recommended)
|
352
359
|
export default {
|
353
360
|
connectionString: "...",
|
354
361
|
auth: {
|
355
|
-
apiKey:
|
362
|
+
apiKey: process.env.API_KEY // Single key shorthand
|
356
363
|
}
|
357
364
|
};
|
358
365
|
|
@@ -360,50 +367,45 @@ export default {
|
|
360
367
|
export default {
|
361
368
|
connectionString: "...",
|
362
369
|
auth: {
|
363
|
-
apiKeys: [
|
370
|
+
apiKeys: [process.env.API_KEY_1, process.env.API_KEY_2]
|
364
371
|
}
|
365
372
|
};
|
366
373
|
|
367
|
-
//
|
374
|
+
// Full syntax with custom header (optional)
|
368
375
|
export default {
|
369
376
|
connectionString: "...",
|
370
377
|
auth: {
|
371
378
|
strategy: "api-key",
|
372
|
-
apiKeyHeader: "x-
|
373
|
-
apiKeys: [
|
374
|
-
"your-api-key-1",
|
375
|
-
"your-api-key-2",
|
376
|
-
// Can also use environment variables
|
377
|
-
"env:API_KEYS" // Reads comma-separated keys from process.env.API_KEYS
|
378
|
-
]
|
379
|
+
apiKeyHeader: "x-custom-key", // Default: "x-api-key"
|
380
|
+
apiKeys: ["key1", "key2"]
|
379
381
|
}
|
380
382
|
};
|
381
383
|
|
382
384
|
// Client SDK usage
|
383
385
|
const sdk = new SDK({
|
384
386
|
baseUrl: "http://localhost:3000",
|
385
|
-
auth: { apiKey:
|
387
|
+
auth: { apiKey: process.env.API_KEY }
|
386
388
|
});
|
387
389
|
```
|
388
390
|
|
389
391
|
### JWT Authentication (HS256)
|
390
392
|
|
391
393
|
```typescript
|
392
|
-
// postgresdk.config.ts -
|
394
|
+
// postgresdk.config.ts - Simple syntax (recommended)
|
393
395
|
export default {
|
394
396
|
connectionString: "...",
|
395
397
|
auth: {
|
396
|
-
jwt:
|
398
|
+
jwt: process.env.JWT_SECRET // Shared secret shorthand
|
397
399
|
}
|
398
400
|
};
|
399
401
|
|
400
|
-
//
|
402
|
+
// Full syntax with issuer/audience validation (optional)
|
401
403
|
export default {
|
402
404
|
connectionString: "...",
|
403
405
|
auth: {
|
404
406
|
strategy: "jwt-hs256",
|
405
407
|
jwt: {
|
406
|
-
sharedSecret: process.env.JWT_SECRET
|
408
|
+
sharedSecret: process.env.JWT_SECRET,
|
407
409
|
issuer: "my-app", // Optional: validates 'iss' claim
|
408
410
|
audience: "my-users" // Optional: validates 'aud' claim
|
409
411
|
}
|
@@ -772,15 +774,114 @@ process.on("SIGTERM", async () => {
|
|
772
774
|
});
|
773
775
|
```
|
774
776
|
|
775
|
-
##
|
777
|
+
## SDK Distribution
|
778
|
+
|
779
|
+
postgresdk makes it easy to distribute your generated SDK to client applications. When you generate your SDK, it's automatically bundled and served through your API, allowing client apps to pull the latest SDK directly.
|
780
|
+
|
781
|
+
### Publishing Your SDK
|
782
|
+
|
783
|
+
When you run `postgresdk generate`, the SDK is automatically:
|
784
|
+
1. Generated as TypeScript files for both server and client
|
785
|
+
2. Bundled and made available through API endpoints
|
786
|
+
3. Ready to be pulled by client applications
|
787
|
+
|
788
|
+
Your API automatically serves the SDK at these endpoints:
|
789
|
+
- `GET /sdk/manifest` - SDK metadata and file list
|
790
|
+
- `GET /sdk/download` - Download all SDK files as JSON
|
791
|
+
- `GET /sdk/files/:path` - Download individual SDK files
|
792
|
+
|
793
|
+
### Pulling the SDK in Client Apps
|
794
|
+
|
795
|
+
Client applications can pull your SDK using the `postgresdk pull` command:
|
796
|
+
|
797
|
+
```bash
|
798
|
+
# Install postgresdk in your client app
|
799
|
+
npm install -D postgresdk
|
800
|
+
|
801
|
+
# Pull the SDK from your API
|
802
|
+
postgresdk pull --from=https://api.myapp.com --output=./src/sdk
|
803
|
+
|
804
|
+
# Or with authentication
|
805
|
+
postgresdk pull --from=https://api.myapp.com --output=./src/sdk --token=your-token
|
806
|
+
```
|
807
|
+
|
808
|
+
### Configuration-based Pull
|
809
|
+
|
810
|
+
Create a `postgresdk.config.ts` in your client app:
|
811
|
+
|
812
|
+
```typescript
|
813
|
+
export default {
|
814
|
+
pull: {
|
815
|
+
from: "https://api.myapp.com",
|
816
|
+
output: "./src/sdk",
|
817
|
+
token: process.env.API_TOKEN // Optional auth
|
818
|
+
}
|
819
|
+
};
|
820
|
+
```
|
821
|
+
|
822
|
+
Then simply run:
|
823
|
+
```bash
|
824
|
+
postgresdk pull
|
825
|
+
```
|
826
|
+
|
827
|
+
### Automated SDK Updates
|
828
|
+
|
829
|
+
You can automate SDK updates in your client app's build process:
|
830
|
+
|
831
|
+
```json
|
832
|
+
// package.json
|
833
|
+
{
|
834
|
+
"scripts": {
|
835
|
+
"prebuild": "postgresdk pull",
|
836
|
+
"build": "tsc"
|
837
|
+
}
|
838
|
+
}
|
839
|
+
```
|
840
|
+
|
841
|
+
### SDK Versioning
|
842
|
+
|
843
|
+
The pulled SDK includes metadata about when it was generated and from where:
|
844
|
+
|
845
|
+
```typescript
|
846
|
+
// .postgresdk.json (auto-generated)
|
847
|
+
{
|
848
|
+
"version": "1.0.0",
|
849
|
+
"generated": "2024-01-15T10:30:00Z",
|
850
|
+
"pulledFrom": "https://api.myapp.com",
|
851
|
+
"pulledAt": "2024-01-15T11:00:00Z"
|
852
|
+
}
|
853
|
+
```
|
854
|
+
|
855
|
+
## CLI Commands
|
776
856
|
|
777
857
|
```bash
|
778
|
-
postgresdk [options]
|
858
|
+
postgresdk <command> [options]
|
859
|
+
|
860
|
+
Commands:
|
861
|
+
init Create a postgresdk.config.ts file with all options
|
862
|
+
generate Generate SDK from database
|
863
|
+
pull Pull SDK from API endpoint
|
864
|
+
version Show version
|
865
|
+
help Show help
|
779
866
|
|
780
|
-
Options:
|
867
|
+
Init Options:
|
868
|
+
(no options) Creates postgresdk.config.ts in current directory
|
869
|
+
|
870
|
+
Generate Options:
|
781
871
|
-c, --config <path> Path to config file (default: postgresdk.config.ts)
|
782
|
-
|
783
|
-
|
872
|
+
|
873
|
+
Pull Options:
|
874
|
+
--from <url> API URL to pull SDK from
|
875
|
+
--output <path> Output directory (default: ./src/sdk)
|
876
|
+
--token <token> Authentication token
|
877
|
+
-c, --config <path> Path to config file with pull settings
|
878
|
+
|
879
|
+
Examples:
|
880
|
+
postgresdk init # Create config file
|
881
|
+
postgresdk generate # Generate using default config
|
882
|
+
postgresdk generate -c custom.config.ts
|
883
|
+
postgresdk pull --from=https://api.com --output=./src/sdk
|
884
|
+
postgresdk pull -c client.config.ts # Pull using config file
|
784
885
|
```
|
785
886
|
|
786
887
|
## How It Works
|
@@ -0,0 +1 @@
|
|
1
|
+
export declare function pullCommand(args: string[]): Promise<void>;
|
package/dist/cli.js
CHANGED
@@ -17,6 +17,16 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
17
17
|
return to;
|
18
18
|
};
|
19
19
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
20
|
+
var __export = (target, all) => {
|
21
|
+
for (var name in all)
|
22
|
+
__defProp(target, name, {
|
23
|
+
get: all[name],
|
24
|
+
enumerable: true,
|
25
|
+
configurable: true,
|
26
|
+
set: (newValue) => all[name] = () => newValue
|
27
|
+
});
|
28
|
+
};
|
29
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
20
30
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
21
31
|
|
22
32
|
// node_modules/dotenv/package.json
|
@@ -460,6 +470,242 @@ var require_config = __commonJS(() => {
|
|
460
470
|
})();
|
461
471
|
});
|
462
472
|
|
473
|
+
// src/cli-init.ts
|
474
|
+
var exports_cli_init = {};
|
475
|
+
__export(exports_cli_init, {
|
476
|
+
initCommand: () => initCommand
|
477
|
+
});
|
478
|
+
import { existsSync, writeFileSync } from "fs";
|
479
|
+
import { resolve } from "path";
|
480
|
+
async function initCommand(args) {
|
481
|
+
console.log(`\uD83D\uDE80 Initializing postgresdk configuration...
|
482
|
+
`);
|
483
|
+
const configPath = resolve(process.cwd(), "postgresdk.config.ts");
|
484
|
+
if (existsSync(configPath)) {
|
485
|
+
console.error("❌ Error: postgresdk.config.ts already exists");
|
486
|
+
console.log(" To reinitialize, please remove or rename the existing file first.");
|
487
|
+
process.exit(1);
|
488
|
+
}
|
489
|
+
const envPath = resolve(process.cwd(), ".env");
|
490
|
+
const hasEnv = existsSync(envPath);
|
491
|
+
try {
|
492
|
+
writeFileSync(configPath, CONFIG_TEMPLATE, "utf-8");
|
493
|
+
console.log("✅ Created postgresdk.config.ts");
|
494
|
+
console.log(`
|
495
|
+
\uD83D\uDCDD Next steps:`);
|
496
|
+
console.log(" 1. Edit postgresdk.config.ts with your database connection");
|
497
|
+
if (!hasEnv) {
|
498
|
+
console.log(" 2. Consider creating a .env file for sensitive values:");
|
499
|
+
console.log(" DATABASE_URL=postgres://user:pass@localhost:5432/mydb");
|
500
|
+
console.log(" API_KEY=your-secret-key");
|
501
|
+
console.log(" JWT_SECRET=your-jwt-secret");
|
502
|
+
}
|
503
|
+
console.log(" 3. Run 'postgresdk generate' to create your SDK");
|
504
|
+
console.log(`
|
505
|
+
\uD83D\uDCA1 Tip: The config file has detailed comments for all options.`);
|
506
|
+
console.log(" Uncomment the options you want to customize.");
|
507
|
+
} catch (error) {
|
508
|
+
console.error("❌ Error creating config file:", error);
|
509
|
+
process.exit(1);
|
510
|
+
}
|
511
|
+
}
|
512
|
+
var CONFIG_TEMPLATE = `/**
|
513
|
+
* PostgreSDK Configuration
|
514
|
+
*
|
515
|
+
* This file configures how postgresdk generates your SDK.
|
516
|
+
* Environment variables are automatically loaded from .env files.
|
517
|
+
*/
|
518
|
+
|
519
|
+
export default {
|
520
|
+
// ========== DATABASE CONNECTION (Required) ==========
|
521
|
+
|
522
|
+
/**
|
523
|
+
* PostgreSQL connection string
|
524
|
+
* Format: postgres://user:password@host:port/database
|
525
|
+
*/
|
526
|
+
connectionString: process.env.DATABASE_URL || "postgres://user:password@localhost:5432/mydb",
|
527
|
+
|
528
|
+
// ========== BASIC OPTIONS ==========
|
529
|
+
|
530
|
+
/**
|
531
|
+
* Database schema to introspect
|
532
|
+
* @default "public"
|
533
|
+
*/
|
534
|
+
// schema: "public",
|
535
|
+
|
536
|
+
/**
|
537
|
+
* Output directory for server-side code (routes, validators, etc.)
|
538
|
+
* @default "./generated/server"
|
539
|
+
*/
|
540
|
+
// outServer: "./generated/server",
|
541
|
+
|
542
|
+
/**
|
543
|
+
* Output directory for client SDK
|
544
|
+
* @default "./generated/client"
|
545
|
+
*/
|
546
|
+
// outClient: "./generated/client",
|
547
|
+
|
548
|
+
// ========== ADVANCED OPTIONS ==========
|
549
|
+
|
550
|
+
/**
|
551
|
+
* Column name for soft deletes. When set, DELETE operations will update
|
552
|
+
* this column instead of removing rows.
|
553
|
+
* @default null (hard deletes)
|
554
|
+
* @example "deleted_at"
|
555
|
+
*/
|
556
|
+
// softDeleteColumn: null,
|
557
|
+
|
558
|
+
/**
|
559
|
+
* Maximum depth for nested relationship includes to prevent infinite loops
|
560
|
+
* @default 3
|
561
|
+
*/
|
562
|
+
// includeDepthLimit: 3,
|
563
|
+
|
564
|
+
/**
|
565
|
+
* How to handle date/timestamp columns in TypeScript
|
566
|
+
* - "date": Use JavaScript Date objects
|
567
|
+
* - "string": Use ISO 8601 strings
|
568
|
+
* @default "date"
|
569
|
+
*/
|
570
|
+
// dateType: "date",
|
571
|
+
|
572
|
+
// ========== AUTHENTICATION ==========
|
573
|
+
|
574
|
+
/**
|
575
|
+
* Authentication configuration for your API
|
576
|
+
*
|
577
|
+
* Simple syntax examples:
|
578
|
+
* auth: { apiKey: process.env.API_KEY }
|
579
|
+
* auth: { jwt: process.env.JWT_SECRET }
|
580
|
+
*
|
581
|
+
* Multiple API keys:
|
582
|
+
* auth: { apiKeys: [process.env.KEY1, process.env.KEY2] }
|
583
|
+
*
|
584
|
+
* Full syntax for advanced options:
|
585
|
+
*/
|
586
|
+
// auth: {
|
587
|
+
// // Strategy: "none" | "api-key" | "jwt-hs256"
|
588
|
+
// strategy: "none",
|
589
|
+
//
|
590
|
+
// // For API Key authentication
|
591
|
+
// apiKeyHeader: "x-api-key", // Header name for API key
|
592
|
+
// apiKeys: [ // List of valid API keys
|
593
|
+
// process.env.API_KEY_1,
|
594
|
+
// process.env.API_KEY_2,
|
595
|
+
// ],
|
596
|
+
//
|
597
|
+
// // For JWT (HS256) authentication
|
598
|
+
// jwt: {
|
599
|
+
// sharedSecret: process.env.JWT_SECRET, // Secret for signing/verifying
|
600
|
+
// issuer: "my-app", // Optional: validate 'iss' claim
|
601
|
+
// audience: "my-users", // Optional: validate 'aud' claim
|
602
|
+
// }
|
603
|
+
// },
|
604
|
+
|
605
|
+
// ========== SDK DISTRIBUTION (Pull Configuration) ==========
|
606
|
+
|
607
|
+
/**
|
608
|
+
* Configuration for pulling SDK from a remote API
|
609
|
+
* Used when running 'postgresdk pull' command
|
610
|
+
*/
|
611
|
+
// pull: {
|
612
|
+
// from: "https://api.myapp.com", // API URL to pull SDK from
|
613
|
+
// output: "./src/sdk", // Local directory for pulled SDK
|
614
|
+
// token: process.env.API_TOKEN, // Optional authentication token
|
615
|
+
// },
|
616
|
+
};
|
617
|
+
`;
|
618
|
+
var init_cli_init = () => {};
|
619
|
+
|
620
|
+
// src/cli-pull.ts
|
621
|
+
var exports_cli_pull = {};
|
622
|
+
__export(exports_cli_pull, {
|
623
|
+
pullCommand: () => pullCommand
|
624
|
+
});
|
625
|
+
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
626
|
+
import { join as join2, dirname as dirname2, resolve as resolve2 } from "path";
|
627
|
+
import { existsSync as existsSync2 } from "fs";
|
628
|
+
import { pathToFileURL as pathToFileURL2 } from "url";
|
629
|
+
async function pullCommand(args) {
|
630
|
+
let configPath = "postgresdk.config.ts";
|
631
|
+
const configIndex = args.findIndex((a) => a === "-c" || a === "--config");
|
632
|
+
if (configIndex !== -1 && args[configIndex + 1]) {
|
633
|
+
configPath = args[configIndex + 1];
|
634
|
+
}
|
635
|
+
let fileConfig = {};
|
636
|
+
const fullConfigPath = resolve2(process.cwd(), configPath);
|
637
|
+
if (existsSync2(fullConfigPath)) {
|
638
|
+
console.log(`\uD83D\uDCCB Loading ${configPath}`);
|
639
|
+
try {
|
640
|
+
const configUrl = pathToFileURL2(fullConfigPath).href;
|
641
|
+
const module = await import(configUrl);
|
642
|
+
const config2 = module.default || module;
|
643
|
+
if (config2.pull) {
|
644
|
+
fileConfig = config2.pull;
|
645
|
+
}
|
646
|
+
} catch (err) {
|
647
|
+
console.error("⚠️ Failed to load config file:", err);
|
648
|
+
}
|
649
|
+
}
|
650
|
+
const cliConfig = {
|
651
|
+
from: args.find((a) => a.startsWith("--from="))?.split("=")[1],
|
652
|
+
output: args.find((a) => a.startsWith("--output="))?.split("=")[1],
|
653
|
+
token: args.find((a) => a.startsWith("--token="))?.split("=")[1]
|
654
|
+
};
|
655
|
+
const config = {
|
656
|
+
output: "./src/sdk",
|
657
|
+
...fileConfig,
|
658
|
+
...Object.fromEntries(Object.entries(cliConfig).filter(([_, v]) => v !== undefined))
|
659
|
+
};
|
660
|
+
if (!config.from) {
|
661
|
+
console.error("❌ Missing API URL. Specify via --from or in postgresdk.config.ts");
|
662
|
+
console.error(`
|
663
|
+
Example config file:`);
|
664
|
+
console.error(`export default {
|
665
|
+
pull: {
|
666
|
+
from: "https://api.company.com",
|
667
|
+
output: "./src/sdk"
|
668
|
+
}
|
669
|
+
}`);
|
670
|
+
process.exit(1);
|
671
|
+
}
|
672
|
+
console.log(`\uD83D\uDD04 Pulling SDK from ${config.from}`);
|
673
|
+
console.log(`\uD83D\uDCC1 Output directory: ${config.output}`);
|
674
|
+
try {
|
675
|
+
const headers = config.token ? { Authorization: `Bearer ${config.token}` } : {};
|
676
|
+
const manifestRes = await fetch(`${config.from}/sdk/manifest`, { headers });
|
677
|
+
if (!manifestRes.ok) {
|
678
|
+
throw new Error(`Failed to fetch SDK manifest: ${manifestRes.status} ${manifestRes.statusText}`);
|
679
|
+
}
|
680
|
+
const manifest = await manifestRes.json();
|
681
|
+
console.log(`\uD83D\uDCE6 SDK version: ${manifest.version}`);
|
682
|
+
console.log(`\uD83D\uDCC5 Generated: ${manifest.generated}`);
|
683
|
+
console.log(`\uD83D\uDCC4 Files: ${manifest.files.length}`);
|
684
|
+
const sdkRes = await fetch(`${config.from}/sdk/download`, { headers });
|
685
|
+
if (!sdkRes.ok) {
|
686
|
+
throw new Error(`Failed to download SDK: ${sdkRes.status} ${sdkRes.statusText}`);
|
687
|
+
}
|
688
|
+
const sdk = await sdkRes.json();
|
689
|
+
for (const [path, content] of Object.entries(sdk.files)) {
|
690
|
+
const fullPath = join2(config.output, path);
|
691
|
+
await mkdir2(dirname2(fullPath), { recursive: true });
|
692
|
+
await writeFile2(fullPath, content, "utf-8");
|
693
|
+
console.log(` ✓ ${path}`);
|
694
|
+
}
|
695
|
+
await writeFile2(join2(config.output, ".postgresdk.json"), JSON.stringify({
|
696
|
+
version: sdk.version,
|
697
|
+
generated: sdk.generated,
|
698
|
+
pulledFrom: config.from,
|
699
|
+
pulledAt: new Date().toISOString()
|
700
|
+
}, null, 2));
|
701
|
+
console.log(`✅ SDK pulled successfully to ${config.output}`);
|
702
|
+
} catch (err) {
|
703
|
+
console.error(`❌ Pull failed:`, err);
|
704
|
+
process.exit(1);
|
705
|
+
}
|
706
|
+
}
|
707
|
+
var init_cli_pull = () => {};
|
708
|
+
|
463
709
|
// src/index.ts
|
464
710
|
var import_config = __toESM(require_config(), 1);
|
465
711
|
import { join } from "node:path";
|
@@ -1748,6 +1994,7 @@ function emitRouter(tables, hasAuth) {
|
|
1748
1994
|
`);
|
1749
1995
|
return `/* Generated. Do not edit. */
|
1750
1996
|
import { Hono } from "hono";
|
1997
|
+
import { SDK_MANIFEST } from "./sdk-bundle";
|
1751
1998
|
${imports}
|
1752
1999
|
${hasAuth ? `export { authMiddleware } from "./auth";` : ""}
|
1753
2000
|
|
@@ -1775,7 +2022,34 @@ export function createRouter(
|
|
1775
2022
|
deps: { pg: { query: (text: string, params?: any[]) => Promise<{ rows: any[] }> } }
|
1776
2023
|
): Hono {
|
1777
2024
|
const router = new Hono();
|
2025
|
+
|
2026
|
+
// Register table routes
|
1778
2027
|
${registrations}
|
2028
|
+
|
2029
|
+
// SDK distribution endpoints
|
2030
|
+
router.get("/sdk/manifest", (c) => {
|
2031
|
+
return c.json({
|
2032
|
+
version: SDK_MANIFEST.version,
|
2033
|
+
generated: SDK_MANIFEST.generated,
|
2034
|
+
files: Object.keys(SDK_MANIFEST.files)
|
2035
|
+
});
|
2036
|
+
});
|
2037
|
+
|
2038
|
+
router.get("/sdk/download", (c) => {
|
2039
|
+
return c.json(SDK_MANIFEST);
|
2040
|
+
});
|
2041
|
+
|
2042
|
+
router.get("/sdk/files/:path{.*}", (c) => {
|
2043
|
+
const path = c.req.param("path");
|
2044
|
+
const content = SDK_MANIFEST.files[path];
|
2045
|
+
if (!content) {
|
2046
|
+
return c.text("File not found", 404);
|
2047
|
+
}
|
2048
|
+
return c.text(content, 200, {
|
2049
|
+
"Content-Type": "text/plain; charset=utf-8"
|
2050
|
+
});
|
2051
|
+
});
|
2052
|
+
|
1779
2053
|
return router;
|
1780
2054
|
}
|
1781
2055
|
|
@@ -1809,6 +2083,29 @@ export * from "./include-spec";
|
|
1809
2083
|
`;
|
1810
2084
|
}
|
1811
2085
|
|
2086
|
+
// src/emit-sdk-bundle.ts
|
2087
|
+
function emitSdkBundle(clientFiles) {
|
2088
|
+
const files = {};
|
2089
|
+
for (const file of clientFiles) {
|
2090
|
+
const parts = file.path.split("/");
|
2091
|
+
const clientIndex = parts.lastIndexOf("client");
|
2092
|
+
if (clientIndex >= 0 && clientIndex < parts.length - 1) {
|
2093
|
+
const relativePath = parts.slice(clientIndex + 1).join("/");
|
2094
|
+
files[relativePath] = file.content;
|
2095
|
+
}
|
2096
|
+
}
|
2097
|
+
const version = `1.0.0`;
|
2098
|
+
const generated = new Date().toISOString();
|
2099
|
+
return `/* Generated. Do not edit. */
|
2100
|
+
|
2101
|
+
export const SDK_MANIFEST = {
|
2102
|
+
version: "${version}",
|
2103
|
+
generated: "${generated}",
|
2104
|
+
files: ${JSON.stringify(files, null, 2)}
|
2105
|
+
};
|
2106
|
+
`;
|
2107
|
+
}
|
2108
|
+
|
1812
2109
|
// src/types.ts
|
1813
2110
|
function normalizeAuthConfig(input) {
|
1814
2111
|
if (!input)
|
@@ -1915,6 +2212,11 @@ async function generate(configPath) {
|
|
1915
2212
|
path: join(serverDir, "router.ts"),
|
1916
2213
|
content: emitRouter(Object.values(model.tables), !!normalizedAuth?.strategy && normalizedAuth.strategy !== "none")
|
1917
2214
|
});
|
2215
|
+
const clientFiles = files.filter((f) => f.path.includes(clientDir));
|
2216
|
+
files.push({
|
2217
|
+
path: join(serverDir, "sdk-bundle.ts"),
|
2218
|
+
content: emitSdkBundle(clientFiles)
|
2219
|
+
});
|
1918
2220
|
console.log("✍️ Writing files...");
|
1919
2221
|
await writeFiles(files);
|
1920
2222
|
console.log(`✅ Generated ${files.length} files`);
|
@@ -1924,41 +2226,75 @@ async function generate(configPath) {
|
|
1924
2226
|
|
1925
2227
|
// src/cli.ts
|
1926
2228
|
var import_config2 = __toESM(require_config(), 1);
|
1927
|
-
import { resolve } from "node:path";
|
2229
|
+
import { resolve as resolve3 } from "node:path";
|
1928
2230
|
import { readFileSync } from "node:fs";
|
1929
2231
|
import { fileURLToPath } from "node:url";
|
1930
|
-
import { dirname as
|
2232
|
+
import { dirname as dirname3, join as join3 } from "node:path";
|
1931
2233
|
var __filename2 = fileURLToPath(import.meta.url);
|
1932
|
-
var __dirname2 =
|
1933
|
-
var packageJson = JSON.parse(readFileSync(
|
2234
|
+
var __dirname2 = dirname3(__filename2);
|
2235
|
+
var packageJson = JSON.parse(readFileSync(join3(__dirname2, "../package.json"), "utf-8"));
|
1934
2236
|
var VERSION = packageJson.version;
|
1935
2237
|
var args = process.argv.slice(2);
|
1936
|
-
|
2238
|
+
var command = args[0];
|
2239
|
+
if (args.includes("--version") || args.includes("-v") || command === "version") {
|
1937
2240
|
console.log(`postgresdk v${VERSION}`);
|
1938
2241
|
process.exit(0);
|
1939
2242
|
}
|
1940
|
-
if (args.includes("--help") || args.includes("-h")) {
|
2243
|
+
if (args.includes("--help") || args.includes("-h") || command === "help" || !command) {
|
1941
2244
|
console.log(`
|
1942
2245
|
postgresdk - Generate typed SDK from PostgreSQL
|
1943
2246
|
|
1944
2247
|
Usage:
|
1945
|
-
postgresdk [options]
|
2248
|
+
postgresdk <command> [options]
|
2249
|
+
|
2250
|
+
Commands:
|
2251
|
+
init Create a postgresdk.config.ts file
|
2252
|
+
generate Generate SDK from database
|
2253
|
+
pull Pull SDK from API endpoint
|
2254
|
+
version Show version
|
2255
|
+
help Show help
|
2256
|
+
|
2257
|
+
Init Options:
|
2258
|
+
(no options)
|
1946
2259
|
|
1947
|
-
Options:
|
2260
|
+
Generate Options:
|
1948
2261
|
-c, --config <path> Path to config file (default: postgresdk.config.ts)
|
1949
|
-
|
1950
|
-
|
2262
|
+
|
2263
|
+
Pull Options:
|
2264
|
+
--from <url> API URL to pull SDK from
|
2265
|
+
--output <path> Output directory (default: ./src/sdk)
|
2266
|
+
--token <token> Authentication token
|
2267
|
+
-c, --config <path> Path to config file with pull settings
|
2268
|
+
|
2269
|
+
Examples:
|
2270
|
+
postgresdk init # Create config file
|
2271
|
+
postgresdk generate # Generate using postgresdk.config.ts
|
2272
|
+
postgresdk generate -c custom.config.ts
|
2273
|
+
postgresdk pull --from=https://api.com --output=./src/sdk
|
2274
|
+
postgresdk pull # Pull using config file
|
1951
2275
|
`);
|
1952
2276
|
process.exit(0);
|
1953
2277
|
}
|
1954
|
-
|
1955
|
-
|
1956
|
-
|
1957
|
-
|
1958
|
-
|
1959
|
-
|
1960
|
-
|
1961
|
-
|
1962
|
-
|
2278
|
+
if (command === "init") {
|
2279
|
+
const { initCommand: initCommand2 } = await Promise.resolve().then(() => (init_cli_init(), exports_cli_init));
|
2280
|
+
await initCommand2(args.slice(1));
|
2281
|
+
} else if (command === "generate") {
|
2282
|
+
let configPath = "postgresdk.config.ts";
|
2283
|
+
const configIndex = args.findIndex((a) => a === "-c" || a === "--config");
|
2284
|
+
if (configIndex !== -1 && args[configIndex + 1]) {
|
2285
|
+
configPath = args[configIndex + 1];
|
2286
|
+
}
|
2287
|
+
try {
|
2288
|
+
await generate(resolve3(process.cwd(), configPath));
|
2289
|
+
} catch (err) {
|
2290
|
+
console.error("❌ Generation failed:", err);
|
2291
|
+
process.exit(1);
|
2292
|
+
}
|
2293
|
+
} else if (command === "pull") {
|
2294
|
+
const { pullCommand: pullCommand2 } = await Promise.resolve().then(() => (init_cli_pull(), exports_cli_pull));
|
2295
|
+
await pullCommand2(args.slice(1));
|
2296
|
+
} else {
|
2297
|
+
console.error(`❌ Unknown command: ${command}`);
|
2298
|
+
console.error(`Run 'postgresdk help' for usage information`);
|
1963
2299
|
process.exit(1);
|
1964
2300
|
}
|
package/dist/index.js
CHANGED
@@ -16,6 +16,16 @@ var __toESM = (mod, isNodeMode, target) => {
|
|
16
16
|
return to;
|
17
17
|
};
|
18
18
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
19
|
+
var __export = (target, all) => {
|
20
|
+
for (var name in all)
|
21
|
+
__defProp(target, name, {
|
22
|
+
get: all[name],
|
23
|
+
enumerable: true,
|
24
|
+
configurable: true,
|
25
|
+
set: (newValue) => all[name] = () => newValue
|
26
|
+
});
|
27
|
+
};
|
28
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
19
29
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
20
30
|
|
21
31
|
// node_modules/dotenv/package.json
|
@@ -1747,6 +1757,7 @@ function emitRouter(tables, hasAuth) {
|
|
1747
1757
|
`);
|
1748
1758
|
return `/* Generated. Do not edit. */
|
1749
1759
|
import { Hono } from "hono";
|
1760
|
+
import { SDK_MANIFEST } from "./sdk-bundle";
|
1750
1761
|
${imports}
|
1751
1762
|
${hasAuth ? `export { authMiddleware } from "./auth";` : ""}
|
1752
1763
|
|
@@ -1774,7 +1785,34 @@ export function createRouter(
|
|
1774
1785
|
deps: { pg: { query: (text: string, params?: any[]) => Promise<{ rows: any[] }> } }
|
1775
1786
|
): Hono {
|
1776
1787
|
const router = new Hono();
|
1788
|
+
|
1789
|
+
// Register table routes
|
1777
1790
|
${registrations}
|
1791
|
+
|
1792
|
+
// SDK distribution endpoints
|
1793
|
+
router.get("/sdk/manifest", (c) => {
|
1794
|
+
return c.json({
|
1795
|
+
version: SDK_MANIFEST.version,
|
1796
|
+
generated: SDK_MANIFEST.generated,
|
1797
|
+
files: Object.keys(SDK_MANIFEST.files)
|
1798
|
+
});
|
1799
|
+
});
|
1800
|
+
|
1801
|
+
router.get("/sdk/download", (c) => {
|
1802
|
+
return c.json(SDK_MANIFEST);
|
1803
|
+
});
|
1804
|
+
|
1805
|
+
router.get("/sdk/files/:path{.*}", (c) => {
|
1806
|
+
const path = c.req.param("path");
|
1807
|
+
const content = SDK_MANIFEST.files[path];
|
1808
|
+
if (!content) {
|
1809
|
+
return c.text("File not found", 404);
|
1810
|
+
}
|
1811
|
+
return c.text(content, 200, {
|
1812
|
+
"Content-Type": "text/plain; charset=utf-8"
|
1813
|
+
});
|
1814
|
+
});
|
1815
|
+
|
1778
1816
|
return router;
|
1779
1817
|
}
|
1780
1818
|
|
@@ -1808,6 +1846,29 @@ export * from "./include-spec";
|
|
1808
1846
|
`;
|
1809
1847
|
}
|
1810
1848
|
|
1849
|
+
// src/emit-sdk-bundle.ts
|
1850
|
+
function emitSdkBundle(clientFiles) {
|
1851
|
+
const files = {};
|
1852
|
+
for (const file of clientFiles) {
|
1853
|
+
const parts = file.path.split("/");
|
1854
|
+
const clientIndex = parts.lastIndexOf("client");
|
1855
|
+
if (clientIndex >= 0 && clientIndex < parts.length - 1) {
|
1856
|
+
const relativePath = parts.slice(clientIndex + 1).join("/");
|
1857
|
+
files[relativePath] = file.content;
|
1858
|
+
}
|
1859
|
+
}
|
1860
|
+
const version = `1.0.0`;
|
1861
|
+
const generated = new Date().toISOString();
|
1862
|
+
return `/* Generated. Do not edit. */
|
1863
|
+
|
1864
|
+
export const SDK_MANIFEST = {
|
1865
|
+
version: "${version}",
|
1866
|
+
generated: "${generated}",
|
1867
|
+
files: ${JSON.stringify(files, null, 2)}
|
1868
|
+
};
|
1869
|
+
`;
|
1870
|
+
}
|
1871
|
+
|
1811
1872
|
// src/types.ts
|
1812
1873
|
function normalizeAuthConfig(input) {
|
1813
1874
|
if (!input)
|
@@ -1914,6 +1975,11 @@ async function generate(configPath) {
|
|
1914
1975
|
path: join(serverDir, "router.ts"),
|
1915
1976
|
content: emitRouter(Object.values(model.tables), !!normalizedAuth?.strategy && normalizedAuth.strategy !== "none")
|
1916
1977
|
});
|
1978
|
+
const clientFiles = files.filter((f) => f.path.includes(clientDir));
|
1979
|
+
files.push({
|
1980
|
+
path: join(serverDir, "sdk-bundle.ts"),
|
1981
|
+
content: emitSdkBundle(clientFiles)
|
1982
|
+
});
|
1917
1983
|
console.log("✍️ Writing files...");
|
1918
1984
|
await writeFiles(files);
|
1919
1985
|
console.log(`✅ Generated ${files.length} files`);
|
package/dist/types.d.ts
CHANGED
@@ -27,5 +27,11 @@ export interface Config {
|
|
27
27
|
includeDepthLimit?: number;
|
28
28
|
dateType?: "date" | "string";
|
29
29
|
auth?: AuthConfigInput;
|
30
|
+
pull?: PullConfig;
|
31
|
+
}
|
32
|
+
export interface PullConfig {
|
33
|
+
from: string;
|
34
|
+
output?: string;
|
35
|
+
token?: string;
|
30
36
|
}
|
31
37
|
export declare function normalizeAuthConfig(input: AuthConfigInput | undefined): AuthConfig | undefined;
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "postgresdk",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.2.0",
|
4
4
|
"description": "Generate a typed server/client SDK from a Postgres schema (includes, Zod, Hono).",
|
5
5
|
"type": "module",
|
6
6
|
"bin": {
|
@@ -22,7 +22,10 @@
|
|
22
22
|
},
|
23
23
|
"scripts": {
|
24
24
|
"build": "bun build src/cli.ts src/index.ts --outdir dist --target node --format esm --external=pg --external=zod --external=hono --external=node:* && tsc -p tsconfig.build.json --emitDeclarationOnly",
|
25
|
-
"test": "bun test
|
25
|
+
"test": "bun test:init && bun test:gen && bun test:pull",
|
26
|
+
"test:init": "bun test/test-init.ts",
|
27
|
+
"test:gen": "bun test/test-gen.ts",
|
28
|
+
"test:pull": "bun test/test-pull.ts",
|
26
29
|
"prepublishOnly": "npm run build",
|
27
30
|
"publish:patch": "./publish.sh",
|
28
31
|
"publish:minor": "./publish.sh",
|