postgresdk 0.6.11 → 0.6.14

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/cli.js CHANGED
@@ -470,6 +470,675 @@ var require_config = __commonJS(() => {
470
470
  })();
471
471
  });
472
472
 
473
+ // src/utils.ts
474
+ import { mkdir, writeFile } from "fs/promises";
475
+ import { dirname } from "path";
476
+ async function writeFiles(files) {
477
+ for (const f of files) {
478
+ await mkdir(dirname(f.path), { recursive: true });
479
+ await writeFile(f.path, f.content, "utf-8");
480
+ }
481
+ }
482
+ async function ensureDirs(dirs) {
483
+ for (const d of dirs)
484
+ await mkdir(d, { recursive: true });
485
+ }
486
+ var pascal = (s) => s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
487
+ var init_utils = () => {};
488
+
489
+ // src/emit-sdk-contract.ts
490
+ var exports_emit_sdk_contract = {};
491
+ __export(exports_emit_sdk_contract, {
492
+ generateUnifiedContractMarkdown: () => generateUnifiedContractMarkdown,
493
+ generateUnifiedContract: () => generateUnifiedContract,
494
+ emitUnifiedContract: () => emitUnifiedContract
495
+ });
496
+ function generateUnifiedContract(model, config) {
497
+ const resources = [];
498
+ const relationships = [];
499
+ const tables = model && model.tables ? Object.values(model.tables) : [];
500
+ if (process.env.SDK_DEBUG) {
501
+ console.log(`[SDK Contract] Processing ${tables.length} tables`);
502
+ }
503
+ for (const table of tables) {
504
+ resources.push(generateResourceWithSDK(table, model));
505
+ for (const fk of table.fks) {
506
+ relationships.push({
507
+ from: table.name,
508
+ to: fk.toTable,
509
+ type: "many-to-one",
510
+ description: `Each ${table.name} belongs to one ${fk.toTable}`
511
+ });
512
+ }
513
+ }
514
+ const contract = {
515
+ version: "2.0.0",
516
+ generatedAt: new Date().toISOString(),
517
+ description: "Unified API and SDK contract - your one-stop reference for all operations",
518
+ sdk: {
519
+ initialization: generateSDKInitExamples(),
520
+ authentication: generateSDKAuthExamples(config.auth)
521
+ },
522
+ resources,
523
+ relationships
524
+ };
525
+ return contract;
526
+ }
527
+ function generateSDKInitExamples() {
528
+ return [
529
+ {
530
+ description: "Basic initialization",
531
+ code: `import { SDK } from './client';
532
+
533
+ const sdk = new SDK({
534
+ baseUrl: 'http://localhost:3000'
535
+ });`
536
+ },
537
+ {
538
+ description: "With authentication",
539
+ code: `import { SDK } from './client';
540
+
541
+ const sdk = new SDK({
542
+ baseUrl: 'https://api.example.com',
543
+ auth: {
544
+ apiKey: process.env.API_KEY
545
+ }
546
+ });`
547
+ },
548
+ {
549
+ description: "With custom fetch (for Node.js < 18)",
550
+ code: `import { SDK } from './client';
551
+ import fetch from 'node-fetch';
552
+
553
+ const sdk = new SDK({
554
+ baseUrl: 'https://api.example.com',
555
+ fetch: fetch as any
556
+ });`
557
+ }
558
+ ];
559
+ }
560
+ function generateSDKAuthExamples(auth) {
561
+ const examples = [];
562
+ if (!auth || auth.strategy === "none" || !auth.strategy) {
563
+ examples.push({
564
+ strategy: "none",
565
+ description: "No authentication required",
566
+ code: `const sdk = new SDK({
567
+ baseUrl: 'http://localhost:3000'
568
+ });`
569
+ });
570
+ }
571
+ if (auth?.strategy === "api-key") {
572
+ examples.push({
573
+ strategy: "apiKey",
574
+ description: "API Key authentication",
575
+ code: `const sdk = new SDK({
576
+ baseUrl: 'https://api.example.com',
577
+ auth: {
578
+ apiKey: 'your-api-key',
579
+ apiKeyHeader: 'x-api-key' // optional, defaults to 'x-api-key'
580
+ }
581
+ });`
582
+ });
583
+ }
584
+ if (auth?.strategy === "jwt-hs256") {
585
+ examples.push({
586
+ strategy: "jwt",
587
+ description: "JWT Bearer token authentication",
588
+ code: `const sdk = new SDK({
589
+ baseUrl: 'https://api.example.com',
590
+ auth: {
591
+ jwt: 'your-jwt-token' // or async: () => getToken()
592
+ }
593
+ });`
594
+ });
595
+ examples.push({
596
+ strategy: "jwt-async",
597
+ description: "JWT with async token provider",
598
+ code: `const sdk = new SDK({
599
+ baseUrl: 'https://api.example.com',
600
+ auth: {
601
+ jwt: async () => {
602
+ const token = await refreshToken();
603
+ return token;
604
+ }
605
+ }
606
+ });`
607
+ });
608
+ }
609
+ examples.push({
610
+ strategy: "custom",
611
+ description: "Custom headers provider",
612
+ code: `const sdk = new SDK({
613
+ baseUrl: 'https://api.example.com',
614
+ auth: async () => ({
615
+ 'Authorization': 'Bearer ' + await getToken(),
616
+ 'X-Request-ID': generateRequestId()
617
+ })
618
+ });`
619
+ });
620
+ return examples;
621
+ }
622
+ function generateResourceWithSDK(table, model) {
623
+ const Type = pascal(table.name);
624
+ const tableName = table.name;
625
+ const basePath = `/v1/${tableName}`;
626
+ const hasSinglePK = table.pk.length === 1;
627
+ const pkField = hasSinglePK ? table.pk[0] : "id";
628
+ const sdkMethods = [];
629
+ const endpoints = [];
630
+ sdkMethods.push({
631
+ name: "list",
632
+ signature: `list(params?: ListParams): Promise<${Type}[]>`,
633
+ description: `List ${tableName} with filtering, sorting, and pagination`,
634
+ example: `// Get all ${tableName}
635
+ const items = await sdk.${tableName}.list();
636
+
637
+ // With filters and pagination
638
+ const filtered = await sdk.${tableName}.list({
639
+ limit: 20,
640
+ offset: 0,
641
+ ${table.columns[0]?.name || "field"}_like: 'search',
642
+ order_by: '${table.columns[0]?.name || "created_at"}',
643
+ order_dir: 'desc'
644
+ });
645
+
646
+ // With related data
647
+ const withRelations = await sdk.${tableName}.list({
648
+ include: '${table.fks[0]?.toTable || "related_table"}'
649
+ });`,
650
+ correspondsTo: `GET ${basePath}`
651
+ });
652
+ endpoints.push({
653
+ method: "GET",
654
+ path: basePath,
655
+ description: `List all ${tableName} records`,
656
+ queryParameters: generateQueryParams(table),
657
+ responseBody: `${Type}[]`
658
+ });
659
+ if (hasSinglePK) {
660
+ sdkMethods.push({
661
+ name: "getByPk",
662
+ signature: `getByPk(${pkField}: string, params?: GetParams): Promise<${Type} | null>`,
663
+ description: `Get a single ${tableName} by primary key`,
664
+ example: `// Get by ID
665
+ const item = await sdk.${tableName}.getByPk('123e4567-e89b-12d3-a456-426614174000');
666
+
667
+ // With related data
668
+ const withRelations = await sdk.${tableName}.getByPk('123', {
669
+ include: '${table.fks[0]?.toTable || "related_table"}'
670
+ });
671
+
672
+ // Check if exists
673
+ if (item === null) {
674
+ console.log('Not found');
675
+ }`,
676
+ correspondsTo: `GET ${basePath}/:${pkField}`
677
+ });
678
+ endpoints.push({
679
+ method: "GET",
680
+ path: `${basePath}/:${pkField}`,
681
+ description: `Get ${tableName} by ID`,
682
+ queryParameters: {
683
+ include: "string - Comma-separated list of related resources"
684
+ },
685
+ responseBody: `${Type}`
686
+ });
687
+ }
688
+ sdkMethods.push({
689
+ name: "create",
690
+ signature: `create(data: Insert${Type}): Promise<${Type}>`,
691
+ description: `Create a new ${tableName}`,
692
+ example: `import type { Insert${Type} } from './client/types/${tableName}';
693
+
694
+ const newItem: Insert${Type} = {
695
+ ${generateExampleFields(table, "create")}
696
+ };
697
+
698
+ const created = await sdk.${tableName}.create(newItem);
699
+ console.log('Created:', created.${pkField});`,
700
+ correspondsTo: `POST ${basePath}`
701
+ });
702
+ endpoints.push({
703
+ method: "POST",
704
+ path: basePath,
705
+ description: `Create new ${tableName}`,
706
+ requestBody: `Insert${Type}`,
707
+ responseBody: `${Type}`
708
+ });
709
+ if (hasSinglePK) {
710
+ sdkMethods.push({
711
+ name: "update",
712
+ signature: `update(${pkField}: string, data: Update${Type}): Promise<${Type}>`,
713
+ description: `Update an existing ${tableName}`,
714
+ example: `import type { Update${Type} } from './client/types/${tableName}';
715
+
716
+ const updates: Update${Type} = {
717
+ ${generateExampleFields(table, "update")}
718
+ };
719
+
720
+ const updated = await sdk.${tableName}.update('123', updates);`,
721
+ correspondsTo: `PATCH ${basePath}/:${pkField}`
722
+ });
723
+ endpoints.push({
724
+ method: "PATCH",
725
+ path: `${basePath}/:${pkField}`,
726
+ description: `Update ${tableName}`,
727
+ requestBody: `Update${Type}`,
728
+ responseBody: `${Type}`
729
+ });
730
+ }
731
+ if (hasSinglePK) {
732
+ sdkMethods.push({
733
+ name: "delete",
734
+ signature: `delete(${pkField}: string): Promise<${Type}>`,
735
+ description: `Delete a ${tableName}`,
736
+ example: `const deleted = await sdk.${tableName}.delete('123');
737
+ console.log('Deleted:', deleted);`,
738
+ correspondsTo: `DELETE ${basePath}/:${pkField}`
739
+ });
740
+ endpoints.push({
741
+ method: "DELETE",
742
+ path: `${basePath}/:${pkField}`,
743
+ description: `Delete ${tableName}`,
744
+ responseBody: `${Type}`
745
+ });
746
+ }
747
+ const fields = table.columns.map((col) => generateFieldContract2(col, table));
748
+ return {
749
+ name: Type,
750
+ tableName,
751
+ description: `Resource for ${tableName} operations`,
752
+ sdk: {
753
+ client: `sdk.${tableName}`,
754
+ methods: sdkMethods
755
+ },
756
+ api: {
757
+ endpoints
758
+ },
759
+ fields
760
+ };
761
+ }
762
+ function generateFieldContract2(column, table) {
763
+ const field = {
764
+ name: column.name,
765
+ type: postgresTypeToJsonType2(column.pgType),
766
+ tsType: postgresTypeToTsType(column),
767
+ required: !column.nullable && !column.hasDefault,
768
+ description: generateFieldDescription2(column, table)
769
+ };
770
+ const fk = table.fks.find((fk2) => fk2.from.length === 1 && fk2.from[0] === column.name);
771
+ if (fk) {
772
+ field.foreignKey = {
773
+ table: fk.toTable,
774
+ field: fk.to[0] || "id"
775
+ };
776
+ }
777
+ return field;
778
+ }
779
+ function postgresTypeToTsType(column) {
780
+ const baseType = (() => {
781
+ switch (column.pgType) {
782
+ case "int":
783
+ case "integer":
784
+ case "smallint":
785
+ case "bigint":
786
+ case "decimal":
787
+ case "numeric":
788
+ case "real":
789
+ case "double precision":
790
+ case "float":
791
+ return "number";
792
+ case "boolean":
793
+ case "bool":
794
+ return "boolean";
795
+ case "date":
796
+ case "timestamp":
797
+ case "timestamptz":
798
+ return "string";
799
+ case "json":
800
+ case "jsonb":
801
+ return "Record<string, any>";
802
+ case "uuid":
803
+ return "string";
804
+ case "text[]":
805
+ case "varchar[]":
806
+ return "string[]";
807
+ case "int[]":
808
+ case "integer[]":
809
+ return "number[]";
810
+ default:
811
+ return "string";
812
+ }
813
+ })();
814
+ if (column.nullable) {
815
+ return `${baseType} | null`;
816
+ }
817
+ return baseType;
818
+ }
819
+ function generateExampleFields(table, operation) {
820
+ const fields = [];
821
+ let count = 0;
822
+ for (const col of table.columns) {
823
+ if (col.hasDefault && ["id", "created_at", "updated_at"].includes(col.name)) {
824
+ continue;
825
+ }
826
+ if (operation === "update" && count >= 2) {
827
+ break;
828
+ }
829
+ if (operation === "create" && col.nullable && count >= 3) {
830
+ continue;
831
+ }
832
+ const value = generateExampleValue(col);
833
+ fields.push(` ${col.name}: ${value}`);
834
+ count++;
835
+ }
836
+ return fields.join(`,
837
+ `);
838
+ }
839
+ function generateExampleValue(column) {
840
+ const name = column.name.toLowerCase();
841
+ if (name.includes("email"))
842
+ return `'user@example.com'`;
843
+ if (name.includes("name"))
844
+ return `'John Doe'`;
845
+ if (name.includes("title"))
846
+ return `'Example Title'`;
847
+ if (name.includes("description"))
848
+ return `'Example description'`;
849
+ if (name.includes("phone"))
850
+ return `'+1234567890'`;
851
+ if (name.includes("url"))
852
+ return `'https://example.com'`;
853
+ if (name.includes("price") || name.includes("amount"))
854
+ return `99.99`;
855
+ if (name.includes("quantity") || name.includes("count"))
856
+ return `10`;
857
+ if (name.includes("status"))
858
+ return `'active'`;
859
+ if (name.includes("_id"))
860
+ return `'related-id-123'`;
861
+ switch (column.pgType) {
862
+ case "boolean":
863
+ case "bool":
864
+ return "true";
865
+ case "int":
866
+ case "integer":
867
+ case "smallint":
868
+ case "bigint":
869
+ return "42";
870
+ case "decimal":
871
+ case "numeric":
872
+ case "real":
873
+ case "double precision":
874
+ case "float":
875
+ return "123.45";
876
+ case "date":
877
+ return `'2024-01-01'`;
878
+ case "timestamp":
879
+ case "timestamptz":
880
+ return `'2024-01-01T00:00:00Z'`;
881
+ case "json":
882
+ case "jsonb":
883
+ return `{ key: 'value' }`;
884
+ case "uuid":
885
+ return `'123e4567-e89b-12d3-a456-426614174000'`;
886
+ default:
887
+ return `'example value'`;
888
+ }
889
+ }
890
+ function generateQueryParams(table) {
891
+ const params = {
892
+ limit: "number - Max records to return (default: 50)",
893
+ offset: "number - Records to skip",
894
+ order_by: "string - Field to sort by",
895
+ order_dir: "'asc' | 'desc' - Sort direction",
896
+ include: "string - Related resources to include"
897
+ };
898
+ let filterCount = 0;
899
+ for (const col of table.columns) {
900
+ if (filterCount >= 3)
901
+ break;
902
+ const type = postgresTypeToJsonType2(col.pgType);
903
+ params[col.name] = `${type} - Filter by ${col.name}`;
904
+ if (type === "string") {
905
+ params[`${col.name}_like`] = `string - Search in ${col.name}`;
906
+ } else if (type === "number" || type === "date/datetime") {
907
+ params[`${col.name}_gt`] = `${type} - Greater than`;
908
+ params[`${col.name}_lt`] = `${type} - Less than`;
909
+ }
910
+ filterCount++;
911
+ }
912
+ params["..."] = "Additional filters for all fields";
913
+ return params;
914
+ }
915
+ function postgresTypeToJsonType2(pgType) {
916
+ switch (pgType) {
917
+ case "int":
918
+ case "integer":
919
+ case "smallint":
920
+ case "bigint":
921
+ case "decimal":
922
+ case "numeric":
923
+ case "real":
924
+ case "double precision":
925
+ case "float":
926
+ return "number";
927
+ case "boolean":
928
+ case "bool":
929
+ return "boolean";
930
+ case "date":
931
+ case "timestamp":
932
+ case "timestamptz":
933
+ return "date/datetime";
934
+ case "json":
935
+ case "jsonb":
936
+ return "object";
937
+ case "uuid":
938
+ return "uuid";
939
+ case "text[]":
940
+ case "varchar[]":
941
+ return "string[]";
942
+ case "int[]":
943
+ case "integer[]":
944
+ return "number[]";
945
+ default:
946
+ return "string";
947
+ }
948
+ }
949
+ function generateFieldDescription2(column, table) {
950
+ const descriptions = [];
951
+ if (column.name === "id") {
952
+ descriptions.push("Primary key");
953
+ } else if (column.name === "created_at") {
954
+ descriptions.push("Creation timestamp");
955
+ } else if (column.name === "updated_at") {
956
+ descriptions.push("Last update timestamp");
957
+ } else if (column.name === "deleted_at") {
958
+ descriptions.push("Soft delete timestamp");
959
+ } else if (column.name.endsWith("_id")) {
960
+ const relatedTable = column.name.slice(0, -3);
961
+ descriptions.push(`Foreign key to ${relatedTable}`);
962
+ } else {
963
+ descriptions.push(column.name.replace(/_/g, " "));
964
+ }
965
+ return descriptions.join(", ");
966
+ }
967
+ function generateUnifiedContractMarkdown(contract) {
968
+ const lines = [];
969
+ lines.push("# API & SDK Contract");
970
+ lines.push("");
971
+ lines.push(contract.description);
972
+ lines.push("");
973
+ lines.push(`**Version:** ${contract.version}`);
974
+ lines.push(`**Generated:** ${new Date(contract.generatedAt).toLocaleString()}`);
975
+ lines.push("");
976
+ lines.push("## SDK Setup");
977
+ lines.push("");
978
+ lines.push("### Installation");
979
+ lines.push("");
980
+ lines.push("```bash");
981
+ lines.push("# The SDK is generated in the client/ directory");
982
+ lines.push("# Import it directly from your generated code");
983
+ lines.push("```");
984
+ lines.push("");
985
+ lines.push("### Initialization");
986
+ lines.push("");
987
+ for (const example of contract.sdk.initialization) {
988
+ lines.push(`**${example.description}:**`);
989
+ lines.push("");
990
+ lines.push("```typescript");
991
+ lines.push(example.code);
992
+ lines.push("```");
993
+ lines.push("");
994
+ }
995
+ if (contract.sdk.authentication.length > 0) {
996
+ lines.push("### Authentication");
997
+ lines.push("");
998
+ for (const auth of contract.sdk.authentication) {
999
+ lines.push(`**${auth.description}:**`);
1000
+ lines.push("");
1001
+ lines.push("```typescript");
1002
+ lines.push(auth.code);
1003
+ lines.push("```");
1004
+ lines.push("");
1005
+ }
1006
+ }
1007
+ lines.push("## Resources");
1008
+ lines.push("");
1009
+ for (const resource of contract.resources) {
1010
+ lines.push(`### ${resource.name}`);
1011
+ lines.push("");
1012
+ lines.push(resource.description);
1013
+ lines.push("");
1014
+ lines.push("#### SDK Methods");
1015
+ lines.push("");
1016
+ lines.push(`Access via: \`${resource.sdk.client}\``);
1017
+ lines.push("");
1018
+ for (const method of resource.sdk.methods) {
1019
+ lines.push(`**${method.name}**`);
1020
+ lines.push(`- Signature: \`${method.signature}\``);
1021
+ lines.push(`- ${method.description}`);
1022
+ if (method.correspondsTo) {
1023
+ lines.push(`- API: \`${method.correspondsTo}\``);
1024
+ }
1025
+ lines.push("");
1026
+ lines.push("```typescript");
1027
+ lines.push(method.example);
1028
+ lines.push("```");
1029
+ lines.push("");
1030
+ }
1031
+ lines.push("#### API Endpoints");
1032
+ lines.push("");
1033
+ for (const endpoint of resource.api.endpoints) {
1034
+ lines.push(`- \`${endpoint.method} ${endpoint.path}\``);
1035
+ lines.push(` - ${endpoint.description}`);
1036
+ if (endpoint.requestBody) {
1037
+ lines.push(` - Request: \`${endpoint.requestBody}\``);
1038
+ }
1039
+ if (endpoint.responseBody) {
1040
+ lines.push(` - Response: \`${endpoint.responseBody}\``);
1041
+ }
1042
+ }
1043
+ lines.push("");
1044
+ lines.push("#### Fields");
1045
+ lines.push("");
1046
+ lines.push("| Field | Type | TypeScript | Required | Description |");
1047
+ lines.push("|-------|------|------------|----------|-------------|");
1048
+ for (const field of resource.fields) {
1049
+ const required = field.required ? "✓" : "";
1050
+ const fk = field.foreignKey ? ` → ${field.foreignKey.table}` : "";
1051
+ lines.push(`| ${field.name} | ${field.type} | \`${field.tsType}\` | ${required} | ${field.description}${fk} |`);
1052
+ }
1053
+ lines.push("");
1054
+ }
1055
+ if (contract.relationships.length > 0) {
1056
+ lines.push("## Relationships");
1057
+ lines.push("");
1058
+ for (const rel of contract.relationships) {
1059
+ lines.push(`- **${rel.from}** → **${rel.to}** (${rel.type}): ${rel.description}`);
1060
+ }
1061
+ lines.push("");
1062
+ }
1063
+ lines.push("## Type Imports");
1064
+ lines.push("");
1065
+ lines.push("```typescript");
1066
+ lines.push("// Import SDK and types");
1067
+ lines.push("import { SDK } from './client';");
1068
+ lines.push("");
1069
+ lines.push("// Import types for a specific table");
1070
+ lines.push("import type {");
1071
+ lines.push(" SelectTableName, // Full record type");
1072
+ lines.push(" InsertTableName, // Create payload type");
1073
+ lines.push(" UpdateTableName // Update payload type");
1074
+ lines.push("} from './client/types/table_name';");
1075
+ lines.push("");
1076
+ lines.push("// Import all types");
1077
+ lines.push("import type * as Types from './client/types';");
1078
+ lines.push("```");
1079
+ lines.push("");
1080
+ return lines.join(`
1081
+ `);
1082
+ }
1083
+ function emitUnifiedContract(model, config) {
1084
+ const contract = generateUnifiedContract(model, config);
1085
+ const contractJson = JSON.stringify(contract, null, 2);
1086
+ return `/**
1087
+ * Unified API & SDK Contract
1088
+ *
1089
+ * This module exports a comprehensive contract that describes both
1090
+ * API endpoints and SDK usage for all resources.
1091
+ *
1092
+ * Use this as your primary reference for:
1093
+ * - SDK initialization and authentication
1094
+ * - Available methods and their signatures
1095
+ * - API endpoints and parameters
1096
+ * - Type definitions and relationships
1097
+ */
1098
+
1099
+ export const contract = ${contractJson};
1100
+
1101
+ export const contractMarkdown = \`${generateUnifiedContractMarkdown(contract).replace(/`/g, "\\`")}\`;
1102
+
1103
+ /**
1104
+ * Get the contract in different formats
1105
+ */
1106
+ export function getContract(format: 'json' | 'markdown' = 'json') {
1107
+ if (format === 'markdown') {
1108
+ return contractMarkdown;
1109
+ }
1110
+ return contract;
1111
+ }
1112
+
1113
+ /**
1114
+ * Quick reference for all SDK clients
1115
+ */
1116
+ export const sdkClients = ${JSON.stringify(contract.resources.map((r) => ({
1117
+ name: r.tableName,
1118
+ client: r.sdk.client,
1119
+ methods: r.sdk.methods.map((m) => m.name)
1120
+ })), null, 2)};
1121
+
1122
+ /**
1123
+ * Type export reference
1124
+ */
1125
+ export const typeImports = \`
1126
+ // Import the SDK
1127
+ import { SDK } from './client';
1128
+
1129
+ // Import types for a specific resource
1130
+ ${contract.resources.slice(0, 1).map((r) => `import type { Select${r.name}, Insert${r.name}, Update${r.name} } from './client/types/${r.tableName}';`).join(`
1131
+ `)}
1132
+
1133
+ // Import all types
1134
+ import type * as Types from './client/types';
1135
+ \`;
1136
+ `;
1137
+ }
1138
+ var init_emit_sdk_contract = __esm(() => {
1139
+ init_utils();
1140
+ });
1141
+
473
1142
  // src/cli-init.ts
474
1143
  var exports_cli_init = {};
475
1144
  __export(exports_cli_init, {
@@ -978,22 +1647,8 @@ export const buildWithFor = (t: TableName) =>
978
1647
  `;
979
1648
  }
980
1649
 
981
- // src/utils.ts
982
- import { mkdir, writeFile } from "fs/promises";
983
- import { dirname } from "path";
984
- var pascal = (s) => s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
985
- async function writeFiles(files) {
986
- for (const f of files) {
987
- await mkdir(dirname(f.path), { recursive: true });
988
- await writeFile(f.path, f.content, "utf-8");
989
- }
990
- }
991
- async function ensureDirs(dirs) {
992
- for (const d of dirs)
993
- await mkdir(d, { recursive: true });
994
- }
995
-
996
1650
  // src/emit-zod.ts
1651
+ init_utils();
997
1652
  function emitZod(table, opts) {
998
1653
  const Type = pascal(table.name);
999
1654
  const zFor = (pg) => {
@@ -1037,6 +1692,7 @@ export type Update${Type} = z.infer<typeof Update${Type}Schema>;
1037
1692
  }
1038
1693
 
1039
1694
  // src/emit-routes-hono.ts
1695
+ init_utils();
1040
1696
  function emitHonoRoutes(table, _graph, opts) {
1041
1697
  const fileTableName = table.name;
1042
1698
  const Type = pascal(table.name);
@@ -1197,6 +1853,7 @@ ${hasAuth ? `
1197
1853
  }
1198
1854
 
1199
1855
  // src/emit-client.ts
1856
+ init_utils();
1200
1857
  function emitClient(table, useJsExtensions) {
1201
1858
  const Type = pascal(table.name);
1202
1859
  const ext = useJsExtensions ? ".js" : "";
@@ -1979,6 +2636,7 @@ export async function authMiddleware(c: Context, next: Next) {
1979
2636
  }
1980
2637
 
1981
2638
  // src/emit-router-hono.ts
2639
+ init_utils();
1982
2640
  function emitHonoRouter(tables, hasAuth, useJsExtensions) {
1983
2641
  const tableNames = tables.map((t) => t.name).sort();
1984
2642
  const ext = useJsExtensions ? ".js" : "";
@@ -2358,6 +3016,7 @@ export async function deleteRecord(
2358
3016
  }
2359
3017
 
2360
3018
  // src/emit-tests.ts
3019
+ init_utils();
2361
3020
  function emitTableTest(table, model, clientPath, framework = "vitest") {
2362
3021
  const Type = pascal(table.name);
2363
3022
  const tableName = table.name;
@@ -2619,11 +3278,80 @@ export TEST_API_URL="http://localhost:3000"
2619
3278
  echo "⏳ Waiting for database..."
2620
3279
  sleep 3
2621
3280
 
2622
- # TODO: Run your migrations on the test database
2623
- # Example:
2624
- # echo "\uD83D\uDCCA Running migrations..."
2625
- # npm run migrate -- --database-url="$TEST_DATABASE_URL"
3281
+ # REQUIRED: Run migrations on the test database
3282
+ echo ""
3283
+ echo "\uD83D\uDCCA Database Migration Step"
3284
+ echo "========================================="
3285
+ echo ""
3286
+ echo "⚠️ IMPORTANT: You must run migrations before tests can work!"
3287
+ echo ""
3288
+ echo "Choose one of the following options:"
3289
+ echo ""
3290
+ echo "Option 1: Add your migration command (recommended):"
3291
+ echo " Uncomment and modify one of these examples:"
3292
+ echo ""
3293
+ echo " # For Prisma:"
3294
+ echo " # npx prisma migrate deploy"
3295
+ echo ""
3296
+ echo " # For Drizzle:"
3297
+ echo " # npx drizzle-kit push --config=./drizzle.config.ts"
3298
+ echo ""
3299
+ echo " # For node-pg-migrate:"
3300
+ echo " # npm run migrate up"
3301
+ echo ""
3302
+ echo " # For Knex:"
3303
+ echo " # npx knex migrate:latest"
3304
+ echo ""
3305
+ echo " # For TypeORM:"
3306
+ echo " # npm run typeorm migration:run"
3307
+ echo ""
3308
+ echo " # For custom migration scripts:"
3309
+ echo " # node ./scripts/migrate.js"
3310
+ echo ""
3311
+ echo "Option 2: Skip migrations (only if your database is already set up):"
3312
+ echo " Uncomment the line: # SKIP_MIGRATIONS=true"
3313
+ echo ""
3314
+ echo "========================================="
3315
+ echo ""
3316
+
3317
+ # MIGRATION_COMMAND:
3318
+ # Add your migration command here. Examples:
3319
+ # MIGRATION_COMMAND="npx prisma migrate deploy"
3320
+ # MIGRATION_COMMAND="npx drizzle-kit push --config=./drizzle.config.ts"
3321
+ # MIGRATION_COMMAND="npm run migrate up"
3322
+
3323
+ # Or skip migrations if your database is pre-configured:
3324
+ # SKIP_MIGRATIONS=true
2626
3325
 
3326
+ if [ -z "\${MIGRATION_COMMAND}" ] && [ -z "\${SKIP_MIGRATIONS}" ]; then
3327
+ echo "❌ ERROR: No migration strategy configured!"
3328
+ echo ""
3329
+ echo " Please edit this script and either:"
3330
+ echo " 1. Set MIGRATION_COMMAND with your migration command"
3331
+ echo " 2. Set SKIP_MIGRATIONS=true if migrations aren't needed"
3332
+ echo ""
3333
+ echo " Tests cannot run without a properly migrated database schema."
3334
+ echo ""
3335
+ exit 1
3336
+ fi
3337
+
3338
+ if [ ! -z "\${MIGRATION_COMMAND}" ]; then
3339
+ echo "\uD83D\uDCCA Running migrations..."
3340
+ echo " Command: \${MIGRATION_COMMAND}"
3341
+ eval "\${MIGRATION_COMMAND}"
3342
+ if [ $? -ne 0 ]; then
3343
+ echo "❌ Migration failed! Please check your migration command and database connection."
3344
+ exit 1
3345
+ fi
3346
+ echo "✅ Migrations completed successfully"
3347
+ elif [ "\${SKIP_MIGRATIONS}" = "true" ]; then
3348
+ echo "⏭️ Skipping migrations (SKIP_MIGRATIONS=true)"
3349
+ else
3350
+ echo "❌ Invalid migration configuration"
3351
+ exit 1
3352
+ fi
3353
+
3354
+ echo ""
2627
3355
  echo "\uD83D\uDE80 Starting API server..."
2628
3356
  echo "⚠️ TODO: Uncomment and customize the API server startup command below:"
2629
3357
  echo ""
@@ -2997,10 +3725,12 @@ function generateTestCases(table, sampleData, updateData, hasForeignKeys = false
2997
3725
  }
2998
3726
 
2999
3727
  // src/emit-api-contract.ts
3728
+ init_utils();
3000
3729
  function generateApiContract(model, config) {
3001
3730
  const resources = [];
3002
3731
  const relationships = [];
3003
- for (const table of Object.values(model.tables)) {
3732
+ const tables = Object.values(model.tables || {});
3733
+ for (const table of tables) {
3004
3734
  resources.push(generateResourceContract(table, model));
3005
3735
  for (const fk of table.fks) {
3006
3736
  relationships.push({
@@ -3270,6 +4000,10 @@ export function getApiContract(format: 'json' | 'markdown' = 'json') {
3270
4000
  `;
3271
4001
  }
3272
4002
 
4003
+ // src/index.ts
4004
+ init_emit_sdk_contract();
4005
+ init_utils();
4006
+
3273
4007
  // src/types.ts
3274
4008
  function normalizeAuthConfig(input) {
3275
4009
  if (!input)
@@ -3367,6 +4101,9 @@ async function generate(configPath) {
3367
4101
  path: join(serverDir, "core", "operations.ts"),
3368
4102
  content: emitCoreOperations()
3369
4103
  });
4104
+ if (process.env.SDK_DEBUG) {
4105
+ console.log(`[Index] About to process ${Object.keys(model.tables || {}).length} tables for generation`);
4106
+ }
3370
4107
  for (const table of Object.values(model.tables)) {
3371
4108
  const typesSrc = emitTypes(table, { numericMode: "string" });
3372
4109
  files.push({ path: join(serverDir, "types", `${table.name}.ts`), content: typesSrc });
@@ -3416,6 +4153,20 @@ async function generate(configPath) {
3416
4153
  path: join(serverDir, "api-contract.ts"),
3417
4154
  content: emitApiContract(model, cfg)
3418
4155
  });
4156
+ const contractCode = emitUnifiedContract(model, cfg);
4157
+ files.push({
4158
+ path: join(serverDir, "contract.ts"),
4159
+ content: contractCode
4160
+ });
4161
+ const { generateUnifiedContract: generateUnifiedContract2, generateUnifiedContractMarkdown: generateUnifiedContractMarkdown2 } = await Promise.resolve().then(() => (init_emit_sdk_contract(), exports_emit_sdk_contract));
4162
+ if (process.env.SDK_DEBUG) {
4163
+ console.log(`[Index] Model has ${Object.keys(model.tables || {}).length} tables before contract generation`);
4164
+ }
4165
+ const contract = generateUnifiedContract2(model, cfg);
4166
+ files.push({
4167
+ path: join(serverDir, "CONTRACT.md"),
4168
+ content: generateUnifiedContractMarkdown2(contract)
4169
+ });
3419
4170
  if (generateTests) {
3420
4171
  console.log("\uD83E\uDDEA Generating tests...");
3421
4172
  const relativeClientPath = relative(testDir, clientDir);