@huluwz/pg-ethiopian-calendar 1.1.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 +105 -0
- package/dist/cli/init.d.ts +7 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +170 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +96 -0
- package/dist/index.js.map +1 -0
- package/docs/drizzle.md +147 -0
- package/docs/prisma.md +128 -0
- package/docs/raw.md +200 -0
- package/docs/typeorm.md +157 -0
- package/package.json +45 -0
- package/sql/ethiopian_calendar.sql +356 -0
- package/sql/migrations/1.0.0_to_1.1.0.sql +246 -0
package/README.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# @huluwz/pg-ethiopian-calendar
|
|
2
|
+
|
|
3
|
+
Ethiopian calendar functions for PostgreSQL. Works on any PostgreSQL including managed services (Neon, Supabase, Railway, AWS RDS).
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@huluwz/pg-ethiopian-calendar)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
## Install
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @huluwz/pg-ethiopian-calendar
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Quick Start
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Generate migration (auto-detects ORM)
|
|
18
|
+
npx ethiopian-calendar init
|
|
19
|
+
|
|
20
|
+
# Or specify ORM
|
|
21
|
+
npx ethiopian-calendar init prisma
|
|
22
|
+
npx ethiopian-calendar init drizzle
|
|
23
|
+
npx ethiopian-calendar init typeorm
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Then run your ORM's migration command.
|
|
27
|
+
|
|
28
|
+
## SQL Functions
|
|
29
|
+
|
|
30
|
+
```sql
|
|
31
|
+
-- Current Ethiopian date (two ways)
|
|
32
|
+
SELECT to_ethiopian_date(); -- '2018-04-23'
|
|
33
|
+
SELECT to_ethiopian_date(NOW()); -- same
|
|
34
|
+
|
|
35
|
+
-- Specific date
|
|
36
|
+
SELECT to_ethiopian_date('2024-01-01'::timestamp); -- '2016-04-23'
|
|
37
|
+
|
|
38
|
+
-- Ethiopian → Gregorian
|
|
39
|
+
SELECT from_ethiopian_date('2016-04-23'); -- '2024-01-01 00:00:00'
|
|
40
|
+
|
|
41
|
+
-- Current Ethiopian timestamp (with time)
|
|
42
|
+
SELECT to_ethiopian_timestamp(); -- '2018-04-23 14:30:00'
|
|
43
|
+
|
|
44
|
+
-- Check version
|
|
45
|
+
SELECT ethiopian_calendar_version(); -- '1.1.0'
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Generated Columns
|
|
49
|
+
|
|
50
|
+
```sql
|
|
51
|
+
CREATE TABLE orders (
|
|
52
|
+
id SERIAL PRIMARY KEY,
|
|
53
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
54
|
+
created_at_ethiopian TIMESTAMP GENERATED ALWAYS AS
|
|
55
|
+
(to_ethiopian_timestamp(created_at)) STORED
|
|
56
|
+
);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## With Prisma
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
const order = await prisma.order.create({
|
|
63
|
+
data: { name: 'Test' }
|
|
64
|
+
});
|
|
65
|
+
console.log(order.createdAtEthiopian); // auto-populated
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## With Drizzle
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { sql } from 'drizzle-orm';
|
|
72
|
+
|
|
73
|
+
export const orders = pgTable('orders', {
|
|
74
|
+
id: serial('id').primaryKey(),
|
|
75
|
+
createdAt: timestamp('created_at').defaultNow(),
|
|
76
|
+
createdAtEthiopian: timestamp('created_at_ethiopian')
|
|
77
|
+
.generatedAlwaysAs(sql`to_ethiopian_timestamp(created_at)`),
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## API
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
import { getSql, VERSION, detectOrm } from '@huluwz/pg-ethiopian-calendar';
|
|
85
|
+
|
|
86
|
+
getSql(); // Full SQL content
|
|
87
|
+
detectOrm(); // Auto-detect installed ORM
|
|
88
|
+
VERSION; // '1.1.0'
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Supported ORMs
|
|
92
|
+
|
|
93
|
+
- Prisma
|
|
94
|
+
- Drizzle
|
|
95
|
+
- TypeORM
|
|
96
|
+
- Raw SQL
|
|
97
|
+
|
|
98
|
+
## Links
|
|
99
|
+
|
|
100
|
+
- [GitHub](https://github.com/HuluWZ/pg-ethiopian-calendar)
|
|
101
|
+
- [Documentation](https://github.com/HuluWZ/pg-ethiopian-calendar#readme)
|
|
102
|
+
|
|
103
|
+
## License
|
|
104
|
+
|
|
105
|
+
PostgreSQL License
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":";AACA;;;GAGG"}
|
package/dist/cli/init.js
ADDED
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* CLI for Ethiopian Calendar PostgreSQL migrations
|
|
5
|
+
* @example npx ethiopian-calendar init prisma
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
const fs_1 = require("fs");
|
|
9
|
+
const path_1 = require("path");
|
|
10
|
+
const index_1 = require("../index");
|
|
11
|
+
const fmt = {
|
|
12
|
+
reset: "\x1b[0m",
|
|
13
|
+
bold: "\x1b[1m",
|
|
14
|
+
red: "\x1b[31m",
|
|
15
|
+
green: "\x1b[32m",
|
|
16
|
+
yellow: "\x1b[33m",
|
|
17
|
+
blue: "\x1b[34m",
|
|
18
|
+
cyan: "\x1b[36m",
|
|
19
|
+
};
|
|
20
|
+
const log = console.log;
|
|
21
|
+
const print = {
|
|
22
|
+
info: (msg) => log(`${fmt.blue}ℹ${fmt.reset} ${msg}`),
|
|
23
|
+
success: (msg) => log(`${fmt.green}✔${fmt.reset} ${msg}`),
|
|
24
|
+
error: (msg) => log(`${fmt.red}✖${fmt.reset} ${msg}`),
|
|
25
|
+
};
|
|
26
|
+
const DROP_FUNCTIONS_SQL = `
|
|
27
|
+
DROP FUNCTION IF EXISTS pg_ethiopian_to_datetime(timestamp);
|
|
28
|
+
DROP FUNCTION IF EXISTS pg_ethiopian_to_timestamp(timestamp);
|
|
29
|
+
DROP FUNCTION IF EXISTS pg_ethiopian_from_date(text);
|
|
30
|
+
DROP FUNCTION IF EXISTS pg_ethiopian_to_date(timestamp);
|
|
31
|
+
DROP FUNCTION IF EXISTS ethiopian_calendar_version();
|
|
32
|
+
DROP FUNCTION IF EXISTS current_ethiopian_date();
|
|
33
|
+
DROP FUNCTION IF EXISTS to_ethiopian_datetime(timestamp);
|
|
34
|
+
DROP FUNCTION IF EXISTS to_ethiopian_timestamp();
|
|
35
|
+
DROP FUNCTION IF EXISTS to_ethiopian_timestamp(timestamp);
|
|
36
|
+
DROP FUNCTION IF EXISTS from_ethiopian_date(text);
|
|
37
|
+
DROP FUNCTION IF EXISTS to_ethiopian_date();
|
|
38
|
+
DROP FUNCTION IF EXISTS to_ethiopian_date(timestamp);
|
|
39
|
+
DROP FUNCTION IF EXISTS _ethiopian_to_jdn(integer, integer, integer);
|
|
40
|
+
DROP FUNCTION IF EXISTS _jdn_to_ethiopian(integer);
|
|
41
|
+
DROP FUNCTION IF EXISTS _jdn_to_gregorian(integer);
|
|
42
|
+
DROP FUNCTION IF EXISTS _gregorian_to_jdn(integer, integer, integer);
|
|
43
|
+
`.trim();
|
|
44
|
+
function generateTypeOrmMigration(sql) {
|
|
45
|
+
const className = `EthiopianCalendar${Date.now()}`;
|
|
46
|
+
return `import { MigrationInterface, QueryRunner } from "typeorm";
|
|
47
|
+
|
|
48
|
+
export class ${className} implements MigrationInterface {
|
|
49
|
+
async up(queryRunner: QueryRunner): Promise<void> {
|
|
50
|
+
await queryRunner.query(\`${sql.replace(/`/g, "\\`")}\`);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async down(queryRunner: QueryRunner): Promise<void> {
|
|
54
|
+
await queryRunner.query(\`${DROP_FUNCTIONS_SQL}\`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
`;
|
|
58
|
+
}
|
|
59
|
+
function writeMigration(orm, outputPath) {
|
|
60
|
+
const dir = (0, path_1.dirname)(outputPath);
|
|
61
|
+
if (!(0, fs_1.existsSync)(dir)) {
|
|
62
|
+
(0, fs_1.mkdirSync)(dir, { recursive: true });
|
|
63
|
+
}
|
|
64
|
+
const sql = (0, index_1.getSql)();
|
|
65
|
+
const content = orm === "typeorm" ? generateTypeOrmMigration(sql) : sql;
|
|
66
|
+
(0, fs_1.writeFileSync)(outputPath, content);
|
|
67
|
+
}
|
|
68
|
+
const VALID_ORMS = ["prisma", "drizzle", "typeorm", "raw"];
|
|
69
|
+
const NEXT_STEPS = {
|
|
70
|
+
prisma: ["npx prisma migrate dev"],
|
|
71
|
+
drizzle: ["npx drizzle-kit migrate"],
|
|
72
|
+
typeorm: ["npx typeorm migration:run -d src/data-source.ts"],
|
|
73
|
+
raw: ["psql -d DATABASE -f ethiopian_calendar.sql"],
|
|
74
|
+
};
|
|
75
|
+
function showHelp() {
|
|
76
|
+
log(`
|
|
77
|
+
${fmt.cyan}${fmt.bold}Ethiopian Calendar for PostgreSQL${fmt.reset} ${fmt.yellow}v${index_1.VERSION}${fmt.reset}
|
|
78
|
+
|
|
79
|
+
${fmt.bold}Usage:${fmt.reset}
|
|
80
|
+
npx ethiopian-calendar init [orm]
|
|
81
|
+
npx ethiopian-calendar version
|
|
82
|
+
npx ethiopian-calendar migrations
|
|
83
|
+
|
|
84
|
+
${fmt.bold}ORMs:${fmt.reset}
|
|
85
|
+
prisma, drizzle, typeorm, raw
|
|
86
|
+
|
|
87
|
+
${fmt.bold}Examples:${fmt.reset}
|
|
88
|
+
npx ethiopian-calendar init ${fmt.cyan}# auto-detect${fmt.reset}
|
|
89
|
+
npx ethiopian-calendar init prisma
|
|
90
|
+
`);
|
|
91
|
+
}
|
|
92
|
+
function showVersion() {
|
|
93
|
+
log(`${fmt.bold}v${index_1.VERSION}${fmt.reset}`);
|
|
94
|
+
log(`\nCheck database: ${fmt.cyan}SELECT ethiopian_calendar_version();${fmt.reset}\n`);
|
|
95
|
+
}
|
|
96
|
+
function showMigrations() {
|
|
97
|
+
const migrations = (0, index_1.listMigrations)();
|
|
98
|
+
log(`\n${fmt.bold}Available Upgrades:${fmt.reset}`);
|
|
99
|
+
if (migrations.length === 0) {
|
|
100
|
+
log(" None\n");
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
migrations.forEach((m) => log(` ${m.from} → ${m.to}`));
|
|
104
|
+
log("");
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
function initMigration(ormArg) {
|
|
108
|
+
let orm;
|
|
109
|
+
if (ormArg) {
|
|
110
|
+
if (!VALID_ORMS.includes(ormArg)) {
|
|
111
|
+
print.error(`Unknown ORM: ${ormArg}`);
|
|
112
|
+
log(`\nSupported: ${VALID_ORMS.join(", ")}\n`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
orm = ormArg;
|
|
116
|
+
print.info(`Using: ${orm}`);
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
print.info("Detecting ORM...");
|
|
120
|
+
const detected = (0, index_1.detectOrm)();
|
|
121
|
+
if (!detected) {
|
|
122
|
+
print.error("No ORM detected");
|
|
123
|
+
log(`\nSpecify one: npx ethiopian-calendar init <${VALID_ORMS.join("|")}>\n`);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
orm = detected;
|
|
127
|
+
print.success(`Detected: ${orm}`);
|
|
128
|
+
}
|
|
129
|
+
const outputPath = (0, index_1.getMigrationPath)(orm);
|
|
130
|
+
try {
|
|
131
|
+
writeMigration(orm, outputPath);
|
|
132
|
+
print.success(`Created: ${outputPath}`);
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
print.error(`Failed: ${err instanceof Error ? err.message : err}`);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
log(`\n${fmt.bold}Next:${fmt.reset} ${NEXT_STEPS[orm][0]}`);
|
|
139
|
+
log(`\n${fmt.bold}Functions:${fmt.reset}`);
|
|
140
|
+
log(` to_ethiopian_date() → text ${fmt.cyan}# current date${fmt.reset}`);
|
|
141
|
+
log(` to_ethiopian_date(timestamp) → text`);
|
|
142
|
+
log(` from_ethiopian_date(text) → timestamp`);
|
|
143
|
+
log(` to_ethiopian_timestamp() → timestamp ${fmt.cyan}# current timestamp${fmt.reset}`);
|
|
144
|
+
log(` to_ethiopian_timestamp(ts) → timestamp\n`);
|
|
145
|
+
}
|
|
146
|
+
const [cmd, arg] = process.argv.slice(2);
|
|
147
|
+
switch (cmd) {
|
|
148
|
+
case undefined:
|
|
149
|
+
case "help":
|
|
150
|
+
case "--help":
|
|
151
|
+
case "-h":
|
|
152
|
+
showHelp();
|
|
153
|
+
break;
|
|
154
|
+
case "version":
|
|
155
|
+
case "--version":
|
|
156
|
+
case "-v":
|
|
157
|
+
showVersion();
|
|
158
|
+
break;
|
|
159
|
+
case "migrations":
|
|
160
|
+
showMigrations();
|
|
161
|
+
break;
|
|
162
|
+
case "init":
|
|
163
|
+
initMigration(arg);
|
|
164
|
+
break;
|
|
165
|
+
default:
|
|
166
|
+
print.error(`Unknown command: ${cmd}`);
|
|
167
|
+
showHelp();
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
//# sourceMappingURL=init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare const VERSION = "1.1.0";
|
|
2
|
+
export type SupportedORM = "prisma" | "drizzle" | "typeorm" | "raw";
|
|
3
|
+
/** Returns the full SQL migration content */
|
|
4
|
+
export declare function getSql(): string;
|
|
5
|
+
/** Returns path to the SQL migration file */
|
|
6
|
+
export declare function getSqlPath(): string;
|
|
7
|
+
/** Returns path to ORM-specific documentation */
|
|
8
|
+
export declare function getDocsPath(orm: SupportedORM): string;
|
|
9
|
+
/** Checks if an ORM package is installed */
|
|
10
|
+
export declare function isOrmInstalled(orm: Exclude<SupportedORM, "raw">): boolean;
|
|
11
|
+
/** Auto-detects installed ORM, returns null if none found */
|
|
12
|
+
export declare function detectOrm(): Exclude<SupportedORM, "raw"> | null;
|
|
13
|
+
/** Returns the migration output path for the specified ORM */
|
|
14
|
+
export declare function getMigrationPath(orm: SupportedORM, name?: string): string;
|
|
15
|
+
/** Returns path to version upgrade migration, or null if not found */
|
|
16
|
+
export declare function getUpgradePath(from: string, to: string): string | null;
|
|
17
|
+
/** Returns upgrade migration SQL content, or null if not found */
|
|
18
|
+
export declare function getUpgradeSql(from: string, to: string): string | null;
|
|
19
|
+
export interface Migration {
|
|
20
|
+
from: string;
|
|
21
|
+
to: string;
|
|
22
|
+
path: string;
|
|
23
|
+
}
|
|
24
|
+
/** Lists all available upgrade migrations */
|
|
25
|
+
export declare function listMigrations(): Migration[];
|
|
26
|
+
/** SQL query to check installed version in database */
|
|
27
|
+
export declare function getVersionCheckSql(): string;
|
|
28
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,OAAO,UAAU,CAAC;AAE/B,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,KAAK,CAAC;AAWpE,6CAA6C;AAC7C,wBAAgB,MAAM,IAAI,MAAM,CAE/B;AAED,6CAA6C;AAC7C,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAED,iDAAiD;AACjD,wBAAgB,WAAW,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAErD;AAED,4CAA4C;AAC5C,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,GAAG,OAAO,CAOzE;AAED,6DAA6D;AAC7D,wBAAgB,SAAS,IAAI,OAAO,CAAC,YAAY,EAAE,KAAK,CAAC,GAAG,IAAI,CAG/D;AAOD,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,EAAE,IAAI,SAAuB,GAAG,MAAM,CASvF;AAED,sEAAsE;AACtE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGtE;AAED,kEAAkE;AAClE,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGrE;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,6CAA6C;AAC7C,wBAAgB,cAAc,IAAI,SAAS,EAAE,CAY5C;AAED,uDAAuD;AACvD,wBAAgB,kBAAkB,IAAI,MAAM,CAK3C"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VERSION = void 0;
|
|
4
|
+
exports.getSql = getSql;
|
|
5
|
+
exports.getSqlPath = getSqlPath;
|
|
6
|
+
exports.getDocsPath = getDocsPath;
|
|
7
|
+
exports.isOrmInstalled = isOrmInstalled;
|
|
8
|
+
exports.detectOrm = detectOrm;
|
|
9
|
+
exports.getMigrationPath = getMigrationPath;
|
|
10
|
+
exports.getUpgradePath = getUpgradePath;
|
|
11
|
+
exports.getUpgradeSql = getUpgradeSql;
|
|
12
|
+
exports.listMigrations = listMigrations;
|
|
13
|
+
exports.getVersionCheckSql = getVersionCheckSql;
|
|
14
|
+
const fs_1 = require("fs");
|
|
15
|
+
const path_1 = require("path");
|
|
16
|
+
exports.VERSION = "1.1.0";
|
|
17
|
+
const ORM_PACKAGES = {
|
|
18
|
+
prisma: "@prisma/client",
|
|
19
|
+
drizzle: "drizzle-orm",
|
|
20
|
+
typeorm: "typeorm",
|
|
21
|
+
};
|
|
22
|
+
const SQL_DIR = (0, path_1.join)((0, path_1.dirname)(__dirname), "sql");
|
|
23
|
+
const DOCS_DIR = (0, path_1.join)((0, path_1.dirname)(__dirname), "docs");
|
|
24
|
+
/** Returns the full SQL migration content */
|
|
25
|
+
function getSql() {
|
|
26
|
+
return (0, fs_1.readFileSync)((0, path_1.join)(SQL_DIR, "ethiopian_calendar.sql"), "utf8");
|
|
27
|
+
}
|
|
28
|
+
/** Returns path to the SQL migration file */
|
|
29
|
+
function getSqlPath() {
|
|
30
|
+
return (0, path_1.join)(SQL_DIR, "ethiopian_calendar.sql");
|
|
31
|
+
}
|
|
32
|
+
/** Returns path to ORM-specific documentation */
|
|
33
|
+
function getDocsPath(orm) {
|
|
34
|
+
return (0, path_1.join)(DOCS_DIR, `${orm}.md`);
|
|
35
|
+
}
|
|
36
|
+
/** Checks if an ORM package is installed */
|
|
37
|
+
function isOrmInstalled(orm) {
|
|
38
|
+
try {
|
|
39
|
+
require.resolve(ORM_PACKAGES[orm]);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Auto-detects installed ORM, returns null if none found */
|
|
47
|
+
function detectOrm() {
|
|
48
|
+
const orms = Object.keys(ORM_PACKAGES);
|
|
49
|
+
return orms.find(isOrmInstalled) ?? null;
|
|
50
|
+
}
|
|
51
|
+
/** Generates timestamp for migration filenames */
|
|
52
|
+
function timestamp() {
|
|
53
|
+
return new Date().toISOString().replace(/[-:T]/g, "").slice(0, 14);
|
|
54
|
+
}
|
|
55
|
+
/** Returns the migration output path for the specified ORM */
|
|
56
|
+
function getMigrationPath(orm, name = "ethiopian_calendar") {
|
|
57
|
+
const ts = timestamp();
|
|
58
|
+
const paths = {
|
|
59
|
+
prisma: (0, path_1.join)("prisma", "migrations", `${ts}_${name}`, "migration.sql"),
|
|
60
|
+
drizzle: (0, path_1.join)("drizzle", `${ts}_${name}.sql`),
|
|
61
|
+
typeorm: (0, path_1.join)("src", "migrations", `${ts}-${name}.ts`),
|
|
62
|
+
raw: `${name}.sql`,
|
|
63
|
+
};
|
|
64
|
+
return paths[orm];
|
|
65
|
+
}
|
|
66
|
+
/** Returns path to version upgrade migration, or null if not found */
|
|
67
|
+
function getUpgradePath(from, to) {
|
|
68
|
+
const file = (0, path_1.join)(SQL_DIR, "migrations", `${from}_to_${to}.sql`);
|
|
69
|
+
return (0, fs_1.existsSync)(file) ? file : null;
|
|
70
|
+
}
|
|
71
|
+
/** Returns upgrade migration SQL content, or null if not found */
|
|
72
|
+
function getUpgradeSql(from, to) {
|
|
73
|
+
const file = getUpgradePath(from, to);
|
|
74
|
+
return file ? (0, fs_1.readFileSync)(file, "utf8") : null;
|
|
75
|
+
}
|
|
76
|
+
/** Lists all available upgrade migrations */
|
|
77
|
+
function listMigrations() {
|
|
78
|
+
const dir = (0, path_1.join)(SQL_DIR, "migrations");
|
|
79
|
+
if (!(0, fs_1.existsSync)(dir))
|
|
80
|
+
return [];
|
|
81
|
+
const pattern = /^(\d+\.\d+\.\d+)_to_(\d+\.\d+\.\d+)\.sql$/;
|
|
82
|
+
return (0, fs_1.readdirSync)(dir)
|
|
83
|
+
.map((file) => {
|
|
84
|
+
const match = file.match(pattern);
|
|
85
|
+
return match ? { from: match[1], to: match[2], path: (0, path_1.join)(dir, file) } : null;
|
|
86
|
+
})
|
|
87
|
+
.filter((m) => m !== null);
|
|
88
|
+
}
|
|
89
|
+
/** SQL query to check installed version in database */
|
|
90
|
+
function getVersionCheckSql() {
|
|
91
|
+
return `SELECT COALESCE(
|
|
92
|
+
(SELECT proname FROM pg_proc WHERE proname = 'ethiopian_calendar_version' LIMIT 1),
|
|
93
|
+
NULL
|
|
94
|
+
)::text AS installed, '${exports.VERSION}' AS latest;`;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/docs/drizzle.md
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
# Using Ethiopian Calendar with Drizzle ORM
|
|
2
|
+
|
|
3
|
+
## Quick Setup
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
# Install the package
|
|
7
|
+
npm install @huluwz/pg-ethiopian-calendar
|
|
8
|
+
|
|
9
|
+
# Generate migration
|
|
10
|
+
npx ethiopian-calendar init drizzle
|
|
11
|
+
|
|
12
|
+
# Apply migration
|
|
13
|
+
npx drizzle-kit migrate
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Manual Setup
|
|
17
|
+
|
|
18
|
+
If you prefer to set up manually:
|
|
19
|
+
|
|
20
|
+
### 1. Create Migration File
|
|
21
|
+
|
|
22
|
+
Copy the SQL from `node_modules/@huluwz/pg-ethiopian-calendar/sql/ethiopian_calendar.sql` to your Drizzle migrations folder (e.g., `drizzle/0001_ethiopian_calendar.sql`)
|
|
23
|
+
|
|
24
|
+
### 2. Apply Migration
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npx drizzle-kit migrate
|
|
28
|
+
# or
|
|
29
|
+
npx drizzle-kit push
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage Examples
|
|
33
|
+
|
|
34
|
+
### Schema with Generated Columns
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
import { pgTable, serial, timestamp, text } from 'drizzle-orm/pg-core'
|
|
38
|
+
import { sql } from 'drizzle-orm'
|
|
39
|
+
|
|
40
|
+
export const orders = pgTable('orders', {
|
|
41
|
+
id: serial('id').primaryKey(),
|
|
42
|
+
customerName: text('customer_name').notNull(),
|
|
43
|
+
createdAt: timestamp('created_at').defaultNow(),
|
|
44
|
+
|
|
45
|
+
// Generated column - Ethiopian date calculated automatically
|
|
46
|
+
createdAtEthiopian: timestamp('created_at_ethiopian')
|
|
47
|
+
.generatedAlwaysAs(sql`to_ethiopian_timestamp(created_at)`),
|
|
48
|
+
})
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Using in Queries
|
|
52
|
+
|
|
53
|
+
```typescript
|
|
54
|
+
import { db } from './db'
|
|
55
|
+
import { orders } from './schema'
|
|
56
|
+
import { sql } from 'drizzle-orm'
|
|
57
|
+
|
|
58
|
+
// Insert - Ethiopian date is auto-generated
|
|
59
|
+
const [order] = await db.insert(orders)
|
|
60
|
+
.values({ customerName: 'Abebe Kebede' })
|
|
61
|
+
.returning()
|
|
62
|
+
|
|
63
|
+
console.log(order.createdAtEthiopian) // Ethiopian timestamp!
|
|
64
|
+
|
|
65
|
+
// Get current Ethiopian date
|
|
66
|
+
const result = await db.execute(
|
|
67
|
+
sql`SELECT current_ethiopian_date() as today`
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
// Convert a specific date
|
|
71
|
+
const converted = await db.execute(
|
|
72
|
+
sql`SELECT to_ethiopian_date('2026-01-01'::timestamp) as ethiopian`
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
// Filter by Ethiopian date
|
|
76
|
+
const filtered = await db
|
|
77
|
+
.select()
|
|
78
|
+
.from(orders)
|
|
79
|
+
.where(sql`to_ethiopian_date(created_at) = '2018-04-23'`)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Creating Table with Raw SQL
|
|
83
|
+
|
|
84
|
+
If you prefer to create the table in a migration:
|
|
85
|
+
|
|
86
|
+
```sql
|
|
87
|
+
CREATE TABLE orders (
|
|
88
|
+
id SERIAL PRIMARY KEY,
|
|
89
|
+
customer_name TEXT NOT NULL,
|
|
90
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
91
|
+
created_at_ethiopian TIMESTAMP GENERATED ALWAYS AS
|
|
92
|
+
(to_ethiopian_timestamp(created_at)) STORED
|
|
93
|
+
);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Functional Index
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
import { index } from 'drizzle-orm/pg-core'
|
|
100
|
+
import { sql } from 'drizzle-orm'
|
|
101
|
+
|
|
102
|
+
// In your schema or migration
|
|
103
|
+
export const ordersEthiopianIdx = index('idx_orders_ethiopian')
|
|
104
|
+
.on(sql`to_ethiopian_date(created_at)`)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Or in raw SQL:
|
|
108
|
+
|
|
109
|
+
```sql
|
|
110
|
+
CREATE INDEX idx_orders_ethiopian
|
|
111
|
+
ON orders (to_ethiopian_date(created_at));
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Available Functions
|
|
115
|
+
|
|
116
|
+
| Function | Description |
|
|
117
|
+
|----------|-------------|
|
|
118
|
+
| `to_ethiopian_date(timestamp)` | Convert to Ethiopian date string (YYYY-MM-DD) |
|
|
119
|
+
| `from_ethiopian_date(text)` | Convert Ethiopian date to Gregorian timestamp |
|
|
120
|
+
| `to_ethiopian_timestamp(timestamp)` | Convert preserving time (for generated columns) |
|
|
121
|
+
| `current_ethiopian_date()` | Get current Ethiopian date |
|
|
122
|
+
|
|
123
|
+
## Using sql Template
|
|
124
|
+
|
|
125
|
+
All functions can be used with Drizzle's `sql` template:
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { sql } from 'drizzle-orm'
|
|
129
|
+
|
|
130
|
+
// In select
|
|
131
|
+
const result = await db.select({
|
|
132
|
+
id: orders.id,
|
|
133
|
+
ethiopianDate: sql<string>`to_ethiopian_date(${orders.createdAt})`,
|
|
134
|
+
}).from(orders)
|
|
135
|
+
|
|
136
|
+
// In where
|
|
137
|
+
const filtered = await db.select()
|
|
138
|
+
.from(orders)
|
|
139
|
+
.where(sql`to_ethiopian_date(${orders.createdAt}) > '2018-01-01'`)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Links
|
|
143
|
+
|
|
144
|
+
- [GitHub Repository](https://github.com/HuluWZ/pg-ethiopian-calendar)
|
|
145
|
+
- [NPM Package](https://www.npmjs.com/package/@huluwz/pg-ethiopian-calendar)
|
|
146
|
+
- [Drizzle ORM Docs](https://orm.drizzle.team/)
|
|
147
|
+
|