greenseed 0.1.4 → 0.1.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/README.md +61 -0
- package/dist/cli.cjs +33 -0
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +33 -0
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Greenseed
|
|
2
|
+
|
|
3
|
+
Greenseed is a small CLI tool to seed a PostgreSQL database from JSON files.
|
|
4
|
+
|
|
5
|
+
It reads data from JSON files and inserts it into database tables safely using primary keys.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install greenseed
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
greenseed push
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Options:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
-c, --config <file> Config file (default: seed.config.json)
|
|
23
|
+
-d, --dbvar <name> Override database env var name
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Config file
|
|
27
|
+
|
|
28
|
+
Create a `seed.config.json` file.
|
|
29
|
+
|
|
30
|
+
Example:
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"seedFileExtensions": [".json"],
|
|
35
|
+
"databaseUrlEnvVar": "DATABASE_URL",
|
|
36
|
+
"onMissingFile": "error",
|
|
37
|
+
"tables": [
|
|
38
|
+
{
|
|
39
|
+
"table": "users",
|
|
40
|
+
"schema": "public",
|
|
41
|
+
"primaryKeys": ["id"],
|
|
42
|
+
"source": "./data/users.json"
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## JSON data
|
|
49
|
+
|
|
50
|
+
Each source file must be a JSON array.
|
|
51
|
+
|
|
52
|
+
Example:
|
|
53
|
+
|
|
54
|
+
```json
|
|
55
|
+
[
|
|
56
|
+
{ "id": 1, "name": "Alice" },
|
|
57
|
+
{ "id": 2, "name": "Bob" }
|
|
58
|
+
]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Extra fields not in the table are ignored.
|
package/dist/cli.cjs
CHANGED
|
@@ -204,6 +204,30 @@ async function run(opts) {
|
|
|
204
204
|
await sql.end();
|
|
205
205
|
console.log(C.green + "Seed completed" + C.reset);
|
|
206
206
|
}
|
|
207
|
+
var DEFAULT_CONFIG = {
|
|
208
|
+
seedFileExtensions: [".json"],
|
|
209
|
+
databaseUrlEnvVar: "DATABASE_URL",
|
|
210
|
+
onMissingFile: "error",
|
|
211
|
+
tables: [
|
|
212
|
+
{
|
|
213
|
+
table: "example_table",
|
|
214
|
+
schema: "public",
|
|
215
|
+
primaryKeys: ["id"],
|
|
216
|
+
source: "./data/example_table.json"
|
|
217
|
+
}
|
|
218
|
+
]
|
|
219
|
+
};
|
|
220
|
+
function init(configPath) {
|
|
221
|
+
const resolved = path2__default.default.resolve(process.cwd(), configPath);
|
|
222
|
+
if (fs__default.default.existsSync(resolved)) {
|
|
223
|
+
throw new Error("Config already exists: " + resolved);
|
|
224
|
+
}
|
|
225
|
+
fs__default.default.writeFileSync(
|
|
226
|
+
resolved,
|
|
227
|
+
JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n",
|
|
228
|
+
"utf8"
|
|
229
|
+
);
|
|
230
|
+
}
|
|
207
231
|
|
|
208
232
|
// src/cli.ts
|
|
209
233
|
var prog = sade__default.default("greenseed");
|
|
@@ -215,6 +239,15 @@ prog.command("push").option("-c, --config <file>", "Config file", "seed.config.j
|
|
|
215
239
|
process.exit(1);
|
|
216
240
|
}
|
|
217
241
|
});
|
|
242
|
+
prog.command("init").option("-c, --config <file>", "Config file", "seed.config.json").action((opts) => {
|
|
243
|
+
try {
|
|
244
|
+
init(opts.config);
|
|
245
|
+
console.log("\x1B[32mConfig created: " + opts.config + "\x1B[0m");
|
|
246
|
+
} catch (err) {
|
|
247
|
+
console.error("\x1B[31m" + String(err) + "\x1B[0m");
|
|
248
|
+
process.exit(1);
|
|
249
|
+
}
|
|
250
|
+
});
|
|
218
251
|
prog.parse(process.argv);
|
|
219
252
|
//# sourceMappingURL=cli.cjs.map
|
|
220
253
|
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/db.ts","../src/fs.ts","../src/app.ts","../src/cli.ts"],"names":["postgres","fs","path","v","dotenv","sade"],"mappings":"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","file":"cli.cjs","sourcesContent":["import postgres from \"postgres\"\n\nexport function connect(url: string): postgres.Sql {\n\treturn postgres(url, { max: 10, idle_timeout: 20 })\n}\n\nexport async function check(sql: postgres.Sql): Promise<void> {\n\ttry {\n\t\tawait sql`select 1`\n\t} catch (err) {\n\t\tthrow new Error(\"Database connection failed\")\n\t}\n}\n\nexport async function tableExists(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<boolean> {\n\tconst res = await sql<{ exists: boolean }[]>`\n\t\tselect to_regclass(${\"\\\"\" + schema + \"\\\".\\\"\" + table + \"\\\"\"}) is not null as exists\n\t`\n\tif (!res[0]) {\n\t\tthrow new Error(\"Failed checking table: \" + schema + \".\" + table)\n\t}\n\treturn res[0].exists\n}\n\nexport async function validateTables(\n\tsql: postgres.Sql,\n\ttables: { schema: string; table: string }[],\n): Promise<void> {\n\tfor (const t of tables) {\n\t\tconst ok = await tableExists(sql, t.schema, t.table)\n\t\tif (!ok) {\n\t\t\tthrow new Error(\"Missing table: \" + t.schema + \".\" + t.table)\n\t\t}\n\t}\n}\n\nexport async function metadata(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<{ columns: string[] }> {\n\tconst cols = await sql<{ column_name: string }[]>`\n\t\tselect column_name\n\t\tfrom information_schema.columns\n\t\twhere table_schema = ${schema}\n\t\tand table_name = ${table}\n\t\torder by ordinal_position\n\t`\n\treturn { columns: cols.map((c) => c.column_name) }\n}\n\nexport async function insert(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n\trows: Record<string, any>[],\n\tcolumns: string[],\n\tprimaryKeys: string[],\n): Promise<number> {\n\tif (primaryKeys.length === 0) {\n\t\tthrow new Error(\"No primary keys for \" + schema + \".\" + table)\n\t}\n\n\tconst q = await sql`\n\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t${sql(rows, columns)}\n\t\ton conflict (${sql(primaryKeys)}) do nothing\n\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t`\n\treturn q.length\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nexport function exists(p: string): boolean {\n\treturn fs.existsSync(p)\n}\n\nexport function readFile(p: string): string {\n\ttry {\n\t\treturn fs.readFileSync(p, \"utf8\")\n\t} catch (err) {\n\t\tthrow new Error(\"Failed to read file: \" + p)\n\t}\n}\n\nexport function resolvePath(p: string): string {\n\treturn path.resolve(process.cwd(), p)\n}\n\nexport function ext(p: string): string {\n\treturn path.extname(p).toLowerCase()\n}\n\nexport function loadJsonArray(p: string, onMissing: \"skip\" | \"error\"): any[] {\n\tif (!exists(p)) {\n\t\tif (onMissing === \"skip\") {\n\t\t\treturn []\n\t\t}\n\t\tthrow new Error(\"Source file not found: \" + p)\n\t}\n\n\tconst raw = readFile(p)\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\tthrow new Error(\"Invalid JSON: \" + p)\n\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error(\"Data is not an array: \" + p)\n\t}\n\n\treturn parsed\n}\n\nexport function chunk<T>(data: T[], size: number): T[][] {\n\tconst out: T[][] = []\n\tfor (let i = 0; i < data.length; i += size) {\n\t\tout.push(data.slice(i, i + size))\n\t}\n\treturn out\n}\n","import * as v from \"valibot\"\nimport dotenv from \"dotenv\"\nimport path from \"path\"\nimport { connect, check, validateTables, metadata, insert } from \"./db\"\nimport { resolvePath, loadJsonArray, chunk, readFile } from \"./fs\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tgreen: \"\\x1b[32m\",\n\tyellow: \"\\x1b[33m\",\n\tred: \"\\x1b[31m\",\n\tgray: \"\\x1b[90m\",\n}\n\nconst ConfigSchema = v.object({\n\tseedFileExtensions: v.array(v.picklist([\".json\"])),\n\tdatabaseUrlEnvVar: v.string(),\n\tonMissingFile: v.optional(v.picklist([\"skip\", \"error\"]), \"error\"),\n\ttables: v.array(\n\t\tv.object({\n\t\t\ttable: v.string(),\n\t\t\tschema: v.optional(v.string(), \"public\"),\n\t\t\tprimaryKeys: v.array(v.string()),\n\t\t\tsource: v.string(),\n\t\t}),\n\t),\n})\n\ntype Opts = { config: string; dbvar?: string }\n\nexport async function run(opts: Opts): Promise<void> {\n\tdotenv.config()\n\n\tconst configPath = resolvePath(opts.config || \"seed.config.json\")\n\n\tlet raw: unknown\n\ttry {\n\t\traw = JSON.parse(readFile(configPath))\n\t} catch (e) {\n\t\tthrow new Error(\n\t\t\t`Failed to load config at ${configPath}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t)\n\t}\n\n\tconst parsed = v.safeParse(ConfigSchema, raw)\n\n\tif (!parsed.success) {\n\t\tthrow new Error(\"Invalid configuration\")\n\t}\n\n\tconst config = parsed.output\n\tconst dbVar =\n\t\topts.dbvar && opts.dbvar.length > 0 ? opts.dbvar : config.databaseUrlEnvVar\n\n\tconst dbUrl = process.env[dbVar]\n\tif (!dbUrl) {\n\t\tthrow new Error(\"Missing DB env var: \" + dbVar)\n\t}\n\n\tconst sql = connect(dbUrl)\n\tawait check(sql)\n\n\tawait validateTables(\n\t\tsql,\n\t\tconfig.tables.map((t) => ({\n\t\t\ttable: t.table,\n\t\t\tschema: t.schema,\n\t\t})),\n\t)\n\n\tconsole.log(C.gray + \"Starting seed process\" + C.reset)\n\n\tfor (const t of config.tables) {\n\t\tconst source = path.resolve(path.dirname(configPath), t.source)\n\t\tconst rows = loadJsonArray(source, config.onMissingFile || \"error\")\n\n\t\tif (rows.length === 0) {\n\t\t\tconsole.log(C.yellow + \"Skipped \" + t.table + C.reset)\n\t\t\tcontinue\n\t\t}\n\n\t\tconst meta = await metadata(sql, t.schema, t.table)\n\t\tconst validCols = Object.keys(rows[0]).filter((c) =>\n\t\t\tmeta.columns.includes(c),\n\t\t)\n\n\t\tlet inserted = 0\n\t\tfor (const part of chunk(rows, 1000)) {\n\t\t\tinserted += await insert(\n\t\t\t\tsql,\n\t\t\t\tt.schema,\n\t\t\t\tt.table,\n\t\t\t\tpart,\n\t\t\t\tvalidCols,\n\t\t\t\tt.primaryKeys,\n\t\t\t)\n\t\t}\n\n\t\tconsole.log(\n\t\t\tC.green +\n\t\t\t\tt.schema +\n\t\t\t\t\".\" +\n\t\t\t\tt.table +\n\t\t\t\t\": \" +\n\t\t\t\tinserted +\n\t\t\t\t\"/\" +\n\t\t\t\trows.length +\n\t\t\t\tC.reset,\n\t\t)\n\t}\n\n\tawait sql.end()\n\tconsole.log(C.green + \"Seed completed\" + C.reset)\n}\n","#!/usr/bin/env node\nimport sade from \"sade\"\nimport { run } from \"./app\"\n\nconst prog = sade(\"greenseed\")\n\nprog\n\t.command(\"push\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.option(\"-d, --dbvar <name>\", \"DB env var override\")\n\t.action(async (opts) => {\n\t\ttry {\n\t\t\tawait run(opts)\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog.parse(process.argv)\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/db.ts","../src/fs.ts","../src/app.ts","../src/init.ts","../src/cli.ts"],"names":["postgres","fs","path","v","dotenv","sade"],"mappings":"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","file":"cli.cjs","sourcesContent":["import postgres from \"postgres\"\n\nexport function connect(url: string): postgres.Sql {\n\treturn postgres(url, { max: 10, idle_timeout: 20 })\n}\n\nexport async function check(sql: postgres.Sql): Promise<void> {\n\ttry {\n\t\tawait sql`select 1`\n\t} catch (err) {\n\t\tthrow new Error(\"Database connection failed\")\n\t}\n}\n\nexport async function tableExists(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<boolean> {\n\tconst res = await sql<{ exists: boolean }[]>`\n\t\tselect to_regclass(${\"\\\"\" + schema + \"\\\".\\\"\" + table + \"\\\"\"}) is not null as exists\n\t`\n\tif (!res[0]) {\n\t\tthrow new Error(\"Failed checking table: \" + schema + \".\" + table)\n\t}\n\treturn res[0].exists\n}\n\nexport async function validateTables(\n\tsql: postgres.Sql,\n\ttables: { schema: string; table: string }[],\n): Promise<void> {\n\tfor (const t of tables) {\n\t\tconst ok = await tableExists(sql, t.schema, t.table)\n\t\tif (!ok) {\n\t\t\tthrow new Error(\"Missing table: \" + t.schema + \".\" + t.table)\n\t\t}\n\t}\n}\n\nexport async function metadata(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<{ columns: string[] }> {\n\tconst cols = await sql<{ column_name: string }[]>`\n\t\tselect column_name\n\t\tfrom information_schema.columns\n\t\twhere table_schema = ${schema}\n\t\tand table_name = ${table}\n\t\torder by ordinal_position\n\t`\n\treturn { columns: cols.map((c) => c.column_name) }\n}\n\nexport async function insert(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n\trows: Record<string, any>[],\n\tcolumns: string[],\n\tprimaryKeys: string[],\n): Promise<number> {\n\tif (primaryKeys.length === 0) {\n\t\tthrow new Error(\"No primary keys for \" + schema + \".\" + table)\n\t}\n\n\tconst q = await sql`\n\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t${sql(rows, columns)}\n\t\ton conflict (${sql(primaryKeys)}) do nothing\n\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t`\n\treturn q.length\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nexport function exists(p: string): boolean {\n\treturn fs.existsSync(p)\n}\n\nexport function readFile(p: string): string {\n\ttry {\n\t\treturn fs.readFileSync(p, \"utf8\")\n\t} catch (err) {\n\t\tthrow new Error(\"Failed to read file: \" + p)\n\t}\n}\n\nexport function resolvePath(p: string): string {\n\treturn path.resolve(process.cwd(), p)\n}\n\nexport function ext(p: string): string {\n\treturn path.extname(p).toLowerCase()\n}\n\nexport function loadJsonArray(p: string, onMissing: \"skip\" | \"error\"): any[] {\n\tif (!exists(p)) {\n\t\tif (onMissing === \"skip\") {\n\t\t\treturn []\n\t\t}\n\t\tthrow new Error(\"Source file not found: \" + p)\n\t}\n\n\tconst raw = readFile(p)\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\tthrow new Error(\"Invalid JSON: \" + p)\n\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error(\"Data is not an array: \" + p)\n\t}\n\n\treturn parsed\n}\n\nexport function chunk<T>(data: T[], size: number): T[][] {\n\tconst out: T[][] = []\n\tfor (let i = 0; i < data.length; i += size) {\n\t\tout.push(data.slice(i, i + size))\n\t}\n\treturn out\n}\n","import * as v from \"valibot\"\nimport dotenv from \"dotenv\"\nimport path from \"path\"\nimport { connect, check, validateTables, metadata, insert } from \"./db\"\nimport { resolvePath, loadJsonArray, chunk, readFile } from \"./fs\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tgreen: \"\\x1b[32m\",\n\tyellow: \"\\x1b[33m\",\n\tred: \"\\x1b[31m\",\n\tgray: \"\\x1b[90m\",\n}\n\nconst ConfigSchema = v.object({\n\tseedFileExtensions: v.array(v.picklist([\".json\"])),\n\tdatabaseUrlEnvVar: v.string(),\n\tonMissingFile: v.optional(v.picklist([\"skip\", \"error\"]), \"error\"),\n\ttables: v.array(\n\t\tv.object({\n\t\t\ttable: v.string(),\n\t\t\tschema: v.optional(v.string(), \"public\"),\n\t\t\tprimaryKeys: v.array(v.string()),\n\t\t\tsource: v.string(),\n\t\t}),\n\t),\n})\n\ntype Opts = { config: string; dbvar?: string }\n\nexport async function run(opts: Opts): Promise<void> {\n\tdotenv.config()\n\n\tconst configPath = resolvePath(opts.config || \"seed.config.json\")\n\n\tlet raw: unknown\n\ttry {\n\t\traw = JSON.parse(readFile(configPath))\n\t} catch (e) {\n\t\tthrow new Error(\n\t\t\t`Failed to load config at ${configPath}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t)\n\t}\n\n\tconst parsed = v.safeParse(ConfigSchema, raw)\n\n\tif (!parsed.success) {\n\t\tthrow new Error(\"Invalid configuration\")\n\t}\n\n\tconst config = parsed.output\n\tconst dbVar =\n\t\topts.dbvar && opts.dbvar.length > 0 ? opts.dbvar : config.databaseUrlEnvVar\n\n\tconst dbUrl = process.env[dbVar]\n\tif (!dbUrl) {\n\t\tthrow new Error(\"Missing DB env var: \" + dbVar)\n\t}\n\n\tconst sql = connect(dbUrl)\n\tawait check(sql)\n\n\tawait validateTables(\n\t\tsql,\n\t\tconfig.tables.map((t) => ({\n\t\t\ttable: t.table,\n\t\t\tschema: t.schema,\n\t\t})),\n\t)\n\n\tconsole.log(C.gray + \"Starting seed process\" + C.reset)\n\n\tfor (const t of config.tables) {\n\t\tconst source = path.resolve(path.dirname(configPath), t.source)\n\t\tconst rows = loadJsonArray(source, config.onMissingFile || \"error\")\n\n\t\tif (rows.length === 0) {\n\t\t\tconsole.log(C.yellow + \"Skipped \" + t.table + C.reset)\n\t\t\tcontinue\n\t\t}\n\n\t\tconst meta = await metadata(sql, t.schema, t.table)\n\t\tconst validCols = Object.keys(rows[0]).filter((c) =>\n\t\t\tmeta.columns.includes(c),\n\t\t)\n\n\t\tlet inserted = 0\n\t\tfor (const part of chunk(rows, 1000)) {\n\t\t\tinserted += await insert(\n\t\t\t\tsql,\n\t\t\t\tt.schema,\n\t\t\t\tt.table,\n\t\t\t\tpart,\n\t\t\t\tvalidCols,\n\t\t\t\tt.primaryKeys,\n\t\t\t)\n\t\t}\n\n\t\tconsole.log(\n\t\t\tC.green +\n\t\t\t\tt.schema +\n\t\t\t\t\".\" +\n\t\t\t\tt.table +\n\t\t\t\t\": \" +\n\t\t\t\tinserted +\n\t\t\t\t\"/\" +\n\t\t\t\trows.length +\n\t\t\t\tC.reset,\n\t\t)\n\t}\n\n\tawait sql.end()\n\tconsole.log(C.green + \"Seed completed\" + C.reset)\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nconst DEFAULT_CONFIG = {\n\tseedFileExtensions: [\".json\"],\n\tdatabaseUrlEnvVar: \"DATABASE_URL\",\n\tonMissingFile: \"error\",\n\ttables: [\n\t\t{\n\t\t\ttable: \"example_table\",\n\t\t\tschema: \"public\",\n\t\t\tprimaryKeys: [\"id\"],\n\t\t\tsource: \"./data/example_table.json\",\n\t\t},\n\t],\n}\n\nexport function init(configPath: string): void {\n\tconst resolved = path.resolve(process.cwd(), configPath)\n\n\tif (fs.existsSync(resolved)) {\n\t\tthrow new Error(\"Config already exists: \" + resolved)\n\t}\n\n\tfs.writeFileSync(\n\t\tresolved,\n\t\tJSON.stringify(DEFAULT_CONFIG, null, 2) + \"\\n\",\n\t\t\"utf8\",\n\t)\n}\n","#!/usr/bin/env node\nimport sade from \"sade\"\nimport { run } from \"./app\"\nimport { init } from \"./init\"\n\nconst prog = sade(\"greenseed\")\n\nprog\n\t.command(\"push\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.option(\"-d, --dbvar <name>\", \"DB env var override\")\n\t.action(async (opts) => {\n\t\ttry {\n\t\t\tawait run(opts)\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog\n\t.command(\"init\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.action((opts) => {\n\t\ttry {\n\t\t\tinit(opts.config)\n\t\t\tconsole.log(\"\\x1b[32mConfig created: \" + opts.config + \"\\x1b[0m\")\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog.parse(process.argv)\n"]}
|
package/dist/cli.js
CHANGED
|
@@ -175,6 +175,30 @@ async function run(opts) {
|
|
|
175
175
|
await sql.end();
|
|
176
176
|
console.log(C.green + "Seed completed" + C.reset);
|
|
177
177
|
}
|
|
178
|
+
var DEFAULT_CONFIG = {
|
|
179
|
+
seedFileExtensions: [".json"],
|
|
180
|
+
databaseUrlEnvVar: "DATABASE_URL",
|
|
181
|
+
onMissingFile: "error",
|
|
182
|
+
tables: [
|
|
183
|
+
{
|
|
184
|
+
table: "example_table",
|
|
185
|
+
schema: "public",
|
|
186
|
+
primaryKeys: ["id"],
|
|
187
|
+
source: "./data/example_table.json"
|
|
188
|
+
}
|
|
189
|
+
]
|
|
190
|
+
};
|
|
191
|
+
function init(configPath) {
|
|
192
|
+
const resolved = path2.resolve(process.cwd(), configPath);
|
|
193
|
+
if (fs.existsSync(resolved)) {
|
|
194
|
+
throw new Error("Config already exists: " + resolved);
|
|
195
|
+
}
|
|
196
|
+
fs.writeFileSync(
|
|
197
|
+
resolved,
|
|
198
|
+
JSON.stringify(DEFAULT_CONFIG, null, 2) + "\n",
|
|
199
|
+
"utf8"
|
|
200
|
+
);
|
|
201
|
+
}
|
|
178
202
|
|
|
179
203
|
// src/cli.ts
|
|
180
204
|
var prog = sade("greenseed");
|
|
@@ -186,6 +210,15 @@ prog.command("push").option("-c, --config <file>", "Config file", "seed.config.j
|
|
|
186
210
|
process.exit(1);
|
|
187
211
|
}
|
|
188
212
|
});
|
|
213
|
+
prog.command("init").option("-c, --config <file>", "Config file", "seed.config.json").action((opts) => {
|
|
214
|
+
try {
|
|
215
|
+
init(opts.config);
|
|
216
|
+
console.log("\x1B[32mConfig created: " + opts.config + "\x1B[0m");
|
|
217
|
+
} catch (err) {
|
|
218
|
+
console.error("\x1B[31m" + String(err) + "\x1B[0m");
|
|
219
|
+
process.exit(1);
|
|
220
|
+
}
|
|
221
|
+
});
|
|
189
222
|
prog.parse(process.argv);
|
|
190
223
|
//# sourceMappingURL=cli.js.map
|
|
191
224
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/db.ts","../src/fs.ts","../src/app.ts","../src/cli.ts"],"names":["path"],"mappings":"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","file":"cli.js","sourcesContent":["import postgres from \"postgres\"\n\nexport function connect(url: string): postgres.Sql {\n\treturn postgres(url, { max: 10, idle_timeout: 20 })\n}\n\nexport async function check(sql: postgres.Sql): Promise<void> {\n\ttry {\n\t\tawait sql`select 1`\n\t} catch (err) {\n\t\tthrow new Error(\"Database connection failed\")\n\t}\n}\n\nexport async function tableExists(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<boolean> {\n\tconst res = await sql<{ exists: boolean }[]>`\n\t\tselect to_regclass(${\"\\\"\" + schema + \"\\\".\\\"\" + table + \"\\\"\"}) is not null as exists\n\t`\n\tif (!res[0]) {\n\t\tthrow new Error(\"Failed checking table: \" + schema + \".\" + table)\n\t}\n\treturn res[0].exists\n}\n\nexport async function validateTables(\n\tsql: postgres.Sql,\n\ttables: { schema: string; table: string }[],\n): Promise<void> {\n\tfor (const t of tables) {\n\t\tconst ok = await tableExists(sql, t.schema, t.table)\n\t\tif (!ok) {\n\t\t\tthrow new Error(\"Missing table: \" + t.schema + \".\" + t.table)\n\t\t}\n\t}\n}\n\nexport async function metadata(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<{ columns: string[] }> {\n\tconst cols = await sql<{ column_name: string }[]>`\n\t\tselect column_name\n\t\tfrom information_schema.columns\n\t\twhere table_schema = ${schema}\n\t\tand table_name = ${table}\n\t\torder by ordinal_position\n\t`\n\treturn { columns: cols.map((c) => c.column_name) }\n}\n\nexport async function insert(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n\trows: Record<string, any>[],\n\tcolumns: string[],\n\tprimaryKeys: string[],\n): Promise<number> {\n\tif (primaryKeys.length === 0) {\n\t\tthrow new Error(\"No primary keys for \" + schema + \".\" + table)\n\t}\n\n\tconst q = await sql`\n\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t${sql(rows, columns)}\n\t\ton conflict (${sql(primaryKeys)}) do nothing\n\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t`\n\treturn q.length\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nexport function exists(p: string): boolean {\n\treturn fs.existsSync(p)\n}\n\nexport function readFile(p: string): string {\n\ttry {\n\t\treturn fs.readFileSync(p, \"utf8\")\n\t} catch (err) {\n\t\tthrow new Error(\"Failed to read file: \" + p)\n\t}\n}\n\nexport function resolvePath(p: string): string {\n\treturn path.resolve(process.cwd(), p)\n}\n\nexport function ext(p: string): string {\n\treturn path.extname(p).toLowerCase()\n}\n\nexport function loadJsonArray(p: string, onMissing: \"skip\" | \"error\"): any[] {\n\tif (!exists(p)) {\n\t\tif (onMissing === \"skip\") {\n\t\t\treturn []\n\t\t}\n\t\tthrow new Error(\"Source file not found: \" + p)\n\t}\n\n\tconst raw = readFile(p)\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\tthrow new Error(\"Invalid JSON: \" + p)\n\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error(\"Data is not an array: \" + p)\n\t}\n\n\treturn parsed\n}\n\nexport function chunk<T>(data: T[], size: number): T[][] {\n\tconst out: T[][] = []\n\tfor (let i = 0; i < data.length; i += size) {\n\t\tout.push(data.slice(i, i + size))\n\t}\n\treturn out\n}\n","import * as v from \"valibot\"\nimport dotenv from \"dotenv\"\nimport path from \"path\"\nimport { connect, check, validateTables, metadata, insert } from \"./db\"\nimport { resolvePath, loadJsonArray, chunk, readFile } from \"./fs\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tgreen: \"\\x1b[32m\",\n\tyellow: \"\\x1b[33m\",\n\tred: \"\\x1b[31m\",\n\tgray: \"\\x1b[90m\",\n}\n\nconst ConfigSchema = v.object({\n\tseedFileExtensions: v.array(v.picklist([\".json\"])),\n\tdatabaseUrlEnvVar: v.string(),\n\tonMissingFile: v.optional(v.picklist([\"skip\", \"error\"]), \"error\"),\n\ttables: v.array(\n\t\tv.object({\n\t\t\ttable: v.string(),\n\t\t\tschema: v.optional(v.string(), \"public\"),\n\t\t\tprimaryKeys: v.array(v.string()),\n\t\t\tsource: v.string(),\n\t\t}),\n\t),\n})\n\ntype Opts = { config: string; dbvar?: string }\n\nexport async function run(opts: Opts): Promise<void> {\n\tdotenv.config()\n\n\tconst configPath = resolvePath(opts.config || \"seed.config.json\")\n\n\tlet raw: unknown\n\ttry {\n\t\traw = JSON.parse(readFile(configPath))\n\t} catch (e) {\n\t\tthrow new Error(\n\t\t\t`Failed to load config at ${configPath}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t)\n\t}\n\n\tconst parsed = v.safeParse(ConfigSchema, raw)\n\n\tif (!parsed.success) {\n\t\tthrow new Error(\"Invalid configuration\")\n\t}\n\n\tconst config = parsed.output\n\tconst dbVar =\n\t\topts.dbvar && opts.dbvar.length > 0 ? opts.dbvar : config.databaseUrlEnvVar\n\n\tconst dbUrl = process.env[dbVar]\n\tif (!dbUrl) {\n\t\tthrow new Error(\"Missing DB env var: \" + dbVar)\n\t}\n\n\tconst sql = connect(dbUrl)\n\tawait check(sql)\n\n\tawait validateTables(\n\t\tsql,\n\t\tconfig.tables.map((t) => ({\n\t\t\ttable: t.table,\n\t\t\tschema: t.schema,\n\t\t})),\n\t)\n\n\tconsole.log(C.gray + \"Starting seed process\" + C.reset)\n\n\tfor (const t of config.tables) {\n\t\tconst source = path.resolve(path.dirname(configPath), t.source)\n\t\tconst rows = loadJsonArray(source, config.onMissingFile || \"error\")\n\n\t\tif (rows.length === 0) {\n\t\t\tconsole.log(C.yellow + \"Skipped \" + t.table + C.reset)\n\t\t\tcontinue\n\t\t}\n\n\t\tconst meta = await metadata(sql, t.schema, t.table)\n\t\tconst validCols = Object.keys(rows[0]).filter((c) =>\n\t\t\tmeta.columns.includes(c),\n\t\t)\n\n\t\tlet inserted = 0\n\t\tfor (const part of chunk(rows, 1000)) {\n\t\t\tinserted += await insert(\n\t\t\t\tsql,\n\t\t\t\tt.schema,\n\t\t\t\tt.table,\n\t\t\t\tpart,\n\t\t\t\tvalidCols,\n\t\t\t\tt.primaryKeys,\n\t\t\t)\n\t\t}\n\n\t\tconsole.log(\n\t\t\tC.green +\n\t\t\t\tt.schema +\n\t\t\t\t\".\" +\n\t\t\t\tt.table +\n\t\t\t\t\": \" +\n\t\t\t\tinserted +\n\t\t\t\t\"/\" +\n\t\t\t\trows.length +\n\t\t\t\tC.reset,\n\t\t)\n\t}\n\n\tawait sql.end()\n\tconsole.log(C.green + \"Seed completed\" + C.reset)\n}\n","#!/usr/bin/env node\nimport sade from \"sade\"\nimport { run } from \"./app\"\n\nconst prog = sade(\"greenseed\")\n\nprog\n\t.command(\"push\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.option(\"-d, --dbvar <name>\", \"DB env var override\")\n\t.action(async (opts) => {\n\t\ttry {\n\t\t\tawait run(opts)\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog.parse(process.argv)\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/db.ts","../src/fs.ts","../src/app.ts","../src/init.ts","../src/cli.ts"],"names":["path","fs"],"mappings":";;;;;;;;AAEO,SAAS,QAAQ,GAAA,EAA2B;AAClD,EAAA,OAAO,SAAS,GAAA,EAAK,EAAE,KAAK,EAAA,EAAI,YAAA,EAAc,IAAI,CAAA;AACnD;AAEA,eAAsB,MAAM,GAAA,EAAkC;AAC7D,EAAA,IAAI;AACH,IAAA,MAAM,GAAA,CAAA,QAAA,CAAA;AAAA,EACP,SAAS,GAAA,EAAK;AACb,IAAA,MAAM,IAAI,MAAM,4BAA4B,CAAA;AAAA,EAC7C;AACD;AAEA,eAAsB,WAAA,CACrB,GAAA,EACA,MAAA,EACA,KAAA,EACmB;AACnB,EAAA,MAAM,MAAM,MAAM,GAAA;AAAA,qBAAA,EACI,GAAA,GAAO,MAAA,GAAS,KAAA,GAAU,KAAA,GAAQ,GAAI,CAAA;AAAA,CAAA,CAAA;AAE5D,EAAA,IAAI,CAAC,GAAA,CAAI,CAAC,CAAA,EAAG;AACZ,IAAA,MAAM,IAAI,KAAA,CAAM,yBAAA,GAA4B,MAAA,GAAS,MAAM,KAAK,CAAA;AAAA,EACjE;AACA,EAAA,OAAO,GAAA,CAAI,CAAC,CAAA,CAAE,MAAA;AACf;AAEA,eAAsB,cAAA,CACrB,KACA,MAAA,EACgB;AAChB,EAAA,KAAA,MAAW,KAAK,MAAA,EAAQ;AACvB,IAAA,MAAM,KAAK,MAAM,WAAA,CAAY,KAAK,CAAA,CAAE,MAAA,EAAQ,EAAE,KAAK,CAAA;AACnD,IAAA,IAAI,CAAC,EAAA,EAAI;AACR,MAAA,MAAM,IAAI,KAAA,CAAM,iBAAA,GAAoB,EAAE,MAAA,GAAS,GAAA,GAAM,EAAE,KAAK,CAAA;AAAA,IAC7D;AAAA,EACD;AACD;AAEA,eAAsB,QAAA,CACrB,GAAA,EACA,MAAA,EACA,KAAA,EACiC;AACjC,EAAA,MAAM,OAAO,MAAM,GAAA;AAAA;AAAA;AAAA,uBAAA,EAGK,MAAM;AAAA,mBAAA,EACV,KAAK;AAAA;AAAA,CAAA,CAAA;AAGzB,EAAA,OAAO,EAAE,SAAS,IAAA,CAAK,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,WAAW,CAAA,EAAE;AAClD;AAEA,eAAsB,OACrB,GAAA,EACA,MAAA,EACA,KAAA,EACA,IAAA,EACA,SACA,WAAA,EACkB;AAClB,EAAA,IAAI,WAAA,CAAY,WAAW,CAAA,EAAG;AAC7B,IAAA,MAAM,IAAI,KAAA,CAAM,sBAAA,GAAyB,MAAA,GAAS,MAAM,KAAK,CAAA;AAAA,EAC9D;AAEA,EAAA,MAAM,IAAI,MAAM,GAAA;AAAA,cAAA,EACD,GAAA,CAAI,MAAA,GAAS,GAAA,GAAM,KAAK,CAAC;AAAA,EAAA,EACrC,GAAA,CAAI,IAAA,EAAM,OAAO,CAAC;AAAA,eAAA,EACL,GAAA,CAAI,WAAW,CAAC,CAAA;AAAA,YAAA,EACnB,IAAI,WAAA,CAAY,CAAC,CAAA,IAAK,EAAE,CAAC;AAAA,CAAA,CAAA;AAEtC,EAAA,OAAO,CAAA,CAAE,MAAA;AACV;ACvEO,SAAS,OAAO,CAAA,EAAoB;AAC1C,EAAA,OAAO,EAAA,CAAG,WAAW,CAAC,CAAA;AACvB;AAEO,SAAS,SAAS,CAAA,EAAmB;AAC3C,EAAA,IAAI;AACH,IAAA,OAAO,EAAA,CAAG,YAAA,CAAa,CAAA,EAAG,MAAM,CAAA;AAAA,EACjC,SAAS,GAAA,EAAK;AACb,IAAA,MAAM,IAAI,KAAA,CAAM,uBAAA,GAA0B,CAAC,CAAA;AAAA,EAC5C;AACD;AAEO,SAAS,YAAY,CAAA,EAAmB;AAC9C,EAAA,OAAOA,KAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,IAAO,CAAC,CAAA;AACrC;AAMO,SAAS,aAAA,CAAc,GAAW,SAAA,EAAoC;AAC5E,EAAA,IAAI,CAAC,MAAA,CAAO,CAAC,CAAA,EAAG;AACf,IAAA,IAAI,cAAc,MAAA,EAAQ;AACzB,MAAA,OAAO,EAAC;AAAA,IACT;AACA,IAAA,MAAM,IAAI,KAAA,CAAM,yBAAA,GAA4B,CAAC,CAAA;AAAA,EAC9C;AAEA,EAAA,MAAM,GAAA,GAAM,SAAS,CAAC,CAAA;AAEtB,EAAA,IAAI,MAAA;AACJ,EAAA,IAAI;AACH,IAAA,MAAA,GAAS,IAAA,CAAK,MAAM,GAAG,CAAA;AAAA,EACxB,CAAA,CAAA,MAAQ;AACP,IAAA,MAAM,IAAI,KAAA,CAAM,gBAAA,GAAmB,CAAC,CAAA;AAAA,EACrC;AAEA,EAAA,IAAI,CAAC,KAAA,CAAM,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC3B,IAAA,MAAM,IAAI,KAAA,CAAM,wBAAA,GAA2B,CAAC,CAAA;AAAA,EAC7C;AAEA,EAAA,OAAO,MAAA;AACR;AAEO,SAAS,KAAA,CAAS,MAAW,IAAA,EAAqB;AACxD,EAAA,MAAM,MAAa,EAAC;AACpB,EAAA,KAAA,IAAS,IAAI,CAAA,EAAG,CAAA,GAAI,IAAA,CAAK,MAAA,EAAQ,KAAK,IAAA,EAAM;AAC3C,IAAA,GAAA,CAAI,KAAK,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,CAAA,GAAI,IAAI,CAAC,CAAA;AAAA,EACjC;AACA,EAAA,OAAO,GAAA;AACR;;;AC/CA,IAAM,CAAA,GAAI;AAAA,EACT,KAAA,EAAO,SAAA;AAAA,EACP,KAAA,EAAO,UAAA;AAAA,EACP,MAAA,EAAQ,UAAA;AAAA,EAER,IAAA,EAAM;AACP,CAAA;AAEA,IAAM,eAAiB,CAAA,CAAA,MAAA,CAAO;AAAA,EAC7B,oBAAsB,CAAA,CAAA,KAAA,CAAQ,CAAA,CAAA,QAAA,CAAS,CAAC,OAAO,CAAC,CAAC,CAAA;AAAA,EACjD,mBAAqB,CAAA,CAAA,MAAA,EAAO;AAAA,EAC5B,aAAA,EAAiB,WAAW,CAAA,CAAA,QAAA,CAAS,CAAC,QAAQ,OAAO,CAAC,GAAG,OAAO,CAAA;AAAA,EAChE,MAAA,EAAU,CAAA,CAAA,KAAA;AAAA,IACP,CAAA,CAAA,MAAA,CAAO;AAAA,MACR,OAAS,CAAA,CAAA,MAAA,EAAO;AAAA,MAChB,MAAA,EAAU,CAAA,CAAA,QAAA,CAAW,CAAA,CAAA,MAAA,EAAO,EAAG,QAAQ,CAAA;AAAA,MACvC,WAAA,EAAe,CAAA,CAAA,KAAA,CAAQ,CAAA,CAAA,MAAA,EAAQ,CAAA;AAAA,MAC/B,QAAU,CAAA,CAAA,MAAA;AAAO,KACjB;AAAA;AAEH,CAAC,CAAA;AAID,eAAsB,IAAI,IAAA,EAA2B;AACpD,EAAA,MAAA,CAAO,MAAA,EAAO;AAEd,EAAA,MAAM,UAAA,GAAa,WAAA,CAAY,IAAA,CAAK,MAAA,IAAU,kBAAkB,CAAA;AAEhE,EAAA,IAAI,GAAA;AACJ,EAAA,IAAI;AACH,IAAA,GAAA,GAAM,IAAA,CAAK,KAAA,CAAM,QAAA,CAAS,UAAU,CAAC,CAAA;AAAA,EACtC,SAAS,CAAA,EAAG;AACX,IAAA,MAAM,IAAI,KAAA;AAAA,MACT,CAAA,yBAAA,EAA4B,UAAU,CAAA,EAAA,EAAK,CAAA,YAAa,QAAQ,CAAA,CAAE,OAAA,GAAU,MAAA,CAAO,CAAC,CAAC,CAAA;AAAA,KACtF;AAAA,EACD;AAEA,EAAA,MAAM,MAAA,GAAW,CAAA,CAAA,SAAA,CAAU,YAAA,EAAc,GAAG,CAAA;AAE5C,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AACpB,IAAA,MAAM,IAAI,MAAM,uBAAuB,CAAA;AAAA,EACxC;AAEA,EAAA,MAAM,SAAS,MAAA,CAAO,MAAA;AACtB,EAAA,MAAM,KAAA,GACL,KAAK,KAAA,IAAS,IAAA,CAAK,MAAM,MAAA,GAAS,CAAA,GAAI,IAAA,CAAK,KAAA,GAAQ,MAAA,CAAO,iBAAA;AAE3D,EAAA,MAAM,KAAA,GAAQ,OAAA,CAAQ,GAAA,CAAI,KAAK,CAAA;AAC/B,EAAA,IAAI,CAAC,KAAA,EAAO;AACX,IAAA,MAAM,IAAI,KAAA,CAAM,sBAAA,GAAyB,KAAK,CAAA;AAAA,EAC/C;AAEA,EAAA,MAAM,GAAA,GAAM,QAAQ,KAAK,CAAA;AACzB,EAAA,MAAM,MAAM,GAAG,CAAA;AAEf,EAAA,MAAM,cAAA;AAAA,IACL,GAAA;AAAA,IACA,MAAA,CAAO,MAAA,CAAO,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,MACzB,OAAO,CAAA,CAAE,KAAA;AAAA,MACT,QAAQ,CAAA,CAAE;AAAA,KACX,CAAE;AAAA,GACH;AAEA,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,CAAE,IAAA,GAAO,uBAAA,GAA0B,EAAE,KAAK,CAAA;AAEtD,EAAA,KAAA,MAAW,CAAA,IAAK,OAAO,MAAA,EAAQ;AAC9B,IAAA,MAAM,MAAA,GAASA,MAAK,OAAA,CAAQA,KAAAA,CAAK,QAAQ,UAAU,CAAA,EAAG,EAAE,MAAM,CAAA;AAC9D,IAAA,MAAM,IAAA,GAAO,aAAA,CAAc,MAAA,EAAQ,MAAA,CAAO,iBAAiB,OAAO,CAAA;AAElE,IAAA,IAAI,IAAA,CAAK,WAAW,CAAA,EAAG;AACtB,MAAA,OAAA,CAAQ,IAAI,CAAA,CAAE,MAAA,GAAS,aAAa,CAAA,CAAE,KAAA,GAAQ,EAAE,KAAK,CAAA;AACrD,MAAA;AAAA,IACD;AAEA,IAAA,MAAM,OAAO,MAAM,QAAA,CAAS,KAAK,CAAA,CAAE,MAAA,EAAQ,EAAE,KAAK,CAAA;AAClD,IAAA,MAAM,YAAY,MAAA,CAAO,IAAA,CAAK,IAAA,CAAK,CAAC,CAAC,CAAA,CAAE,MAAA;AAAA,MAAO,CAAC,CAAA,KAC9C,IAAA,CAAK,OAAA,CAAQ,SAAS,CAAC;AAAA,KACxB;AAEA,IAAA,IAAI,QAAA,GAAW,CAAA;AACf,IAAA,KAAA,MAAW,IAAA,IAAQ,KAAA,CAAM,IAAA,EAAM,GAAI,CAAA,EAAG;AACrC,MAAA,QAAA,IAAY,MAAM,MAAA;AAAA,QACjB,GAAA;AAAA,QACA,CAAA,CAAE,MAAA;AAAA,QACF,CAAA,CAAE,KAAA;AAAA,QACF,IAAA;AAAA,QACA,SAAA;AAAA,QACA,CAAA,CAAE;AAAA,OACH;AAAA,IACD;AAEA,IAAA,OAAA,CAAQ,GAAA;AAAA,MACP,CAAA,CAAE,KAAA,GACD,CAAA,CAAE,MAAA,GACF,GAAA,GACA,CAAA,CAAE,KAAA,GACF,IAAA,GACA,QAAA,GACA,GAAA,GACA,IAAA,CAAK,MAAA,GACL,CAAA,CAAE;AAAA,KACJ;AAAA,EACD;AAEA,EAAA,MAAM,IAAI,GAAA,EAAI;AACd,EAAA,OAAA,CAAQ,GAAA,CAAI,CAAA,CAAE,KAAA,GAAQ,gBAAA,GAAmB,EAAE,KAAK,CAAA;AACjD;AC9GA,IAAM,cAAA,GAAiB;AAAA,EACtB,kBAAA,EAAoB,CAAC,OAAO,CAAA;AAAA,EAC5B,iBAAA,EAAmB,cAAA;AAAA,EACnB,aAAA,EAAe,OAAA;AAAA,EACf,MAAA,EAAQ;AAAA,IACP;AAAA,MACC,KAAA,EAAO,eAAA;AAAA,MACP,MAAA,EAAQ,QAAA;AAAA,MACR,WAAA,EAAa,CAAC,IAAI,CAAA;AAAA,MAClB,MAAA,EAAQ;AAAA;AACT;AAEF,CAAA;AAEO,SAAS,KAAK,UAAA,EAA0B;AAC9C,EAAA,MAAM,WAAWA,KAAAA,CAAK,OAAA,CAAQ,OAAA,CAAQ,GAAA,IAAO,UAAU,CAAA;AAEvD,EAAA,IAAIC,EAAAA,CAAG,UAAA,CAAW,QAAQ,CAAA,EAAG;AAC5B,IAAA,MAAM,IAAI,KAAA,CAAM,yBAAA,GAA4B,QAAQ,CAAA;AAAA,EACrD;AAEA,EAAAA,EAAAA,CAAG,aAAA;AAAA,IACF,QAAA;AAAA,IACA,IAAA,CAAK,SAAA,CAAU,cAAA,EAAgB,IAAA,EAAM,CAAC,CAAA,GAAI,IAAA;AAAA,IAC1C;AAAA,GACD;AACD;;;ACxBA,IAAM,IAAA,GAAO,KAAK,WAAW,CAAA;AAE7B,IAAA,CACE,OAAA,CAAQ,MAAM,CAAA,CACd,MAAA,CAAO,uBAAuB,aAAA,EAAe,kBAAkB,CAAA,CAC/D,MAAA,CAAO,oBAAA,EAAsB,qBAAqB,CAAA,CAClD,MAAA,CAAO,OAAO,IAAA,KAAS;AACvB,EAAA,IAAI;AACH,IAAA,MAAM,IAAI,IAAI,CAAA;AAAA,EACf,SAAS,GAAA,EAAK;AACb,IAAA,OAAA,CAAQ,KAAA,CAAM,UAAA,GAAa,MAAA,CAAO,GAAG,IAAI,SAAS,CAAA;AAClD,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EACf;AACD,CAAC,CAAA;AAEF,IAAA,CACE,OAAA,CAAQ,MAAM,CAAA,CACd,MAAA,CAAO,qBAAA,EAAuB,eAAe,kBAAkB,CAAA,CAC/D,MAAA,CAAO,CAAC,IAAA,KAAS;AACjB,EAAA,IAAI;AACH,IAAA,IAAA,CAAK,KAAK,MAAM,CAAA;AAChB,IAAA,OAAA,CAAQ,GAAA,CAAI,0BAAA,GAA6B,IAAA,CAAK,MAAA,GAAS,SAAS,CAAA;AAAA,EACjE,SAAS,GAAA,EAAK;AACb,IAAA,OAAA,CAAQ,KAAA,CAAM,UAAA,GAAa,MAAA,CAAO,GAAG,IAAI,SAAS,CAAA;AAClD,IAAA,OAAA,CAAQ,KAAK,CAAC,CAAA;AAAA,EACf;AACD,CAAC,CAAA;AAEF,IAAA,CAAK,KAAA,CAAM,QAAQ,IAAI,CAAA","file":"cli.js","sourcesContent":["import postgres from \"postgres\"\n\nexport function connect(url: string): postgres.Sql {\n\treturn postgres(url, { max: 10, idle_timeout: 20 })\n}\n\nexport async function check(sql: postgres.Sql): Promise<void> {\n\ttry {\n\t\tawait sql`select 1`\n\t} catch (err) {\n\t\tthrow new Error(\"Database connection failed\")\n\t}\n}\n\nexport async function tableExists(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<boolean> {\n\tconst res = await sql<{ exists: boolean }[]>`\n\t\tselect to_regclass(${\"\\\"\" + schema + \"\\\".\\\"\" + table + \"\\\"\"}) is not null as exists\n\t`\n\tif (!res[0]) {\n\t\tthrow new Error(\"Failed checking table: \" + schema + \".\" + table)\n\t}\n\treturn res[0].exists\n}\n\nexport async function validateTables(\n\tsql: postgres.Sql,\n\ttables: { schema: string; table: string }[],\n): Promise<void> {\n\tfor (const t of tables) {\n\t\tconst ok = await tableExists(sql, t.schema, t.table)\n\t\tif (!ok) {\n\t\t\tthrow new Error(\"Missing table: \" + t.schema + \".\" + t.table)\n\t\t}\n\t}\n}\n\nexport async function metadata(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<{ columns: string[] }> {\n\tconst cols = await sql<{ column_name: string }[]>`\n\t\tselect column_name\n\t\tfrom information_schema.columns\n\t\twhere table_schema = ${schema}\n\t\tand table_name = ${table}\n\t\torder by ordinal_position\n\t`\n\treturn { columns: cols.map((c) => c.column_name) }\n}\n\nexport async function insert(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n\trows: Record<string, any>[],\n\tcolumns: string[],\n\tprimaryKeys: string[],\n): Promise<number> {\n\tif (primaryKeys.length === 0) {\n\t\tthrow new Error(\"No primary keys for \" + schema + \".\" + table)\n\t}\n\n\tconst q = await sql`\n\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t${sql(rows, columns)}\n\t\ton conflict (${sql(primaryKeys)}) do nothing\n\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t`\n\treturn q.length\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nexport function exists(p: string): boolean {\n\treturn fs.existsSync(p)\n}\n\nexport function readFile(p: string): string {\n\ttry {\n\t\treturn fs.readFileSync(p, \"utf8\")\n\t} catch (err) {\n\t\tthrow new Error(\"Failed to read file: \" + p)\n\t}\n}\n\nexport function resolvePath(p: string): string {\n\treturn path.resolve(process.cwd(), p)\n}\n\nexport function ext(p: string): string {\n\treturn path.extname(p).toLowerCase()\n}\n\nexport function loadJsonArray(p: string, onMissing: \"skip\" | \"error\"): any[] {\n\tif (!exists(p)) {\n\t\tif (onMissing === \"skip\") {\n\t\t\treturn []\n\t\t}\n\t\tthrow new Error(\"Source file not found: \" + p)\n\t}\n\n\tconst raw = readFile(p)\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\tthrow new Error(\"Invalid JSON: \" + p)\n\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error(\"Data is not an array: \" + p)\n\t}\n\n\treturn parsed\n}\n\nexport function chunk<T>(data: T[], size: number): T[][] {\n\tconst out: T[][] = []\n\tfor (let i = 0; i < data.length; i += size) {\n\t\tout.push(data.slice(i, i + size))\n\t}\n\treturn out\n}\n","import * as v from \"valibot\"\nimport dotenv from \"dotenv\"\nimport path from \"path\"\nimport { connect, check, validateTables, metadata, insert } from \"./db\"\nimport { resolvePath, loadJsonArray, chunk, readFile } from \"./fs\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tgreen: \"\\x1b[32m\",\n\tyellow: \"\\x1b[33m\",\n\tred: \"\\x1b[31m\",\n\tgray: \"\\x1b[90m\",\n}\n\nconst ConfigSchema = v.object({\n\tseedFileExtensions: v.array(v.picklist([\".json\"])),\n\tdatabaseUrlEnvVar: v.string(),\n\tonMissingFile: v.optional(v.picklist([\"skip\", \"error\"]), \"error\"),\n\ttables: v.array(\n\t\tv.object({\n\t\t\ttable: v.string(),\n\t\t\tschema: v.optional(v.string(), \"public\"),\n\t\t\tprimaryKeys: v.array(v.string()),\n\t\t\tsource: v.string(),\n\t\t}),\n\t),\n})\n\ntype Opts = { config: string; dbvar?: string }\n\nexport async function run(opts: Opts): Promise<void> {\n\tdotenv.config()\n\n\tconst configPath = resolvePath(opts.config || \"seed.config.json\")\n\n\tlet raw: unknown\n\ttry {\n\t\traw = JSON.parse(readFile(configPath))\n\t} catch (e) {\n\t\tthrow new Error(\n\t\t\t`Failed to load config at ${configPath}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t)\n\t}\n\n\tconst parsed = v.safeParse(ConfigSchema, raw)\n\n\tif (!parsed.success) {\n\t\tthrow new Error(\"Invalid configuration\")\n\t}\n\n\tconst config = parsed.output\n\tconst dbVar =\n\t\topts.dbvar && opts.dbvar.length > 0 ? opts.dbvar : config.databaseUrlEnvVar\n\n\tconst dbUrl = process.env[dbVar]\n\tif (!dbUrl) {\n\t\tthrow new Error(\"Missing DB env var: \" + dbVar)\n\t}\n\n\tconst sql = connect(dbUrl)\n\tawait check(sql)\n\n\tawait validateTables(\n\t\tsql,\n\t\tconfig.tables.map((t) => ({\n\t\t\ttable: t.table,\n\t\t\tschema: t.schema,\n\t\t})),\n\t)\n\n\tconsole.log(C.gray + \"Starting seed process\" + C.reset)\n\n\tfor (const t of config.tables) {\n\t\tconst source = path.resolve(path.dirname(configPath), t.source)\n\t\tconst rows = loadJsonArray(source, config.onMissingFile || \"error\")\n\n\t\tif (rows.length === 0) {\n\t\t\tconsole.log(C.yellow + \"Skipped \" + t.table + C.reset)\n\t\t\tcontinue\n\t\t}\n\n\t\tconst meta = await metadata(sql, t.schema, t.table)\n\t\tconst validCols = Object.keys(rows[0]).filter((c) =>\n\t\t\tmeta.columns.includes(c),\n\t\t)\n\n\t\tlet inserted = 0\n\t\tfor (const part of chunk(rows, 1000)) {\n\t\t\tinserted += await insert(\n\t\t\t\tsql,\n\t\t\t\tt.schema,\n\t\t\t\tt.table,\n\t\t\t\tpart,\n\t\t\t\tvalidCols,\n\t\t\t\tt.primaryKeys,\n\t\t\t)\n\t\t}\n\n\t\tconsole.log(\n\t\t\tC.green +\n\t\t\t\tt.schema +\n\t\t\t\t\".\" +\n\t\t\t\tt.table +\n\t\t\t\t\": \" +\n\t\t\t\tinserted +\n\t\t\t\t\"/\" +\n\t\t\t\trows.length +\n\t\t\t\tC.reset,\n\t\t)\n\t}\n\n\tawait sql.end()\n\tconsole.log(C.green + \"Seed completed\" + C.reset)\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nconst DEFAULT_CONFIG = {\n\tseedFileExtensions: [\".json\"],\n\tdatabaseUrlEnvVar: \"DATABASE_URL\",\n\tonMissingFile: \"error\",\n\ttables: [\n\t\t{\n\t\t\ttable: \"example_table\",\n\t\t\tschema: \"public\",\n\t\t\tprimaryKeys: [\"id\"],\n\t\t\tsource: \"./data/example_table.json\",\n\t\t},\n\t],\n}\n\nexport function init(configPath: string): void {\n\tconst resolved = path.resolve(process.cwd(), configPath)\n\n\tif (fs.existsSync(resolved)) {\n\t\tthrow new Error(\"Config already exists: \" + resolved)\n\t}\n\n\tfs.writeFileSync(\n\t\tresolved,\n\t\tJSON.stringify(DEFAULT_CONFIG, null, 2) + \"\\n\",\n\t\t\"utf8\",\n\t)\n}\n","#!/usr/bin/env node\nimport sade from \"sade\"\nimport { run } from \"./app\"\nimport { init } from \"./init\"\n\nconst prog = sade(\"greenseed\")\n\nprog\n\t.command(\"push\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.option(\"-d, --dbvar <name>\", \"DB env var override\")\n\t.action(async (opts) => {\n\t\ttry {\n\t\t\tawait run(opts)\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog\n\t.command(\"init\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.action((opts) => {\n\t\ttry {\n\t\t\tinit(opts.config)\n\t\t\tconsole.log(\"\\x1b[32mConfig created: \" + opts.config + \"\\x1b[0m\")\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog.parse(process.argv)\n"]}
|